FIPS 186-3 未指定如何将 ECDSA 签名序列化为字节数组。
我所知道的所有 API 和协议都使用两种不同的标准方式之一:
1) 零填充 r 和 s 所以它们是 n 的长度(G 的阶)并将它们连接起来:
byte[] rArr = toUnsignedByteArray(r);
byte[] sArr = toUnsignedByteArray(s);
int nLength = (n.bitLength()+7)/8;
byte[] res = new byte[2*nLength];
System.arraycopy(rArr, 0, res, nLength - rArr.length, rArr.length);
System.arraycopy(sArr, 0, res, 2* nLength - sArr.length, nLength);
toUnsignedByteArray
是这样的:
byte[] toUnsignedByteArray(BigInteger bi){
byte[] ba = bi.toByteArray();
if(ba[0] != 0){
return ba;
}
else
{
byte[] ba2 = new byte[ba.length - 1];
System.arraycopy(ba, 1, ba2, 0, ba.length - 1);
return ba2;
}
}
2) 使用以下 ASN.1 结构的 DER 编码:
ECDSASignature ::= SEQUENCE {
r INTEGER,
s INTEGER
}
假设 n 最多为 487 位(如果您需要支持更大的密钥,长度编码会变得更复杂一些):
byte[] rArr = r.toByteArray();
byte[] sArr = s.toByteArray();
byte[] res = new byte[6 + rArr.length + sArr.length];
res[0] = 0x30;
res[1] = 4 + rArr.length + sArr.length;
res[2] = 0x02;
res[3] = rArr.length;
System.arraycopy(rArr, 0, res, 4, rArr.length);
res[4+rArr.length] = 0x02;
res[5+rArr.length] = sArr.length;
System.arraycopy(sArr, 0, res, 6+rArr.length, sArr.length);
解码做这样的事情(这是非常基本的,非防错解码):
if(enc[0]!=0x30) return false; // bad encoding
if(enc[1]&0x80!=0) return false; // unsupported length encoding
if(enc[2]!=0x02) return false; // bad encoding
if(enc[3]&0x80!=0) return false; // unsupported length encoding
int rLength = enc[3];
byte[] rArr = new byte[rLength];
System.arraycopy(enc, 4, rArr, 0, rLength);
BigInteger r = new BigInteger(rArr);
if(enc[4+rLength]!=0x02) return false; // bad encoding
if(enc[5+rLength]&0x80!=0) return false; // unsupported length encoding
int sLength = enc[5+rLength];
byte[] sArr = new byte[sLength];
System.arraycopy(enc, 6+rLength, sArr, 0, sLength);
BigInteger s = new BigInteger(sArr);
Java 安全架构使用第二种编码。