19

I'm looking for a java library or code to generate certificates, public and private keys on the fly without to use third party programs (such as openssl).

I think something that is doeing keytool+openssl but from Java code.

Consider a java servlet based web application secured with ssl and client authentification. I want the servlet container generate client certificates (eg. pkcs12 format) on request only with Java code.

Donald Duck
  • 8,409
  • 22
  • 75
  • 99
PeterMmm
  • 24,152
  • 13
  • 73
  • 111
  • Alternatively you could just invoke the SUN java keytool class and provide the needed parameters to generate the certificates. But these classes are in the com.sun* package and will potentially change. In theory everything is present in Java to generate your own certificates, but it is not publically available. – David Nouls May 29 '09 at 11:19
  • Related: https://stackoverflow.com/questions/29852290/self-signed-x509-certificate-with-bouncy-castle-in-java – Maarten Bodewes Jun 11 '18 at 16:42
  • The [Bouncy Castle crypto libraries](http://www.bouncycastle.org/documentation.html) are fairly comprehensive. – Thilo May 29 '09 at 10:46
  • stackoverflow is funny, closes the question and people(including me) keep thumbing up the question and bookmarking it :/ good old sof – Mohammad Elsayed Mar 09 '22 at 17:39

2 Answers2

13

You can generate Certificate in java dynamically, by using a pair or keys. (Public Key, Private Keys). Get These keys as BigInteger format and checking the following code to generate certificate.

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

Hope this will help you.

takrl
  • 6,356
  • 3
  • 60
  • 69
Sekhar
  • 149
  • 1
  • 3
8

Legacy Warning Begin:

  • This code only sets the CommonName/CN/Subject.
  • The correct place now is the SubjectAltName.

From Chrome Deprecates Subject CN Matching:

Chrome 58 will require that certificates specify the hostname(s) to which they apply in the SubjectAltName field; values in the Subject field will be ignored."

Legacy Warning End

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



    }
}
jww
  • 97,681
  • 90
  • 411
  • 885
Ray Hulha
  • 10,701
  • 5
  • 53
  • 53
  • 1
    DNS names are *not* supposed to be in the `commonName` (assuming that's where its supposed to be added in the code above). The practice is deprecated by both the IETF and CA/Browser Forums. DNS names need to go in the `subjectAltNames`, but the code lacks it. – jww Aug 29 '14 at 11:40
  • 1
    DigiCert seems to disagree: "To secure https://www.example.com, your common name must be www.example.com or *.example.com for a wildcard certificate." https://www.digicert.com/easy-csr/keytool.htm – Ray Hulha Aug 29 '14 at 11:44
  • Check out [RFC 6125](http://tools.ietf.org/html/rfc6125), Section 6.4.4 or the CA/Browser [Baseline Security Requirements](https://cabforum.org/wp-content/uploads/Baseline_Requirements_V1_1_9.pdf), Section 9.2.2. DidgiCert should know better since they are a [member of the CA/B](https://cabforum.org/members/). – jww Aug 29 '14 at 11:48
  • 1
    If you manage to create a certificate for Tomcat or any other Java web server with the domain only in the subjectAltNames and post the link here and it works ( other then being self signed ) I will update my answer as I have strong doubts this will work. It might be in the RFC but sometimes the real world behaves differently. – Ray Hulha Aug 29 '14 at 11:51
  • I stand corrected: "Chrome 58 will require that certificates specify the hostname(s) to which they apply in the SubjectAltName field; values in the Subject field will be ignored." https://textslashplain.com/2017/03/10/chrome-deprecates-subject-cn-matching/ You were right and I was wrong! – Ray Hulha May 10 '17 at 22:22
  • Warning: this relies on Sun specific code - i.e. *implementation specific* code - which means that 1. it *may* break at any (major) version increase 2. it will not be available on other Java compatible platforms than Oracle SE 3. it will not be considered pure Java by Oracle itself. – Maarten Bodewes May 14 '17 at 12:01
  • So how can you create the SAN dnsname instead? – user1156544 Jul 05 '17 at 10:06
  • You best bet is to look if X500Name has another constructor with a SAN parameter. – Ray Hulha Jul 05 '17 at 13:05
  • 2
    The package for CertAndKeyGen changed to sun.security.tools.keytool.CertAndKeyGen – Charlie Sep 23 '19 at 00:02
  • How would this need to change in order to use an existing private key? Just generate a new certificate for the private key instead of generating a new private key *and* certificate? – Christopher Schultz Nov 02 '21 at 17:32