11.3 Methods of the SSLSocket Class Besides the methods we've already discussed and those it inherits from java.net.Socket , the SSLSocket class has a number of methods for configuring exactly how much and what kind of authentication and encryption is performed. For instance, you can choose weaker or stronger algorithms, require clients to prove their identity, force reauthentication of both sides, and more. 11.3.1 Choosing the Cipher Suites Different implementations of the JSSE support different combinations of authentication and encryption algorithms. For instance, the implementation Sun bundles with Java 1.4 only supports 128-bit AES encryption, whereas IAIK's iSaSiLk (http://jce.iaik.tugraz.at/products/02_isasilk/) supports 256-bit AES encryption. The getSupportedCipherSuites() method tells you which combination of algorithms is available on a given socket: public abstract String[] getSupportedCipherSuites( ) However, not all cipher suites that are understood are necessarily allowed on the connection. Some may be too weak and consequently disabled. The getEnabledCipherSuites() method tells you which suites this socket is willing to use: public abstract String[] getEnabledCipherSuites( ) The actual suite used is negotiated between the client and server at connection time. It's possible that the client and the server won't agree on any suite. It's also possible that although a suite is enabled on both client and server, one or the other or both won't have the keys and certificates needed to use the suite. In either case, the createSocket( ) method will throw an SSLException , a subclass of IOException . You can change the suites the client attempts to use via the setEnabledCipherSuites( ) method: public abstract void setEnabledCipherSuites(String[] suites) The argument to this method should be a list of the suites you want to use. Each name must be one of the suites listed by getSupportedCipherSuites( ) . Otherwise , an IllegalArgumentException will be thrown. Sun's JDK 1.4 supports these 23 cipher suites: -
SSL_RSA_WITH_RC4_128_MD5 -
SSL_RSA_WITH_RC4_128_SHA -
TLS_RSA_WITH_AES_128_CBC_SHA -
TLS_DHE_RSA_WITH_AES_128_CBC_SHA -
TLS_DHE_DSS_WITH_AES_128_CBC_SHA -
SSL_RSA_WITH_3DES_EDE_CBC_SHA -
SSL_DHE_RSA_WITH_3DES_EDE_CBC_SHA -
SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA -
SSL_RSA_WITH_DES_CBC_SHA -
SSL_DHE_RSA_WITH_DES_CBC_SHA -
SSL_DHE_DSS_WITH_DES_CBC_SHA -
SSL_RSA_EXPORT_WITH_RC4_40_MD5 -
SSL_RSA_EXPORT_WITH_DES40_CBC_SHA -
SSL_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA -
SSL_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA -
SSL_RSA_WITH_NULL_MD5 -
SSL_RSA_WITH_NULL_SHA -
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 -
SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA Each name has an algorithm divided into four parts : protocol, key exchange algorithm, encryption algorithm, and checksum. For example, the name SSL_DH_anon_EXPORT_WITH_DES40_CBC_SHA mean s Secure Sockets Layer Version 3; Diffie-Hellman method for key agreement; no authentication; Data Encryption Standard encryption with 40-bit keys; Cipher Block Chaining, and the Secure Hash Algorithm checksum. By default, the JDK 1.4 implementation enables all the encrypted authenticated suites (the first 15 members of this list). If you want nonauthenticated transactions or authenticated but unencrypted transactions, you must enable those suites explicitly with the setEnabledCipherSuites( ) method. Besides key lengths, there's an important difference between DES/AES and RC4-based ciphers. DES and AES are block ciphers; that is, they encrypt a certain number of bits at a time. DES always encrypts 64 bits. If 64 bits aren't available, the encoder has to pad the input with extra bits. AES can encrypt blocks of 128, 192, or 256 bits, but still has to pad the input if it doesn't come out to an even multiple of the block size . This isn't a problem for file transfer applications such as secure HTTP and FTP, where more or less all the data is available at once. However, it's problematic for user -centered protocols such as chat and Telnet. RC4 is a stream cipher that can encrypt one byte at a time and is more appropriate for protocols that may need to send a single byte at a time. For example, let's suppose that Edgar has some fairly powerful parallel computers at his disposal and can quickly break any encryption that's 64 bits or less and that Gus and Angela know this. Furthermore, they suspect that Edgar can blackmail one of their ISPs or the phone company into letting him tap the line, so they want to avoid anonymous connections that are vulnerable to man-in-the-middle attacks. To be safe, Gus and Angela decide to use at least 111-bit, authenticated encryption. It then behooves them to enable only the strongest available algorithms. This code fragment accomplishes that: String[] strongSuites = {"SSL_DHE_DSS_WITH_3DES_EDE_CBC_SHA", "SSL_RSA_WITH_RC4_128_MD5", "SSL_RSA_WITH_RC4_128_SHA", "SSL_RSA_WITH_3DES_EDE_CBC_SHA"}; socket.setEnabledCipherSuites(strongSuites); If the other side of the connection doesn't support strong encryption, the socket will throw an exception when they try to read from or write to it, thus ensuring that no confidential information is accidentally transmitted over a weak channel. 11.3.2 Event Handlers Network communications are slow compared to the speed of most computers. Authenticated network communications are even slower. The necessary key generation and setup for a secure connection can easily take several seconds. Consequently, you may want to deal with the connection asynchronously. JSSE uses the standard event model introduced in Java 1.1 to notify programs when the handshaking between client and server is complete. The pattern is a familiar one. In order to get notifications of handshake-complete events, simply implement the HandshakeCompletedListener interface: public interface HandshakeCompletedListener extends java.util.EventListener This interface declares the handshakeCompleted( ) method: public void handshakeCompleted(HandshakeCompletedEvent event) This method receives as an argument a HandshakeCompletedEvent : public class HandshakeCompletedEvent extends java.util.EventObject The HandshakeCompletedEvent class provides four methods for getting information about the event: public SSLSession getSession( ) public String getCipherSuite( ) public X509Certificate[] getPeerCertificateChain( ) throws SSLPeerUnverifiedException public SSLSocket getSocket( ) Particular HandshakeCompletedListener objects register their interest in handshake-completed events from a particular SSLSocket via its addHandshakeCompletedListener( ) and removeHandshakeCompletedListener( ) methods: public abstract void addHandshakeCompletedListener( HandshakeCompletedListener listener) public abstract void removeHandshakeCompletedListener( HandshakeCompletedListener listener) throws IllegalArgumentException 11.3.3 Session Management SSL is commonly used on web servers, and for good reason. Web connections tend to be transitory ; every page requires a separate socket. For instance, checking out of Amazon.com on its secure server requires seven separate page loads, more if you have to edit an address or choose gift-wrapping. Imagine if every one of those pages took an extra 10 seconds or more to negotiate a secure connection. Because of the high overhead involved in handshaking between two hosts for secure communications, SSL allows sessions to be established that extend over multiple sockets. Different sockets within the same session use the same set of public and private keys. If the secure connection to Amazon.com takes seven sockets, all seven will be established within the same session and use the same keys. Only the first socket within that session will have to endure the overhead of key generation and exchange. As a programmer using JSSE, you don't need to do anything extra to take advantage of sessions. If you open multiple secure sockets to one host on one port within a reasonably short period of time, JSSE will reuse the session's keys automatically. However, in high-security applications, you may want to disallow session-sharing between sockets or force reauthentication of a session. In the JSSE, sessions are represented by instances of the SSLSession interface; you can use the methods of this interface to check the times the session was created and last accessed, invalidate the session, and get various information about the session: public byte[] getId( ) public SSLSessionContext getSessionContext( ) public long getCreationTime( ) public long getLastAccessedTime( ) public void invalidate( ) public void putValue(String name, Object value) public Object getValue(String name) public void removeValue(String name) public String[] getValueNames( ) public X509Certificate[] getPeerCertificateChain( ) throws SSLPeerUnverifiedException public String getCipherSuite( ) public String getPeerHost( ) The getSession( ) method of SSLSocket returns the Session this socket belongs to: public abstract SSLSession getSession( ) However, sessions are a trade-off between performance and security. It is more secure to renegotiate the key for each and every transaction. If you've got really spectacular hardware and are trying to protect your systems from an equally determined, rich, motivated, and competent adversary, you may want to avoid sessions. To prevent a socket from creating a session that passes false to setEnableSessionCreation( ) , use: public abstract void setEnableSessionCreation(boolean allowSessions) The getEnableSessionCreation() method returns true if multisocket sessions are allowed, false if they're not: public abstract boolean getEnableSessionCreation( ) On rare occasions, you may even want to reauthenticate a connection; that is, throw away all the certificates and keys that have previously been agreed to and start over with a new session. The startHandshake( ) method does this: public abstract void startHandshake( ) throws IOException 11.3.4 Client Mode It's a rule of thumb that in most secure communications, the server is required to authenticate itself using the appropriate certificate. However, the client is not. That is, when I buy a book from Amazon using its secure server, it has to prove to my browser's satisfaction that it is indeed Amazon and not Joe Random Hacker. However, I do not have to prove to Amazon that I am Elliotte Rusty Harold. For the most part, this is as it should be, since purchasing and installing the trusted certificates necessary for authentication is a fairly user- hostile experience that readers shouldn't have to go through just to buy the latest Nutshell handbook. However, this asymmetry can lead to credit card fraud. To avoid problems like this, sockets can be required to authenticate themselves . This strategy wouldn't work for a service open to the general public. However, it might be reasonable in certain internal, high-security applications. The setUseClientMode() method determines whether the socket needs to use authentication in its first handshake. The name of the method is a little misleading. It can be used for both client- and server-side sockets. However, when true is passed in, it means the socket is in client mode (whether it's on the client side or not) and will not offer to authenticate itself. When false is passed, it will try to authenticate itself: public abstract void setUseClientMode(boolean mode) throws IllegalArgumentException This property can be set only once for any given socket. Attempting to set it a second time throws an IllegalArgumentException . The getUseClientMode() method simply tells you whether this socket will use authentication in its first handshake: public abstract boolean getUseClientMode( ) A secure socket on the server side (that is, one returned by the accept( ) method of an SSLServerSocket ) uses the setNeedClientAuth( ) method to require that all clients connecting to it authenticate themselves (or not): public abstract void setNeedClientAuth(boolean needsAuthentication) throws IllegalArgumentException This method throws an IllegalArgumentException if the socket is not on the server side. The getNeedClientAuth() method returns true if the socket requires authentication from the client side, false otherwise: public abstract boolean getNeedClientAuth( ) |