Secure Chat Service Final Report
John Porter
CSC 8530
4/17/2000
Table of Contents
The Secure Chat Service provides a facility that allows two users to exchange encrypted messages with one another in real time. The service consists primarily of two parts, the chat server and the chat client. The chat server exists primarily to authenticate uses of the system and to bring chat participants together. The chat client provides a convenient interface to facilitate secure, real-time communications between two parties.
I began this project with several goals in mind, the first of which was to learn more about computer security in the areas of data encryption and secure transmission of data. Secondly, I was interested in learning more about Java. I've had a great deal of C++ programming experience which includes network and multi-threaded programming. I wanted to explore these same topics in Java as well as get and introductory overview of Java Swing.
There are two means of data encryption used in the Secure Chat Service. The first is an asymmetric cipher used for encrypting messages exchanged between the chat clients and the chat server. This cipher consists of a public key, which can be freely distributed, and a private key which is secret. The ElGamal encryption algorithm was used to implement this cipher. ElGamal is named for its creator, Taher ElGamal. The patent for this algorithm has recently expired making it freely available and thus a good choice for this project. The ElGamal cipher is implemented in this project as a block cipher. The block sizes of the cipher depend on the size of its key, meaning that larger key sizes allow larger blocks of data to be encrypted at a time, data to be encrypted that does not completely fill a block will be padded to the correct length. Larger key sizes also increase the security of the system. 512 bit ElGamal keys are used in the Secure Chat Service.
The second type of encryption used in the system uses a DES symmetric cipher. This cipher is used to encrypt the conversation between two chat clients. The DES cipher is implemented in cipher feedback (CFB) mode. CFB mode allows a block cipher to act like a stream cipher. A cipher in CFB mode can encrypt pieces of data that are smaller that the block size. In fact, CFB can be used to encrypt any data size, from one bit to the block size. CFB is commonly used to encrypt or decrypt one byte at a time, which is called CFB8. CFB8 is used by the secure chat clients to encrypt and decrypt their conversation one character at a time. This type of encryption is well suited for just such a stream based application.
The Java security API provides a set of packages that are used for writing secure programs. This API is split into two pieces. The overall design of the cryptography classes is governed by the Java Cryptography Architecture (JCA). The JCA specifies design patterns and an extensible architecture for defining cryptographic concepts and algorithms. The JCA is designed to separate cryptographic concepts from their implementations. The concepts are encapsulated by classes in the java.security and javax.crypto packages. Implementations are supplied by cryptographic providers. The JDK 1.2 comes with a default provider that implements a few cryptographic algorithms. The second piece of the API is the Java Cryptography Extension (JCE) and is for U.S. and Canadian distribution only. The JCE is an extension of the JCA and includes another cryptographic provider called SunJCE. These two packages contain interfaces and classes that allow programs to easily manipulate certificates, ciphers, keys, signatures and other cryptographic constructs.
Chat Server Architecture
The chat server's primary role is to authenticate users requesting services of the chat system and to connect authenticated users who wish to participate in a chat. The server uses two support files to enable it to perform its required functions. The first file contains the userids and passwords for all users registered in the system. The second file is the key store file that contains the servers public/private key pair as well as the public keys of all users registered in the system. All messages sent to the server are encrypted in the server's public key and all messages sent from the server to clients are encrypted with the client's public key. A diagram of this interaction can be found here. Maintenance of these two files is currently handled via command line utility programs.
Communication between clients and the server take place over a TCP/IP socket connection. Each time the server receives a connection from a client it spawns a new thread to handle the request. This helps to ensure a more timely response to client requests. The userid and password of the user is included in each request message. If the user authentication is successful, the requested operation is performed and a response message is returned. If the authentication fails or the requested operation cannot be performed, an error message will be returned to the client. The chat server accepts four types of messages from clients detailed below.
Messages sent to, and received from the server are implemented as java objects. Java serialization makes transmission of an object over a TCP/IP connection a rather simple task. The JCE contains a class which makes the transmission of an encrypted object just as easy. The SealedObject class is part of the javax.crypto package. This class is essentially just a wrapper around an object and an associated cipher with which to encrypt and decrypt the object. The constructor for the SealedObject class takes an Object and a Cipher and encrypts the object with the cipher at construct time. Furthermore, SealedObject also implements the Serializable interface and can therefore be serialized across any stream including our TCP/IP connection. When this SealedObject is received at its destination, the receiver calls the getObject method on the SealedObject, supplying the appropriate cipher for decryption and thus retrieving the original object.
Chat Client Architecture
The chat client is a multi-threaded Java Swing application. One thread is used to process events from the user interface while a second thread listens for incoming chat requests from the server and handles chat communications once a peer connection has been established. Like the chat server, each chat client has a file that contains its pubic/private key pair as well as the public key of the server. When a user starts the chat client program, he is presented with a logon screen requesting the user's userid and password. Upon entering this information, the client program logs onto the server and saves the userid and password for future use since all messages exchanged with the server require this information for authentication. The client uses the ElGamal asymmetric cipher to communicate with the server as discussed previously. It also uses a DES symmetric cipher to encrypt its conversation with another client. Once a DES key is obtained from the server via a begin chat request message. The client initiating the chat connects to its peer using the connection information it got from the server along with the DES key. This connection is a TCP/IP socket connection.
The JCE contains a pair of classes that greatly aid in establishing an encrypted conversation between two chat clients. These classes are the CipherInputStream and the CipherOutputStream which are part of the javax.crypto package. These two streams wrap around an existing stream and a cipher such that any data sent through the stream will be encrypted by the associated cipher and any data received from the stream will be decrypted by the associated cipher. The chat client makes use of these classes by wrapping the stream obtained from the socket with a DES cipher. Once this is done any character entered by the user in the input window is transmitted through the CipherOutputStream, encrypted, and transmitted to the chat client's peer. The receiving client reads characters that are decrypted automatically by the CipherInputStream and displays them in its output window.
The Java Cryptography Architecture provides a straightforward API for adding security features to any program. One of the most powerful features of the JCA is its concept of security providers. The provider architecture allows developers to write to a standard interface without having to worry about any of the specific implementation details for a particular encryption or security algorithm. Developers can then plug in any provider that is appropriate to obtain the desired security functionality. For example, I had originally intended to use PGP as the asymmetric key algorithm used to communicate between clients and the server. The only one available for the JCE was very beta. I opted for the more stable ElGamal implementation instead. Once the PGP provider becomes more robust it is a relatively simple matter to swap the providers and generate new keys for the clients and the server without having to change any of the program logic.
In the original proposal for the chat server I had planned to use certificates as a means of adding a layer of validity to the public keys used by the clients and server. This was a relatively important feature since the chat server was going to act as a key server as well. While the JCA has classes for reading and extracting public keys from certificates, oddly enough there is no programmatic way to generate a certificate. Certificates were not vital to the design of the system since users are required to log into the chat server for validation anyway which provides a layer of confidence that certificates would only serve to reinforce. Without the use of certificates, the key server aspect of the chat server became less interesting so I focused instead on the differing means of encryption that could be used. Namely asymmetric cryptography from client to server and symmetric cryptography from client to client.
The encrypted communication from client to client was relatively straight forward. By utilizing a stream cipher, the DES encryption used between clients fits in nicely with the java.io package. The Cipher streams can be wrapped around any other stream providing simple encrypted streams. The primary challenge I faced was with the client to server communication. I wanted to model the messages passed between these two processes as classes which is appropriate for Java's object oriented nature. Objects can easily be serialized across a network connection, but the problem remained as to how to apply the appropriate encryption before the serialization took place without having to write a significant amount of error prone marshalling code. I struggled with this for some time before coming across the SealedObject in the JCE. The SealedObject class provides a very clean way to encrypt any object with any cipher.
As with most undertakings, once you get past the initial learning curve, things get much easier. This is certainly true of Java and the JCA. The networking and threading aspects of the project were very straight forward compared to the same concepts in C++. That allowed me to focus most of my efforts on understanding the Java Cryptography Architecture which was my primary goal. I found the JCA very useful for implementing all of the cryptographic concepts I was exploring. Its difficult to say how much, if any, performance was lost by implementing the project in Java as compared to C++, but in retrospection, developing this project in any other language would have required many more development hours. Overall performance seems quite satisfactory once past the rather slow cipher initialization phase.
Garfinkel, Simson. PGP: Pretty Good Privacy
O'Reilly & Associates, Inc. 1995
Knudsen, Jonathan. Java Cryptography
O'Reilly & Associates, Inc. 1998
Flanagan, David. Java in a Nutshell A Desktop Quick Reference
O'Reilly & Associates, Inc. 1999