Bouncy Castle Crypto API is a lightweight cryptography API in Java. It also have a J2ME version. This tip will give you an example of encryption and decryption of messages using Bouncy Castle Crypto API.

Let’s start with the utility class we will use in our midlet to encrypt/decrypt arbitrary text messages. Encryptor class below allows you to encrypt and decrypt arbitrary messages using encryptString and decryptString methods:

 import org.bouncycastle.crypto.*;
import org.bouncycastle.crypto.engines.*;
import org.bouncycastle.crypto.modes.*;
import org.bouncycastle.crypto.params.*;

// A simple example that uses the Bouncy Castle
// lightweight cryptography API to perform DES
// encryption of arbitrary data.

public class Encryptor {
    
    private BufferedBlockCipher cipher;
    private KeyParameter key;
    
    // Initialize the cryptographic engine.
    // The key array should be at least 8 bytes long.
    
    public Encryptor( byte[] key ){
        /*
        cipher = new PaddedBlockCipher(
                 new CBCBlockCipher(
                 new DESEngine() ) );
         */
        
        cipher = new PaddedBlockCipher(
                new CBCBlockCipher(
                new BlowfishEngine() ) );
        
        this.key = new KeyParameter( key );
    }
    
    // Initialize the cryptographic engine.
    // The string should be at least 8 chars long.
    
    public Encryptor( String key ){
        this( key.getBytes() );
    }
    
    // Private routine that does the gritty work.
    
    private byte[] callCipher( byte[] data )
    throws CryptoException {
        int    size =
                cipher.getOutputSize( data.length );
        byte[] result = new byte[ size ];
        int    olen = cipher.processBytes( data, 0,
                data.length, result, 0 );
        olen += cipher.doFinal( result, olen );
        
        if( olen < size ){
            byte[] tmp = new byte[ olen ];
            System.arraycopy(
                    result, 0, tmp, 0, olen );
            result = tmp;
        }
        
        return result;
    }
    
    // Encrypt arbitrary byte array, returning the
    // encrypted data in a different byte array.
    
    public synchronized byte[] encrypt( byte[] data )
    throws CryptoException {
        if( data == null || data.length == 0 ){
            return new byte[0];
        }
        
        cipher.init( true, key );
        return callCipher( data );
    }
    
    // Encrypts a string.
    
    public byte[] encryptString( String data )
    throws CryptoException {
        if( data == null || data.length() == 0 ){
            return new byte[0];
        }
        
        return encrypt( data.getBytes() );
    }
    
    // Decrypts arbitrary data.
    
    public synchronized byte[] decrypt( byte[] data )
    throws CryptoException {
        if( data == null || data.length == 0 ){
            return new byte[0];
        }
        
        cipher.init( false, key );
        return callCipher( data );
    }
    
    // Decrypts a string that was previously encoded
    // using encryptString.
    
    public String decryptString( byte[] data )
    throws CryptoException {
        if( data == null || data.length == 0 ){
            return "";
        }
        
        return new String( decrypt( data ) );
    }
}

And here is the source of our midlet which uses Encryptor class described above to encrypt/decrypt messages entered by the user:
import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.rms.*;

import org.bouncycastle.crypto.*;

import java.math.BigInteger;

public class CryptoTest extends MIDlet {
    
    private Display display;
    private Command exitCommand = new Command( "Exit", Command.EXIT, 1 );
    private Command okCommand = new Command( "OK", Command.OK, 1 );
    
    private Encryptor   encryptor;
    private RecordStore rs;
    
    /** Creates a new instance of CryptoTest */
    public CryptoTest() {
    }
    
    /**
     * This method initializes UI of the application.
     */
    private void initialize() {
    }
    
    public void startApp() {
        initialize();
        if( display == null ){ // first time called...
            initMIDlet();
        }
    }
    
    public void pauseApp() {
    }
    
    public void destroyApp(boolean unconditional) {
        exitMIDlet();
    }
    
    private void initMIDlet(){
        display = Display.getDisplay( this );
        
        // Open a record store here        
        try {
            // This need to be changed to start with a clean reocrd
            rs = RecordStore.openRecordStore( "test3",
                    true );
        } catch( RecordStoreException e ){
            // put in error handling here
        }
        
        display.setCurrent( new AskForKey() );
    }
    
    public void exitMIDlet(){
        try {
            if( rs != null ){
                rs.closeRecordStore();
            }
        } catch( RecordStoreException e ){
        }
        
        notifyDestroyed();
    }
    
    private void displayException( Exception e ){
        Alert a = new Alert( "Exception" );
        a.setString( e.toString() );
        a.setTimeout( Alert.FOREVER );
        display.setCurrent( a, new AskForKey() );
    }
    
    class AskForKey extends TextBox
            implements CommandListener {
        public AskForKey(){
            super( "Enter a secret key:", "", 8, 0 );
            setCommandListener( this );
            addCommand( okCommand );
            addCommand( exitCommand );
        }
        
        public void commandAction( Command c,
                Displayable d ){
            if( c == exitCommand ){
                exitMIDlet();
            }
            
            String key = getString();
            if( key.length() < 8 ){
                Alert a = new Alert( "Key too short" );
                a.setString( "The key must be " +
                        "8 characters long" );
                setString( "" );
                display.setCurrent( a, this );
                return;
            }
            
            encryptor = new Encryptor( key );
            
            try {
                if( rs.getNextRecordID() == 1 ){
                    display.setCurrent(
                            new EnterMessage() );
                } else {
                    byte[] data = rs.getRecord( 1 );
                    String str =
                            encryptor.decryptString( data );
                    
                    Alert a =
                            new Alert( "Decryption" );
                    a.setTimeout( Alert.FOREVER );
                    a.setString(
                            "The decrypted string is '" +
                            str + "'" );
                    display.setCurrent( a, this );
                }
            } catch( RecordStoreException e ){
                displayException( e );
            } catch( CryptoException e ){
                displayException( e );
            }
        }
    }
    
    class EnterMessage extends TextBox
            implements CommandListener {
        public EnterMessage(){
            
            
            super( "Enter a message to encrypt:", "",
                    100, 0 );
            
            BigInteger bigInt = new BigInteger("199999");
            
            setCommandListener( this );
            addCommand( okCommand );
        }
        
        public void commandAction( Command c,
                Displayable d ){
            String msg = getString();
            
            try {
                byte[] data =
                        encryptor.encryptString( msg );
                rs.addRecord( data, 0, data.length );
            } catch( RecordStoreException e ){
                displayException( e );
            } catch( CryptoException e ){
                displayException( e );
            }
            
            display.setCurrent( new AskForKey() );
        }
    }
    
}

 

 

 

Also note at the time of this writing that some version of Bouncy Castle Crypto API is not compiled for MIDP 2.0. When you tried to compile this example with Bouncy Castle Crypto API, you may get errors. (Some of the test classes use some classes not available to J2ME which is only available to J2SE) In that case, you may try other versions of the API to eliminate this problem.

If the above method still does not work for you, we recommend you to add full source code of the API to your project and eliminate problems one by one by removing some test classes and removing unnecessary but problematic parts of the library. (Note that you should also check the license before distributing the changes you made!)

References

  1. The Legion of the Bouncy Castle

 

 Related Tips