Java 使用BouncyCastle生成,解读CSR

Java 使用BouncyCastle生成,解读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);
	}
	
}
posted @ 2022-06-21 10:20  穷苦书生  阅读(4056)  评论(1编辑  收藏  举报