概述
在 PKI(Public Key Infrastructure,公开密钥基础建设)体系中,证书签名请求(也称为 CSR 或证书请求)是由客户端提交给 CA(Certificate Authority)用于申请数字证书的信息。其中 PKCS#10 规范是 CSR 中最常见的格式。
比如,张三和李四通过互联网进行信息传输,他们两者都希望接受到的信息就是对方发送的原始内容,没有被任何篡改,且不能被抵赖。要实现这个目的,其中一个方法就是使用 PKI。在 PKI 中,他们双方都需要生成密钥对,即公钥和私钥。公钥顾名思义就是公开的密钥,大家都可以得到,而发送的信息则使用他们各自的私钥进行加密,相应的也只有他们各自的公钥才可以解密信息。
那么在实际中,我们是如何分享公钥呢?我们可以通过一个名为 CA(Certification Authority)的第三方机构实现公钥分享,其中还会涉及到 CA 的从属认证机构 RA(Registration Authority,注册管理中心)。CA 将用户的个人身份和公开密钥链接在一起;RA 则确保公开密钥和个人身份链接正确,防止抵赖。归根结底,CA 的作用就是对我们的公钥进行签名,而 CA 作为数字证书的权威机构,CA 的公钥证书一般都已经内置在了各类客户端软件中,所以客户端可以通过验证签发给我们证书的 CA 机构的合法性来判断我们这本证书的合法性。我们将公钥提交到 CA 的这一过程就叫 CSR。
Java生成CSR
- 使用标准加密算法获取KeyPairGenerator的实例,此处使用RSA。
- 通过提供密钥大小和随机性来源来初始化实例。
- 生成CSR中使用的PrivateKey和PublicKey。
- 使用PublicKey初始化PKCS10。
- 使用标准算法获取签名实例,此处使用MD5WithRSA。
- 使用PrivateKey初始化签名对象。
- 通过传递通用名称,组织单位,组织,位置,州和国家/地区来创建X500Name对象。
- 使用X500Signer,Signature和X500Name对象对PKCS10对象进行编码和签名。
- 将PKCS10对象打印到PrintStream。然后就可以使用了。
示例代码
首先,在项目中加入 BouncyCastly 库,以下是 Maven 依赖配置项:
<dependency><groupId>org.bouncycastle</groupId><artifactId>bcpkix-jdk18on</artifactId><version>1.72</version>
</dependency>
然后,下面是示例代码:
public class CsrUtils {private static final Provider BC = new BouncyCastleProvider();/*** 生成 PKCS#10 证书请求*/public static String generateCsr(boolean isRsaNotEcc) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, OperatorCreationException, IOException {// 使用 RSA/ECC 算法,生成密钥对(公钥、私钥)KeyPairGenerator generator = KeyPairGenerator.getInstance(isRsaNotEcc ? "RSA" : "EC", BC);if (isRsaNotEcc) {// RSAgenerator.initialize(2048);} else {// ECCgenerator.initialize(new ECGenParameterSpec("sm2p256v1"));}KeyPair keyPair = generator.generateKeyPair();PrivateKey privateKey = keyPair.getPrivate();PublicKey publicKey = keyPair.getPublic();//打印私钥,注意:请务必保存您的私钥printOpensslPemFormatKeyFileContent(privateKey, isRsaNotEcc);// 按需添加证书主题项,X500Principal subject = new X500Principal("C=CN");// 使用私钥和 SHA256WithRSA/SM3withSM2 算法创建签名者对象ContentSigner signer = new JcaContentSignerBuilder(isRsaNotEcc ? "SHA256WithRSA" : "SM3withSM2").setProvider(BC).build(privateKey);// 创建 CSRPKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, publicKey);PKCS10CertificationRequest csr = builder.build(signer);// 打印 OpenSSL PEM 格式文件字符串printOpensslPemFormatCsrFileContent(csr);// 以 Base64 字符串形式返回 CSRreturn Base64.getEncoder().encodeToString(csr.getEncoded());}/*** 打印 OpenSSL PEM 格式文件字符串的 SSL证书密钥 KEY 文件内容* @param privateKey 私钥* @param isRsaNotEcc {@code true}:使用 RSA 加密算法;{@code false}:使用 ECC(SM2)加密算法*/private static void printOpensslPemFormatKeyFileContent(PrivateKey privateKey, boolean isRsaNotEcc) throws IOException {PemObject pem = new PemObject(isRsaNotEcc ? "PRIVATE KEY" : "EC PRIVATE KEY", privateKey.getEncoded());StringWriter str = new StringWriter();PemWriter pemWriter = new PemWriter(str);pemWriter.writeObject(pem);pemWriter.close();str.close();System.out.println(str.toString());}/*** 打印 OpenSSL PEM 格式文件字符串的 SSL 证书请求 CSR 文件内容* @param csr 证书请求对象*/private static void printOpensslPemFormatCsrFileContent(PKCS10CertificationRequest csr) throws IOException {PemObject pem = new PemObject("CERTIFICATE REQUEST", csr.getEncoded());StringWriter str = new StringWriter();PemWriter pemWriter = new PemWriter(str);pemWriter.writeObject(pem);pemWriter.close();str.close();System.out.println(str.toString());}
}
其他
除此之外,我们还可以使用Java密钥工具Java Keytool的-genkeypair命令来生成私钥和证书请求文件。
keytool -genkeypair -keyalg RSA -keysize 2048 -keystore keystore.jks -storepass password -validity 365 -alias myalias