使用node.js中的crypto进行aes-128-cbc加密/解密不会产生与Java代码相似的结果

时间:2019-07-20 09:37:29

标签: javascript node.js encryption aes

我正在使用第三方API,在该API中,我必须以aes-128加密形式发送数据。为了帮助您进行加密/解密,他们给了我Java代码。我试图在Node.js中复制它,但是我无法像普通的Java代码一样应用加密,因此遇到了错误。

java中的工作代码是-

public class AesCryptUtil {
    Cipher ecipher;
    /**
     * Input a string that will be md5 hashed to create the key.
     * 
     * @return void, cipher initialized
     */
    public AesCryptUtil() {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128);
            this.setupCrypto(kgen.generateKey());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public AesCryptUtil(String key) {
        SecretKeySpec skey = new SecretKeySpec(getMD5(key), "AES");
        this.setupCrypto(skey);
    }

    private void setupCrypto(SecretKey key) {
        // Create an 8-byte initialization vector
        byte[] iv = new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f };

        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        try {
            ecipher = Cipher.getInstance("AES/CBC/PKCS5Padding");

            // CBC requires an initialization vector
            ecipher.init(Cipher.ENCRYPT_MODE, key, paramSpec);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    // Buffer used to transport the bytes from one stream to another
    byte[] buf = new byte[1024];

    public void encrypt(InputStream in, OutputStream out) {
        try {
            // Bytes written to out will be encrypted
            out = new CipherOutputStream(out, ecipher);

            // Read in the cleartext bytes and write to out to encrypt
            int numRead = 0;
            while ((numRead = in.read(buf)) >= 0) {
                out.write(buf, 0, numRead);
            }
            out.close();
        } catch (java.io.IOException e) {
            e.printStackTrace();
        }
    }
/**
     * Input is a string to encrypt.
     * 
     * @return a Hex string of the byte array
     */
    public String encrypt(String plaintext) {
        try {
            byte[] ciphertext = ecipher.doFinal(plaintext.getBytes("UTF-8"));
            return this.byteToHex(ciphertext);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }

    }


    /**
     * Input encrypted String represented in HEX
     **/

    private static byte[] getMD5(String input) {
        try {
            byte[] bytesOfMessage = input.getBytes("UTF-8");
            MessageDigest md = MessageDigest.getInstance("MD5");
            return md.digest(bytesOfMessage);
        } catch (Exception e) {
            return null;
        }
    }

    static final String HEXES = "0123456789ABCDEF";

    public static String byteToHex(byte[] raw) {
        if (raw == null) {
            return null;
        }
        String result = "";
        for (int i = 0; i < raw.length; i++) {
            result += Integer.toString((raw[i] & 0xff) + 0x100, 16).substring(1);
        return result;
    }

    public static byte[] hexToByte(String hexString) {
        int len = hexString.length();
        byte[] ba = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            ba[i / 2] = (byte) ((Character.digit(hexString.charAt(i), 16) << 4)
                    + Character.digit(hexString.charAt(i + 1), 16));
        }
        return ba;
    }

    public static void main(String args[]) {
        String result = null;
        String err = null;
        String key = "DEEA29E294D8B2241FB41EF254AAB86F";
        String data = "<?xml version=" + "1.0" + " encoding=" + "UTF-8" + "?><xmltagopen></xmltagclose>";
        String action = "enc";

        if (key == null)
            err = "error: no key";
        else if (key.length() < 32)
            err = "error: key length less than 32 bytes";
        else if (data == null || action == null)
            err = "error: no data";
        else if (action == null)
            err = "error: no action";
        else if (!action.equals("enc") && !action.equals("dec"))
            err = "error: invalid action";

        if (err == null) {
            try {
                AesCryptUtil encrypter = new AesCryptUtil(key);

                if (action.equals("enc"))
                    result = encrypter.encrypt(data);
                else
                    result = encrypter.decrypt(data);
            } catch (Exception e) {
                err = "error : Exception in performing the requested operation : " + e;
            }
        }
        if (result != null)
            System.out.println(result);
        else
            System.out.println(err);
    }

}

我试图在node.js中做类似的事情,但是它不起作用,并且我从api中收到错误消息,提示加密不正确-

const xml = '<?xml version="1.0" encoding="UTF-8"?><xmltagopen</xmltagclose>';
    let key = 'DEEA29E294D8B2241FB41EF254AAB86F';
    let encodeKey = crypto.createHash('md5').update(key, 'utf8').digest("hex");
    console.log(encodeKey);
    let ivBuffer = new Buffer.from([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f]);
    let iv = ivBuffer.toString('hex');

    let cipher = crypto.createCipher('aes-128-cbc', encodeKey, iv);
    let encText = cipher.update(xml, 'utf8', 'hex');
    encText += cipher.final('hex');
    console.log(encText);

1 个答案:

答案 0 :(得分:0)

已弃用的crypto.createCipher函数(如API文档中所述),从提供的密码中导出加密密钥:

  

crypto.createCipher()的实现使用OpenSSL函数EVP_BytesToKey导出密钥,摘要算法设置为 MD5,一次迭代,并且没有盐。缺少盐会使字典受到攻击,因为相同的密码始终会创建相同的密钥。低迭代次数和非加密安全的哈希算法可以非常快速地测试密码。

改为使用crypto.createCipheriv

例如这样的例子:

var crypto  = require( 'crypto' );
var cipher  = crypto.createCipheriv( 'aes-192-cbc', encryptionKey, Buffer.from(iv) );
var buf1 = cipher.update( Buffer.from(data) );
var buf2 = cipher.final();
var encrypted = Buffer.concat([buf1, buf2]);