Codec.java
/**
* Copyright (c) 2012 Nokia Corporation. All rights reserved.
* Nokia and Nokia Connecting People are registered trademarks of Nokia
Corporation.
* Oracle and Java are trademarks or registered trademarks of Oracle and/or its
* affiliates. Other product and company names mentioned herein may be trademarks
* or trade names of their respective owners.
* See LICENSE.TXT for license information.
*/
package com.nokia.example.satsa;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
// 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 synchronise access
private MessageDigest digest = null;
private Cipher cipher = null;
private boolean operative = true;
// Builds the Codec object and initializes the
// cipher and digest
Codec() {
try {
digest = MessageDigest.getInstance("SHA-1");
} catch (NoSuchAlgorithmException e) {
// Basically this should not happen since we have to know
// if SHA-1 is availabe 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;
}
}
// Encrypt text with given AES key. It encodes the message
// including the length in two bytes and the plaintext
synchronised 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 ciperthext 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");
}
}
synchronised 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");
}
}
synchronised 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");
}
}
// Decrypt text with given AES key. It decodes the message
// reading the message length and then the message itself
synchronised 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");
}
}
// Displays ecrypted 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();
}
}