Js and Java use CryptoJS.pad.Pkcs7, AES/CBC/PKCS7Padding to realize data encryption and decryption (crypto-js, bouncycastle, hutool-java)

        Recently, the development has encountered a need for a docking interface, and this interface is directly called by the front-end Ajax. There are encryption and decryption processes before and after the interface. CryptoJS is used, and the encryption mode is: padding: CryptoJS.pad.Pkcs7

The front-end encryption method is

Pre-request Script:

var reqeust_data = 
{ 
    "data": {
                   "你的参数1": "***",
                   "你的参数2": "***", 
                   "你的参数3": "***" 
            },
    "req_time":Date.parse(new Date()), // 请求时间戳
    "request_string":randomString(32), // 32位随机字符串
}
var aes_key = pm.request.getHeaders().AES_KEY;
console.log("传入参数 -> " + JSON.stringify(reqeust_data));
var en_reqdata = AES_CBC_encrypt(JSON.stringify(reqeust_data), aes_key);
pm.environment.set("params",en_reqdata);   // {
   
   {params}} 为接口传入body的参数

//Generate 32-bit random string
function randomString(len) {
    len = len || 32;
    var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';   
    var maxPos = $chars.length;
    var random_str = '';
    for (i = 0; i < len; i++) {
      pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
    }
    return random_str;
 }

//aes encode
function AES_CBC_encrypt(message, key) {
    let keyHex = CryptoJS.enc.Hex.parse(key); //
    let ivHex = CryptoJS.enc.Utf8.parse("0000000000000000");
    let messageHex = CryptoJS.enc.Utf8.parse(message);
    let encrypted = CryptoJS.AES.encrypt(messageHex, keyHex, {
        iv: ivHex,
        mode: CryptoJS.mode.CBC,
        padding: CryptoJS.pad.Pkcs7
    });
    return encrypted.toString();
}

//aes decode
function decrypt(word, keyStr){
  //console.log('decrypt key:' + keyStr);
  let keyHex = CryptoJS.enc.Hex.parse(keyStr); //
  let ivHex = CryptoJS.enc.Utf8.parse("0000000000000000");
  let base64 = CryptoJS.enc.Base64.parse(word);
  let src =  CryptoJS.enc.Base64.stringify(base64);
  var decrypt = CryptoJS.AES.decrypt(src, keyHex, 
  {
       iv:  ivHex,
       mode:CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
  });
  return decrypt.toString(CryptoJS.enc.Utf8);
}

Decrypted Js script after request

Tests:

var aes_key = pm.request.getHeaders().AES_KEY;
var result = decrypt(responseBody, aes_key);
console.log("返回信息明文 -> " + result);

//aes decode
function decrypt(word, keyStr){
  //console.log('decrypt key:' + keyStr);
  let keyHex = CryptoJS.enc.Hex.parse(keyStr); //
  let ivHex = CryptoJS.enc.Utf8.parse("0000000000000000");
  let base64 = CryptoJS.enc.Base64.parse(word);
  let src =  CryptoJS.enc.Base64.stringify(base64);
  var decrypt = CryptoJS.AES.decrypt(src, keyHex, 
  {
       iv:  ivHex,
       mode:CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7
  });
  return decrypt.toString(CryptoJS.enc.Utf8);
}

        So now if we need to use Java to implement this encryption and then request the interface, we can first implement a tool class with Hutool so that we can call it. But for CryptoJS in Js, padding: CryptoJS.pad.Pkcs7, the default mode corresponding to AES in Java is: AES/ECB/PKCS7Padding. And it happens that Hutool's enumeration class does not have this mode:

 In Hutool's issue, someone just raised this question, and the original author also responded

 Hutool alone does not have this encryption algorithm, so it needs to cooperate with the BC library to use it

First, you need to import the BC library in Maven:

<dependency> 
    <groupId>org.bouncycastle</groupId> 
    <artifactId>bcprov-jdk15on</artifactId> 
    <version>1.68</version> 
</dependency> 

Packages in the Java Standard Library java.securityprovide a standard mechanism that allows seamless integration by third-party providers. If we want to use the AES/ECB/PKCS7Padding algorithm provided by BouncyCastle, we need to register BouncyCastle first (some need it, some don’t, just run it):

Among them, the registration of BouncyCastle is realized through the following statement:

Security.addProvider(new BouncyCastleProvider());

 Then start writing our encryption tool class

package com.psds.credit.data.utils;

import cn.hutool.core.util.HexUtil;
import cn.hutool.crypto.Mode;
import cn.hutool.crypto.symmetric.AES;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Value;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

/**
 * @author JMzz
 * @description
 */
@Slf4j
public class AESUtil {

    private static String AES_KEY = "你的密钥";

    private static final String IV_KEY = "0000000000000000";


    public static String encrypt(String message, String key, Mode mode, String padding) {
        byte[] baseKey = null;
        if (StringUtils.isNotEmpty(key)) {
            baseKey = HexUtil.decodeHex(key);
        } else {
            baseKey = HexUtil.decodeHex(AES_KEY);
        }
        byte[] ivBytes = IV_KEY.getBytes(StandardCharsets.UTF_8);
        byte[] messageBytes = message.getBytes(StandardCharsets.UTF_8);
        AES aes = new AES(mode.name(), padding, baseKey, ivBytes);
        return new Base64().encodeAsString(aes.encrypt(messageBytes));
    }

    public static String decrypt(String message, String key, Mode mode, String padding) {
        byte[] baseKey = null;
        if (StringUtils.isNotEmpty(key)) {
            baseKey = HexUtil.decodeHex(key);
        } else {
            baseKey = HexUtil.decodeHex(AES_KEY);
        }
        byte[] ivBytes = IV_KEY.getBytes(StandardCharsets.UTF_8);
        AES aes = new AES(mode.name(), padding, baseKey, ivBytes);
        return aes.decryptStr(message);
    }


    // 不用HexUtil.decodeHex,也可以这个方法
    private static byte[] hexStringToByteArray(String s) {
        int len = s.length();
        byte[] data = new byte[len / 2];
        for (int i = 0; i < len; i += 2) {
            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
                    + Character.digit(s.charAt(i+1), 16));
        }
        return data;
    }


    public static String randomString(int len) {
        len = len > 0 ? len : 32;
        String chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
        int maxPos = chars.length();
        StringBuilder pwd = new StringBuilder();
        for (int i = 0; i < len; i++) {
            pwd.append(chars.charAt((int) (Math.random() * maxPos)));
        }
        return pwd.toString();
    }


}

To decrypt, just use the obtained response.body() to decrypt.

For the difference between AES/CBC/PKCS7Padding and AES/CBC/PKCS5Padding:

        PKCS5Padding is a subset of PKCS7Padding. It can be understood that PKCS5Padding is a specific block of PKCS7Padding. JDK's PKCS5Padding implementation is implemented in accordance with the PKCS7Padding standard, so there is no difference between using PKCS5Padding and PKCS7Padding when building Cipher in Java.

Guess you like

Origin blog.csdn.net/weixin_45740811/article/details/129322049