简介
日常过程中,有时候需要用到自签名的证书,故特意把生成则证书写成一个工具类,方便随时需要的时候生成。
添加依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
执行工具类
创建CAUtil
package com.topinfo.basic.ca;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.X509CertificateHolder;
import org.bouncycastle.cert.X509v3CertificateBuilder;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.openssl.jcajce.JcaPEMWriter;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.OperatorCreationException;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.util.io.pem.PemObject;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Base64;
import java.util.Date;
/**
* @ClassName: CAUtil
* @Description: 证书 工具类
* @Author: 杨攀
* @Date: 2024/4/28 15:52
* @Copyright: 2024 www.tuxun.net Inc. All rights reserved.
*/
public class CAUtil {
public static void main(String[] args) throws Exception {
// 生成 密钥对,为CA生成一个RSA密钥对
KeyPair keyPair = generatorKeyPair();
String dirName = "CN=tuxun.com, O=topinfo, L=zhejiang, ST=hangzhou, C=CN";
// 创建自签名的CA证书, 构建X509证书
X509Certificate certificate = generatorCA(keyPair, dirName);
// 私钥
PrivateKey caPrivateKey = keyPair.getPrivate();
// 别名
String alias = "topinfo-ca";
// 密码
char[] password = "1234qwer".toCharArray();
// jks 存储路径
String jksPath = "D:/upload/topinfo_certificate.jks";
// CA跟证书 保存为JKS格式
saveAsJKS(certificate, caPrivateKey, alias, password, jksPath);
// p12 存储路径
String p12Path = "D:/upload/topinfo_certificate.p12";
// CA跟证书 保存为P12格式
saveAsPKCS12(certificate, caPrivateKey, alias, password, p12Path);
// 证书转换为PEM格式并写入文件
String pemPath = "D:/upload/topinfo_certificate.pem";
// PrivateKey(私钥)转换为PEM格式
String pemKeyPath = "D:/upload/topinfo_certificate_key.pem";
// 保持PEM格式(Nginx支持该格式)
saveAsPEM(certificate, caPrivateKey, pemPath, pemKeyPath);
}
/**
* 生成密钥对, 在生成CA证书之前,我们需要首先生成一个密钥对,其中包含一个私钥和一个公钥。私钥用于签署证书,而公钥将被包含在证书中,供其他方验证。
*
* @param
* @return java.security.KeyPair
* @author 杨攀
* @date 2024/4/28 15:58
*/
private static KeyPair generatorKeyPair() throws NoSuchAlgorithmException {
KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
// 例如,使用2048位长度的密钥
keyPairGen.initialize(2048, new SecureRandom());
KeyPair caKeyPair = keyPairGen.generateKeyPair();
// 私钥
PrivateKey caPrivateKey = caKeyPair.getPrivate();
String privateKey = Base64.getEncoder().encodeToString(caPrivateKey.getEncoded());
System.out.println("私钥:" + privateKey);
// 公钥
PublicKey caPublicKey = caKeyPair.getPublic();
String publicKey = Base64.getEncoder().encodeToString(caPublicKey.getEncoded());
System.out.println("公钥:" + publicKey);
return caKeyPair;
}
/**
* 生成CA证书, 生成密钥对后,我们可以使用该密钥对生成CA证书
*
* @param keyPair 秘钥对
* @param dirName 证书属性
* @return void
* @author 杨攀
* @date 2024/4/28 15:58
*/
private static X509Certificate generatorCA(KeyPair keyPair, String dirName) throws IOException, OperatorCreationException, CertificateException {
// 设置CA证书属性
X500Name dnName = new X500Name(dirName);
long now = System.currentTimeMillis();
Date startDate = new Date(now);
// 有效期二十年
Date endDate = new Date(now + 20 * 365 * 24 * 60 * 60 * 1000);
// 公钥
PublicKey caPublicKey = keyPair.getPublic();
// 私钥
PrivateKey caPrivateKey = keyPair.getPrivate();
// 构建证书
X509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
// 发行人与主体相同,因为是自签名
dnName,
// 序列号
BigInteger.probablePrime(64, new SecureRandom()),
startDate,
endDate,
dnName,
caPublicKey);
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSAEncryption").build(caPrivateKey);
// 使用CA私钥签署证书
X509CertificateHolder certHolder = certBuilder.build(signer);
X509Certificate certificate = new JcaX509CertificateConverter().getCertificate(certHolder);
return certificate;
}
/**
* 保存为JKS格式
*
* @param certificate 根证书
* @param privateKey PrivateKey实例
* @param alias 别名
* @param password KeyStore的密码, 如:"1234qwer".toCharArray();
* @param filePath JKS文件保存路径, 如: D:/upload/server.jks
* @return void
* @author 杨攀
* @date 2024/4/30 9:45
*/
public static void saveAsJKS(X509Certificate certificate, PrivateKey privateKey, String alias, char[] password, String filePath) throws Exception {
// 创建一个新的KeyStore
KeyStore keyStore = KeyStore.getInstance("JKS");
keyStore.load(null, password); // 初始化KeyStore,没有密钥库则传null
// 将私钥和证书添加到KeyStore
keyStore.setKeyEntry(alias, privateKey, password, new Certificate[]{certificate});
// 保存KeyStore到文件
try (FileOutputStream fos = new FileOutputStream(filePath)) {
keyStore.store(fos, password);
}
System.out.println("JKS:" + filePath);
}
/**
* 保存为PKCS12格式
*
* @param certificate 根证书
* @param privateKey PrivateKey实例
* @param alias 别名
* @param password KeyStore的密码, 如:"1234qwer".toCharArray();
* @param filePath JKS文件保存路径, 如: D:/upload/server.p12
* @return void
* @author 杨攀
* @date 2024/4/30 9:53
*/
public static void saveAsPKCS12(X509Certificate certificate, PrivateKey privateKey, String alias, char[] password, String filePath) throws Exception {
KeyStore.ProtectionParameter protection = new KeyStore.PasswordProtection(password);
// 使用Builder创建PKCS12类型的KeyStore
KeyStore.ProtectionParameter protParam = new KeyStore.PasswordProtection(password);
KeyStore.Builder builder = KeyStore.Builder.newInstance("PKCS12", null, protParam);
builder.getKeyStore().setEntry(alias, new KeyStore.PrivateKeyEntry(privateKey, new Certificate[]{certificate}), protParam);
// 保存KeyStore到文件
try (FileOutputStream fos = new FileOutputStream(filePath)) {
builder.getKeyStore().store(fos, password);
}
System.out.println("PKCS12:" + filePath);
}
/**
* 保存为PEM格式
*
* @param certificate 根证书
* @param caPrivateKey PrivateKey实例
* @param pemPath PEM格式文件保存路径,如:D:/upload/topinfo_certificate.pem
* @param pemKeyPath PEM格式Key文件保存路径,如: D:/upload/topinfo_certificate_key.pem
* @return void
* @author 杨攀
* @date 2024/4/30 12:22
*/
private static void saveAsPEM(X509Certificate certificate, PrivateKey caPrivateKey, String pemPath, String pemKeyPath) {
// 对于X509Certificate(证书)转换为PEM格式并写入文件:
try (JcaPEMWriter pemWriter = new JcaPEMWriter(new FileWriter(pemPath))) {
pemWriter.writeObject(new PemObject("CERTIFICATE", certificate.getEncoded()));
} catch (IOException | CertificateEncodingException e) {
System.err.println("保存为PEM格式错误: ");
e.printStackTrace();
}
// 对于PrivateKey(私钥)转换为PEM格式并写入文件:
try (JcaPEMWriter pemWriter = new JcaPEMWriter(new FileWriter(pemKeyPath))) {
pemWriter.writeObject(new PemObject("PRIVATE KEY", caPrivateKey.getEncoded()));
} catch (IOException e) {
System.err.println("EM格式Key文件错误: ");
e.printStackTrace();
}
}
}
具体说明代码注释都有说明,一个mian方法执行即可。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。