java - CryptoJS AES 加密和 Java AES 解密

标签 java encryption cryptography cryptojs

我之所以问这个问题,是因为 2 天来我已经阅读了很多关于加密 AES 加密的帖子,就在我以为我明白了的时候,我意识到我根本没有明白。

这篇文章是最接近我的问题的,我有完全相同的问题但没有得到解答:

CryptoJS AES encryption and JAVA AES decryption value mismatch

我试过很多方法,但我做对了。

首先

我得到的是已经加密的字符串(我只是得到代码看看他们是怎么做的),所以修改加密方式不是一个选项。这就是为什么所有类似的问题对我都没有那么有用。

第二

我确实可以访问 key 并且可以修改它(因此如果需要,可以选择调整长度)。

加密是在 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 key 长度

我不知道从这里去哪里。在阅读了很多关于此的内容之后,解决方案似乎是散列或填充,但我无法控制加密方法,因此我无法真正对 secret 进行散列或填充。

但正如我所说,我可以更改 key ,使其可以匹配特定长度,我已经尝试更改它,但由于我是在黑暗中拍摄,所以我真的不知道这是不是解决方案。

所以,我的问题基本上是,如果我得到加密的字符串(像第一个脚本一样在 javascript 中)和 key ,有没有办法解密它(在 Java 中)?如果可以,怎么做?

最佳答案

免责声明:除非您了解包括链接模式、 key 派生函数、IV 和 block 大小在内的加密概念,否则请勿使用加密。并且不要推出自己的安全方案,而要坚持既定的方案。仅仅引入加密算法并不意味着应用程序变得更加安全。

CryptoJS实现了和OpenSSL一样的 key 推导功能,同样的格式将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 的 key 派生函数 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 .否则, key 大小为 256 的 AES 将无法工作并抛出异常:

java.security.InvalidKeyException: Illegal key size

更新

我已经替换了Ola Bini's Java code EVP_BytesToKey,我在第一个版本的答案中使用了它,它具有更惯用且更易于理解的 Java 代码(见上文)。

另见 How to decrypt file in Java encrypted with openssl command using AES? .

关于java - CryptoJS AES 加密和 Java AES 解密,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41432896/

相关文章:

c++ - Wincrypt : Unable to decrypt file which was encrypted in C#. NTE_BAD_DATA 在 CryptDecrypt

java - 黑莓中使用 NoPadding 进行 AES 加密

C# 哈希字节到 Java -- 将 SQL Server HashByte 转换为 Java

Java 到 Win32 加密 API

java - 有什么方法可以基于自由运行、独立于系统时钟的时钟来停放线程吗?

java - DAO 和服务层的设计模式

java - 为什么 "while"在普林斯顿的加权快速联合实现中使用

API key 内容加密

java - 如何通过单击 JButton 启动第二个 JFrame?

java - 根据 RFC 测试 vector 计算 Java 中的 ECDSA 签名