编写本文的同时借鉴了多位同学写的demo, 最后发现这位同学写的最详细, 放上该作者的链接, 原版链接: https://download.csdn.net/download/ererfei/9474502
结合公司的业务需求, 我自己编写测试了SM2的demo并增加了相关工具类, 链接: https://download.csdn.net/download/s78365126/10741527
国密算法
国密即国家密码局认定的国产密码算法。主要有SM1,SM2,SM3,SM4。密钥长度和分组长度均为128位。
SM1 为对称加密。其加密强度与AES相当。该算法不公开,调用该算法时,需要通过加密芯片的接口进行调用。
SM2为非对称加密,基于ECC。该算法已公开。由于该算法基于ECC,故其签名速度与秘钥生成速度都快于RSA。ECC 256位(SM2采用的就是ECC 256位的一种)安全强度比RSA 2048位高,但运算速度快于RSA。
SM3 消息摘要。可以用MD5作为对比理解。该算法已公开。校验结果为256位。
SM4 无线局域网标准的分组数据算法。对称加密,密钥长度和分组长度均为128位。
SM2算法:SM2椭圆曲线公钥密码算法是我国自主设计的公钥密码算法,包括SM2-1椭圆曲线数字签名算法,SM2-2椭圆曲线密钥交换协议,SM2-3椭圆曲线公钥加密算法,分别用于实现数字签名密钥协商和数据加密等功能。SM2算法与RSA算法不同的是,SM2算法是基于椭圆曲线上点群离散对数难题,相对于RSA算法,256位的SM2密码强度已经比2048位的RSA密码强度要高
学习sm2算法,首先学习ECC算法
ECC算法描述:
1、用户A选定一条适合加密的椭圆曲线Ep(a,b)(如:y2=x3+ax+b),并取椭圆曲线上一点,作为基点G。
2、用户A选择一个私有密钥k,并生成公开密钥(公钥PB)K=kG。
3、用户A将Ep(a,b)和点(公钥)K,G传给用户B。
4、用户B接到信息后 ,将待传输的明文(M)编码到Ep(a,b)上一点M,并产生一个随机整数r(r
6、用户B将C1、C2传给用户A。
7、用户A接到信息后,计算C1-kC2,结果就是点M。因为C1-kC2=M+rK-k(rG)=M+rK-r(kG)=M
再对点M进行解码就可以得到明文。
密码学中,描述一条Fp上的椭圆曲线,常用到六个参量:
T=(p,a,b,G,n,h)。
(p 、a 、b 用来确定一条椭圆曲线,G为基点,n为点G的阶,h 是椭圆曲线上所有点的个数m与n相除的整数部分)
这几个参量取值的选择,直接影响了加密的安全性。参量值一般要求满足以下几个条件:
1、p 当然越大越安全,但越大,计算速度会变慢,200位左右可以满足一般安全要求;
2、p≠n×h;
3、pt≠1 (mod n),1≤t<20;
4、4a3+27b2≠0 (mod p);
5、n 为素数;
6、h≤4。
所以关于sm2算法的流程如图
SM2算法就是ECC椭圆曲线密码机制,但在签名、密钥交换方面不同于ECDSA、ECDH等国际标准,而是采取了更为安全的机制。另外,SM2推荐了一条256位的曲线作为标准曲线。
SM2标准包括总则,数字签名算法,密钥交换协议,公钥加密算法四个部分,并在每个部分的附录详细说明了实现的相关细节及示例。
SM2算法主要考虑素域Fp和F2m上的椭圆曲线,分别介绍了这两类域的表示,运算,以及域上的椭圆曲线的点的表示,运算和多倍点计算算法。然后介绍了编程语言中的数据转换,包括整数和字节串,字节串和比特串,域元素和比特串,域元素和整数,点和字节串之间的数据转换规则。
详细说明了有限域上椭圆曲线的参数生成以及验证,椭圆曲线的参数包括有限域的选取,椭圆曲线方程参数,椭圆曲线群基点的选取等,并给出了选取的标准以便于验证。最后给椭圆曲线上密钥对的生成以及公钥的验证,用户的密钥对为(s,sP),其中s为用户的私钥,sP为用户的公钥,由于离散对数问题从sP难以得到s,并针对素域和二元扩域给出了密钥对生成细节和验证方式。总则中的知识也适用于SM9算法。
在总则的基础上给出了数字签名算法(包括数字签名生成算法和验证算法),密钥交换协议以及公钥加密算法(包括加密算法和解密算法),并在每个部分给出了算法描述,算法流程和相关示例。
数字签名算法,密钥交换协议以及公钥加密算法都使用了国家密管理局批准的SM3密码杂凑算法和随机数发生器。数字签名算法,密钥交换协议以及公钥加密算法根据总则来选取有限域和椭圆曲线,并生成密钥对。
SM2算法在很多方面都优于RSA算法(RSA发展得早应用普遍,SM2领先也很自然),与RSA安全性对比如下图
SM3算法:SM3杂凑算法是我国自主设计的密码杂凑算法,适用于商用密码应用中的数字签名和验证消息认证码的生成与验证以及随机数的生成,可满足多种密码应用的安全需求。为了保证杂凑算法的安全性,其产生的杂凑值的长度不应太短,例如MD5输出128比特杂凑值,输出长度太短,影响其安全性SHA-1算法的输出长度为160比特,SM3算法的输出长度为256比特,因此SM3算法的安全性要高于MD5算法和SHA-1算法。
对长度为l(l<2^64)比特的消息m,SM3杂凑算法经过填充和迭代压缩,生成杂凑值,杂凑值长度为256比特。
假设消息m的长度为l比特。首先将比特“1”添加到消息的末尾,再添加k个“0”,k是满足l+1+k448mod512的最小的非负整数。然后再添加一个64位比特串,该比特串是长度l的二进制表示。
填充后的消息m′的比特长度为512的倍数。
例如:对消息01100001 01100010 01100011,其长度l=24,经填充得到比特串:
01100001 01100010 01100011 1 00...00(423比特)00...011000(64比特l的二进制表示)
下面的是实现了SM3的标准输出
此算法是一个分组算法,用于无线局域网产品。该算法的分组长度为128比特,密钥长度为128比特。加密算法与密钥扩展算法都采用32轮非线性迭代结构。解密算法与加密算法的结构相同,只是轮密钥的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
此算法采用非线性迭代结构,每次迭代由一个轮函数给出,其中轮函数由一个非线性变换和线性变换复合而成,非线性变换由S盒所给出。其中rki为轮密钥,合成置换T组成轮函数。轮密钥的产生与上图流程类似,由加密密钥作为输入生成,轮函数中的线性变换不同,还有些参数的区别。
过程:
基本运算:SM4密码算法使用模2加和循环移位作为基本运算。
基本密码部件:SM4密码算法使用了S盒、非线性变换τ、线性变换部件L、合成变换T基本密码部件。
轮函数:SM4密码算法采用对基本轮函数进行迭代的结构。利用上述基本密码部件,便可构成轮函数。SM4密码算法的轮函数是一种以字为处理单位的密码函数。
加密算法:SM4密码算法是一个分组算法。数据分组长度为128比特,密钥长度为128比特。加密算法采用32轮迭代结构,每轮使用一个轮密钥。
解密算法:SM4密码算法是对合运算,因此解密算法与加密算法的结构相同,只是轮密铝的使用顺序相反,解密轮密钥是加密轮密钥的逆序。
密钥扩展算法:SM4密码算法使用128位的加密密钥,并采用32轮法代加密结构,每一轮加密使用一个32位的轮密钥,共使用32个轮密钥。因此需要使用密钥扩展算法,从加密密钥产生出32个轮密钥。
SM4的安全性:SM4密码算法经过我国专业密码机构的充分分析测试,可以抵抗差分攻击、线性攻击等现有攻击,因此是安全的。
首先需要下载一个java操作SM算法的jar包, maven依赖为
org.bouncycastle
bcprov-jdk15
1.45
package com.security.cipher.sm;
import org.bouncycastle.crypto.generators.ECKeyPairGenerator;
import org.bouncycastle.crypto.params.ECDomainParameters;
import org.bouncycastle.crypto.params.ECKeyGenerationParameters;
import org.bouncycastle.math.ec.ECCurve;
import org.bouncycastle.math.ec.ECFieldElement;
import org.bouncycastle.math.ec.ECFieldElement.Fp;
import org.bouncycastle.math.ec.ECPoint;
import java.math.BigInteger;
import java.security.SecureRandom;
public class SM2 {
/*
// 测试参数
public static final String[] ecc_param = {
"8542D69E4C044F18E8B92435BF6FF7DE457283915C45517D722EDB8B08F1DFC3",
"787968B4FA32C3FD2417842E73BBFEFF2F3C848B6831D7E0EC65228B3937E498",
"63E4C6D3B23B0C849CF84241484BFE48F61D59A5B16BA06E6E12D1DA27C5249A",
"8542D69E4C044F18E8B92435BF6FF7DD297720630485628D5AE74EE7C32E79B7",
"421DEBD61B62EAB6746434EBC3CC315E32220B3BADD50BDC4C4E6C147FEDD43D",
"0680512BCBB42C07D47349D2153B70C4E5D7FDFCBFA36EA1A85841B9E46E09A2"
};
*/
// 正式参数
public static String[] ecc_param = {
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF",
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC",
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93",
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123",
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7",
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0"
};
public static SM2 Instance() {
return new SM2();
}
public final BigInteger ecc_p;
public final BigInteger ecc_a;
public final BigInteger ecc_b;
public final BigInteger ecc_n;
public final BigInteger ecc_gx;
public final BigInteger ecc_gy;
public final ECCurve ecc_curve;
public final ECPoint ecc_point_g;
public final ECDomainParameters ecc_bc_spec;
public final ECKeyPairGenerator ecc_key_pair_generator;
public final ECFieldElement ecc_gx_fieldelement;
public final ECFieldElement ecc_gy_fieldelement;
public SM2() {
this.ecc_p = new BigInteger(ecc_param[0], 16);
this.ecc_a = new BigInteger(ecc_param[1], 16);
this.ecc_b = new BigInteger(ecc_param[2], 16);
this.ecc_n = new BigInteger(ecc_param[3], 16);
this.ecc_gx = new BigInteger(ecc_param[4], 16);
this.ecc_gy = new BigInteger(ecc_param[5], 16);
this.ecc_gx_fieldelement = new Fp(this.ecc_p, this.ecc_gx);
this.ecc_gy_fieldelement = new Fp(this.ecc_p, this.ecc_gy);
this.ecc_curve = new ECCurve.Fp(this.ecc_p, this.ecc_a, this.ecc_b);
this.ecc_point_g = new ECPoint.Fp(this.ecc_curve, this.ecc_gx_fieldelement, this.ecc_gy_fieldelement);
this.ecc_bc_spec = new ECDomainParameters(this.ecc_curve, this.ecc_point_g, this.ecc_n);
ECKeyGenerationParameters ecc_ecgenparam;
ecc_ecgenparam = new ECKeyGenerationParameters(this.ecc_bc_spec, new SecureRandom());
this.ecc_key_pair_generator = new ECKeyPairGenerator();
this.ecc_key_pair_generator.init(ecc_ecgenparam);
}
public byte[] sm2GetZ(byte[] userId, ECPoint userKey) {
SM3Digest sm3 = new SM3Digest();
int len = userId.length * 8;
sm3.update((byte) (len >> 8 & 0xFF));
sm3.update((byte) (len & 0xFF));
sm3.update(userId, 0, userId.length);
byte[] p = Util.byteConvert32Bytes(ecc_a);
sm3.update(p, 0, p.length);
p = Util.byteConvert32Bytes(ecc_b);
sm3.update(p, 0, p.length);
p = Util.byteConvert32Bytes(ecc_gx);
sm3.update(p, 0, p.length);
p = Util.byteConvert32Bytes(ecc_gy);
sm3.update(p, 0, p.length);
p = Util.byteConvert32Bytes(userKey.getX().toBigInteger());
sm3.update(p, 0, p.length);
p = Util.byteConvert32Bytes(userKey.getY().toBigInteger());
sm3.update(p, 0, p.length);
byte[] md = new byte[sm3.getDigestSize()];
sm3.doFinal(md, 0);
return md;
}
public void sm2Sign(byte[] md, BigInteger userD, ECPoint userKey, SM2Result sm2Result) {
BigInteger e = new BigInteger(1, md);
BigInteger k = null;
ECPoint kp = null;
BigInteger r = null;
BigInteger s = null;
do {
do {
// 正式环境
/*AsymmetricCipherKeyPair keypair = ecc_key_pair_generator.generateKeyPair();
ECPrivateKeyParameters ecpriv = (ECPrivateKeyParameters) keypair.getPrivate();
ECPublicKeyParameters ecpub = (ECPublicKeyParameters) keypair.getPublic();
k = ecpriv.getD();
kp = ecpub.getQ();*/
// 国密规范测试 随机数k
String kS = "6CB28D99385C175C94F94E934817663FC176D925DD72B727260DBAAE1FB2F96F";
k = new BigInteger(kS, 16);
kp = this.ecc_point_g.multiply(k);
System.out.println("计算曲线点X1: " + kp.getX().toBigInteger().toString(16));
System.out.println("计算曲线点Y1: " + kp.getY().toBigInteger().toString(16));
System.out.println("");
// r
r = e.add(kp.getX().toBigInteger());
r = r.mod(ecc_n);
} while (r.equals(BigInteger.ZERO) || r.add(k).equals(ecc_n));
// (1 + dA)~-1
BigInteger da_1 = userD.add(BigInteger.ONE);
da_1 = da_1.modInverse(ecc_n);
// s
s = r.multiply(userD);
s = k.subtract(s).mod(ecc_n);
s = da_1.multiply(s).mod(ecc_n);
} while (s.equals(BigInteger.ZERO));
sm2Result.r = r;
sm2Result.s = s;
}
public void sm2Verify(byte md[], ECPoint userKey, BigInteger r, BigInteger s, SM2Result sm2Result) {
sm2Result.R = null;
BigInteger e = new BigInteger(1, md);
BigInteger t = r.add(s).mod(ecc_n);
if (t.equals(BigInteger.ZERO)) {
return;
} else {
ECPoint x1y1 = ecc_point_g.multiply(sm2Result.s);
System.out.println("计算曲线点X0: " + x1y1.getX().toBigInteger().toString(16));
System.out.println("计算曲线点Y0: " + x1y1.getY().toBigInteger().toString(16));
System.out.println("");
x1y1 = x1y1.add(userKey.multiply(t));
System.out.println("计算曲线点X1: " + x1y1.getX().toBigInteger().toString(16));
System.out.println("计算曲线点Y1: " + x1y1.getY().toBigInteger().toString(16));
System.out.println("");
sm2Result.R = e.add(x1y1.getX().toBigInteger()).mod(ecc_n);
System.out.println("R: " + sm2Result.R.toString(16));
return;
}
}
}
package com.security.cipher.sm;
import java.math.BigInteger;
import org.bouncycastle.math.ec.ECPoint;
public class SM2Result
{
public SM2Result() {
}
// 签名/验签
public BigInteger r;
public BigInteger s;
public BigInteger R;
// 密钥交换
public byte[] sa;
public byte[] sb;
public byte[] s1;
public byte[] s2;
public ECPoint keyra;
public ECPoint keyrb;
}
package com.security.cipher.sm;
import org.bouncycastle.asn1.ASN1EncodableVector;
import org.bouncycastle.asn1.ASN1InputStream;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.DERInteger;
import org.bouncycastle.asn1.DERObject;
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.math.ec.ECPoint;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.math.BigInteger;
import java.util.Enumeration;
public class SM2Utils
{
public static byte[] encrypt(byte[] publicKey, byte[] data) throws IOException
{
if (publicKey == null || publicKey.length == 0)
{
return null;
}
if (data == null || data.length == 0)
{
return null;
}
byte[] source = new byte[data.length];
System.arraycopy(data, 0, source, 0, data.length);
Cipher cipher = new Cipher();
SM2 sm2 = SM2.Instance();
ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);
ECPoint c1 = cipher.Init_enc(sm2, userKey);
cipher.Encrypt(source);
byte[] c3 = new byte[32];
cipher.Dofinal(c3);
// System.out.println("C1 " + Util.byteToHex(c1.getEncoded()));
// System.out.println("C2 " + Util.byteToHex(source));
// System.out.println("C3 " + Util.byteToHex(c3));
//C1 C2 C3拼装成加密字串
String encryptdata = Util.encodeHexString(c1.getEncoded())+Util.encodeHexString(source)+Util.encodeHexString(c3);
return Util.hexStringToBytes(encryptdata);
/*
DERInteger x = new DERInteger(c1.getX().toBigInteger());
DERInteger y = new DERInteger(c1.getY().toBigInteger());
DEROctetString derDig = new DEROctetString(c3);
DEROctetString derEnc = new DEROctetString(source);
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(x);
v.add(y);
v.add(derDig);
v.add(derEnc);
DERSequence seq = new DERSequence(v);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DEROutputStream dos = new DEROutputStream(bos);
dos.writeObject(seq);
return bos.toByteArray();
*/
}
public static byte[] decrypt(byte[] privateKey, byte[] encryptedData) throws IOException
{
if (privateKey == null || privateKey.length == 0)
{
return null;
}
if (encryptedData == null || encryptedData.length == 0)
{
return null;
}
//byte[] enc = new byte[encryptedData.length];
//System.arraycopy(encryptedData, 0, enc, 0, encryptedData.length);
//加密字节数组转换为十六进制的字符串 长度变为encryptedData.length * 2
String data = Util.byteToHex(encryptedData);
/***分解加密字串
* (C1 = C1标志位2位 + C1实体部分128位 = 130)
* (C3 = C3实体部分64位 = 64)
* (C2 = encryptedData.length * 2 - C1长度 - C2长度)
*/
byte[] c1Bytes = Util.hexToByte(data.substring(0,130));
int c2Len = encryptedData.length - 97;
byte[] c2 = Util.hexToByte(data.substring(130,130 + 2 * c2Len));
byte[] c3 = Util.hexToByte(data.substring(130 + 2 * c2Len,194 + 2 * c2Len));
SM2 sm2 = SM2.Instance();
BigInteger userD = new BigInteger(1, privateKey);
/*
ByteArrayInputStream bis = new ByteArrayInputStream(enc);
ASN1InputStream dis = new ASN1InputStream(bis);
DERObject derObj = dis.readObject();
ASN1Sequence asn1 = (ASN1Sequence) derObj;
DERInteger x = (DERInteger) asn1.getObjectAt(0);
DERInteger y = (DERInteger) asn1.getObjectAt(1);
ECPoint c1 = sm2.ecc_curve.createPoint(x.getValue(), y.getValue(), true);
Cipher cipher = new Cipher();
cipher.Init_dec(userD, c1);
DEROctetString data = (DEROctetString) asn1.getObjectAt(3);
enc = data.getOctets();
cipher.Decrypt(enc);
byte[] c3 = new byte[32];
cipher.Dofinal(c3);
return enc;
*/
//通过C1实体字节来生成ECPoint
ECPoint c1 = sm2.ecc_curve.decodePoint(c1Bytes);
Cipher cipher = new Cipher();
cipher.Init_dec(userD, c1);
cipher.Decrypt(c2);
cipher.Dofinal(c3);
//返回解密结果
return c2;
}
public static byte[] sign(byte[] userId, byte[] privateKey, byte[] sourceData) throws IOException
{
if (privateKey == null || privateKey.length == 0)
{
return null;
}
if (sourceData == null || sourceData.length == 0)
{
return null;
}
SM2 sm2 = SM2.Instance();
BigInteger userD = new BigInteger(privateKey);
System.out.println("userD: " + userD.toString(16));
System.out.println("");
ECPoint userKey = sm2.ecc_point_g.multiply(userD);
System.out.println("椭圆曲线点X: " + userKey.getX().toBigInteger().toString(16));
System.out.println("椭圆曲线点Y: " + userKey.getY().toBigInteger().toString(16));
System.out.println("");
SM3Digest sm3 = new SM3Digest();
byte[] z = sm2.sm2GetZ(userId, userKey);
System.out.println("SM3摘要Z: " + Util.getHexString(z));
System.out.println("");
System.out.println("M: " + Util.getHexString(sourceData));
System.out.println("");
sm3.update(z, 0, z.length);
sm3.update(sourceData, 0, sourceData.length);
byte[] md = new byte[32];
sm3.doFinal(md, 0);
System.out.println("SM3摘要值: " + Util.getHexString(md));
System.out.println("");
SM2Result sm2Result = new SM2Result();
sm2.sm2Sign(md, userD, userKey, sm2Result);
System.out.println("r: " + sm2Result.r.toString(16));
System.out.println("s: " + sm2Result.s.toString(16));
System.out.println("");
DERInteger d_r = new DERInteger(sm2Result.r);
DERInteger d_s = new DERInteger(sm2Result.s);
ASN1EncodableVector v2 = new ASN1EncodableVector();
v2.add(d_r);
v2.add(d_s);
DERObject sign = new DERSequence(v2);
byte[] signdata = sign.getDEREncoded();
return signdata;
}
@SuppressWarnings("unchecked")
public static boolean verifySign(byte[] userId, byte[] publicKey, byte[] sourceData, byte[] signData) throws IOException
{
if (publicKey == null || publicKey.length == 0)
{
return false;
}
if (sourceData == null || sourceData.length == 0)
{
return false;
}
SM2 sm2 = SM2.Instance();
ECPoint userKey = sm2.ecc_curve.decodePoint(publicKey);
SM3Digest sm3 = new SM3Digest();
byte[] z = sm2.sm2GetZ(userId, userKey);
sm3.update(z, 0, z.length);
sm3.update(sourceData, 0, sourceData.length);
byte[] md = new byte[32];
sm3.doFinal(md, 0);
System.out.println("SM3摘要值: " + Util.getHexString(md));
System.out.println("");
ByteArrayInputStream bis = new ByteArrayInputStream(signData);
ASN1InputStream dis = new ASN1InputStream(bis);
DERObject derObj = dis.readObject();
Enumeration e = ((ASN1Sequence) derObj).getObjects();
BigInteger r = ((DERInteger)e.nextElement()).getValue();
BigInteger s = ((DERInteger)e.nextElement()).getValue();
SM2Result sm2Result = new SM2Result();
sm2Result.r = r;
sm2Result.s = s;
System.out.println("r: " + sm2Result.r.toString(16));
System.out.println("s: " + sm2Result.s.toString(16));
System.out.println("");
sm2.sm2Verify(md, userKey, sm2Result.r, sm2Result.s, sm2Result);
return sm2Result.r.equals(sm2Result.R);
}
/*
public static void main(String[] args) throws Exception
{
String plainText = "message digest";
byte[] sourceData = plainText.getBytes();
// 国密规范测试私钥
String prik = "128B2FA8BD433C6C068C8D803DFF79792A519A55171B1B650C23661D15897263";
String prikS = new String(Base64.encode(Util.hexToByte(prik)));
System.out.println("prikS: " + prikS);
System.out.println("");
// 国密规范测试用户ID
String userId = "[email protected]";
System.out.println("ID: " + Util.getHexString(userId.getBytes()));
System.out.println("");
System.out.println("签名: ");
byte[] c = SM2Utils.sign(userId.getBytes(), Base64.decode(prikS.getBytes()), sourceData);
System.out.println("sign: " + Util.getHexString(c));
System.out.println("");
// 国密规范测试公钥
String pubk = "040AE4C7798AA0F119471BEE11825BE46202BB79E2A5844495E97C04FF4DF2548A7C0240F88F1CD4E16352A73C17B7F16F07353E53A176D684A9FE0C6BB798E857";
String pubkS = new String(Base64.encode(Util.hexToByte(pubk)));
System.out.println("pubkS: " + pubkS);
System.out.println("");
System.out.println("验签: ");
boolean vs = SM2Utils.verifySign(userId.getBytes(), Base64.decode(pubkS.getBytes()), sourceData, c);
System.out.println("验签结果: " + vs);
System.out.println("");
System.out.println("加密: ");
byte[] cipherText = SM2Utils.encrypt(Base64.decode(pubkS.getBytes()), sourceData);
System.out.println(new String(Base64.encode(cipherText)));
System.out.println("");
System.out.println("解密: ");
plainText = new String(SM2Utils.decrypt(Base64.decode(prikS.getBytes()), cipherText));
System.out.println(plainText);
}
*/
public static void SM2_TEST() throws Exception
{
String plainText = "1122334455667788";
byte[] sourceData = Util.hexStringToBytes(plainText);
// 国密规范测试私钥
String prik = "969FC0F73FA117A040B37D5B5018382A74D40590EAA02809B87FA09196F8276D";
// 国密规范测试公钥
String pubk = "04ABC2230A05A72CEB667B20019C4F2A580E4D0A3BE9D20BF914565AB3B82631E1C0E15803FA3ADE3E6D9EEF293CBD8BAECC51D82B61404A39584198B6985686FB";
System.out.println("加密: ");
byte[] cipherText = SM2Utils.encrypt(Util.hexStringToBytes(pubk), sourceData);
System.out.println(Util.encodeHexString(cipherText));
System.out.println("");
System.out.println("解密: ");
String data = "04ADE0AFDD137B5E9B2CF3F4D71D329E06F3E8006598A12BB8B6A4B31F8E1D2266EFB1015812E10DC058940A3C8AB8BA29FFE788F85A5D236C3526BBA8D0E0A10D5806DAE0C2DEFADC1A49CE657D4311CDF65D9F38F1CF5004F2E4BA922EA538C9E75007CA0C7AADD8";
plainText = Util.encodeHexString(SM2Utils.decrypt(Util.hexStringToBytes(prik), Util.hexStringToBytes(data)));
System.out.println(plainText);
}
}
数据类型转换工具
package com.security.cipher.sm;
import java.math.BigInteger;
public class Util
{
/**
* 整形转换成网络传输的字节流(字节数组)型数据
*
* @param num 一个整型数据
* @return 4个字节的自己数组
*/
public static byte[] intToBytes(int num)
{
byte[] bytes = new byte[4];
bytes[0] = (byte) (0xff & (num >> 0));
bytes[1] = (byte) (0xff & (num >> 8));
bytes[2] = (byte) (0xff & (num >> 16));
bytes[3] = (byte) (0xff & (num >> 24));
return bytes;
}
/**
* 四个字节的字节数据转换成一个整形数据
*
* @param bytes 4个字节的字节数组
* @return 一个整型数据
*/
public static int byteToInt(byte[] bytes)
{
int num = 0;
int temp;
temp = (0x000000ff & (bytes[0])) << 0;
num = num | temp;
temp = (0x000000ff & (bytes[1])) << 8;
num = num | temp;
temp = (0x000000ff & (bytes[2])) << 16;
num = num | temp;
temp = (0x000000ff & (bytes[3])) << 24;
num = num | temp;
return num;
}
/**
* 长整形转换成网络传输的字节流(字节数组)型数据
*
* @param num 一个长整型数据
* @return 4个字节的自己数组
*/
public static byte[] longToBytes(long num)
{
byte[] bytes = new byte[8];
for (int i = 0; i < 8; i++)
{
bytes[i] = (byte) (0xff & (num >> (i * 8)));
}
return bytes;
}
/**
* 大数字转换字节流(字节数组)型数据
*
* @param n
* @return
*/
public static byte[] byteConvert32Bytes(BigInteger n)
{
byte tmpd[] = (byte[])null;
if(n == null)
{
return null;
}
if(n.toByteArray().length == 33)
{
tmpd = new byte[32];
System.arraycopy(n.toByteArray(), 1, tmpd, 0, 32);
}
else if(n.toByteArray().length == 32)
{
tmpd = n.toByteArray();
}
else
{
tmpd = new byte[32];
for(int i = 0; i < 32 - n.toByteArray().length; i++)
{
tmpd[i] = 0;
}
System.arraycopy(n.toByteArray(), 0, tmpd, 32 - n.toByteArray().length, n.toByteArray().length);
}
return tmpd;
}
/**
* 换字节流(字节数组)型数据转大数字
*
* @param b
* @return
*/
public static BigInteger byteConvertInteger(byte[] b)
{
if (b[0] < 0)
{
byte[] temp = new byte[b.length + 1];
temp[0] = 0;
System.arraycopy(b, 0, temp, 1, b.length);
return new BigInteger(temp);
}
return new BigInteger(b);
}
/**
* 根据字节数组获得值(十六进制数字)
*
* @param bytes
* @return
*/
public static String getHexString(byte[] bytes)
{
return getHexString(bytes, true);
}
/**
* 根据字节数组获得值(十六进制数字)
*
* @param bytes
* @param upperCase
* @return
*/
public static String getHexString(byte[] bytes, boolean upperCase)
{
String ret = "";
for (int i = 0; i < bytes.length; i++)
{
ret += Integer.toString((bytes[i] & 0xff) + 0x100, 16).substring(1);
}
return upperCase ? ret.toUpperCase() : ret;
}
/**
* 打印十六进制字符串
*
* @param bytes
*/
public static void printHexString(byte[] bytes)
{
for (int i = 0; i < bytes.length; i++)
{
String hex = Integer.toHexString(bytes[i] & 0xFF);
if (hex.length() == 1)
{
hex = '0' + hex;
}
System.out.print("0x" + hex.toUpperCase() + ",");
}
System.out.println("");
}
/**
* Convert hex string to byte[]
*
* @param hexString
* the hex string
* @return byte[]
*/
public static byte[] hexStringToBytes(String hexString)
{
if (hexString == null || hexString.equals(""))
{
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++)
{
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
/**
* Convert char to byte
*
* @param c
* char
* @return byte
*/
public static byte charToByte(char c)
{
return (byte) "0123456789ABCDEF".indexOf(c);
}
/**
* 用于建立十六进制字符的输出的小写字符数组
*/
private static final char[] DIGITS_LOWER = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
/**
* 用于建立十六进制字符的输出的大写字符数组
*/
private static final char[] DIGITS_UPPER = {'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data) {
return encodeHex(data, true);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toLowerCase true
传换成小写格式 , false
传换成大写格式
* @return 十六进制char[]
*/
public static char[] encodeHex(byte[] data, boolean toLowerCase) {
return encodeHex(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* 将字节数组转换为十六进制字符数组
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制char[]
*/
protected static char[] encodeHex(byte[] data, char[] toDigits) {
int l = data.length;
char[] out = new char[l << 1];
// two characters form the hex value.
for (int i = 0, j = 0; i < l; i++) {
out[j++] = toDigits[(0xF0 & data[i]) >>> 4];
out[j++] = toDigits[0x0F & data[i]];
}
return out;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @return 十六进制String
*/
public static String encodeHexString(byte[] data) {
return encodeHexString(data, true);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toLowerCase true
传换成小写格式 , false
传换成大写格式
* @return 十六进制String
*/
public static String encodeHexString(byte[] data, boolean toLowerCase) {
return encodeHexString(data, toLowerCase ? DIGITS_LOWER : DIGITS_UPPER);
}
/**
* 将字节数组转换为十六进制字符串
*
* @param data byte[]
* @param toDigits 用于控制输出的char[]
* @return 十六进制String
*/
protected static String encodeHexString(byte[] data, char[] toDigits) {
return new String(encodeHex(data, toDigits));
}
/**
* 将十六进制字符数组转换为字节数组
*
* @param data 十六进制char[]
* @return byte[]
* @throws RuntimeException 如果源十六进制字符数组是一个奇怪的长度,将抛出运行时异常
*/
public static byte[] decodeHex(char[] data) {
int len = data.length;
if ((len & 0x01) != 0) {
throw new RuntimeException("Odd number of characters.");
}
byte[] out = new byte[len >> 1];
// two characters form the hex value.
for (int i = 0, j = 0; j < len; i++) {
int f = toDigit(data[j], j) << 4;
j++;
f = f | toDigit(data[j], j);
j++;
out[i] = (byte) (f & 0xFF);
}
return out;
}
/**
* 将十六进制字符转换成一个整数
*
* @param ch 十六进制char
* @param index 十六进制字符在字符数组中的位置
* @return 一个整数
* @throws RuntimeException 当ch不是一个合法的十六进制字符时,抛出运行时异常
*/
protected static int toDigit(char ch, int index) {
int digit = Character.digit(ch, 16);
if (digit == -1) {
throw new RuntimeException("Illegal hexadecimal character " + ch
+ " at index " + index);
}
return digit;
}
/**
* 数字字符串转ASCII码字符串
*
* @param String
* 字符串
* @return ASCII字符串
*/
public static String StringToAsciiString(String content) {
String result = "";
int max = content.length();
for (int i = 0; i < max; i++) {
char c = content.charAt(i);
String b = Integer.toHexString(c);
result = result + b;
}
return result;
}
/**
* 十六进制转字符串
*
* @param hexString
* 十六进制字符串
* @param encodeType
* 编码类型4:Unicode,2:普通编码
* @return 字符串
*/
public static String hexStringToString(String hexString, int encodeType) {
String result = "";
int max = hexString.length() / encodeType;
for (int i = 0; i < max; i++) {
char c = (char) hexStringToAlgorism(hexString
.substring(i * encodeType, (i + 1) * encodeType));
result += c;
}
return result;
}
/**
* 十六进制字符串装十进制
*
* @param hex
* 十六进制字符串
* @return 十进制数值
*/
public static int hexStringToAlgorism(String hex) {
hex = hex.toUpperCase();
int max = hex.length();
int result = 0;
for (int i = max; i > 0; i--) {
char c = hex.charAt(i - 1);
int algorism = 0;
if (c >= '0' && c <= '9') {
algorism = c - '0';
} else {
algorism = c - 55;
}
result += Math.pow(16, max - i) * algorism;
}
return result;
}
/**
* 十六转二进制
*
* @param hex
* 十六进制字符串
* @return 二进制字符串
*/
public static String hexStringToBinary(String hex) {
hex = hex.toUpperCase();
String result = "";
int max = hex.length();
for (int i = 0; i < max; i++) {
char c = hex.charAt(i);
switch (c) {
case '0':
result += "0000";
break;
case '1':
result += "0001";
break;
case '2':
result += "0010";
break;
case '3':
result += "0011";
break;
case '4':
result += "0100";
break;
case '5':
result += "0101";
break;
case '6':
result += "0110";
break;
case '7':
result += "0111";
break;
case '8':
result += "1000";
break;
case '9':
result += "1001";
break;
case 'A':
result += "1010";
break;
case 'B':
result += "1011";
break;
case 'C':
result += "1100";
break;
case 'D':
result += "1101";
break;
case 'E':
result += "1110";
break;
case 'F':
result += "1111";
break;
}
}
return result;
}
/**
* ASCII码字符串转数字字符串
*
* @param String
* ASCII字符串
* @return 字符串
*/
public static String AsciiStringToString(String content) {
String result = "";
int length = content.length() / 2;
for (int i = 0; i < length; i++) {
String c = content.substring(i * 2, i * 2 + 2);
int a = hexStringToAlgorism(c);
char b = (char) a;
String d = String.valueOf(b);
result += d;
}
return result;
}
/**
* 将十进制转换为指定长度的十六进制字符串
*
* @param algorism
* int 十进制数字
* @param maxLength
* int 转换后的十六进制字符串长度
* @return String 转换后的十六进制字符串
*/
public static String algorismToHexString(int algorism, int maxLength) {
String result = "";
result = Integer.toHexString(algorism);
if (result.length() % 2 == 1) {
result = "0" + result;
}
return patchHexString(result.toUpperCase(), maxLength);
}
/**
* 字节数组转为普通字符串(ASCII对应的字符)
*
* @param bytearray
* byte[]
* @return String
*/
public static String byteToString(byte[] bytearray) {
String result = "";
char temp;
int length = bytearray.length;
for (int i = 0; i < length; i++) {
temp = (char) bytearray[i];
result += temp;
}
return result;
}
/**
* 二进制字符串转十进制
*
* @param binary
* 二进制字符串
* @return 十进制数值
*/
public static int binaryToAlgorism(String binary) {
int max = binary.length();
int result = 0;
for (int i = max; i > 0; i--) {
char c = binary.charAt(i - 1);
int algorism = c - '0';
result += Math.pow(2, max - i) * algorism;
}
return result;
}
/**
* 十进制转换为十六进制字符串
*
* @param algorism
* int 十进制的数字
* @return String 对应的十六进制字符串
*/
public static String algorismToHEXString(int algorism) {
String result = "";
result = Integer.toHexString(algorism);
if (result.length() % 2 == 1) {
result = "0" + result;
}
result = result.toUpperCase();
return result;
}
/**
* HEX字符串前补0,主要用于长度位数不足。
*
* @param str
* String 需要补充长度的十六进制字符串
* @param maxLength
* int 补充后十六进制字符串的长度
* @return 补充结果
*/
static public String patchHexString(String str, int maxLength) {
String temp = "";
for (int i = 0; i < maxLength - str.length(); i++) {
temp = "0" + temp;
}
str = (temp + str).substring(0, maxLength);
return str;
}
/**
* 将一个字符串转换为int
*
* @param s
* String 要转换的字符串
* @param defaultInt
* int 如果出现异常,默认返回的数字
* @param radix
* int 要转换的字符串是什么进制的,如16 8 10.
* @return int 转换后的数字
*/
public static int parseToInt(String s, int defaultInt, int radix) {
int i = 0;
try {
i = Integer.parseInt(s, radix);
} catch (NumberFormatException ex) {
i = defaultInt;
}
return i;
}
/**
* 将一个十进制形式的数字字符串转换为int
*
* @param s
* String 要转换的字符串
* @param defaultInt
* int 如果出现异常,默认返回的数字
* @return int 转换后的数字
*/
public static int parseToInt(String s, int defaultInt) {
int i = 0;
try {
i = Integer.parseInt(s);
} catch (NumberFormatException ex) {
i = defaultInt;
}
return i;
}
/**
* 十六进制串转化为byte数组
*
* @return the array of byte
*/
public static byte[] hexToByte(String hex)
throws IllegalArgumentException {
if (hex.length() % 2 != 0) {
throw new IllegalArgumentException();
}
char[] arr = hex.toCharArray();
byte[] b = new byte[hex.length() / 2];
for (int i = 0, j = 0, l = hex.length(); i < l; i++, j++) {
String swap = "" + arr[i++] + arr[i];
int byteint = Integer.parseInt(swap, 16) & 0xFF;
b[j] = new Integer(byteint).byteValue();
}
return b;
}
/**
* 字节数组转换为十六进制字符串
*
* @param b
* byte[] 需要转换的字节数组
* @return String 十六进制字符串
*/
public static String byteToHex(byte b[]) {
if (b == null) {
throw new IllegalArgumentException(
"Argument b ( byte array ) is null! ");
}
String hs = "";
String stmp = "";
for (int n = 0; n < b.length; n++) {
stmp = Integer.toHexString(b[n] & 0xff);
if (stmp.length() == 1) {
hs = hs + "0" + stmp;
} else {
hs = hs + stmp;
}
}
return hs.toUpperCase();
}
public static byte[] subByte(byte[] input, int startIndex, int length) {
byte[] bt = new byte[length];
for (int i = 0; i < length; i++) {
bt[i] = input[i + startIndex];
}
return bt;
}
}
文件与字节数组转换工具
package com.security.cipher.sm;
import java.io.*;
/**
* @Description:
* @Author: 陈欢
* @Date: 2018/10/24 10:56
*/
public class FileToByteArray {
public static byte[] file2ByteArray(String filePath) {
File file = new File(filePath);
if (!file.exists()) {
return null;
}
FileInputStream stream = null;
ByteArrayOutputStream out = null;
try {
stream = new FileInputStream(file);
out = new ByteArrayOutputStream(1000);
byte[] b = new byte[1000];
int n;
while ((n = stream.read(b)) != -1) {
out.write(b, 0, n);
}
return out.toByteArray(); // 此方法大文件OutOfMemory
} catch (Exception e) {
System.out.println(e.toString());
} finally {
try {
stream.close();
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
public static byte[] fileToBetyArray(String filePath) {
FileInputStream fileInputStream = null;
File file = new File(filePath);
byte[] bFile = null;
try {
bFile = new byte[(int) file.length()];
fileInputStream = new FileInputStream(file);
fileInputStream.read(bFile);
fileInputStream.close();
for (int i = 0; i < bFile.length; i++) {
System.out.print((char) bFile[i]);
}
System.out.println("Done");
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fileInputStream.close();
bFile.clone();
} catch (IOException e) {
e.printStackTrace();
}
}
return bFile;
}
public static File BetyToFile(String filePath, byte[] data) {
File file = new File(filePath);
BufferedOutputStream stream = null;
FileOutputStream fstream = null;
//byte[] data = new byte[(int) file.length()];
try {
fstream = new FileOutputStream(file);
stream = new BufferedOutputStream(fstream);
stream.write(data);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (stream != null) {
stream.close();
}
if (null != fstream) {
fstream.close();
}
} catch (IOException e1) {
e1.printStackTrace();
}
}
return file;
}
public static byte[] file2buf(File fobj) {
byte[] buffer = null;
try {
if (!fobj.exists()) {
return null;
}
FileInputStream fis = new FileInputStream(fobj);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
byte[] b = new byte[1024];
int len = -1;
while ((len = fis.read(b)) != -1) {
bos.write(b, 0, len);
}
fis.close();
bos.close();
buffer = bos.toByteArray();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return buffer;
}
}
至此, 一个完整的SM2算法的加密解密就测试完了
本文章国密算法知识转载自: https://www.cnblogs.com/blankicefire/p/9129202.html, 侵权请联系删除