如何在Java中从PEM编码公钥生成包含x5c/x5t的JWK?
生成包含x5c和x5t#S256字段的RSA JWK(基于BouncyCastle)
问题背景
我持有PEM编码的RSA公钥和自签名证书,使用Nimbus JOSE + JWT库生成JWK时,输出结果缺失x5c(X.509证书链)和x5t#S256(证书SHA256指纹)字段,需要补全这两部分的实现。
现有代码
String getJWTKey(final String rawKey) throws Exception { final KeyFactory kf = KeyFactory.getInstance("RSA"); final SubjectPublicKeyInfo pubKeyInfo = (SubjectPublicKeyInfo) new PEMParser(new StringReader(rawKey)).readObject(); final X509EncodedKeySpec keySpecX509 = new X509EncodedKeySpec(pubKeyInfo.getEncoded()); final RSAPublicKey rsaPubKey = (RSAPublicKey) kf.generatePublic(keySpecX509); // How to get these two? List<Base64> certChain = null; Base64URL thumbprint = null; final RSAKey key = new RSAKey.Builder(rsaPubKey) .keyUse(KeyUse.SIGNATURE) .algorithm(new Algorithm("RS256")) .keyID("mykey") .x509CertChain(certChain) .x509CertSHA256Thumbprint(thumbprint) .build(); return key.toJSONString(); }
缺失的实现逻辑
要生成x5c和x5t#S256字段,需要完成以下两步:
- 解析PEM证书并构建证书链:读取PEM中的X.509证书,将其DER编码转为Base64格式,存入列表作为证书链
- 计算证书SHA256指纹:对证书的DER编码计算SHA256哈希,再将哈希值转为Base64URL编码格式
完整修正代码
import com.nimbusds.jose.jwk.RSAKey; import com.nimbusds.jose.jwk.KeyUse; import com.nimbusds.jose.jwk.Algorithm; import com.nimbusds.jose.util.Base64; import com.nimbusds.jose.util.Base64URL; import org.bouncycastle.cert.X509CertificateHolder; import org.bouncycastle.openssl.PEMParser; import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo; import java.io.StringReader; import java.security.KeyFactory; import java.security.spec.X509EncodedKeySpec; import java.security.MessageDigest; import java.util.ArrayList; import java.util.List; String getJWTKey(final String rawPemContent) throws Exception { // 若未全局注册BouncyCastle,需添加以下代码 // Security.addProvider(new BouncyCastleProvider()); List<Base64> certChain = new ArrayList<>(); RSAPublicKey rsaPubKey = null; Base64URL certThumbprint = null; try (PEMParser pemParser = new PEMParser(new StringReader(rawPemContent))) { Object obj; while ((obj = pemParser.readObject()) != null) { if (obj instanceof SubjectPublicKeyInfo) { // 解析RSA公钥 SubjectPublicKeyInfo pubKeyInfo = (SubjectPublicKeyInfo) obj; X509EncodedKeySpec keySpec = new X509EncodedKeySpec(pubKeyInfo.getEncoded()); KeyFactory kf = KeyFactory.getInstance("RSA"); rsaPubKey = (RSAPublicKey) kf.generatePublic(keySpec); } else if (obj instanceof X509CertificateHolder) { // 解析X.509证书,构建证书链 X509CertificateHolder certHolder = (X509CertificateHolder) obj; byte[] certDer = certHolder.getEncoded(); certChain.add(Base64.encode(certDer)); // 计算证书SHA256指纹(对应x5t#S256字段) MessageDigest sha256Digest = MessageDigest.getInstance("SHA-256"); byte[] thumbprintBytes = sha256Digest.digest(certDer); certThumbprint = Base64URL.encode(thumbprintBytes); } } } // 构建完整的RSA JWK RSAKey.Builder jwkBuilder = new RSAKey.Builder(rsaPubKey) .keyUse(KeyUse.SIGNATURE) .algorithm(new Algorithm("RS256")) .keyID("mykey"); if (!certChain.isEmpty()) { jwkBuilder.x509CertChain(certChain); jwkBuilder.x509CertSHA256Thumbprint(certThumbprint); } RSAKey jwk = jwkBuilder.build(); // 若需返回JWKS格式(包含keys数组),可改用以下代码 // JWKS jwks = new JWKS(jwk); // return jwks.toJSONString(); return jwk.toJSONString(); }
关键说明
- 证书链解析:通过
PEMParser遍历PEM内容,识别X509CertificateHolder对象,将其DER编码转为Base64字符串,对应JWK的x5c字段 - 指纹计算:对证书的DER编码计算SHA256哈希,再用
Base64URL.encode()处理哈希值,对应JWK的x5t#S256字段 - 依赖要求:确保项目已引入BouncyCastle相关依赖,如
bcprov-jdk15on
内容的提问来源于stack exchange,提问作者stwissel
相关产品推荐
相关产品推荐

