使用Java生成证书、公钥和私钥

19

我在寻找一个Java库或代码,可用于生成证书、公钥和私钥,无需使用第三方程序(如openssl)。

我认为应该有一些类似keytool+openssl的东西,但是是从Java代码中实现的。

考虑一个基于Java servlet的Web应用程序,使用SSL和客户端身份验证进行安全保护。 我想让servlet容器仅在请求时使用Java代码生成客户端证书(例如pkcs12格式)。


Bouncy Castle加密库非常全面。 - Thilo
或者你可以直接调用SUN Java keytool类并提供所需的参数来生成证书。但是这些类在com.sun*包中,可能会发生变化。理论上,Java中存在生成自己证书的所有内容,但它不是公开可用的。 - David Nouls
相关:https://dev59.com/9F0a5IYBdhLWcg3w3L8- - Maarten Bodewes
stackoverflow很有趣,关闭了问题,但人们(包括我)仍然会点赞和收藏它 :/ 好老的sof - Mohammad Elsayed
2个回答

13

你可以使用一对密钥(公钥、私钥)在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()));

希望这能对你有所帮助。


8
只有添加Bouncy Castle JSSE提供程序,您在回答中没有提到它。 - user207421

8

遗留问题警告:

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

来自Chrome Deprecates Subject CN Matching

Chrome 58将要求证书在SubjectAltName字段中指定适用于其的主机名; 在Subject字段中的值将被忽略。

遗留问题警告结束

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);



    }
}

1
DNS名称不应该出现在commonName中(假设这是上面代码中应该添加的位置)。这种做法已被IETF和CA/Browser论坛弃用。DNS名称需要放在subjectAltNames中,但是代码缺少它。 - jww
1
DigiCert似乎持不同意见:“为了保护https://www.example.com,您的通用名称必须是www.example.com或*.example.com(通配符证书)。” https://www.digicert.com/easy-csr/keytool.htm - Ray Hulha
1
如果您成功为Tomcat或任何其他Java Web服务器创建了仅在subjectAltNames中包含域的证书,并在此处发布链接并且它可以正常工作(除了自签名之外),我将更新我的答案,因为我非常怀疑这会起作用。它可能在RFC中,但有时现实世界的行为是不同的。 - Ray Hulha
警告:这依赖于Sun的特定代码 - 即实现特定代码,这意味着:1.它可能在任何(主要)版本增加时中断 2.它不会在其他Java兼容平台上可用,只有Oracle SE 3. Oracle本身不会认为它是纯Java。 - Maarten Bodewes
2
CertAndKeyGen的包已更改为sun.security.tools.keytool.CertAndKeyGen。 - Charlie
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接