Sorry about the red box, but we really need you to update your browser. Read this excellent article if you're wondering why we are no longer supporting this browser version. Go to Browse Happy for browser suggestions and how to update.

Encrypting and decrypting data

Factory classes are the standard way to encrypt and decrypt data. The factory classes are net.rim.device.api.crypto.EncryptorFactory and net.rim.device.api.crypto.DecryptorFactory .

Engine classes are available in case you need to directly access algorithms. All the engine classes derive from the net.rim.device.api.crypto.BlockEncryptorEngine interface.

Encrypting and decrypting data using factories

In most cases, encryption and decryption is done using factories. The two factory classes are EncryptorFactory and DecryptorFactory .

Code sample: Encrypting data using a factory

The first two inputs of the EncryptorFactory class are the output stream used to collect the ciphertext and the key used to encrypt the data. The key, in this case a DES key, is created as shown in the following code sample.

DESKey key = new DESKey();

The details of the generation and distribution of keys are not specified in this example. In this example, the same instance of the key is used to encrypt and decrypt the message. In a real world scenario, the protocol would first be agreed upon, and then the keys would be created and distributed.

In the following code sample, EncryptorFactory.getEncryptorOutputStream() is invoked and the resulting output stream is stored in the encryptor variable. The encryptor variable is created in a try clause to catch and display any exceptions that might occur.

try {
   //Create the EncryptorFactory.
   EncryptorOutputStream encryptor =
                   EncryptorFactory.getEncryptorOutputStream( key.getData(),
                   stream, encryptionType, IV );
   encryptor.write( message.getBytes() );
   encryptor.close();
   cipherText = stream.toByteArray();

   //Output the messages to the console.
   System.out.println("Original Message: " + message);
   System.out.println("Ciphertext: " + cipherText);
}

Code sample: Decrypting data using a factory

The decryption process is similar to the encryption process.

In the following code sample, the input stream is created to hold the ciphertext from the encryption process. Like the key, the ciphertext was previously transmitted to the receiver by the sender. The getDecryptorInputStream method of the DecryptorFactory class is called and the resulting object is created and stored in a variable of type DecryptorInputStream called decryptor.

Because the data may contain extra padding, a for loop is used to extract the data from the output stream. The following example uses a loop that reads from the input stream and exits when it reads the first empty block of data. The decryption routine occurs within a try block.

The arrays are compared to determine that the decryption was successful, and a message is displayed in the console. If the decryption fails, exception error messages appear in the console.

ByteArrayInputStream input = new ByteArrayInputStream( stream.toByteArray() )
DecryptorInputStream decryptor = 
        DecryptorFactory.getDecryptorInputStream(symmetricKey,input);
      byte[] decrypted = new byte[0];
      byte[] temp = new byte[10];
           for( ;; ) {
             int read = decryptor.read( temp, 0, 10 );
                if( read < 0 ) {
                   // We are at the end of the stream.
                   break;
                  }
                  // Copy.
                  net.rim.vm.Array.resize( decrypted, read + decrypted.length );
                  System.arraycopy( temp, 0, decrypted, decrypted.length - read, read );
                  }
                  
         if( Arrays.equals( message.getBytes(), decrypted ) ) {
               System.out.println("Encryption/Decryption Passed.");
          } else {
              System.out.println("Encryption/Decryption Failed.");
          }
        }

Block algorithms

A block algorithm operates on fixed-length groups of bits called blocks, and applies the same transformation to each one. The block of ciphertext that a block algorithm produces is the same length as the plaintext it encrypts.

An initialization vector is a string of random information that is used with some block cipher modes to ensure that a unique ciphertext message appears each time a plaintext message is encrypted.

Block ciphers

A block cipher is a symmetric key cipher that operates on fixed-length groups of bits called blocks. A mode of operation describes the process of encypting a block cipher under a single key.

The RIM Cryptographic API supports the following modes of operation for block encryption:

  • ECB (default for all engine classes)
  • CBC
  • CFB
  • OFB
  • CTR

You can use CFB, OFB, or CTR modes to convert a block algorithm to a stream algorithm.

All encryption engines operate in ECB mode, which means that they take in one block at a time and return one block at a time. There is no chaining or dependence on other blocks for the encryption of the ciphertext. ECB is not the most highly secure mode for encryption. If you require higher security, consider using CBC or one of the other supported modes.

Block formatters and unformatters

Block formatting is done by padding and encoding blocks of data. To help you format and unformat blocks, the RIM Cryptographic API provides the net.rim.device.api.crypto.BlockFormatterEngine interface and the net.rim.device.api.crypto.BlockUnformatterEngine interface. These interfaces include the algorithms PKCS5, OAEP, and PKCS1. PKCS5 provides padding, and OAEP and PKCS1 provide block encoding.

A BlockFormatterEngine uses a BlockEncryptorEngine to encrypt the data after the appropriate encoding or padding has been performed. BlockEncryptor uses both a BlockEncryptorEngine and a BlockFormatterEngine, so that you can use either of them with the stream-based approach.

The following code samples demonstrate two approaches to using block formatters.

Code sample: Using block formatters

The following code sample uses the PKCS5FormatterEngine. It specifies input and output, and then creates a random new DES key. It creates the engine and the block encoder engine. It encodes and encrypts the data using PKCS5. The last Boolean argument indicates whether or not this is the last block.

// Input (less than 8 bytes).
byte[] input = { 0x00, 0x00, 0x00, 0x00, 0x00 };

// Output.
byte[] output = new byte[ 8 ];

// Create a random new DES key.
DESKey key = new DESKey();

// Create the engine.
DESEncryptorEngine desEngine = new DESEncryptorEngine( key );

// Create the block encoder engine.
PKCS5FormatterEngine formatter = new PKCS5FormatterEngine( desEngine );

// Encode and encrypt the information using PKCS5.
// The last boolean argument indicates whether this is the last block.
formatter.formatAndEncrypt( input, 0, input.length, output, 0, true );
System.out.println("Ciphertext = " + new String( output ) + ".")

Code sample: Using a formatter to encrypt a stream of data

The following code sample uses the formatter to encrypt a stream of data. This example uses DES in CBC mode with PKCS5 padding.

The code sample specifies input and then creates a new DES key with the given data. It creates the DES engine, CBC engine, and PKCS5 Encoder engine. Using the input byte array, it creates an output stream. Using the CBCEncryptorEngine, it creates the encryptor. It writes the data to encrypt. Finally, it closes the output stream and grabs the bytes.

It is important to call close() with padding encoders, as it ensures that the last block is encoded properly.

// Input.
byte[] input   = { (byte)'H', (byte)'e', (byte)'l', (byte)'l', (byte)'o',
                   (byte)',', (byte)' ', (byte)'w',(byte)'o', (byte)'r',
                   (byte)'l', (byte)'d', byte)'!', (byte)'!' };

// Initialization vector.
byte[] iv      = { (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC,(byte)0xFB,
                   (byte)0xFA, (byte)0xF9, (byte)0xF8 };

// Key data.
byte[] keyData = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67,(byte)0x89,
                   (byte)0x01, (byte)0x23, (byte)0x45 };

// Create a new DES key with the given data.
DESKey key = new DESKey( keyData );

// Create the DES engine.
DESEncryptorEngine desEngine = new DESEncryptorEngine( key );

// Create the CBC engine.
CBCEncryptorEngine cbcEngine = new CBCEncryptorEngine( desEngine,
                               new InitializationVector( iv ) );

// Create the PKCS5 Encoder engine.
PKCS5FormatterEngine formatter = new PKCS5FormatterEngine( cbcEngine );

// Create a stream from the input byte array.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

// Create the Encryptor using the CBCEncryptorEngine.
BlockEncryptor encryptor = new BlockEncryptor( formatter, outputStream );

// Write the data to encrypt.
encryptor.write( input, 0, input.length );

// Close the output stream and obtain the bytes.
encryptor.close();
byte[] output = outputStream.toByteArray();
System.out.println( "Ciphertext = " + new String( output ) + "." );

Code sample: Using an unformatter

To use an unformatter, extend the methods for encrypting and decrypting data.

The following code sample demonstrates the unformatter. It specifies the output of the previous example, and specifies input. It creates a new DES key with the given data. It creates the DES engine, CBC engine, and PKCS5 Decoder engine. It creates a stream from the input byte array. Using the CBCDecryptorEngine, it creates the encryptor. It reads in the decrypted data, and then it closes the decryptor and the input stream.

// Output from previous example.
byte[] output = { (byte)0x58, (byte)0x95, (byte)0xBC, (byte)0xF4, (byte)0x3F,
                  (byte)0xD8, (byte)0xD8, (byte)0xAD, (byte)0x80, (byte)0x9A,
                  (byte)0x95, (byte)0x34, (byte)0xB4, (byte)0xCC, (byte)0x76,
                  (byte)0x79 };

// Input.
byte[] input = new byte[14];
byte[] iv = { (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC, (byte)0xFB,
              (byte)0xFA, (byte)0xF9, (byte)0xF8 };
byte[] keyData = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67,
                   (byte)0x89, (byte)0x01, (byte)0x23, (byte)0x45 };

// Create a new DES key with the given data.
DESKey key = new DESKey( keyData );

// Create the DES engine.
DESDecryptorEngine desEngine = new DESDecryptorEngine( key );

// Create the CBC engine.
CBCDecryptorEngine cbcEngine = new CBCDecryptorEngine( desEngine,
                               new InitializationVector( iv ) );

// Create the PKCS5 Decoder engine.
PKCS5UnformatterEngine unformatter = new PKCS5UnformatterEngine( cbcEngine );

// Create a stream from the input byte array.
ByteArrayInputStream inputStream = new ByteArrayInputStream( output );

// Create the Decryptor using the CBCDecryptorEngine.
BlockDecryptor decryptor = new BlockDecryptor( unformatter, inputStream );

// Read in the decrypted data.
decryptor.read( input, 0, input.length );

// Close the decryptor and the input stream.
decryptor.close();
inputStream.close();
System.out.println("Ciphertext = " + new String( output ) + ".");
System.out.println("Plaintext = " + new String( input ) + ".");

Stream ciphers

A stream cipher, also called a stream algorithm, combines plaintext bits with a keystream. A keystream is a pseudorandom algorithm bit stream. Plaintext digits are encrypted one at a time.

The stream cipher part of the RIM Cryptographic API is based on the common stream interface in Java, and uses similar read and write function calls to interact with the stream. After a stream cipher is established, you don't need to write code for what it does: all activity is automatically performed by the class on the underlying stream.

A pseudorandom source is a bit stream generator that is initialized with a seed. It supplies an arbitrarily long stream of difficult-to-predict and non-repeating bits. The PseudoRandomSource interface represents a PRNG. A PRNG expands a finite length seed into an arbitrarily long stream of pseudo-random bytes. The PseudoRandomSource classes provide methods for retrieving bytes from the PRNG and resetting the state of the PRNG.

Most stream ciphers create the ciphertext by applying an exclusive-or to the plaintext with a keystream. The keystream is generated from a PRNG.

To use the ARC4 algorithm, an instance of the ARC4PseudoRandomSource class is required. Instead of using the ARC4PseudoRandomSource class directly to encrypt data, you might find it more flexible and reliable to use the PRNGEncryptor class, which implements the StreamEncryptor class.

Code sample: Stream encryption using the ARC4 algorithm

The following code sample illustrates encryption using a stream cipher. It specifies the input byte array. It uses the RandomSource to acquire input bytes. It prints out plaintext. It sets up the ARC4Key with length 256 bytes. It creates the ARC4PseudoRandomSource. It creates the output stream to store the encrypted data. It creates the PRNGEncryptor stream encryptor, which uses the ARC4PseudoRandomSource and an output stream. To encrypt, it calls write() with the plaintext. Finally, it prints the ciphertext.

// Input byte array.
byte[] plaintext = new byte[128];

// Use the Random source to get input bytes.
RandomSource.getBytes( plaintext );

// Print out the plaintext.
System.out.println("Plaintext = " + new String( plaintext ) + ".");

// Set up the ARC4Key of length 256 bytes.
ARC4Key key = new ARC4Key( 256 );

// Create the ARC4PseudoRandomSource.
ARC4PseudoRandomSource source = new ARC4PseudoRandomSource( key );

// Create the output stream to store the encrypted data.
ByteArrayOutputStream out = new ByteArrayOutputStream();

// Create the PRNGEncryptor stream encryptor, which uses
// the ARC4PseudoRandomSource and an output stream.
PRNGEncryptor encryptStream = new PRNGEncryptor( source, out );

// To encrypt, invoke write with the plaintext.
encryptStream.write( plaintext, 0, plaintext.length );
encryptStream.close();
byte[] ciphertext = out.toByteArray();

// Print out the ciphertext.
System.out.println( "Ciphertext = " + new String(ciphertext) + "." );

The following code sample shows you how to decrypt the data. It reloads the key to decrypt the plaintext. It creates an input stream with ciphertext as the basis. It creates an instance of a PRNGDecryptor to decrypt the data. It decrypts by reading from the stream and storing the decrypted data in plaintext. It prints the plaintext.

// Reload the key to get the ciphertext back to plaintext (decrypt).
source = new ARC4PseudoRandomSource( key );

// Create an input stream with ciphertext as the basis.
ByteArrayInputStream in = new ByteArrayInputStream( ciphertext );

// Create an instance of a PRNGDecryptor to decrypt the data.
PRNGDecryptor decryptStream = new PRNGDecryptor( source, in );

// Decrypt by reading from the stream and storing the decrypted data in plaintext.
decryptStream.read( plaintext, 0, ciphertext.length );
decryptStream.close();

// Print out the plaintext.
System.out.println( "Plaintext = " + new String( plaintext ) + "." );

Encryptor engines

Encryptor engines are an alternative to encryptor factories (the EncryptorFactory class). The EncryptorFactory class is built on engines. Both engines and factories produce identical results.

Engines allow you to access the lowest level of abstraction and directly access algorithms. For example, you could use the DESEncryptorEngine class to directly access the DES algorithm by passing in a plaintext byte array and receiving an encrypted byte array.

All the engine classes derive from the net.rim.device.api.crypto.BlockEncryptorEngine interface.

The code samples below demonstrate the two main approaches to using encryptor engines: a block-based approach that uses an encryptor engine to encrypt blocks of data; and a stream-based approach that uses a block encryptor to encrypt a stream of data.

Code sample: Encrypting blocks of data with an encryptor engine

In ECB mode, the engine takes one block at a time and returns one block at a time. There is no chaining or dependence on other blocks for the encryption of the ciphertext. This is not the most secure mode for encryption, but it is the more straightforward approach.

The following sample code uses the DESEncryptorEngine class. It shows how to encrypt data using DES and a block algorithm in ECB mode. First input is defined, and then output is defined. Next, the code creates a new DES key with the given data and creates the engine. Finally, it encrypts the input with an offset of zero.

This example assumes that the characters are low enough in the Unicode character set to have a code point less than 256 (requiring a single byte value) so that the byte array length is the same as the number of characters. This may not always be the case with character sets such as Arabic, Cyrillic, and Chinese.

// Input.
byte[] input = { 'T','e','s','t','i','n','g','!' };

// Output.
byte[] output = new byte[ 8 ];
byte[] keyData = { (byte)0x01, (byte)0x23, (byte)0x45,(byte)0x67,
                   (byte)0x89, (byte)0x01, (byte)0x23, (byte)0x45 };

// Create a new DES key with the given data.
DESKey key = new DESKey( keyData );

// Create engine.
DESEncryptorEngine engine = new DESEncryptorEngine( key );

// Encrypt the input with offset of zero.
engine.encrypt( input, 0, output, 0);
System.out.println("Ciphertext = " + new String( output ) + ".");

The following sample code shows how to decrypt data using a block algorithm. This sample uses the DESDecryptorEngine class to perform decryption.

// Input.
byte[] input = { (byte)0x77, (byte)0xFB, (byte)0xF4, byte)0x94,(byte)0xE9,
                 (byte)0x70, (byte)0xDD, (byte)0x0B };

// Output.
byte[] output = new byte[ 8 ];

// Key data from encryption.
byte[] keyData = { (byte)0x01,(byte)0x23, byte)0x45, byte)0x67, (byte)0x89,
                   (byte)0x01, (byte)0x23, (byte)0x45 };

// Create a new DES key with the given data.
DESKey key = new DESKey( keyData );

// Create engine.
DESDecryptorEngine engine = new DESDecryptorEngine( key );

// Decrypt the input with offset of zero.
engine.decrypt( input, 0, output, 0);
System.out.println("Plaintext = " + new String( output ) + ".");

Code sample: Encrypting streams of data with an encryptor engine

The following code sample uses CBC mode. This approach is more verbose and time-consuming to write than the previous example, but it yields clearer and more reliable results.

This approach uses an initialization vector. The initialization vector is a byte array of random data that is used to further randomize generated data. Initialization vectors can be used with some block cipher modes, such as CBC, to ensure that when the same plaintext message is encrypted twice, it will not result in the same ciphertext message both times.

This approach to encryption uses a BlockEncryptor to encrypt a stream of data with DES. The actual encryption is performed with the call to write(). The rest of the code is for setting up or tearing down the encryptors and engines.

//Input.
byte[] input = { (byte)'T', (byte)'h', (byte)'i', (byte)'s',
                 (byte)' ', (byte)'i', (byte)'s', (byte)' ',
                 (byte)'a', (byte)' ', (byte)'t', (byte)'e',
                 (byte)'s', (byte)'t', (byte)'!', (byte)'!' };

//Initialization vector.
byte[] iv = { (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC,
              (byte)0xFB, (byte)0xFA, (byte)0xF9, (byte)0xF8 };

//Key data.
byte[] keyData = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67,
                   (byte)0x89, (byte)0x01, (byte)0x23, (byte)0x45 };

// Create a new DES key with the given data.
DESKey key = new DESKey( keyData );

// Create the DES Engine.
DESEncryptorEngine desEngine = new DESEncryptorEngine( key );

// Create the CBC engine.
CBCEncryptorEngine cbcEngine = new CBCEncryptorEngine(desEngine, new InitializationVector(iv));

// Create a stream to hold the encrypted data.
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

// Create the Encryptor using the CBCEncryptorEngine and the output stream.
BlockEncryptor encryptor = new BlockEncryptor( cbcEngine, outputStream );

// Write out the data to encrypt.
encryptor.write( input, 0, input.length );

// Close the encryptor and grab the bytes.
encryptor.close();
byte[] output = outputStream.toByteArray();

System.out.println("Ciphertext = " + new String( output ) + ".");

The following code uses BlockDecryptor to decrypt a stream of data with DES. This example demonstrates DES decryption in CBC mode.

//Input
byte[] input = { (byte)0x58, (byte)0x47, (byte)0x50, (byte)0x34,
                 (byte)0x9F, (byte)0xEF, (byte)0x0D, (byte)0x77,
                 (byte)0x31, (byte)0x4E, (byte)0xB7, (byte)0x73,
                 (byte)0x56, (byte)0xAC, (byte)0x3C, (byte)0xD6 };

//Initialization vector.
byte[] iv = { (byte)0xFF, (byte)0xFE, (byte)0xFD, (byte)0xFC,
              (byte)0xFB, (byte)0xFA, (byte)0xF9, (byte)0xF8 };

//Output.
byte[] output = new byte[ 16 ];

//Key data.
byte[] keyData = { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67,
                   (byte)0x89, (byte)0x01, (byte)0x23, (byte)0x45 };

// Create a new DES key with the given data.
DESKey key = new DESKey( keyData );

// Create the DES Engine.
DESDecryptorEngine desEngine = new DESDecryptorEngine( key );

// Create the CBC engine.
CBCDecryptorEngine cbcEngine = new CBCDecryptorEngine(desEngine, 
                               new InitializationVector(iv));

// Create a stream from the input byte array.
ByteArrayInputStream inputStream = new ByteArrayInputStream( input );

// Create the Decryptor using the CBCDecryptorEngine.
BlockDecryptor decryptor = new BlockDecryptor( cbcEngine, inputStream );

// Read in the decrypted data.
int ret = decryptor.read( output, 0, output.length );

// Close the encryptor and grab the bytes.
decryptor.close();
inputStream.close();

System.out.println("Plaintext = " + new String( output ) + ".");

Handling exceptions

The RIM Cryptographic API has two exceptions:

Exception Description

javacard.security.CryptoException

CryptoException is the most commonly thrown exception. Where possible, a subclass exception is thrown that indicates what caused the error. Examples of CryptoException subclasses are InvalidKeyPairException, InvalidKeyFormatException, and InvalidSignatureFormatException.

If none of the subclasses suit the error, CryptoException is thrown.

CryptoIOException

CryptoIOException is an IOException that contains a CryptoException. Some stream-related classes such as EncryptorOutputStream can only throw an IOException.

CryptoIOException is thrown only when an IOException has occurred within a crypto member. For example, if an error occurs and an IOException is thrown within an EncryptorOutputStream, a CryptoIOException is thrown instead of the IOException.

To make it easier for you to debug your application, the CryptoIOException is handled separately from the IOException.

In the following code sample, a try/catch block is used to allow each exception to be caught by the exception handler. The string associated with the exception is output to the console so that you can check the log to determine if an exception was thrown.

try
{
    //*cryptographic code
} 
catch( IOException e ) 
{
    System.out.println(e.toString());
}
catch( CryptoTokenException e )
{
    System.out.println(e.toString());
}