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

2024-02-08 17:20

本文主要是介绍国密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解码,最后使用私钥进行解密,还原数据

测试结果如下图:

这篇关于国密SM2算法进行数据的加密、签名和验签、解密的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/691697

相关文章

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

使用Jackson进行JSON生成与解析的新手指南

《使用Jackson进行JSON生成与解析的新手指南》这篇文章主要为大家详细介绍了如何使用Jackson进行JSON生成与解析处理,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 核心依赖2. 基础用法2.1 对象转 jsON(序列化)2.2 JSON 转对象(反序列化)3.

C#使用SQLite进行大数据量高效处理的代码示例

《C#使用SQLite进行大数据量高效处理的代码示例》在软件开发中,高效处理大数据量是一个常见且具有挑战性的任务,SQLite因其零配置、嵌入式、跨平台的特性,成为许多开发者的首选数据库,本文将深入探... 目录前言准备工作数据实体核心技术批量插入:从乌龟到猎豹的蜕变分页查询:加载百万数据异步处理:拒绝界面

Python使用自带的base64库进行base64编码和解码

《Python使用自带的base64库进行base64编码和解码》在Python中,处理数据的编码和解码是数据传输和存储中非常普遍的需求,其中,Base64是一种常用的编码方案,本文我将详细介绍如何使... 目录引言使用python的base64库进行编码和解码编码函数解码函数Base64编码的应用场景注意

Java进行文件格式校验的方案详解

《Java进行文件格式校验的方案详解》这篇文章主要为大家详细介绍了Java中进行文件格式校验的相关方案,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、背景异常现象原因排查用户的无心之过二、解决方案Magandroidic Number判断主流检测库对比Tika的使用区分zip

Java使用Curator进行ZooKeeper操作的详细教程

《Java使用Curator进行ZooKeeper操作的详细教程》ApacheCurator是一个基于ZooKeeper的Java客户端库,它极大地简化了使用ZooKeeper的开发工作,在分布式系统... 目录1、简述2、核心功能2.1 CuratorFramework2.2 Recipes3、示例实践3

Java利用JSONPath操作JSON数据的技术指南

《Java利用JSONPath操作JSON数据的技术指南》JSONPath是一种强大的工具,用于查询和操作JSON数据,类似于SQL的语法,它为处理复杂的JSON数据结构提供了简单且高效... 目录1、简述2、什么是 jsONPath?3、Java 示例3.1 基本查询3.2 过滤查询3.3 递归搜索3.4

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

MySQL大表数据的分区与分库分表的实现

《MySQL大表数据的分区与分库分表的实现》数据库的分区和分库分表是两种常用的技术方案,本文主要介绍了MySQL大表数据的分区与分库分表的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有... 目录1. mysql大表数据的分区1.1 什么是分区?1.2 分区的类型1.3 分区的优点1.4 分

Mysql删除几亿条数据表中的部分数据的方法实现

《Mysql删除几亿条数据表中的部分数据的方法实现》在MySQL中删除一个大表中的数据时,需要特别注意操作的性能和对系统的影响,本文主要介绍了Mysql删除几亿条数据表中的部分数据的方法实现,具有一定... 目录1、需求2、方案1. 使用 DELETE 语句分批删除2. 使用 INPLACE ALTER T