如何在不使用BouncyCastle的Java中为CertificateSigningInfo添加Key Usage
在Java中无需BouncyCastle为PKCS#10请求添加Key Usage扩展
嘿,我来帮你搞定这个需求——不用BouncyCastle,纯用Java自带的ASN.1工具类给CertificateSigningInfo(PKCS#10请求的核心部分)加上Key Usage扩展。
首先得搞懂PKCS#10的结构:CertificationRequestInfo是一个ASN.1序列,最后一个可选字段是attributes集合,而Key Usage这类X.509扩展是通过ExtensionRequest属性(OID:1.2.840.113549.1.9.14)来传递的。下面我会基于你现有的代码,一步步修改实现:
核心思路
- 构造Key Usage对应的ASN.1
BIT STRING:每个Key Usage用途对应一个比特位(从0开始计数,比如digitalSignature是位0,keyEncipherment是位2)。 - 把Key Usage包装成标准的X.509扩展结构。
- 将扩展放入
ExtensionRequest属性中,再把属性集合添加到CertificationRequestInfo的末尾。
纯Java实现代码(无BouncyCastle依赖)
import java.io.ByteArrayOutputStream; import java.io.IOException; import java.math.BigInteger; import java.security.PublicKey; import sun.security.asn1.ASN1BitString; import sun.security.asn1.ASN1Integer; import sun.security.asn1.ASN1ObjectIdentifier; import sun.security.asn1.ASN1Sequence; import sun.security.asn1.DEROutputStream; import sun.security.x509.X500Name; public class CSRGenerator { // Key Usage扩展的OID private static final String KEY_USAGE_OID = "2.5.29.15"; // ExtensionRequest属性的OID private static final String EXTENSION_REQUEST_OID = "1.2.840.113549.1.9.14"; public static byte[] createCertificationRequestInfo(X500Name x500Name, PublicKey publicKey) throws IOException { ByteArrayOutputStream csrInfoBaos = new ByteArrayOutputStream(); DEROutputStream derOut = new DEROutputStream(csrInfoBaos); // 1. 写入PKCS#10版本号(v1对应0) derOut.writeObject(new ASN1Integer(BigInteger.ZERO)); // 2. 写入主体X500名称 derOut.writeObject(x500Name); // 3. 写入公钥(需解析为ASN.1序列) derOut.writeObject(ASN1Sequence.fromByteArray(publicKey.getEncoded())); // --- 新增:构造并添加Key Usage扩展 --- // 步骤1:生成Key Usage的BIT STRING // 示例:启用digitalSignature(位0)和keyEncipherment(位2) // 比特位对应:位0=1,位2=1 → 字节值为0b00000101=5,未使用比特数为0 byte[] keyUsageBytes = new byte[]{5}; ASN1BitString keyUsageBitString = new ASN1BitString(keyUsageBytes, 0); // 步骤2:构造单个X.509扩展(SEQUENCE { oid, critical, value }) ByteArrayOutputStream extBaos = new ByteArrayOutputStream(); DEROutputStream extDerOut = new DEROutputStream(extBaos); extDerOut.writeObject(new ASN1ObjectIdentifier(KEY_USAGE_OID)); extDerOut.writeObject(Boolean.TRUE); // 将Key Usage标记为critical(可选,根据需求调整) extDerOut.writeObject(keyUsageBitString); ASN1Sequence keyUsageExtension = ASN1Sequence.fromByteArray(extBaos.toByteArray()); // 步骤3:构造Extensions集合(SEQUENCE OF Extension) ByteArrayOutputStream extsBaos = new ByteArrayOutputStream(); DEROutputStream extsDerOut = new DEROutputStream(extsBaos); extsDerOut.writeObject(keyUsageExtension); ASN1Sequence extensions = ASN1Sequence.fromByteArray(extsBaos.toByteArray()); // 步骤4:构造ExtensionRequest属性 ByteArrayOutputStream attrBaos = new ByteArrayOutputStream(); DEROutputStream attrDerOut = new DEROutputStream(attrBaos); attrDerOut.writeObject(new ASN1ObjectIdentifier(EXTENSION_REQUEST_OID)); // 属性值是一个SET,这里放入我们的Extensions序列 ByteArrayOutputStream setBaos = new ByteArrayOutputStream(); DEROutputStream setDerOut = new DEROutputStream(setBaos); setDerOut.writeObject(extensions); attrDerOut.writeObject(ASN1Sequence.fromByteArray(setBaos.toByteArray())); ASN1Sequence extensionRequestAttr = ASN1Sequence.fromByteArray(attrBaos.toByteArray()); // 步骤5:构造Attributes集合并写入 ByteArrayOutputStream attrsBaos = new ByteArrayOutputStream(); DEROutputStream attrsDerOut = new DEROutputStream(attrsBaos); attrsDerOut.writeObject(extensionRequestAttr); ASN1Sequence attributes = ASN1Sequence.fromByteArray(attrsBaos.toByteArray()); derOut.writeObject(attributes); // 最后将整个CSR Info包装为ASN.1序列 ByteArrayOutputStream finalBaos = new ByteArrayOutputStream(); DEROutputStream finalDerOut = new DEROutputStream(finalBaos); finalDerOut.write(ASN1Sequence.TAG, csrInfoBaos.toByteArray()); return finalBaos.toByteArray(); } }
关键细节说明
- Key Usage比特位计算:你可以根据需求调整
keyUsageBytes的值,比如要启用keyCertSign(位5)和cRLSign(位6),对应的字节是0b01100000=96。 - 内部API注意事项:代码中用到了
sun.security下的类,这些是Java的内部API。在Java 9+版本中,需要添加--add-opens java.base/sun.security.asn1=ALL-UNNAMED和--add-opens java.base/sun.security.x509=ALL-UNNAMED参数来允许访问;Java 8及以下版本可以直接使用。 - Critical标志:代码中把Key Usage标记为
critical,如果你的场景不需要,可以把Boolean.TRUE改成Boolean.FALSE。
这样修改后,你生成的CertificateSigningInfo就会携带Key Usage扩展,后续签名得到的PKCS#10请求就能被CA正确识别这个扩展了。
内容的提问来源于stack exchange,提问作者user1563721
相关产品推荐
相关产品推荐

