Different types of keystore in Java -- PKCS12

  Pi Ke        2015-01-04 21:08:49       80,385        10    

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

JAVA  PKCS12  KEYSTORE  TUTORIAL 

       

  RELATED


  10 COMMENTS


lintao [Reply]@ 2015-01-15 01:42:54

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?

Pi Ke [Reply]@ 2015-01-15 06:14:30

There is no difference for choosing p12 or pfx as the file extension. Actually, you can choose specify other extension name as well. The PKCS12 keystore will parse the actual contents not based on the file extension. But p12 is more preferable as it is the standard name for PKCS12. pfx was created by Microsoft which means Personal Information Exchange. It is the predecessor of p12.

The reason why using setXXX() instead of addXXX() is because setXXX() can mean both addXXX() and updateXXX(). So it's a combination of both add and update. This saves extra method creation.

Hope it helps.

lintao [Reply]@ 2015-01-15 20:29:29

Thanks! For the setXXX() method name question, I acctually want to know if there is any story why it's chosen to combine the 2 functions( add and update) in one method. In my understanding, using separated methods make the API more clear for others to use. Just curious about if there is any restriction or special consideration. :)

Pi Ke [Reply]@ 2015-01-16 04:46:15

Hmm, actually I think the reason is that since there is a getXXX(0 method, as a user, s/he would naturally expects there should be a setXXX() method. So a setXXX() is created. And later, the setXXX() can achieve both the update and add capability. So no updateXXX() and addXXX() are needed anymore. Want to keep the API concise.

mohamed [Reply]@ 2015-06-07 06:36:07

How i can store an existing certificate .p12 (located in the root )in a keystore which i can use lately to sign a pdf document !!!

Pi Ke [Reply]@ 2015-06-07 07:07:47

In this case, I think your best friend is the keytool, it can be used to import an existing certificate into a keystore. See docs here : docs.oracle.com/javase/8/docs/technotes/tools/unix/keytool.html

You can also import it progrmammtically by reading the certificate data and generate the certificate object with the proper methods provided by Java API. I amy write a post to demonstrate it later.

mohamed [Reply]@ 2015-06-07 08:05:46

thank u , i need this very much because i m trying from long time to read a certificate programatically and use it in signing a pdf document so please help me it's urgent .

Pi Ke [Reply]@ 2015-06-08 07:06:43

I have written a post which demonstrates how to read a certificate file programmatically and then store it in a p12 file. www.pixelstech.net/article/1433764001-Generate-certificate-from-cert-file-in-Java

BTW : What do you mean by "an existing certificate .p12" in your previous comment? As far as I know p12 is an extension of PKCS12 keystore but not a certificate extension.

Jon [Reply]@ 2017-03-30 08:36:43

Great documentation.  I've got a use case where I'd like to programmatically convert a a PKCS12 to a JKS.  Any thoughts?

Ke Pi [Reply]@ 2017-03-31 19:12:31

Technically this is doable but comes with some limitation. Since JKS cannot have secret key stored while PKCS12 can have it. Hence when converting the PKCS12 to JKS, only private keys and certificates can be transferred. 

The steps would be first load the PKCS12 keystore as mentioned in this post and read all key entries and certificate chains out, then create a JKS keystore/load existing JKS keystore as described in Different types of keystore in Java -- JKS. Then put those entries into the created/loaded JKS keystore.



  RANDOM FUN

Client server communication