本文介绍了用于加密/解密的 Java SimpleCrypto 类在 Coldfusion 9 和 Java (Android) 中产生不同的结果的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用广泛使用的 SimpleCrypto java 类在 Java (Android) 中加密字符串并在 ColdFusion 9 中解密字符串(反之亦然).我已经将完全相同的 SimpleCrypto 类导入到 ColdFusion 中,并像这样调用它:

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#">

当使用密钥apple"加密字符串john"时,它会在 CF 中输出:9E90A36325AE4F4F7352D6469A7068A2

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

当我在 Android 中使用 EXACT SAME 类时:

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();
 }

日志输出:CBE2ADDBA9882F545DFEC1700E7CD518

The log outputs: CBE2ADDBA9882F545DFEC1700E7CD518

不用说,我要疯了,因为这些结果是不同的.有谁知道为什么在 ColdFusion 和 Java 中使用完全相同的代码会产生不同的结果?任何帮助将不胜感激.

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.

这是 SimpleCrypto.java 的源代码:

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));
    }

}

推荐答案

getRawKey() 方法有缺陷.它使用 SecureRandom 的实例而不是密钥派生函数 (KDF).

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

根据实现,setSeed() 方法将将种子添加到当前状态或将其用作唯一的种子em>.Java SE 7 及之前版本中的 Oracle 提供者将其用作单个种子,其他提供者(例如最新版本的 Android 中基于 OpenSSL 的提供者)可能会简单地将种子添加到状态中.这意味着检索到的密钥可能确实完全随机;任何用它加密的东西都永远无法解密.

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.

此外,"SHA1PRNG" 的确切实现尚未明确定义.所以不同的提供者可能使用不同的实现.请仅将 SecureRandom 实例用于随机数生成.

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.

如果您有密码,请使用基于密码的密钥派生函数(例如 PBKDF2)将其转换为合适的密钥.如果您有一个具有足够熵的秘密,您可以尝试找到基于密钥的密钥派生函数 (KBKDF) 的实现,例如 Bouncy Castle 中的 HKDF.

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.

除了密钥推导之外,该示例代码还存在编码/解码问题.它还使用不安全的 ECB 操作模式(Oracle 提供程序中 Java 的默认模式).

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).

不要使用SimpleCrypto,这是一个糟糕的例子.

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

这篇关于用于加密/解密的 Java SimpleCrypto 类在 Coldfusion 9 和 Java (Android) 中产生不同的结果的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-31 20:42