簡體   English   中英

CryptoJS AES 加密和 Java AES 解密

[英]CryptoJS AES encryption and Java AES decryption

我之所以問這個,是因為我已經閱讀了 2 天關於加密 AES 加密的許多帖子,就在我以為我得到它的時候,我意識到我根本沒有得到它。

這篇文章最接近我的問題,我有完全相同的問題,但沒有得到答復:

CryptoJS AES 加密和 JAVA AES 解密值不匹配

我嘗試了很多方法,但我沒有做對。

第一關

我得到了已經加密的字符串(我只得到了代碼來看看他們是怎么做的),所以修改加密方式不是一種選擇。 這就是為什么所有類似的問題對我來說都沒有那么有用。

第二

我確實可以訪問密鑰並且可以修改它(因此,如果需要,可以選擇調整長度)。

加密是在 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 中)? 如果是這樣,該怎么做?

免責聲明:除非您了解加密概念,包括鏈接模式、密鑰派生函數、IV 和塊大小,否則不要使用加密。 並且不要推出自己的安全方案,而是堅持一個既定的方案。 僅僅加入加密算法並不意味着應用程序變得更加安全。

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 Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy 否則,密鑰大小為 256 的 AES 將不起作用並拋出異常:

java.security.InvalidKeyException: Illegal key size

更新

我已經替換了Ola Bini 的 Java 代碼EVP_BytesToKey ,我在我的答案的第一個版本中使用了它,用更慣用和更容易理解的 Java 代碼(見上文)。

另請參閱如何使用 AES 解密使用 openssl 命令加密的 Java 文件? .

在一個系統上加密並在另一個系統上解密時,您會受到系統默認值的支配。 如果任何系統默認值不匹配(並且通常不匹配),那么您的解密將失敗。

一切都必須字節對字節在雙方相同。 實際上,這意味着指定雙方的所有內容,而不是依賴默認值。 如果您在兩端使用相同的系統,則只能使用默認值。 即便如此,最好准確指定。

兩端的密鑰、IV、加密方式、填充和字符串到字節的轉換都需要相同。 尤其值得檢查關鍵字節是否相同。 如果您使用密鑰派生函數 (KDF) 來生成您的密鑰,則該密鑰的所有參數都需要相同,因此必須准確指定。

您的“無效 AES 密鑰長度”很可能表明生成您的密鑰存在問題。 您使用getBytes() 那可能是一個錯誤。 您需要指定獲得的字節類型:ANSI、UTF-8、EBCDIC 等等。 字符串到字節轉換的默認假設是此問題的可能原因。 指定要在兩端顯式使用的轉換。 這樣您就可以確保它們匹配。

如果加密和解密的參數不完全匹配,加密就會失敗。 例如,即使密鑰中有一點差異也會導致它失敗。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM