当前位置:   article > 正文

国密SM2算法进行数据的加密、签名和验签、解密_sm2加解密

sm2加解密

一、算法介绍

SM2算法是中国密码学研究所(中国国家加密管理局)发布的一种非对称加密算法,适用于数字签名、密钥交换和公钥加密等场景。该算法基于椭圆曲线密码学,使用椭圆曲线上的点进行运算。

SM2算法的安全性主要基于椭圆曲线离散对数问题的难解性。它采用的是一个特定的椭圆曲线参数集,该参数集已经在国际上得到了广泛的认可。SM2算法的安全性与RSA算法和Diffie-Hellman算法相当。

SM2算法的主要特点如下:

  1. 安全性高:SM2算法基于椭圆曲线离散对数问题,具有较高的安全性。
  2. 算法效率高:SM2算法的计算量相对较小,适合在资源受限的环境中使用。
  3. 适用性广泛:SM2算法可用于数字签名、密钥交换和公钥加密等多种密码应用场景。
  4. 算法标准化:SM2算法已被国际电信联盟(ITU-T)和国际标准化组织(ISO)认可为国际标准。

总的来说,SM2算法是一种安全性高、效率高且广泛应用的非对称加密算法,被广泛应用于各种密码场景中。

二、引入pom依赖

  1. <dependency>
  2. <groupId>org.bouncycastle</groupId>
  3. <artifactId>bcprov-jdk15on</artifactId>
  4. <version>1.70</version>
  5. </dependency>
  6. <!--注意检查是否已经引入lombok依赖,已经引入则不需要此依赖-->
  7. <dependency>
  8. <groupId>org.projectlombok</groupId>
  9. <artifactId>lombok</artifactId>
  10. <scope>provided</scope>
  11. </dependency>

主要是引入了工具包和lombok依赖

 三、密钥对工具类

  1. package com.hl.sm2demo.util;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  4. import java.security.*;
  5. import java.security.spec.ECGenParameterSpec;
  6. import java.security.spec.PKCS8EncodedKeySpec;
  7. import java.security.spec.X509EncodedKeySpec;
  8. import java.util.Base64;
  9. import java.util.Map;
  10. /**
  11. * @ Description 国密公私钥对工具类
  12. */
  13. @Slf4j
  14. public class KeyUtils {
  15. public static final String PUBLIC_KEY = "publicKey";
  16. public static final String PRIVATE_KEY = "privateKey";
  17. /**
  18. * 生成国密公私钥对
  19. */
  20. public static Map<String, String> generateSmKey() throws Exception {
  21. KeyPairGenerator keyPairGenerator = null;
  22. SecureRandom secureRandom = new SecureRandom();
  23. ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
  24. keyPairGenerator = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
  25. keyPairGenerator.initialize(sm2Spec);
  26. keyPairGenerator.initialize(sm2Spec, secureRandom);
  27. KeyPair keyPair = keyPairGenerator.generateKeyPair();
  28. PrivateKey privateKey = keyPair.getPrivate();
  29. PublicKey publicKey = keyPair.getPublic();
  30. String publicKeyStr = new String(Base64.getEncoder().encode(publicKey.getEncoded()));
  31. String privateKeyStr = new String(Base64.getEncoder().encode(privateKey.getEncoded()));
  32. return Map.of(PUBLIC_KEY, publicKeyStr, PRIVATE_KEY, privateKeyStr);
  33. }
  34. /**
  35. * 将Base64转码的公钥串,转化为公钥对象
  36. */
  37. public static PublicKey createPublicKey(String publicKey) {
  38. PublicKey publickey = null;
  39. try {
  40. X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));
  41. KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());
  42. publickey = keyFactory.generatePublic(publicKeySpec);
  43. } catch (Exception e) {
  44. log.error("将Base64转码的公钥串,转化为公钥对象异常:{}", e.getMessage(), e);
  45. }
  46. return publickey;
  47. }
  48. /**
  49. * 将Base64转码的私钥串,转化为私钥对象
  50. */
  51. public static PrivateKey createPrivateKey(String privateKey) {
  52. PrivateKey publickey = null;
  53. try {
  54. PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));
  55. KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());
  56. publickey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);
  57. } catch (Exception e) {
  58. log.error("将Base64转码的私钥串,转化为私钥对象异常:{}", e.getMessage(), e);
  59. }
  60. return publickey;
  61. }
  62. }

1.生成国密公私钥对
方法generateSmKey()使用KeyPairGenerator生成一个基于椭圆曲线"sm2p256v1"的KeyPair,这是SM2算法所使用的曲线。
公钥和私钥分别编码为Base64字符串,然后以键值对的形式存储在Map中,键分别为PUBLIC_KEY和PRIVATE_KEY。

2.公钥对象的转换
方法createPublicKey(String publicKey)接收一个Base64编码的公钥字符串,通过X509EncodedKeySpec解析成公钥对象。这里使用了KeyFactory实例化EC类型的公钥。

3.私钥对象的转换
方法createPrivateKey(String privateKey)类似地,接收一个Base64编码的私钥字符串,通过PKCS8EncodedKeySpec解析成私钥对象。同样使用KeyFactory实例化EC类型的私钥。

四、 SM2工具类

  1. package com.hl.sm2demo.util;
  2. import lombok.extern.slf4j.Slf4j;
  3. import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
  4. import org.bouncycastle.crypto.InvalidCipherTextException;
  5. import org.bouncycastle.crypto.engines.SM2Engine;
  6. import org.bouncycastle.crypto.params.ECDomainParameters;
  7. import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
  8. import org.bouncycastle.crypto.params.ECPublicKeyParameters;
  9. import org.bouncycastle.crypto.params.ParametersWithRandom;
  10. import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
  11. import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
  12. import org.bouncycastle.jce.provider.BouncyCastleProvider;
  13. import org.bouncycastle.jce.spec.ECParameterSpec;
  14. import java.security.*;
  15. /**
  16. * @ Description SM2实现工具类
  17. */
  18. @Slf4j
  19. public class Sm2Util {
  20. /* 这行代码是在Java中用于向安全系统添加Bouncy Castle安全提供器的。
  21. Bouncy Castle是一个流行的开源加密库,它提供了许多密码学算法和安全协议的实现。
  22. 通过调用Security.addProvider并传入BouncyCastleProvider对象,你可以注册Bouncy Castle提供的安全服务和算法到Java的安全框架中。
  23. 这样一来,你就可以在你的应用程序中使用Bouncy Castle所提供的加密算法、密钥生成和管理等功能。*/
  24. static {
  25. Security.addProvider(new BouncyCastleProvider());
  26. }
  27. /**
  28. * 根据publicKey对原始数据data,使用SM2加密
  29. */
  30. public static byte[] encrypt(byte[] data, PublicKey publicKey) {
  31. ECPublicKeyParameters localECPublicKeyParameters = getEcPublicKeyParameters(publicKey);
  32. SM2Engine localSM2Engine = new SM2Engine();
  33. localSM2Engine.init(true, new ParametersWithRandom(localECPublicKeyParameters, new SecureRandom()));
  34. byte[] arrayOfByte2;
  35. try {
  36. arrayOfByte2 = localSM2Engine.processBlock(data, 0, data.length);
  37. return arrayOfByte2;
  38. } catch (InvalidCipherTextException e) {
  39. log.error("SM2加密失败:{}", e.getMessage(), e);
  40. return null;
  41. }
  42. }
  43. private static ECPublicKeyParameters getEcPublicKeyParameters(PublicKey publicKey) {
  44. ECPublicKeyParameters localECPublicKeyParameters = null;
  45. if (publicKey instanceof BCECPublicKey localECPublicKey) {
  46. ECParameterSpec localECParameterSpec = localECPublicKey.getParameters();
  47. ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),
  48. localECParameterSpec.getG(), localECParameterSpec.getN());
  49. localECPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), localECDomainParameters);
  50. }
  51. return localECPublicKeyParameters;
  52. }
  53. /**
  54. * 根据privateKey对加密数据encode data,使用SM2解密
  55. */
  56. public static byte[] decrypt(byte[] encodeData, PrivateKey privateKey) {
  57. SM2Engine localSM2Engine = new SM2Engine();
  58. BCECPrivateKey sm2PriK = (BCECPrivateKey) privateKey;
  59. ECParameterSpec localECParameterSpec = sm2PriK.getParameters();
  60. ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),
  61. localECParameterSpec.getG(), localECParameterSpec.getN());
  62. ECPrivateKeyParameters localECPrivateKeyParameters = new ECPrivateKeyParameters(sm2PriK.getD(),
  63. localECDomainParameters);
  64. localSM2Engine.init(false, localECPrivateKeyParameters);
  65. try {
  66. return localSM2Engine.processBlock(encodeData, 0, encodeData.length);
  67. } catch (InvalidCipherTextException e) {
  68. log.error("SM2解密失败:{}", e.getMessage(), e);
  69. return null;
  70. }
  71. }
  72. /**
  73. * 私钥签名
  74. */
  75. public static byte[] signByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {
  76. Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);
  77. sig.initSign(privateKey);
  78. sig.update(data);
  79. return sig.sign();
  80. }
  81. /**
  82. * 公钥验签
  83. */
  84. public static boolean verifyByPublicKey(byte[] data, PublicKey publicKey, byte[] signature) throws Exception {
  85. Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);
  86. sig.initVerify(publicKey);
  87. sig.update(data);
  88. return sig.verify(signature);
  89. }
  90. }

1.加密:
encrypt方法使用SM2算法对原始数据进行加密。它首先获取ECPublicKeyParameters对象,然后初始化SM2Engine并进行加密处理。如果加密过程中出现InvalidCipherTextException,则记录错误日志并返回null。

2.解密:
decrypt方法根据私钥解密加密数据。同样,初始化SM2Engine,进行解密处理。如果解密过程中出现InvalidCipherTextException,则记录错误日志并返回null。

3.签名:
signByPrivateKey方法使用私钥对数据进行签名。首先,创建一个Signature实例,指定SM2签名算法,初始化签名器,更新数据,然后生成签名。

4.验证:
verifyByPublicKey方法使用公钥验证签名。创建Signature实例,指定SM2签名算法,初始化验证器,更新数据,然后执行验证。返回验证结果,即布尔值表示签名是否有效

五、案例测试Junit 

  1. package com.hl.sm2demo;
  2. import com.hl.sm2demo.util.KeyUtils;
  3. import com.hl.sm2demo.util.Sm2Util;
  4. import org.junit.jupiter.api.Test;
  5. import org.springframework.boot.test.context.SpringBootTest;
  6. import java.security.PrivateKey;
  7. import java.security.PublicKey;
  8. import java.util.Base64;
  9. import java.util.Map;
  10. @SpringBootTest
  11. class Sm2DemoApplicationTests {
  12. PublicKey publicKey = null;
  13. PrivateKey privateKey = null;
  14. @Test
  15. public void test() throws Exception {
  16. //生成公私钥对
  17. Map<String,String> keys = KeyUtils.generateSmKey();
  18. String testStr = "hello JAVA";
  19. System.out.println("原始字符串:" + testStr);
  20. System.out.println("公钥:" + keys.get(KeyUtils.PUBLIC_KEY));
  21. publicKey = KeyUtils.createPublicKey(keys.get(KeyUtils.PUBLIC_KEY));
  22. System.out.println("私钥:" + keys.get(KeyUtils.PRIVATE_KEY));
  23. privateKey = KeyUtils.createPrivateKey(keys.get(KeyUtils.PRIVATE_KEY));
  24. System.out.println();
  25. //公钥加密
  26. byte[] encrypt = Sm2Util.encrypt(testStr.getBytes(), publicKey);
  27. //加密转base64
  28. String encryptBase64Str = Base64.getEncoder().encodeToString(encrypt);
  29. System.out.println("加密数据:" + encryptBase64Str);
  30. //私钥签名,方便对方收到数据后用公钥验签
  31. byte[] sign = Sm2Util.signByPrivateKey(testStr.getBytes(), privateKey);
  32. System.out.println("数据签名:" + Base64.getEncoder().encodeToString(sign));
  33. //公钥验签,验证通过后再进行数据解密
  34. boolean b = Sm2Util.verifyByPublicKey(testStr.getBytes(), publicKey, sign);
  35. System.out.println("数据验签:" + b);
  36. //私钥解密
  37. byte[] decode = Base64.getDecoder().decode(encryptBase64Str);
  38. byte[] decrypt = Sm2Util.decrypt(decode, privateKey);
  39. assert decrypt != null;
  40. System.out.println("解密数据:" + new String(decrypt));
  41. }
  42. }

测试代码主要逻辑:

密钥准备:首先生成一个公私钥对字符串,再使用工具分别转换为公私钥的java对象;

加密过程:把数据使用公钥加密,把密文转换成base64编码格式,再用私钥签名;

解密过程:拿到加密数据后用公钥验签,以确保密文数据没有被第三方拦截篡改,验证通过后,base64解码,最后使用私钥进行解密,还原数据

测试结果如下图:

六、总结

SM2是一款优秀的国产加密算法,目前已经应用到多种领域内,保障了国家信息安全 ,我们日常开发中也可以尽量多使用SM系列的加密算法,如SM3、SM4等,支持国产从点点滴滴做起!

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/不正经/article/detail/672401
推荐阅读
相关标签
  

闽ICP备14008679号