Java聚合对外网关,使用国密SM4采用CBC分组填充模式实现数据加密工具类,Jmeter压测

添加依赖配置

		<!-- 仓库地址: https://mvnrepository.com/artifact/commons-codec/commons-codec -->
		<!--	org.apache.commons.codec.binary.Base64	-->
		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
			<version>1.15</version>
		</dependency>

		<!-- 国密 sm4 依赖jar包 -->
		<dependency>
			<groupId>org.bouncycastle</groupId>
			<artifactId>bcprov-jdk15on</artifactId>
			<version>1.68</version>
		</dependency>

国密SM4并采用CBC分组填充模式,密钥长度为16字节。nonce为16字节,加密使用的nonce和加签一致。
1、密钥为提供提供,nonce为请求中随机生成的字符串
2、使用nonce作为iv
3、使用key、iv,对敏感数据进行加密,得到JSON形式的资源对象
4、对JSON形式的资源对象进行 Base64 编码

工具类实现,如下代码对iv赋值做了简写,可结合自身需要修改:

import com.alibaba.fastjson2.JSON;
import com.risk.decision.params.ConfirmParams;
import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

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

/**
 * @Author 国密4工具类
 * @Date 2023/10/19 14:21
 * @Description 数据加密解密
 **/
public class Sm4CBCUtils {

    static {
        Security.addProvider(new BouncyCastleProvider());
    }

    public static final String SM4 = "SM4";
    public static final String SM4_CBC_PADDING = "SM4/CBC/PKCS5Padding";

    /**
     * 参数加密
     * @param secretKey 秘钥值
     * @param decryptContext 加密的信息
     * @return String 加密后的值
     * @throws Exception 异常信息
     */
    public static String encryptCBCPadding(String secretKey, String decryptContext) throws Exception {
        Cipher cipher = encryptGenerateCbcCipher(Cipher.ENCRYPT_MODE, secretKey.getBytes());
        byte[] bytes = cipher.doFinal(decryptContext.getBytes());
        byte[] ciphertext = Base64.encodeBase64(bytes);
        return new String(ciphertext, StandardCharsets.UTF_8);
    }

    public static Cipher encryptGenerateCbcCipher(int mode, byte[] key) throws Exception {
        Key sm4Key = new SecretKeySpec(key,SM4);
        Cipher cipher = Cipher.getInstance(SM4_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
        byte[] iv = new byte[16];
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(mode, sm4Key, ivParameterSpec);
        return cipher;
    }


    /**
     * 接口解密
     * @param secretKey 秘钥值
     * @param decryptContext 加密的参数
     * @return String 解密的信息
     * @throws Exception 异常信息
     */
    public static String decryptCBCPadding(String secretKey, String decryptContext) throws Exception {
        byte[] decodedBytes = Base64.decodeBase64(decryptContext.getBytes(StandardCharsets.UTF_8));
        Cipher cipher = decryptGenerateCbcCipher(Cipher.DECRYPT_MODE, secretKey.getBytes());
        byte[] bytes = cipher.doFinal(decodedBytes);
        return new String(bytes, StandardCharsets.UTF_8);
    }

    public static Cipher decryptGenerateCbcCipher(int mode, byte[] key) throws Exception {
        Key sm4Key = new SecretKeySpec(key,SM4);
        Cipher cipher = Cipher.getInstance(SM4_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
        // 16字节的偏移向量
        byte[] iv = new byte[16];
        IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
        cipher.init(mode, sm4Key, ivParameterSpec);
        return cipher;
    }


}

注: 关于iv值的说明:

在SM4算法中,CBC模式是一种常用的分组密码模式。vi(Initialization Vector)是CBC模式中的初始向量,它的作用是为了增加密码的安全性和随机性

vi是一个固定长度的随机数,它与明文进行异或运算后再进行加密。vi的作用在于使得相同的明文在不同的加密过程中产生不同的密文,增加了密码的随机性。这样做的好处是即使明文相同,密文也会有所不同,从而增加了密码的安全性。

vi是一个必须满足一定要求的随机数,不能随意写死。如果vi被写死为固定的值,那么相同的明文每次加密后都会生成相同的密文,这样就破坏了CBC模式的安全性,攻击者可以通过观察密文的重复模式来推断出明文的信息。

因此,vi需要在每次加密过程中都是随机且不可预测的。一种常见的做法是每次加密时生成一个随机的vi,并将其与密文一起存储或传输,在解密时使用相同的vi进行解密操作。这样可以确保每次加密的结果都是不同的,从而提高密码的安全性。

测试方法:

    public static void main(String[] args) throws Exception {
        ConfirmParams confirmParams = new ConfirmParams();
        confirmParams.setPhoneCode("12388889999");
        confirmParams.setIdCode("999888202309096666");
        String strParams = JSON.toJSONString(confirmParams);
        String secretKey = "qwertyuiop123456";

        // 数据加密
        String encodedText = encryptCBCPadding(secretKey, strParams);
        System.out.println(String.format("加密后的值: %s",encodedText));

        // 数据解密
        String decryptContext = decryptCBCPadding(secretKey, encodedText);
        System.out.println(String.format("解密后的值: %s",decryptContext));

    }

结果打印:

加密后的值: B7Ug2lFn95m8J2JwNdNPt4VKqWV5NF8HCy5Fx0WfEchBGn3uhcDU1Ko6NBwinCieujp5ko2dCkzzIoYcEkm65g==
解密后的值: {"idCode":"999888202309096666","phoneCode":"12388889999"}

接口压测:

服务信息:2台Linux服务器(HA),负载均衡

Jmeter压测: Jmeter安装在Windows环境,受可使用端口的影响,设置50线程,压测40秒

添加断言,方法中处理解密异常,响应码为501,如果断言响应码包含501,则代表响应成功。不包含则表示响应失败。

压测结果,异常率100%,表示接口中并没有返回501的响应码,即解密方法是稳定的,如图:

国密4加密工具ECB模式查看上一篇文章:

SM4国密4在jdk1.7版本和jdk1.8版本中的工具类使用

国密算法SM4是一种对称加密算法,可以用于对数据进行加密和解密。CBC(Cipher Block Chaining)模式SM4算法的一种工作模式,它将明文分组与前一个密文分组进行异或运算后再进行加密。下面是SM4 CBC模式的加解密过程: 加密过程: 1. 初始化一个随机的IV(Initialization Vector)作为首个密文分组。 2. 将明文按照分组长度进行划分,每个分组的大小与SM4算法的分组长度相同(通常为128位)。 3. 对每个分组进行如下操作: a. 将当前明文分组与上一个密文分组进行异或运算。 b. 使用SM4算法对异或结果进行加密,得到当前密文分组。 4. 将所有密文分组连接起来,得到最终的密文。 解密过程: 1. 初始化一个随机的IV作为首个密文分组。 2. 将密文按照分组长度进行划分,每个分组的大小与SM4算法的分组长度相同(通常为128位)。 3. 对每个分组进行如下操作: a. 使用SM4算法对当前密文分组进行解密。 b. 将解密结果与上一个密文分组进行异或运算,得到当前明文分组。 4. 将所有明文分组连接起来,得到最终的明文。 需要注意的是,在CBC模式中,每个分组加密都依赖于前一个分组的密文,因此在加密和解密过程中需要保持IV的一致性。同时,在使用CBC模式时,需要对明文进行填充(padding)以适应分组长度。常用的填充方式包括PKCS7和ZeroPadding。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值