1. 概述

签署证书签名请求(CSR)是密码学中的常见操作。本教程将介绍如何使用 Bouncy Castle 库完成 CSR 签署。

2. CSR 签署流程

CSR 签署本质是证书颁发机构(CA)验证请求信息后签发证书的过程。 CA 使用其私钥对证书进行签名,生成的证书可用于建立客户端与服务器间的安全连接。

使用 Bouncy Castle 签署 CSR 需要以下核心步骤:

  1. 生成可信 CA 证书和私钥
  2. 创建证书签名请求(CSR)
  3. 使用 CA 证书和私钥签署 CSR

3. 环境准备

首先在项目中添加 Bouncy Castle 依赖(以 Maven 为例):

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk18on</artifactId>
    <version>1.76</version>
</dependency>

然后创建安全提供者注册类:

static {
    Security.addProvider(new BouncyCastleProvider());
}

4. 使用 Bouncy Castle 签署 CSR

签署过程包含多个关键步骤,下面逐一解析。

4.1 生成 CA 证书和私钥

CA 作为可信实体负责签发证书。首先生成 CA 密钥对:

public static KeyPair generateRSAKeyPair() {
    KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
    keyPairGenerator.initialize(2048);
    return keyPairGenerator.generateKeyPair();
}

4.2 创建证书签名请求

基于密钥对生成 CSR:

public static PKCS10CertificationRequest generateCSR(KeyPair pair) {
    PKCS10CertificationRequestBuilder p10Builder = new JcaPKCS10CertificationRequestBuilder(
      new X500Principal("CN=Requested Test Certificate"), pair.getPublic());
    JcaContentSignerBuilder csBuilder = new JcaContentSignerBuilder("SHA256withRSA");
    ContentSigner signer = csBuilder.build(pair.getPrivate());
    return p10Builder.build(signer);
}

4.3 签署 CSR

使用 CA 证书和私钥签署 CSR 的核心代码:

public X509Certificate sign(PKCS10CertificationRequest inputCSR, PrivateKey caPrivate, KeyPair pair) {
    AlgorithmIdentifier sigAlgId = new DefaultSignatureAlgorithmIdentifierFinder().find("SHA1withRSA");
    AlgorithmIdentifier digAlgId = new DefaultDigestAlgorithmIdentifierFinder().find(sigAlgId);

    AsymmetricKeyParameter foo = PrivateKeyFactory.createKey(caPrivate.getEncoded());
    SubjectPublicKeyInfo keyInfo = SubjectPublicKeyInfo.getInstance(pair.getPublic().getEncoded());

    X509v3CertificateBuilder myCertificateGenerator = new X509v3CertificateBuilder(
      new X500Name("CN=issuer"), 
      new BigInteger("1"), 
      new Date(System.currentTimeMillis()), 
      new Date(System.currentTimeMillis() + 30L * 365 * 24 * 60 * 60 * 1000), 
      inputCSR.getSubject(), 
      keyInfo);

    ContentSigner sigGen = new BcRSAContentSignerBuilder(sigAlgId, digAlgId).build(foo);

    X509CertificateHolder holder = myCertificateGenerator.build(sigGen);
    Certificate eeX509CertificateStructure = holder.toASN1Structure();

    CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");

    InputStream is1 = new ByteArrayInputStream(eeX509CertificateStructure.getEncoded());
    X509Certificate theCert = (X509Certificate) cf.generateCertificate(is1);
    is1.close();
    return theCert;
}

关键步骤解析:

  1. ✅ 通过 DefaultSignatureAlgorithmIdentifierFinderDefaultDigestAlgorithmIdentifierFinder 确定签名和摘要算法
  2. ✅ 使用 PrivateKeyFactory 从编码字节创建 CA 私钥
  3. ✅ 通过 SubjectPublicKeyInfo 指定公钥信息
  4. ✅ 构建证书生成器,设置颁发者、序列号、有效期、主题和公钥
  5. ⚠️ 使用签名算法、摘要算法和 CA 私钥创建 ContentSigner,这是证书签名的核心
  6. ✅ 构建证书并转换为 X509Certificate 格式返回

5. 测试验证

编写测试用例验证签署流程:

@Test
public void givenCSR_whenSignWithBC_thenSuccess() {
    SignCSRBouncyCastle signCSRBouncyCastle = new SignCSRBouncyCastle();
    KeyPair pair = SignCSRBouncyCastle.generateRSAKeyPair();
    PKCS10CertificationRequest csr = SignCSRBouncyCastle.generateCSR(pair);
    KeyPair caPair = SignCSRBouncyCastle.generateRSAKeyPair();
    X509Certificate signedCert = signCSRBouncyCastle.signCSR(csr, caPair.getPrivate(), pair);

    assertThat(signedCert).isNotNull();
    assertThat(signedCert.getSubjectDN().getName()).isEqualTo("CN=Requested Test Certificate");
    assertDoesNotThrow(() -> signedCert.verify(caPair.getPublic()));
}

测试流程:

  1. 生成普通密钥对和 CSR
  2. 生成 CA 密钥对
  3. 使用 CA 私钥签署 CSR
  4. ✅ 验证证书非空且主题正确
  5. ✅ 使用 CA 公钥验证证书签名(无异常抛出即成功)

6. 总结

本教程完整演示了使用 Bouncy Castle 签署 CSR 的全过程:

  • 生成 RSA 密钥对
  • 创建证书签名请求
  • 构建 CA 证书并完成签署
  • 通过测试验证签名有效性

实际应用中需注意:

  • ⚠️ 生产环境应使用更安全的密钥存储方案
  • ✅ 证书有效期应根据实际需求设置
  • ❌ 避免在代码中硬编码证书信息

原始标题:Sign CSR Using Bouncy Castle | Baeldung