【问题标题】:Generate certificates, public and private keys with Java [closed]使用 Java 生成证书、公钥和私钥 [关闭]
【发布时间】:2023-07-14 22:25:01
【问题描述】:

我正在寻找一个 java 库或代码来生成证书、公钥和私钥 在不使用第三方程序(例如 openssl)的情况下即时运行。

我认为是在做 keytool+openssl 但来自 Java 代码的东西。

考虑一个使用 ssl 和客户端身份验证保护的基于 java servlet 的 Web 应用程序。 我希望 servlet 容器仅使用 Java 代码根据请求生成客户端证书(例如 pkcs12 格式)。

【问题讨论】:

  • 您也可以只调用 SUN java keytool 类并提供生成证书所需的参数。但这些类位于 com.sun* 包中,并且可能会发生变化。从理论上讲,Java 中的所有内容都可以生成您自己的证书,但它不是公开可用的。

标签: java ssl openssl keytool


【解决方案1】:

旧版警告开始

  • 此代码仅设置 CommonName/CN/Subject
  • 现在正确的位置是 SubjectAltName

来自Chrome Deprecates Subject CN Matching

Chrome 58 将要求证书在 SubjectAltName 字段中指定它们应用的主机名;主题字段中的值将被忽略。"

旧版警告结束

import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Date;

import sun.security.x509.CertAndKeyGen;
import sun.security.x509.X500Name;

public class UseKeyTool {

    private static final int keysize = 1024;
    private static final String commonName = "www.test.de";
    private static final String organizationalUnit = "IT";
    private static final String organization = "test";
    private static final String city = "test";
    private static final String state = "test";
    private static final String country = "DE";
    private static final long validity = 1096; // 3 years
    private static final String alias = "tomcat";
    private static final char[] keyPass = "changeit".toCharArray();

    // copied most ideas from sun.security.tools.KeyTool.java

    @SuppressWarnings("restriction")
    public static void main(String[] args) throws Exception {

        KeyStore keyStore = KeyStore.getInstance("JKS");
        keyStore.load(null, null);

        CertAndKeyGen keypair = new CertAndKeyGen("RSA", "SHA1WithRSA", null);

        X500Name x500Name = new X500Name(commonName, organizationalUnit, organization, city, state, country);

        keypair.generate(keysize);
        PrivateKey privKey = keypair.getPrivateKey();

        X509Certificate[] chain = new X509Certificate[1];

        chain[0] = keypair.getSelfCertificate(x500Name, new Date(), (long) validity * 24 * 60 * 60);

        keyStore.setKeyEntry(alias, privKey, keyPass, chain);

        keyStore.store(new FileOutputStream(".keystore"), keyPass);



    }
}

【讨论】:

  • DNS 名称 not 应该在 commonName 中(假设应该在上面的代码中添加它)。 IETF 和 CA/浏览器论坛都反对这种做法。 DNS 名称需要放在subjectAltNames 中,但代码中缺少它。
  • DigiCert 似乎不同意:“为了保护 example.com,您的通用名称必须是 www.example.com 或 *.example.com 以获得通配符证书。” digicert.com/easy-csr/keytool.htm
  • 查看RFC 6125,第 6.4.4 节或 CA/浏览器 Baseline Security Requirements,第 9.2.2 节。 DidgiCert 应该知道得更多,因为他们是 member of the CA/B
  • 如果您设法为 Tomcat 或任何其他 Java Web 服务器创建一个仅在 subjectAltNames 中具有域的证书并在此处发布链接并且它可以工作(除自签名之外)我会更新我的答案因为我非常怀疑这会奏效。它可能在 RFC 中,但有时现实世界的行为会有所不同。
  • CertAndKeyGen 的包更改为 sun.security.tools.keytool.CertAndKeyGen
【解决方案2】:

您可以使用一对或密钥在 java 中动态生成证书。 (公钥、私钥)。以 BigInteger 格式获取这些密钥并检查以下代码以生成证书。

RSAPrivateKeySpec serPrivateSpec = new RSAPrivateKeySpec(
    new BigInteger(val of pub key), new BigInteger(val of pri key));
fact = KeyFactory.getInstance("RSA");
PrivateKey serverPrivateKey = fact.generatePrivate(serPrivateSpec);

RSAPublicKeySpec serPublicSpec = new RSAPublicKeySpec(
    new BigInteger(agentCL.getSerPubMod()), new BigInteger(agentCL.getSerPubExp()));
PublicKey serverPublicKey = fact.generatePublic(serPublicSpec);

keyStore = KeyStore.getInstance(IMXAgentCL.STORE_TYPE);
keyStore.load(null, SOMEPWD.toCharArray());

Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());

X509Certificate[] serverChain = new X509Certificate[1];
X509V3CertificateGenerator serverCertGen = new X509V3CertificateGenerator();
X500Principal serverSubjectName = new X500Principal("CN=OrganizationName");
serverCertGen.setSerialNumber(new BigInteger("123456789"));
// X509Certificate caCert=null;
serverCertGen.setIssuerDN(somename);
serverCertGen.setNotBefore(new Date());
serverCertGen.setNotAfter(new Date());
serverCertGen.setSubjectDN(somename);
serverCertGen.setPublicKey(serverPublicKey);
serverCertGen.setSignatureAlgorithm("MD5WithRSA");
// certGen.addExtension(X509Extensions.AuthorityKeyIdentifier, false,new
// AuthorityKeyIdentifierStructure(caCert));
serverCertGen.addExtension(X509Extensions.SubjectKeyIdentifier, false,
    new SubjectKeyIdentifierStructure(serverPublicKey));
serverChain[0] = serverCertGen.generateX509Certificate(serverPrivateKey, "BC"); // note: private key of CA

keyStore.setEntry("xyz",
    new KeyStore.PrivateKeyEntry(serverPrivateKey, serverChain),
    new KeyStore.PasswordProtection("".toCharArray()));

希望这会对你有所帮助。

【讨论】:

  • 仅通过添加 Bouncy Castle JSSE 提供程序,您在回答中未提及。