You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Java中使用Bouncy Castle如何覆盖X500Name的CN字段?

我之前刚好解决过这个问题——Bouncy Castle的X500Name是不可变类,没法直接调用update方法修改字段,不过我们可以通过克隆原CSR的主体RDN列表,替换掉CN字段来实现你的需求。下面是具体的实现思路和代码:

核心思路

  1. 从CSR中获取原始的X500Name主体信息
  2. 遍历原始的RDN(相对辨别名)列表,保留除CN外的所有字段,替换或添加自定义CN
  3. 用处理后的RDN列表生成新的X500Name
  4. 使用这个新的主体信息来签署证书

完整代码示例

import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;

import java.io.StringReader;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CsrSignerWithCustomCN {
    public static void main(String[] args) throws Exception {
        // 注册Bouncy Castle安全提供者
        Security.addProvider(new BouncyCastleProvider());

        // 示例:解析PEM格式的CSR(实际场景中可从文件/流读取)
        String csrPem = "-----BEGIN CERTIFICATE REQUEST-----\n" +
                "MIICtzCCAX8CAQAwgY0xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDTjELMAkGA1UE\n" +
                "BxMCQ04xDzANBgNVBAoTBlRlc3RDbzEPMA0GA1UECxMGVGVzdENvMRgwFgYDVQQD\n" +
                "Ex90ZXN0LmNvbS5leGFtcGxlLmNvbTEeMBwGCSqGSIb3DQEJARYPYWRtaW5AZXhh\n" +
                "bXBsZS5jb20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDf9Kd8z6+\n" +
                "你的CSR内容...\n" +
                "-----END CERTIFICATE REQUEST-----";

        PEMParser pemParser = new PEMParser(new StringReader(csrPem));
        org.bouncycastle.pkcs.PKCS10CertificationRequest csr =
                (org.bouncycastle.pkcs.PKCS10CertificationRequest) pemParser.readObject();
        pemParser.close();

        // 1. 获取CSR原始主体
        X500Name originalSubject = csr.getSubject();

        // 2. 处理RDN列表,替换CN字段
        RDN[] originalRdns = originalSubject.getRDNs();
        List<RDN> newRdnList = new ArrayList<>();
        boolean cnReplaced = false;

        for (RDN rdn : originalRdns) {
            if (rdn.getFirst().getType().equals(BCStyle.CN)) {
                // 替换原CN为自定义值
                newRdnList.add(new RDN(BCStyle.CN, "mycustomcn"));
                cnReplaced = true;
            } else {
                // 保留其他所有字段
                newRdnList.add(rdn);
            }
        }

        // 处理原主体没有CN的情况,直接添加自定义CN
        if (!cnReplaced) {
            newRdnList.add(new RDN(BCStyle.CN, "mycustomcn"));
        }

        // 生成新的主体X500Name
        X500Name newSubject = new X500Name(newRdnList.toArray(new RDN[0]));

        // 3. 准备CA密钥对(实际场景请使用真实CA的证书和私钥)
        KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "BC");
        keyGen.initialize(2048);
        KeyPair caKeyPair = keyGen.generateKeyPair();
        X500Name caSubject = new X500Name("CN=MyRootCA, O=MyOrganization");

        // 4. 构建并签署证书
        Date startDate = new Date();
        Date endDate = new Date(startDate.getTime() + 365L * 24 * 60 * 60 * 1000); // 1年有效期

        X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
                caSubject,
                java.math.BigInteger.valueOf(System.currentTimeMillis()),
                startDate,
                endDate,
                newSubject, // 使用修改后的主体
                csr.getSubjectPublicKeyInfo()
        );

        // 使用CA私钥签名
        ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
                .setProvider("BC")
                .build(caKeyPair.getPrivate());

        X509Certificate signedCert = new JcaX509CertificateConverter()
                .setProvider("BC")
                .getCertificate(certBuilder.build(signer));

        // 验证结果:输出证书主体,确认CN已替换
        System.out.println("签署后证书主体: " + signedCert.getSubjectX500Principal().getName());
    }
}

关键细节说明

  • 不可变的X500Name:Bouncy Castle的X500Name设计为不可变,所以必须通过重新构建RDN列表来修改字段,不能直接编辑
  • 兼容无CN的场景:代码中加入了判断,如果原CSR主体没有CN字段,会自动添加自定义CN,避免遗漏
  • 保留原有字段:遍历过程中保留了除CN外的所有原始RDN,确保其他主体信息(如O、OU、C等)完全继承自CSR

内容的提问来源于stack exchange,提问作者Robert Daniels

火山引擎 最新活动