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