用于加密/解密的Java SimpleCrypto类在Coldfusion 9和Java(Android)中产生不同的结果

我试图使用广泛使用的SimpleCrypto java类来加密Java(Android)中的字符串并解密ColdFusion 9中的字符串(反之亦然)。 我已将完全相同的SimpleCrypto类导入ColdFusion并将其命名为:

  sc = createObject("java", "SimpleCrypto").init(); encrypted = sc.encrypt(myKey, "john");   

当用“apple”键加密字符串“john”时,它在CF中输出:9E90A36325AE4F4F7352D6469A7068A2

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

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

日志输出:CBE2ADDBA9882F545DFEC1700E7CD518

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

以下是SimpleCrypto.java的源代码:

 import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; /** * Usage: * 
 * String crypto = SimpleCrypto.encrypt(masterpassword, cleartext) * ... * String cleartext = SimpleCrypto.decrypt(masterpassword, crypto) * 

* @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 >4)&0x0f)).append(HEX.charAt(b&0x0f)); } }

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

根据实现, setSeed()方法将种子添加到当前状态,或者将其用作唯一种子 。 Java SE 7及更早版本中的Oracle提供程序将使用它作为单个种子,其他提供程序(例如在最新版本的Android中基于OpenSSL的提供程序)可能只是将种子添加到状态。 这意味着检索到的密钥可能确实是完全随机的; 因此,用它加密的任何东西都不能被解密

此外, "SHA1PRNG"的确切实现尚未明确定义。 因此不同的提供商可能使用不同的实现。 请仅使用SecureRandom实例生成随机数。

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

除了密钥推导之外,该样本代码也存在编码/解码问题。 它还使用不安全的ECB操作模式(Oracle提供程序中Java的缺省值)。

不要使用SimpleCrypto ,这是一个可怕的例子。