0

I am trying to encrypt in NodeJS.

But I am not able to achieve the same encrypted text in Node.

NodeJS code:

const Crypto = require('crypto');
const config = {
  host: 'http://******',
  appkey: 'app',
  secret: "3b47a59e834bf11564f899586665a310", // length is 32
  iv: "2192432927840278" // length is 16
}

function enCryptoAes (data, key, iv = '') {
    let clearEncoding = 'utf8';
    let cipherEncoding = 'base64';
    let cipherChunks = [];
    let algorithm = 'aes-128-cbc'
    switch(key.length) {
        case 16:
            algorithm = 'aes-128-cbc'
            break;
        case 32:
            algorithm = 'aes-256-cbc'
            break;
    }
    let cipher = Crypto.createCipheriv(algorithm, key, iv);

    cipher.setAutoPadding(true);
    cipherChunks.push(cipher.update(data, clearEncoding, cipherEncoding));
    cipherChunks.push(cipher.final(cipherEncoding));

    return cipherChunks.join('');
}

function getParamsAesStr(params) {
  let paramsArr = Object.keys(params).map(key => `${key}=${params[key]}`)
  paramsArr = paramsArr.sort()

  let signStr = Buffer.from(paramsArr.join('').toLowerCase(), 'utf-8').toString()
  let sign = Crypto.createHmac('md5', config.secret).update(signStr).digest('hex')
 
  let paramsStr = `${paramsArr.join('&')}&sign=${sign}`
  
  let paramsAesStr = enCryptoAes(paramsStr, config.secret.slice(0, 16), config.iv)
  paramsAesStr = Buffer.from(paramsStr, 'utf-8').toString('base64')
  return paramsAesStr
}

Java code:

public class ApiParameterParser {
    public static final String PARAMS_KEY = "param";

  public static final String SIGN_KEY = "sign";

  public static final String APPKEY_KEY = "appkey";

    private static final String ENCODING = "UTF-8";

  private static String secretKey = "3b47a59e834bf11564f899586665a310";

    public static Map<String, Object> encodeParameters(Map params) {
        if (params == null || params.size() == 0 || Strings.isNullOrEmpty(secretKey)) {
            return null;
        }
        try {
            String appkey = getParamString(params, APPKEY_KEY);
            params.remove(APPKEY_KEY);
            String sign = sign(params, secretKey);
            params.put(SIGN_KEY, sign);
            byte[] aesBytes = AESUtil.encryptByAES(
                    paramsToString(params, ENCODING).getBytes(ENCODING),
                    HMacMD5Util.hexStringToBytes(secretKey));
            // base64
            Base64 base64 = new Base64();
            String base64Str = base64.encodeToString(aesBytes);
            return base64Str;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
public static byte[] decryptByAES(byte[] data, byte[] secretKey) {
        try {
            byte[] result = null;
            SecretKeySpec skey = new SecretKeySpec(processAesKey(secretKey), ENCRY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(CIPHER_MODE);
            cipher.init(Cipher.DECRYPT_MODE, skey, new IvParameterSpec(ivParameter.getBytes()));
            result = cipher.doFinal(data);
            return result;
        } catch (Exception e) {
            logger.error("AES error", e);
        }
        return null;
    }
  public static String paramsToString(Map<String, Object> params, String encoding) throws UnsupportedEncodingException {
        if (params == null || params.size() == 0) {
            return "";
        }
        Set<String> keys = params.keySet();
        StringBuilder sb = new StringBuilder();
        for (String k : keys) {
            sb.append(k + "=" + URLEncoder.encode(getParamString(params, k), encoding) + "&");
        }
        return sb.toString().substring(0, sb.length() - 1);
    }

    public static String getParamString(Map params, String key) {
        if (params == null || params.size() == 0) {
            return "";
        }
        String buf = "";
        if (params.get(key) instanceof String[]) {
            buf = ((String[]) params.get(key))[0];
        } else {
            buf = String.valueOf(params.get(key));
        }
        return buf;
    }
}
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESUtil {
    private static String ivParameter = "2192432927840278";

    private static final String ENCRY_ALGORITHM = "AES";

    private static final String CIPHER_MODE = "AES/CBC/PKCS5Padding";

    public static byte[] encryptByAES(byte[] data, byte[] secretKey) {
        try {
            byte[] result = null;
            SecretKeySpec skey = new SecretKeySpec(processAesKey(secretKey), ENCRY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(CIPHER_MODE);
            cipher.init(Cipher.ENCRYPT_MODE, skey, new IvParameterSpec(ivParameter.getBytes()));
            result = cipher.doFinal(data);
            return result;
        } catch (Exception e) {
            logger.error("AES error", e);
        }
        return null;
    }

  private static byte[] processAesKey(byte[] key) {
        if (key == null) {
            return null;
        }
        int length = 16;
        if (key.length == length) {
            return key;
        }
        byte[] result = new byte[length];
        if (key.length > length) {
            System.arraycopy(key, 0, result, 0, length);
            return result;
        } else {
            int i = 0;
            for (byte item : key) {
                result[i++] = item;
            }
            return result;
        }
    }
}

public class HMacMD5Util {
   public static byte[] hexStringToBytes(String data) {
        if (data == null || data.equals("")) {
            return null;
        }
        data = data.toUpperCase();
        int length = data.length() / 2;
        char[] hexChars = data.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
        }
        return d;
    }
}

When the value of params is query params:

{
pageNo: 1, pageSize: 10
}

The encrypted text general by my Node code is:

"cGFnZU5vPTEmcGFnZVNpemU9MTAmc2lnbj1kMzBhZGI0MGUxM2RlYTQzYzEzMDhkOTAzZTJkOWFjMw=="
getParamsAesStr({pageNo: 1, pageSize: 10})

I am getting a different encrypted text. I am not able to achieve the same result in NodeJs as they did in java.

And the encrypted text by Node can't decrypt by the java code ```decryptByAES`` right

What's wrong with my NodeJs code.

L.Min
  • 75
  • 5
  • You Do not provide a full running NodeJs example so I assume that the paramsAes differ from Java (check the binary representation) – Michael Fehr Jul 15 '21 at 12:09
  • Your Java code treats 3b47a59e834bf11564f899586665a310 as the **hex** representation (2 chars per byte) of a 16-byte key i.e. bytes 3b 47 a5 ... while your nodejs uses the first 16 _characters_ as the key i.e. bytes 33 62 34 ... which are wildly different and encrypt differently. But your nodejs code then _discards_ the (bad) ciphertext and returns base64 of the **plaintext**. Nobody could ever decrypt this because it isn't encrypted. – dave_thompson_085 Jul 15 '21 at 16:33
  • @dave_thompson_085 Can you tell me how to handle the key string for encryption in Node to feat the key bytes encryption in Java? – L.Min Jul 16 '21 at 02:46
  • (1) nodejs convert hex string to bytes (for use as key): https://stackoverflow.com/q/52261494/ https://stackoverflow.com/q/18880301/ (2) delete the line `paramsAesStr = Buffer.from(paramsStr, 'utf-8').toString('base64')` and use the value from the preceding line `paramsAesStr = enCryptoAes ...` (0) nit: 'feat' is not a verb in English, and not appropriate here anyway; you could say the nodejs code should 'match' or 'duplicate' the Java code. – dave_thompson_085 Jul 16 '21 at 11:10
  • Thanks to your help,I've get the right encrypted text which match the Java code,thank you! – L.Min Jul 16 '21 at 12:32

0 Answers0