2017-01-02 355 views
2

我只问这个,因为我已经阅读了很多关于加密AES加密的帖子,现在我已经阅读了很多文章,而当我想到我得到它时,我意识到我没有得到它所有。CryptoJS AES加密和Java AES解密

这个职位是一个最接近我的问题,我有完全一样的问题,但它是没有答案:

CryptoJS AES encryption and JAVA AES decryption value mismatch

我曾尝试在许多方面这样做,但我还没有得到它的权利。

首先

,我发现了已加密的字符串(我只得到了代码,看看他们是如何做),这样修改的加密方式是不是一种选择。这就是为什么所有类似的问题对我没有用处。

我也有机会获得密钥,我可以修改它(所以调节长度,如果neccessary一个选项)。

加密是在CryptoJS上完成的,他们将加密的字符串作为GET参数发送。

GetParamsForAppUrl.prototype.generateUrlParams = function() { 
const self = this; 
return new Promise((resolve, reject) => { 
    const currentDateInMilliseconds = new Date().getTime(); 
    const secret = tokenSecret.secret; 
    var encrypted = CryptoJS.AES.encrypt(self.authorization, secret); 
    encrypted = encrypted.toString(); 
    self.urlParams = { 
    token: encrypted, 
    time: currentDateInMilliseconds 
    }; 
    resolve(); 
}); 
}; 

我可以很容易地使用CryptoJS与解密此JavaScript的:

var decrypted = CryptoJS.AES.decrypt(encrypted_string, secret); 
    console.log(decrypted.toString(CryptoJS.enc.Utf8)); 

但我并不想这样做的JavaScript,出于安全考虑,所以我想在这个解密Java的:

String secret = "secret"; 
byte[] cipherText = encrypted_string.getBytes("UTF8"); 
SecretKey secKey = new SecretKeySpec(secret.getBytes(), "AES"); 
Cipher aesCipher = Cipher.getInstance("AES"); 
aesCipher.init(Cipher.DECRYPT_MODE, secKey); 
byte[] bytePlainText = aesCipher.doFinal(byteCipherText); 
String myDecryptedText = = new String(bytePlainText); 

之前,我有我在做什么什么想法,我试过的base64解码,加入当然没有的一些IV和很多东西我看了,它的工作。

但是在我开始明白,有点,我在做什么,我写了上面简单的脚本,并让我在岗位上同样的错误:无效的AES密钥长度

我不知道从这往哪儿走。在阅读了很多关于这个之后,解决方案似乎是哈希或填充,但我无法控制加密方法,所以我不能真正哈希或填充它。但是正如我所说,我可以更改密钥,以便它可以匹配一些特定的长度,我试图改变它,但因为我在黑暗中拍摄,所以我不知道这是不是真的解决方案。

所以,我的问题基本上是,如果我得到了加密字符串(像第一个脚本的JavaScript)和私有密钥,是有办法解密(在Java中)?如果是这样,该怎么办?

+0

Javascript侧面的* secret *是什么样的?这是否也是一个字符串?它是一个十六进制字符串,例如“34D3724F1A”? – Codo

+0

'String'不是二进制数据的容器。 – EJP

回答

8

CryptoJS实现了与OpenSSL相同的密钥派生函数,以及将IV放入加密数据的相同格式。因此,所有处理OpenSSL编码数据的Java代码都适用。

考虑下面的JavaScript代码:

var text = "The quick brown fox jumps over the lazy dog. "; 
var secret = "René Über"; 
var encrypted = CryptoJS.AES.encrypt(text, secret); 
encrypted = encrypted.toString(); 
console.log("Cipher text: " + encrypted); 

我们得到密文:

U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk= 

在Java方面,我们有

String secret = "René Über"; 
String cipherText = "U2FsdGVkX1+tsmZvCEFa/iGeSA0K7gvgs9KXeZKwbCDNCs2zPo+BXjvKYLrJutMK+hxTwl/hyaQLOaD7LLIRo2I5fyeRMPnroo6k8N9uwKk="; 

byte[] cipherData = Base64.getDecoder().decode(cipherText); 
byte[] saltData = Arrays.copyOfRange(cipherData, 8, 16); 

MessageDigest md5 = MessageDigest.getInstance("MD5"); 
final byte[][] keyAndIV = GenerateKeyAndIV(32, 16, 1, saltData, secret.getBytes(StandardCharsets.UTF_8), md5); 
SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES"); 
IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]); 

byte[] encrypted = Arrays.copyOfRange(cipherData, 16, cipherData.length); 
Cipher aesCBC = Cipher.getInstance("AES/CBC/PKCS5Padding"); 
aesCBC.init(Cipher.DECRYPT_MODE, key, iv); 
byte[] decryptedData = aesCBC.doFinal(encrypted); 
String decryptedText = new String(decryptedData, StandardCharsets.UTF_8); 

System.out.println(decryptedText); 

结果是:

The quick brown fox jumps over the lazy dog. 

这是我们开始的文字。表情符号,口音和变音符号也适用。

GenerateKeyAndIV是重新实现OpenSSL的密钥派生函数EVP_BytesToKey(请参阅https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c)的帮助器函数。

/** 
* Generates a key and an initialization vector (IV) with the given salt and password. 
* <p> 
* This method is equivalent to OpenSSL's EVP_BytesToKey function 
* (see https://github.com/openssl/openssl/blob/master/crypto/evp/evp_key.c). 
* By default, OpenSSL uses a single iteration, MD5 as the algorithm and UTF-8 encoded password data. 
* </p> 
* @param keyLength the length of the generated key (in bytes) 
* @param ivLength the length of the generated IV (in bytes) 
* @param iterations the number of digestion rounds 
* @param salt the salt data (8 bytes of data or <code>null</code>) 
* @param password the password data (optional) 
* @param md the message digest algorithm to use 
* @return an two-element array with the generated key and IV 
*/ 
public static byte[][] GenerateKeyAndIV(int keyLength, int ivLength, int iterations, byte[] salt, byte[] password, MessageDigest md) { 

    int digestLength = md.getDigestLength(); 
    int requiredLength = (keyLength + ivLength + digestLength - 1)/digestLength * digestLength; 
    byte[] generatedData = new byte[requiredLength]; 
    int generatedLength = 0; 

    try { 
     md.reset(); 

     // Repeat process until sufficient data has been generated 
     while (generatedLength < keyLength + ivLength) { 

      // Digest data (last digest if available, password data, salt if available) 
      if (generatedLength > 0) 
       md.update(generatedData, generatedLength - digestLength, digestLength); 
      md.update(password); 
      if (salt != null) 
       md.update(salt, 0, 8); 
      md.digest(generatedData, generatedLength, digestLength); 

      // additional rounds 
      for (int i = 1; i < iterations; i++) { 
       md.update(generatedData, generatedLength, digestLength); 
       md.digest(generatedData, generatedLength, digestLength); 
      } 

      generatedLength += digestLength; 
     } 

     // Copy key and IV into separate byte arrays 
     byte[][] result = new byte[2][]; 
     result[0] = Arrays.copyOfRange(generatedData, 0, keyLength); 
     if (ivLength > 0) 
      result[1] = Arrays.copyOfRange(generatedData, keyLength, keyLength + ivLength); 

     return result; 

    } catch (DigestException e) { 
     throw new RuntimeException(e); 

    } finally { 
     // Clean out temporary data 
     Arrays.fill(generatedData, (byte)0); 
    } 
} 

请注意,您必须安装Java加密扩展(JCE)Unlimited Strength Jurisdiction Policy。否则,用256密钥大小AES公司将无法正常工作,并抛出一个异常:

java.security.InvalidKeyException: Illegal key size 

更新

我已经更换的EVP_BytesToKeyOla Bini's Java code,我在我的答案的第一个版本使用,一个更习惯和更容易理解的Java代码(见上文)。

另请参阅How to decrypt file in Java encrypted with openssl command using AES?

+0

我已经安装了JCE,我读了一些关于密钥大小,我相信有将它升级到256的方式,但我不介意大小,128是好的。这看起来非常有希望,明天会试一试并回复你,谢谢。 – Lauro182

+0

如果您无法更改数据的加密方式,则无法选择密钥长度。我将不得不使用256位。 – Codo

+0

好吧,我下载了文件,备份了当前文件并替换了它们。我重新启动应用程序,重新启动计算机,但我仍然得到:非法密钥大小在aesCBC.init(Cipher.DECRYPT_MODE,键,IV); – Lauro182

1

在一个系统上进行加密并在另一个系统上解密时,系统默认是有效的。如果任何系统默认值不匹配(而且他们通常不会),那么您的解密将失败。

一切都必须是字节字节在两边相同。有效地,这意味着指定双方的一切,而不是依靠违约。如果您在两端使用相同的系统,则只能使用默认值。即使如此,最好指定一个确切的。

Key,IV,加密模式,填充和字符串到字节转换在两端都需要相同。特别值得检查的是关键字节是相同的。如果您使用密钥派生函数(Key Deriva Function,KDF)来生成密钥,那么所有参数都必须相同,因此需要精确指定。

您的“无效的AES密钥长度”可能表明生成密钥时出现问题。您使用getBytes()。这可能是一个错误。你需要指定你得到的字节类型:ANSI,UTF-8,EBCDIC等等。字符串到字节转换的默认假设是这个问题的可能原因。指定要在两端显式使用的转换。这样你可以确定它们匹配。

加密被设计为失败,如果参数不完全匹配的加密和解密。例如,即使密钥中的一位差异也会导致失败。