2017-01-02 352 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等等。字符串到字節轉換的默認假設是這個問題的可能原因。指定要在兩端顯式使用的轉換。這樣你可以確定它們匹配。

加密被設計爲失敗,如果參數不完全匹配的加密和解密。例如,即使密鑰中的一位差異也會導致失敗。