Java SimpleCrypto Class for encryption / decryptio

2019-01-12 02:02发布

问题:

I am trying to use the widely used SimpleCrypto java class to encrypt a string in Java (Android) and decrypt the string in ColdFusion 9 (and vice versa). I have imported the exact same SimpleCrypto class into ColdFusion and called it like this:

<cfset myKey = "apple">
<cfscript>
    sc = createObject("java", "SimpleCrypto").init();
    encrypted = sc.encrypt(myKey, "john");
</cfscript>
<cfdump var="#encrypted#">

When encrypting the string "john" with a key of "apple" it outputs this in CF: 9E90A36325AE4F4F7352D6469A7068A2

When I use the EXACT SAME class in Android:

String key = "apple";    
try {
    sEncrypted = SimpleCrypto.encrypt(key, "john");
    Log.d(TAG, sEncrypted);
 } catch (Exception e) {
     e.printStackTrace();
 }

The log outputs: CBE2ADDBA9882F545DFEC1700E7CD518

Needless to say, I'm going bonkers because these results are different. Does anyone know why using the same exact code in ColdFusion and Java would produce different results? Any help would be greatly appreciated.

Here is the source code for SimpleCrypto.java:

import java.security.SecureRandom;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/**
 * Usage:
 * <pre>
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext)
 * ...
 * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto)
 * </pre>
 * @author ferenc.hechler
 */

public class SimpleCrypto {

    public static String encrypt(String seed, String cleartext) throws Exception {
            byte[] rawKey = getRawKey(seed.getBytes());
            byte[] result = encrypt(rawKey, cleartext.getBytes());
            return toHex(result);
    }

    public static String decrypt(String seed, String encrypted) throws Exception {
            byte[] rawKey = getRawKey(seed.getBytes());
            byte[] enc = toByte(encrypted);
            byte[] result = decrypt(rawKey, enc);
            return new String(result);
    }

    private static byte[] getRawKey(byte[] seed) throws Exception {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
            sr.setSeed(seed);
        kgen.init(128, sr); // 192 and 256 bits may not be available
        SecretKey skey = kgen.generateKey();
        byte[] raw = skey.getEncoded();
        return raw;
    }


    private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(clear);
            return encrypted;
    }

    private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES");
        cipher.init(Cipher.DECRYPT_MODE, skeySpec);
        byte[] decrypted = cipher.doFinal(encrypted);
            return decrypted;
    }

    public static String toHex(String txt) {
            return toHex(txt.getBytes());
    }
    public static String fromHex(String hex) {
            return new String(toByte(hex));
    }

    public static byte[] toByte(String hexString) {
            int len = hexString.length()/2;
            byte[] result = new byte[len];
            for (int i = 0; i < len; i++)
                    result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
            return result;
    }

    public static String toHex(byte[] buf) {
            if (buf == null)
                    return "";
            StringBuffer result = new StringBuffer(2*buf.length);
            for (int i = 0; i < buf.length; i++) {
                    appendHex(result, buf[i]);
            }
            return result.toString();
    }
    private final static String HEX = "0123456789ABCDEF";
    private static void appendHex(StringBuffer sb, byte b) {
            sb.append(HEX.charAt((b>>4)&0x0f)).append(HEX.charAt(b&0x0f));
    }

}

回答1:

The getRawKey() method is flawed. It uses an instance of SecureRandom instead of a key derivation function (KDF).

Depending on the implementation, the setSeed() method will either add the seed to the current state or it will use it as the only seed. The Oracle provider in Java SE 7 and before will use it as the single seed, other providers such as those based on OpenSSL in the latest versions of Android may simply add the seed to the state. This means that the retrieved key may indeed be entirely random; anything encrypted with it can therefore not be decrypted, ever.

Furthermore, the exact implementation of "SHA1PRNG" has not been well defined. So different providers may use a different implementations. Please use SecureRandom instances for random number generation only.

If you have a password, use a Password Based Key Derivation Function such as PBKDF2 to convert it to a suitable key. If you have a secret with enough entropy, you could try and find an implementation of a Key Based Key Derivation Function (KBKDF), for instance HKDF in Bouncy Castle.

Besides the key derivation, there are encoding/decoding issues with that sample code as well. It also uses the insecure ECB mode of operation (the default for Java in the Oracle provider).

Don't use SimpleCrypto, it is a terrible example.