Creating Java KeyStore Programmatically

Challenge: A Java application at runtime that will receive a public X509 certificate and a RSA private key will create a Java KeyStore on the fly. Normally, I don't have to deal with the JSSE API in too much inner detail. The complex cases don't really come up in normal year to year application building and integrations. But this gave me a chance to play with that API more. I could find point examples for each specific point on the web, but I had a hard time finding an overall already together of what exactly I wanted to do. Thus, sharing my end result.

Preface: There is a command line keystore tool that is much easier to use and has tons of example on the web of how to use. Thus, this is only for the time that you really need to do this. For instance, this can be used if the private key is coming from some sort of secured source. But, be aware that throwing around a raw private key could be more of a risk than using a nice password protected Java KeyStore file. But, then we wouldn't have a challenge and an article, right?

Step 1 - Create a New Empty KeyStore

This is pretty simple - get an instance and load from null.

public KeyStore createEmptyKeyStore() throws IOException, GeneralSecurityException {

    KeyStore keyStore = KeyStore.getInstance(“JKS”);

    keyStore.load(null,null);

    return keyStore;

}

Step 2 - Load Public Certificate

Loading the certificate is easy. We just need an InputStream to the source. That could be from a file, classpath, or what have you. (And if the certificate is in memory, you can always wrap a ByteArrayInputStream on it.)

public X509Certificate loadCertificate(InputStream publicCertIn) throws IOException, GeneralSecurityException {

    CertificateFactory factory = CertificateFactory.getInstance("X.509");

    X509Certificate cert = (X509Certificate)factory.generateCertificate(publicCertIn);

    return cert;

}

Step 3 - Load Private Certificate

This is the hardest part. Compared to the public certificate, there's a surprising large amount of code.

One gotcha. The typical private key, such as generated by openssl, has a BEGIN and END lines with the actual certificate base64 encoded in the middle. You need to strip the BEGIN/END lines off and base64 decode it. That could be a string replace, parse, or I saw a really nifty example of using a regular expression. I liked the last one the best, so I show the code that way:

public PrivateKey loadPrivateKey(InputStream privateKeyIn) throws IOException, GeneralSecurityException {
    
    //need the full file - org.apache.commons.io.IOUtils is handy
    byte[] fullFileAsBytes = IOUtils.toByteArray( privateKeyIn );
    //remember this is supposed to be a text source with the BEGIN/END and base64 in the middle of the file
    String fullFileAsString = new String(fullFileAsBytes);

    //nifty regular expression to extract out between BEGIN/END
    Pattern parse = Pattern.compile("(?m)(?s)^---*BEGIN.*---*$(.*)^---*END.*---*$.*");
    String encoded = parse.matcher(fullFileAsString).replaceFirst("$1");

    //decode the Base64 string
    byte[] keyDecoded = Base64.getMimeDecoder().decode(encoded);

    //for my example, the source is in common PKCS#8 format
    PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyDecoded);

    //from there we can use the KeyFactor to generate
    KeyFactory keyFactory = KeyFactory.getInstance("RSA");
    PrivateKey privateKey = keyFactory.generatePrivate(keySpec);

    return privateKey;
}

That is a lot of code to do something that doesn't seem like it should be so much work. I was digging into this more and there's another API - Bouncy House. It offers some useful utilities and support for other formats. Using it makes for smaller code, here's the same example. (It does take a Reader, but you could convert from InputStream. Besides, what is the best source object really depends on how exactly the private key is coming in.)

public X509Certificate loadPrivateKey(Reader privateKeyReader) throws IOException, GeneralSecurityException {
   Security.addProvider(new BouncyCastleProvider());
   PEMParser pemParser = new PEMParser(br);
   PrivateKeyInfo pki = (PrivateKeyInfo)pemParser.readObject();
   JcaPEMKeyConverter conv = new JcaPEMKeyConverter();
   PrivateKey privateKey = conv.getPrivateKey(pki);
   return privateKey;
}

//Related note, if your file is a key pair, it handles that too, instead:                    
   PEMKeyPair pemKeyPair = (PEMKeyPair) pemParser.readObject(); //here
   KeyPair kp = new JcaPEMKeyConverter().getKeyPair(pemKeyPair);
   PrivateKey privateKey = kp.getPrivate();

Step 4 - Bring It Together

Now you have a public certificate, a private key, and an empty keystore. Time to bring them together.

public KeyStore createKeyStore(InputStream publicCertIn, InputStream privateKeyIn) throws IOException, GeneralSecurityException {
    
    KeyStore keyStore = createEmptyKeyStore();
    
    X509Certificate publicCert = loadCertificate(publicCertIn);

    PrivateKey privateKey = loadPrivateKey(privateKeyIn);
    
    
    keyStore.setCertificateEntry("aliasForCertHere", publicCert);

    keyStore.setKeyEntry("aliasForPrivateKeyHere", privateKey, "PasswordForPrivateKeyHere", new Certificate[]{publicCert});

    return keyStore;
}
 
  

From there, depends on what you need to do. In my case, I next needed the KeyStore converted into bytes. From there you could write to a file, give to an API that needs a KeyStore, or what have you. In any case, here's how you convert from KeyStore into a Byte Array.

public byte[] convertKeyStoreToBytes(KeyStore keyStore) throws IOException, GeneralSecurityException {
   ByteArrayOutputStream out = new ByteArrayOutputStream();
   keyStore.store(out, "PasswordForPrivateKeyHere".toCharArray());
   byte[] bytes = out.toByteArray();
   return bytes;
}
byte[] keyStoreAsBytes = out.toByteArray();

Since this was an example, I abbreviated parts of the above. Code could be cleaned up, resources could be closed, exceptions could be narrowed down or not, embedded literals could be passed or constants, and so on. My main goal was to show using how to use the API to do these steps.

To view or add a comment, sign in

More articles by Bill Young

  • Salesforce Business Technology Architect-a-thon

    What was my next challenge after running a marathon? My day job is Enterprise Architecture, so I helped organize an…

    2 Comments
  • Salesforce REST API using Spring Web Services

    There are many examples out there of using the Salesforce REST API. But I didn't come across any showing explicitly how…

    1 Comment
  • Salesforce SFDX Plugin - REST API Access

    The Salesforce SFDX plugin provides a library of some pre-fab commands, like run a SOQL query. The sample hi command…

  • SFDX Plugin for Record Type Ids in JSON Files

    SFDX has a great feature (data:tree:export and import) where you can export data from a Salesforce instance to use as…

    5 Comments
  • Creating A Salesforce SFDX Plugin

    It was a rainy evening outside. That meant the new roof on my shed had to wait.

  • Adventures With Java URL Handler

    Have you ever wanted the native URL Java object to support your personal protocol? Well me neither, until I came across…

  • Sharing Scratch Orgs In Salesforce DX

    I like that Salesforce DX enables Scratch Orgs to be per individual. But, it can still be handy to share a single…

    1 Comment

Others also viewed

Explore content categories