How to generate X.509 certificate in Java 1.8

Here is how to do it, the simplest way, to generate the base64 string. No complications with KeyStores or things like that. I must say that I found this code in several places over the Internet. When I tried to run it I had some exceptions, I solved them, at the time of this writing, the following code works with out any exceptions using jdk1.8.0_121.

I am providing the source code so that you can copy and paste, of course rename the file as HowToGenerateX509CertificateExample.java, and run it.

import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.util.Base64;
import java.util.Date;
import sun.security.x509.AlgorithmId;
import sun.security.x509.CertificateAlgorithmId;
import sun.security.x509.CertificateSerialNumber;
import sun.security.x509.CertificateValidity;
import sun.security.x509.CertificateVersion;
import sun.security.x509.CertificateX509Key;
import sun.security.x509.X500Name;
import sun.security.x509.X509CertImpl;
import sun.security.x509.X509CertInfo;

public class HowToGenerateX509CertificateExample {

    public static void main(String args[]) {
        String commonName = "Hector";
        String organizationalUnit = "Cyber Security";
        String organization = "Cybernetics Inc.";
        String country = "US";

        int keySize = 2048;
        int validDays = 365;

        try {
            X500Name distinguishedName = new X500Name(
                commonName,
                organizationalUnit,
                organization,
                country
            );
            KeyPair kp = generateRSAKeyPair(keySize);

            PrivateKey privkey = kp.getPrivate();
            X509CertInfo info = new X509CertInfo();

            Date since = new Date(); // Since Now
            Date until = new Date(since.getTime() + validDays * 86400000l); // Until x days (86400000 milliseconds in one day)

            CertificateValidity interval = new CertificateValidity(since, until);
            BigInteger sn = new BigInteger(64, new SecureRandom());

            info.set(X509CertInfo.VALIDITY, interval);
            info.set(X509CertInfo.SERIAL_NUMBER, new CertificateSerialNumber(sn));
            info.set(X509CertInfo.SUBJECT, distinguishedName);
            info.set(X509CertInfo.ISSUER, distinguishedName);
            info.set(X509CertInfo.KEY, new CertificateX509Key(kp.getPublic()));
            info.set(
                X509CertInfo.VERSION,
                new CertificateVersion(CertificateVersion.V3)
            );

            AlgorithmId algo = new AlgorithmId(AlgorithmId.md5WithRSAEncryption_oid);
            info.set(X509CertInfo.ALGORITHM_ID, new CertificateAlgorithmId(algo));

            // Sign the cert to identify the algorithm that is used.
            X509CertImpl cert = new X509CertImpl(info);
            cert.sign(privkey, "SHA1withRSA");

            // Update the algorithm and sign again
            algo = (AlgorithmId) cert.get(X509CertImpl.SIG_ALG);
            info.set(
                CertificateAlgorithmId.NAME + "." + CertificateAlgorithmId.ALGORITHM,
                algo
            );

            cert = new X509CertImpl(info);
            cert.sign(privkey, "SHA1withRSA");

            System.out.println(byteArraytoBase64String(cert.getEncoded()));
        } catch (
            IOException
            | NoSuchAlgorithmException
            | CertificateException
            | InvalidKeyException
            | NoSuchProviderException
            | SignatureException e
        ) {
            e.printStackTrace();
        }
    }

    private static KeyPair generateRSAKeyPair(int keySize)
        throws NoSuchAlgorithmException {
        KeyPairGenerator kpg;

        kpg = KeyPairGenerator.getInstance("RSA");
        kpg.initialize(keySize);

        KeyPair kp = kpg.genKeyPair();

        return kp;
    }

    private static String byteArraytoBase64String(final byte[] data) {
        Base64.Encoder enc = Base64.getEncoder();
        return enc.encodeToString(data);
    }
}

Some important things to mention. I really don’t understand why the certificate is signed the first time with SHA1withRSA, then CertificateAlgorithmId.NAME is set, and then is signed again with SHA1withRSA, then what is the purpose of algo?

Anyway, you should see something similar to this in the console.

MIIDJDCCAgygAwIBAgIIKe/B3lNqD+kwDQYJKoZIhvcNAQEFBQAwUjELMAkGA1UEBhMCVVMxGTAXBgNVBAoTEEN5YmVybmV0aWNzIEluYy4xFzAVBgNVBAsTDkN5YmVyIFNlY3VyaXR5MQ8wDQYDVQQDEwZIZWN0b3IwHhcNMTcwMzE4MjI0ODExWhcNMTgwMzE4MjI0ODExWjBSMQswCQYDVQQGEwJVUzEZMBcGA1UEChMQQ3liZXJuZXRpY3MgSW5jLjEXMBUGA1UECxMOQ3liZXIgU2VjdXJpdHkxDzANBgNVBAMTBkhlY3RvcjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAInkXXJ+e4voZ0cCkbS7uz3MWYDAijbTXaQPDlgEFL05c+KWhm28qWmARwc1169FUDm5SAFs04Z8wtrUhBToEjqPxPCYCCXIMQqKSTmC3rPPOePowu8iAwEYKTLL9nZcyC0sPkvmdT5cqF2aCKtREvLQnyMBSxyMzxrcXEpyZ9Y3yUCgTKf/1VSgsYvkKhYGIYBGDo2wiIKGerO5ixENF5KCiDhaaMzm0VnaRh0ZxyIDqk4Wg24+0d3/V3K0y0udltOOlkCfjxfN8urLiwKVdWxicaIhbziWq8C4WevoTJKGc6f16W14w0opc82KZ+NLwvIX4/reixznZMLkIwhY6+MCAwEAATANBgkqhkiG9w0BAQUFAAOCAQEAib+cU4D7wbkqz3Y6eHrsF9NoKCh1bwms2w07I3UNkQYq51ur3r0ONCdhwzqeFV77/5b8Etm3jDDsmWXvGb9uHNpqhr29n6PB8rQLCJkd65U7FoaVaXfYvGxEVRSV5+ixnwVtFjHYxWDyqhJGuK7LoFbfJXydpFyN87i9t75/8kdy5ucGjaShYBQ1MCWD6ElgMqbJ2piT1bGBjF7Hu4CxMi/rPjp9YTYhcDmn2n/Y8uPLUeQJIdssrvzumw9GslzVb/2trCR4d1T44NZE8YIYm1HZquJ39cyOSsiRPwN/cCW7QBz3c6/bU0LyJgiY8ktzF/rPMZ/0PvL5Nhn9VKpDAg==

If you want to verify if the certificate is correctly generated you can use SSL Shopper Certificate Decoder tool. You should be able to see the same info you entered.

Sources: