首页 > 解决方案 > Java (Cipher) 和 JavaScript (crypto-js) 之间的加密互操作

问题描述

我的任务是修复一个互操作加密算法,该算法从以前开始工作得非常好,但由于未知原因突然失控,没有人接触过这两种语言(Java 和 JS)的任何代码。

我不太熟悉密码学,所以我不知道寻找或使用什么可能的解决方案。任务基本上是把 Java 上的加密代码翻译成 JavaScript,这两者都将产生一个要通过 Java 解密的 Base64 字符串。

以下是使用 Java 和 JS 进行的加密以及在 Java 上的解密过程的代码片段:

Java 加密

public static String encryptMsg(String message) {
    @SuppressLint("GetInstance") Cipher cipher = null;
    try {
        cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, secret);
        byte[] cipherText = cipher.doFinal(message.getBytes(UTF_CHARSET));
        return Base64.encodeToString(cipherText, Base64.DEFAULT);
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
        e.printStackTrace();
    } catch (NullPointerException e) {
        //Do nothing, nothing to encrypt
    }
    return null;
}

JavaScript 加密

function encryptData(data, key) {
 const options = {
  mode: Crypto.mode.ECB,
  padding: Crypto.pad.Pkcs7
 }
 const secret = Crypto.enc.Utf8.parse(key)
 const encrypted = Crypto.AES.encrypt(data, secret, options)
 return encrypted.ciphertext.toString(Crypto.enc.Base64)
}

Java解密

public static String decryptMsg(String base64cipherText) {
    @SuppressLint("GetInstance") Cipher cipher = null;
    try {
        cipher = Cipher.getInstance(TRANSFORMATION);
        cipher.init(Cipher.DECRYPT_MODE, secret);
        String decryptString = new String(cipher.doFinal(Base64.decode(base64cipherText, Base64.DEFAULT)), UTF_CHARSET);
        return decryptString;
    } catch (NoSuchAlgorithmException | NoSuchPaddingException | BadPaddingException | IllegalBlockSizeException | InvalidKeyException e) {
        e.printStackTrace();
    } catch (NullPointerException e) {
        //Do nothing, nothing to decrypt
    }
    return null;
}

目前,结果null在解密时使用 JavaScript 加密函数返回加密字符串,因此它可能正确加密(?)我不确定我在这里遗漏了什么或做错了什么......

标签: encryptionaes

解决方案


似乎您缺少 IV(初始化向量)。

真的不知道 IV 是什么,或者这里是否需要它,加密 Java 代码没有在任何地方说明它

IV 是一个初始化向量,允许重用密钥来加密多个消息(或块),请在使用时查看CBC 块模式

我不确定 JavaScript API,但至少我可以用 Java 举个例子。你也可以看看我关于加密示例的博客

Java 加密

 SecureRandom rnd = new SecureRandom();
 byte[] iv = new byte[SYMMETRIC_BLOCK_SIZE / 8];
 IvParameterSpec ivParamSpec = new IvParameterSpec(iv);
 SecretKey symmetricKey = new SecretKeySpec(encryptionParams.getKey(), SYMMETRIC_KEY_ALG);

Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.ENCRYPT_MODE, symmetricKey, ivParamSpec);

byte[] encrypted = cipher.doFinal(encryptionParams.getPlaintext());
/* and encoded form can contain form of base64( IV + ciphertext )  */

对于 CBC 模式,IV 必须是随机的。如果您不指定 IVParameter,则会生成它,您可以从cipher.getIV();. IV 可以是公开的,它通常放在密文之前,因为需要 IV 来解密密文本身。

Java解密

/* if IV is prepended before the ciphertext, it can be fetched as sub-array
  of the decoded message */
IvParameterSpec ivParamSpec = new IvParameterSpec(iv);

Cipher cipher = Cipher.getInstance(SYMMETRIC_CIPHER_NAME);
cipher.init(Cipher.DECRYPT_MODE, symmetricKey, ivParamSpec);

byte[] decrypted = cipher.doFinal(encryptionParams.getCiphertext());

在此示例中没有 Mac(包括消息验证码),您可以查看链接示例。

对于 JavaScript,您应该查看使用的 API,但原理保持不变(您也必须以某种方式生成、使用、传递和提供 IV)。这个博客似乎包含更完整的代码。

var iv = CryptoJS.lib.WordArray.random(128/8);
var encrypted = CryptoJS.AES.encrypt(msg, key, { 
  iv: iv, 
  padding: CryptoJS.pad.Pkcs7,
  mode: CryptoJS.mode.CBC
});

推荐阅读