You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求Java原生替代OpenSSL实现证书创建与签名的方案

好的,完全懂你想摆脱调用OpenSSL进程、纯用Java实现证书创建与签名的需求!下面我会给出对应你OpenSSL命令的Java实现方案,主要用到Java标准安全库+BouncyCastle(它能大幅简化X509v3扩展比如SAN的处理,比纯标准库省心太多)。

先准备依赖

首先需要引入BouncyCastle的依赖,如果你用Maven,在pom.xml里加:

<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcpkix-jdk15on</artifactId>
    <version>1.70</version>
</dependency>
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk15on</artifactId>
    <version>1.70</version>
</dependency>

Gradle的话就在build.gradle里加:

implementation 'org.bouncycastle:bcpkix-jdk15on:1.70'
implementation 'org.bouncycastle:bcprov-jdk15on:1.70'

步骤1:生成RSA密钥对(对应openssl genrsa 2048)

这一步用Java标准库就能搞定,生成服务器的私钥和公钥:

import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Security;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

// 先注册BouncyCastle Provider
static {
    Security.addProvider(new BouncyCastleProvider());
}

public static KeyPair generateRSAKeyPair() throws Exception {
    KeyPairGenerator keyGen = KeyPairGenerator.getInstance("RSA", "BC");
    keyGen.initialize(2048); // 对应2048位密钥
    return keyGen.generateKeyPair();
}

步骤2:创建并签名X509证书(对应openssl req + openssl ca)

这一步是核心,要处理Subject DN、Subject Alternative Name(SAN,包括DNS和Email),并用你的CA证书+私钥签名:

import java.math.BigInteger;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.util.Date;
import org.bouncycastle.asn1.x500.X500Name;
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.GeneralName;
import org.bouncycastle.asn1.x509.GeneralNames;
import org.bouncycastle.asn1.x509.SubjectAlternativeName;

public static X509Certificate signCertificate(
        PrivateKey caPrivateKey, X509Certificate caCert,
        PublicKey serverPublicKey, KeyPair serverKeyPair,
        String cname, String altname, String email,
        String country, String state, String org, String orgUnit,
        int daysValid) throws Exception {

    // 1. 构建证书主体DN
    X500Name subject = new X500Name(
            "C=" + country +
            ", ST=" + state +
            ", O=" + org +
            ", OU=" + orgUnit +
            ", CN=" + cname);

    // 2. 设置证书有效期
    Date startDate = new Date();
    Date endDate = new Date(startDate.getTime() + (long) daysValid * 24 * 60 * 60 * 1000);

    // 3. 生成证书序列号(唯一即可,这里用当前时间戳)
    BigInteger serial = BigInteger.valueOf(System.currentTimeMillis());

    // 4. 构建X509v3证书,添加SAN扩展
    JcaX509v3CertificateBuilder certBuilder = new JcaX509v3CertificateBuilder(
            new X500Name(caCert.getSubjectDN().getName()),
            serial,
            startDate,
            endDate,
            subject,
            serverPublicKey);

    // 添加SAN扩展:DNS + Email
    GeneralNames generalNames = new GeneralNames(
            new GeneralName(GeneralName.dNSName, altname),
            new GeneralName(GeneralName.rfc822Name, email));
    certBuilder.addExtension(Extension.subjectAlternativeName, false, generalNames);

    // 5. 用CA私钥签名证书(用SHA256withRSA算法,和OpenSSL默认一致)
    ContentSigner signer = new JcaContentSignerBuilder("SHA256withRSA")
            .setProvider("BC")
            .build(caPrivateKey);

    // 6. 生成最终证书
    return new JcaX509CertificateConverter()
            .setProvider("BC")
            .getCertificate(certBuilder.build(signer));
}

另外,你需要先加载CA的私钥和证书,这里给个加载示例:

import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;

// 从PKCS12格式的CA密钥库加载CA私钥和证书
public static Object[] loadCA(String caPfxPath, String caPfxPassword) throws Exception {
    KeyStore caKeyStore = KeyStore.getInstance("PKCS12");
    try (FileInputStream fis = new FileInputStream(caPfxPath)) {
        caKeyStore.load(fis, caPfxPassword.toCharArray());
    }
    // 假设CA证书的别名是"ca",如果不确定可以遍历密钥库找别名
    String alias = "ca";
    PrivateKey caPrivateKey = (PrivateKey) caKeyStore.getKey(alias, caPfxPassword.toCharArray());
    X509Certificate caCert = (X509Certificate) caKeyStore.getCertificate(alias);
    return new Object[]{caPrivateKey, caCert};
}

步骤3:生成PKCS12文件(对应openssl pkcs12 -export)

把服务器私钥、服务器证书、CA证书打包成PKCS12格式:

import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.cert.Certificate;

public static void generatePKCS12(
        KeyPair serverKeyPair, X509Certificate serverCert, X509Certificate caCert,
        String pfxPath, String pfxPassword) throws Exception {

    KeyStore pkcs12 = KeyStore.getInstance("PKCS12");
    pkcs12.load(null, null);

    // 导入服务器私钥和证书链(服务器证书 + CA证书)
    Certificate[] certChain = new Certificate[]{serverCert, caCert};
    pkcs12.setKeyEntry("server", serverKeyPair.getPrivate(), pfxPassword.toCharArray(), certChain);

    // 写入文件
    try (FileOutputStream fos = new FileOutputStream(pfxPath)) {
        pkcs12.store(fos, pfxPassword.toCharArray());
    }
}

整体调用示例

把上面的方法串起来使用:

public static void main(String[] args) throws Exception {
    // 1. 加载CA私钥和证书
    Object[] caInfo = loadCA("path/to/ca.pfx", "ca-password");
    PrivateKey caPrivateKey = (PrivateKey) caInfo[0];
    X509Certificate caCert = (X509Certificate) caInfo[1];

    // 2. 生成服务器密钥对
    KeyPair serverKeyPair = generateRSAKeyPair();

    // 3. 签名服务器证书
    X509Certificate serverCert = signCertificate(
            caPrivateKey, caCert,
            serverKeyPair.getPublic(), serverKeyPair,
            "your-cname", "your-altname.com", "admin@your-altname.com",
            "CN", "Beijing", "YourCompany", "ITDept",
            365); // 有效期365天

    // 4. 生成PKCS12文件
    generatePKCS12(serverKeyPair, serverCert, caCert, "path/to/server.pfx", "pfx-password");
}

注意事项

  • 确保BouncyCastle Provider被正确注册,不然会报算法找不到的错误。
  • 如果你需要保存服务器的私钥到单独的PEM文件,可以用Apache Commons Codec或者BouncyCastle的PEMWriter来处理,不过一般PKCS12已经包含私钥和证书,足够用了。
  • 证书序列号要保证唯一,示例里用了时间戳,你也可以用自增的数字或者随机数。

内容的提问来源于stack exchange,提问作者Mister Lamp

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:07:42