【SSL】使用Java生成SSL自签名证书
- 1.自签名证书介绍
-
- [1.1 什么是自签名证书](#1.1 什么是自签名证书)
- [1.2 主要作用](#1.2 主要作用)
- 2.代码实现
-
- [2.1 添加pom.xml](#2.1 添加pom.xml)
- [2.1 初始化Bouncy Castle 扩展](#2.1 初始化Bouncy Castle 扩展)
- [2.2 生成自签名证书](#2.2 生成自签名证书)
-
- [2.2.1 证书生成核心代码](#2.2.1 证书生成核心代码)
- [2.2.2 转化为通用的证书、公钥、私钥格式](#2.2.2 转化为通用的证书、公钥、私钥格式)
- 3.完整实现
1.自签名证书介绍
1.1 什么是自签名证书
| 特性维度 | 自签名证书 | CA签发证书 |
|---|---|---|
| 颁发者 | 使用者自己 | 受信任的CA机构颁发 |
| 信任机制 | 需要手动在各客户端信任,不被浏览器/操作系统默认信任 | 被操作系统和浏览器预置根证书全局信任 |
| 成本 | 免费 | 按年付费 |
| 签发速度 | 随时生成 | 不同版本签发效率不同 |
| 安全性 | 存在显著风险 | 安全性高具有OSSP、CRL |
| 适用场景 | 内部测试、开发环境、内网等 | 公网 |
1.2 主要作用
-
本地开发与测试环境
这是自签名证书最常用也最合适的场景。在开发网站或应用程序时,如果需要测试HTTPS功能(例如,测试单点登录SSO、支付回调等),配置受信任的CA证书流程繁琐。使用自签名证书可以快速搭建一个加密的测试环境,确保功能开发顺利进行
-
封闭的内部网络应用
对于一些仅限于内部员工访问的系统,如公司内网的Wiki、监控平台、设备管理后台等,如果所有访问客户端(如员工电脑)可以统一安装并信任该自签名证书,那么它也能提供安全的加密通信,同时节省成本
-
物联网设备初始配置与内部通信
一些智能物联网设备在初次启动配置时,可能会使用自签名证书提供一个临时的加密通道在微服务架构或设备集群内部,服务间的通信有时也会使用自签名证书进行身份验证和加密。
2.代码实现
2.1 添加pom.xml
xml
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
<version>1.70</version> <!-- 请使用最新稳定版本 -->
</dependency>
2.1 初始化Bouncy Castle 扩展
java
static {
Security.addProvider(new BouncyCastleProvider());
}
2.2 生成自签名证书
java
public static Map<String, String> gen(String commonName, String o, String i, String st) throws Exception {
Map<String, String> certMap = Maps.newHashMap();
// 1. 生成RSA密钥对
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", CertToolConstants.ECC_BC_STR);
kpg.initialize(2048);
KeyPair keyPair = kpg.generateKeyPair();
// 2. 生成自签名证书
Certificate cert = generateSelfSignedCertificate(keyPair, commonName, o, i, st);
// 3. 转换为PEM格式并返回
certMap.put("证书文件", encodeCertificateToPEMString(cert));
certMap.put("私钥", encodePrivateKeyToPEMString(keyPair.getPrivate()));
certMap.put("公钥", encodeKeyToPEMString(keyPair.getPublic()));
return certMap;
}
2.2.1 证书生成核心代码
java
public static Certificate generateSelfSignedCertificate(KeyPair keyPair, String commonName, String O, String l, String st) throws Exception {
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X500Principal dnName = new X500Principal("CN=" + commonName + ", OU=IT Dept, O=" + O + ", L=" + l + ", ST=" + st + ", C=CN");
X500Principal is = new X500Principal("CN=xzq717.com, OU=IT Dept, O=Xcc Trust OV, L=Beijing, ST=Beijing, C=CN");
//自签名证书序列号
certGen.setSerialNumber(BigInteger.probablePrime(128, new SecureRandom()));
certGen.setIssuerDN(dnName); // 颁发者
certGen.setSubjectDN(is); // 证书主体
certGen.setPublicKey(keyPair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
//设置证书开始日期
certGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24));
//设置证书截止日期
certGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 365)));
certGen.addExtension(org.bouncycastle.asn1.x509.Extension.basicConstraints, true,
new org.bouncycastle.asn1.x509.BasicConstraints(true));
return certGen.generate(keyPair.getPrivate(), CertToolConstants.ECC_BC_STR);
}
2.2.2 转化为通用的证书、公钥、私钥格式
java
public static String toPemString(byte[] contentBytes, String type) throws IOException {
StringWriter stringWriter = new StringWriter();
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
PemObject pemObject = new PemObject(type, contentBytes);
pemWriter.writeObject(pemObject);
}
return stringWriter.toString();
}
public static String encodeCertificateToPEMString(Certificate certificate) throws Exception {
return toPemString(certificate.getEncoded(), "CERTIFICATE");
}
public static String encodeKeyToPEMString(PublicKey publicKey) throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey.getEncoded());
return toPemString(keySpec.getEncoded(), "PUBLIC KEY");
}
public static String encodePrivateKeyToPEMString(PrivateKey privateKey) throws Exception {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
return toPemString(keySpec.getEncoded(), "PRIVATE KEY");
}
3.完整实现
java
package com.xzq717.utils;
import com.google.common.collect.Maps;
import com.xcc.tools.constants.CertToolConstants;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.io.pem.PemObject;
import org.bouncycastle.util.io.pem.PemWriter;
import org.bouncycastle.x509.X509V3CertificateGenerator;
import javax.security.auth.x500.X500Principal;
import java.io.FileWriter;
import java.io.IOException;
import java.io.StringWriter;
import java.math.BigInteger;
import java.security.*;
import java.security.cert.Certificate;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
public class SelfSignedCertGenerator {
static {
Security.addProvider(new BouncyCastleProvider());
}
public static Certificate generateSelfSignedCertificate(KeyPair keyPair, String commonName, String O, String l, String st) throws Exception {
X509V3CertificateGenerator certGen = new X509V3CertificateGenerator();
X500Principal dnName = new X500Principal("CN=" + commonName + ", OU=IT Dept, O=" + O + ", L=" + l + ", ST=" + st + ", C=CN");
X500Principal is = new X500Principal("CN=xzq717.com, OU=IT Dept, O=Xcc Trust OV, L=Beijing, ST=Beijing, C=CN");
certGen.setSerialNumber(BigInteger.probablePrime(128, new SecureRandom()));
certGen.setIssuerDN(dnName);
certGen.setSubjectDN(is);
certGen.setPublicKey(keyPair.getPublic());
certGen.setSignatureAlgorithm("SHA256WithRSAEncryption");
certGen.setNotBefore(new Date(System.currentTimeMillis() - 1000L * 60 * 60 * 24));
certGen.setNotAfter(new Date(System.currentTimeMillis() + (1000L * 60 * 60 * 24 * 365)));
certGen.addExtension(org.bouncycastle.asn1.x509.Extension.basicConstraints, true, new org.bouncycastle.asn1.x509.BasicConstraints(true));
return certGen.generate(keyPair.getPrivate(), CertToolConstants.ECC_BC_STR);
}
public static Map<String, String> gen(String commonName, String o, String i, String st) throws Exception {
Map<String, String> certMap = Maps.newHashMap();
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA", CertToolConstants.ECC_BC_STR);
kpg.initialize(2048);
KeyPair keyPair = kpg.generateKeyPair();
Certificate cert = generateSelfSignedCertificate(keyPair, commonName, o, i, st);
// 保存为 PEM 文件
certMap.put("证书文件", encodeCertificateToPEMString(cert));
certMap.put("私钥", encodePrivateKeyToPEMString(keyPair.getPrivate()));
certMap.put("公钥", encodeKeyToPEMString(keyPair.getPublic()));
return certMap;
}
public static String toPemString(byte[] contentBytes, String type) throws IOException {
StringWriter stringWriter = new StringWriter();
try (PemWriter pemWriter = new PemWriter(stringWriter)) {
PemObject pemObject = new PemObject(type, contentBytes);
pemWriter.writeObject(pemObject);
}
return stringWriter.toString();
}
public static String encodeCertificateToPEMString(Certificate certificate) throws Exception {
return toPemString(certificate.getEncoded(), "CERTIFICATE");
}
public static String encodeKeyToPEMString(PublicKey publicKey) throws Exception {
X509EncodedKeySpec keySpec = new X509EncodedKeySpec(publicKey.getEncoded());
return toPemString(keySpec.getEncoded(), "PUBLIC KEY");
}
public static String encodePrivateKeyToPEMString(PrivateKey privateKey) throws Exception {
PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded());
return toPemString(keySpec.getEncoded(), "PRIVATE KEY");
}
}