Java SSL Implementation May 12, 1999 Richard Cardone I’ve implemented a functioning subset of the Secure Sockets Layer (SSL) protocol in pure Java with the ultimate goal of distributing a nonproprietary version of SSL. To test whether this implementation performs at a practical level, other project members have performed a number of comparative performance tests and reported their results in a separate document. 1 Introduction Distributed applications, especially those that span wide area networks, require a level of authentication, data integrity and data privacy only available through the sophisticated use of a combination of cryptographic algorithms. Some applications use encrypted communication links, but many important classes of applications--such as those that use the Internet--cannot depend on secure networks. For these applications, there’s a requirement for a secure transport layer protocol that also delivers good performance and ease of programming. The Secure Sockets Layer (SSL) protocol has proven its utility and flexibility by providing robust security at the transport layer in distributed applications and, especially, in web browsers. Programmers writing in C/C++ can download proven SSL implementations from the web for free. Among the most prominent implementations is SSLeay, authored by Andrew Young [1], and SSLREF from Netscape [2]. For Java programmers, a number of commercially available pure Java implementations of SSL exist, but their cost can be substantial and licensing policies restrictive [3,4]. Java programmers could build their own JNI/SSLeay library, but this would sacrifice portability. There is at least one SSLeay-based implementation available on the web for free, but requires modifications to the standard Java sockets library source code in addition to restricting portability. The initial project motivation was to provide a functioning, pure Java version of SSL so that secure, distributed Java applications could be built using a standard protocol. To determine if such an implementation would actually be practical in real world usage, we wanted to compare the performance of the pure Java implementation to other implementation approaches. Specifically, we wanted to see if a C/C++ implementation of SSL called from Java using JNI would provide better performance. We also wanted to how the pure Java implementation performed with and without JIT compilation and under native compilation. We would also like to benchmark all the Java implementations and runtime environments against a C/C++ compiled implementation. At this time, a pure Java version subset of the SSL protocol has been implemented. The two cipher suites supported allow valid performance testing to take place. We were unable to implement a JNI version of SSL in time, so no comparisons can be made between the two Java implementation approaches. Francois Caen and Malcolm Haynes have reported in a separate document the results of their performance testing. The next section introduces the SSL protocol from an architectural point of view. The following sections discuss the design and implementation of our pure Java implementation, the results of our performance tests and our conclusions. 1 2 Background 2.1 SSL Architecture SSL Architecture SSL SSL Change Handshake Cipher Spec Protocol Protocol SSL Alert Protocol Application Data Protocol SSL Record Layer TCP IP Figure 1. The SSL Record and Message Protocol Layers (Stallings [5]). The SSL Architecture is defined as two layers that reside just above the transport in the TCP/IP protocol stack. All SSL messages originate in one of the message protocol modules and pass through the SLL Record Layer. The record layer handles physical record blocking and applies cryptographic transformations. Taken together, the four modules in the upper SSL layer form the Message Protocol Layer. All SSL input and output takes the form of messages defined by one of these four protocol modules. The Handshake Protocol establishes the security characteristics of an SSL session. Handshake messages negotiate the session’s cryptographic parameters and perform client/server authentication. The Change Cipher Spec Protocol is a one-message protocol used during the handshake negotiation. Its sole purpose is to signal that the newly negotiated cipher suite is now in effect. SSL Alert messages report status between communicating peers to indicate errors or normal connection termination. Application Data Protocol messages contain user data protected according to the current cryptographic parameters. The SSL Architecture does not specify any application programming interface. Each implementation is free to devise its own interface allowing clients to send and receive SSL messages. Accordingly, SSL implementations are not interchangeable from an application point of view. SSL makes a distinction between sessions and connections. A connection is an actual TCP connection between a client and server. The active connection initiator is always assigned the client role. On the other hand, an SSL session is an agreed upon set of cryptographic and non-cryptographic parameters. When a connection is initiated, the client can negotiate a new session or request that a previously negotiated session be re-used. Thus, sessions can outlive the connection under which they were created. Also, multiple connections can simultaneously participate in or use the same SSL session. Both the client and server must cache sessions if they are going to be re-used. Session re-use allows an abbreviated handshake protocol to be performed that avoids running the computationally intensive key exchange algorithm. The SSL specification also allows client and server applications to re-negotiate a session’s parameters anytime after the initial handshake completes. Application data messages may continue to flow during renegotiations. During any handshake negotiation, the client and server agree upon a cipher suite which 2 specifies a key exchange algorithm, a MAC algorithm and a cipher algorithm. The cryptographic parameters generated under the cipher suite are known as the cipher spec. 2.1.1 SSL Record Layer SSL Record Layer Data Fragment Compress Add MAC Encrypt Add Header Figure 2. The Record Layer (Stallings [5]). The SSL Record Layer interacts directly with TCP on the protocol stack. Figure 2 illustrates how data written from the Message Protocol layer to the network is processed by the Record layer. The data is first fragmented into blocks no larger than 214 (16384) bytes. Each block is then compressed to 2 14 + 1024 bytes or less (hopefully compression will not actually expand the data very often) 1. At this point, the compressed data block is hashed with a secret key to form a MAC as specified by the session’s current cipher suite. The MAC is then appended to the compressed data block and both are encrypted, again in accordance with the current cipher suite. The final result cannot be larger than 2 14 + 2048 bytes in length. A small header is then appended to the front of the block before it is transmitted to the TCP layer. The Record layer processing in the receiving application performs the inverse operations in reverse order to decode the SSL message. 1 Currently, SSL defines no compression algorithms other than the null algorithm. 3 2.1.2 SSL Handshake Protocol SSL Handshake Protocol Client Server ClientHello ServerHello Certificate * ServerKeyExchange * CertificateRequest * ServerHelloDone Certificate * ClientKeyExchange CertificateVerify * [ChangeCipherSpec] Finished [ChangeCipherSpec] Finished Application Data Application Data Figure 3. The new session handshake (SSL Specification [7]). The typical handshake protocol consists of the above sequence of messages. Note that a full handshake requires essentially two round trip message batches before application data can be securely transmitted with the newly negotiated cipher suite. Messages in Figure 3 annotated with an asterisk are optional messages that may be exchanged depending on the negotiation. 2.1.2.1 Cipher Spec Selection The client begins the handshake message by sending a ClientHello message which contains a timestamp, a block of 28 pseudo-random bytes, and the client’s list of preferred cipher suites. If a previously negotiated session is to be re-used, the client also sends a non-null session id. The server responds to the ClientHello message with a ServerHello message. The response contains a server timestamp, the server’s block of 28 pseudo-random bytes, the cipher suite selected by the server from the client’s list, the session id, and optional key exchange data. If the session chosen by the server was previously negotiated, then the server follows immediately with its ChangeCipherSpec and Finalize messages. The client will respond by sending ChangeCipherSpec and Finalize messages. If all validation checking is successful, application data can be transmitted securely under the pre-established session. Note that session re-use skips the secret key exchange and authentication processing steps. If the client requests a new session be negotiated or if the server decides to negotiate a new session, authentication and secret key exchange must be performed before final session validation can occur. 2.1.2.2 Secret Key Exchange SSL specifies a number of key exchange algorithms that can be used to generate shared secret material. This material is then used to generate keys and initialization vectors for use in MAC processing and encryption. Depending on the cipher suite chosen, key exchange information may be included in the ServerHello and/or the optional ServerKeyExchange message. The client will always respond with a ClientKeyExchange message to allow the key exchange algorithm to be run to completion on both client and server. 4 2.1.2.3 Authentication SSL specifies cipher suites that allow for anonymous, server authenticated and mutually authenticated sessions. The exchange of optional Certificate, CertificateRequest and CertificateVerify message permits the required level of authentication to be carried out using X.509v3 certificates. 2.1.2.4 Change Cipher Spec After all authentication and key exchange processing has completed, each party sends the ChangeCipherSpec and Finalize messages to the other. Figure 3 describes this message exchange during new session handshakes; the server transmits first during re-used session handshakes. In all negotiations, the ChangeCipherSpec message tells the receiver that all subsequent data read from this connection will use the just agreed upon cipher specifications. The Finalize message is always the first message sent using the new session parameters and is used to validate that both parties have participated in the negotiation and share the same secret information. This validation processing will be described in more detail in section “Integrity and Privacy” on page 5. 2.2 Secret Material Generation Generating Secrets RSA Diffie-Hellman Fortezza Pre-Master-Secret Master Secret Key-Block Material Keys/IV Figure 4. Shared secret material. The result of any of the three supported key exchange algorithms is known as the SSL pre-master secret. To generate the master secret, the pre-master secret is hashed six times using both MD5 and SHA. Client and server random values from the Hello messages, along with nonces that vary on each hash, are also used as input into each hash. In the end, the generated master secret is a 48 byte array of shared, secret material. The master secret, along with the client and server random values and other nonces, is then hashed using SHA. The resultant 20 byte value is then hashed again with the master secret using MD5 to yield a 16 byte result. The whole procedure is repeated (with varying nonces) as many times as necessary to generate enough secure material for a cipher write key, a MAC write key and a write initialization vector (needed for certain block cipher modes) for both the client and the server. The selected cipher suite determines the exact size of each of these six entities and, therefore, the number of hash iterations that must be performed. 2.2.1 Integrity and Privacy The Finalize message terminates the handshake protocol. It’s used to validate that both parties share the same secrets and can encrypt and decrypt using the newly agreed upon cipher suite. The data contained in 5 the Finalize message consists of an MD5 hash and an SHA hash. The data hashed in each case includes the master secret, a number of nonces, and all previous handshake messages that occurred during the negotiation. Hashing all previous handshake messages deters attacks in which messages are changed en route. Any message tampering will cause the receiver to calculate a different set of Finalize hashes than the sender. As with all messages, the contents of the Finalize message are then hashed during MAC processing in the record layer. This MAC hash includes as input the message sequence number along with the appropriate secret MAC write key. Read and write sequence numbers are maintained and incremented by each party on a connection. Hashing the appropriate sequence number in the MAC guards against replay attacks. The Finalize message contents and the MAC are then encrypted before being sent over the network. If the receiver cannot decrypt the message and validate the MAC, the MD5 and the SHA hash, the session is terminated before any application data is sent under the new cipher suite. In general, record layer MAC processing is designed to guard data integrity even if encryption keys and cipher initialization vectors have been compromised. In such cases, an attacker would still have to learn the MAC secret key to change the data or replay old messages. Since the read and write side of any connection uses different keys and initialization vectors, both sets of secrets need to be compromised to subvert traffic in both directions. 3 Pure Java SSL Design 3.1 Delivered Implementation The pure Java SSL implementation delivers the following subset of SSL functionality: 1. 2. 3. 4. Support for all SSL architectural features at the design level. Support for the ChangeCipherSpec/Alert/Data message protocols. Support for the Handshake protocol once per connection. Fully compliant cryptographic support for the SSL_NULL_WITH_NULL_NULL, SSL_DH_anon_WITH_DES_CBC_SHA, and SSL_DH_anon_3DES_EDE_CBC_SHA cipher suites. Features not implemented but anticipated in the design include: 1. 2. 3. 4. Session re-use. Session re-negotiation. X.509 authentication. Full-feature API support. The current code footprint is approximately 7000 lines of code in 65 classes. The SSL code calls Sun’s Java Cryptography Architecture 1.2 and Java Cryptography Extension 1.2 classes for all basic cryptographic algorithms (DES, 3DES, MD5, SHA, DH, etc.). 3.2 SSL API The application programming interface that Java applications use to interact with SSL is the API recommended by Sun in the javax.net and javax.net.ssl packages. Parts of the interface not directly related to input/output operations were not completely implemented. The SocketFactory and ServerSocketFactory classes are used to obtain SSLSocket and SSLServerSocket objects respectively. The initial SSL handshake takes place during the construction of all SSLSockets. The user can explicitly enable specific cipher suites for consideration at this time. By the time an application gets a reference to an SSLSocket, all security initialization has taken place and the connection will encrypt and decrypt all application data automatically. 6 SSLSocket is a subclass of the standard Socket class and SSLServerSocket subclasses ServerSocket. The accept() method of the ServerSocket class has been overridden in SSLServerSocket to return a connected SSLSocket object. All appropriate input/output methods have been overridden in SSLSocket to enable processing of all SSL protocol messages. 3.3 SSL High Level Design An SSL session defines the set of cryptographic and related parameters under which any SSL connection using that session operates. The Session class, which implements the SSLSession interface, provides the SSL session semantics. A Session contains of a session identifier, the peer certificate, the compression method, a number of CipherSpec objects, the master secret, and the resumable flag. A CipherSpec object contains the negotiated cryptographic parameters. These include the selected cipher algorithm, the cipher type, the MAC algorithm and the number of bytes in the MAC results, cipher keys and cipher initialization vectors. Each Session object contains four CipherSpec objects: the current read, current write, pending read and pending write objects. The SSLSocket class is a subclass of the standard TCP Socket class. It extends the Socket class with the following private fields: - A Session object. The client and server random blocks. The client’s write cipher keys, write MAC keys and write cipher initialization vectors. 2 The server’s write cipher keys, write MAC keys and write cipher initialization vectors. 2 The read and write message sequence numbers. The message protocol state machine. The underlying TCP socket’s input and output streams. The encryption and decryption cipher objects. The clientMode flag. The SSLSocket class embodies the implementation of an SSL connection. During construction, the object is assigned either the SSL client or server role and the SSL handshake protocol is executed accordingly. Upon successful construction, the object is returned to the calling application and may be used in wherever a standard TCP socket would be used. In addition to the Session and SSLSocket classes, the mechanisms that provide the SSL Record Layer and SSL Message Protocol Layer functionality also play a central design role. These will be described in the following sections. There are also five SSLException classes used to report problems to the calling application and a number of important auxiliary classes that manage SSL enumerations and constants. 3.4 Record Layer Each of the possible SSL messages in the Handshake, ChangeCipherSpec and Alert protocols have a corresponding Java class defined for it. These Msg* classes support the ExternInterface which.allows messages to be converted to byte arrays in the transmission format defined by the SSL specification. The record layer function is implemented in two methods of the SSLSocket class with the help of a number of auxiliary classes. The Record class encapsulates a message header and a payload byte array. The SSLSocket.readRecord() method reads data from the underlying TCP socket, processes the data according to the current cipher suite and the transformations defined by the record layer (see “SSL Record Layer “on page 3), and returns a newly created Record object. Upon completion, this Record object contains an SSL message in externalized form. 2 The current implementation associates the keys and initialization vectors with the session instead of the connection. The effect is the same because session re-use is not implemented. 7 The SSLSocket.writeRecord() method takes as input an SSL message object that has been externalized into a byte array and then encapsulated in a Record object. This byte array is then appropriately fragmented and transformed before being transmitted on the TCP socket. 3.5 Message Protocol Layer All data transmitted over SSL connections are messages of one the four SSL protocols in externalized form. A full SSL implementation allows session re-negotiation anytime after the initial handshake. Thus, messages from various protocols are often intermingled. A single state machine approach was taken to handle all message protocol layer traffic in a unified way. The StateMachine class is initialized on SSLSocket construction based on whether the connection endpoint is in client or server mode. Each mode has its own State/Event matrix statically initialized with its appropriate Actions. All incoming and outgoing messages are viewed as events which, when applied to the current state, execute a pre-configured action and return a new state. There are 11 client states, 8 server states and 15 events defined. There are 13 concrete Action classes currently implemented to execute state transitions. Consult the Appendix for a listing. The state machine receives events from the user application and the TCP network transport layer. Events received off the network are processed by the record layer and returned as Record objects to the Message Protocol layer. The Record objects are passed to the StateMachine.processRecord() method which deserializes them into message objects. Next, a State/Event matrix lookup returns an Action object whose nextState() method is called to effect the state transition. Application initiated reads are handled like any other network event. Therefore, the only event that does not originate from the network or from within the message protocol layer itself are application initiated writes. These events are packaged into Record objects, passed to the StateMachine.processRecord() method for state validation, and then sent through the record layer via the SSLSocket.writeRecord() call. Most of the SSL protocol semantics are implemented in the Action classes. Besides parameter processing and validation, handshake related actions often send response messages to the peer and perform cryptographic calculations. This work in performed in the nextState() methods of Action objects. All Actions return the new connection state. 3.6 Design Rationale The implemented CipherInputStream and CipherOutputStream classes extend the standard InputStream and OutputStream classes and present the user with the familiar stream-based model for input/output operations. Internally, however, our implementation manipulates byte arrays explicitly. The Message Protocol and Record layers are not designed as a set of filters successively applied to a data stream, even though the simplicity of such an organization is desirable. The reason for this lies in the way SSL is specified. For example, there is a need to store previously sent messages for later reprocessing. The Finalize message requires that all previous handshake messages, whether sent or received, be available for hashing to complete the handshake protocol (see “Integrity and Privacy” on page 5). Another complication arises from the fact that the SSL line protocol consists of a cleartext header followed by a ciphertext payload. Thus, a simple cipher stream filter operating on all data would not suffice internally. Similarly, on-the-fly compression, encryption and MAC processing do not conveniently allow resultant length calculations to be stored in message headers using a purely streambased approach. This non-stream approach led to the implementation of the custom ExternInterface for object serialization, rather than using the standard, stream-oriented, Serializable/Externalizable interfaces. 8 4 Conclusions Though simple in concept, an SSL implementation requires a fair amount of precise coding because of the complexity of the protocol and its dynamic features. Almost every handshake message has a variable format to accommodate the requirements of all the supported cryptographic algorithms. Thus, message encoding and decoding involves a fair amount of semantic checking even for well-formed messages. Many of the handshake messages themselves are optional depending on the negotiation, adding more complexity to enforcing protocol semantics. There are a number of design challenges that any SSL implementation must address. These include minimizing the number of times data is copied during message processing and securely handling data important to cryptographic algorithms. For example, once cryptographic data are no longer needed, it’s often prudent to overwrite the data before freeing the object that contains it. This occurs at a number of different processing stages are requires careful consideration when references to such data are passed around. 4.1 Future Work The availability of a free pure Java implementation of the non-proprietary parts (i.e., non-RSA) of SSL would probably be welcome by many Java programmers. If such an implementation were reasonably fast, dependable and supported all the advanced features of SSL (and soon, SSL’s successor, TLC), its value would even be greater. Time permitting, this is where the current implementation should go. In addition, an open implementation could also be designed in such a way as to allow new, non-standard algorithms to be easily incorporated into the SSL base. Customized versions of SSL could allow applications to define their own compression algorithms, use stronger ciphers or work with specialized hardware. 5 References [1] SSLeay by E.A. Young. Code and documentation found at http://psych.psy.uq.oz.au/~ftp/Crypto. [2] SSLREF by Netscape Communications Corporation. Code and documentation found at http://test-drive.netscape.com/tdrive-new/sslref.html. [3] SSL Plus by Consensus Development Corporation. Product information at http://www.consensus.com. [4] iSaSiLk by the Institute for Applied Information Processing and Communications (IAIK) at http://jcewww.iaik.tu-graz.ac.at/index.htm. [5] Cryptography and Network Security, Principles and Practice, William Stallings, Prentice-Hall Inc., 1999. [6] Applied Cryptography, Protocols Algorithms and Source Code in C, Bruce Schneier, John Wiley & Sons, Inc., 1996. [7] “The SSL Protocol, Version 3.0,” Alan Freier, Philip Karlton, Paul Kocher, Internet Draft, Transport Layer Security Working Group, November 18, 1996. 6 Appendix 6.1 SSL State Machine Events SMEVENT_START_HANDSHAKE SMEVENT_CLT_HELLO SMEVENT_CERTIFICATE 9 SMEVENT_CLT_KEYEXCHANGE SMEVENT_CLT_CERTIFICATE_VER SMEVENT_CHG_CIPHER SMEVENT_FINISHED SMEVENT_SVR_HELLO SMEVENT_SVR_KEYEXCHANGE SMEVENT_SVR_CERTIFICATE_REQ SMEVENT_SVR_HELLO_DONE SMEVENT_HELLO_REQ SMEVENT_FATAL_ALERT SMEVENT_CERTIFICATE_ALERT SMEVENT_DATA 6.2 SSL State Machine Client States SMSTATE_CLT_INIT SMSTATE_CLT_HELLO_SENT SMSTATE_CLT_HELLO_RECV SMSTATE_CLT_CERTIFICATE_RECV SMSTATE_CLT_KEYEXCHANGE_RECV SMSTATE_CLT_CERTIFICATE_REQ_RECV SMSTATE_CLT_HELLO_DONE_RECV SMSTATE_CLT_FINISHED_SENT SMSTATE_CLT_CHG_CIPHER_RECV SMSTATE_CLT_FINISHED SMSTATE_CLT_CLOSED 6.3 SSL State Machine Server States SMSTATE_SVR_INIT SMSTATE_SVR_HELLO_DONE_SENT SMSTATE_SVR_CERTIFICATE_RECV SMSTATE_SVR_KEYEXCHANGE_RECV SMSTATE_SVR_CERTIFICATE_VER_RECV SMSTATE_SVR_CHG_CIPHER_RECV SMSTATE_SVR_FINISHED SMSTATE_SVR_CLOSED 6.4 SSL Source File Listing javax\net\ServerSocketFactory.java javax\net\SocketFactory.java javax\net\ssl\Action.java javax\net\ssl\ActionCertificate.java javax\net\ssl\ActionChangeCipher.java javax\net\ssl\ActionCltCertificateVer.java javax\net\ssl\ActionCltHello.java javax\net\ssl\ActionCltKeyExchange.java javax\net\ssl\ActionFinished.java javax\net\ssl\ActionIgnore.java javax\net\ssl\ActionInterface.java javax\net\ssl\ActionInvalidEvent.java javax\net\ssl\ActionStartHandshake.java javax\net\ssl\ActionSvrCertificateReq.java javax\net\ssl\ActionSvrHello.java javax\net\ssl\ActionSvrHelloDone.java 10 javax\net\ssl\ActionSvrKeyExchange.java javax\net\ssl\CipherSpec.java javax\net\ssl\Constants.java javax\net\ssl\CryptoInputStream.java javax\net\ssl\CryptoOutputStream.java javax\net\ssl\Enums.java javax\net\ssl\Env.java javax\net\ssl\ExternInterface.java javax\net\ssl\HandshakeCompletedEvent.java javax\net\ssl\HandshakeCompletedListener.java javax\net\ssl\MsgAlert.java javax\net\ssl\MsgChangeCipher.java javax\net\ssl\MsgHdr.java javax\net\ssl\MsgHS.java javax\net\ssl\MsgHSCltHello.java javax\net\ssl\MsgHSCltKeyExchange.java javax\net\ssl\MsgHSFinished.java javax\net\ssl\MsgHSSvrHello.java javax\net\ssl\MsgHSSvrHelloDone.java javax\net\ssl\MsgHSSvrKeyExchange.java javax\net\ssl\MsgInterface.java javax\net\ssl\RandomWithTS.java javax\net\ssl\Record.java javax\net\ssl\Session.java javax\net\ssl\SessionCrypto.java javax\net\ssl\SessionId.java javax\net\ssl\SSLException.java javax\net\ssl\SSLHandshakeException.java javax\net\ssl\SSLKeyException.java javax\net\ssl\SSLPeerUnverifiedException.java javax\net\ssl\SSLProtocolException.java javax\net\ssl\SSLServerSocket.java javax\net\ssl\SSLServerSocketFactory.java javax\net\ssl\SSLSession.java javax\net\ssl\SSLSessionBindingEvent.java javax\net\ssl\SSLSessionBindingListener.java javax\net\ssl\SSLSessionContext.java javax\net\ssl\SSLSocket.java javax\net\ssl\SSLSocketFactory.java javax\net\ssl\StateMachine.java javax\net\ssl\WrappedByteArray.java 11