In this post let's explore the fundamentals of a Java Key Store (JKS) and a Public Key Certificate.
The key store is a storage facility to store cryptographic keys and certificates used for SSL communications.
There are different types of key stores available in Java depending on the type of entries the Keystore can store and how the Keystore can store the entries. Java Key Store (JKS) is one of the widely used key store types available in Java.
Each key store can hold a unique set of keys depending on the key store type.
In General, A key store contains the following types of keys,
Private Key Entry/Entries (With a certificate chain for the corresponding Public Key Entry/Entries).
Trusted Certificates (Third-party Public keys + Certificate Chains)
Secret Key Entry/Entries
Private Key (PrivateKeyEntry):
It is the key used for asymmetric encryption and signing a digital signature. The private key entry is password protected. Generally, a JKS type of key store can have only one private key entry in a key store file.
Some key store types, allow having multiple private key entries in a single key store.
Along with the Private Key entry, a certificate chain for the corresponding Public Key entry also will be there.
Trusted Certificates (TrustedCertificateEntry): It holds the third-party Public Keys used for asymmetric encryption and digital signature validation along with the Trusted Certificate Chains.
A key store can hold any number of Trusted Certificates.
Secret Key (SecretKeyEntry): It is the key used for symmetric encryption.
Generally, a JKS doesn't have SecretKey entries.
There are different types of Java key stores depending on the entries a key store can store and how these entries are stored in the key store. Some of the popular Java key stores are JKS, JCEKS, PKCS12, DKS, and BKS.
Let's explore some of these Java key store types and their differences.
Java Key Store (JKS)
"Is a repository of security certificates – either authorization certificates or public key certificates – plus corresponding private keys, used for instance in SSL encryption".
Implementation can be found at java.security.KeyStore
This key store is Java-specific and usually has an extension of .jks (eg: wso2carbon.jks)
According to the Java specification, JKS key store manages different types of entries.
Each type of entry implements the KeyStore.Entry interface.
Three basic KeyStore.Entry implementations are provided:
KeyStore.PrivateKeyEntry
KeyStore.SecretKeyEntry
KeyStore.TrustedCertificateEntry
Please refer to the latest Java 14 documentation for further details https://docs.oracle.com/en/java/javase/14/docs/api/java.base/java/security/KeyStore.html
Features of JKS:
Since this is a Java-specific key store cannot be used with other programming languages.
It doesn't support storing Secret Key entries.
Only a single Private Key entry can be stored in a key store file, multiple Private Key entries are not allowed.
Private Key entries stored in the JKS key store can't be extracted.
Now let's see how we can implement a key store using Java.
Java provides few options to work with key stores :
Writing a Java code
Using the inbuilt tool named "keytool" which comes along with the JDK.
Keytool is a command-line tool that is shipped along with the JDK. It can be used to create keystore, generate keys, import and export certificates, etc.
Note that keytool is only available with Java JDK installations and not with Java JRE runtimes.
Creating a JKS key store with Keytool
Let's create a JKS key store with Private/Public key pairs using the key tool.
keytool -genkey -v -keystore sample_keystore.jks -alias sample -keyalg RSA -keysize 2048 -validity 365
The above command will create a JKS key store named "sample_keystore.jks" which contains, a 2048 bit Private/Public key pair aliased "sample" created using the RSA cryptographic algorithm and a self-signed certificate (SHA256withRSA) with a validity period of 365 days.
While executing the above command to generate the key store, you will be prompted for the "CN", "key store password" and "key password" as below,
CN should generally match the hostname of the server.
Generally is good to use the same password for the keystore password and the key password (for the alias "sample").
Some servers like Tomcat Catalina Server by default use the "key store password" as the "key password" for the connectors if not explicitly mentioned.
Configuring Tomcat Catalina Server with the JKS key store
If you are using a Tomcat Catalina Server and using separate "key password" and "key store password", you need to explicitly configure it in the server.xml file of the Tomcat Server found in the <TOMCAT_HOME>/conf/ directory.
<!-- Define an SSL Coyote HTTP/1.1 Connector on port 8443 -->
<Connector
protocol="org.apache.coyote.http11.Http11NioProtocol"
port="8443" maxThreads="200"
scheme="https" secure="true" SSLEnabled="true"
keystoreFile="${path_to_keystore}/sample_keystore.jks"
keystorePass="keystore_password"
keyPass="key_password"
clientAuth="false" sslProtocol="TLS"/>
Note that keystorePass is used to specify the "key store password" while keyPass is used to specify the "key password".
Please refer to the Tomcat documentation https://tomcat.apache.org/tomcat-7.0-doc/ssl-howto.html#Prepare_the_Certificate_Keystore
Listing the entries in the JKS key store
Now let's list the key store entries of the "sample_keystore.jks"
keytool -list -v -keystore sample_keystore.jks
The entries will look like below with a PrivateKeyEntry and a Certificate chain
Converting the JKS key store to PKCS12 key store
As you would have noticed when we created a JKS key store using the key tool, there was this following Warning message,
Warning:
The JKS keystore uses a proprietary format.
It is recommended to migrate to PKCS12 which is an industry standard format.
JKS and JCEKS key stores are in Java proprietary format, and it is recommended to use PKCS12 format key store as it is the standard industry format. Later in this post, we will discover more on JCEKS and PKCS12 key store formats.
Let's convert the "sample_keystore.jks" to a PKCS12 key store with .pfx extension.
keytool -importkeystore -srckeystore sample_keystore.jks -destkeystore sample_keystore.pfx -deststoretype pkcs12
A successful conversion will result in the following outcome
Exporting the Public key certificate from the JKS key store in a DER format
Now, let's try to export the Public key certificate from the "sample_keystore.jks" with alias "sample" to "sample.crt" Public certificate file.
keytool -exportcert -alias sample -file sample.crt -keystore sample_keystore.jks
Results will look like below,
If you try to view the content of the "sample.crt" file it would be in a non-human readable format. This is because the Public certificate generated is in a binary encoded certificate.
Public certificates can have content with the following two Encodings :
Binary DER-encoded certificates (Non-human readable).
ASCII (Base64) PEM-encoded certificates (Human-readable).
DER: Distinguished Encoding Rules (Subset of Basic Encoding Rules of ASN.1)
PEM: Privacy-Enhanced Mail
Note: Encoding is a process of transforming the data into a different format using a publicly available schema. It can easily be reversed. The purpose of encoding is not to keep the information secure rather it is to transform the data into a format safe to be consumed by different systems.
Public certificate can have the following common file extensions:
".crt" extension: Certificate may be encoded as binary DER Or ASCII PEM.
".der" extension: Certificate encoded with binary DER.
.pem extension: Certificate encoded with ASCII (Base64).
So, to get the Public certificate in a human-readable format we can export it either using PEM encoded .crt extension Or PEM encoded .pem extension.
keytool -exportcert -alias sample -file sample.pem -keystore sample_keystore.jks
Exporting the Public certificate with .pem extension as above will still result in a binary encoded Public key certificate.
This is because, regardless of the -file format provided, keytool's -exportcert command will by default always create the Public key certificate as a binary encoded certificate.
Export the Public key certificate from the JKS key store in a PEM format
In order to export the Public certificate from the JKS key store in a PEM format, we need to pass an additional -rfc argument while using the -exportcert command.
keytool -exportcert -alias sample -file sample.pem -keystore sample_keystore.jks -rfc
Content of the "sample.pem" Public certificate will look like below,
As you can see from above PEM format Public certificate has the -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- tags.
Import a third-party Public key certificate to the JKS key store
Let's try to import a third-party's Public certificate as a Trusted CA Certificate.
I have extracted the "wso2carbon.pem" file from the "wso2carbon.jks" (default key store shipped with WSO2 Products). Let's try to import "wso2carbon.pem" Public certificate to the "sample_keystore.jks"
keytool -import -trustcacerts -alias wso2carbon -file wso2carbon.pem -keystore sample_keystore.jks
Results will look like below,
Export the Private key entry from the JKS key store
By default, the JKS key store type doesn't support exporting the Private key entry using the key tool. But it can be achieved by converting the JKS key store to a PKCS12 key store.
Let's explore how we can export the Private key entry from the "sample_keystore.jks",
Convert the JKS "sample_keystore.jks" key store to a PKCS12 key store "sample_keystore.pfx". We have already done it.
Export the Private key entry from the PKCS12 key store using the openssl into "sample_keystore_private_key.pem" file using PEM encoding
openssl pkcs12 -in sample_keystore.pfx -nodes -nocerts -out sample_keystore_private_key.pem
pkcs12 => Key store format we are using.
-nodes => No DES format so the Private key won't be encrypted.
-nocerts => No certificates at all will be output.
Exported Private key entry will look like the below,
We need to provide the "sample_keystore.pfx" key store password as the Import Password.
As you can see the Private key entry is with the PEM encoding contains -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- tags. The Private key is not encrypted.
Convert the Private key entry from PEM encoding to DER encoding
By default, OpenSSL command saves the Private key entry (encrypted/unencrypted) to the file using the PEM encoding. We can convert the PEM format to DER format using the below command,
openssl pkey -in sample_keystore_private_key.pem -inform pem -out sample_keystore_private_key.der -outform der
Exported Unencrypted Private key in DER format will look like below,
As you can see from above "sample_keystore_private_key.der" contains the unencrypted private key entry encoded in binary DER format.
Export the Triple-DES Encrypted Private key entry from the JKS key store
By default, if we don't specify the encryption algorithm Or pass -nodes command, OpenSSL will encrypt the Private key entry using a 3DES encryption algorithm.
Convert the JKS key store to the PKCS12 key store.
openssl pkcs12 -in sample_keystore.pfx -nocerts -out sample_keystore_private_key_default_enc.pem
As you can see from the above "sample_keystore_private_key_default_enc.pem" contains the Triple-DES encrypted (3DES) private key entry. Please refer to the link for further details on Triple-DEs encryption. Note the tags with -----BEGIN ENCRYPTED PRIVATE KEY----- and -----END ENCRYPTED PRIVATE KEY----- which shows us the Private key entry is encrypted.
Apart from using the default 3DES algorithm, we can export the encrypted Private key entry by specifying one of the following encryption algorithms.
AES (aes128, aes192, and aes256) => -aes128, -aes192, -aes256
DES => -des
3DES => -des3
The default is triple DES.
Export the AES256 Encrypted Private key entry from the JKS key store
Let's now export the AES256 encrypted Private key entry by, passing the encryption algorithm as below,
Convert the JKS key store to the PKCS12 key store.
openssl pkcs12 -in sample_keystore.pfx -aes256 -nocerts -out sample_keystore_private_key_aes256.pem
Note that we are passing an additional -aes256 argument.
Exported AES256 encrypted private key will look like below,
Export the RSA Private key entry from the JKS key store
Convert the JKS key store to the PKCS12 key store.
As you may have noticed that the Private key entry contains Bag Attributes and Key Attributes apart from the Encrypted/Un-encrypted Private key entry.
This is because the private key entry we extracted has a PrivateKeyInfo ASN.1 structure as defined in the PKCS#8 (RFC 5208). PrivateKeyInfo structure allows the Private key entry to contain :
A Private key
An OID that identifies the key type
Newer versions of OpenSSL generate Private key with PKCS#8 format which has -----BEGIN PRIVATE KEY----- and -----END PRIVATE KEY----- tags.
At the start of this blog, we created the "sample_keystore.jks" with an RSA 2048 Private and Public key pair. So we need to export the RSA Private key from the key store.
Also, we need to use the RSA Private key for SSL communication.
RSAPrivateKey ASN.1 structure defined in PKCS#1 (RFC 3447) allows the Private key entry to only contain the Private key eliminating the "Bag Attributes" and "Key Attributes".
Older versions of OpenSSL generate Private key entry in PKCS#1 format with -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY----- tags.
Since we are using the newer version of OpenSSL to extract the Private key entry, we got the Private key entry in PKCS#8 format with OID.
In order to generate the Private key entry with "RSAPrivateKey" let's use the below command,
openssl pkcs12 -in sample_keystore.pfx -nodes -nocerts | openssl rsa -out sample_keystore_private_key_rsa.pem
RSA Private key entry will look like below,
As you can see from the above the Private key entry only contains the private key without other attributes.
Note the tags with -----BEGIN RSA PRIVATE KEY----- and -----END RSA PRIVATE KEY-----
which shows us the Private key entry is an RSAPrivateKey. The RSA private key is not encrypted.
Convert the PKCS#8 Private key to RSA Private key
Now let's consider we already have a Private key entry in PKCS#8 format and we need to convert it to an RSA Private key.
Let's use "sample_keystore_private_key.pem", unencrypted PKCS#8 format private key entry and convert it to RSA Private key.
Let's use the below command,
openssl rsa -in sample_keystore_private_key.pem -out sample_keystore_private_key_rsa_converted.pem
The converted "sample_keystore_private_key_rsa_converted.pem" will look like below,
Convert the PKCS#8 Private key to Triple DES Encrypted RSA Private key
The RSA Private Key we exported Or converted is not encrypted and poses a security threat. Let's see how we can encrypt the RSA private key while converting.
openssl rsa -in sample_keystore_private_key.pem -des3 -out sample_keystore_private_key_rsa_des3.pem
As you can see from the above, the RSA Private key is encrypted.
Note the Proc-Type: 4,ENCRYPTED attribute which shows us the RSA private key entry is encrypted.
Import a Private key Entry to a JKS key Store
We can't directly import Private key entry to a JKS type key store. We can import the Private key to a PKCS12 key store and convert it to a JKS key store.
Generating a JKS key store with Secret key entry
According to the JKS key store specification, we can't store non-PrivateKeys in a "JKS" type key store.
Let's verify the above statement by trying to generate a JKS key store with a Secret key as below,
keytool -genseckey -alias secretKey -keypass secretKey -keyalg AES -keysize 256 -keystore secretKey.jks -storepass secretKey -storetype JKS -v
-genseckey option is used to generate the Secret key.
-keyalg argument is used to pass the Secret key algorithm.
Common secret key algorithms include DES, 3DES, and AES.
The above command will result in a key tool error like below,
As you can see even though the 256-bit AES secret key is generated it cannot be stored in the JKS type key store.
Similarly, we can't import a Secret key entry to a JKS key store.
We have explored different aspects of the JKS key store in this blog. We will continue to explore the other types of key stores in future blogs.
Thank you for reading.
Cheers!!!
Comentarios