Category Archives: SSL

PEMParser update

In my last post I talked about using PEMParser to load private keys from OpenSSL PEM files. After further testing I discovered I needed to refine the code a little.

There appears to be a couple of different ways the private key can be encoded in the file. I discovered this after generating some new test keys only to find they didn’t work with my existing code.

Object privatekey = parser.readObject();

if (privatekey instanceof PEMEncryptedKeyPair) {
     try {
	privatekey = ((PEMEncryptedKeyPair)privatekey).decryptKeyPair(new JcePEMDecryptorProviderBuilder().build(passphrase));
     } catch (Exception e) {
	throw new InvalidPassphraseException(e);
     }
} else if(privatekey instanceof PKCS8EncryptedPrivateKeyInfo) {
     try {
	privatekey = converter.getPrivateKey(((PKCS8EncryptedPrivateKeyInfo)privatekey).decryptPrivateKeyInfo(new JceOpenSSLPKCS8DecryptorProviderBuilder().build(passphrase)));
     } catch (Exception e) {
	throw new InvalidPassphraseException(e);
     }
}

if (privatekey instanceof PEMKeyPair) {
	return loadKeyPair((PEMKeyPair)privatekey);
} else if(privatekey instanceof RSAPrivateCrtKey){
	return loadKeyPair((RSAPrivateCrtKey)privatekey);
} else {
	throw new FileFormatException("The file doesn't seem to have any supported key types obj=" + privatekey);
}

Loading PEM keys & certificates from Java

I’ve worked for years with Java and SSL sockets and I’ve always struggled with support issues when it comes to SSL certificates. The problem is that most non-developer folks use Apache style PEM certificates and have come pretty use to them. When you present them with a different format and require they configure your application with a Java KeyStore your bound to get problems. The only solution is to allow your end users to configure your solution with the formats they know and love.

Thats why I decided to go with a more flexible approach for configuring Hypersocket with SSL certificates. I want end users to be able to upload their existing PEM keys and certificates and have this work out-of-the-box with no conversion, no formatting changes. Just use the files as they come. Luckily there are tools available at the Bouncycastle project that now make this possible.

First of all I’ve installed the Bouncycastle JCE provider 1.48 in the security providers list.

Security.addProvider(new BouncyCastleProvider());

I’m now going to load the private key from a PEM file, if the file is encrypted we will decrypt the key. I’m using PEMParser from the Bouncycastle PKIX/CMS/EAC/PKCS/TSP/OPENSSL package. Some checks and balances have been removed from the following code:

PEMParser parser = new PEMParser(new InputStreamReader(keyfile));

// Load the key object
Object privatekey = parser.readObject();

// Check to see if the object returned is an encrypted key pair
if (privatekey instanceof PEMEncryptedKeyPair) {
    privatekey = ((PEMEncryptedKeyPair)privatekey).decryptKeyPair(new JcePEMDecryptorProviderBuilder().build("xxxxxxxx".toCharArray()));
} 

// Cast to a PEMKeyPair
PEMKeyPair pair = (PEMKeyPair) privatekey;

// Get the encoded objects ready for conversion to Java objects
byte[] encodedPublicKey = pair.getPublicKeyInfo().getEncoded();
byte[] encodedPrivateKey = pair.getPrivateKeyInfo().getEncoded();

// Now convert to Java objects
KeyFactory keyFactory = KeyFactory.getInstance( "RSA");
			
X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(encodedPublicKey);
PublicKey publicKey = keyFactory.generatePublic(publicKeySpec);

PKCS8EncodedKeySpec privateKeySpec = new PKCS8EncodedKeySpec(encodedPrivateKey);
PrivateKey privateKey = keyFactory.generatePrivate(privateKeySpec);

KeyPair pair = new KeyPair(publicKey, privateKey);

We now have a KeyPair loaded into memory, but before we can initialize an SSL context we need to get the certificate from the .crt file. We can use the same APi to load the certificate:

PEMParser parser = new PEMParser(new InputStreamReader(certfile));

X509CertificateHolder obj = (X509CertificateHolder) parser.readObject();
Certificate cert = JcaX509CertificateConverter().setProvider("BC").getCertificate(obj);

Now the only thing left to do is to place these into a KeyStore object so we can initialize SSL context.

KeyStore store = KeyStore.getInstance("JKS");
store.load(null);

store.setKeyEntry(alias, pair.getPrivate(), passphrase,	new java.security.cert.Certificate[] { cert });