国密算法
国密算法
分类
1、SM1是一种分组加密算法
对称加密算法中的分组加密算法,其分组长度、秘钥长度都是128bit,算法安全保密强度跟 AES 相当,但是算法不公开,仅以IP核的形式存在于芯片中,需要通过加密芯片的接口进行调用。
采用该算法已经研制了系列芯片、智能IC卡、智能密码钥匙、加密卡、加密机等安全产品,广泛应用于电子政务、电子商务及国民经济的各个应用领域(包括国家政务通、警务通等重要领域)。
2、SM2是非对称加密算法
它是基于椭圆曲线密码的公钥密码算法标准,其秘钥长度256bit,包含数字签名、密钥交换和公钥加密,用于替换RSA/DH/ECDSA/ECDH等国际算法。可以满足电子认证服务系统等应用需求,由国家密码管理局于2010年12月17号发布。
SM2采用的是ECC 256位的一种,其安全强度比RSA 2048位高,且运算速度快于RSA。
3、SM3是一种密码杂凑算法
用于替代MD5/SHA-1/SHA-2等国际算法,适用于数字签名和验证、消息认证码的生成与验证以及随机数的生成,可以满足电子认证服务系统等应用需求,于2010年12月17日发布。
它是在SHA-256基础上改进实现的一种算法,采用Merkle-Damgard结构,消息分组长度为512bit,输出的摘要值长度为256bit。
4、SM4是分组加密算法
跟SM1类似,是我国自主设计的分组对称密码算法,用于替代DES/AES等国际算法。SM4算法与AES算法具有相同的密钥长度、分组长度,都是128bit。于2012年3月21日发布,适用于密码应用中使用分组密码的需求。
5、SM7是一种分组加密算法
该算法没有公开。SM7适用于非接IC卡应用包括身份识别类应用(门禁卡、工作证、参赛证),票务类应用(大型赛事门票、展会门票),支付与通卡类应用(积分消费卡、校园一卡通、企业一卡通、公交一卡通)。
6、SM9是基于标识的非对称密码算法
用椭圆曲线对实现的基于标识的数字签名算法、密钥交换协议、密钥封装机制和公钥加密与解密算法,包括数字签名生成算法和验证算法,并给出了数字签名与验证算法及其相应的流程。并提供了相应的流程。可以替代基于数字证书的PKI/CA体系。
SM9主要用于用户的身份认证。据新华网公开报道,SM9的加密强度等同于3072位密钥的RSA加密算法,于2016年3月28日发布。
算法下载地址:http://www.scctc.org.cn/templates/Download/index.aspx?nodeid=71
规范
国标文档中定义国密算法使用256位椭圆曲线;
原文摘抄:
SM2"使用素数域256位椭圆曲线“
SM2标准中规定采用256比特的椭圆曲线域参数,并采用256比特的密码杂凑算法,并规定某些步骤中须采用SM3。
SM2密文由C1、C2、C3三部分构成,如何对SM2密文进行编码在已经公布的两个标准中有所不同,在早期公布的《SM2椭圆曲线公钥密码算法 第4部分:公钥加密算法》中,SM2密文中的三部分依次输出,没有采用如Tag-Length-Value形式的编码,我们称其为Plain编码。在之后公布的GM/T国标中,SM2密文采用ASN.1/DER方式编码。
SM3是国密密码杂凑算法标准,由国家密码管理局于2010年12月公布。SM3的输出杂凑值长度为256比特(32字节),与国际标准SHA-256等长。SM3设计安全性为128比特,安全性与256比特椭圆曲线/SM2、SM4/SMS4、AES-128等同。
工具包BouncyCastle
Java标准库提供了一系列常用的哈希算法。但如果我们要用的某种算法,Java标准库没有提供怎么办,BouncyCastle就是一个提供了很多哈希算法和加密算法的第三方库。它提供了Java标准库没有的一些算法。
安装
方式一
动态安装
引入包,
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.71</version>
</dependency>
最新的包及jdk支持 可以戳 这里
使用时需要通过Security.addProvider(new BouncyCastleProvider())动态加载到jvm
@Test
public void case5() throws Exception {
// 注册BouncyCastle:
String key="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
System.out.println(key.length());
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("UTF-8"), "AES"));
byte[] resultByte = cipher.doFinal("zw".getBytes("UTF-8"));
System.out.println(Hex.toHexString(resultByte));
}
方式二
静态安装
(1)去BouncyCastle官网下载provider的包,然后放入$JAVA_HOME\jre\lib\ext目录下;
(2)修改配置文件
java8: %java_home%\jre\lib\security\java.security
java9+: %JAVA_HOME%\conf\security.
加入一行配置:
security.provider.按顺序填数字=org.bouncycastle.jce.provider.BouncyCastleProvider
@Test
public void case5() throws Exception {
String key="BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB";
System.out.println(key.length());
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS7Padding");
cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key.getBytes("UTF-8"), "AES"));
byte[] resultByte = cipher.doFinal("zw".getBytes("UTF-8"));
System.out.println(Hex.toHexString(resultByte));
}
java代码实现
SM2工具类
SM2KeyInfo.java
package com.tom.crypto;
/**
* @ClassName SM2KeyInfo
* @Description 生成密钥info
* @Author tom
*/
public class SM2KeyInfo {
/**
*
* BC库使用的公钥=64个字节+1个字节(04标志位),BC库使用的私钥=32个字节
* SM2秘钥的组成部分有 私钥D 、公钥X 、 公钥Y , 他们都可以用长度为64的16进制的HEX串表示,
* SM2公钥并不是直接由X+Y表示 , 而是额外添加了一个头,当启用压缩时:公钥=有头+公钥X ,即省略了公钥Y的部分
*/
private String publicKeyHex;
private String privateKeyHex;
private String pubX;
private String pubY;
public SM2KeyInfo() {
}
public String getPublicKeyHex() {
return publicKeyHex;
}
public void setPublicKeyHex(String publicKeyHex) {
this.publicKeyHex = publicKeyHex;
}
public String getPrivateKeyHex() {
return privateKeyHex;
}
public void setPrivateKeyHex(String privateKeyHex) {
this.privateKeyHex = privateKeyHex;
}
public String getPubX() {
return pubX;
}
public void setPubX(String pubX) {
this.pubX = pubX;
}
public String getPubY() {
return pubY;
}
public void setPubY(String pubY) {
this.pubY = pubY;
}
@Override
public String toString() {
final StringBuffer sb = new StringBuffer("SM2KeyInfo{");
sb.append("publicKeyHex='").append(publicKeyHex).append('\'');
sb.append(", privateKeyHex='").append(privateKeyHex).append('\'');
sb.append(", pubX='").append(pubX).append('\'');
sb.append(", pubY='").append(pubY).append('\'');
sb.append('}');
return sb.toString();
}
}
SM2Util.java
package com.tom.crypto;
import org.bouncycastle.asn1.gm.GMNamedCurves;
import org.bouncycastle.asn1.x9.X9ECParameters;
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.jcajce.spec.SM2ParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.jce.spec.ECParameterSpec;
import org.bouncycastle.jce.spec.ECPrivateKeySpec;
import org.bouncycastle.jce.spec.ECPublicKeySpec;
import org.bouncycastle.math.ec.ECPoint;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.*;
import java.security.spec.ECGenParameterSpec;
/**
* Sm2算法工具类
* 支持公钥加密、私钥解密 ,私钥加签 公钥解签
* 收集网上资料整理:
* 参考bouncycastle demo
*/
public class SM2Utils {
//椭圆曲线ECParameters ASN.1 结构,sm2P256V1代表国密SM2推荐参数定义的椭圆曲线
private static X9ECParameters sm2ECParameters = GMNamedCurves.getByName("sm2p256v1");
//椭圆曲线公钥或私钥的基本域参数。
private static ECParameterSpec ecDomainParameters = new ECParameterSpec(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
//构造domain参数
private static ECDomainParameters domainParameters = new ECDomainParameters(sm2ECParameters.getCurve(), sm2ECParameters.getG(), sm2ECParameters.getN());
/**
* 生成密钥对
*
* @return
*/
public static KeyPair generateECKeyPair() {
//使用标准名称创建EC参数生成的参数规范
final ECGenParameterSpec sm2Spec = new ECGenParameterSpec("sm2p256v1");
// 获取一个椭圆曲线类型的密钥对生成器
final KeyPairGenerator kpg;
try {
kpg = KeyPairGenerator.getInstance("EC", new BouncyCastleProvider());
// 使用SM2算法域参数集初始化密钥生成器(默认使用以最高优先级安装的提供者的 SecureRandom 的实现作为随机源)
// 使用SM2的算法域参数集和指定的随机源初始化密钥生成器
kpg.initialize(sm2Spec, new SecureRandom());
// 通过密钥生成器生成密钥对
KeyPair keyPair = kpg.generateKeyPair();
return keyPair;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 生成密钥相关信息
*
* @return
*/
public static SM2KeyInfo generateSM2Key() {
SM2KeyInfo sm2KeyInfo = new SM2KeyInfo();
KeyPair keyPair = generateECKeyPair();
PublicKey publicKey = keyPair.getPublic();
if (publicKey instanceof BCECPublicKey) {
//获取65字节非压缩缩的十六进制公钥串(0x04)
BCECPublicKey bcecPublicKey = (BCECPublicKey) publicKey;
ECPoint ecPoint = bcecPublicKey.getQ();
String publicKeyHex = Hex.toHexString(ecPoint.getEncoded(false));
String pubX = ecPoint.getXCoord().toBigInteger().toString(16);
String pubY = ecPoint.getYCoord().toBigInteger().toString(16);
sm2KeyInfo.setPubX(pubX);
sm2KeyInfo.setPubY(pubY);
sm2KeyInfo.setPublicKeyHex(publicKeyHex);
}
PrivateKey privateKey = keyPair.getPrivate();
if (privateKey instanceof BCECPrivateKey) {
//获取32字节十六进制私钥串
String privateKeyHex = ((BCECPrivateKey) privateKey).getD().toString(16);
sm2KeyInfo.setPrivateKeyHex(privateKeyHex);
}
return sm2KeyInfo;
}
/**
* @param publicKeyHex SM2十六进制公钥
* @param data 明文数据
* @return
* @Description 公钥加密
*/
public static String encrypt(String publicKeyHex, String data) throws UnsupportedEncodingException, InvalidCipherTextException {
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().decodePoint(Hex.decode(publicKeyHex));
// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 设置sm2为加密模式
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] in = data.getBytes("utf-8");
byte[] arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
return Hex.toHexString(arrayOfBytes);
}
/**
* 加密 返回16进制字符串
*
* @param pubX
* @param pubY
* @param data 明文
* @return
*/
public static String encrypt(String pubX, String pubY, String data) throws UnsupportedEncodingException, InvalidCipherTextException {
BigInteger x = new BigInteger(pubX, 16);
BigInteger y = new BigInteger(pubY, 16);
//提取公钥点
ECPoint pukPoint = sm2ECParameters.getCurve().createPoint(x, y);
// 公钥前面的02或者03表示是压缩公钥,04表示未压缩公钥, 04的时候,可以去掉前面的04
ECPublicKeyParameters publicKeyParameters = new ECPublicKeyParameters(pukPoint, domainParameters);
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 设置sm2为加密模式
sm2Engine.init(true, new ParametersWithRandom(publicKeyParameters, new SecureRandom()));
byte[] in = data.getBytes("utf-8");
byte[] arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
return Hex.toHexString(arrayOfBytes);
}
/**
* 公钥加密 加密模式是SM2Engine.Mode.C1C3C2
*
* @param publicKey SM2公钥
* @param data 明文数据
* @return String
* @Description 公钥加密
* @Author msx
*/
public static String encrypt(BCECPublicKey publicKey, String data) throws UnsupportedEncodingException, InvalidCipherTextException {
//通过公钥对象获取公钥的基本域参数。
ECParameterSpec ecParameterSpec = publicKey.getParameters();
// 构造ECC算法参数,曲线方程、椭圆曲线G点、大整数N
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
//通过公钥值和公钥基本参数创建公钥参数对象
ECPublicKeyParameters ecPublicKeyParameters = new ECPublicKeyParameters(publicKey.getQ(), ecDomainParameters);
//根据加密模式实例化SM2公钥加密引擎
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
//初始化加密引擎
sm2Engine.init(true, new ParametersWithRandom(ecPublicKeyParameters, new SecureRandom()));
//将明文字符串转换为指定编码的字节串
byte[] in = data.getBytes("utf-8");
//通过加密引擎对字节数串行加密
byte[] arrayOfBytes = sm2Engine.processBlock(in, 0, in.length);
//将加密后的字节串转换为十六进制字符串
return Hex.toHexString(arrayOfBytes);
}
/**
* @param privateKey SM私钥
* @param cipherData 密文数据
* @return
* @Description 私钥解密
* @Author msx
*/
public static String decrypt(BCECPrivateKey privateKey, String cipherData) throws UnsupportedEncodingException, InvalidCipherTextException {
// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
if (!cipherData.startsWith("04")) {
cipherData = "04" + cipherData;
}
//将十六进制字符串密文转换为字节数组(需要与加密一致,加密是:加密后的字节数组转换为了十六进制字符串)
byte[] cipherDataByte = Hex.decode(cipherData);
//通过私钥对象获取私钥的基本域参数。
ECParameterSpec ecParameterSpec = privateKey.getParameters();
ECDomainParameters ecDomainParameters = new ECDomainParameters(ecParameterSpec.getCurve(),
ecParameterSpec.getG(), ecParameterSpec.getN());
//通过私钥值和私钥钥基本参数创建私钥参数对象
ECPrivateKeyParameters ecPrivateKeyParameters = new ECPrivateKeyParameters(privateKey.getD(),
ecDomainParameters);
//通过解密模式创建解密引擎并初始化
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
sm2Engine.init(false, ecPrivateKeyParameters);
//通过解密引擎对密文字节串进行解密
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
//将解密后的字节串转换为utf8字符编码的字符串(需要与明文加密时字符串转换成字节串所指定的字符编码保持一致)
return new String(arrayOfBytes, "utf-8");
}
/**
* @param privateKeyHex SM2十六进制私钥
* @param cipherData 密文数据
* @return String
* @Description 私钥解密
* @Author msx
*/
public static String decrypt(String privateKeyHex, String cipherData) throws UnsupportedEncodingException, InvalidCipherTextException {
// 使用BC库加解密时密文以04开头,传入的密文前面没有04则补上
if (!cipherData.startsWith("04")) {
cipherData = "04" + cipherData;
}
//将十六进制字符串密文转换为字节数组(需要与加密一致,加密是:加密后的字节数组转换为了十六进制字符串)
byte[] cipherDataByte = Hex.decode(cipherData);
BigInteger privateKeyD = new BigInteger(privateKeyHex, 16);
ECPrivateKeyParameters privateKeyParameters = new ECPrivateKeyParameters(privateKeyD, domainParameters);
SM2Engine sm2Engine = new SM2Engine(SM2Engine.Mode.C1C3C2);
// 设置sm2为解密模式
sm2Engine.init(false, privateKeyParameters);
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte, 0, cipherDataByte.length);
return new String(arrayOfBytes);
}
/**
* @param pubKeyHex 64字节十六进制公钥字符串(如果公钥字符串为65字节首个字节为0x04:表示该公钥为非压缩格式,操作时需要删除)
* @return BCECPublicKey SM2公钥对象
* @Description 公钥字符串转换为 BCECPublicKey 公钥对象
* @Author msx
*/
public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex) {
//截取64字节有效的SM2公钥(如果公钥首个字节为0x04)
if (pubKeyHex.length() > 128) {
pubKeyHex = pubKeyHex.substring(pubKeyHex.length() - 128);
}
//将公钥拆分为x,y分量(各32字节)
String pubX = pubKeyHex.substring(0, 64);
String pubY = pubKeyHex.substring(pubX.length());
//将公钥x、y分量转换为BigInteger类型
BigInteger x = new BigInteger(pubX, 16);
BigInteger y = new BigInteger(pubY, 16);
//通过公钥x、y分量创建椭圆曲线公钥规范
ECPublicKeySpec ecPublicKeySpec = new ECPublicKeySpec(sm2ECParameters.getCurve().createPoint(x, y), ecDomainParameters);
//通过椭圆曲线公钥规范,创建出椭圆曲线公钥对象(可用于SM2加密及验签)
return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
}
/**
* @param privateKeyHex 32字节十六进制私钥字符串
* @return BCECPrivateKey SM2私钥对象
* @Description 私钥字符串转换为 BCECPrivateKey 私钥对象
* @Author msx
*/
public static BCECPrivateKey getBCECPrivateKeyByPrivateKeyHex(String privateKeyHex) {
//将十六进制私钥字符串转换为BigInteger对象
BigInteger d = new BigInteger(privateKeyHex, 16);
//通过私钥和私钥域参数集创建椭圆曲线私钥规范
ECPrivateKeySpec ecPrivateKeySpec = new ECPrivateKeySpec(d, ecDomainParameters);
//通过椭圆曲线私钥规范,创建出椭圆曲线私钥对象(可用于SM2解密和签名)
return new BCECPrivateKey("EC", ecPrivateKeySpec, BouncyCastleProvider.CONFIGURATION);
}
/**
* Generate an encoded SM2 signature based on the SM3 digest using the
* passed in EC private key and input data.
*
* @param ecPrivate the private key for generating the signature with.
* @param input the input to be signed.
* @return the encoded signature.
*/
public static byte[] generateSM2Signature(
PrivateKey ecPrivate, byte[] input)
throws GeneralSecurityException {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.initSign(ecPrivate);
signature.update(input);
return signature.sign();
}
/**
* Return true if the passed in SM3withSM2 signature verifies against
* the passed in EC public key and input.
*
* @param ecPublic the public key of the signature creator.
* @param input the input that was supposed to have been signed.
* @param encSignature the encoded signature.
* @return true if the signature verifies, false otherwise.
*/
public static boolean verifySM2Signature(
PublicKey ecPublic, byte[] input, byte[] encSignature)
throws GeneralSecurityException {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.initVerify(ecPublic);
signature.update(input);
return signature.verify(encSignature);
}
/**
* Generate an encoded SM2 signature based on the SM3 digest using the
* passed in EC private key and input data.
*
* @param ecPrivate the private key for generating the signature with.
* @param sm2Spec the SM2 specification carrying the ID of the signer.
* @param input the input to be signed.
* @return the encoded signature.
*/
public static byte[] generateSM2Signature(
PrivateKey ecPrivate, SM2ParameterSpec sm2Spec, byte[] input)
throws GeneralSecurityException {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.setParameter(sm2Spec);
signature.initSign(ecPrivate);
signature.update(input);
return signature.sign();
}
/**
* Return true if the passed in SM3withSM2 signature verifies against
* the passed in EC public key and input.
*
* @param ecPublic the public key of the signature creator.
* @param sm2Spec the SM2 specification carrying the expected ID of the signer.
* @param input the input that was supposed to have been signed.
* @param encSignature the encoded signature.
* @return true if the signature verifies, false otherwise.
*/
public static boolean verifySM2Signature(
PublicKey ecPublic, SM2ParameterSpec sm2Spec,
byte[] input, byte[] encSignature)
throws GeneralSecurityException {
Signature signature = Signature.getInstance("SM3withSM2", "BC");
signature.setParameter(sm2Spec);
signature.initVerify(ecPublic);
signature.update(input);
return signature.verify(encSignature);
}
}
SM2UtilTest.java
package com.tom.crypto;
import org.bouncycastle.jcajce.spec.SM2ParameterSpec;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.util.Strings;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Assert;
import org.junit.Test;
import java.security.Security;
/**
* @ClassName TestSM2
* @Description
* @Author tom
*/
public class SM2UtilTest {
static {
Security.addProvider(new BouncyCastleProvider());
}
@Test
public void testSm2() throws Exception {
SM2KeyInfo sm2KeyInfo = SM2Util.generateSM2Key();
System.out.println(sm2KeyInfo);
String publicKeyHex = sm2KeyInfo.getPublicKeyHex();
String privateKeyHex = sm2KeyInfo.getPrivateKeyHex();
String pubX = sm2KeyInfo.getPubX();
String pubY = sm2KeyInfo.getPubY();
/**
* 公钥加密
*/
String plainText = "=========原文数据====zw!!!=====";
String encryptData = SM2Util.encrypt(publicKeyHex, plainText);
System.out.println("---->encrypt加密结果:" + encryptData);
String encryptData2 = SM2Util.encrypt(pubX, pubY, plainText);
System.out.println("---->encrypt加密结果:" + encryptData2);
/**
* 私钥解密
*/
String decryptData = SM2Util.decrypt(privateKeyHex, encryptData);
Assert.assertEquals(plainText,decryptData);
System.out.println("---->decrypt解密结果:" + decryptData);
String decryptData2 = SM2Util.decrypt(privateKeyHex, encryptData2);
System.out.println("---->decrypt2解密结果:" + plainText);
Assert.assertEquals(plainText,decryptData2);
byte[] signResult = SM2Util.generateSM2Signature(SM2Util.getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), plainText.getBytes());
String signResultHex = Hex.toHexString(signResult);
System.out.println("---->签名结果:" + signResultHex);
boolean verifyResult = SM2Util.verifySM2Signature(SM2Util.getECPublicKeyByPublicKeyHex(publicKeyHex), plainText.getBytes(), Hex.decode(signResultHex));
System.out.println("---->验签结果:" + verifyResult);
Assert.assertTrue(verifyResult);
SM2ParameterSpec sm2Spec = new SM2ParameterSpec(
Strings.toByteArray("zwtom@baidu.com"));
byte[] sm2Signature = SM2Util.generateSM2Signature(
SM2Util.getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), sm2Spec,
Strings.toByteArray(plainText));
String sm2SignatureHex = Hex.toHexString(sm2Signature);
System.out.println("---->签名结果2:" + sm2SignatureHex);
boolean verifyReuslt2 = SM2Util.verifySM2Signature(
SM2Util.getECPublicKeyByPublicKeyHex(publicKeyHex), sm2Spec,
Strings.toByteArray(plainText), Hex.decode(sm2SignatureHex));
System.out.println("---->验签结果:" + verifyReuslt2);
Assert.assertTrue(verifyReuslt2);
}
}
运算结果:
SM2KeyInfo{publicKeyHex='04ae5070d54d6603482d0f5abb888e39f58503f4ac6bf48ce8bcf64ba10f348af801ca7b0c439cf57d884f1a77ff8350006ee5eae5deac2e186ddeb873e4b0d427', privateKeyHex='5648e255a8460aa34663ebc0fb01069a844142fdda8cb663cefe56a2e0a8af00', pubX='ae5070d54d6603482d0f5abb888e39f58503f4ac6bf48ce8bcf64ba10f348af8', pubY='1ca7b0c439cf57d884f1a77ff8350006ee5eae5deac2e186ddeb873e4b0d427'}
---->encrypt加密结果:04ecadd6f10fdf720c9c86354a9cd5e1cc6b776d42b3c158275f0685baa033c2aab125de6889027093cb6f151bdd1f2fa174f6780a17d3190c8093ad0c13a93bc3debe26fb9f45e7ecd6c207b9a918ab321a1b1939881539dd8782166aa16350671055d3ef5cad464662f0b1545adeea2f3c778b01fd0c81ad3bf7827876154d368c3fbd
---->encrypt加密结果:04ea902db9a38d92ae21064a56b990ebafc95a9374a7bacbd1e61f5c845532a5c116c30f82198164283b1e6fdaa939e1da7fc334825faeebc3b75c2c0e211fbf56e0cfd57464fc8753ae5c4992459dafe8b9a8395c3e7a4ead0ad8fa5f7fe4609a3b0db09eb4a812424d74d715ed1a5e761710533a280f3baedd5ba1d55e258a63c18c02
---->decrypt解密结果:=========原文数据====zw!!!=====
---->decrypt2解密结果:=========原文数据====zw!!!=====
---->签名结果:3045022100dcbc6cdde483ba71f04f56e15409a9d7555bfeb52fefbf4d096bfdf0fc97fda602206a2d14db705fe7c1890dcd5bd7fd3748b5f86f0c9b97037feb65202ab5abda5b
---->验签结果:true
---->签名结果2:3045022100e791b7df8ee931cc7df4f86df3d9f6a8b08d85824e32657d5f254040c65789ae022024b679695961352435d9975fc7b277f25e220dbaba4a00c8bc83514f2c149d5f
---->验签结果:true
SM3工具类
SM3Util.java
package com.tom.crypto;
import org.bouncycastle.crypto.digests.SM3Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import java.util.Arrays;
/**
* SM3算法工具类
*/
public class SM3Util {
/**
* 计算SM3摘要值
*
* @param srcData 原文
* @return 摘要值,对于SM3算法来说是32字节
*/
public static byte[] hash(byte[] srcData) {
SM3Digest digest = new SM3Digest();
digest.update(srcData, 0, srcData.length);
byte[] hash = new byte[digest.getDigestSize()];
digest.doFinal(hash, 0);
return hash;
}
/**
* 验证摘要
*
* @param srcData 原文
* @param sm3Hash 摘要值
* @return 返回true标识验证成功,false标识验证失败
*/
public static boolean verify(byte[] srcData, byte[] sm3Hash) {
byte[] newHash = hash(srcData);
if (Arrays.equals(newHash, sm3Hash)) {
return true;
} else {
return false;
}
}
/**
* 计算SM3 Mac值
*
* @param key key值,可以是任意长度的字节数组
* @param srcData 原文
* @return Mac值,对于HMac-SM3来说是32字节
*/
public static byte[] hmac(byte[] key, byte[] srcData) {
KeyParameter keyParameter = new KeyParameter(key);
SM3Digest digest = new SM3Digest();
HMac mac = new HMac(digest);
mac.init(keyParameter);
mac.update(srcData, 0, srcData.length);
byte[] result = new byte[mac.getMacSize()];
mac.doFinal(result, 0);
return result;
}
}
SM3UtilTest 工具类
package com.tom.crypto;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.bouncycastle.pqc.math.linearalgebra.ByteUtils;
import org.bouncycastle.util.encoders.Hex;
import org.junit.Assert;
import org.junit.Test;
import java.security.Security;
import java.util.Arrays;
public class SM3UtilTest {
static String plainText = "=========原文数据====zw!!!=====";
static {
Security.addProvider(new BouncyCastleProvider());
}
@Test
public void testHashAndVerify() {
try {
byte[] hash = SM3Util.hash(plainText.getBytes("utf-8"));
System.out.println("SM3 hash result:\n" + Hex.toHexString(hash));
boolean flag = SM3Util.verify(plainText.getBytes("utf-8"), hash);
Assert.assertTrue(flag);
} catch (Exception ex) {
ex.printStackTrace();
Assert.fail();
}
}
@Test
public void testHmacSM3() {
try {
byte[] hmacKey = new byte[]{1, 2, 3, 4, 5, 6, 7, 8};
byte[] hmac = SM3Util.hmac(hmacKey, plainText.getBytes("utf-8"));
System.out.println("SM3 hash result:\n" + Arrays.toString(hmac));
} catch (Exception ex) {
ex.printStackTrace();
Assert.fail();
}
}
}
参考网站
BouncyCastle介绍:
https://www.liaoxuefeng.com/wiki/1252599548343744/1305362418368545
BouncyCastle wiki :
https://github.com/bcgit/bc-java/wiki
BouncyCastle 官网
https://www.bouncycastle.org/latest_releases.html
国密局标准:
https://openstd.samr.gov.cn/bzgk/gb/std_list?p.p1=0&p.p90=circulation_date&p.p91=desc&p.p2=32918
草案:
https://datatracker.ietf.org/doc/html/draft-shen-sm2-ecdsa-02文章来源:https://www.toymoban.com/news/detail-449943.html
开源密码箱工具
http://gmssl.org/docs/quickstart.html文章来源地址https://www.toymoban.com/news/detail-449943.html
到了这里,关于国密SM2/SM3算法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!