Java SM2 国密算法(最权威)!

国密SM2算法简介

国密SM2算法是一种椭圆曲线公钥密码算法,其安全性基于椭圆曲线离散对数难题。该算法由国家密码管理局设计并公开,用于国家关键信息系统的数据加密、解密和数字签名等操作,是我国自主创新的一种密码算法。
在这里插入图片描述

一、SM2算法概述

SM2算法是一种基于椭圆曲线密码的公钥密码算法,其安全性主要基于椭圆曲线离散对数难题。该算法由国家密码管理局设计并公开,是我国自主创新的一种密码算法,可应用于数据加密、解密、数字签名等操作。SM2算法包括密钥生成算法、加密算法、解密算法和数字签名算法等部分。

二、SM2算法的应用场景

SM2算法作为一种公钥密码算法,可广泛应用于各种场景,如:

  1. 数据加密:SM2算法可用于加密和解密数据,保障数据传输的安全性。
  2. 数字签名:SM2算法可用于生成数字签名,验证文档或数据的完整性和真实性。
  3. 身份认证:SM2算法可用于身份认证,验证用户的身份信息。
  4. 网络安全:SM2算法可用于网络安全,保护网络传输的数据,防止被黑客攻击。

三、SM2算法的优势

SM2算法作为一种自主创新的密码算法,具有以下优势:

  1. 安全性高:SM2算法基于椭圆曲线离散对数难题,安全性较高,能够有效地防止黑客攻击。
  2. 效率高:SM2算法具有较高的运算效率,能够满足大量数据加密、解密和数字签名的需求。
  3. 灵活性好:SM2算法支持多种密钥长度,可根据实际需求灵活选择密钥长度,适用于不同的应用场景。
  4. 自主创新:SM2算法是我国自主创新的密码算法,具有独立的知识产权,能够保障国家关键信息系统的信息安全。

## 四、SM2算法的未来发展

总之,SM2算法是一种安全、高效的公钥密码算法,具有广泛的应用前景和未来发展潜力。在我国自主创新的道路上,SM2算法将继续发挥重要作用,为保障国家关键信息系统的信息安全做出更大的贡献。

国密算法Java版本

pom.xml 文件加入引用

       <dependency><groupId>org.bouncycastle</groupId><artifactId>bcprov-jdk15to18</artifactId><version>1.69</version></dependency><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.10</version></dependency><!--以下包 可以适当引用--><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.9.3</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.26</version></dependency>

SM2扩展类

这个类主要的的作用是对SM2 的扩展。

  • 扩展SM2 加密排列方式C1C2C3 还是C1C3C2
  • 扩展SM2 加签和验签算法SM3Digest
package com.yunce.ycsm;/** @Auther:徐志强* @Date:2023/9/11* @Description:* @VERSON:1.0*/import org.bouncycastle.crypto.CipherParameters;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.params.*;
import org.bouncycastle.math.ec.ECConstants;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
import java.math.BigInteger;
import java.security.SecureRandom;/*** 对org.bouncycastle:bcprov-jdk15on:1.57扩展* <br/>BC库加密结果是按C1C2C3,国密标准是C1C3C2(加密芯片也是这个排列),* <br/>本扩展主要实现加密结果排列方式可选**/
public class SM2EngineExtend {private final Digest digest;/**是否为加密模式*/private boolean forEncryption;private ECKeyParameters ecKey;private ECDomainParameters ecParams;private int curveLength;private SecureRandom random;/**密文排序方式*/private int cipherMode;/**BC库默认排序方式-C1C2C3*/public static int CIPHERMODE_BC = 0;/**国密标准排序方式-C1C3C2*/public static int CIPHERMODE_NORM = 1;public SM2EngineExtend() {this(new SM3Digest());}public SM2EngineExtend(Digest digest) {this.digest = digest;}/*** 设置密文排序方式* @param cipherMode*/public void setCipherMode(int cipherMode){this.cipherMode = cipherMode;}/*** 默认初始化方法,使用国密排序标准* @param forEncryption - 是否以加密模式初始化* @param param - 曲线参数*/public void init(boolean forEncryption, CipherParameters param) {init(forEncryption, CIPHERMODE_NORM, param);}/*** 默认初始化方法,使用国密排序标准* @param forEncryption 是否以加密模式初始化* @param cipherMode 加密数据排列模式:1-标准排序;0-BC默认排序* @param param 曲线参数*/public void init(boolean forEncryption, int cipherMode, CipherParameters param) {this.forEncryption = forEncryption;this.cipherMode = cipherMode;if (forEncryption) {ParametersWithRandom rParam = (ParametersWithRandom) param;ecKey = (ECKeyParameters) rParam.getParameters();ecParams = ecKey.getParameters();ECPoint s = ((ECPublicKeyParameters) ecKey).getQ().multiply(ecParams.getH());if (s.isInfinity()) {throw new IllegalArgumentException("invalid key: [h]Q at infinity");}random = rParam.getRandom();} else {ecKey = (ECKeyParameters) param;ecParams = ecKey.getParameters();}curveLength = (ecParams.getCurve().getFieldSize() + 7) / 8;}/*** 加密或解密输入数据* @param in* @param inOff* @param inLen* @return* @throws InvalidCipherTextException*/public byte[] processBlock( byte[] in, int inOff, int inLen) throws InvalidCipherTextException {if (forEncryption) {// 加密return encrypt(in, inOff, inLen);} else {return decrypt(in, inOff, inLen);}}/*** 加密实现,根据cipherMode输出指定排列的结果,默认按标准方式排列* @param in* @param inOff* @param inLen* @return* @throws InvalidCipherTextException*/private byte[] encrypt(byte[] in, int inOff, int inLen)throws InvalidCipherTextException {byte[] c2 = new byte[inLen];System.arraycopy(in, inOff, c2, 0, c2.length);byte[] c1;ECPoint kPB;do {BigInteger k = nextK();ECPoint c1P = ecParams.getG().multiply(k).normalize();c1 = c1P.getEncoded(false);kPB = ((ECPublicKeyParameters) ecKey).getQ().multiply(k).normalize();kdf(digest, kPB, c2);}while (notEncrypted(c2, in, inOff));byte[] c3 = new byte[digest.getDigestSize()];addFieldElement(digest, kPB.getAffineXCoord());digest.update(in, inOff, inLen);addFieldElement(digest, kPB.getAffineYCoord());digest.doFinal(c3, 0);if (cipherMode == CIPHERMODE_NORM){return Arrays.concatenate(c1, c3, c2);}return Arrays.concatenate(c1, c2, c3);}/*** 解密实现,默认按标准排列方式解密,解密时解出c2部分原文并校验c3部分* @param in* @param inOff* @param inLen* @return* @throws InvalidCipherTextException*/private byte[] decrypt(byte[] in, int inOff, int inLen)throws InvalidCipherTextException {byte[] c1 = new byte[curveLength * 2 + 1];System.arraycopy(in, inOff, c1, 0, c1.length);ECPoint c1P = ecParams.getCurve().decodePoint(c1);ECPoint s = c1P.multiply(ecParams.getH());if (s.isInfinity()) {throw new InvalidCipherTextException("[h]C1 at infinity");}c1P = c1P.multiply(((ECPrivateKeyParameters) ecKey).getD()).normalize();byte[] c2 = new byte[inLen - c1.length - digest.getDigestSize()];if (cipherMode == CIPHERMODE_BC) {System.arraycopy(in, inOff + c1.length, c2, 0, c2.length);}else{// C1 C3 C2System.arraycopy(in, inOff + c1.length + digest.getDigestSize(), c2, 0, c2.length);}kdf(digest, c1P, c2);byte[] c3 = new byte[digest.getDigestSize()];addFieldElement(digest, c1P.getAffineXCoord());digest.update(c2, 0, c2.length);addFieldElement(digest, c1P.getAffineYCoord());digest.doFinal(c3, 0);int check = 0;// 检查密文输入值C3部分和由摘要生成的C3是否一致if (cipherMode == CIPHERMODE_BC) {for (int i = 0; i != c3.length; i++) {check |= c3[i] ^ in[c1.length + c2.length + i];}}else{for (int i = 0; i != c3.length; i++) {check |= c3[i] ^ in[c1.length + i];}}clearBlock(c1);clearBlock(c3);if (check != 0) {clearBlock(c2);throw new InvalidCipherTextException("invalid cipher text");}return c2;}private boolean notEncrypted(byte[] encData, byte[] in, int inOff) {for (int i = 0; i != encData.length; i++) {if (encData[i] != in[inOff]) {return false;}}return true;}private void kdf(Digest digest, ECPoint c1, byte[] encData) {int ct = 1;int v = digest.getDigestSize();byte[] buf = new byte[digest.getDigestSize()];int off = 0;for (int i = 1; i <= ((encData.length + v - 1) / v); i++) {addFieldElement(digest, c1.getAffineXCoord());addFieldElement(digest, c1.getAffineYCoord());digest.update((byte) (ct >> 24));digest.update((byte) (ct >> 16));digest.update((byte) (ct >> 8));digest.update((byte) ct);digest.doFinal(buf, 0);if (off + buf.length < encData.length) {xor(encData, buf, off, buf.length);} else {xor(encData, buf, off, encData.length - off);}off += buf.length;ct++;}}private void xor(byte[] data, byte[] kdfOut, int dOff, int dRemaining) {for (int i = 0; i != dRemaining; i++) {data[dOff + i] ^= kdfOut[i];}}private BigInteger nextK() {int qBitLength = ecParams.getN().bitLength();BigInteger k;do {k = new BigInteger(qBitLength, random);}while (k.equals(ECConstants.ZERO) || k.compareTo(ecParams.getN()) >= 0);return k;}private void addFieldElement(Digest digest, ECFieldElement v) {byte[] p = BigIntegers.asUnsignedByteArray(curveLength, v.toBigInteger());digest.update(p, 0, p.length);}/*** clear possible sensitive data*/private void clearBlock(byte[] block) {for (int i = 0; i != block.length; i++) {block[i] = 0;}}}

SM2 工具类

工具类实现以下功能

  • 加密(排列算法设定)
  • 解密
  • 加签
  • 验签
package yc.util.sm;
/** @Auther:徐志强* @Date:2023/9/11* @Description:* @VERSON:1.0*/import cn.hutool.crypto.asymmetric.SM2;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
import org.bouncycastle.crypto.InvalidCipherTextException;
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.math.ec.ECPoint;
import org.bouncycastle.util.encoders.Hex;import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.SecureRandom;public class SM2Utils {/**sm2曲线参数名称*/public static final String CRYPTO_NAME_SM2 = "sm2p256v1";/*** SM2加密算法* @param publicKey 公钥* @param data 待加密的数据* @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04*/public static String encrypt(String publicKey, String data) throws InvalidCipherTextException {// 按国密排序标准加密return encrypt(publicKey, data, SM2EngineExtend.CIPHERMODE_BC);}/*** SM2加密算法 (设置密文排列方式)* @param publicKey 公钥* @param data 待加密的数据* @param cipherMode 密文排列方式0-C1C2C3;1-C1C3C2;* @return 密文,BC库产生的密文带由04标识符,与非BC库对接时需要去掉开头的04*/public static String encrypt(String publicKey, String data, int cipherMode) throws InvalidCipherTextException {// 获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数NECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());//提取公钥点ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKey));// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);SM2EngineExtend sm2Engine = new SM2EngineExtend();// 设置sm2为加密模式sm2Engine.init(true, cipherMode, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));byte[] arrayOfBytes = null;try {byte[] in = data.getBytes();arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);} catch (Exception e) {throw  e;}return Hex.toHexString(arrayOfBytes);}/*** SM2解密算法* @param privateKey    私钥* @param cipherData    密文数据* @description  密文数据以04开头,传入的密文前面没有04则补上* @return*/public static String decrypt(String privateKey, String cipherData) throws InvalidCipherTextException {// // 按国密排序标准解密return decrypt(privateKey, cipherData, SM2EngineExtend.CIPHERMODE_BC);}/*** SM2解密算法* @param privateKey    私钥* @param cipherData    密文数据* @param cipherMode 密文排列方式0-C1C2C3;1-C1C3C2;* @return*/public static String decrypt(String privateKey, String cipherData, int cipherMode) throws InvalidCipherTextException {// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上if (!cipherData.startsWith("04")){cipherData = "04" + cipherData;}byte[] cipherDataByte = Hex.decode(cipherData);//获取一条SM2曲线参数X9ECParameters sm2ECParameters = GMNamedCurves.getByName(CRYPTO_NAME_SM2);//构造domain参数ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());BigInteger privateKeyD = new BigInteger(privateKey, 16);ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);SM2EngineExtend sm2Engine = new SM2EngineExtend();// 设置sm2为解密模式sm2Engine.init(false, cipherMode, privateKeyParameters);String result = "";try {byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);return new String(arrayOfBytes);} catch (Exception e) {throw  e;}}/*** SM2 加签* @param privateKey 承建商私钥* @param id 授权码* @param source 16进制加密原文* @return 16进制签名*/public static String sign(String privateKey,String id,  String source) throws UnsupportedEncodingException {SM2 sm2=  new SM2(privateKey,null);String id16=Hex.toHexString(id.getBytes("utf-8"));String source16=Hex.toHexString(source.getBytes("utf-8"));String sign= sm2.signHex(source16 ,id16);return sign;}/*** 校验签名* @param publicKey 民科提供公钥* @param data 16进制加密原文* @param signHex 16进制签名* @param id 授权码* @return*/public static  boolean verifysign(String publicKey,String data, String signHex, String id) throws UnsupportedEncodingException {SM2 sm = new SM2(null,publicKey);String id16=Hex.toHexString(id.getBytes("utf-8"));String source16=Hex.toHexString(data.getBytes("utf-8"));boolean isSign= sm.verifyHex(data,signHex,id);return isSign;}
}

加密示例(单元测试)

1.对“Hello Word”进行一系列的SM2操作。

加密-单元测试

package yc.util.sm;/** @Auther:徐志强* @Date:2023/12/18* @Description:* @VERSON:1.0*/
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;public class SM2UtilsTest {@Testpublic void testEncrypt() {try {String publicKey = "04979c0b359aba90a846e7724ab66621c79af8095a459e5ca981845952d865403f8644c5e79eb7d8ea0bcb9c2fb53f82e8e1108a85d96a3e1d4e687bb91a622ea4";String data     = "hello word";/*密文排列方式0-C1C2C3;1-C1C3C2;*/String result = SM2Utils.encrypt(publicKey, data, 1);System.out.println("加密后:");System.out.println(result);} catch (InvalidCipherTextException e) {// Handle exception if necessary}}
}
加密后:
044e6f524065ebd5a758eaa9a8278a4bf51a49bc043ddfcf7b7ed5fe4475fd36b66077618af044acff157b065ba02045baaa75ff23efa7d366228ed5006f3b8b52f72db1811064e8485337708044b2d30d13691513ba0a939c19d1e8dc3faa4089862cc33f01d4b3e6b818

解密-单元测试

 String privateKey = "00e5f6e0d04985ba1ced8c6fef49c5dd3dd84f6542d64cafb6ffde94a433392e3c";String cipherData = "04972df81d2685884f0dd3a20cf9dc743203bf66e00c98b9761b7a569b27c1231ce271329c6121704e8e59df9a080f982390b8901829f3e6fc1ac3a2d0b7bfac7c0634d8b95dfdbf316922762a874b3708dc2ce7e7dd5a49a667d15f878ef85437f68f10d8bc05d5b5aede";int cipherMode = 1;String result = SM2Utils.decrypt(privateKey, cipherData, cipherMode);System.out.println("解密后:");System.out.println(result);

加签-单元测试

   @Testpublic void testSign() throws UnsupportedEncodingException, UnsupportedEncodingException {String privateKey = "00e5f6e0d04985ba1ced8c6fef49c5dd3dd84f6542d64cafb6ffde94a433392e3c";String id = "admin";String source = "Hello Word";String actualSign = SM2Utils.sign(privateKey, id, source);System.out.println("加签:");System.out.println(actualSign );}

验签-单元测试

  @Testpublic void testVerifysign() throws UnsupportedEncodingException {String publicKey = "04979c0b359aba90a846e7724ab66621c79af8095a459e5ca981845952d865403f8644c5e79eb7d8ea0bcb9c2fb53f82e8e1108a85d96a3e1d4e687bb91a622ea4";String data = "Hello Word";String signHex = "3046022100d8edc9c5f73d7845cec3e29b7a3196b4b21c9a1f2b9edf93d33fe4cb4f8c45bd022100b6c382a4bb72e3290fea140787a09df4e6ad5174debe03b8026bcc7e43369c79";String id = "admin";boolean result = SM2Utils.verifysign(publicKey, data, signHex, id);System.out.println(result);Assert.isTrue(result);}

在这里插入图片描述

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

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

相关文章

通话状态监听-Android13

通话状态监听-Android13 1、Android Telephony 模块结构2、监听和广播获取通话状态2.1 注册2.2 通话状态通知2.3 通话状态 3、通知状态流程* 关键日志 frameworks/base/core/java/android/telephony/PhoneStateListener.java 1、Android Telephony 模块结构 Android Telephony…

开发电子商务网站/APP如何对接淘宝/天猫商品详情的API接口来丰富自建商城的产品展示

随着电子商务的快速发展&#xff0c;越来越多的企业开始意识到建立电子商务网站的重要性。下面我们将从产品、营销和客户服务三个方面来探讨电子商务网站的构建与运营策略。 1产品分析 在构建电子商务网站时&#xff0c;首先要对产品进行深入的分析。要明确产品的特点、优势和…

From Human Attention to Computational Attention (Foundation2)

Chapter 3 How to Measure Attention? 对注意力感兴趣的研究人员通常有以下一个或多个目标: (1)识别环境中被观察者选择和优先考虑的信息源; (2)量化注意力对任务表现的影响; (3)识别注意力的神经关联。 在考虑测量注意的方法时&#xff0c;区分显性和隐性定向机制是很重要的…

Flink系列之:监控Checkpoint

Flink系列之&#xff1a;监控Checkpoint 一、概览二、概览&#xff08;Overview&#xff09;选项卡三、历史记录&#xff08;History&#xff09;选项卡四、历史记录数量配置五、摘要信息&#xff08;Summary&#xff09;选项卡六、配置信息&#xff08;Configuration&#xff…

【XR806开发板试用】+ 通过网络控制led并上报按键状态

通过网络控制led并上报按键状态 本次做一个手机通过mqtt服务器控制板子上的LED亮灭&#xff0c;板子也可以将按钮状态变化通过mqtt服务器上报给手机的功能 硬件上&#xff0c;从原理图看&#xff0c;LED接到了PA21&#xff0c;高电平点亮。 按键则时接到了PA11&#xff0c;并…

VuePress安装及使用

前言 VuePress 是一个以 Markdown 为中心的静态网站生成器。你可以使用 Markdown 来书写内容&#xff08;如文档、博客等&#xff09;&#xff0c;然后 VuePress 会帮助你生成一个静态网站来展示它们。 例如&#xff1a;JavaFX 前言 这个博客网站就是使用 VuePress 生成的&am…

「Verilog学习笔记」交通灯

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 timescale 1ns/1nsmodule triffic_light(input rst_n, //异位复位信号&#xff0c;低电平有效input clk, //时钟信号input pass_request,output wire[7:0]clock,output reg…

Kotlin 笔记 -- Kotlin 语言特性的理解(二)

都是编译成字节码&#xff0c;为什么 Kotlin 能支持 Java 中没有的特性&#xff1f; kotlin 有哪些 Java 中没有的特性&#xff1a; 类型推断、可变性、可空性自动拆装箱、泛型数组高阶函数、DSL顶层函数、扩展函数、内联函数伴生对象、数据类、密封类、单例类接口代理、inter…

PostgreSQL常用命令

数据库版本 :9.6.6 注意 :PostgreSQL中的不同类型的权限有 SELECT,INSERT,UPDATE,DELETE,TRUNCATE,REFERENCES,TRIGGER,CREATE,CONNECT,TEMPORARY,EXECUTE 和 USAGE。 1. 登录PG数据库 以管理员身份 postgres 登陆,然后通过 #psql -U postgres #sudo -i -u postgres …

备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2:离线数据处理

MySQLhttps://www.mysql.com/ 将下发的ds_db01.sql数据库文件放置mysql中 12、编写Scala代码&#xff0c;使用Spark将MySQL的ds_db01库中表user_info的全量数据抽取到Hive的ods库中表user_info。字段名称、类型不变&#xff0c;同时添加静态分区&#xff0c;分区字段为etl_da…

【PWN】学习笔记(三)【返回导向编程】(下)

目录 课程回顾ret2libc![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/ebe1a9a9e54f4319946621dbe89c5774.png)做题 ret2libc2ret2libc3 课程 课程链接&#xff1a;https://www.bilibili.com/video/BV1854y1y7Ro/?vd_source7b06bd7a9dd90c45c5c9c44d12e7b4e6 课程…

互联网加竞赛 python 机器视觉 车牌识别 - opencv 深度学习 机器学习

1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 基于python 机器视觉 的车牌识别系统 &#x1f947;学长这里给一个题目综合评分(每项满分5分) 难度系数&#xff1a;3分工作量&#xff1a;3分创新点&#xff1a;3分 &#x1f9ff; 更多资…