I am trying to connect to an SSL server which requires me to authenticate myself. In order to use SSL over Apache MINA I need a suitable JKS file. However, I have only been given a .PEM file.
How would I go about creating a JKS file from a PEM file?
First, convert your certificate in a DER format :
openssl x509 -outform der -in certificate.pem -out certificate.der
And after, import it in the keystore :
keytool -import -alias your-alias -keystore cacerts -file certificate.der
If you only want to import a certificate in PEM format into a keystore, keytool will do the job:
keytool -import -alias *alias* -keystore cacerts -file *cert.pem*
I've developed http://code.google.com/p/java-keyutil/ which imports PEM certificates straight into a Java keystore. Its primary purpose is to import a multi-part PEM Operating System certificate bundles such as ca-bundle.crt. These often includes headers which keytool cannot handle
</self promotion>
In my case I had a pem file which contained two certificates and an encrypted private key to be used in mutual SSL authentication. So my pem file looked like this:
-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,C8BF220FC76AA5F9 ... -----END RSA PRIVATE KEY----- -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----
Here is what I did
Split the file into three separate files, so that each one contains just one entry, starting with ---BEGIN..
and ending with ---END..
lines. Lets assume we now have three files: cert1.pem
, cert2.pem
, and pkey.pem
.
Convert pkey.pem
into DER format using openssl and the following syntax:
openssl pkcs8 -topk8 -nocrypt -in pkey.pem -inform PEM -out pkey.der -outform DER
Note, that if the private key is encrypted you need to supply a password( obtain it from the supplier of the original pem file ) to convert to DER format, openssl
will ask you for the password like this: "enter a passphrase for pkey.pem
: ".
If conversion is successful, you will get a new file called pkey.der
.
Create a new java keystore and import the private key and the certificates:
String keypass = "password"; // this is a new password, you need to come up with to protect your java key store file String defaultalias = "importkey"; KeyStore ks = KeyStore.getInstance("JKS", "SUN"); // this section does not make much sense to me, // but I will leave it intact as this is how it was in the original example I found on internet: ks.load( null, keypass.toCharArray()); ks.store( new FileOutputStream ( "mykeystore" ), keypass.toCharArray()); ks.load( new FileInputStream ( "mykeystore" ), keypass.toCharArray()); // end of section.. // read the key file from disk and create a PrivateKey FileInputStream fis = new FileInputStream("pkey.der"); DataInputStream dis = new DataInputStream(fis); byte[] bytes = new byte[dis.available()]; dis.readFully(bytes); ByteArrayInputStream bais = new ByteArrayInputStream(bytes); byte[] key = new byte[bais.available()]; KeyFactory kf = KeyFactory.getInstance("RSA"); bais.read(key, 0, bais.available()); bais.close(); PKCS8EncodedKeySpec keysp = new PKCS8EncodedKeySpec ( key ); PrivateKey ff = kf.generatePrivate (keysp); // read the certificates from the files and load them into the key store: Collection col_crt1 = CertificateFactory.getInstance("X509").generateCertificates(new FileInputStream("cert1.pem")); Collection col_crt2 = CertificateFactory.getInstance("X509").generateCertificates(new FileInputStream("cert2.pem")); Certificate crt1 = (Certificate) col_crt1.iterator().next(); Certificate crt2 = (Certificate) col_crt2.iterator().next(); Certificate[] chain = new Certificate[] { crt1, crt2 }; String alias1 = ((X509Certificate) crt1).getSubjectX500Principal().getName(); String alias2 = ((X509Certificate) crt2).getSubjectX500Principal().getName(); ks.setCertificateEntry(alias1, crt1); ks.setCertificateEntry(alias2, crt2); // store the private key ks.setKeyEntry(defaultalias, ff, keypass.toCharArray(), chain ); // save the key store to a file ks.store(new FileOutputStream ( "mykeystore" ),keypass.toCharArray());
(optional) Verify the content of your new key store:
$ keytool -list -keystore mykeystore -storepass password
Keystore type: JKS Keystore provider: SUN
Your keystore contains 3 entries:
cn=...,ou=...,o=.., Sep 2, 2014, trustedCertEntry, Certificate fingerprint (SHA1): 2C:B8: ...
importkey, Sep 2, 2014, PrivateKeyEntry, Certificate fingerprint (SHA1): 9C:B0: ...
cn=...,o=...., Sep 2, 2014, trustedCertEntry, Certificate fingerprint (SHA1): 83:63: ...
(optional) Test your certificates and private key from your new key store against your SSL server: ( You may want to enable debugging as an VM option: -Djavax.net.debug=all )
char[] passw = "password".toCharArray(); KeyStore ks = KeyStore.getInstance("JKS", "SUN"); ks.load(new FileInputStream ( "mykeystore" ), passw ); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passw); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); TrustManager[] tm = tmf.getTrustManagers(); SSLContext sclx = SSLContext.getInstance("TLS"); sclx.init( kmf.getKeyManagers(), tm, null); SSLSocketFactory factory = sclx.getSocketFactory(); SSLSocket socket = (SSLSocket) factory.createSocket( "192.168.1.111", 443 ); socket.startHandshake(); //if no exceptions are thrown in the startHandshake method, then everything is fine..
Finally register your certificates with HttpsURLConnection if plan to use it:
char[] passw = "password".toCharArray(); KeyStore ks = KeyStore.getInstance("JKS", "SUN"); ks.load(new FileInputStream ( "mykeystore" ), passw ); KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(ks, passw); TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); tmf.init(ks); TrustManager[] tm = tmf.getTrustManagers(); SSLContext sclx = SSLContext.getInstance("TLS"); sclx.init( kmf.getKeyManagers(), tm, null); HostnameVerifier hv = new HostnameVerifier() { public boolean verify(String urlHostName, SSLSession session) { if (!urlHostName.equalsIgnoreCase(session.getPeerHost())) { System.out.println("Warning: URL host '" + urlHostName + "' is different to SSLSession host '" + session.getPeerHost() + "'."); } return true; } }; HttpsURLConnection.setDefaultSSLSocketFactory( sclx.getSocketFactory() ); HttpsURLConnection.setDefaultHostnameVerifier(hv);
I'm always forgetting how to do this because it's something that I just do once in a while, this is one possible solution, and it just works:
- Go to your favourite browser and download the main certificate from the secured website.
Execute the two following lines of code:
$ openssl x509 -outform der -in GlobalSignRootCA.crt -out GlobalSignRootCA.der $ keytool -import -alias GlobalSignRootCA -keystore GlobalSignRootCA.jks -file GlobalSignRootCA.der
If executing in Java SE environment add the following options:
$ java -Djavax.net.ssl.trustStore=GlobalSignRootCA.jks -Djavax.net.ssl.trustStorePassword=trustStorePassword -jar MyJar.jar
Or add the following to the java code:
System.setProperty("javax.net.ssl.trustStore", "GlobalSignRootCA.jks"); System.setProperty("javax.net.ssl.trustStorePassword","trustStorePassword");
The other option for step 2 is to just using the keytool
command. Bellow is an example with a chain of certificates:
$ keytool -import -file org.eu.crt -alias orgcrt -keystore globalsignrs.jks $ keytool -import -file GlobalSignOrganizationValidationCA-SHA256-G2.crt -alias globalsignorgvalca -keystore globalsignrs.jks $ keytool -import -file GlobalSignRootCA.crt -alias globalsignrootca -keystore globalsignrs.jks
If you need an easy way to load PEM files in Java without having to deal with external tools (opensll, keytool), here is my code I use in production :
import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.security.KeyFactory; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.X509Certificate; import java.security.interfaces.RSAPrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.PKCS8EncodedKeySpec; import java.util.ArrayList; import java.util.List; import javax.net.ssl.KeyManager; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.SSLServerSocketFactory; import javax.xml.bind.DatatypeConverter; public class PEMImporter { public static SSLServerSocketFactory createSSLFactory(File privateKeyPem, File certificatePem, String password) throws Exception { final SSLContext context = SSLContext.getInstance("TLS"); final KeyStore keystore = createKeyStore(privateKeyPem, certificatePem, password); final KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509"); kmf.init(keystore, password.toCharArray()); final KeyManager[] km = kmf.getKeyManagers(); context.init(km, null, null); return context.getServerSocketFactory(); } /** * Create a KeyStore from standard PEM files * * @param privateKeyPem the private key PEM file * @param certificatePem the certificate(s) PEM file * @param the password to set to protect the private key */ public static KeyStore createKeyStore(File privateKeyPem, File certificatePem, final String password) throws Exception, KeyStoreException, IOException, NoSuchAlgorithmException, CertificateException { final X509Certificate[] cert = createCertificates(certificatePem); final KeyStore keystore = KeyStore.getInstance("JKS"); keystore.load(null); // Import private key final PrivateKey key = createPrivateKey(privateKeyPem); keystore.setKeyEntry(privateKeyPem.getName(), key, password.toCharArray(), cert); return keystore; } private static PrivateKey createPrivateKey(File privateKeyPem) throws Exception { final BufferedReader r = new BufferedReader(new FileReader(privateKeyPem)); String s = r.readLine(); if (s == null || !s.contains("BEGIN PRIVATE KEY")) { r.close(); throw new IllegalArgumentException("No PRIVATE KEY found"); } final StringBuilder b = new StringBuilder(); s = ""; while (s != null) { if (s.contains("END PRIVATE KEY")) { break; } b.append(s); s = r.readLine(); } r.close(); final String hexString = b.toString(); final byte[] bytes = DatatypeConverter.parseBase64Binary(hexString); return generatePrivateKeyFromDER(bytes); } private static X509Certificate[] createCertificates(File certificatePem) throws Exception { final List<X509Certificate> result = new ArrayList<X509Certificate>(); final BufferedReader r = new BufferedReader(new FileReader(certificatePem)); String s = r.readLine(); if (s == null || !s.contains("BEGIN CERTIFICATE")) { r.close(); throw new IllegalArgumentException("No CERTIFICATE found"); } StringBuilder b = new StringBuilder(); while (s != null) { if (s.contains("END CERTIFICATE")) { String hexString = b.toString(); final byte[] bytes = DatatypeConverter.parseBase64Binary(hexString); X509Certificate cert = generateCertificateFromDER(bytes); result.add(cert); b = new StringBuilder(); } else { if (!s.startsWith("----")) { b.append(s); } } s = r.readLine(); } r.close(); return result.toArray(new X509Certificate[result.size()]); } private static RSAPrivateKey generatePrivateKeyFromDER(byte[] keyBytes) throws InvalidKeySpecException, NoSuchAlgorithmException { final PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(keyBytes); final KeyFactory factory = KeyFactory.getInstance("RSA"); return (RSAPrivateKey) factory.generatePrivate(spec); } private static X509Certificate generateCertificateFromDER(byte[] certBytes) throws CertificateException { final CertificateFactory factory = CertificateFactory.getInstance("X.509"); return (X509Certificate) factory.generateCertificate(new ByteArrayInputStream(certBytes)); } }
Have fun.