使用 Bouncy Castle 签署 CSR

在本教程中,我们学习了如何使用 Bouncy Castle 库签署 CSR。我们生成了密钥对,创建了 CSR,生成了 CA 密钥对,并使用 CA 证书签署了 CSR。我们还编写了一个测试来验证签名过程。

签署证书签名请求 (CSR) 是密码学中的常见操作。在本教程中,我们将学习如何使用 Bouncy Castle 库签署 CSR。

签署 CSR
签署 CSR 是证书颁发机构 (CA) 验证 CSR 中的信息并颁发证书的过程。CA使用其私钥签署证书。签署的证书随后可以在客户端和服务器之间建立安全连接。

要使用 Bouncy Castle 签署 CSR,我们需要执行几个基本步骤:

  • 生成受信任实体CA证书和私钥。
  • 生成证书签名请求 (CSR)。
  • 使用 CA 证书和私钥签署 CSR。

设置
我们需要将 Bouncy Castle 库添加到我们的项目中,以便我们可以使用它来签署 CSR。让我们将其Maven 依赖项添加到我们的pom.xml文件中:

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

接下来,我们需要创建一个SecurityProvider类来注册 Bouncy Castle 提供程序:

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

使用 Bouncy Castle 签署 CSR
使用 Bouncy Castle 签署 CSR 涉及几个步骤。让我们详细了解每个步骤。

1. 生成受信任实体 CA 证书和私钥
CA 是向客户端颁发证书的受信任实体。我们必须生成 CA 证书和私钥来签署 CSR。让我们从生成密钥对开始:

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

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);
}

3. 签署证书签名请求
接下来,我们必须创建一个证书生成器,使用 CA 证书和私钥对 CSR 进行签名。让我们来看看对 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;
}

该方法首先识别用于签署证书的签名和摘要算法。我们使用DefaultSignatureAlgorithmIdentifierFinder和DefaultDigestAlgorithmIdentifierFinder类来查找这些算法。

AsymmetricKeyParameter用于从编码字节创建 CA 的私钥。我们使用PrivateKeyFactory类从编码字节创建私钥。SubjectPublicKeyInfo用于指定公钥信息。

接下来,我们创建证书生成器。它设置证书的颁发者、序列号、有效期、主题和公钥。

然后,我们使用签名和摘要算法以及 CA 的私钥创建ContentSigner,用于签署证书。

最后,该方法构建证书,将其转换为 X509Certificate ,并返回它。

测试
我们来编写一个测试来验证签名过程:

@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()));
}

在测试中,我们生成密钥对并创建 CSR。然后,我们生成 CA 密钥对并使用 CA 私钥对 CSR 进行签名。最后,我们使用 CA 公钥验证签名的证书。