Skip to content

用 Java 复刻 NodeJS 中已废弃的加密解密方法

NodeJS crypto.createCiphercrypto.createDecipher 这两个创建加密解密实例的方法在 v10.0.0 之后已经废弃。

如果有以上 Node 加密解密代码需要用 Java 复刻,参考以下 stackoverflow 上的问题:

node.js - How to port encrypt (AES192) method from nodejs to java correctly? - Stack Overflow

具体代码如下:

package com.qcc.udf;

import org.apache.commons.lang3.ArrayUtils;
import org.springframework.security.crypto.codec.Hex;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;

public class DescriptionAESUDF {
    public static void main(String[] args){
        // 加密
        System.out.println("Result : " + encrypt("xxx","xxx"));
        // 解密
        System.out.println("Result : " + decrypt("xxxxxxxx","xxx"));
    }

    public static byte[][] EVP_BytesToKey(int key_len, int iv_len, MessageDigest md, byte[] salt, byte[] data, int count) {
        byte[][] both = new byte[2][];
        byte[] key = new byte[key_len];
        int key_ix = 0;
        byte[]  iv = new byte[iv_len];
        int iv_ix = 0;
        both[0] = key;
        both[1] = iv;
        byte[] md_buf = null;
        int nkey = key_len;
        int niv = iv_len;
        int i = 0;
        if(data == null) {
            return both;
        }
        int addmd = 0;
        for(;;) {
            md.reset();
            if(addmd++ > 0) {
                md.update(md_buf);
            }
            md.update(data);
            if(null != salt) {
                md.update(salt,0,8);
            }
            md_buf = md.digest();
            for(i=1;i<count;i++) {
                md.reset();
                md.update(md_buf);
                md_buf = md.digest();
            }
            i=0;
            if(nkey > 0) {
                for(;;) {
                    if(nkey == 0) break;
                    if(i == md_buf.length) break;
                    key[key_ix++] = md_buf[i];
                    nkey--;
                    i++;
                }
            }
            if(niv > 0 && i != md_buf.length) {
                for(;;) {
                    if(niv == 0) break;
                    if(i == md_buf.length) break;
                    iv[iv_ix++] = md_buf[i];
                    niv--;
                    i++;
                }
            }
            if(nkey == 0 && niv == 0) {
                break;
            }
        }
        for(i=0;i<md_buf.length;i++) {
            md_buf[i] = 0;
        }
        return both;
    }

    // 加密
    public static String encrypt(String data, String password) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            int keySizeBits = 192 / Byte.SIZE; //AES with 192 bits key = 16 bytes
            int ivSize = cipher.getBlockSize();
            final byte[][] keyAndIV = EVP_BytesToKey(keySizeBits, ivSize, md5, null, password.getBytes(StandardCharsets.US_ASCII), 1);
            SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
            IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);

            cipher.init(Cipher.ENCRYPT_MODE, key, iv);

            byte[] cipherText = cipher.update(data.getBytes());
            cipherText = ArrayUtils.addAll(cipherText, cipher.doFinal());

            return new String(Hex.encode(cipherText));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    // 解密
    public static String decrypt(String data, String password) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            int keySizeBits = 192 / Byte.SIZE; //AES with 192 bits key = 16 bytes
            int ivSize = cipher.getBlockSize();
            final byte[][] keyAndIV = EVP_BytesToKey(keySizeBits, ivSize, md5, null, password.getBytes(StandardCharsets.US_ASCII), 1);
            SecretKeySpec key = new SecretKeySpec(keyAndIV[0], "AES");
            IvParameterSpec iv = new IvParameterSpec(keyAndIV[1]);

            cipher.init(Cipher.DECRYPT_MODE, key, iv);

            byte[] cipherText = cipher.update(toByte(data));
            cipherText = ArrayUtils.addAll(cipherText, cipher.doFinal());

            return new String(cipherText);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static byte[] toByte(String hexString) {
        int len = hexString.length()/2;
        byte[] result = new byte[len];
        for (int i = 0; i < len; i++)
            result[i] = Integer.valueOf(hexString.substring(2*i, 2*i+2), 16).byteValue();
        return result;
    }
}