Different types of keystore in Java -- JKS

  Pi Ke        2014-09-05 20:21:51       46,879        3         

JKS is Java Keystore, a proprietary keystore type designed for Java. It can be used to store private keys and certificates used for SSL communication, it cannot store secret keys however. The keytool shipped with JDKs cannot extract private keys stored on JKS. This type of keystore usually has an extension of jks.

Next we will show how to operate the JKS keystore with pure Java code.

Create JKS keystore

The simplest method to create a JKS keystore to create an empty keystore. We can first get an instance of KeyStore and then load a null keystore. After loading the null keystore, we just need to call KeyStore.store() with the keystore name and password of the keystore.

Below is a simple demo:

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(null,null);
	
	keyStore.store(new FileOutputStream("mytestkey.jks"), "password".toCharArray());
}catch(Exception ex){
	ex.printStackTrace();
}

Post execution of above call, you will see a keystore named mytestkey.jks in current working directory. Now the keystore is empty without any entries.

Store private key

Now let's store one private key and its associated certificate chain into the keystore. Note we can not store a private key without an associated certificate chain into a keystore using JDK. With some other library or native libraries, you may be able to store a private key without associated certificate chain.

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	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("mykey", key, "password".toCharArray(), chain);
	
	keyStore.store(new FileOutputStream("mytestkey.jks"), "password".toCharArray());
}catch(Exception ex){
	ex.printStackTrace();
}

First, we will create a private key and a self signed certificate and then call KeyStore.setKeyEntry() with the specified alias, key, the password for the key and its associated certificate chain. Remember we need to call KeyStore.store() to store the key into the keystore.

The alias is the label of the entry so that it can be found easily later.

Store certificate

We can store certificate on JKS keystore. The certificate to be store should be a X509Certificate. It can be stored on the keystore without associated private key. This process is similar to storing private key.

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	CertAndKeyGen gen = new CertAndKeyGen("RSA","SHA1WithRSA");
	gen.generate(1024);
	
	X509Certificate cert=gen.getSelfCertificate(new X500Name("CN=SINGLE_CERTIFICATE"), (long)365*24*3600);
	
	keyStore.setCertificateEntry("single_cert", cert);
	
	keyStore.store(new FileOutputStream("mytestkey.jks"), "password".toCharArray());
}catch(Exception ex){
	ex.printStackTrace();
}

Loading private key

After storing the keys, we can also load the entries inside the keystore. Here we are saying to load private key, actually it's not the case here, as we described earlier, the private key cannot be extracted from JKS using Java. Here we actually extract the certificate chain of the private key.

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	Key key = keyStore.getKey("mykey", "password".toCharArray());
//			System.out.println("Private key : "+key.toString());   //You will get a NullPointerException if you uncomment this line
	
	java.security.cert.Certificate[] chain =  keyStore.getCertificateChain("mykey");
	for(java.security.cert.Certificate cert:chain){
		System.out.println(cert.toString());
	}
}catch(Exception ex){
	ex.printStackTrace();
}

Note the commented line, the key will be null as expected. We can get the certificate chain as normal though.

[
[
  Version: V3
  Subject: CN=ROOT
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 90980299845597512779139009881469177009407272139633139241921529845092210461181243924599150259446249079941561941533303439718936138867375776965995893255358889228584415558006141961051402385279285497775776996780406808976543439543789816486513982581378223575354716191394304768315366544413052547926792470794374067383
  public exponent: 65537
  Validity: [From: Sat Sep 06 09:57:28 CST 2014,
               To: Sun Sep 06 09:57:28 CST 2015]
  Issuer: CN=ROOT
  SerialNumber: [    206b697b]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 53 6A FD FE E6 3A 5E 6E   A6 43 C4 F4 D1 56 D4 08  Sj...:^n.C...V..
0010: 7E 3B 8B 73 68 71 56 AB   96 FE 24 E7 2D DC 04 BB  .;.shqV...$.-...
0020: 14 B0 C6 71 8D F0 3E EC   FE D8 5B BB 8C 0F 55 63  ...q..>...[...Uc
0030: 2B 38 8E 45 F1 2D F0 BB   8C 6D 13 A8 11 37 E1 FA  +8.E.-...m...7..
0040: 77 AF C7 73 72 2B 40 4F   74 32 F6 3C 24 E6 AB ED  w..sr+@Ot2.<$...
0050: 2C 6F 19 2E DC 58 5F CB   75 62 40 2F 3E BE 59 99  ,o...X_.ub@/>.Y.
0060: C0 1F 7A 70 15 AF C3 66   B3 4F C9 11 C3 45 59 EF  ..zp...f.O...EY.
0070: 36 F4 1C C9 9B FA 5E 43   A0 28 DB 07 0D F2 53 6E  6.....^C.(....Sn

]

Loading certificate

This is similar to loading private key, we need to pass the alias of the certificate we want to extract.

try{
	KeyStore keyStore = KeyStore.getInstance("JKS");
	keyStore.load(new FileInputStream("mytestkey.jks"),"password".toCharArray());
	
	java.security.cert.Certificate cert = keyStore.getCertificate("single_cert");
	
	System.out.println(cert.toString());
}catch(Exception ex){
	ex.printStackTrace();
}

The output will be:

[
[
  Version: V3
  Subject: CN=SINGLE_CERTIFICATE
  Signature Algorithm: SHA1withRSA, OID = 1.2.840.113549.1.1.5

  Key:  Sun RSA public key, 1024 bits
  modulus: 99756834215197288877309915243024788596281418171661241282881476656110879586349799740269767889529808199104172091786860877280382867461569439907754755558759387462421169749111354565793974372777424046360810758009149155148290676527032833774084635148674232352006810533640038723102562578516643345287042787777951043863
  public exponent: 65537
  Validity: [From: Sat Sep 06 10:14:33 CST 2014,
               To: Sun Sep 06 10:14:33 CST 2015]
  Issuer: CN=SINGLE_CERTIFICATE
  SerialNumber: [    6943e549]

]
  Algorithm: [SHA1withRSA]
  Signature:
0000: 35 58 70 96 F4 35 82 2A   95 9F BB 31 02 6E 7C 29  5Xp..5.*...1.n.)
0010: 4A FE AF EB 2D B5 3A A7   C7 9D 4C 9A 34 2C 5C 46  J...-.:...L.4,\F
0020: C2 82 A8 AC 1A C0 98 A5   67 21 74 7B 1E E2 E5 AC  ........g!t.....
0030: DE B2 1D 87 BE 16 45 9B   D0 2A D3 2B F6 E1 4B 35  ......E..*.+..K5
0040: 27 8B A7 0A EF F2 07 41   90 A6 69 07 BE 87 C5 B1  '......A..i.....
0050: 54 DE DB A2 5A 41 47 3B   3F A7 74 6F 5C C8 8D B4  T...ZAG;?.to\...
0060: C8 65 2B 0F 8E 94 A8 80   C7 8B B5 78 FA C2 9C ED  .e+........x....
0070: 8E EC 28 E4 8E 62 A1 59   6A BC 37 7B 0D FC C7 AF  ..(..b.Yj.7.....

]

Import keys and certificates

This process is actually very simple, we first need to load the keystore where the certificate to be imported. Then we also need to load another keystore where we need to import certificate to. Next, we need to get the certificate from source keystore and put it into the destination keystore.

Since we cannot extract private key from JKS, so we can only import certificate to JKS. However, we can extract private keys from other types of keystore(PKCS12) and then store them in JKS keystore.

One final piece of information. Oracle provides two versions of JKS keystore : case sensitive and case insensitive. When calling KeyStore.getInstance("JKS"), a case insensitive version of JKS instance is created, when KeyStore.getInstance("CaseExactJKS") is called, a case sensitive version of JKS instance will be created. Usually case insensitive is recommended as an user should distinguish different entries with different alias names instead of different alias name cases. For more information about case sensitivity, please refer to this post.

We will cover other type of keystore in future posts.

DEMO  EXAMPLE  KEYSTORE  JKS 

       

  RELATED


  3 COMMENTS


lintao [Reply]@ 2015-01-12 03:43:48

Pi Ke,

I have a comment about this code in  "Loading private key" part:

Key key = keyStore.getKey("alias", "password".toCharArray());

By using "alias", I think you're trying to say private key should be extract by its alias. But, in "Store Private Key" part, the private key has been stored as "mykey". So, to be consistent, I think the upper code should use "mykey"  although the value is null too due to JKS type.  Is my understanding right?  Thanks!

NightCat [Reply]@ 2015-01-12 07:11:36

Yes. You are correct. The alias name here actually doesn't matter. But to be consistent, I have updated it to mykey. Thank you for pointing this out.

Cat Mucius [Reply]@ 2020-02-21 15:52:41

If value of the private key cannot be extracted from a JKS keystore, then how can Cipher object of any provider use it?

Is there some "backdoor" function utilized?



  RANDOM FUN

Documentation is like sex