How to encrypt / decrypt with AES from Bouncy Castle API in J2ME applications

In this post we will see how to use the AES (Advanced Encryption Standard) or Rijndael algorithm implementation from the Bouncy Castle API for J2ME platform. The example will use the AES lightweight engine to encrypt and decrypt a file managed by a J2ME (Midlet) application.

The Bouncy Castle Crypto API for Java provides a lightweight cryptographic API that works with everything from the J2ME to the JDK 1.6 platform.

The API is different for J2ME platform than the one for JDK platform. For example, the lightweight API has different implementations for the two platforms:

and also, at functions level, the situation in also different:

  • for the JDK platform the main class for cryptographic algorithms is Cipher;
  • for the J2ME platform there are classes for each cryptographic algorithm; e.g. for AES there are 3: AESEngine, AESFastEngine and AESLightEngine.

The AES (Advanced Encryption Standard) or Rijndael algorithm has these important characteristics:

  • finalist and winner of the AES (Advanced Encryption Standard) contest launched by NIST in 1997
  • created by two Belgians mathematicians: Joan Daemen and Vincent Rijman (Rijndael comes from their name)
  • became cryptographic standard in 2000
  • uses keys with 128, 192 or 256 bits
  • is a symmetric cryptographic algorithm (the same key is used both for encryption and decryption)
  • processes blocks of 128, 192 or 256 bits;
  • effective both on Intel platforms and other software or hardwareplatforms
  • it can be implemented on 32 bit processors and smart cards (8-bitprocessors);
  • faster than DES;
  • it is more secure than 3DES;

For AES, the Bouncy Castle Crypto API for J2ME platform provides three implementations (their description is taken from the Bouncy Castle API documentation):

  • AESEngine – “The middle performance version uses only one 256 word table for each, for a total of 2Kbytes, adding 12 rotate operations per round to compute the values contained in the other tables from the contents of the first” [Bouncy Castle API documentation]
  • AESFastEngine – “The fastest uses 8Kbytes of static tables to precompute round calculations, 4 256 word tables for encryption and 4 for decryption” [Bouncy Castle API documentation]
  • AESLightEngine – “The slowest version uses no static tables at all and computes the values in each round” [Bouncy Castle API documentation]

In this example, we will use the first implementation – AESEngine, that will process 128 bits (16 bytes) blocks and will use a 192 bit key.

Because, the files that are going to be encrypted may have or NOT (most of the times) a dimension that is multiple of block size (128 bits), we must use padding for the last block. In this case we will use the PaddedBufferedBlockCipher class, which is “a wrapper class that allows block ciphers to be used to process data in a piecemeal fashion with padding” [Bouncy Castle API documentation].

The encryption solution (complete encryption/decryption solution file) is defined by these steps:

1. define the PaddedBufferedBlockCipher instance used for encryption – encryptCipher in this solution; the PaddedBufferedBlockCipher class provides 2 constructors; both require a BlockCipher instance:

  • one constructor uses by default PKCS7 padding (used by this solution)
  • the second constructor requires a BlockCipherPadding instance;

for the BlockCipher instance it is created an AESEngine object;

2. init the cipher for encryption with a key; the key could be predefined or received; these two first steps are implemented by the class constructors:

public class AES_BC {

    PaddedBufferedBlockCipher encryptCipher;
    PaddedBufferedBlockCipher decryptCipher;

    // Buffer used to transport the bytes from one stream to another
    byte[] buf = new byte[16];              //input buffer
    byte[] obuf = new byte[512];            //output buffer

    byte[] key = null;

    public AES_BC(){
	//predefined 192 bit key value
        key = "SECRET_1SECRET_2SECRET_3".getBytes();
        InitCiphers();
    }
    public AES_BC(byte[] keyBytes){
        key = new byte[keyBytes.length];
        System.arraycopy(keyBytes, 0 , key, 0, keyBytes.length);
        InitCiphers();
    }

    private void InitCiphers(){
        encryptCipher = new PaddedBufferedBlockCipher(new AESEngine());
        encryptCipher.init(true, new KeyParameter(key));
        decryptCipher =  new PaddedBufferedBlockCipher(new AESEngine());
        decryptCipher.init(false, new KeyParameter(key));
    }

3. read bytes from the file; in the solution, we read 16 bytes blocks from the file; each block is processed by the int processBytes(byte[] in,int inOff, int len, byte[] out, int outOff) function; the output of the processed block is put in the out buffer which is written in the encrypted file.

4. VERY IMPORTANT STEP call the doFinal function which will process the last block in the buffer; the internal mechanism of the algorithm implementation, based in its encryption mode (ECB, CBC, or other) keeps an internal buffer which must be also discarded into the output file (this is NOT the last block of the input file); the doFinal it is a MUST DO step.

The last two steps are implemented by the encrypt function:

    public void encrypt(InputStream in, OutputStream out)
    throws ShortBufferException, IllegalBlockSizeException,
    BadPaddingException,DataLengthException,
    IllegalStateException, InvalidCipherTextException
    {
        try {
            // Bytes written to out will be encrypted
            // Read in the cleartext bytes from in InputStream and
            //      write them encrypted to out OutputStream

            int noBytesRead = 0;        //number of bytes read from input
            int noBytesProcessed = 0;   //number of bytes processed

            while ((noBytesRead = in.read(buf)) >= 0) {
                //System.out.println(noBytesRead +" bytes read");

                noBytesProcessed =
		 encryptCipher.processBytes(buf, 0, noBytesRead, obuf, 0);

		//System.out.println(noBytesProcessed +" bytes processed");
                out.write(obuf, 0, noBytesProcessed);
            }

             //System.out.println(noBytesRead +" bytes read");
             noBytesProcessed = encryptCipher.doFinal(obuf, 0);

             //System.out.println(noBytesProcessed +" bytes processed");
             out.write(obuf, 0, noBytesProcessed);

            out.flush();
        }
        catch (java.io.IOException e) {
            System.out.println(e.getMessage());
        }
    }

The decryption solution is similar to the encryption one and is implemented by the decrypt function.

The complete solution is implemented by the AES_BC class in this complete encryption/decryption solution file.

In order to use these 2 functions you must open the cleartext file and the encrypted one. This is a sample from a running MIDlet that show you how to use the encryption:

    void encryptFile(String fileName)
    {
         try {
            FileConnection fci =
                (FileConnection)Connector.open("file://localhost/" + currDirName + fileName);
            if (!fci.exists()) {
                throw new IOException("File does not exists");
            }
             //createFile("encrypt.txt", false);
             FileConnection fco =
                (FileConnection)Connector.open("file://localhost/" + currDirName + "encrypt.txt");

             if (!fco.exists())
                 fco.create();

            if (!fco.exists()) {
                throw new IOException("Can not create encrypted file");
            }

            InputStream fis = fci.openInputStream();
            OutputStream fos = fco.openOutputStream();

            AES_BC encrypter = new AES_BC();

	    // Encrypt
            encrypter.encrypt(fis, fci.fileSize(), fos);

            fis.close();
            fos.close();

            Alert alert =
                new Alert("Confirmation","File encryption terminated", null, AlertType.INFO);
            alert.setTimeout(Alert.FOREVER);
            Display.getDisplay(this).setCurrent(alert);
         }
         catch (Exception e) {
            Alert alert =
                new Alert("Encryption error!",
                    "Can not access file " + fileName + " in directory " + currDirName +
                    "Exception: " + e.getMessage(), null, AlertType.ERROR);
            alert.setTimeout(Alert.FOREVER);
            Display.getDisplay(this).setCurrent(alert);
        }
    }

The complete solution is implemented by the AES_BC class in this complete encryption/decryption solution file.

Related posts:

Like it? Then share this post or check the external adds. Sharing is the best way to appreciate the author.