You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不使用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)来传递的。下面我会基于你现有的代码,一步步修改实现:

核心思路

  1. 构造Key Usage对应的ASN.1 BIT STRING:每个Key Usage用途对应一个比特位(从0开始计数,比如digitalSignature是位0,keyEncipherment是位2)。
  2. 把Key Usage包装成标准的X.509扩展结构。
  3. 将扩展放入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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:18:48