Securing Network Data

Team-Fly

Let's look at something a little more complicated. Suppose you wish to conceal the data you are sending over the network. The protected password example showed one way for a client to authenticate itself to the server, but we've still got the problem of eavesdroppers picking up credit card numbers or other sensitive information off the network.

This example consists of a matched MIDlet and servlet. The MIDlet, StealthMIDlet, has a simple user interface that allows you to enter a message. This message is encrypted using an RC4 stream cipher and sent to the servlet. On the server side, StealthServlet receives the encrypted message, decrypts it, and sends back its own encrypted message. Both messages pass over the insecure Internet as ciphertext, which is difficult for attackers to read without the proper keys.

RC4 is a symmetric encryption algorithm, which means that the same key is used to encrypt and decrypt data. StealthMIDlet and StealthServlet use two keys, one for each direction of data travel. One key is used to encrypt data in the MIDlet and decrypt it in the servlet; the other key encrypts data in the servlet and decrypts it in the MIDlet.

The servlet services multiple client MIDlets, each with their own encrypting and decrypting keys. Therefore, the servlet must keep track of two keys per client without getting them mixed up. It uses an HTTP session object to do this. Every time a client request is received, the servlet finds the corresponding ciphers in the session object. If the ciphers don't exist, they are created and initialized using client-specific keys.

This system provides both data confidentiality and authentication. The client and server are authenticated to each other because they must possess the correct keys to exchange data.

Figure 12-4 shows the main user interface of StealthMIDlet. It allows you to enter a message you want to encrypt and send to the server. When you're ready, hit the Send command to kick things off.


Figure 12-4: Enter your secret message in StealthMIDlet's main screen.

The servlet decrypts your message and sends back an encrypted response, which is displayed by the MIDlet as shown in Figure 12-5.


Figure 12-5: The servlet sends back its own secret message.

Using Bouncy Castle Ciphers

In the Bouncy Castle cryptography package, stream ciphers are represented by the org.bouncycastle.crypto.StreamCipher interface. You just need to initialize the cipher, using init(), and then you can encrypt or decrypt data using processBytes().

The Bouncy Castle package only provides one direct stream cipher implementation, org.bouncycastle.crypto.engines.RC4. If you'd prefer to use a different algorithm, you can use a block cipher instead. You can treat block ciphers like stream ciphers using Cipher Feedback (CFB) mode. In the Bouncy Castle package, this is implemented in the org.bouncycastle.crypto.StreamBlockCipher class. This technique gives you access to Bouncy Castle's considerable arsenal of block cipher implementations, from the wizened DES through Blowfish, Rijndael, and more. For more information on cipher modes, see Chapter 7 of Java Cryptography.

Our simple implementation instantiates a couple of RC4 objects, something like this:

 StreamCipher inCipher = new RC4Engine(); StreamCipher outCipher = new RC4Engine(); 

The ciphers need to be initialized before they can be used. The first parameter to init() should be true if the cipher will be encrypting data, false for decryption. The second parameter is essentially the key, wrapped up in a KeyParameter object.

 // Assume we have retrieved inKey and outKey, both byte arrays. inCipher.init(false, new KeyParameter(inKey)); outCipher.init(true, new KeyParameter(outKey)); 

To encrypt data, we just need to create an array to hold the ciphertext. Then call the stream cipher's processBytes() method to perform the encryption. The processBytes() method accepts the plaintext array, an index into the plaintext, the number of bytes that should be processed, the ciphertext array, and the index at which the ciphertext should be written.

 // Assume we have a byte array called plaintext. byte[] ciphertext = new byte[plaintext.length]; outCipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0); 

Decryption is identical, except you would use a cipher that has been initialized for decryption.

Implementation

The source code for StealthMIDlet is shown in Listing 12-5. This MIDlet has a simple user interface, initialized in the startApp() method. The MIDlet's ciphers are also created and initialized in startApp().

Listing 12-5: StealthMIDlet, a data encryption MIDlet.

start example
 import java.io.*; import javax.microedition.io.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import org.bouncycastle.crypto.StreamCipher; import org.bouncycastle.crypto.engines.RC4Engine; import org.bouncycastle.crypto.params.KeyParameter; public class StealthMIDlet extends MIDlet {   private Display mDisplay;   private TextBox mTextBox;   private String mSession;   private StreamCipher mOutCipher, mInCipher;   public StealthMIDlet() {     mOutCipher = new RC4Engine();     mInCipher = new RC4Engine();   }   public void startApp() {     if (mSession == null) {       // Load the keys from resource files.       byte[] inKey = getInKey();       byte[] outKey = getOutKey();       // Initialize the ciphers.       mOutCipher.init(true, new KeyParameter(outKey));       mInCipher.init(false, new KeyParameter(inKey));     }     mDisplay = Display.getDisplay(this);     if (mTextBox == null) {       mTextBox = new TextBox("StealthMIDlet", "The eagle has landed", 256, 0);       mTextBox.addCommand(new Command("Exit", Command.EXIT, 0));       mTextBox.addCommand(new Command("Send", Command.SCREEN, 0));       mTextBox.setCommandListener(new CommandListener() {         public void commandAction(Command c, Displayable s) {           if (c.getCommandType() == Command.EXIT) notifyDestroyed();           else send();         }       });     }     mDisplay.setCurrent(mTextBox);   }   private void send() {     // Encrypt our message.     byte[] plaintext = mTextBox.getString().getBytes();     byte[] ciphertext = new byte[plaintext.length];     mOutCipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);     char[] hexCiphertext = HexCodec.bytesToHex(ciphertext);     // Create the GET URL. Our user name and the encrypted, hex     //   encoded message are included as parameters. The user name     //   and base URL are retrieved as application properties.     String baseURL = getAppProperty("StealthMIDlet.baseURL");     URLBuilder ub = new URLBuilder(baseURL);     ub.addParameter("user", getAppProperty("StealthMIDlet.user"));     ub.addParameter("message", new String(hexCiphertext));     String url = ub.toString();     try {       // Query the server and retrieve the response.       HttpConnection hc = (HttpConnection)Connector.open(url);       if (mSession != null)         hc.setRequestProperty("cookie", mSession);       InputStream in = hc.openInputStream();       String cookie = hc.getHeaderField("Set-cookie");       if (cookie != null) {         int semicolon = cookie.indexOf(';');         mSession = cookie.substring(0, semicolon);       }       int length = (int)hc.getLength();       ciphertext = new byte[length];       in.read(ciphertext);       in.close();       hc.close();     }     catch (IOException ioe) {       Alert a = new Alert("Exception", ioe.toString(), null, null);       a.setTimeout(Alert.FOREVER);       mDisplay.setCurrent(a, mTextBox);     }     // Decrypt the server response.     String hex = new String(ciphertext);     byte[] dehexed = HexCodec.hexToBytes(hex.toCharArray());     byte[] deciphered = new byte[dehexed.length];     mInCipher.processBytes(dehexed, 0, dehexed.length, deciphered, 0);     String decipheredString = new String(deciphered);     Alert a = new Alert("Response", decipheredString, null, null);     a.setTimeout(Alert.FOREVER);     mDisplay.setCurrent(a, mTextBox);   }   // Normally you would probably read keys from resource files   //   in the MIDlet suite JAR, using the getResourceAsStream()   //   method in Class. Here I just use hardcoded values that match   //   the hardcoded values in StealthServlet.   private byte[] getInKey() {     return "Incoming MIDlet key".getBytes();   }   private byte[] getOutKey() {     return "Outgoing MIDlet key".getBytes();   }   public void pauseApp() {}   public void destroyApp(boolean unconditional) {} } 
end example

When the user invokes the Send command, StealthMIDlet encrypts the user's message with its outgoing cipher. It then encodes the ciphertext as hexadecimal text in preparation for sending it to the servlet. The user's name and the ciphertext is packaged into a GET URL and sent to the server. Additionally, StealthMIDlet keeps track of a cookie that is used for session tracking. If the server sends back a session id cookie, it is saved in StealthMIDlet's mSession member variable. The saved cookie is sent with each subsequent request. This allows the server to retrieve session information for this client. Without this session information, each HTTP request from client to server would need to reinitialize the ciphers so they didn't get unsynchronized.

StealthMIDlet retrieves the response from the server as hexadecimal ciphertext. It converts the string to a byte array, and then decrypts the byte array using the MIDlet's incoming cipher. The decrypted message is displayed in an Alert.

StealthMIDlet makes use of the same HexCodec and URLBuilder classes that were presented earlier in this chapter.

On the server side, things are a little more complicated. StealthServlet should be capable of handling multiple clients, which means it should maintain a pair of ciphers for each user that connects. This is done using HTTP sessions, one session per user. When a client request comes in, StealthServlet attempts to find two ciphers in the user's session. If they don't exist, as will be the case the first time a user connects to the servlet, new ciphers are created. The ciphers are initialized using keys that are unique to each user. Exactly how these keys are located is left up to you. In this simple implementation, the getInKey() and getOutKey() methods are hard-coded.

You should notice that the keys on the servlet side appear to be reversed from the MIDlet. This is because the servlet's incoming cipher should decrypt using the same key as the MIDlet's outgoing cipher.

Once StealthServlet has located or created the ciphers that correspond to a particular user, it decrypts the incoming message and prints it out to the server console. Then it encrypts a response message (also hard-coded) and sends the response back to the MIDlet.

The entire StealthServlet class is shown in Listing 12-6.

Listing 12-6: The source code for StealthServlet.

start example
 import javax.servlet.http.*; import javax.servlet.*; import java.io.*; import java.util.*; import org.bouncycastle.crypto.StreamCipher; import org.bouncycastle.crypto.engines.RC4Engine; import org.bouncycastle.crypto.params.KeyParameter; public class StealthServlet extends HttpServlet {   public void doGet(HttpServletRequest request,       HttpServletResponse response)       throws ServletException, IOException {     String user = request.getParameter("user");     // Try to find the user's cipher pair.     HttpSession session = request.getSession();     StreamCipher inCipher = (StreamCipher)session.getAttribute("inCipher");     StreamCipher outCipher = (StreamCipher)session.getAttribute("outCipher");     // If the ciphers aren't found, create and initialize a new pair.     if (inCipher == null && outCipher == null) {       // Retrieve the client's keys.       byte[] inKey = getInKey(user);       byte[] outKey = getOutKey(user);       // Create and initialize the ciphers.       inCipher = new RC4Engine();       outCipher = new RC4Engine();       inCipher.init(true, new KeyParameter(inKey));       outCipher.init(false, new KeyParameter(outKey));       // Now put them in the session object.       session.setAttribute("inCipher", inCipher);       session.setAttribute("outCipher", outCipher);     }     // Retrieve the client's message.     String clientHex = request.getParameter("message");     byte[] clientCiphertext = HexCodec.hexToBytes(clientHex);     byte[] clientDecrypted = new byte[clientCiphertext.length];     inCipher.processBytes(clientCiphertext, 0, clientCiphertext.length,         clientDecrypted, 0);     System.out.println("message = " + new String(clientDecrypted));     // Create the response message.     String message = "Hello, this is StealthServlet.";     // Encrypt the message.     byte[] plaintext = message.getBytes();     byte[] ciphertext = new byte[plaintext.length];     outCipher.processBytes(plaintext, 0, plaintext.length, ciphertext, 0);     char[] hexCiphertext = HexCodec.bytesToHex(ciphertext);     response.setContentType("text/plain");     response.setContentLength(hexCiphertext.length);     PrintWriter out = response.getWriter();     out.println(hexCiphertext);   }   private byte[] getInKey(String user) {     return "Outgoing MIDlet key".getBytes();   }   private byte[] getOutKey(String user) {     return "Incoming MIDlet key".getBytes();   } } 
end example

Suggested Enhancements

A few relatively minor enhancements would make this a serious application. The first area to tackle is key handling. StealthMIDlet should load its keys from resource files in the MIDlet suite JAR rather than using hard-coded values. This is possible using the getResourceAsStream() method in Class.

Likewise, StealthServlet should locate and load keys from a database or some kind of file respository. Something as simple as a standard naming scheme based on user names might be sufficient.

The keys themselves should be larger than the hard-coded samples here-how large is up to you. As long ago as 1996, the U.S. government was fairly sanguine about allowing the export of 40 bit RC4 technology, so you can rest assured that 40 bits is probably way too short. As the key length increases, of course, you may start to have memory or performance problems, particularly in a constrained environment like MIDP. Try to find a good balance between performance and security.

Furthermore, you might want to consider using a different algorithm, like Blowfish or Rijndael. The Bouncy Castle cryptography package has plenty of options in the org.bouncycastle.crypto.engines package. As I mentioned, you can treat a block cipher like a stream cipher using CFB mode.

Finally, the communication between the servlet and the MIDlet could be improved. It would be nice, for example, if the servlet had some way to tell the MIDlet it couldn't find a session. It's possible that the MIDlet will send up a cookie for a session that has expired on the server side. In the current implementation, the servlet will create a new set of ciphers, ones that are not synchronized with the MIDlet's ciphers. One way to solve this problem would be to have the servlet pass a response code to the MIDlet. One response code might mean, "I lost your session. Please reinitialize your ciphers and try again."

Deployment Issues

Suppose you dressed up this example and incorporated it into a product. What are the issues with distribution? For each copy of your software, you need to generate a pair of keys. These keys are stored as resource files inside the MIDlet suite JAR, which means that for each copy of your software, you'll need to generate a unique MIDlet suite JAR. At the same time, you need to save the keys on the server side somewhere. When the client MIDlet makes a connection, you need to be able to find the corresponding keys. None of this is particularly difficult, and it can be automated.

The MIDlet suite JAR contains keys that should be secret. Therefore, it is a security risk to transmit the JAR to a customer over the Internet. You might transfer it via HTTPS to a customer's browser, and then rely on him or her to install the MIDlet suite on a mobile telephone or other small device via a serial cable.


Team-Fly


Wireless Java. Developing with J2ME
ColdFusion MX Professional Projects
ISBN: 1590590775
EAN: 2147483647
Year: 2000
Pages: 129

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net