Creating Secure Server Sockets

fSecure client sockets are only half of the equation. The other half is SSL-enabled server sockets. These are instances of the javax.net.SSLServerSocket class:

public abstract class SSLServerSocket extends ServerSocket

Like SSLSocket, all the constructors in this class are protected. Like SSLSocket, instances of SSLServerSocket are created by an abstract factory class, javax.net.SSLServerSocketFactory:

public abstract class SSLServerSocketFactory extends ServerSocketFactory

Also like SSLSocketFactory, an instance of SSLServerSocketFactory is returned by a static SSLServerSocketFactory.getDefault( ) method:

public static ServerSocketFactory getDefault( )

And like SSLSocketFactory, SSLServerSocketFactory has three overloaded createServerSocket( ) methods that return instances of SSLServerSocket and are easily understood by analogy with the java.net.ServerSocket constructors:

public abstract ServerSocket createServerSocket(int port) throws IOException public abstract ServerSocket createServerSocket(int port, int queueLength) throws IOException public abstract ServerSocket createServerSocket(int port, int queueLength, InetAddress interface) throws IOException

If that were all there was to creating secure server sockets, they would be quite straightforward and simple to use. Unfortunately, that's not all there is to it. The factory that SSLServerSocketFactory.getDefault( ) returns generally only supports server authentication. It does not support encryption. To get encryption as well, server-side secure sockets require more initialization and setup. Exactly how this setup is performed is implementation-dependent. In Sun's reference implementation, a com.sun.net.ssl.SSLContext object is responsible for creating fully configured and initialized secure server sockets. The details vary from JSSE implementation to JSSE implementation, but to create a secure server socket in the reference implementation, you have to:

demonstrates this procedure with a complete SecureOrderTaker for accepting orders and printing them on System.out. Of course, in a real app, you'd do something more interesting with the orders.

Example 11-2. SecureOrderTaker

import java.net.*;
import java.io.*;
import java.util.*;
import java.security.*;
import javax.net.ssl.*;
import javax.net.*;
public class SecureOrderTaker {
 public final static int DEFAULT_PORT = 7000;
 public final static String algorithm = "SSL";
 public static void main(String[] args) {
 int port = DEFAULT_PORT; if (args.length > 0) {
 try {
 port = Integer.parseInt(args[0]);
 if (port < 0 || port >= 65536) {
 System.out.println("Port must between 0 and 65535");
 return; }
 } catch (NumberFormatException ex) {} } try {
 SSLContext context = SSLContext.getInstance(algorithm);
 // The reference implementation only supports X.509 keys
 KeyManagerFactory kmf = KeyManagerFactory.getInstance("SunX509");
 // Sun's default kind of key store
 KeyStore ks = KeyStore.getInstance("JKS");
 // For security, every key store is encrypted with a // pass phrase that must be provided before we can load // it from disk. The pass phrase is stored as a char[] array
 // so it can be wiped from memory quickly rather than
 // waiting for a garbage collector. Of course using a string
 // literal here completely defeats that purpose.
 char[] password = "2andnotafnord".toCharArray( );
 ks.load(new FileInputStream("jnp3e.keys"), password);
 kmf.init(ks, password);
 // context.init(kmf.getKeyManagers( ), null, null);
 SSLServerSocketFactory factory = context.getServerSocketFactory( );
 SSLServerSocket server = (SSLServerSocket) factory.createServerSocket(port);
 String[] supported = server.getSupportedCipherSuites( );
 String[] anonCipherSuitesSupported = new String[supported.length]; int numAnonCipherSuitesSupported = 0;
 for (int i = 0; i < supported.length; i++) {
 if (supported[i].indexOf("_anon_") > 0) {
 anonCipherSuitesSupported[numAnonCipherSuitesSupported++] = supported[i];
 }
 } String[] oldEnabled = server.getEnabledCipherSuites( );
 String[] newEnabled = new String[oldEnabled.length + numAnonCipherSuitesSupported];
 System.arraycopy(oldEnabled, 0, newEnabled, 0, oldEnabled.length);
 System.arraycopy(anonCipherSuitesSupported, 0, newEnabled, oldEnabled.length, numAnonCipherSuitesSupported);
 server.setEnabledCipherSuites(newEnabled); // Now all the set up is complete and we can focus // on the actual communication. try {
 while (true) {
 // This socket will be secure,
 // but there's no indication of that in the code!
 Socket theConnection = server.accept( );
 InputStream in = theConnection.getInputStream( );
 int c;
 while ((c = in.read( )) != -1) {
 System.out.write(c);
 } theConnection.close( );
 } // end while
 } // end try
 catch (IOException ex) {
 System.err.println(ex);
 } // end catch
 } // end try
 catch (IOException ex) {
 ex.printStackTrace( );
 } // end catch
 catch (KeyManagementException ex) {
 ex.printStackTrace( );
 } // end catch
 catch (KeyStoreException ex) {
 ex.printStackTrace( );
 } // end catch
 catch (NoSuchAlgorithmException ex) {
 ex.printStackTrace( );
 } // end catch
 catch (java.security.cert.CertificateException ex) {
 ex.printStackTrace( );
 } // end catch
 catch (UnrecoverableKeyException ex) {
 ex.printStackTrace( );
 } // end catch
 } // end main
} // end server

This example loads the necessary keys and certificates from a file named jnp3e.keys in the current working directory protected with the password "2andnotafnord". What this example doesn't show you is how that file was created. It was built with the keytool program that's bundled with the JDK like this:

D:\JAVA>keytool -genkey -alias ourstore -keystore jnp3e.keys
Enter keystore password: 2andnotafnord
What is your first and last name?
 [Unknown]: Elliotte
What is the name of your organizational unit?
 [Unknown]: Me, Myself, and I What is the name of your organization?
 [Unknown]: Cafe au Lait
What is the name of your City or Locality?
 [Unknown]: Brooklyn
What is the name of your State or Province?
 [Unknown]: New York
What is the two-letter country code for this unit?
 [Unknown]: NY
Is <CN=Elliotte, OU="Me, Myself, and I", O=Cafe au Lait, L=Brooklyn, ST=New York, C=NY> correct?
 [no]: y
Enter key password for <ourstore>
 (RETURN if same as keystore password):

When this is finished, you'll have a file named jnp3e.keys, which contains your public keys. However, no one will believe that these are your public keys unless you have them certified by a trusted third party such as Verisign (). Unfortunately, this certification costs money. The cheapest option is $14.95 per year for a Class 1 Digital ID. Verisign hides the sign-up form for this kind of ID deep within its web site, apparently to get you to sign up for the much more expensive options that are prominently featured on its home page. At the time of this writing, the sign-up form is at . Verisign has changed this URL several times in the past, making it much harder to find than its more expensive options. In the more expensive options, Verisign goes to greater lengths to guarantee that you are who you say you are. Before signing up for any kind of digital ID, you should be aware that purchasing one has potentially severe legal consequences. In some jurisdictions, poorly thought-out laws make digital ID owners liable for all purchases made and contracts signed using their digital ID, regardless of whether the ID was stolen or forged. If you just want to explore the JSSE before deciding whether to go through the hassle, expense, and liability of purchasing a verified certificate, Sun includes a verified keystore file called testkeys, protected with the password "passphrase", that has some JSSE samples (). However, this isn't good enough for real work. For more information about exactly what's going on and what the various options are, as well as other ways to create key and certificate files, consult the online documentation for the keytool utility that came with your JDK, the Java Cryptography Architecture guide at , or the previously mentioned tutorials Java Cryptography, by Jonathan Knudsen, or Java Security, by Scott Oaks (both from Oracle). Another approach is to use cipher suites that don't require authentication. There are six of these in Sun's JDK 1.4: SSL_DH_anon_WITH_RC4_128_MD5, TLS_DH_anon_WITH_AES_128_CBC_SHA, SSL_DH_anon_WITH_3DES_EDE_CBC_SHA, SSL_DH_anon_WITH_DES_CBC_SHA, SSL_DH_anon_EXPORT_WITH_RC4_40_MD5, and SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA. These are not enabled by default because they're vulnerable to a man-in-the-middle attack, but at least they allow you to write simple programs without paying Verisign any money.