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


    添加依赖配置

    1. <dependency>
    2. <groupId>commons-codecgroupId>
    3. <artifactId>commons-codecartifactId>
    4. <version>1.15version>
    5. dependency>
    6. <dependency>
    7. <groupId>org.bouncycastlegroupId>
    8. <artifactId>bcprov-jdk15onartifactId>
    9. <version>1.68version>
    10. dependency>

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

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

    1. import com.alibaba.fastjson2.JSON;
    2. import com.risk.decision.params.ConfirmParams;
    3. import org.apache.commons.codec.binary.Base64;
    4. import org.bouncycastle.jce.provider.BouncyCastleProvider;
    5. import javax.crypto.*;
    6. import javax.crypto.spec.IvParameterSpec;
    7. import javax.crypto.spec.SecretKeySpec;
    8. import java.nio.charset.StandardCharsets;
    9. import java.security.*;
    10. /**
    11. * @Author 国密4工具类
    12. * @Date 2023/10/19 14:21
    13. * @Description 数据加密解密
    14. **/
    15. public class Sm4CBCUtils {
    16. static {
    17. Security.addProvider(new BouncyCastleProvider());
    18. }
    19. public static final String SM4 = "SM4";
    20. public static final String SM4_CBC_PADDING = "SM4/CBC/PKCS5Padding";
    21. /**
    22. * 参数加密
    23. * @param secretKey 秘钥值
    24. * @param decryptContext 加密的信息
    25. * @return String 加密后的值
    26. * @throws Exception 异常信息
    27. */
    28. public static String encryptCBCPadding(String secretKey, String decryptContext) throws Exception {
    29. Cipher cipher = encryptGenerateCbcCipher(Cipher.ENCRYPT_MODE, secretKey.getBytes());
    30. byte[] bytes = cipher.doFinal(decryptContext.getBytes());
    31. byte[] ciphertext = Base64.encodeBase64(bytes);
    32. return new String(ciphertext, StandardCharsets.UTF_8);
    33. }
    34. public static Cipher encryptGenerateCbcCipher(int mode, byte[] key) throws Exception {
    35. Key sm4Key = new SecretKeySpec(key,SM4);
    36. Cipher cipher = Cipher.getInstance(SM4_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
    37. byte[] iv = new byte[16];
    38. IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
    39. cipher.init(mode, sm4Key, ivParameterSpec);
    40. return cipher;
    41. }
    42. /**
    43. * 接口解密
    44. * @param secretKey 秘钥值
    45. * @param decryptContext 加密的参数
    46. * @return String 解密的信息
    47. * @throws Exception 异常信息
    48. */
    49. public static String decryptCBCPadding(String secretKey, String decryptContext) throws Exception {
    50. byte[] decodedBytes = Base64.decodeBase64(decryptContext.getBytes(StandardCharsets.UTF_8));
    51. Cipher cipher = decryptGenerateCbcCipher(Cipher.DECRYPT_MODE, secretKey.getBytes());
    52. byte[] bytes = cipher.doFinal(decodedBytes);
    53. return new String(bytes, StandardCharsets.UTF_8);
    54. }
    55. public static Cipher decryptGenerateCbcCipher(int mode, byte[] key) throws Exception {
    56. Key sm4Key = new SecretKeySpec(key,SM4);
    57. Cipher cipher = Cipher.getInstance(SM4_CBC_PADDING, BouncyCastleProvider.PROVIDER_NAME);
    58. // 16字节的偏移向量
    59. byte[] iv = new byte[16];
    60. IvParameterSpec ivParameterSpec = new IvParameterSpec(iv);
    61. cipher.init(mode, sm4Key, ivParameterSpec);
    62. return cipher;
    63. }
    64. }

    注: 关于iv值的说明:

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

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

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

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

    测试方法:

    1. public static void main(String[] args) throws Exception {
    2. ConfirmParams confirmParams = new ConfirmParams();
    3. confirmParams.setPhoneCode("12388889999");
    4. confirmParams.setIdCode("999888202309096666");
    5. String strParams = JSON.toJSONString(confirmParams);
    6. String secretKey = "qwertyuiop123456";
    7. // 数据加密
    8. String encodedText = encryptCBCPadding(secretKey, strParams);
    9. System.out.println(String.format("加密后的值: %s",encodedText));
    10. // 数据解密
    11. String decryptContext = decryptCBCPadding(secretKey, encodedText);
    12. System.out.println(String.format("解密后的值: %s",decryptContext));
    13. }

    结果打印:

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

    接口压测:

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

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

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

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

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

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

  • 相关阅读:
    15个小技巧,助你源码阅读事半功倍
    【JavaScript-动画原理】如何使用js进行动画效果的实现
    2022 谷歌出海创业加速器展示日: 见证入营企业成长收获
    springBoot 配置拦截器
    CSS设置盒子阴影
    (CVE-2022-21661)WordPress SQL 注入漏洞分析和复现
    visual studio 2017安装完成后,编译c++项目失败,提示E696,无法打开ctype.h errno.h float.h stdio.h
    使用Premiere、PhotoShop和Audition做视频特效
    elasticsearch无法访问9200端口
    leecode#Excel表列序号#组合两个表
  • 原文地址:https://blog.csdn.net/amosjob/article/details/134385442