UWP ECDSP 签名
UWP ECDSP Signature
我想用这个代码做一个 ECDSA 签名:
AsymmetricKeyAlgorithmProvider objAsymmAlgProv = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.EcdsaSha256);
CryptographicKey keypair = objAsymmAlgProv.CreateKeyPairWithCurveName(EccCurveNames.SecP256r1);
BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
buffMsg = CryptographicBuffer.ConvertStringToBinary("Test Message", encoding);
IBuffer buffSIG = CryptographicEngine.Sign(keypair, buffMsg);
byte [] SignByteArray = buffSIG.ToArray();
bool res = CryptographicEngine.VerifySignature(keypair, buffMsg, buffSIG);
VerifySignature
总是 returns 正确,这没关系。
但是我的签名有些问题。
为什么签名的长度(SignByteArray
)是固定的? (0x40 字节)。
以及为什么 SignByteArray [0]
和 SignByteArray [2]
值不正确? (我觉得应该是0x30和0x02)
ECDSA 规范最后确定这对 (r
, s
) 是签名。它忽略了做的是指出应该如何写下它们。
Windows 和 .NET 使用 IEEE (P)1363 格式,即 big-endian r
concat big-endian s
。 r
和 s
具有相同的大小(由密钥大小决定),因此签名的长度总是偶数,r 是前半部分。
OpenSSL 使用 ASN.1/DER 编码,即 SEQUENCE(INTEGER(r), INTEGER(s))。 DER 编码可以一直下降到 6 个字节(30 04 02 00 02 00
,在退化的 r=0,s=0 中)并且平均比 IEEE 形式大 6 个字节。它编码为 30 [length, one or more bytes] 02 [length, one or more bytes] [optional padding 00] [big-endian r with no leading 00s] 02 [length, one or more bytes] [optional padding 00] [big-endian s with no leading 00s]
.
DER 形式过于依赖数据,无法具体描述,因此举个例子应该有所帮助。假设我们在 32 位字段中使用曲线并生成 (r=1016, s=2289644760).
IEEE 1363:
// r
00 00 03 F8
// s
88 79 34 D8
德国:
SEQUENCE(INTEGER(1016), INTEGER(2289644760))
// Encode r
// 1016 => 0x3F8 => 03 F8 (length 02)
SEQUENCE(
02 02
03 F8,
INTEGER(2289644760))
// Encode s
// 2289644760 => 0x887934D8 => 88 79 34 D8
// But since the high bit is set this is a negative number (-2005322536),
// and s is defined to be positive. So insert a 00 to ensure the high bit is clear.
// => 00 88 79 34 D8 (length 05)
SEQUENCE(
02 02
03 F8
02 05
00 88 79 34 D8)
// And encode the sequence, whose payload length we can now count as 11 (0B)
30 0B
02 02
03 F8
02 05
00 88 79 34 D8
因此 Windows/.NET 发出 00 00 03 F8 88 79 34 D8
,而 OpenSSL 发出 30 0B 02 02 03 F8 02 05 00 88 79 34 D8
。但他们都只是在说 (r, s) = (1016, 2289644760)
.
(旁白:您观察到 DER 编码中的签名 [2] == 0x02 对于您正在使用的大小密钥是正确的,但是在大约 496 位密钥处,序列长度在统计上可能需要超过一个字节;因此对于 P-521 密钥,它最有可能以 03 81 88 02
开头,在 88
字节中具有可变性)
我想用这个代码做一个 ECDSA 签名:
AsymmetricKeyAlgorithmProvider objAsymmAlgProv = AsymmetricKeyAlgorithmProvider.OpenAlgorithm(AsymmetricAlgorithmNames.EcdsaSha256);
CryptographicKey keypair = objAsymmAlgProv.CreateKeyPairWithCurveName(EccCurveNames.SecP256r1);
BinaryStringEncoding encoding = BinaryStringEncoding.Utf8;
buffMsg = CryptographicBuffer.ConvertStringToBinary("Test Message", encoding);
IBuffer buffSIG = CryptographicEngine.Sign(keypair, buffMsg);
byte [] SignByteArray = buffSIG.ToArray();
bool res = CryptographicEngine.VerifySignature(keypair, buffMsg, buffSIG);
VerifySignature
总是 returns 正确,这没关系。
但是我的签名有些问题。
为什么签名的长度(SignByteArray
)是固定的? (0x40 字节)。
以及为什么 SignByteArray [0]
和 SignByteArray [2]
值不正确? (我觉得应该是0x30和0x02)
ECDSA 规范最后确定这对 (r
, s
) 是签名。它忽略了做的是指出应该如何写下它们。
Windows 和 .NET 使用 IEEE (P)1363 格式,即 big-endian r
concat big-endian s
。 r
和 s
具有相同的大小(由密钥大小决定),因此签名的长度总是偶数,r 是前半部分。
OpenSSL 使用 ASN.1/DER 编码,即 SEQUENCE(INTEGER(r), INTEGER(s))。 DER 编码可以一直下降到 6 个字节(30 04 02 00 02 00
,在退化的 r=0,s=0 中)并且平均比 IEEE 形式大 6 个字节。它编码为 30 [length, one or more bytes] 02 [length, one or more bytes] [optional padding 00] [big-endian r with no leading 00s] 02 [length, one or more bytes] [optional padding 00] [big-endian s with no leading 00s]
.
DER 形式过于依赖数据,无法具体描述,因此举个例子应该有所帮助。假设我们在 32 位字段中使用曲线并生成 (r=1016, s=2289644760).
IEEE 1363:
// r
00 00 03 F8
// s
88 79 34 D8
德国:
SEQUENCE(INTEGER(1016), INTEGER(2289644760))
// Encode r
// 1016 => 0x3F8 => 03 F8 (length 02)
SEQUENCE(
02 02
03 F8,
INTEGER(2289644760))
// Encode s
// 2289644760 => 0x887934D8 => 88 79 34 D8
// But since the high bit is set this is a negative number (-2005322536),
// and s is defined to be positive. So insert a 00 to ensure the high bit is clear.
// => 00 88 79 34 D8 (length 05)
SEQUENCE(
02 02
03 F8
02 05
00 88 79 34 D8)
// And encode the sequence, whose payload length we can now count as 11 (0B)
30 0B
02 02
03 F8
02 05
00 88 79 34 D8
因此 Windows/.NET 发出 00 00 03 F8 88 79 34 D8
,而 OpenSSL 发出 30 0B 02 02 03 F8 02 05 00 88 79 34 D8
。但他们都只是在说 (r, s) = (1016, 2289644760)
.
(旁白:您观察到 DER 编码中的签名 [2] == 0x02 对于您正在使用的大小密钥是正确的,但是在大约 496 位密钥处,序列长度在统计上可能需要超过一个字节;因此对于 P-521 密钥,它最有可能以 03 81 88 02
开头,在 88
字节中具有可变性)