使用Node.js中的AES/CBC/PKCS5Padding

3

我正在尝试将我的Java代码转换为Node.js代码。
在加密方面遇到了一些问题。
这是我的Java代码:编译后的代码

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.UnsupportedEncodingException;
import java.security.GeneralSecurityException;
import java.util.Base64;

public class AESCBCPKCS5Encryption {
    private static final String ALGORITHM = "AES/CBC/PKCS5Padding";

    public static String encrypt(String message, String key) throws GeneralSecurityException, UnsupportedEncodingException {
        if (message == null || key == null) {
            throw new IllegalArgumentException("text to be encrypted and key should not be null");
        }
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        byte[] messageArr = message.getBytes();
        SecretKeySpec keySpec = new SecretKeySpec(Base64.getDecoder().decode(key), "AES");
        byte[] ivParams = new byte[16];
        byte[] encoded = new byte[messageArr.length + 16];
        System.arraycopy(ivParams, 0, encoded, 0, 16);
        System.arraycopy(messageArr, 0, encoded, 16, messageArr.length);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(ivParams));
        byte[] encryptedBytes = cipher.doFinal(encoded);
        encryptedBytes = Base64.getEncoder().encode(encryptedBytes);
        return new String(encryptedBytes);
    }

    public static String decrypt(String encryptedStr, String key) throws GeneralSecurityException, UnsupportedEncodingException {
        if (encryptedStr == null || key == null) {
            throw new IllegalArgumentException("text to be decrypted and key should not be null");
        }
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        SecretKeySpec keySpec = new
                SecretKeySpec(Base64.getDecoder().decode(key), "AES");
        byte[] encoded = encryptedStr.getBytes();
        encoded = Base64.getDecoder().decode(encoded);
        byte[] decodedEncrypted = new byte[encoded.length - 16];
        System.arraycopy(encoded, 16, decodedEncrypted, 0, encoded.length - 16);
        byte[] ivParams = new byte[16];
        System.arraycopy(encoded, 0, ivParams, 0, ivParams.length);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(ivParams));
        byte[] decryptedBytes = cipher.doFinal(decodedEncrypted);
        return new String(decryptedBytes);
    }

    public static void main(String[] args) throws GeneralSecurityException, UnsupportedEncodingException {
        String str = "<Request xmlns=\"http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd\" ><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>";
        String key = "e3a74e3c7599f3ab4601d587bd2cc768";
        String enc = encrypt(str, key);
        System.out.println(enc);
        String dec = decrypt(enc, key);
        System.out.println(dec);
    }
}

这是我的JavaScript代码:

var crypto = require('crypto');

function getAlgorithm(keyBase64) {

    var key = Buffer.from(keyBase64, 'base64');
    switch (key.length) {
        case 16:
            return 'aes-128-cbc';
        case 32:
            return 'aes-256-cbc';

    }

    throw new Error('Invalid key length: ' + key.length);
}


function encrypt(plainText, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const cipher  = crypto.createCipheriv(getAlgorithm(keyBase64), key, iv);
    let encrypted = cipher.update(plainText, 'utf8', 'base64');
    encrypted += cipher.final('base64');
    return encrypted;
}

function decrypt(messagebase64, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv);
    let decrypted  = decipher.update(messagebase64, 'base64');
    decrypted += decipher.final();
    return decrypted;
}


var keyBase64 = "DWIzFkO22qfVMgx2fIsxOXnwz10pRuZfFJBvf4RS3eY=";
var ivBase64  = 'e3a74e3c7599f3ab4601d587bd2cc768';
var plainText = '<Request xmlns="http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd"><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>';

var cipherText          = encrypt(plainText, keyBase64, ivBase64);
var decryptedCipherText = decrypt(cipherText, keyBase64, ivBase64);

console.log('Algorithm: ' + getAlgorithm(keyBase64));
console.log('Plaintext: ' + plainText);
console.log('Ciphertext: ' + cipherText);
console.log('Decoded Ciphertext: ' + decryptedCipherText);

加密出现错误,
我在这里做错了什么?


(1)NodeJS 代码缺少 IV 和明文的连接(“encrypt”方法),以及(加密的)IV 和密文的分离(“decrypt”方法)。注意:对于加密,IV 和 ciphertext 的连接就足够了。IV 不是一个秘密,因此不需要加密。(2)在 Java 代码中,使用零向量作为 IV,伪随机 IV 更有用。(3)两种代码使用不同的密钥(Java 代码使用 24 字节密钥,因此是 AES-192,在 NodeJS 代码中使用 32 字节密钥,因此是 AES-256)。 - Topaco
3个回答

5

CBC模式下AES的初始化向量长度为16字节,在JAVA代码中取前16个字节作为IV,但是在node.js的代码中使用所有24个字节的IV,因此会出现错误。请尝试使用以下代码:

var crypto = require('crypto');

function getAlgorithm(keyBase64) {

    var key = Buffer.from(keyBase64, 'base64');
    switch (key.length) {
        case 16:
            return 'aes-128-cbc';
        case 32:
            return 'aes-256-cbc';

    }

    throw new Error('Invalid key length: ' + key.length);
}


function encrypt(plainText, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const cipher  = crypto.createCipheriv(getAlgorithm(keyBase64), key, iv.slice(0, 16));
    let encrypted = cipher.update(plainText, 'utf8', 'base64');
    encrypted += cipher.final('base64');
    return encrypted;
}

function decrypt(messagebase64, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv  = Buffer.from(ivBase64, 'base64');

    const decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv.slice(0, 16));
    let decrypted  = decipher.update(messagebase64, 'base64');
    decrypted += decipher.final();
    return decrypted;
}


var keyBase64 = "DWIzFkO22qfVMgx2fIsxOXnwz10pRuZfFJBvf4RS3eY=";
var ivBase64  = 'e3a74e3c7599f3ab4601d587bd2cc768';
var plainText = '<Request xmlns="http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd"><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>';

var cipherText          = encrypt(plainText, keyBase64, ivBase64);
var decryptedCipherText = decrypt(cipherText, keyBase64, ivBase64);

console.log('Algorithm: ' + getAlgorithm(keyBase64));
console.log('Plaintext: ' + plainText);
console.log('Ciphertext: ' + cipherText);
console.log('Decoded Ciphertext: ' + decryptedCipherText);


3

在Java中,密钥被转换为16字节,因此应用了“AES/CBC/PKCS5Padding”算法,而在Node.js中,密钥被转换为24字节,因此我们需要应用“aes-192-cbc”算法。

var crypto = require('crypto');

const encrypt = (plainText, keyBase64) =>{

    const textBuffer = Buffer.from(plainText);
    const key = Buffer.from(keyBase64, 'base64');
    var iv = Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]);
    var encoded = Buffer.concat([iv,textBuffer]);
    const cipher = crypto.createCipheriv('aes-192-cbc', key, iv);
    let encrypted = cipher.update(encoded, 'binary', 'base64')
    encrypted += cipher.final('base64');
    return encrypted;
};

const decrypt =  (messagebase64, keyBase64) =>{

    const key = Buffer.from(keyBase64, 'base64');
    const encoded = Buffer.from(messagebase64, 'base64');
    var iv = encoded.slice(0, 16);
    var decoded = encoded.slice(16,encoded.length);
    const decipher = crypto.createDecipheriv('aes-192-cbc', key, iv);
    let decrypted = decipher.update(decoded, 'binary','utf-8');
    decrypted += decipher.final();
    return decrypted;
}



 String str = "<Request xmlns=\"http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd\" ><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>";

String key = "e3a74e3c7599f3ab4601d587bd2cc768";

console.log("input:   "+str);
const hash = encrypt(str,key);
console.log("encrypted:::   " + hash);
const text = decrypt(hash,key);
console.log("output:   "+text); 

当最终数据是二进制内容时,使用utf-8编码吗? - e-info128

0

这是上述问题的解决方案。每当我使用上述代码进行加密时,都会出现解密错误。我在代码中进行了小更新,在加密时在明文开头添加了16个字符。

var crypto = require('crypto');
function getAlgorithm(keyBase64) {

    var key = Buffer.from(keyBase64, 'base64');

    switch (key.length) {
        case 16:
            return 'aes-128-cbc';
        case 32:
            return 'aes-256-cbc';

    }
    throw new Error('Invalid key length: ' + key.length);
}


function encrypt(plainText, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv = Buffer.from(ivBase64, 'base64');

    const cipher = crypto.createCipheriv(getAlgorithm(keyBase64), key, iv);
    let encrypted = cipher.update(iv.subarray(0, 16) + plainText, 'utf8', 'base64')
    encrypted += cipher.final('base64');
    return encrypted;
};

function decrypt(messagebase64, keyBase64, ivBase64) {

    const key = Buffer.from(keyBase64, 'base64');
    const iv = Buffer.from(ivBase64, 'base64');
    const decipher = crypto.createDecipheriv(getAlgorithm(keyBase64), key, iv);
    let decrypted = decipher.update(messagebase64, 'base64');
    decrypted += decipher.final();
    return decrypted;
}

var keyBase64 = "DWIzFkO22qfVMgx2fIsxOXnwz10pRuZfFJBvf4RS3eY=";
var ivBase64 = Buffer.from([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]).toString('base64');;
var plainText = '<Request xmlns="http://www.kotak.com/schemas/CorpCCPaymentsOTP/CorpCCPaymentsOTP.xsd"><username>ENKASH</username><password>Corp@123</password><SrcAppCd>ENKA SH</SrcAppCd><RefNo>Ref1111111111</RefNo><CardNo>4280933990002698</CardNo ><OTP>12345</OTP></Request>';



var cipherText = encrypt(plainText, keyBase64, ivBase64);
var decryptedCipherText = decrypt(cipherText, keyBase64, ivBase64);

console.log('Algorithm: ' + getAlgorithm(keyBase64));
console.log('Plaintext: ' + plainText);
console.log('Ciphertext: ' + cipherText);
console.log('Decoded Ciphertext: ' + decryptedCipherText);


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