Java 使用BouncyCastle生成,解读CSR
Java 使用BouncyCastle生成,解读CSR
以下内容均来自网络整理,实测可用,记录如下:
参考资料
- Github-通过 Java 代码生成 RSA/SM2(ECC)证书请求
- (纯翻译转载)-java 生成csr_Java以编程方式生成CSR
- 使用Java或BouncyCastle解码/读取CSR
- Java RSA加密/解密
- CSR在线验证,在线生成工具
第三方加密算法库-依赖包:
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
<version>1.60</version>
</dependency>
关键类
javax.security.auth.x500.X500Principal
org.bouncycastle.asn1.x500.style.BCStyle
org.bouncycastle.asn1.x500.style.RFC4519Style
org.bouncycastle.asn1.ASN1ObjectIdentifier
主要功能
- 使用BC生成密钥对;
- 打印字符串公钥,秘钥;
- 生成CSR, 解析CSR;
- 从字符串加载公钥,密钥;
- 使用公钥加密;
- 使用私钥解密;
工具类代码
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import org.bouncycastle.asn1.ASN1ObjectIdentifier;
import org.bouncycastle.asn1.x500.RDN;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.openssl.PEMParser;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.bouncycastle.pkcs.PKCS10CertificationRequestBuilder;
import org.bouncycastle.pkcs.jcajce.JcaPKCS10CertificationRequestBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import javax.crypto.Cipher;
import javax.security.auth.x500.X500Principal;
import java.io.*;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
/**
* @desc: Java 代码生成 PKCS#10 规范的证书签名请求 CSR
* @author: Linwei
* @created: 2022-06-20 13:26:06
*/
public class CsrUtil {
private static final Provider BC = new BouncyCastleProvider();
/**
* @author: Linwei
* @created: 2022/6/20 13:30
* @desc: 生成PKCS#10格式的CSR
* @param {@code true}:使用 RSA 加密算法;{@code false}:使用 ECC(SM2)加密算法
* @return P10证书签名请求 Base64 字符串
*/
public static String generateCsr(boolean isRsaNotEcc, String sn) throws NoSuchAlgorithmException, InvalidAlgorithmParameterException, OperatorCreationException, IOException {
// 使用 RSA/ECC 算法,生成密钥对(公钥、私钥)
KeyPairGenerator generator = KeyPairGenerator.getInstance(isRsaNotEcc ? "RSA" : "EC", BC);
if (isRsaNotEcc) {
// RSA
generator.initialize(2048);
} else {
// ECC
generator.initialize(new ECGenParameterSpec("sm2p256v1"));
}
KeyPair keyPair = generator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
// 打印私钥,注意:请务必保存您的私钥
System.out.println("----------打印私钥");
String privateKeyStr = getOpensslPemFormatKeyFileContent(privateKey, isRsaNotEcc);
System.out.println(privateKeyStr);
System.out.println("----------打印公钥");
String publicKeyStr = getOpensslPemFormatKeyFileContent(publicKey, isRsaNotEcc);
System.out.println(publicKeyStr);
// 按需添加证书主题项,
// 有些 CSR 不需要我们在主题项中添加各字段,
// 如 `C=CN, CN=吴仙杰, E=wuxianjiezh@gmail.com, OU=3303..., L=杭州, S=浙江`,
// 而是通过额外参数提交,故这里我只简单地指定了国家码
/**
* CN common name (域名)
* OU Organizational unit (部门)
* O Organization Name (组织)
* L Location
* ST State
* C Country
* SN device serial number name
*/
String subjectParam = "CN=*.linwei.com,OU=IT,O=MyCompany,L=Guangzhou,ST=Guangdong," +
"C=CN," +
"SERIALNUMBER=" + sn;
X500Principal subject = new X500Principal(subjectParam);
// 使用私钥和 SHA256WithRSA/SM3withSM2 算法创建签名者对象
ContentSigner signer = new JcaContentSignerBuilder(isRsaNotEcc ? "SHA256WithRSA" : "SM3withSM2")
.setProvider(BC)
.build(privateKey);
// 创建 CSR
PKCS10CertificationRequestBuilder builder = new JcaPKCS10CertificationRequestBuilder(subject, publicKey);
PKCS10CertificationRequest csr = builder.build(signer);
// 打印 OpenSSL PEM 格式文件字符串
System.out.println("----------打印PEM 格式CSR");
String csrStr = getOpensslPemFormatCsrFileContent(csr);
System.out.println(csrStr);
// 以 Base64 字符串形式返回 CSR
String baseStr = Base64.getEncoder().encodeToString(csr.getEncoded());
System.out.println("----------打印Base64格式CSR");
System.out.println(baseStr);
return baseStr;
}
/**
* 打印 OpenSSL PEM 格式文件字符串的 SSL证书密钥 KEY 文件内容
*
* @param privateKey 私钥
* @param isRsaNotEcc {@code true}:使用 RSA 加密算法;{@code false}:使用 ECC(SM2)加密算法
* @return 返回私钥字符串
*/
private static String getOpensslPemFormatKeyFileContent(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();
return str.toString();
}
private static String getOpensslPemFormatKeyFileContent(PublicKey publicKeyKey, boolean isRsaNotEcc) throws IOException {
PemObject pem = new PemObject(isRsaNotEcc ? "PUBLIC KEY" : "EC PUBLIC KEY", publicKeyKey.getEncoded());
StringWriter str = new StringWriter();
PemWriter pemWriter = new PemWriter(str);
pemWriter.writeObject(pem);
pemWriter.close();
str.close();
return str.toString();
}
/**
* 打印 OpenSSL PEM 格式文件字符串的 SSL 证书请求 CSR 文件内容
*
* @param csr 证书请求对象
* @return 返回CSR
*/
private static String getOpensslPemFormatCsrFileContent(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();
return str.toString();
}
/**
* @author: Linwei
* @created: 2022/6/21 10:08
* @desc: CSR字符串转证书签名请求对象
* @param csrStr PKCS#10 PEM CSR完整字符串
*/
public static PKCS10CertificationRequest convertPemToPKCS10CertificationRequest(String csrStr) throws Exception{
Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
PKCS10CertificationRequest csr = null;
ByteArrayInputStream pemStream = null;
pemStream = new ByteArrayInputStream(csrStr.getBytes("UTF-8"));
Reader pemReader = new BufferedReader(new InputStreamReader(pemStream));
PEMParser pemParser = new PEMParser(pemReader);
Object parsedObj = pemParser.readObject();
if (parsedObj instanceof PKCS10CertificationRequest) {
csr = (PKCS10CertificationRequest) parsedObj;
}
return csr;
}
/**
* @author: Linwei
* @created: 2022/6/21 10:10
* @desc: 读取CSR中的主题信息
* @param asn1ObjectIdentifier
* @param x500Name
*/
public static String getX500Field(String asn1ObjectIdentifier, X500Name x500Name) {
RDN[] rdnArray = x500Name.getRDNs(new ASN1ObjectIdentifier(asn1ObjectIdentifier));
String retVal = null;
for (RDN item : rdnArray) {
retVal = item.getFirst().getValue().toString();
}
return retVal;
}
/**
* @author: Linwei
* @created: 2022/6/21 10:10
* @desc: 读取CSR中的主题信息
* @param asn1ObjectIdentifier
* @param x500Name
*/
public static String getX500Field(ASN1ObjectIdentifier asn1ObjectIdentifier, X500Name x500Name) {
RDN[] rdnArray = x500Name.getRDNs(asn1ObjectIdentifier);
String retVal = null;
for (RDN item : rdnArray) {
retVal = item.getFirst().getValue().toString();
}
return retVal;
}
/**
* 从字符串中加载公钥
*
*/
public static PublicKey loadPublicKey(String publicKeyStr) throws Exception {
try {
// byte[] buffer = StrUtil.bytes(publicKeyStr, CharsetUtil.CHARSET_UTF_8);
// 注意:先用BASE64解密字符串, 否则会报错误:invalid key format ssl invalid key format
byte[] buffer = Base64.getDecoder().decode(publicKeyStr);
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(buffer);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PublicKey publicKey = keyFactory.generatePublic(keySpec);
return publicKey ;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}
/**
* 从字符串中加载私钥
*/
public static PrivateKey loadPrivateKey(String privateKeyStr) throws Exception {
try {
byte[] buffer = Base64.getDecoder().decode(privateKeyStr);
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
return privateKey;
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeySpecException e) {
throw new RuntimeException(e);
}
}
/**
* 加密
*/
public static byte[] encrypt(PublicKey publicKey, String message) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
return cipher.doFinal(message.getBytes("UTF-8"));
}
/**
* 解密
*/
public static byte[] decrypt(PrivateKey privateKey, byte [] encrypted) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encrypted);
}
}
测试代码
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.asn1.x500.style.BCStyle;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pkcs.PKCS10CertificationRequest;
import org.junit.Assert;
import org.junit.Test;
import java.security.*;
import java.util.Base64;
/**
* @desc: CSR 生成/读取测试类
* @author: Linwei
* @created: 2022-06-21 11:02:57
*/
public class CsrUtilTest {
@Test
public void testGenerateCsr() throws Exception{
for(int i=1;i<=10;i++){
String sn = StrUtil.fillBefore(Integer.toString(i),'0',8);
System.out.println(sn);
CsrUtil.generateCsr(true,sn);
System.out.println("-----------------长长的分割线-------------------------");
}
System.out.println(System.currentTimeMillis());
}
@Test
public void readCsrObject() throws Exception{
String csrStr = "-----BEGIN CERTIFICATE REQUEST-----\n" +
"MIIC0jCCAboCAQAwgYwxDzANBgNVBAUTBjAwMDAwMDEdMBsGCgmSJomT8ixkARkW\n" +
"DSouZGVzYXlzdi5jb20xCzAJBgNVBAYTAkNOMRIwEAYDVQQIEwlHdWFuZ2Rvbmcx\n" +
"CzAJBgNVBAcTAkhaMQ4wDAYDVQQKEwVEZXNheTENMAsGA1UECxMESmF2YTENMAsG\n" +
"A1UEAxMEVEVTVDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMk4zpqc\n" +
"2gAKHHmlU182nz5WnM8kps1vzKCWVRO465iMOFePI2a0heW7qQASbriS9qkj9dxi\n" +
"CBcXlEkq8ba4sDM/VCXZlarVXqjnGy+2KNxgGNuJhM9M9aJD5cS6i6WVbxbhEGMk\n" +
"1ECW9GffxqUxsBW1t8M0DYTwqVSszvOm9WP+IzT+9kGXHxsImrRm/igwjSplpU2f\n" +
"TzBXm9oD6nGQh996iHPoij8dGfvEtGenSsfs/SrS9FcG6hemdj5PXvlvR897mUgr\n" +
"6JEw8E2WMVvvzCcckCfMBmwIgGL+zCOJ4T+oy+vPBJhb7zMcfvKiZ8S/7J2tQuVT\n" +
"5NUQAQCz5IDDC/UCAwEAAaAAMA0GCSqGSIb3DQEBCwUAA4IBAQAJLoUwVqU69V/c\n" +
"tIHC0R47qrEWmJgU8svs9uUXxR3F++70/yAiajniBI5bPQ+uZwwIPtR34rquDMkh\n" +
"YWrkJIj5ymyf/w1SIOT/9fCJNrrfgHaTUFFcyVm6+J3LfEFrQsBYgQUrBwGCM7eB\n" +
"cn+Qsp3LFwspta535K3sBhg+ZKuSKZM94bcFv2MAx8EJ3Z1NmOCPT9nmr5RD38ib\n" +
"FpMbNtOCTObcoA0a30Pd3nDkBFq1R6AXDuJu3k0ZwhJuW/HQKKfSNpUsE7Tzv5EP\n" +
"esvx1eTIcM1sKnhNCxBKiLlNX//VEjjXrvybCprLIr5LRli9KmNHA5U6bUdNAEdf\n" +
"TGRAE7E1\n" +
"-----END CERTIFICATE REQUEST-----";
PKCS10CertificationRequest csr = CsrUtil.convertPemToPKCS10CertificationRequest(csrStr);
X500Name x500Name = csr.getSubject();
// 这里就会打印所有的主题
System.out.println("x500Name is: " + x500Name );
System.out.println("STATE: " + CsrUtil.getX500Field(BCStyle.STREET, x500Name));
System.out.println("LOCALE: " + CsrUtil.getX500Field(BCStyle.L, x500Name));
System.out.println("SERIALNUMBER: " + CsrUtil.getX500Field(BCStyle.SERIALNUMBER, x500Name));
}
// 使用BC生成密钥对
@Test
public void testGenerateKeyPairs() throws Exception{
Provider BC = new BouncyCastleProvider();
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA", BC);
generator.initialize(2048);
KeyPair keyPair = generator.generateKeyPair();
PrivateKey privateKey = keyPair.getPrivate();
PublicKey publicKey = keyPair.getPublic();
String pubKey = Base64.getEncoder().encodeToString(publicKey.getEncoded());
String priKey = Base64.getEncoder().encodeToString(privateKey.getEncoded());
System.out.println(pubKey);
System.out.println("------------------------");
System.out.println(priKey);
}
/**--------------------------------------------*/
public static String pubKey = "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx6qNGGJXq9bhszqBLYEW" +
"MXX4PuVhKkvD/Q4yFM0yQwUCQRF7onfk9KMl54HKf6r4IEygL3Wz/nYN+lG9JYPH" +
"hz8w23oYNMn5MyWTWztfHLjRr5HfnIgi5skHb3W6Q4SeCjq7IkwBX5b4xIpmcdYP" +
"pg/Wm4299qc8MxY+fbBRq4tfTsqaYT5eovDNmgLtCeMjVUrmv4qGIs9ewrT9WexD" +
"qmsjFoDWTVzjVsL73GtVer/2ONdUAb9/YlkCazX2TBaNk4rG4YKxkMFTw8KcHYCG" +
"JEmbnHpFw+mJijrOI+p8dilGRMxsCWYYRXJQQUjt0wVRrZYrKU094aYnGg76Wj8c" +
"vQIDAQAB";
public static String priKey = "MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDHqo0YYler1uGz" +
"OoEtgRYxdfg+5WEqS8P9DjIUzTJDBQJBEXuid+T0oyXngcp/qvggTKAvdbP+dg36" +
"Ub0lg8eHPzDbehg0yfkzJZNbO18cuNGvkd+ciCLmyQdvdbpDhJ4KOrsiTAFflvjE" +
"imZx1g+mD9abjb32pzwzFj59sFGri19OypphPl6i8M2aAu0J4yNVSua/ioYiz17C" +
"tP1Z7EOqayMWgNZNXONWwvvca1V6v/Y411QBv39iWQJrNfZMFo2TisbhgrGQwVPD" +
"wpwdgIYkSZucekXD6YmKOs4j6nx2KUZEzGwJZhhFclBBSO3TBVGtlispTT3hpica" +
"DvpaPxy9AgMBAAECggEADeUFKJs/X/VLg5N2Bdqj1rv6PeDsisBMWpcwTljxPyS2" +
"wXHGrsm1JH5uAYGmEThZSAt3t5XIpClG/mfNx9yMeBGiWW5SXOoT7hvr+JSrIRk4" +
"zlpSZgGQnDWFJPExDch0JGDDQQxqVA1HhoszEdzu/dlq+oGPT1OCpvN3VHvT/cyi" +
"VNio17BK8Bj7KrnnwnhGJ5HfJftP8kZU3PVCPSbWfUT6pTdFD1POY0lVBIMZKKDO" +
"+YGZ5EHs9LDxvaxjH3mmvv4A/R39q8OtPvwg+26JryJ3s/jtfUu7PH0wdMGsuqeL" +
"GPrVlwZW7KCcImNmpQpWWuR8v/06th4e2ptH6DqwwQKBgQDkEBdI8JkF08Ey0VQR" +
"+CfzJkT23muvxvOrqGs5On5VKBLaNtqUBq7s0upMJ/NkXJN4SEf+PwKXmhCUAYzG" +
"LdDELJlGPSoQyJDmt7WAuwZMPGjc0t4NzVQr/vXu+dGauOVS9C+Vm8ny9mw1aqdC" +
"mM42fTWK3D0jPz2fglEv49YidQKBgQDgH/WSTyox+zxLWsp7n6GhuqQdUTWOYheM" +
"JkGUFombM/oP8ddNizly16+nx8V/X0BikSPuj3GCCUog4NdIrKe0yMIgx7qz6wcX" +
"6eIS/VEcvEX/gH2jWaTwtcsWNuNSu8praIdc91MjrsYlco4m19EN8pW1t32Y5L9K" +
"93UzroI4KQKBgQDGU7fsFk78YkmAh8k0VzlmmcEfbgz3r2v3u16DRfrW0yKR4WHz" +
"mFxGVqSp1ZQzks4rq6/vyZvXeoMwMqjZLr592srv6gDK16ArA1czu3Cem2oVnsq7" +
"9fNczzvPtjAw/Nlwail3USMtxl4TlcwefgPWHsyRFTWVkUlljU5M9zUggQKBgAkK" +
"wQmKEBW8IQ6ZNxoNsqOnt/9Gy3ZXFsQctwCWeC6+xhPmmd0TBNpRQVDvilQ9L4fK" +
"ezygpN+uKENzTrwr2wg8ITjZVfr0PHEGioxhk+go4FoSgP8aGsFrVCotRLaNxPjY" +
"Wr2xbLU/09am7H08r750Tv3pzTNh5yXLHftbyv0hAoGAbFL3m9jQb11CSfL4Jp6B" +
"md9OewV6w05rVwI5+5al1RjcmntEeYAebqOSiNWr4cyJ8fwUMmENbm+oTSpB3JbJ" +
"79m2BZtHCUCnKdmOkKKOiNCUymNtfzcgUIeMb7rdwjKVjuCxwFK0Bkl9QgcDfQBU" +
"S03BUmuIHr2ECuINQxek9s8=";
@Test
public void testEncryptAndDecrypt() throws Exception {
String message = "你好,This is a secret message";
// 加载公钥
PublicKey publicKey = CsrUtil.loadPublicKey(pubKey);
byte [] encrypt = CsrUtil.encrypt(publicKey, message);
System.out.println(StrUtil.str(encrypt, CharsetUtil.CHARSET_UTF_8));
// 加载密钥
PrivateKey privateKey = CsrUtil.loadPrivateKey(priKey);
byte[] decrypt = CsrUtil.decrypt(privateKey, encrypt);
String decryptStr = StrUtil.str(decrypt, CharsetUtil.CHARSET_UTF_8);
System.out.println(decryptStr);
Assert.assertEquals(message, decryptStr);
}
}
边系鞋带边思考人生.