使用CryptoJS进行AES加密

8

我需要使用JavaScript实现AES加密。使用AES/CBC/NoPadding模式并创建一个方法来完成16长度的块。我已经使用Java解决了这个问题。它看起来像这样:

public static String encrypt(byte[] key, byte[] initVector, String value) {
    try {
        IvParameterSpec iv = new IvParameterSpec(initVector);
        SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
        Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(completeBlocks(value));
        return Base64.encodeBase64String(encrypted);
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException ex) {
        System.out.println("Error: " + ex);
    }

    return null;
}

/**
 * Completes 16 lenght blocks
 *
 * @param message
 *
 *
 */
static byte[] completeBlocks(String message) {
    try {

        int bytesLenght = message.getBytes("UTF-8").length;
        if (bytesLenght % 16 != 0) {
            byte[] newArray = new byte[bytesLenght + (16 - (bytesLenght % 16))];
            System.arraycopy(message.getBytes(), 0, newArray, 0, bytesLenght);
            return newArray;
        }

        return message.getBytes("UTF-8");

    } catch (UnsupportedEncodingException ex) {
        System.out.println("" + ex);
    }
    return null;
}

public static void main(String[] args) {

    String key = "253D3FB468A0E24677C28A624BE0F939";
    String strToEncrypt = "My Secret text";
    final byte[] initVector = new byte[16];
    String resultado = encrypt(new BigInteger(key, 16).toByteArray(), initVector, strToEncrypt.trim());
    System.out.println("ENCRYPTED:");
    System.out.println(resultado);
}

使用输入 key = 253D3FB468A0E24677C28A624BE0F939strToEncrypt = "My Secret text" 和零向量(IV)进行加密。它会产生如下输出:

7StScX3LnPUly/VNzBes0w==

我知道这是期望的输出结果,正确无误! 我尝试使用JavaScript复制此结果。我使用了CryptoJs库。但我无法生成与Java相同的输出结果。我已经尝试过:

var text = "My Secret text";
var key = CryptoJS.enc.Base64.parse("253D3FB468A0E24677C28A624BE0F939");
var iv  = CryptoJS.enc.Base64.parse("                ");
var encrypted = CryptoJS.AES.encrypt(text, key, {iv: iv});
console.log(encrypted.toString());

var decrypted = CryptoJS.AES.decrypt(encrypted, key, {iv: iv});
console.log(decrypted.toString(CryptoJS.enc.Utf8));

使用相同的输入,我得到了De+CvPVIyiBX2//EE6gXTg==作为输出。我做错了什么?我怎样才能得到与Java相同的输出?非常感谢!

看起来cryptojs不支持CBC模式。 - Tamas Hegedus
此外,您的密钥是以十六进制而不是Base64给出的,而在Base64中添加空格并不等同于零数组。 - Tamas Hegedus
@TamasHegedus 我想你是对的!!很遗憾,CBC模式似乎不受支持。我找到了这个:https://github.com/ricmoo/aes-js。看起来它支持CBC模式。我要试试看!!非常感谢! - Sergio David Romero
1
Sergio,事实证明它确实支持CBC。它是在https://github.com/sytelus/CryptoJS/blob/1d75606113b9446ee2bb56130709927eb3b3c45d/components/cipher-core.js中实现的,而不是在mode-cbc.js中像其他一些模式那样。 - Tamas Hegedus
@TamasHegedus 我正在检查它!你的看起来很棒! 我要试一试!谢谢 - Sergio David Romero
2个回答

15

假设您会像解决空IV这样的问题并且这是一个概念证明,那么您的代码失败是因为:

  1. 您在Java中未使用填充,您需要在JS中使用相同的填充。
  2. 您在Java中手动使用null进行填充,您需要在JS中执行相同的操作。
  3. 您对密钥进行了base64解码,但它不是base64(它是十六进制字节的字符串)。
  4. Java IV是一组null,但在JS中您使用空格(并错误地将其视为base64)。

要在JS中复制输出,请执行以下操作:

CryptoJS.pad.NoPadding = {pad: function(){}, unpad: function(){}};

var text = "My Secret text\0\0";
var key  = CryptoJS.enc.Hex.parse("253D3FB468A0E24677C28A624BE0F939");
var iv   = CryptoJS.enc.Hex.parse("00000000000000000000000000000000");

var encrypted = CryptoJS.AES.encrypt(text, key, {iv: iv, padding: CryptoJS.pad.NoPadding});

console.log(encrypted.toString());

针对:

7StScX3LnPUly/VNzBes0w==


迟到了几分钟。他让我在自定义填充方面出了差错。 - Tamas Hegedus
1
另外,我想指出自定义填充函数存在缺陷,因为在多个位置调用 getBytes 时使用了不同的编码方式。 - Tamas Hegedus
@Alex K. 这看起来相当不错!!我只有一个疑问!我该如何再次解密它!!?谢谢!我尝试过:var decrypt = CryptoJS.AES.decrypt(encrypted、key、{iv:iv}); - Sergio David Romero
最终使用:var decrypted = CryptoJS.AES.decrypt(text.toString(), key, {iv: iv}); console.log(decrypted.toString(CryptoJS.enc.Utf8)); - Sergio David Romero
@SergioDavidRomero,解密不起作用了。你为什么要传递 text.toString() 而不是 encrypt.toString() 呢? - hem
在搜寻了8个小时后,我找到了这个方法,而且它对我有效。 - ashishdhiman2007

2
如果您想使用自定义 IV 进行加密,那么可以这样加密...
    let iv = CryptoJS.lib.WordArray.random(IV_LENGTH);
    const key = CryptoJS.enc.Utf8.parse(ENCRYPTION_KEY);

    let encrypted = CryptoJS.AES.encrypt(text, key, { iv })

    const encrypted = encrypted.iv+':'+encrypted.ciphertext

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接