PKCS12 is an active file format for storing cryptography objects as a single file. It can be used to store secret key, private key and certificate.It is a standardized format published by RSA Laboratories which means it can be used not only in Java but also in other libraries in C, C++ or C# etc. This file format is frequently used to import and export entries from or to other keystore types.
Next we will explain the operations which can be performed on PKCS12 keystore.
Create PKCS12 keystore
Before storing an entry into a PKCS12 keystore, the keystore has to be loaded first. This means we have to have a keystore created first. The simplest way of creating a PKCS12 keystore is :
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(null, null); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); } catch (Exception ex){ ex.printStackTrace(); }
Note, when calling keyStore.load(null, null), two nulls are passed as the input keystore stream and password. This is because we don't have the keystore available yet. After running this program, there should be a keystore file named output.p12 in current working directory.
Store secret key
PKCS12 allows to store secret keys on a limited base. Secret keys are frequently used to encrypt/decrypt data. To transfer the keys conveniently, they can be stored in a keystore like PKCS12 and transferred.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(null, null); KeyGenerator keyGen = KeyGenerator.getInstance("AES"); keyGen.init(128); Key key = keyGen.generateKey(); keyStore.setKeyEntry("secret", key, "password".toCharArray(), null); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); } catch (Exception ex){ ex.printStackTrace(); }
Some secret keys with algorithm AES stored on PKCS12 keystore cannot be extracted in Java. Since PKCS12 is a portable standard, other libraries may support extracting secret keys.
Store private key
The private key and its associated certificate chain can be stored in PKCS12 keystore. The keystore contains private keys and certificates can be used in SSL communications across the web.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); // keyStore.load(new FileInputStream("output.p12"),"password".toCharArray()); keyStore.load(null, null);; CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA"); gen.generate(1024); Key key=gen.getPrivateKey(); X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=ROOT"), (long)365*24*3600); X509Certificate[] chain = new X509Certificate[1]; chain[0]=cert; keyStore.setKeyEntry("private", key, "password".toCharArray(), chain); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); }catch(Exception ex){ ex.printStackTrace(); }
A RSA private key is generated with the CertAndKeyGen and the associated certificate is also generated. Then the key entry is stored in the keyStore by calling keyStore.setEntry(). Don't forget to save the keyStore by calling keyStore.store(), otherwise the entry will be lost when the program exits.
Store certificate
PKCS12 keystore also allows to store certificate by itself without the corresponding private key stored. To store the certificate, the KeyStore.setCertificateEntry() can be called.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); // keyStore.load(new FileInputStream("output.p12"),"password".toCharArray()); keyStore.load(null, null);; CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA"); gen.generate(1024); X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=ROOT"), (long)365*24*3600); keyStore.setCertificateEntry("cert", cert); keyStore.store(new FileOutputStream("output.p12"), "password".toCharArray()); }catch(Exception ex){ ex.printStackTrace(); }
The stored certificate can be extracted by calling KeyStore.getCertificate() with the supplied alias. For example :
Certificate cert = keyStore.getCertificate("cert");
Load private key
One difference between PKCS12 keystore and other keystores such as JKS is that PKCS12's private key can be extracted without NullPointerException. The private key can be correctly extracted with the correct password provided.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); Key pvtKey = keyStore.getKey("private", "password".toCharArray()); System.out.println(pvtKey.toString()); } catch (Exception ex){ ex.printStackTrace(); }
The output of the above code is:
sun.security.rsa.RSAPrivateCrtKeyImpl@ffff2466
Load certificate chain
If a certificate chain is stored in a keystore, it can be loaded by calling KeyStore.getCertificateChain(). Below code is used to extract the associated certificate chain for the associated private key.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); Key pvtKey = keyStore.getKey("private", "password".toCharArray()); System.out.println(pvtKey.toString()); java.security.cert.Certificate[] chain = keyStore.getCertificateChain("private"); for(java.security.cert.Certificate cert:chain){ System.out.println(cert.toString()); } } catch (Exception ex){ ex.printStackTrace(); }
The output is :
[ [ Version: V3 Subject: CN=ROOT Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Sun RSA public key, 1024 bits modulus: 107262652552256813768678166856978781385254195794582600239703451044252881438814396239031781495369251659734172714120481593881055888193254336293673302267462500060447786562885955334870856482264000504019061160524587434562257067298291769329550807938162702640388267016365640782567817416484577163775446236245223552189 public exponent: 65537 Validity: [From: Mon Jan 05 13:03:29 SGT 2015, To: Tue Jan 05 13:03:29 SGT 2016] Issuer: CN=ROOT SerialNumber: [ 5e5ca8a4] ] Algorithm: [SHA1withRSA] Signature: 0000: 22 21 BF 73 A6 6D 12 9B F7 49 6C 0E B3 50 6A 9D "!.s.m...Il..Pj. 0010: FA 30 43 22 32 FF 54 95 80 2E B3 8B 6F 59 D4 B5 .0C"2.T.....oY.. 0020: 6C A6 AE 89 B7 18 9A A8 35 7D 65 37 BF ED A3 F4 l.......5.e7.... 0030: E7 DB 5D 5F 9B DA 4B FA 39 04 9B 4D DB C2 3E FA ..]_..K.9..M..>. 0040: 3B C2 63 F8 1E BE 03 F3 BD 1C D4 8A 8E 3C 51 68 ;.c..........
To understand how to create certificate chain in Java, please refer to Generate certificate in Java -- Certificate chain.
Load certificate
Loading certificate is simple as well by calling KeyStore.getCertificate(), if the alias supplied is mapping to a certificate chain, only the leaf certificate will be returned.
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); java.security.cert.Certificate cert = keyStore.getCertificate("private"); System.out.println(cert); } catch (Exception ex){ ex.printStackTrace(); }
The output looks like:
[ [ Version: V3 Subject: CN=ROOT Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5 Key: Sun RSA public key, 1024 bits modulus: 107262652552256813768678166856978781385254195794582600239703451044252881438814396239031781495369251659734172714120481593881055888193254336293673302267462500060447786562885955334870856482264000504019061160524587434562257067298291769329550807938162702640388267016365640782567817416484577163775446236245223552189 public exponent: 65537 Validity: [From: Mon Jan 05 13:03:29 SGT 2015, To: Tue Jan 05 13:03:29 SGT 2016] Issuer: CN=ROOT SerialNumber: [ 5e5ca8a4] ] Algorithm: [SHA1withRSA] Signature: 0000: 22 21 BF 73 A6 6D 12 9B F7 49 6C 0E B3 50 6A 9D "!.s.m...Il..Pj. 0010: FA 30 43 22 32 FF 54 95 80 2E B3 8B 6F 59 D4 B5 .0C"2.T.....oY.. 0020: 6C A6 AE 89 B7 18 9A A8 35 7D 65 37 BF ED A3 F4 l.......5.e7.... 0030: E7 DB 5D 5F 9B DA 4B FA 39 04 9B 4D DB C2 3E FA ..]_..K.9..M..>. 0040: 3B C2 63 F8 1E BE 03 F3 BD 1C D4 8A 8E 3C 51 68 ;.c..........
Import and export keys and certificates
The PKCS12 keystore can be used to import and export keys and certificates. Since private keys can be extracted from PKCS12 keystores, so the entries can be exported from PKCS12 keystore and then be imported to other keystore types such as JKS.
Below code snippet demonstrate export a private key entry from PKCS12 and import it to JSK keystore:
try{ KeyStore keyStore = KeyStore.getInstance("PKCS12"); keyStore.load(new FileInputStream("output.p12"), "password".toCharArray()); Key pvtKey = keyStore.getKey("private", "password".toCharArray()); java.security.cert.Certificate[] chain = keyStore.getCertificateChain("private"); KeyStore jksStore = KeyStore.getInstance("JKS"); jksStore.load(null, null);; jksStore.setKeyEntry("jksPrivate", pvtKey, "newpassword".toCharArray(), chain); jksStore.store(new FileOutputStream("output.jks"), "password".toCharArray()); } catch (Exception ex){ ex.printStackTrace(); }
PKCS12 keystore is the recommended keystore type if you want to have a portable keystore type and you may want to use it with other non-Java libraries in the future.
For more information on other keystores, please refer to Different types of keystore in Java -- Overview
How can I choose the extension, p12 or pfx, for a PKCS12 keystore? Is there a mandatory rule?
Another question about the KeyStore.setXXX() method. Why does it use setXXX() method name instead of addXXX() to add new entry? Any special thought?