Exploring SSL/TLS - Part 3
Bhaskar S | 10/28/2017 |
Overview
In Part-2, we explored a simple echo client and a server, by creating and using a self-signed SSL/TLS server certificate for secure communication via the default SSL/TLS setup using Java system properties.
In this article, we will explore how to perform all the necessary setup for SSL/TLS communication between the client and the server using the lower level JSSE APIs.
Terminology
In this section, we will list and briefly describe some of the terms referred to in this article.
Term | Description |
---|---|
java.security.KeyStore | a class that implements an in-memory representation of the keystore for storing the keys and the associated identify certificates for a subject (either the client or the server) |
javax.net.ssl.SSLContext | a core engine class that implements the SSL/TLS protocol and acts factory for creating secure sockets |
javax.net.ssl.KeyManager | a class that uses the KeyStore to determine which private key entry (and the corresponding certificate) to use for sending to the remote peer for authentication based on the chosen algorithm |
javax.net.ssl.KeyManagerFactory | a factory class for creating an instance of KeyManager |
javax.net.ssl.TrustManager | a class that uses the KeyStore to determine which public key entry (and the corresponding certificate) to use for validating the peer identity certificate |
javax.net.ssl.TrustManagerFactory | a factory class for creating an instance of TrustManager |
Hands-on SSL/TLS using Java - Part 3
We will leverage the server keystore server.ks and the client truststore client.ts we created in Part-2 to demonstrate the use of SSL/TLS for secure communication between the client and the server using the lower level JSSE APIs.
The following is the simple SSL enabled echo server using the lower level JSSE APIs:
Let us explain and understand some of the methods used in the SecureEchoServer3 code shown above.
The KeyStore.getDefaultType() static method returns the default type of the KeyStore. In the standard Java implementation, it is of type JKS, which is an acronym for Java KeyStore.
The KeyStore.getInstance(String) static method returns an instance of a KeyStore of the specified type.
The load(InputStream, char[]) instance method initializes the KeyStore with keys and certificates from the specified keystore file as an input stream. The second argument is the character array of the password to unlock the keystore file.
The KeyManagerFactory.getDefaultAlgorithm() static method returns the name of the default algorithm used. In the standard Java implementation, it is SunX509.
The KeyManagerFactory.getInstance(String) static method returns an instance of a KeyManagerFactory that implements the specified algorithm.
The init(KeyStore, char[]) instance method initializes the KeyManagerFactory with the specified instance of the KeyStore. The second argument is the character array of the password to unlock a key entries from the KeyStore instance.
The getKeyManagers() instance method returns an array of KeyManager instances, one for each type of key entry in the KeyStore.
The SSLContext.getInstance(String) static method returns an instance of an SSLContext that implements the specified protocol. We have chosen TLSv1.2 as it is the most current and secure version.
The init(KeyManager[], TrustManager[], null) instance method initializes the instance of the SSLContext with the specified array of KeyManager and TrustManager. In our example, we pass in a null for the array of TrustManager since the server does not need a truststore.
Open a new Terminal window, and execute the following command to start the SSL/TLS echo server with the appropriate keystore:
java -cp build/classes com.polarsparc.pki.SecureEchoServer3 ./resources/server.ks server.123
The following should be the typical output:
Echo (server-3), default key store type: jks Echo (server-3), default key manager algorithm: SunX509 Echo (server-3) started on 8443
The following is the simple SSL enabled echo client using the lower level JSSE APIs:
Let us explain and understand some of the methods used in the SecureEchoClient3 code shown above.
The TrustManagerFactory.getDefaultAlgorithm() static method returns the name of the default algorithm used. In the standard Java implementation, it is PKIX.
The TrustManagerFactory.getInstance(String) static method returns an instance of a TrustManagerFactory that implements the specified algorithm.
The init(KeyStore, char[]) instance method initializes the TrustManagerFactory with the specified instance of the KeyStore. The second argument is the character array of the password to unlock a key entries from the KeyStore instance.
The getTrustManagers() instance method returns an array of TrustManager instances, one for each type of key entry in the KeyStore.
The SSLContext.getInstance(String) static method returns an instance of an SSLContext that implements the specified protocol. We have chosen TLSv1.2 as it is the most current and secure version.
The init(KeyManager[], TrustManager[], null) instance method initializes the instance of the SSLContext with the specified array of KeyManager and TrustManager. In our example, we pass in a null for the array of KeyManager since the client does not need a keystore.
Open another Terminal window, and execute the following command to start the SSL/TLS echo client:
java -cp build/classes com.polarsparc.pki.SecureEchoClient3 ./resources/client.ts client.123 "Hello SSL World"
The following should be the typical output:
Echo (client-3), default key store type: jks Echo (client-3), default trust manager algorithm: PKIX
From the terminal window where the server was started, we see the following output:
-> Echo (server-3): Hello SSL World
Mutual Authentication
The following are the basic steps involved in the two-way SSL/TLS handshake:
The SSL/TLS client sends a 'client hello' message to the SSL/TLS server, specifying the supported protocols (SSLv3, TLSv1, TLSv1.1, TLSv1.2), and the supported list of cipher suites
The SSL/TLS server responds with a 'server hello' message, indicating the selected protocol and the cipher suite from the choice presented by the SSL/TLS client. In addition, the SSL/TLS server also sends its digital certificate
The SSL/TLS server requests the client to send its digital certificate for verification
The SSL/TLS client verifies the digital certificate presented by the SSL/TLS server and extracts the public key
The SSL/TLS server verifies the digital certificate presented by the SSL/TLS client and extracts the public key
Once the server certificate is verified by the client and the client certificate by the server (mutual), the SSL/TLS client sends a session master key (shared secret key) that is encrypted with the server's public key
The SSL/TLS client sends a 'finished' message that is encrypted with the session key to the SSL/TLS server, indicating the client side of the SSL/TLS handshake is completed
The SSL/TLS server sends a 'finished' message that is encrypted with the session key to the SSL/TLS client signalling the server side of the SSL/TLS handshake is completed
The SSL/TLS client and server can now securely exchange data that is encrypted with the session key for the duration of the session
The following diagram illustrates the above steps in a pictorial form:
Setup
In this section, we will create a digital certificate for the client that will be verified by the server to demonstrate the two-way SSL/TLS mutual authentication.
The private key and the identify certificate for the client will be stored in a keystore file called client.ks that will be protected with a keystore password. The certificate will be valid for 365 days.
To create the client certificate using the keytool, execute the following command:
keytool -genkeypair -alias client -keystore ./resources/client.ks -keyalg rsa -keysize 2048 -validity 365
The following should be the typical output:
Enter keystore password: client.123 Re-enter new password: client.123 What is your first and last name? [Unknown]: client What is the name of your organizational unit? [Unknown]: testing What is the name of your organization? [Unknown]: polarsparc What is the name of your City or Locality? [Unknown]: na What is the name of your State or Province? [Unknown]: ny What is the two-letter country code for this unit? [Unknown]: us Is CN=client, OU=testing, O=polarsparc, L=na, ST=ny, C=us correct? [no]: yes
To self-sign the client certificate from the keystore client.ks using the keytool, execute the following command:
keytool -selfcert -alias client -keystore ./resources/client.ks -validity 365
The following should be the typical output:
Enter keystore password: client.123
For the server to validate a client certificate, it needs a truststore with the public key and the CA certificate. Since we self-signed the server certificate, we need to extract the public key and the CA certificate to a truststore file called server.ts. It is a two-step process - first export the CA certificate to a file and then import the CA certificate from the file into the truststore.
To export the CA certificate from the keystore client.ks into a file called client.cer in the rfc 1421 format using the keytool, execute the following command:
keytool -exportcert -alias client -keystore ./resources/client.ks -rfc -file ./resources/client.cer
The following should be the typical output:
Enter keystore password: client.123 Certificate stored in file <./resources/client.cer>
To import the CA certificate from the file client.cer in the rfc 1421 format into the truststore server.ts using the keytool, execute the following command:
keytool -importcert -alias client -file ./resources/client.cer -keystore ./resources/server.ts
The following should be the typical output:
Enter keystore password: server.123 Re-enter new password: server.123 Owner: CN=client, OU=testing, O=polarsparc, L=na, ST=ny, C=us Issuer: CN=client, OU=testing, O=polarsparc, L=na, ST=ny, C=us Serial number: 3d5f9f96 Valid from: Fri Oct 27 23:05:11 EDT 2017 until: Sat Oct 27 23:05:11 EDT 2018 Certificate fingerprints: SHA1: DF:ED:5A:19:8B:B2:53:31:80:D7:B1:95:8A:CB:A0:8D:56:31:03:F0 SHA256: 05:8F:0F:11:5F:7E:F3:72:23:E4:3A:C6:58:9C:81:B7:B0:4F:1E:6F:A9:C8:B6:30:09:23:A8:8B:92:86:11:92 Signature algorithm name: SHA256withRSA Subject Public Key Algorithm: 2048-bit RSA key Version: 3 Extensions: #1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [ KeyIdentifier [ 0000: 62 4E EE 82 9C B0 AE 02 54 E3 3D AE 32 6B 09 4B bN......T.=.2k.K 0010: C6 8C 08 FE .... ] ] Trust this certificate? [no]: yes Certificate was added to keystore
At this point, we should have two keystore files - one for the server (server.ks from Part 2) and one for the client (client.ks from above). Similarly, we should have two truststore files - one for the client (client.ts from Part 2) and one for the server (server.ts from above).
Hands-on Two-way SSL/TLS using Java
The following is the simple SSL enabled echo server that uses the lower level JSSE APIs for the two-way SSL/TLS mutual authentication:
The setNeedClientAuth(true) instance method on the SSLServerSocket is what enables the SSL/TLS client authentication.
For the two-way SSL/TLS mutual authentication, the SSL/TLS server needs both a keystore to store its private keys and identify certificate as well as a truststore to store the trusted root CA certificate to validate the SSL/TLS client identity certificate.
The init(KeyManager[], TrustManager[], null) instance method initializes the instance of the SSLContext with the specified array of KeyManager and TrustManager. In this example, we specify valid instances for both the array of KeyManager and TrustManager.
Open a new Terminal window, and execute the following command to start the SSL/TLS echo server with the appropriate keystore and truststore:
java -cp build/classes com.polarsparc.pki.SecureEchoServer4 ./resources/server.ks server.123 \
./resources/server.ts server.123
The following should be the typical output:
Echo (server-4), default key store type: jks Echo (server-4), default key manager algorithm: SunX509 Echo (server-4), default trust manager algorithm: PKIX Echo (server-4) started on 8443
The following is the simple SSL enabled echo client that uses the lower level JSSE APIs for the two-way SSL/TLS mutual authentication:
For the two-way SSL/TLS mutual authentication, the SSL/TLS client needs both a keystore to store its private keys and identify certificate as well as a truststore to store the trusted root CA certificate to validate the SSL/TLS server identity certificate.
The init(KeyManager[], TrustManager[], null) instance method initializes the instance of the SSLContext with the specified array of KeyManager and TrustManager. In this example, we specify valid instances for both the array of KeyManager and TrustManager.
Open another Terminal window, and execute the following command to start the SSL/TLS echo client:
java -cp build/classes com.polarsparc.pki.SecureEchoClient4 ./resources/client.ks client.123 \
./resources/client.ts client.123 "Hello SSL World"
The following should be the typical output:
Echo (client-4), default key store type: jks Echo (client-4), default key manager algorithm: SunX509 Echo (client-4), default trust manager algorithm: PKIX
From the terminal window where the server was started, we see the following output:
-> Echo (server-4): Hello SSL World
YIPPEE !!! We have successfully demonstrated the two-way SSL/TLS authetication.
References