Java 和 Java 脚本中的加密和解密 (Nodejs)

Encryption and Decryption in Java and Javascript (Nodejs)

我在 Java 中加密文本并想在 Java 脚本中解密,反之亦然,我的 Java 代码如下所示

Cipher c = Cipher.getInstance("AES/GCM/NoPadding");
SecretKeySpec k = new SecretKeySpec(this.key.getBytes(), "AES");
c.init(Cipher.ENCRYPT_MODE, k);
        
byte[] encryptedData = c.doFinal(this.value.getBytes());
byte[] iv = c.getIV();
        
sb.append(new String(Base64.getEncoder().encode(iv)));
sb.append(';');
sb.append(new String(Base64.getEncoder().encode(encryptedData)));
// send sb.toString() to the other end

我在 Nodejs 中尝试了 node-forge 和 elliptic 解密,在 node-forge 中我有一个错误抱怨我没有标签,有人可以在 Nodejs 中提供解决方案吗?

Java脚本代码

function convertFromMxBase64(FormatEncryptedString) {
    const speratorIndex = FormatEncryptedString.indexOf(';');
    const encryptedBase64 = mxFormatEncryptedString.substr(speratorIndex + 1);
    const ivBase64 = mxFormatEncryptedString.substr(0, speratorIndex);

    return {
        iv: forge.util.createBuffer(forge.util.decode64(ivBase64), 'utf8'),
        payload: forge.util.createBuffer(forge.util.decode64(encryptedBase64), 'utf8')
    };
}

function decrypt() {

    let { iv, payload } = convertFromMxBase64(mxCombi);
    
    const key = forge.util.createBuffer(theKey, 'utf8');    

    var cipher = forge.cipher.createDecipher("AES-GCM", key); // forge.rc2.createDecryptionCipher(key);
    cipher.start({ iv });
    cipher.update(payload);
    cipher.finish();
    var encrypted = cipher.output;
    // outputs encrypted hex
    console.log(encrypted.toHex());   
}

GCM 模式根据定义使用 authenticity/integrity 检查所需的标签。
NodeJS 端的标签问题是由于没有考虑身份验证标签引起的。在 Java 方面,这不是必需的,因为 SunJCE 提供者通过将标记附加到密文来自动执行此操作:因此,结果的最后 16 个字节是标记。相比之下,在 NodeJS 端,密文和标签是分离处理的,因此这两部分必须 显式 分开,例如:

var ciphertext = forge.util.createBuffer(payload.data.substring(0, payload.length() - 16));
var tag = forge.util.createBuffer(payload.data.substring(payload.length() - 16));

标签和密文必须按如下方式传递给 cipher 实例:

cipher.start({ iv:iv, tag:tag }); // apply tag
cipher.update(ciphertext); // apply ciphertext

另一个错误是在创建ivpayload时使用了UTF-8编码。这是错误的,数据必须作为二进制字符串传递,即 'utf8' 必须替换为 'binary' 或完全删除。

iv: forge.util.createBuffer(forge.util.decode64(ivBase64)), 
payload: forge.util.createBuffer(forge.util.decode64(encryptedBase64))

此外,在 Java 代码的 getBytes() 中应指定编码,例如getBytes(StandardCharsets.UTF_8)。如果不这样做,将使用 platform-dependent 默认编码,这意味着可以根据环境应用不同的编码。

通过这些更改,解密在 NodeJS 端进行。