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

一、背景介绍

数据的加解密有很多种方式,几种常用的加密算法如下:

DES(Data Encryption Standard):对称算法,数据加密标准,速度较快,适用于加密大量数据的场合;

3DES(Triple DES):是基于DES的对称算法,对一块数据用三个不同的密钥进行三次加密,强度更高;

RC2和RC4:对称算法,用变长密钥对大量数据进行加密,比 DES 快;

IDEA(International Data Encryption Algorithm)国际数据加密算法,使用 128 位密钥提供非常强的安全性;

RSA:由 RSA 公司发明,是一个支持变长密钥的公共密钥算法,需要加密的文件块的长度也是可变的,非对称算法

本文着重介绍SM2国密算法的使用,这是一款中国国家密码局发布的一款加密算法,本文介绍使用语言环境为java语言 

二、引入pom依赖

<dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15on</artifactId><version>1.70</version>
</dependency>
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><scope>provided</scope>
</dependency>

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

 三、国密公私钥对工具类KeyUtils

package com.hl.sm2demo.util;import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.jce.provider.BouncyCastleProvider;import java.security.*;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.Map;/*** @ Description 国密公私钥对工具类*/
@Slf4j
public class KeyUtils {public static final String PUBLIC_KEY = "publicKey";public static final String PRIVATE_KEY = "privateKey";/*** 生成国密公私钥对*/public static Map<String, String> generateSmKey() throws Exception {KeyPairGenerator keyPairGenerator = null;SecureRandom secureRandom = new SecureRandom();ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");keyPairGenerator = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());keyPairGenerator.initialize(sm2Spec);keyPairGenerator.initialize(sm2Spec, secureRandom);KeyPair keyPair = keyPairGenerator.generateKeyPair();PrivateKey privateKey = keyPair.getPrivate();PublicKey publicKey = keyPair.getPublic();String publicKeyStr = new String(Base64.getEncoder().encode(publicKey.getEncoded()));String privateKeyStr = new String(Base64.getEncoder().encode(privateKey.getEncoded()));return Map.of(PUBLIC_KEY, publicKeyStr, PRIVATE_KEY, privateKeyStr);}/*** 将Base64转码的公钥串,转化为公钥对象*/public static PublicKey createPublicKey(String publicKey) {PublicKey publickey = null;try {X509EncodedKeySpec publicKeySpec = new X509EncodedKeySpec(Base64.getDecoder().decode(publicKey));KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());publickey = keyFactory.generatePublic(publicKeySpec);} catch (Exception e) {log.error("将Base64转码的公钥串,转化为公钥对象异常:{}", e.getMessage(), e);}return publickey;}/*** 将Base64转码的私钥串,转化为私钥对象*/public static PrivateKey createPrivateKey(String privateKey) {PrivateKey publickey = null;try {PKCS8EncodedKeySpec pkcs8EncodedKeySpec = new PKCS8EncodedKeySpec(Base64.getDecoder().decode(privateKey));KeyFactory keyFactory = KeyFactory.getInstance("EC", new BouncyCastleProvider());publickey = keyFactory.generatePrivate(pkcs8EncodedKeySpec);} catch (Exception e) {log.error("将Base64转码的私钥串,转化为私钥对象异常:{}", e.getMessage(), e);}return publickey;}}

生成公私钥字符串map对象,公钥串转公钥对象方法和私钥串转私钥对象方法 

四、 SM2工具类

package com.hl.sm2demo.util;import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.gm.GMObjectIdentifiers;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.engines.SM2Engine;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.crypto.params.ParametersWithRandom;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPrivateKey;
import org.bouncycastle.jcajce.provider.asymmetric.ec.BCECPublicKey;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;import java.security.*;/*** @ Description SM2实现工具类*/
@Slf4j
public class Sm2Util {
/*    这行代码是在Java中用于向安全系统添加Bouncy Castle安全提供器的。Bouncy Castle是一个流行的开源加密库,它提供了许多密码学算法和安全协议的实现。通过调用Security.addProvider并传入BouncyCastleProvider对象,你可以注册Bouncy Castle提供的安全服务和算法到Java的安全框架中。这样一来,你就可以在你的应用程序中使用Bouncy Castle所提供的加密算法、密钥生成和管理等功能。*/static {Security.addProvider(new BouncyCastleProvider());}/*** 根据publicKey对原始数据data,使用SM2加密*/public static byte[] encrypt(byte[] data, PublicKey publicKey) {ECPublicKeyParameters localECPublicKeyParameters = getEcPublicKeyParameters(publicKey);SM2Engine localSM2Engine = new SM2Engine();localSM2Engine.init(true, new ParametersWithRandom(localECPublicKeyParameters, new SecureRandom()));byte[] arrayOfByte2;try {arrayOfByte2 = localSM2Engine.processBlock(data, 0, data.length);return arrayOfByte2;} catch (InvalidCipherTextException e) {log.error("SM2加密失败:{}", e.getMessage(), e);return null;}}private static ECPublicKeyParameters getEcPublicKeyParameters(PublicKey publicKey) {ECPublicKeyParameters localECPublicKeyParameters = null;if (publicKey instanceof BCECPublicKey localECPublicKey) {ECParameterSpec localECParameterSpec = localECPublicKey.getParameters();ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),localECParameterSpec.getG(), localECParameterSpec.getN());localECPublicKeyParameters = new ECPublicKeyParameters(localECPublicKey.getQ(), localECDomainParameters);}return localECPublicKeyParameters;}/*** 根据privateKey对加密数据encode data,使用SM2解密*/public static byte[] decrypt(byte[] encodeData, PrivateKey privateKey) {SM2Engine localSM2Engine = new SM2Engine();BCECPrivateKey sm2PriK = (BCECPrivateKey) privateKey;ECParameterSpec localECParameterSpec = sm2PriK.getParameters();ECDomainParameters localECDomainParameters = new ECDomainParameters(localECParameterSpec.getCurve(),localECParameterSpec.getG(), localECParameterSpec.getN());ECPrivateKeyParameters localECPrivateKeyParameters = new ECPrivateKeyParameters(sm2PriK.getD(),localECDomainParameters);localSM2Engine.init(false, localECPrivateKeyParameters);try {return localSM2Engine.processBlock(encodeData, 0, encodeData.length);} catch (InvalidCipherTextException e) {log.error("SM2解密失败:{}", e.getMessage(), e);return null;}}/*** 私钥签名*/public static byte[] signByPrivateKey(byte[] data, PrivateKey privateKey) throws Exception {Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);sig.initSign(privateKey);sig.update(data);return sig.sign();}/*** 公钥验签*/public static boolean verifyByPublicKey(byte[] data, PublicKey publicKey, byte[] signature) throws Exception {Signature sig = Signature.getInstance(GMObjectIdentifiers.sm2sign_with_sm3.toString(), BouncyCastleProvider.PROVIDER_NAME);sig.initVerify(publicKey);sig.update(data);return sig.verify(signature);}}

五、案例测试Junit 

package com.hl.sm2demo;import com.hl.sm2demo.util.KeyUtils;
import com.hl.sm2demo.util.Sm2Util;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Map;@SpringBootTest
class Sm2DemoApplicationTests {PublicKey publicKey = null;PrivateKey privateKey = null;@Testpublic void test() throws Exception {//生成公私钥对Map<String,String> keys = KeyUtils.generateSmKey();String testStr = "hello JAVA";System.out.println("原始字符串:" + testStr);System.out.println("公钥:" + keys.get(KeyUtils.PUBLIC_KEY));publicKey = KeyUtils.createPublicKey(keys.get(KeyUtils.PUBLIC_KEY));System.out.println("私钥:" + keys.get(KeyUtils.PRIVATE_KEY));privateKey = KeyUtils.createPrivateKey(keys.get(KeyUtils.PRIVATE_KEY));System.out.println();//公钥加密byte[] encrypt = Sm2Util.encrypt(testStr.getBytes(), publicKey);//加密转base64String encryptBase64Str = Base64.getEncoder().encodeToString(encrypt);System.out.println("加密数据:" + encryptBase64Str);//私钥签名,方便对方收到数据后用公钥验签byte[] sign = Sm2Util.signByPrivateKey(testStr.getBytes(), privateKey);System.out.println("数据签名:" + Base64.getEncoder().encodeToString(sign));//公钥验签,验证通过后再进行数据解密boolean b = Sm2Util.verifyByPublicKey(testStr.getBytes(), publicKey, sign);System.out.println("数据验签:" + b);//私钥解密byte[] decode = Base64.getDecoder().decode(encryptBase64Str);byte[] decrypt = Sm2Util.decrypt(decode, privateKey);assert decrypt != null;System.out.println("解密数据:" + new String(decrypt));}
}

测试代码主要逻辑:

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

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

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

测试结果如下图:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/458112.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Leetcode—42. 接雨水【困难】

2024每日刷题&#xff08;112&#xff09; Leetcode—42. 接雨水 空间复杂度为O(n)的算法思想 实现代码 class Solution { public:int trap(vector<int>& height) {int ans 0;int n height.size();vector<int> l(n);vector<int> r(n);for(int i 0; …

微软.NET6开发的C#特性——接口和属性

我是荔园微风&#xff0c;作为一名在IT界整整25年的老兵&#xff0c;看到不少初学者在学习编程语言的过程中如此的痛苦&#xff0c;我决定做点什么&#xff0c;下面我就重点讲讲微软.NET6开发人员需要知道的C#特性。 C#经历了多年发展&#xff0c; 进行了多次重大创新&#xf…

C# CAD交互界面-自定义窗体(三)

运行环境 vs2022 c# cad2016 调试成功 一、引用 二、开发代码进行详细的说明 初始化与获取AutoCAD核心对象&#xff1a; Database db HostApplicationServices.WorkingDatabase;&#xff1a;这行代码获取当前工作中的AutoCAD数据库对象。在AutoCAD中&#xff0c;所有图形数…

3D室内虚拟灭火体验为预防火灾提供全新方案

室内火灾常见于充电器未拔、电动车、油锅起火及煤气泄露等原因&#xff0c;由于室内空间小、易燃物多&#xff0c;因此极易造成较大的人员财产损失&#xff0c;3D仿真还原技术、通过1&#xff1a;1模拟还原火灾发生全过程&#xff0c;火灾VR安全培训提供全方位、真实感强的模拟…

机器人学、机器视觉与控制 上机笔记(2.1章节)

机器人学、机器视觉与控制 上机笔记&#xff08;2.1章节&#xff09; 1、前言2、本篇内容3、代码记录3.1、新建se23.2、生成坐标系3.3、将T1表示的变换绘制3.4、完整绘制代码3.5、获取点*在坐标系1下的表示3.6、相对坐标获取完整代码 4、结语 1、前言 工作需要&#xff0c;想同…

03 动力云客项目之登录功能后端实现

创建项目 使用Spring initializr初始化项目 老师讲的是3.2.0, 但小版本之间问题应该不大.

光学PCIe 6.0技术引领AI时代超大规模集群

随着云计算、大数据和人工智能技术的快速发展&#xff0c;超大规模数据中心正经历一场前所未有的变革。传统的集中式架构逐渐转变为解聚式&#xff08;disaggregated&#xff09;架构&#xff0c;这种架构将计算、存储和网络资源从单一的物理服务器中分离出来&#xff0c;形成独…

【知识整理】技术团队,新人培养

团队管理简介 团队梯度建设&#xff0c;团队管理 4 件事&#xff1a; 选&#xff1a;招合适的人才进来用&#xff1a;把合适的人才&#xff0c;用在合适的位置育&#xff1a;对人才的引导和培养梯度留&#xff1a;当人才有要走的想法时&#xff0c;如何留住人才 上面的 4 个要…

交易之路:从无知到有知的五个阶段

交易是易学的&#xff0c;它的操作很直观&#xff0c;也是复杂的&#xff0c;它的价格很玄妙。在金融行业日益壮大的背景下&#xff0c;新人辈出&#xff0c;而弱者则逐渐退出。市场生态在不断变化&#xff0c;我们每个人在交易之路上所经历的种种&#xff0c;既清晰可见又模糊…

第二证券:股市的国家队是谁?股市国家队包括哪些机构?

在a股商场上&#xff0c;投资者大致能够分为散户、游资、主力、组织、国家队这几大类&#xff0c;那么&#xff0c;股市的国家队是谁&#xff1f;股市国家队包含哪些组织&#xff1f; 国家队主要是指以下五大类&#xff1a; 1、中心汇金 中心汇金的全称为中心汇金投资有限责…

公众号迁移是否会迁移留言功能?

为什么公众号没有留言功能&#xff1f;从2018年2月开始&#xff0c;新注册的微信公众号取消了留言功能&#xff0c;原因是为了规避一些营销号通过虚假留言骗取读者信任。不过大部分公众号运营者对TX此举感到失望&#xff0c;一方面大片的留言就像店前排队的顾客&#xff0c;能体…

Rust通用代码生成器莲花发布红莲尝鲜版二十一,前端代码生成物有巨大改进

Rust通用代码生成器莲花发布红莲尝鲜版二十一&#xff0c;前端代码生成物有巨大改进 Rust通用代码生成器莲花已发布红莲尝鲜版二十一&#xff0c;此版本采用了新的前端代码生成引擎&#xff1a;时空之门前端代码生成器6.2.0。此引擎支持Nodejs 21,Nodejs 18和Nodejs 14。消除了…