CryptoJS解密(AES)来自Java的文件bytearray

时间:2016-04-25 11:27:54

标签: java encryption cryptography aes cryptojs

我正在用Java加密文件,需要在客户端解密它。 这是服务器端代码:

Key secretKey = new SecretKeySpec("mysecretmysecret".getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
byte[] outputBytes = cipher.doFinal(read(sampleFile));
return outputBytes;

在客户端,我使用Ajax请求来获取文件并使用CryptoJS AES:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'file', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function (e) {
        var encryptedData = this.response;
        var decrypted = CryptoJS.AES.decrypt(encryptedData, "mysecretmysecret");
        console.log(decrypted);
};
xhr.send();

但是这不会解密文件。我在打印机中将其打印为解密值:

W…y.init {words: Array[0], sigBytes: 0}

我也尝试将arraybuffer转换为建议here的WordArray,但结果仍然相同。 如果有人能指出我正确的方向并告诉我我做错了什么,我会非常高兴。

编辑1: 我已经解决了这个问题。我使用的代码是作为答案发布的。

2 个答案:

答案 0 :(得分:1)

让我们回顾一下,在Java中你正在使用

  • AES,
  • ECB(未指定,但通常是默认值; 它不安全!),
  • PKCS#7填充(未指定但通常是默认值;它与PKCS#5填充相同),
  • 16个字符的密码,作为16个字节的密钥(取决于默认的系统编码)。

如果密钥作为字符串传递给CryptoJS,则必须使用OpenSSL的EVP_BytesToKey和单轮MD5从假定密码派生密钥。由于您的密文未以OpenSSL兼容格式编码,因此它将失败。问题是,你不需要那样。

以下代码会正确解密来自Java的密文,但它不是很安全:

var passwordWords = CryptoJS.enc.Utf8.parse("mysecretmysecret");
var decrypted = CryptoJS.AES.decrypt({
    ciphertext: CryptoJS.lib.WordArray.create(encryptedData) // or use some encoding
}, passwordWords, {
    mode: CryptoJS.mode.ECB
});
console.log(decrypted.toString(CryptoJS.enc.Utf8)); // in case the plaintext is text

ECB模式不包含在基本汇总中,因此您必须在主CryptoJS文件之后的页面中包含JavaScript file
此外,CryptoJS默认情况下不处理ArrayBuffer。您需要包含shim(来源:this answer)。

这个问题是它的不安全感。 ECB模式非常不安全。

  • 绝不使用ECB mode 。它是确定性的,因此在语义上不安全。您应该至少使用CBCCTR等随机模式。 IV / nonce不是秘密,因此您可以将其与密文一起发送。一种常见的方法是将它放在密文的前面。

  • 最好对您的密文进行身份验证,以便像padding oracle attack这样的攻击是不可能的。这可以使用经过验证的模式(如GCM或EAX)或encrypt-then-MAC方案来完成。

  • 密钥可以从密码派生,但应使用适当的方案,如PBKDF2。 Java和CryptoJS都支持这些。

答案 1 :(得分:0)

所以我终于解决了这个问题。感谢Artjom指向正确的方向。 我已将Java代码更改为使用带有PKCS5Padding的CBC。

Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
SecretKeySpec myKey = new SecretKeySpec("mysecretmysecret".getBytes(), "AES");
IvParameterSpec IVKey = new IvParameterSpec("mysecretmysecret".getBytes());
cipher.init(Cipher.ENCRYPT_MODE, myKey, IVKey);
byte[] outputBytes = cipher.doFinal(read(sampleFile));
return outputBytes;

我的javascript就是这样:

var xhr = new XMLHttpRequest();
xhr.open('GET', 'file', true);
xhr.responseType = 'arraybuffer';

xhr.onload = function(e) {
    var encryptedData = this.response;
    var passwordWords = CryptoJS.enc.Utf8.parse("mysecretmysecret"); //yes I know, not a big secret!
    var wordArray = CryptoJS.lib.WordArray.create(encryptedData);
    var decrypted = CryptoJS.AES.decrypt({
        ciphertext: wordArray
    }, passwordWords, {
        iv: passwordWords, //yes I used password as iv too. Dont mind.
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    console.log(decrypted); //Eureka!!
};

xhr.send();

decrypted是WordArray。