This is the class that handles the cryptographic operations. The AES algorithm used here works in sequences of 128 bits. This means that the length of the cipher will be a multiple of 16 bytes. Before encryption, the right value for the cipher text's buffer length is properly calculated.
To create the class:
Assign the
class to the package satsa
and import the required classes.
package satsa; import java.security.DigestException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.Key; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.security.spec.InvalidKeySpecException; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; import javax.crypto.spec.SecretKeySpec;
The class Cipher
of
the SATSA_CRYPTO package is used here as it contains a representation of a
cipher that can be used for encryption and decryption. To obtain an instance
of Cipher
, call the getInstance
method
passing a transformation string. Here getInstance("AES/ECB/PKCS5Padding")
requests
for a Cipher
that uses the Advanced Encryption Standard
[AES] algorithm using the Electronic Code Book (ECB) method and the PKCS5
padding scheme.
On the line digest = MessageDigest.getInstance("SHA-1")
,
the getInstance
method will return an instance of the MessageDigest
class
based on the digest algorithm's name and the digest is calculated using the
SHA-1 algorithm. Notice that the digest is actually calculated further below
by using the digest
method.
// Utility class that encapsulates the calls to the SATSA API class Codec { // we can have a global instance of the algorithms // as long as we synchronize access private MessageDigest digest = null; private Cipher cipher = null; private boolean operative = true; private SATSAMIDlet midlet; // Builds the Codec object and initializes the // cipher and digest Codec(SATSAMIDlet midlet) { this.midlet = midlet; try { digest = MessageDigest.getInstance("SHA-1"); } catch (NoSuchAlgorithmException e) { // Basically this should not happen since we have to know // if SHA-1 is available otherwise the whole design // cannot work operative = false; } try { cipher = Cipher.getInstance("AES/ECB/PKCS5Padding"); } catch (NoSuchAlgorithmException e) { // This should not happen since we know the target platform // but we set the operative flag to false just in case operative = false; } catch (NoSuchPaddingException e) { // This should not happen since we know the target platform // but we set the operative flag to false just in case operative = false; } }
Implement the functionality for encrypting messages.
Use the SecretKeySpec
class
to build a key from a byte array used for symmetric encryption algorithms
that need no parameters. Initialize the key from a password by creating an
instance of the SecretKeySpec
class. Then calculate the
size of the cipher text and pass the Key
object to the Cipher
using
the init
method. With the Cipher
initialized,
proceed to feed data using the doFinal
method.
The digest is calculated by using the digest
method.
To do this, a byte array containing the message is passed to the update
method.
// Encrypt text with given AES key. It encodes the message // including the length in two bytes and the plaintext synchronized byte[] encrypt (byte[] keyBits, byte[] plaintext) throws InvalidKeySpecException, InvalidKeyException, IllegalStateException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { if (operative) { // Initialize the key from the password Key key = new SecretKeySpec(keyBits, 0, keyBits.length, "AES"); // add 2 bytes to encode the length of the plaintext // as a short value byte[] plaintextAndLength = new byte[plaintext.length + 2]; plaintextAndLength[0] = (byte)(0xff & (plaintext.length >> 8)); plaintextAndLength[1] = (byte)(0xff & plaintext.length); // build the new plaintext System.arraycopy(plaintext, 0, plaintextAndLength, 2, plaintext.length); // calculate the size of the ciphertext considering // the padding int blocksize = 16; int ciphertextLength = 0; int remainder = plaintextAndLength.length % blocksize; if (remainder == 0) { ciphertextLength = plaintextAndLength.length; } else { ciphertextLength = plaintextAndLength.length - remainder + blocksize; } byte[] cipherText = new byte[ciphertextLength]; // reinitialize the cipher in encryption mode with the given key cipher.init(Cipher.ENCRYPT_MODE, key); // do the encryption cipher.doFinal(plaintextAndLength, 0, plaintextAndLength.length, cipherText, 0); return cipherText; } else { throw new IllegalStateException("Codec not initialized"); } } synchronized byte[] digest(byte message[]) throws DigestException { if (operative) { // Reset the digest and update with the data digest.reset(); digest.update(message, 0, message.length); // SHA-1 produces 160-bit long digests byte[] output = new byte[20]; digest.digest(output, 0, output.length); return output; } else { throw new IllegalStateException("Codec not initialized"); } } synchronized boolean isDigestValid(byte message[], byte[] digest) throws DigestException { if (operative) { byte[] calculatedDigest = digest(message); if (calculatedDigest.length != digest.length) { return false; } // compare byte per byte for (int i=0;i<digest.length;i++) { if (calculatedDigest[i] != digest[i]) { return false; } } return true; } else { throw new IllegalStateException("Codec not initialized"); } }
Implement the functionality for decrypting messages.
Use the SecretKeySpec
class
to build a key from a byte array. Pass the Key
object
to the Cipher
using the init
method.
With the Cipher
initialized, proceed to feed data using
the doFinal
method. Also calculate the plain text and
then decode the final text.
// Decrypt text with given AES key. It decodes the message // reading the message length and then the message itself synchronized byte[] decrypt (byte[] keyBits, byte[] cipherText) throws InvalidKeySpecException, InvalidKeyException, IllegalStateException, ShortBufferException, IllegalBlockSizeException, BadPaddingException, InvalidAlgorithmParameterException { if (operative) { // create a key from the keyBits Key key = new SecretKeySpec(keyBits, 0, keyBits.length, "AES"); // Initialize the cipher in decrypt mode cipher.init(Cipher.DECRYPT_MODE, key); byte[] decrypted = new byte[cipherText.length]; // Decrypt the cipher text cipher.doFinal(cipherText, 0, cipherText.length, decrypted, 0); // Calculate the length of the plaintext int plainTextLength = (decrypted[0] << 8) | (decrypted[1] & 0xff); byte[] finalText = new byte[plainTextLength]; // Decode the final text System.arraycopy(decrypted, 2, finalText, 0, plainTextLength); return finalText; } else { throw new IllegalStateException("Codec not initialized"); } }
Display the encrypted data in hex.
// Displays encrypted data in hex String byteToHex(byte[] data) { StringBuffer hexString = new StringBuffer(); String hexCodes = "0123456789ABCDEF"; for (int i=0; i < data.length; i++) { hexString.append( hexCodes.charAt( (data[i] >> 4) & 0x0f) ); hexString.append( hexCodes.charAt( data[i] & 0x0f) ); if (i< data.length - 1) { hexString.append(":"); } if ( ((i+1)%8) == 0) hexString.append("\n"); } return hexString.toString(); } }