The absence of standard and familiar Java APIs presents one of the biggest obstacles when developing for Java 2 Micro Edition (J2ME). Since J2ME targets much smaller devices, it lacks many libraries and features that are normally available in larger Java installations.
On Java 2 Standard Edition (J2SE) and Java 2 Enterprise Edition (J2EE), the Java Cryptography Architecture and Java Cryptography Extension help streamline the process of adding security to a project. The J2ME platform, however, lacks crypto APIs.
Of course, many J2ME devices, such as Palm handhelds, aren't suited for common cryptographic functions. Their constrained RAM and processors can make operations, such as public key cryptography, infeasible. Still, there's a real need for security on small devices, and they can perform a useful subset of cryptographic operations.
This article explores the use of cryptography on J2ME devices and walks though an implementation of a simple password-based encrypted database program. The database holds user login information and can be used to manage accounts for different machines and Web sites. Although the example targets J2ME for Palm handhelds, its design maximizes portability.
Ciphers
Ciphers are algorithms that use a key to transform a message (plaintext) into an encrypted message (ciphertext) or vice versa. Symmetric ciphers rely on a single key for both encryption and decryption. DES, TripleDES (also called DESede), and Blowfish are symmetric ciphers. All three operate on 64-bit input blocks and produce 64-bit output blocks.
Modes are applied to ciphers to define their behavior. In the simplest mode, Electronic Code Book (ECB), each input block always produces the same output block. Although this makes implementation simple, the one-to-one mapping of input to output leaves the ciphertext vulnerable to attacks. Patterns in the encrypted data become apparent in the ciphertext if there are repeated input blocks, making cryptanalysis much easier.
The cipher block chaining (CBC) mode strengthens the ciphertext by making each block of ciphertext dependent on both the corresponding block of plaintext and all previous plaintext blocks as well. It uses an XOR feedback register to modify the plaintext before encrypting and after decrypting. At the start of an encryption or decryption operation, the XOR register is set to a randomly generated initialization vector (IV) that's transmitted or stored along with the ciphertext. It's not terribly important that the IV is truly random, but it should be different each time plaintext is encrypted with the same key.
Since plaintexts might be any length, extra bytes must be added to pad the input to a multiple of the block size in length. Public Key Cryptography Standard #5 (PKCS#5) padding is the most common padding scheme for symmetric block ciphers.
Choosing appropriate ciphers for J2ME requires special care, since most J2ME devices have relatively slow processors. Table 1 shows some rough benchmarks for a PalmV. The key setup column gives the time it takes for an algorithm to initialize itself with a key. The encryption column shows the amount of time it takes to encrypt one block (8 bytes) of data. Naturally, different implementations of the algorithms will have different performance. The data in Table 1 is by no means a definitive performance analysis.
For these implementations, TripleDES and Blowfish take significantly longer to initialize than DES, but Blowfish encrypts data faster than DES or TripleDES. Depending on the application, either key setup or encryption might dominate the running time. Choosing an appropriate cipher always involves weighing the trade-off between performance and the desired level of security.
Hashes, Passwords, and Keys
Hash algorithms are one-way functions that turn an arbitrary message of bytes into a fixed-length digest, or fingerprint, usually not much more than a dozen bytes long. Good hash algorithms should be collision-resistant, making it computationally infeasible to derive the original message from the digest. Even a minor change in the input should drastically change the resulting fingerprint. MD5 and SHA1 are two commonly used hash functions.
Applications commonly use hash functions to transform user-supplied passwords into symmetric keys. Several standards define techniques to convert passwords, but PKCS#5 specifies a simple technique: convert the password string to a byte array and use it as the input to a hash algorithm. The resulting digest serves as the key.
Running the password through a hash function serves several purposes. First, the hash function transforms variable length passwords into uniform-length hashes. Also, character strings follow certain patterns and good symmetric keys should be as close to random bytes as possible. Hashing the password removes the patterns, making cryptanalysis much harder.
Using a password-based key means that the key is only as secure as the password. If the number of likely passwords is smaller than the total number of keys, it's more efficient to search through all possible passwords than to attack the key directly. It's pointless to use a cipher with a huge key space unless users choose long passwords or passphrases. DES has a key space of 64 bits, of which only 56 are actually used. That's roughly equivalent to randomly selecting nine characters from the set upper- and lowercase letters and numbers. Users are unlikely to choose passwords longer than that for J2ME programs since text entry is typically inconvenient.
The dictionary attack is a common way to attack password-based systems. In it an attacker computes a list of keys from a dictionary of likely passwords. Once the attacker has obtained the encrypted data, he or she uses each potential key to decrypt the ciphertext until one of them produces meaningful plaintext.
A technique called salt helps guard against dictionary attacks. Salt is a set of random bytes that are stored unencrypted along with the ciphertext and are added to the password before it's hashed. It prevents an attacker from precomputing a list of keys since the password dictionary generates a different set of keys for each different salt value. Salt doesn't completely defeat a dictionary attack, but it does make it much more computationally intensive.
Implementing an Encrypted Password Database
Deploying on J2ME requires a virtual machine, configuration, and profile. Sun's KVM is the most compact virtual machine that fulfills the J2ME requirements. Configurations define a set of APIs for use on certain classes of devices. For example, the Connected Limited Device Configuration (CLDC) targets compact devices, such as cell phones and PDAs. Profiles extend the configuration and add functionality applicable to a particular domain. As of this writing, the Mobile Information Device Profile (MIDP) is the only profile that's been finalized. It contains functionality intended for cell phones and two-way pagers.
The primary design goal for this application is to make it as simple as possible to port to different J2ME configurations and profiles. As presented, it targets the CLDC and a PalmOS-specific profile. It contains two device-specific groups of functionality: the GUI and the storage mechanism. The design accommodates different GUIs by separating the application's business logic and presentation. It solves the device-dependent storage problem using the Template pattern. An abstract superclass relies on methods in the concrete subclasses to store the data.
Another important goal is maintaining acceptable performance on the target device. Therefore, the design encapsulates the usage of the computationally intensive algorithms in a single class. Swapping in alternate hash functions or encryption algorithms makes it possible to customize the program for the performance characteristics of different devices. For this application, SHA1 and DES provide reasonable performance and are as secure as the expected passwords (see Figure 1)
Cryptographic Algorithms
The supplied code includes DES and SHA1 implementations in the crypto package. Both take care of padding the input data if needed. The SHA1 class uses the padding defined by the SHA1 algorithm and DES uses PKCS#5 padding and CBC mode. The algorithms' inner workings are beyond the scope of this article. Other sources, such as Applied Cryptography by Bruce Schneier, discuss the algorithms and cryptanalysis in detail.
The DES class throws a CipherException if it encounters a problem encrypting or decrypting. Possible problems include invalid parameters (like an IV that's not 8-bytes long or attempting to decrypt an array that's not a multiple of 8 bytes in length) and invalid padding after decryption. Normally, using the wrong key or IV will result in improperly padded plaintext.
Data Objects
Two data classes, SiteList and SiteInfo, encapsulate the user's information. SiteList keeps track of all the SiteInfos, each of which stores an individual site's profile. Since J2ME doesn't support serialization natively, the classes handle saving and restoring their own state in their load() and save() methods. They use simple byte-array structures as shown in Figure 2.
The SiteInfo class wraps four strings: the site's name, location, username, and password. Each setter method trims the string to ensure it will be less than 127-bytes long (Java's maximum byte value). The class handles serializing its location, username, and password.
The SiteList class keeps track of all the SiteInfos in the system. It holds all the SiteInfos in a vector and is responsible for storing their name strings when it's serialized.
Since hashing the user's password, setting DES up for decryption, and decrypting the data are all computationally intensive operations, it's important to decrypt information only as needed. Keeping the site names in one byte array allows the application to start up faster because the application needs to display only the site names at startup. It can wait until the user selects a particular site to load and decrypts the rest of the SiteInfo data.
Persistent EncryptedStorage
The abstract class EncryptedStorage handles the application's cryptography. It turns the password and salt into a DES key and deals with encrypting and decrypting the serialized SiteList and SiteInfos. When saving data it first creates an initialization vector. Then it encrypts the plaintext bytes and concatenates the IV with the ciphertext. EncryptedStorage then uses the Template pattern and calls an abstract method to store the combined array in the device's storage system. The class gets random bytes from java.util.Random, which produces bytes that are random enough for salt and IV values, but not for keys. Listing 1 shows the code that does the hashing and encrypting.
In this case, PalmEncryptedStorage subclasses EncryptedStorage and talks directly with the Palm's storage system, the database. The Palm-specific classes include com.sun.kjava.Database, a class that permits access to an application's database on the Palm, stored in a set of records. At startup the PalmEncryptedStorage checks if it has already created a database. If not, it makes a new one, generates salt, and stores it in record 0. The IV and ciphertext for the SiteList go in record 1. SiteInfos get stuffed into subsequent records. The SiteInfo at index 0 in the SiteList is kept in record 2, index 1 is kept in record 3, and so forth.
Listing 2 illustrates how the PalmEncryptedStorage interacts with the Palm's database. Three values identify each database: a creator ID (managed by Palm Computing at www.palmos.com/dev/tech/palmos/creatorid/), a database type ID, (separates different applications from a single creator ID), and a database type string (shown in the list of databases in the Palm's memory-usage list).
The class's constructor tries to open an existing database. If it can't find one, it creates a new one, opens it, and stores the 8-byte salt value. The setRecordBytes() method sets the bytes of the specified record index. If a record at that index already exists, it's overwritten; if no record exists, a new record is appended.
User Interface
A class called com.sun.kjava.Spotlet draws the GUI and handles user input. Event handling is very different from Swing's event handling. A Spotlet must register itself to receive user input events and only one can be registered at a time. For stylus events, the Spotlet gets the (x, y) coordinates and must interrogate each GUI widget to see if it occupies those pixels and then acts appropriately. For example, if a user clicks on a text field it should get the focus. For text input, the Spotlet must check if any of the GUI components have the focus and then pass it to that component. Listing 3 is a code snippet from SiteListSpotlet that does event handling.
In this program an abstract class, AbstractSpotlet, draws the familiar Palm UI tab at the top of the screen to show the user which application is running. It also keeps a reference to the PalmPassword object so that the Spotlet can interact with the rest of the application. There are three subclasses, one for each screen in the UI.
The first is the LoginSpotlet. It gets the password from the user and informs him or her if the password was not correct. SiteListSpotlet shows the list of sites and permits the user to either select one to display or create a new entry (see Figure 3). The SiteInfoSpotlet allows the user to view and edit a site's information and then save it (see Figure 4).
Unfortunately, the Palm-specific classes don't include a list GUI widget. The ListBox class provided works like list boxes in other Palm applications. It's backed by a ListBoxModel that supplies the data to be displayed and sends selection events to a ListBoxListener. It calculates the number of elements that can fit vertically, based on the size of the box, and draws as many elements from the ListBoxModel as can fit. It uses the Palm-specific ScrollBar widget to handle scrolling, shifting the elements displayed in response to user input. If the user selects one of the elements in the list, the ListBox notifies its ListBoxListener that an element in the list was selected.
The PalmPassword class contains the main method for the application and acts as a controller for the system. It instantiates the three Spotlets that all keep a reference to the PalmPassword object. When the Spotlets get events from the user, they call methods in the controller to process the request and display other Spotlets as needed.
Building and Running the Application
Building an application for J2ME devices takes a few more steps than building for J2SE. J2ME runtime environments work with the same Java compilers as J2SE. Two additional steps are needed though: preverifying the class files and building the preverified files into a Palm application. Preverification checks each class file to make sure it's a valid Java class file, and stores verification information in it. This minimizes the amount of verification the KVM needs to do, improving runtime performance.
Setting up an IDE or a build tool to handle the process will save a lot of time, as will installing an emulator for the target device on the development workstation. Sun provides detailed installation and usage instructions for the J2ME libraries and utilities.The page also describes how to get a Palm emulator and transfer a ROM image from a Palm device.
Unfortunately, the section labeled "The J2ME CLDC Development Environment" contains a couple of errors. So run the following commands from the c:\j2me_cldc\tools\palm\ directory instead:
md classes
javac src\palm\database\*.java -d classes
copy src\palm\database\DefaultTiny.bmp
classes\palm\database
copy src\palm\database\Wrapper.prc
classes\palm\database
The build.bat file included with the source compiles the source code, preverifies the compiled classes, stores the results in .\preverified, and builds them into a Palm application. Be sure to edit the first line of the batch file to point to the proper directory. After installing the KVM as documented by Sun, simply drag-and-drop PalmPassword.prc onto an emulator or HotSync it to a Palm device and run it.
Summary
The code provided with this article can be downloaded from the JDJ Web site. It gives a solid base for cryptography in J2ME and shows how to separate device-specific code from the main application logic, both for the GUI and for persistent storage. In addition, it provides an implementation of password-based encryption without relying on other APIs. Different hash and encryption algorithms can easily be swapped in to increase the cryptographic security of the data.
Although the example program is functional, it's still far from feature-complete. As presented there's no way to delete a SiteInfo from the list or sort the list. The application could give the user some sort of progress bar during the password hashing and key setup. More fields might be added to let the user input more information about a site, or the ListBox could be changed to display more than just the site's name. Also, if something goes wrong with encryption or decryption, it would be nice to inform the user instead of just swallowing the exception.
A conduit that synchronizes data with an application on the user's desktop machine might also be useful. Because all the device-specific code is kept isolated, it would be simple to create a Swing-based application that could read the encrypted data and allow the user to manage the password database.
It's important to remember that cryptography doesn't automatically make a system secure. For this application, a poorly chosen password will reduce the security significantly. Also, it's necessary to exit the program after use or the passwords will be vulnerable to anyone who happens upon the device. Even the best security is still only risk management.
Just posted is a follow-up to my article in the current issue of JDJ, describing how to port my application to MIDP and including the full source code. The link is available at http://www.isnetworks.com/j2meCrypto/. This is a follow-up to "J2ME Cryptography," an article published in the July 2001 issue of Java Developer's Journal.
Published Jul. 1, 2001— Reads 14,620
Copyright © 2008 SYS-CON Media. All Rights Reserved.
Listing 1
public void init( String password ) {
byte[] toBeHashed = concat( getSalt(),
password.getBytes() );
byte[] hash = mSHA1.digest( toBeHashed );
byte[] keyBytes = new byte[ 8 ];
System.arraycopy( hash, 0, keyBytes, 0, 8 );
mDES.setKey( keyBytes );
}
private byte[] encrypt( byte[] plaintext )
throws StorageException {
try {
byte[] iv = getRandomBytes( 8 );
byte[] ciphertext = mDES.encrypt( plaintext,
iv );
return concat( iv, ciphertext );
}
catch( CipherException e ) {
throw new StorageException( e.getMessage() );
}
}
Listing 2
public PalmEncryptedStorage() {
mDatabase = new Database( DB_TYPE_ID,
CREATOR_ID, Database.READWRITE );
if ( !mDatabase.isOpen() ) {
Database.create( 0, DB_TYPE_STRING,
CREATOR_ID, DB_TYPE_ID, false );
mDatabase = new Database( DB_TYPE_ID,
CREATOR_ID, Database.READWRITE );
mDatabase.addRecord( getRandomBytes( 8 ) );
}
}
private void setRecordBytes( int recordNumber,
byte[] bytes ) {
if ( mDatabase.getNumberOfRecords() <=
recordNumber ) {
mDatabase.addRecord( bytes );
}
else {
mDatabase.setRecord( recordNumber, bytes );
}
}
Listing 3
public void penDown( int x, int y ) {
if ( mListBox.contains( x, y ) ) {
mListBox.handlePenDown( x, y );
}
else if ( mNewButton.pressed( x, y ) ) {
mPalmPassword.showSiteInfo( new SiteInfo() );
}
}
public void penMove( int x, int y ) {
if ( mListBox.contains( x, y ) ) {
mListBox.handlePenMove( x, y );
}
}
Monday, December 29, 2008
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment