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

Java中用Bouncy Castle生成的终端实体X.509证书导出时缺失证书链

问题:Bouncy Castle生成的终端实体证书无证书链且签名者显示“未知”

我通过以下Java代码使用Bouncy Castle生成终端实体X.509证书:

public static X509Certificate createEndEntity(
    X509Certificate signerCert, PrivateKey signerKey,
    String sigAlg, PublicKey certKey)
  throws CertIOException, OperatorCreationException, CertificateException
{
  X500Principal subject = new X500Principal("CN=End Entity");

  X509v3CertificateBuilder  certBldr = new JcaX509v3CertificateBuilder(
      signerCert.getSubjectX500Principal(),
      calculateSerialNumber(),
      calculateDate(0),
      calculateDate(24 * 31),
      subject,
      certKey);

  certBldr.addExtension(Extension.basicConstraints,
          true, new BasicConstraints(false))
      .addExtension(Extension.keyUsage,
          true, new KeyUsage(KeyUsage.digitalSignature));

  ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
                  .setProvider("BC").build(signerKey);

  JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider("BC");

  return converter.getCertificate(certBldr.build(signer));
}

再通过以下代码将证书导出为PEM格式:

StringWriter sw = new StringWriter();
JcaPEMWriter jpw = new JcaPEMWriter(sw);
jpw.writeObject(certificate);
jpw.flush();
String pem = sw.toString();

当前遇到两个问题:

  • 生成的证书不包含指向根CA的证书链
  • 证书签名中的签名者显示为“未知”

备注:生成证书前已在XCA中单独导入根CA。
证书详情截图,显示签名者为未知


解决方案

1. 修复签名者显示“未知”问题

签名者显示未知,是因为终端实体证书缺少Authority Key Identifier(签发者密钥标识)和Subject Key Identifier(主体密钥标识)扩展,无法关联到签发的根CA证书。

修改证书生成代码,添加这两个扩展:

public static X509Certificate createEndEntity(
    X509Certificate signerCert, PrivateKey signerKey,
    String sigAlg, PublicKey certKey)
  throws CertIOException, OperatorCreationException, CertificateException, NoSuchAlgorithmException, InvalidKeyException
{
  X500Principal subject = new X500Principal("CN=End Entity");

  X509v3CertificateBuilder certBldr = new JcaX509v3CertificateBuilder(
      signerCert.getSubjectX500Principal(),
      calculateSerialNumber(),
      calculateDate(0),
      calculateDate(24 * 31),
      subject,
      certKey);

  // 生成签发者证书的Subject Key Identifier
  SubjectPublicKeyInfo signerSpki = SubjectPublicKeyInfo.getInstance(signerCert.getPublicKey().getEncoded());
  SubjectKeyIdentifier signerSki = new JcaSubjectKeyIdentifierBuilder().build(signerSpki);

  // 生成当前证书的Subject Key Identifier
  SubjectKeyIdentifier subjectSki = new JcaSubjectKeyIdentifierBuilder().build(certKey);

  certBldr.addExtension(Extension.basicConstraints, true, new BasicConstraints(false))
          .addExtension(Extension.keyUsage, true, new KeyUsage(KeyUsage.digitalSignature))
          // 添加Authority Key Identifier,关联签发者
          .addExtension(Extension.authorityKeyIdentifier, false, signerSki)
          // 添加Subject Key Identifier,标识当前证书公钥
          .addExtension(Extension.subjectKeyIdentifier, false, subjectSki);

  ContentSigner signer = new JcaContentSignerBuilder(sigAlg)
                  .setProvider("BC").build(signerKey);

  JcaX509CertificateConverter converter = new JcaX509CertificateConverter().setProvider("BC");

  return converter.getCertificate(certBldr.build(signer));
}

添加后,证书就能正确识别签发者身份。

2. 生成包含证书链的PEM文件

单个终端实体证书本身不包含证书链,证书链需要将终端实体证书 + 根CA证书按顺序拼接。修改导出代码:

StringWriter sw = new StringWriter();
JcaPEMWriter jpw = new JcaPEMWriter(sw);
// 先写入终端实体证书
jpw.writeObject(certificate);
// 再写入签发的根CA证书
jpw.writeObject(signerCert);
jpw.flush();
jpw.close();
String pemWithChain = sw.toString();

这样导出的PEM文件就包含完整的证书链了。

额外注意事项

  • 确保calculateSerialNumber()生成全局唯一的证书序列号,避免重复
  • calculateDate()需生成有效的证书起始和过期时间(例如基于当前时间计算)
  • 代码执行前需注册Bouncy Castle Provider:Security.addProvider(new BouncyCastleProvider());

内容的提问来源于stack exchange,提问作者mister.durden

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 21:40:43