SM2签名结果的分析(ASN1、142、128)
0.背景
1.格式
在准则中可以看到,签名计算的最后一步是将两个数字转换为字符串。
SM2签名的长度为128位(R+S = 64+64 = 128),有时候我们看到的不止128位,多半是因为做了ASN1格式转换。
2.分析
下方以软加密和加密机签名的结果做分析:
// 软加密签名结果142
3045022100d596d18be77035b0bb9ef6abf232e9e81f2df3178bedd56d64220dc72c6883a602201b92ddc4c167e22956e5ef32ce19bf4c05f9d6d96aa82c41ace0ba28acba8715
// 加密机签名结果128
d596d18be77035b0bb9ef6abf232e9e81f2df3178bedd56d64220dc72c6883a61b92ddc4c167e22956e5ef32ce19bf4c05f9d6d96aa82c41ace0ba28acba8715
可以看到,上方代码计算结果长度为142,加密机为128。
咦,连长度都对不上。
这里提前给出结果,下方将描述如何转换:
// 142位的是ASN1的格式(der)
3045022100d596d18be77035b0bb9ef6abf232e9e81f2df3178bedd56d64220dc72c6883a602201b92ddc4c167e22956e5ef32ce19bf4c05f9d6d96aa82c41ace0ba28acba8715
// 128位的是裸签名格式(raw)
d596d18be77035b0bb9ef6abf232e9e81f2df3178bedd56d64220dc72c6883a61b92ddc4c167e22956e5ef32ce19bf4c05f9d6d96aa82c41ace0ba28acba8715
2.1 ASN1实体类
由于SM2的签名结果是由两个数字拼接构成,构建时我们使用ASN1Integer。
package cn.yang37.entity.asn1;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.bouncycastle.asn1.*;
/**
* @description: R+S
* @class: SM2Sign
* @author: yang37z@qq.com
* @date: 2024/7/10 16:19
* @version: 1.0
*/
@Data
@Builder
@AllArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class SM2SignASN1 extends ASN1Object {
private ASN1Integer int1;
private ASN1Integer int2;
@Override
public ASN1Primitive toASN1Primitive() {
ASN1EncodableVector vector = new ASN1EncodableVector();
vector.add(int1);
vector.add(int2);
return new DERSequence(vector);
}
}
2.2 raw -> der
对于数据
d596d18be77035b0bb9ef6abf232e9e81f2df3178bedd56d64220dc72c6883a61b92ddc4c167e22956e5ef32ce19bf4c05f9d6d96aa82c41ace0ba28acba8715
由128拆分为两个64位长度的16进制数,利用BigInteger构建咱们的ASN1格式数据即可。
d596d18be77035b0bb9ef6abf232e9e81f2df3178bedd56d64220dc72c6883a6
1b92ddc4c167e22956e5ef32ce19bf4c05f9d6d96aa82c41ace0ba28acba8715
参考方法:SM2SignRaw2DerUtils.raw2Der()
package cn.yang37.sm2;
import cn.yang37.entity.asn1.SM2SignASN1;
import cn.yang37.utils.HexUtils;
import lombok.extern.slf4j.Slf4j;
import org.bouncycastle.asn1.*;
import java.math.BigInteger;
/**
* @description:
* @class: SM2SignRow2DerUtils
* @author: yang37z@qq.com
* @date: 2024/7/10 16:39
* @version: 1.0
*/
@Slf4j
public class SM2SignRaw2DerUtils {
/**
* (hex) 128位长度的裸签名 -> 142长度的der签名
*
* @param rawHex128 .
* @return .
*/
public static String raw2Der(String rawHex128) {
String res = "";
try {
BigInteger bigInteger1 = new BigInteger(rawHex128.substring(0, 64), 16);
BigInteger bigInteger2 = new BigInteger(rawHex128.substring(64, 128), 16);
SM2SignASN1 sm2SignASN1 = SM2SignASN1.builder()
.int1(new ASN1Integer(bigInteger1))
.int2(new ASN1Integer(bigInteger2))
.build();
res = HexUtils.byteArr2Hex(sm2SignASN1.toASN1Primitive().getEncoded());
} catch (Exception e) {
log.error("[Hex] raw -> der,error!", e);
}
return res.toUpperCase();
}
/**
* (hex) 142长度的der签名 -> 128位长度的裸签名
*
* @param derHex142 .
* @return .
*/
public static String der2Raw(String derHex142) {
String res = "";
StringBuilder sb = new StringBuilder();
try {
byte[] decoded = HexUtils.hex2ByteArr(derHex142);
try (ASN1InputStream ais = new ASN1InputStream(decoded)) {
ASN1Primitive primitive = ais.readObject();
if (primitive instanceof ASN1Sequence) {
ASN1Sequence sequence = (ASN1Sequence) primitive;
for (ASN1Encodable encodable : sequence) {
ASN1Primitive asn1Primitive = encodable.toASN1Primitive();
if (asn1Primitive instanceof ASN1Integer) {
BigInteger value = ((ASN1Integer) asn1Primitive).getValue();
sb.append(String.format("%064x", value));
}
}
}
}
res = sb.toString();
} catch (Exception e) {
log.error("[Hex] der -> raw,error!", e);
}
return res.toUpperCase();
}
}
输出:
[Hex] raw -> der: 3045022100D596D18BE77035B0BB9EF6ABF232E9E81F2DF3178BEDD56D64220DC72C6883A602201B92DDC4C167E22956E5EF32CE19BF4C05F9D6D96AA82C41ACE0BA28ACBA8715
2.3 der -> raw
上方的ASN1格式数据也可以解析回去,下面是一个demo:
注意40行,这里是因为SM2签名是两个整数组成,所以我们用的是ASN1Integer在构建,然后把结果数据做了16进制转换。
不是所有的ASN1都应当这样操作,要结合实体类分析,即2.2节的SM2SignASN1。
又比如SM2数字信封是4部分组成,看我的这个问题:SM2加密结果转ASN1格式时如何构造DerOctetString?
参考方法:SM2SignRaw2DerUtils.der2Raw()
输出:
[Hex] der -> raw: D596D18BE77035B0BB9EF6ABF232E9E81F2DF3178BEDD56D64220DC72C6883A61B92DDC4C167E22956E5EF32CE19BF4C05F9D6D96AA82C41ACE0BA28ACBA8715
3.代码
具体的代码,参考这个仓库:easy-cryptography
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 解答了困扰我五年的技术问题。时代确实变了!
· PPT革命!DeepSeek+Kimi=N小时工作5分钟完成?
· What?废柴, 还在本地部署DeepSeek吗?Are you kidding?
· DeepSeek企业级部署实战指南:从服务器选型到Dify私有化落地
· 程序员转型AI:行业分析