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; } } |
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