Java解析ECDSA DER签名提取R/S生成COSE签名异常问题咨询
问题原因及修复方案
根因分析
- 硬编码DER结构偏移:你当前代码默认DER SEQUENCE的长度字段为1字节(短编码形式),仅适用于总长度<=127的DER签名。P-256、P-384的最长DER签名长度分别为72、104,都小于127,所以运行正常;P-521的最长DER签名长度可达140,超过127时会使用长编码形式的长度字段(占2字节),此时硬编码的
rSize = signature[3]实际读到的是R字段的tag值0x02,直接导致长度读取错误,解析出来的R、S长度异常,刚好符合你提到的「仅半数场景可运行、输出长度过短」的表现。 - 缺少固定长度补全逻辑:COSE规范要求ECDSA签名的R、S值必须为和曲线阶匹配的固定大端长度:P-256各32字节、P-384各48字节、P-521各66字节,你提取完无符号字节后没有补前导0对齐长度,也会导致最终拼接的签名长度不符合要求。
修复方案
推荐使用BouncyCastle内置的DER解析能力,避免自己手写偏移解析逻辑出错,修复后代码如下:
import org.bouncycastle.asn1.ASN1Integer; import org.bouncycastle.asn1.ASN1Sequence; import java.math.BigInteger; import java.util.Arrays; // 各曲线对应的R/S固定长度常量 public static final int P256_RS_LEN = 32; public static final int P384_RS_LEN = 48; public static final int P521_RS_LEN = 66; public static byte[] extractRandSToCose(byte[] derSignature, int rsFixedLength) throws Exception { if (derSignature == null || derSignature[0] != 0x30) { return null; } // 自动处理DER长短编码,无需手动计算偏移 ASN1Sequence seq = ASN1Sequence.getInstance(derSignature); BigInteger r = ((ASN1Integer) seq.getObjectAt(0)).getValue(); BigInteger s = ((ASN1Integer) seq.getObjectAt(1)).getValue(); // 转无符号大端字节并补前导0到固定长度 byte[] rBytes = toFixedLengthUnsigned(r, rsFixedLength); byte[] sBytes = toFixedLengthUnsigned(s, rsFixedLength); // 拼接为COSE要求的格式 byte[] coseSig = new byte[rsFixedLength * 2]; System.arraycopy(rBytes, 0, coseSig, 0, rsFixedLength); System.arraycopy(sBytes, 0, coseSig, rsFixedLength, rsFixedLength); return coseSig; } private static byte[] toFixedLengthUnsigned(BigInteger value, int length) { byte[] unsigned = value.toByteArray(); // 去掉BigInteger默认带的前导符号位0x00 if (unsigned[0] == 0x00) { unsigned = Arrays.copyOfRange(unsigned, 1, unsigned.length); } // 长度不足时补前导0对齐要求长度 if (unsigned.length < length) { byte[] padded = new byte[length]; System.arraycopy(unsigned, 0, padded, length - unsigned.length, unsigned.length); return padded; } return unsigned; }
使用示例
- P-256场景调用:
extractRandSToCose(derSig, P256_RS_LEN)返回64字节COSE签名 - P-384场景调用:
extractRandSToCose(derSig, P384_RS_LEN)返回96字节COSE签名 - P-521场景调用:
extractRandSToCose(derSig, P521_RS_LEN)返回132字节COSE签名
内容的提问来源于stack exchange,提问作者Sox -
相关产品推荐
相关产品推荐

