Java算法SM2加密解密
简介
什么是SM2
SM2是国家密码管理局于2010年12⽉17⽇发布的椭圆曲线公钥密码算法。
SM2 算法和 RSA 算法都是公钥密码算法,SM2 算法是⼀种更先进安全的算法,在我们国家商⽤密码体系中被⽤来替换 RSA 算法。
随着密码技术和计算机技术的发展,⽬前常⽤的 1024 位 RSA 算法⾯临严重的安全威胁,我们国家密码管理部门经过研究,决定采⽤ SM2 椭圆曲线算法替换RSA算法。
SM2算法和RSA算法⽐较
SM2性能更优更安全:密码复杂度⾼、处理速度快、机器性能消耗更⼩
-SM2RSA
算法结构基本椭圆曲线(ECC)基于特殊的可逆模幂运算
计算复杂度完全指数级亚指数级
存储空间192-256bit2048-4096bit 秘钥⽣成速度较RSA算法快百倍以上慢
解密加密速度较快⼀般
POM依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15to18</artifactId>
<version>1.68</version>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15to18</artifactId>
<version>1.68</version>
</dependency>
Java⼯具类
package;
import GMNamedCurves;
import X9ECParameters;
import SM2Engine;
import ECDomainParameters;
import ECPrivateKeyParameters;
import ECPublicKeyParameters;
import ParametersWithRandom;
import BCECPrivateKey;
import BCECPublicKey;
import BouncyCastleProvider;
import ECParameterSpec;
import ECPrivateKeySpec;
import ECPublicKeySpec;
import Hex;
import BigInteger;
import*;
import*;
import ECGenParameterSpec;
/**
* @ClassName SM2Utils
* @Description SM2算法⼯具类
* @Author msx
* @Date 2021/9/24 16:50
* @Version 1.0
*/
public class SM2Utils {
/**
* @Description ⽣成秘钥对
* @Author msx
* @return KeyPair
*/
public static KeyPair createECKeyPair(){
//使⽤标准名称创建EC参数⽣成的参数规范
final ECGenParameterSpec sm2Spec =new ECGenParameterSpec("sm2p256v1");
// 获取⼀个椭圆曲线类型的密钥对⽣成器java源代码加密
final KeyPairGenerator kpg;
try{
kpg = Instance("EC",new BouncyCastleProvider());
// 使⽤SM2算法域参数集初始化密钥⽣成器(默认使⽤以最⾼优先级安装的提供者的 SecureRandom 的实现作为随机源)// kpg.initialize(sm2Spec);
// 使⽤SM2的算法域参数集和指定的随机源初始化密钥⽣成器
kpg.initialize(sm2Spec,new SecureRandom());
// 通过密钥⽣成器⽣成密钥对
ateKeyPair();
}catch(Exception e){
e.printStackTrace();
return null;
}
}
/**
* @Description 公钥加密
* @Author msx
* @param publicKeyHex SM2⼗六进制公钥
* @param data        明⽂数据
* @return String
*/
public static String encrypt(String publicKeyHex, String data){
return encrypt(getECPublicKeyByPublicKeyHex(publicKeyHex), data,1);
}
/**
* @Description 公钥加密
* @Author msx
* @param publicKey SM2公钥
* @param data      明⽂数据
* @param modeType  加密模式
* @return String
*/
public static String encrypt(BCECPublicKey publicKey, String data,int modeType){
//加密模式
SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
if(modeType !=1){
mode = SM2Engine.Mode.C1C2C3;
}
/
/通过公钥对象获取公钥的基本域参数。
ECParameterSpec ecParameterSpec = Parameters();
ECDomainParameters ecDomainParameters =new Curve(),
//通过公钥值和公钥基本参数创建公钥参数对象
ECPublicKeyParameters ecPublicKeyParameters =new Q(), ecDomainParameters);
//根据加密模式实例化SM2公钥加密引擎
SM2Engine sm2Engine =new SM2Engine(mode);
//初始化加密引擎
sm2Engine.init(true,new ParametersWithRandom(ecPublicKeyParameters,new SecureRandom()));
byte[] arrayOfBytes =null;
try{
//将明⽂字符串转换为指定编码的字节串
byte[] in = Bytes("utf-8");
//通过加密引擎对字节数串⾏加密
arrayOfBytes = sm2Engine.processBlock(in,0, in.length);
}catch(Exception e){
System.out.println("SM2加密时出现异常:"+ e.getMessage());
e.printStackTrace();
}
//将加密后的字节串转换为⼗六进制字符串
HexString(arrayOfBytes);
}
/**
* @Description 私钥解密
* @Author msx
* @param privateKeyHex SM2⼗六进制私钥
* @param cipherData    密⽂数据
* @return String
*/
public static String decrypt(String privateKeyHex, String cipherData){
return decrypt(getBCECPrivateKeyByPrivateKeyHex(privateKeyHex), cipherData,1);
}
/**
* @Description 私钥解密
* @Author msx
* @param privateKey SM私钥
* @param cipherData 密⽂数据
* @param modeType  解密模式
* @return
*/
public static String decrypt(BCECPrivateKey privateKey, String cipherData,int modeType){
//解密模式
SM2Engine.Mode mode = SM2Engine.Mode.C1C3C2;
if(modeType !=1)
mode = SM2Engine.Mode.C1C2C3;
//将⼗六进制字符串密⽂转换为字节数组(需要与加密⼀致,加密是:加密后的字节数组转换为了⼗六进制字符串)
byte[] cipherDataByte = Hex.decode(cipherData);
//通过私钥对象获取私钥的基本域参数。
ECParameterSpec ecParameterSpec = Parameters();
ECDomainParameters ecDomainParameters =new Curve(),
//通过私钥值和私钥钥基本参数创建私钥参数对象
ECPrivateKeyParameters ecPrivateKeyParameters =new D(),
ecDomainParameters);
//通过解密模式创建解密引擎并初始化
SM2Engine sm2Engine =new SM2Engine(mode);
sm2Engine.init(false, ecPrivateKeyParameters);
String result =null;
try{
//通过解密引擎对密⽂字节串进⾏解密
byte[] arrayOfBytes = sm2Engine.processBlock(cipherDataByte,0, cipherDataByte.length);
//将解密后的字节串转换为utf8字符编码的字符串(需要与明⽂加密时字符串转换成字节串所指定的字符编码保持⼀致)
result =new String(arrayOfBytes,"utf-8");
}catch(Exception e){
System.out.println("SM2解密时出现异常"+ e.getMessage());
}
return result;
}
//椭圆曲线ECParameters ASN.1 结构
private static X9ECParameters x9ECParameters = ByName("sm2p256v1");
//椭圆曲线公钥或私钥的基本域参数。
private static ECParameterSpec ecDomainParameters =new Curve(), G(), N());
/**
* @Description 公钥字符串转换为 BCECPublicKey 公钥对象
* @Author msx
* @param pubKeyHex 64字节⼗六进制公钥字符串(如果公钥字符串为65字节⾸个字节为0x04:表⽰该公钥为⾮压缩格式,操作时需要删除)
* @return BCECPublicKey SM2公钥对象
*/
public static BCECPublicKey getECPublicKeyByPublicKeyHex(String pubKeyHex){
//截取64字节有效的SM2公钥(如果公钥⾸个字节为0x04)
if(pubKeyHex.length()>128){
pubKeyHex = pubKeyHex.substring(pubKeyHex.length()-128);
}
//将公钥拆分为x,y分量(各32字节)
String stringX = pubKeyHex.substring(0,64);
String stringY = pubKeyHex.substring(stringX.length());
//将公钥x、y分量转换为BigInteger类型
BigInteger x =new BigInteger(stringX,16);
BigInteger y =new BigInteger(stringY,16);
//通过公钥x、y分量创建椭圆曲线公钥规范
ECPublicKeySpec ecPublicKeySpec =new Curve().createPoint(x, y), ecDomainParameters);
//通过椭圆曲线公钥规范,创建出椭圆曲线公钥对象(可⽤于SM2加密及验签)
return new BCECPublicKey("EC", ecPublicKeySpec, BouncyCastleProvider.CONFIGURATION);
}
/**
* @Description 私钥字符串转换为 BCECPrivateKey 私钥对象
* @Author msx
* @param privateKeyHex 32字节⼗六进制私钥字符串
* @return BCECPrivateKey SM2私钥对象
*/
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);
}
public static void main(String[] args){
String publicKeyHex =null;
String privateKeyHex =null;
KeyPair keyPair =createECKeyPair();
PublicKey publicKey = Public();
if(publicKey instanceof BCECPublicKey){
//获取65字节⾮压缩缩的⼗六进制公钥串(0x04)
publicKeyHex = HexString(((BCECPublicKey) publicKey).getQ().getEncoded(false));
System.out.println("---->SM2公钥:"+ publicKeyHex);
}
PrivateKey privateKey = Private();
if(privateKey instanceof BCECPrivateKey){
//获取32字节⼗六进制私钥串
privateKeyHex =((BCECPrivateKey) privateKey).getD().toString(16);
System.out.println("---->SM2私钥:"+ privateKeyHex);
}
/**
* 公钥加密
*/
String data ="=========待加密数据=========";
//将⼗六进制公钥串转换为 BCECPublicKey 公钥对象
String encryptData =encrypt(publicKeyHex, data);
String encryptData =encrypt(publicKeyHex, data);
System.out.println("---->加密结果:"+ encryptData);
/**
* 私钥解密
*/
//将⼗六进制私钥串转换为 BCECPrivateKey 私钥对象
data =decrypt(privateKeyHex, encryptData);
System.out.println("---->解密结果:"+ data);
}
}
扩展资料:
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。
(1)SM1 为对称加密。其加密强度与AES相当。该算法不公开,调⽤该算法时,需要通过加密芯⽚的接⼝进⾏调⽤。
(2)SM2为⾮对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥⽣成速度都快于RSA。ECC 256位(SM2采⽤的就是ECC 256位的⼀种)安全强度⽐RSA 2048位⾼,但运算速度快于RSA。
(3)SM3 消息摘要。可以⽤MD5作为对⽐理解。该算法已公开。校验结果为256位。
(4)SM4 ⽆线局域⽹标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。