Tuesday, March 3, 2009

How to - Use Basic Encryption


Data sent between a BlackBerry device and the BlackBerry Enterprise Server™ is encrypted using Triple DES (Date Encryption Standard) or AES (Advanced Encrption Standard). This is performed automatically and does not require application implementation to use it.

There are cases where encrypting data can be required, such as secure communication with an external application using the BlackBerry Mobile Data Server. Communication between the BlackBerry and BlackBerry Mobile Data Server would be automatically encrypted but communication between the BlackBerry Mobile Data Server and an application server would not unless implemented in the BlackBerry application.

Note: The BlackBerry supports https, tls and ssl for secure communication beyond the BlackBerry Mobile Data Server.

The following is an example of how to utilize basic encryption using AES on the BlackBerry:

package com.rim.samples.crypto;
import java.io.*;
import net.rim.device.api.crypto.*;
import net.rim.device.api.util.*;
/**
* This sample code provides information on how to encrypt and
* decrypt data using the most common symmetric key algorithm
* in use today. That algorithm is the Advanced Encryption Standard (AES).
*/
public class BasicCryptoDeveloperLab
{
public static void main( String[] args )
{
try {
// Create the data that you want to encrypt.
String message = "Welcome to the RIM Crypto API.";
byte[] data = message.getBytes();
// Create the key we want to use for encryption and decryption.
// Note that the RandomSource class provides cryptographically random
// data which is suitable for use when creating keys. This is different
// than using the Random class.
byte[] keyData = RandomSource.getBytes( 256 );
// Encrypt the data using
byte[] ciphertext = encrypt( keyData, data );
// Decrypt the data.
byte[] plaintext = decrypt( keyData, ciphertext );
String message2 = new String( plaintext );
if( message.equals( message2 )) {
// The encryption/decryption operation worked as expected.
System.out.println( "Congratulations! You just encrypted and decrypted data." );
} else {
System.out.println( "Oops. The decrypted message should equal the original.
Check your code." );
}
} catch( CryptoException e ) {
System.out.println("An unexpected exception occurred.
Please verify your work or ask for help.");
} catch( IOException e ) {
System.out.println("An unexpected exception occurred.
Please verify your work or ask for help.");
}
}
private static byte[] encrypt( byte[] keyData, byte[] data )
throws CryptoException, IOException
{
// Create the AES key to use for encrypting the data.
// This will create an AES key using as much of the keyData
// as possible.
AESKey key = new AESKey( keyData );
// Now, we want to encrypt the data.
// First, create the encryptor engine that we use for the actual
// encrypting of the data.
AESEncryptorEngine engine = new AESEncryptorEngine( key );
// Since we cannot guarantee that the data will be of an equal block
// length we want to use a padding engine (PKCS5 in this case).
PKCS5FormatterEngine fengine = new PKCS5FormatterEngine( engine );
// Create a BlockEncryptor to hide the engine details away.
ByteArrayOutputStream output = new ByteArrayOutputStream();
BlockEncryptor encryptor = new BlockEncryptor( fengine, output );
// Now, all we need to do is write our data to the output stream.
// But before doing so, let's calculate a hash on the data as well.
// A digest provides a one way hash function to map a large amount
// of data to a unique 20 byte value (in the case of SHA1).
SHA1Digest digest = new SHA1Digest();
digest.update( data );
byte[] hash = digest.getDigest();
// Now, write out all of the data and the hash to ensure that the
// data was not modified in transit.
encryptor.write( data );
encryptor.write( hash );
encryptor.close();
output.close();
// Now, the encrypted data is sitting in the ByteArrayOutputStream.
// We simply want to retrieve it.
return output.toByteArray();
}
private static byte[] decrypt( byte[] keyData, byte[] ciphertext )
throws CryptoException, IOException
{
// First, create the AESKey again.
AESKey key = new AESKey( keyData );
// Now, create the decryptor engine.
AESDecryptorEngine engine = new AESDecryptorEngine( key );
// Since we cannot guarantee that the data will be of an equal block length
// we want to use a padding engine (PKCS5 in this case).
PKCS5UnformatterEngine uengine = new PKCS5UnformatterEngine( engine );
// Create the BlockDecryptor to hide the decryption details away.
ByteArrayInputStream input = new ByteArrayInputStream( ciphertext );
BlockDecryptor decryptor = new BlockDecryptor( uengine, input );
// Now, read in the data. Remember that the last 20 bytes represent
// the SHA1 hash of the decrypted data.
byte[] temp = new byte[ 100 ];
DataBuffer buffer = new DataBuffer();
for( ;; ) {
int bytesRead = decryptor.read( temp );
buffer.write( temp, 0, bytesRead );
if( bytesRead < 100 ) {
// We ran out of data.
break;
}
}
byte[] plaintextAndHash = buffer.getArray();
int plaintextLength = plaintextAndHash.length - SHA1Digest.DIGEST_LENGTH;
byte[] plaintext = new byte[ plaintextLength ];
byte[] hash = new byte[ SHA1Digest.DIGEST_LENGTH ];
System.arraycopy( plaintextAndHash, 0, plaintext, 0, plaintextLength );
System.arraycopy( plaintextAndHash, plaintextLength, hash, 0,
SHA1Digest.DIGEST_LENGTH );
// Now, hash the plaintext and compare against the hash
// that we found in the decrypted data.
SHA1Digest digest = new SHA1Digest();
digest.update( plaintext );
byte[] hash2 = digest.getDigest();
if( !Arrays.equals( hash, hash2 )) {
throw new RuntimeException();
}
return plaintext;
}
}

1 comment:

  1. This code only works when the message to encrypt is made up of 16 byte blocks and requires no padding. Add a single character to the input message and it will fail.

    ReplyDelete

Place your comments here...