* sweet - thanks to Edward Smith for the CF Technote that indicated the key from ColdFusion was Base64 encoded. See generateKey() for the 'fix'
My task is to use Java 1.4.2 to match the results a given ColdFusion code sample for encryption.
Known/given values:
- A 24-byte key
- A 16-byte salt (IVorSalt)
- Encoding is Hex
- Encryption algorithm is AES/CBC/PKCS5Padding
- A sample clear-text value
- The encrypted value of the sample clear-text after going through the ColdFusion code
Assumptions:
- Number of iterations not specified in the ColdFusion code so I assume only one iteration
- 24-byte key so I assume 192-bit encryption
Given/working ColdFusion encryption code sample:
<cfset ThisSalt = "16byte-salt-here">
<cfset ThisAlgorithm = "AES/CBC/PKCS5Padding">
<cfset ThisKey = "a-24byte-key-string-here">
<cfset thisAdjustedNow = now()>
<cfset ThisDateTimeVar = DateFormat( thisAdjustedNow , "yyyymmdd" )>
<cfset ThisDateTimeVar = ThisDateTimeVar & TimeFormat( thisAdjustedNow , "HHmmss" )>
<cfset ThisTAID = ThisDateTimeVar & "|" & someOtherData>
<cfset ThisTAIDEnc = Encrypt( ThisTAID , ThisKey , ThisAlgorithm , "Hex" , ThisSalt)>
My Java 1.4.2 encryption/decryption code swag:
package so.example;
import java.security.*;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.*;
public class SO_AES192 {
private static final String _AES = "AES";
private static final String _AES_CBC_PKCS5Padding = "AES/CBC/PKCS5Padding";
private static final String KEY_VALUE = "a-24byte-key-string-here";
private static final String SALT_VALUE = "16byte-salt-here";
private static final int ITERATIONS = 1;
private static IvParameterSpec ivParameterSpec;
public static String encryptHex(String value) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(_AES_CBC_PKCS5Padding);
ivParameterSpec = new IvParameterSpec(SALT_VALUE.getBytes());
c.init(Cipher.ENCRYPT_MODE, key, ivParameterSpec);
String valueToEncrypt = null;
String eValue = value;
for (int i = 0; i < ITERATIONS; i++) {
// valueToEncrypt = SALT_VALUE + eValue; // pre-pend salt - Length > sample length
valueToEncrypt = eValue; // don't pre-pend salt Length = sample length
byte[] encValue = c.doFinal(valueToEncrypt.getBytes());
eValue = Hex.encodeHexString(encValue);
}
return eValue;
}
public static String decryptHex(String value) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(_AES_CBC_PKCS5Padding);
ivParameterSpec = new IvParameterSpec(SALT_VALUE.getBytes());
c.init(Cipher.DECRYPT_MODE, key, ivParameterSpec);
String dValue = null;
char[] valueToDecrypt = value.toCharArray();
for (int i = 0; i < ITERATIONS; i++) {
byte[] decordedValue = Hex.decodeHex(valueToDecrypt);
byte[] decValue = c.doFinal(decordedValue);
// dValue = new String(decValue).substring(SALT_VALUE.length()); // when salt is pre-pended
dValue = new String(decValue); // when salt is not pre-pended
valueToDecrypt = dValue.toCharArray();
}
return dValue;
}
private static Key generateKey() throws Exception {
// Key key = new SecretKeySpec(KEY_VALUE.getBytes(), _AES); // this was wrong
Key key = new SecretKeySpec(new BASE64Decoder().decodeBuffer(keyValueString), _AES); // had to un-Base64 the 'known' 24-byte key.
return key;
}
}
I cannot create a matching encrypted value nor decrypt a given encrypted value. My guess is it's something to do with how I'm handling the initial vector/salt.
I'm not very crypto-savvy but I'm thinking I should be able to take the sample clear-text and produce the same encrypted value in Java as ColdFusion produced. I am able to encrypt/decrypt my own data with my Java code (so I'm consistent) but I cannot match nor decrypt the ColdFusion sample encrypted value.
I have access to a local webservice that can test the encrypted output. The given ColdFusion output sample passes/decrypts fine (of course). If I try to decrypt the same sample with my Java code (using the actual key and salt) I get a "Given final block not properly padded" error. I get the same net result when I pass my attempt at encryption (using the actual key and salt) to the test webservice.
Any Ideas?