SM2工具类
GB35114 SM2 封装 ,基于 bouncycastle
和 hutool-crypto
# 导入依赖
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk18on</artifactId>
<version>1.78.1</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-crypto</artifactId>
<version>5.8.24</version>
</dependency>
# 说明
GB35114 签名示例
SIGN1 示例: SM2(random2+random1+ serverid)
SIGN2 示例: SM2(random1+random2+deviceid+cryptkey)
其中 cryptkey: encodeBase64(encodeAsn1(encryptSM2(vkek)))
# Sm2Util
查看代码
@Slf4j
public class Sm2Util {
private static SM2 sm2;
/**
* 初始化
* @param pub 16进制公钥
* @param pir 16进制私钥
*/
public static void init(String pub, String pir) {
if(StrUtil.isBlankIfStr(pub) || StrUtil.isBlankIfStr(pir)){
return;
}
ECPrivateKeyParameters pr = BCUtil.toSm2Params(pir);
ECPublicKeyParameters pu = BCUtil.toSm2Params(pub.substring(0, 64), pub.substring(64, 128));
sm2 = new SM2(pr,pu);
}
/**
* 签名
* @param data 加密数据
* @return 签名
*/
public static String sign(String data) {
byte[] sign = sm2.sign(data.getBytes(StandardCharsets.UTF_8));
return Base64.encode(sign);
}
/**
* 验签
* @param data 签名后的数据
* @param sign 签名
* @return 是否验证通过
*/
public static boolean verify(String data, String sign) {
byte[] decode = Base64.decode(sign);
return sm2.verify(data.getBytes(StandardCharsets.UTF_8),decode);
}
/**
* 验签
* @param data 签名后的数据
* @param sign 签名
* @param publicKey 对方公钥
* @return 是否验证通过
*/
public static boolean verify(String data, String sign, PublicKey publicKey) {
if(null == publicKey){
return false;
}
SM2 sm2 = new SM2(null,publicKey);
byte[] decode = Base64.decode(sign);
return sm2.verify(data.getBytes(StandardCharsets.UTF_8),decode);
}
/**
* 加密
* @param data 原数据
* @return 加密字符
*/
public static String encrypt(String data) {
try {
//得到加密前数据byte
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
//得到原始密文
byte[] encrypt = sm2.encrypt(bytes);
//为原始密文添加 ASN1 编码
byte[] asn1byte = encodeAsn1Cipher(encrypt, SM2Engine.Mode.C1C3C2);
return Base64.encode(asn1byte);
} catch (Exception e) {
throw new RuntimeException("信息加密失败,请检查秘钥",e);
}
}
/**
* 加密
* @param data 原数据
* @return 加密字符
*/
public static String encrypt(String data,PublicKey publicKey) {
try {
byte[] bytes = data.getBytes(StandardCharsets.UTF_8);
SM2 sm2 = new SM2(null,publicKey);
byte[] encrypt = sm2.encrypt(bytes);
return Base64.encode(encrypt);
} catch (Exception e) {
throw new RuntimeException("信息加密失败,请检查秘钥",e);
}
}
/**
* 解密
* @param cipherText 密文
* @return 解密字符
*/
public static String decrypt(String cipherText) {
try {
//base 解码数据
byte[] decode = Base64.decode(cipherText);
//解码 ASN1 编码的密文
byte[] data = decodeAsn1Cipher(decode, SM2Engine.Mode.C1C3C2);
//解码原密文
byte[] bytes = sm2.decrypt(data);
return new String(bytes, StandardCharsets.UTF_8);
} catch (Exception e) {
throw new RuntimeException("信息解密失败",e);
}
}
/**
* SM2 标准曲线
*/
private static final X9ECParameters X9_EC_PARAMETERS = GMNamedCurves.getByName("sm2p256v1");
/**
* 解码 ASN1 编码的密文
* @param asn1Cipher DER 编码的密文
* @return 原始密文
*/
public static byte[] decodeAsn1Cipher(byte[] asn1Cipher, SM2Engine.Mode mode) {
ASN1Sequence sequence;
try {
sequence = (ASN1Sequence) DERSequence.fromByteArray(asn1Cipher);
} catch (IOException e) {
throw new RuntimeException(e);
}
BigInteger x = ASN1Integer.getInstance(sequence.getObjectAt(0)).getPositiveValue();
BigInteger y = ASN1Integer.getInstance(sequence.getObjectAt(1)).getPositiveValue();
byte[] c1 = X9_EC_PARAMETERS.getCurve().validatePoint(x, y).getEncoded(false);
byte[] c2, c3;
if (mode == SM2Engine.Mode.C1C3C2) {
c3 = ((ASN1OctetString) sequence.getObjectAt(2)).getOctets();
c2 = ((ASN1OctetString) sequence.getObjectAt(3)).getOctets();
} else {
c2 = ((ASN1OctetString) sequence.getObjectAt(2)).getOctets();
c3 = ((ASN1OctetString) sequence.getObjectAt(3)).getOctets();
}
byte[] plainCipher = new byte[c1.length + c2.length + c3.length];
System.arraycopy(c1, 0, plainCipher, 0, c1.length);
if (mode == SM2Engine.Mode.C1C3C2) {
System.arraycopy(c3, 0, plainCipher, c1.length, c3.length);
System.arraycopy(c2, 0, plainCipher, c1.length + c3.length, c2.length);
} else {
System.arraycopy(c2, 0, plainCipher, c1.length, c2.length);
System.arraycopy(c3, 0, plainCipher, c1.length + c2.length, c3.length);
}
return plainCipher;
}
/**
* 为原始密文添加 ASN1 编码
*
* @param plainCipher 原始密文
* @return ASN1 编码的格式的密文
*/
public static byte[] encodeAsn1Cipher(byte[] plainCipher, SM2Engine.Mode mode) {
// sm2p256v1 的这个固定 65。可以看 GMNamedCurves、ECCurve 代码
final int c1Len = (X9_EC_PARAMETERS.getCurve().getFieldSize() + 7) / 8 * 2 + 1;
byte[] c1 = new byte[c1Len];
System.arraycopy(plainCipher, 0, c1, 0, c1Len);
BigInteger x = X9_EC_PARAMETERS.getCurve().decodePoint(c1).getXCoord().toBigInteger();
BigInteger y = X9_EC_PARAMETERS.getCurve().decodePoint(c1).getYCoord().toBigInteger();
// 长度为 new SM3Digest().getDigestSize()
final int c3Len = 32;
byte[] c3 = new byte[c3Len];
final int c2Len = plainCipher.length - c1Len - c3Len;
byte[] c2 = new byte[c2Len];
ASN1EncodableVector v = new ASN1EncodableVector();
v.add(new ASN1Integer(x));
v.add(new ASN1Integer(y));
if (mode == SM2Engine.Mode.C1C3C2) {
System.arraycopy(plainCipher, c1.length, c3, 0, c3Len);
System.arraycopy(plainCipher, c1Len + c3Len, c2, 0, c2Len);
v.add(new DEROctetString(c3));
v.add(new DEROctetString(c2));
} else {
System.arraycopy(plainCipher, plainCipher.length - c3Len, c3, 0, c3Len);
System.arraycopy(plainCipher, c1Len, c2, 0, c2Len);
v.add(new DEROctetString(c2));
v.add(new DEROctetString(c3));
}
try {
return new DERSequence(v).getEncoded(ASN1Encoding.DER);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
# GB35114 SM2签名
查看代码
@Slf4j
public class GB35114SignUtil {
/**
* 进行 sign1签名
* sign(random2+random1+ serverid)
* @param r2 FDWSF生成随机数
* @param r1 SIP服务器随机数
* @param serverId SIP服务器ID
* @return 签名数据
*/
public static String genSign1(String r2, String r1, String serverId) {
return Sm2Util.sign( sign1Format(r2, r1, serverId));
}
/**
* sign 数据组装
* (random2+random1+ serverId)
* @param r2 FDWSF生成随机数
* @param r1 SIP服务器随机数
* @param serverId SIP服务器ID
*/
private static String sign1Format(String r2, String r1, String serverId) {
return String.format("%s%s%s", r2, r1, serverId);
}
/**
* 验证签名
* @param r1 SIP服务器随机数
* @param r2 FDWSF生成随机数
* @param serverId SIP服务器ID
* @param sign1 sign1
* @param request SIP Request
* @param error 失败处理
*/
public static void vifSign(String r1, String r2, String serverId, String sign1,
SIPRequest request,
Consumer<SIPRequest> error
) {
if (Sm2Util.verify(sign1Format(r2,r1, serverId),sign1)) {
return;
}
error.accept(request);
log.error("验签失败");
throw new RuntimeException("验签失败");
}
/**
* 进行 sign2签名
* sign(random1+random2+deviceid+cryptkey)
* @param r1 SIP服务器随机数
* @param r2 FDWSF生成随机数
* @param deviceId SIP服务器ID
* @param cryptkey base64(VKEK公钥加密)
* @return 签名数据
*/
public static String genSign2(String r1, String r2, String deviceId, String cryptkey) {
String format = sign2Format(r1, r2, deviceId, cryptkey);
log.info("预签名数据:{}", format);
//sign(random1+random2+deviceid+cryptkey)
return Sm2Util.sign(format);
}
/**
* sign2 数据组装
* (random2+random1+ serverid)
* @param r1 SIP服务器随机数
* @param r2 FDWSF生成随机数
* @param deviceId 设备ID
* @param cryptkey cryptkey
*/
private static String sign2Format(String r1, String r2, String deviceId, String cryptkey) {
return String.format("%s%s%s%s", r1, r2, deviceId, cryptkey);
}
/**
* 进行 sign2验证
* (random2+random1+ serverid)
* @param r1 SIP服务器随机数
* @param r2 FDWSF生成随机数
* @param deviceId 设备ID
* @param cryptkey cryptkey
* @param sign2 sign2
* @param request SIP Request
* @param error 失败处理
*/
public static void vifSig2(String r1, String r2, String deviceId, String cryptkey,String sign2,
SIPMessage request,
Consumer<SIPMessage> error
) {
if (Sm2Util.verify(sign2Format(r1, r2, deviceId,cryptkey),sign2)) {
return;
}
error.accept(request);
log.error("验签失败");
throw new RuntimeException("验签失败");
}
}
上次更新: 2024/11/05, 08:29:31