【SSL】什么是自签名证书及使用Java生成SSL自签名证书

【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");
    }


}
相关推荐
春日见1 分钟前
车辆动力学:前后轮车轴
java·开发语言·驱动开发·docker·计算机外设
宋小黑13 分钟前
JDK 6到25 全版本网盘合集 (Windows + Mac + Linux)
java·后端
7哥♡ۣۖᝰꫛꫀꪝۣℋ25 分钟前
Spring-cloud\Eureka
java·spring·微服务·eureka
老毛肚38 分钟前
手写mybatis
java·数据库·mybatis
两点王爷40 分钟前
Java基础面试题——【Java语言特性】
java·开发语言
choke23344 分钟前
[特殊字符] Python 文件与路径操作
java·前端·javascript
choke2331 小时前
Python 基础语法精讲:数据类型、运算符与输入输出
java·linux·服务器
岁岁种桃花儿1 小时前
CentOS7 彻底卸载所有JDK/JRE + 重新安装JDK8(实操完整版,解决kafka/jps报错)
java·开发语言·kafka
roman_日积跬步-终至千里2 小时前
【Java并发】Java 线程池实战:警惕使用CompletableFuture.supplyAsync
java·开发语言·网络
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于Springboot的扶贫众筹平台为例,包含答辩的问题和答案
java·spring boot·后端