FIPS 186-3 は、ECDSA 署名を bytearray としてシリアル化する方法を指定していません。
私が知っているすべての API とプロトコルは、次の 2 つの異なる標準的な方法のいずれかを使用します。
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);
2 番目のエンコーディングは、Java セキュリティ アーキテクチャで使用されます。