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.
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!