4

I have data encrypted in ColdFusion that I need to be able to decrypt and encrypt to the same exact value using Java. I was hoping someone may be able to help me with this. I will specify everything used in ColdFusion except for the actual PasswordKey, which I must keep secret for security purposes. The PasswordKey is 23 characters long. It uses upper and lowercase letters, numbers, and the + and = signs. I know this is a lot to ask, but any help would be greatly appreciated.

I tried using a Java encryption example I found online and just replacing the line below with the 23 characters used in our CF app:

private static final byte[] keyValue = new byte[] {'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' };` 

But I get the error:

java.security.InvalidKeyException: Invalid AES key length: 23 bytes

The CF code is:

Application.PasswordKey = "***********************";
Application.Algorithm = "AES";
Application.Encoding = "hex";

<cffunction name="encryptValue" access="public" returntype="string">
        <cfargument name="strEncryptThis" required="yes">

        <cfreturn Encrypt(TRIM(strEncryptThis), Application.PasswordKey, Application.Algorithm, Application.Encoding)>
</cffunction>


<cffunction name="decryptValue" access="public" returntype="string">
    <cfargument name="strDecryptThis" required="yes">

    <cfreturn Decrypt(TRIM(strDecryptThis), Application.PasswordKey, Application.Algorithm, Application.Encoding)>
</cffunction>
lkamal
  • 3,788
  • 1
  • 20
  • 34
kei23th
  • 283
  • 2
  • 6
  • 14
  • 1
    AES requires its key to be either 128, 192 or 256 bits. Note that the password is not the key. ColdFusion is doing something to turn the password into a key, probably by using some kind of key generating algorithm. Find out what that algorithm is and use it in your java code. – Qwerky Jun 13 '12 at 14:52

4 Answers4

2

Your secret key is most likely a Base64 encoded key (23 chars should decode to about 16 bytes, which is the right length for a 128 bit key for AES).

So, in your java code, first run your secret key string through a Base64 decoder to get a byte[] of the appropriate length (16 bytes) for the AES algorithm.

jtahlborn
  • 52,909
  • 5
  • 76
  • 118
2

128 but AES Encryption supports key key size of 16 bytes.
16 * 8 = 128 bits, even in the example the key is 16 bytes.

Sounds like your key is in Base64 so use Base64.decode(key or key.getBytes()) to get the byte array, check its in 16 bytes otherwise make it 16 bytes by padding.

maaz
  • 4,371
  • 2
  • 30
  • 48
1

Thank you everyone for your help. I wanted to post my final solution for others to use. I am including my entire encryption package code minus the specific password key (again for security). This code creates the same hex string as the CF code listed in the question, and decrypts it back to the proper english text string.

I found the bytesToHex and hexStringToByteArray functions in other question on stackoverflow, so my thanks to users maybeWeCouldStealAVan and Dave L. respectively also. I think I will look into other base 64 encoders/decoders in case the one from sun is ever made unavailable, but this definitely works for now. Thanks again.

package encryptionpackage;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher; 
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;

public class encryption 
{
    // Note: The full CF default is "AES/ECB/PKCS5Padding"
    private static final String ALGORITHM = "AES";
    // The 24 character key from my CF app (base64 encoded)
    // typically generated with:  generateSecretKey("AES") 
    private static final String passKey = "***********************"; 

     public static String encrypt(String valueToEnc) throws Exception 
     {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.ENCRYPT_MODE, key);
        byte[] encValue = c.doFinal(valueToEnc.getBytes());
        String encryptedValue = bytesToHex(encValue);
        return encryptedValue;
    }

    public static String decrypt(String encryptedValue) throws Exception 
    {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decordedValue = hexStringToByteArray(encryptedValue);
        byte[] decValue = c.doFinal(decordedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }

    private static Key generateKey() throws Exception 
    {
          byte[] keyValue;
          keyValue = new BASE64Decoder().decodeBuffer(passKey);
        Key key = new SecretKeySpec(keyValue, ALGORITHM);

        return key;
    }

    public static String bytesToHex(byte[] bytes) 
    { 
        final char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F'}; 
        char[] hexChars = new char[bytes.length * 2]; 
        int v; 
        for ( int j = 0; j < bytes.length; j++ ) 
        { 
            v = bytes[j] & 0xFF; 
            hexChars[j * 2] = hexArray[v >>> 4]; 
            hexChars[j * 2 + 1] = hexArray[v & 0x0F]; 
        } 
        return new String(hexChars); 
    } 

    public static byte[] hexStringToByteArray(String s) 
    { 
        int len = s.length(); 
        byte[] data = new byte[len / 2]; 
        for (int i = 0; i < len; i += 2) 
        { 
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) 
                                 + Character.digit(s.charAt(i+1), 16)); 
        } 
        return data; 
    } 

}
Leigh
  • 28,765
  • 10
  • 55
  • 103
kei23th
  • 283
  • 2
  • 6
  • 14
  • Glad you solved it. Small correction. The base64 key from CF is 24 characters (not 23) which BASE64Decoder decodes into 16 bytes. (Code comments updated to reflect this). – Leigh Jun 13 '12 at 17:59
  • Also, the line `valueToEnc.getBytes()` should be `valueToEnc.getBytes("UTF-8")` for compatibility with CF. If you omit the charset, it [uses the default charset](http://docs.oracle.com/javase/6/docs/api/java/lang/String.html#getBytes%28%29), which is not guaranteed to be compatible with CF's default ("UTF-8"). – Leigh Nov 03 '13 at 15:14
0

AES Encryption only supports a key-size of 128 bits, 192 bits or 256 bits.

http://en.wikipedia.org/wiki/Advanced_Encryption_Standard

You can't just take any byte array and use it as an AES key. In the sample code you see above, the example cleverly used 16 characters, which corresponds to a 128-bit key.

This is because 1 character or rather 1 byte corresponds to 8 bits.

A 16-value byte array will then correspond to 16 * 8 = 128 bits

23 characters = 23 * 8 = 184 bits, thus it is an invalid key-size.

You need either 16 characters, 24 characters, or 32 characters.

That being said, using merely characters for AES encryption is extremely insecure. Do use a proper and secure random key for encrypt purposes.

To generate a secure and random AES key:

SecureRandom random = new SecureRandom();
byte [] secret = new byte[16];
random.nextBytes(secret);

http://docs.oracle.com/javase/6/docs/api/java/security/SecureRandom.html

Lai Xin Chu
  • 2,462
  • 15
  • 29