Java Security: SSL

Secure Socket Layer (SSL) is a protocol used to make communication secured and is a primary choice of the Internet community. The purpose of SSL is capable of securing any transmission over TCP, such as HTTP, SMTP, FTP etc. The location of SSL in TCP/IP protocol suit is shown in Table 19.1:

SSL was first conceived by Netscape in 1994 and is now under the control of Internet Engineering Task Force (IETF). In 1999, IETF renamed SSL to Transport Layer Security (TLS), and released the first specification, version TLS 1.0. Although TLS 1.0 is a modest upgrade to the most recent version of SSL, version 3.0, the differences between SSL 3.0 and TLS 1.0 are minor. Other upgraded versions of TLS (1.1 and 1.2) are also available. However, they are not as widely supported as TLS 1.0 and SSL 3.0.

1. What does SSL do?

We must take care of the following issues before transferring critical information (such as password, credit card number etc) over a public network:

  • Anyone, who has access to the network, may intercept and view the information. This unauthorized third party is sometimes known as an attacker.
  • The attacker may also change the data, on its way to the receiver.
  • The attacker may also relay some previously received data to the receiver.
  • It is necessary to be sure that the party whom you are communicating with is really who you think it is.

SSL addresses all of these issues and provides solutions to the above mentioned problems using several different cryptographic concepts. For example, it uses public-key cryptography with digital signatures to provide authentication, and secret-key cryptography to provide for privacy and data integrity.

2. How does it do?

The following is a brief description of SSL handshake messages.

  1. The client sends a message containing SSL version, the cipher suites supported by the client, a random byte string that is used in subsequent computations, etc.
  2. The server responds with a message containing the cipher suite chosen, the session ID and another random byte string and its digital certificate. If the server requires client authentication, it requests the client’s certificate.
  3. The client tries to verify the server’s digital certificate. If the server is successfully authenticated, client goes to the next step, else warns the user about the problem.
  4. The client creates a pre-master secret key, encrypts it using server’s public key obtained from the server’s certificate in step 2 and sends the encrypted to the server.
  5. If the server requested client authentication, client generates a data unique to this handshake, encrypts with the client’s private key and sends it along with the client’s digital certificate to the server.
  6. If the server has requested client authentication, the server attempts to authenticate the client. If the client is authenticated successfully, the server uses its private key to decrypt the pre-master secret, then performs a series of steps (which the client also performs, starting from the same pre-master secret) to generate the master secret.
  7. Both the client and the server use the master secret to generate the session keys, which are symmetric keys used to encrypt and decrypt information exchanged during the SSL session and to verify its integrity.
  8. The client sends a message to the server informing it that future messages from the client are encrypted with the session key. It also sends a separate encrypted message indicating that the client portion of the handshake is complete.
  9. The server sends a message to the client informing it that future messages from the server are encrypted with the session key. It also sends a separate encrypted message indicating that the server portion of the handshake is complete.
  10. The entire SSL handshake is now complete, and the SSL session starts.

3. An Example

In this section, we shall develop a simple but elegant client-server socket application using SSL. In this application, the client sends a string to the server. The server prints the string and sends it back to the client. The client finally prints it. Although this application is very simple, it demonstrates basic steps required to develop almost all SSL-based secure applications.

3.1. Writing the Server

In a traditional (non-SSL) socket application, the server program first creates a java.net. ServerSocket object. In an SSL-enabled socket application, the equivalent object is j avax.net .ssi. ssLServerSocket object and is created in a slightly different way. All constructors of ssLServerSocket class are protected. Consequently, those constructors cannot be used to instantiate SSLServerSocket objects. The instances are created using SSLServerSocketFactory class. An instance of this factory is usually created as follows:

SSLServerSocketFactory factory = (SSLServerSocketFactory)

SSLServerSocketFactory.getDefault();

The SSLServerSocket object is then created using createServerSocket() method on this factory:

SSLServerSocket sss = (SSLServerSocket) factory.createServerSocket(6789);

This creates an SSLServerSocket object that listens on port 67 8 9. The server then starts listening when an accept() method is invoked as follows:

SSLSocket ss = (SSLSocket) sss.accept();

The server is then ready to accept incoming connections. The complete source code (SSLServer. java ) is given below:

import javax.net.ssl.*;

import java.io.*;

public class SSLServer {

public static void main(String[] args) {

try {

SSLServerSocketFactory factory = (SSLServerSocketFactory)

SSLServerSocketFactory.getDefault();

SSLServerSocket sss = (SSLServerSocket) factory.createServerSocket(6789);

SSLSocket ss = (SSLSocket) sss.accept();

BufferedReader in = new BufferedReader(new

InputStreamReader(ss.getInputStream()));

PrintWriter out = new PrintWriter(ss.getOutputStream(), true);

String line = null;

while ((line = in.readLine()) != null) {

System.out.println(”received<– ”+line);

out.println(line);

System.out.println(”sent –> ”+line);

}

} catch (Exception e) { e.printStackTrace(); }

}

}

This program simply sends strings, obtained from the client, back to the client.

3.2. Writing the Client

The client first creates a SSLSocketFactory as follows:

SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();

The counterpart of Socket in SSL is SSLSocket which is created as follows:

SSLSocket ss = (SSLSocket) factory.createSocket(args[0], 6789);

The complete source code (SSLClient.java) is given below:

import javax.net.ssl.*; import java.io.*; public class SSLClient {

public static void main(String[] args) {

try {

SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();

SSLSocket ss = (SSLSocket) factory.createSocket(args[0], 6789);

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

BufferedReader in = new BufferedReader(new

InputStreamReader(ss.getInputStream()));

PrintWriter out = new PrintWriter(ss.getOutputStream(), true);

String line = null;

while ((line = br.readLine()) != null) { out.println(line);

System.out.println(”sent –> ”+line);

System.out.println(”received<– ”+in.readLine());

}

} catch (Exception e) { e.printStackTrace(); }

}

}

3.3. Compiling and Running the Application

Compile the server program using the following command:

javac SSLServer.java

Similarly, compile the client program using the following command:

javac SSLClient.java

Since, SSL uses certificates, we create a private/public keypair and a self signed certificate for the server and store them in a JKS key store file server.ks using Java’s keytool utility as follows:

keytool -genkey -keystore server.ks

The keytool asks for information necessary to generate a certificate. Provide the necessary information when asked. A sample output is shown in Figure 19.1:

The entire information may also be passed as command line arguments as follows:

keytool -genkey -alias mykey -keyalg RSA -keystore server.ks -storepass 123456

-keypass 123456 -dname ”CN=U. K. Roy, OU=IT, O=JU, L=Kolkata, ST=WB, C=IN”

The list of keystore entries may be displayed using the following command

keytool -list -storepass 123456 -keystore server.ks

It generates a samle output as follows:

Keystore type: JKS

Keystore provider: SUN

Your keystore contains 1 entry

mykey, May 27, 2014, PrivateKeyEntry,

Certificate fingerprint (SHA1):

32:1D:8F:05:04:CB:03:62:C8:09:29:C5:F9:B3:D7:9D:F4:BC:AC:F0

We then extract (called export) the server’s certificate and store it in a separate file

server.cer.

keytool -export -alias mykey -keystore server.ks -storepass 123456 -file server.cer

During export, keytool asks for the password of the key store. Use the same password used to create the key store. A sample output is shown in Figure 19.2:

The content of this certificate file may be displayed using the following command:

keytool -printcert -file server.cer

This generates a sample output as follows:

Certificate fingerprint (SHA1):

32:1D:8F:05:04:CB:03:62:C8:09:29:C5:F9:B3:D7:9D:

F4:BC:AC:F0

E:\ajp\sec\soc\server>keytool -printcert -file server.cer Owner: CN=U. K. Roy, OU=IT, O=JU, L=Kolkata, ST=WB, C=IN Issuer: CN=U. K. Roy, OU=IT, O=JU, L=Kolkata, ST=WB, C=IN Serial number: 47e38e48

Valid from: Tue May 27 13:20:08 IST 2014 until: Mon Aug 25 13:20:08 IST 2014 Certificate fingerprints:

MD5:   72:1F:9D:F8:35:EB:B3:3B:AA:A4:A4:F6:DD:ED:7C:F1

SHA1: 32:1D:8F:05:04:CB:03:62:C8:09:29:C5:F9:B3:D7:9D:F4:BC:AC:F0

SHA256:

4B:CD:03:2B:94:FE:04:A3:21:74:E5:52:8D:2A:F0:83:9E:7E:20:1D:91:

F5:99:90:DF:86:9B:7D:EB:BB:3B:62

Signature algorithm name: SHA1withDSA

Version: 3

Extensions:

#1: ObjectId: 2.5.29.14 Criticality=false SubjectKeyIdentifier [

KeyIdentifier [

0000: DE 14 6A 59 9D 48 45 57 2B 36 C3 B6 FC E8 C1 65 ..jY.HEW+6……………….. e

0010: 3D A4 C1 3B                                                                  =..;

]

]

The usage of keytoll to work with keys, certificates and stores is discussed in detail later in this chapter.

This certificate will be sent by the server to the client in step 2 of SSL handshake. A client maintains a list of certificates it relies (trusts) on in a separate store called trust store. Note that the trust store only contains public certificates, whereas a key store can store private/pub key pair as well as certificates. Copy this certificate file in the client’s home directory which hereafter will be referred to as client_home. Go to the ciient_home directory and use the following command to generate a trust store for the client:

keytool -import -v -keystore client.ts -storepass 123456 -file server.cer

Information about the certificate is displayed and a prompt appears asking if you want to trust the certificate. Type yes (or y), then press Enter. A sample output is shown in Figure 19.3:

At this moment, both client and server are ready to communicate securedly after successful SLL handshake. Start the SSL server first using the following command:

java -Djavax.net.ssl.keyStore=server.ks -Djavax.net.ssl.keyStorePassword=123456

SSLServer

Note that the key store file and its password are passed to Java interpreter using command line arguments. Start the client using the following command

java -Djavax.net.ssl.trustStore=client.ts –

Djavax.net.ssl.trustStorePassword=123456 SSLClient localhost

Here, we provide the trust store file and its password. A sample output of the application is shown in Figure 19.4:

The key store and trust store files and their password may also be specified using some Java properties. Add the following lines in SimpleSSLServer.java:

System.setProperty(”javax.net.ssl.keyStore”, ”server.ks”);

System.setProperty(”javax.net.ssl.keyStorePassword”, ”123456”);

Also add the following lines in SimpleSSLClient.java:

System.setProperty(”javax.net.ssl.trustStore”, ”client.ts”);

System.setProperty(”javax.net.ssl.trustStorePassword”, ”123456”);

After recompilation, we can then run the server and client directly whose output is shown in Figure 19.5:

4. Using Client Authentication

SSL allows server to authenticate a client if the server wants to do so. Add the following entry

in SimpleSSLServer.java:

ss.setNeedClientAuth(true);

This configures the socket (in the server mode) that requires client authentication. Client must send its certificate now, otherwise the server stops the negotiations and terminates the connection. So, we create a keystore at the ciient_home directory and export its certificate:

keytool -genkey -alias mykey -keyalg RSA -keystore client.ks -storepass 123456 -keypass 123456 -dname ”CN=B. C. Dhara, OU=IT, O=JU, L=Kolkata, ST=WB, C=IN”

keytool -export -alias mykey -keystore client.ks -storepass 123456 -file client.cer

Copy client.cer file in server_home. Generate a trust store for the server:

keytool -import -v -keystore server.ts -storepass 123456 -file client.cer

The following is a summary of keystore files used by client and server. We have used .ks and .ts extensions for keystore file and truststore file respectively.

server.ks—server keystore, contains the server’s private key and self-signed X.509 certificate containing associated public key.

server.ts—server truststore, contains the client’s self-signed X.509 certificate. client.ks—client keystore, contains the client’s private key and self-signed X.509 certificate containing associated public key.

client.ts—client truststore, contains the server’s self-signed X.509 certificate.

If keystore and truststore are not specified using System.setProperty() method, then start the server using the following command:

java -Djavax.net.ssl.keyStore=server.ks -Djavax.net.ssl.keyStorePassword=123456 -Djavax.net.ssl.trustStore=server.ts -Djavax.net.ssl.trustStorePassword=123456

SSLServer

And start the client using the following command:

java -Djavax.net.ssl.keyStore=client.ks -Djavax.net.ssl.keyStorePassword=123456

-Djavax.net.ssl.trustStore=client.ts -Djavax.net.ssl.trustStorePassword=123456

SSLClient localhost

Instead of passing trust store information to the server at command line, the following system properties may be set:

System.setProperty(“javax.net.ssl.trustStore”, “server.ts”);

System.setProperty(“javax.net.ssl.trustStorePassword”, “123456”);

Similarly, in the client the following system properties may be specified to provide keystore information:

System.setProperty(”javax.net.ssl.keyStore”, “client.ks”);

System.setProperty(“javax.net.ssl.keyStorePassword”, “123456”);

If a trust store location is not specified (as a command line argument or using system property), the SunJSSE implementation searches for and uses a truststore file in the following locations (in order):

JAVA_HOME\jre\lib\security\jssecacerts

JAVA_HOME\jre\lib\security\cacerts

So, certificates may be added to these trust stores. The default password for Java trust store is “changeit”. Copy server.cer and client.cer files to the directory JAVA_HOME\jre\lib\security. Now, go to this directory and use the following commands to add client.cer and server.cer certificate files to the system trust store file cacerts.

keytool -import -v -alias server -keystore cacerts -storepass changeit -file client.cer

keytool -import -v -alias client -keystore cacerts -storepass changeit -file server.cer

Check if the certificates are really added using the following command:

keytool -list -storepass changeit -keystore cacerts

The relevant portion will look like this:

client, May 29, 2014, trustedCertEntry,

Certificate fingerprint (SHA1):

32:1D:8F:05:04:CB:03:62:C8:09:29:C5:F9:B3:D7:9D:F4:BC:AC:F0

server, May 29, 2014, trustedCertEntry,

Certificate fingerprint (SHA1):

8A:09:40:D4:16:0F:9C:FE:9C:AC:29:F8:4F:6F:0B:A5:C8:28:4A:5E

Now, both server and client can be started without specifying the trust store. When these certificates are no longer needed, remove them from cacerts using the following commands:

keytool -delete -alias client -keystore cacerts -storepass changeit

keytool -delete -alias server -keystore cacerts -storepass changeit

5. Using KeyStore

Java platform also provides a class java.security.KeyStore for a storage facility for cryptographic keys and certificates. The following server program (ssLServeri.java) loads key store and trust store information from files server.ks and server.ts respectively.

import javax.net.ssl.*;

import java.io.*;

import java.security.*;

public class SSLServerl {

public static void main(String[] args) {

try {

char[] password = ”123456”.toCharArray();

KeyStore ks = KeyStore.getInstance(”JKS”);

ks.load(new FileInputStream(”server.ks”), password);

KeyStore ts = KeyStore.getInstance(”JKS”);

ts.load(new FileInputStream(”server.ts”), password);

KeyManagerFactory kmf = KeyManagerFactory.getInstance(”SunX509”);

kmf.init(ks, password);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(”SunX509”);

tmf.init(ts);

SSLContext sslContext = SSLContext.getInstance(”TLS”);

sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

SSLServerSocketFactory factory =

(SSLServerSocketFactory)sslContext.getServerSocketFactory();

//  SSLServerSocketFactory factory = (SSLServerSocketFactory)

SSLServerSocketFactory.getDefault();

SSLServerSocket sss = (SSLServerSocket) factory.createServerSocket(6789);

SSLSocket ss = (SSLSocket) sss.accept();

ss.setNeedClientAuth(true);

InputStreamReader(ss.getInputStream()));

PrinRWriRer out = new PrintWriter(ss.getOutputStream(), true);

String line = null;

while ((line = in.readLine()) != null) {

System.out.println(”received<– ”+line); out.println(line);

System.out.println(”sent –> ”+line);

}

} catch (Exception e) { e.printStackTrace();

}

}

}

Similarly, the following client program loads key store and trust store information from files

client.ks and client.ts respectively.

import javax.net.ssl.*;

import java.io.*;

import java.security.*;

public class SSLClientl {

public static void main(String[] args) {

try {

char[] password = ”123456”.toCharArray();

KeyStore ks = KeyStore.getInstance(”JKS”); ks.load(new

FileInputStream(”client.ks”), password);

KeyStore ts = KeyStore.getInstance(”JKS”);

ts.load(new FileInputStream(”client.ts”), password);

KeyManagerFactory kmf = KeyManagerFactory.getInstance(”SunX509”);

kmf.init(ks, password);

TrustManagerFactory tmf = TrustManagerFactory.getInstance(”SunX509”);

tmf.init(ts);

SSLContext sslContext = SSLContext.getInstance(”TLS”);

sslContext.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);

SSLSocketFactory factory =

(SSLSocketFactory)sslContext.getSocketFactory();

// SSLSocketFactory factory = (SSLSocketFactory)

SSLSocketFactory.getDefault();

SSLSocket ss = (SSLSocket) factory.createSocket(args[0], 6789);

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

BufferedReader in = new BufferedReader(new

InputStreamReader(ss.getInputStream()));

PrintWriter out = new PrintWriter(ss.getOutputStream(), true);

String line = null;

while ((line = br.readLine()) != null) { out.println(line);

System.out.println(”sent –> ”+line);

System.out.println(”received<– ”+in.readLine());

}

} catch (Exception e) { e.printStackTrace();

}

}

}

This application can be run without specifying key store and trust store information at runtime.

6. Ignoring Server Certificates

A client may not always require server authentication. The following client program demonstrates how to turn off server authentication:

import javax.net.ssl.*;

import java.io.*;

import java.security.*;

import java.security.cert.*;

public class SSLClientIgnore {

public static void main(String[] args) {

try {

TrustManager[] trustAllCerts = {

new X509TrustManager() {

public X509Certificate[] getAcceptedIssuers() {return null;}

public void checkClientTrusted(X509Certificate[] certs,

String authType) {}

public void checkServerTrusted(X509Certificate[] certs,

String authType) {}

}

};

SSLContext sc = SSLContext.getInstance(”SSL”); sc.init(null, trustAllCerts, new

SecureRandom());

SSLSocketFactory factory = (SSLSocketFactory)sc.getSocketFactory();

//           SSLSocketFactory factory = (SSLSocketFactory)

SSLSocketFactory.getDefault();

SSLSocket ss = (SSLSocket) factory.createSocket(args[0], 6789);

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

BufferedReader in = new BufferedReader(new

InputStreamReader(ss.getInputStream()));

PrintWriter out = new PrintWriter(ss.getOutputStream(), true);

String line = null;

while ((line = br.readLine()) != null) { out.println(line);

System.out.println(”sent –> ”+line);

System.out.println(”received<– ”+in.readLine());

}

} catch (Exception e) { e.printStackTrace();

}

}

}

In this program, we override the default trust manager with one that trusts all certificates. This client works even if server does not provide any certificate.

If you are using a URL connection then the following will also disable server authentication:

HttpsURLConnection.setDefaultSSLSocketFactory( sc.getSocketFactory() );

Note that a client that does not authenticate a server is vulnerable to several attacks and it is suggested to avoid it as far as possible.

7. Working with HTTPS

This section describes how to configure tomcat web server to support HTTP over SSL. To create a key store file tomcat.ks for tomcat, go to the tomcat_home directory and use the following command:

keytool -genkey -alias tomcat -keyalg RSA -keystore tomcat.ks -storepass 123456

-keypass 123456 -dname ”CN=U. K. Roy, OU=IT, O=JU, L=Kolkata, ST=WB, C = IN”

Modify the tomcat_home\conf\server.xml files as follows:

<Connector port=”8443″ protocol=”HTTP/1.1″ SSLEnabled=”true”

maxThreads=”150” scheme=”https” secure=”true”

keystoreFile=tomcat.ks keystorePass=”123456

clientAuth=”false” sslProtocol=”TLS” />

Restart the tomcat server. The tomcat is now ready to work with HTTP over SSL on port 8443. To talk to this tomcat server we have written a HTTPS client whose complete source code (HTTPSClient. java) is shown below:

// HTTPSClient.java import javax.net.ssl.*; import java.io.*; import java.net.*; import javax.net.*; import java.security.*;

import java.security.cert.X509Certificate;

public class HTTPSClient {

public static void main(String args[]) throws Exception {

String urlString = “https://127.0.0.1:8443/index.jsp”;

URL url = new URL(urlString);

TrustManager[] trustAllCerts =   {

new X509TrustManager() {

public X509Certificate[] getAcceptedIssuers() {return null;}

public void checkClientTrusted(X509Certificate[] certs, String authType) {}

public void checkServerTrusted(X509Certificate[] certs, String authType) {}

}

};

SSLContext sc = SSLContext.getInstance(”SSL”);

sc.init(null, trustAllCerts, new SecureRandom());

SocketFactory factory = sc.getSocketFactory();

Socket socket = factory.createSocket(url.getHost(), url.getPort());

PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true);

BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

out.println(”GET ” + urlString + ” HTTP/1.1\n”);

String line;

while ((line = in.readLine()) != null)

System.out.println(line);

}

}

Run the client as follows:

java HTTPSClient

Note that this client does not need any trust store. The trust manager used in this program overrides checkServerTrusted() method. Since, the body of this method is empty, it accepts server response without authenticating the server.

Alternatively, the tomcat’s certificate may be added to the system trust store file (i.e. java_home\ jre\lib\security\cacerts). To do this, go to the tomcat_home directory and export the public certificate for tomcat to a file tomcat.cer using the following command:

keytool -export -alias tomcat -keystore tomcat.ks -storepass 123456 -file tomcat.cer

Now, copy tomcat.cer file, generated in tomcat_home directory to the directory java_home\ jre\lib\security. Now, go to this directory and use the following command to add tomcat.cer certificate file to the system trust store file cacerts.

keytool -import -v -keystore cacerts -storepass changeit -file tomcat.cer

The https client can now use the default socket factory without using a custom trust manager as follows:

SocketFactory factory = SSLSocketFactory.getDefault();

The server’s certificate is then verified with the entries in the cacerts file which indeed contains a certificate for tomcat.

Source: Uttam Kumar Roy (2015), Advanced Java programming, Oxford University Press.

Leave a Reply

Your email address will not be published. Required fields are marked *