在.NET中使用SHA256签名验证ECDsa

问题描述 投票:0回答:1

对于我正在进行的一个项目,我需要验证数据记录的签名。我们有一个遗留项目,它使用了源代码不再可用的导入 DLL。我们现在正在更新该项目,并希望使签名验证现代化。

支持的签名方法有 RSA 1024、ECDSA 192、ECDSA 224 和 ECDSA 256。

作为示例数据,我有以下公钥:

MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwhDZNphi4J1EiBCQwZ1WBCYbnf9Ne6UqPuLLGJPTj5++io9pxT3McYgFFFPb7HdfJCrPsKmTVwlw9Z1RpQru6g==
,以下签名:
BEgwRgIhAPqcBPjRROiwTBUFTF50iDXSBn+e71c3fIj9kgfxUbAZAiEAR6KM+u8D6TWtaKVdH8EjLxWJf4Vm/u2Z5YptKEjTgHs=
,以及应验证以下数据:
;"4399901879126";"302.1302.006";"4005249001296";"20240627151654328";

我已经使用旧工具验证这些值应该给出积极的结果。

我尝试了以下一些方法,但没有成功:

    private static void Main(string[] args)
    {
        Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

        var publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwhDZNphi4J1EiBCQwZ1WBCYbnf9Ne6UqPuLLGJPTj5++io9pxT3McYgFFFPb7HdfJCrPsKmTVwlw9Z1RpQru6g==";
        var signature = "BEgwRgIhAPqcBPjRROiwTBUFTF50iDXSBn+e71c3fIj9kgfxUbAZAiEAR6KM+u8D6TWtaKVdH8EjLxWJf4Vm/u2Z5YptKEjTgHs=";
        var data = ";\"4399901879126\";\"302.1302.006\";\"4005249001296\";\"20240627151654328\";";

        var value = VerifyEcdsaSignature(Convert.FromBase64String(publicKey), Encoding.Latin1.GetBytes(data), Convert.FromBase64String(signature));
        Console.WriteLine(value);

        Console.ReadLine();
    }

    public static bool VerifySignature(byte[] key, byte[] data, byte[] signature)
    {
        var publicKey = PublicKey.CreateFromSubjectPublicKeyInfo(key, out _);
        var ecdsa = publicKey.GetECDsaPublicKey() ?? ECDsa.Create();
        
       return ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256);
    }

    public static byte[] GetSha224Bytes(byte[] data)
    {
        return Sha224.Create().ComputeHash(data);
    }

    public static byte[] GetSha256Bytes(byte[] data)
    {
        return SHA256.HashData(data);
    }

    private static bool VerifyEcdsaSignature(byte[] key, byte[] bytesData, byte[] bytesSignature)
    {
        var publicKey = PublicKey.CreateFromSubjectPublicKeyInfo(key, out _);
        var ecdsa = publicKey.GetECDsaPublicKey();
        var pubKey = DotNetUtilities.GetECDsaPublicKey(ecdsa);

        ISigner signer = SignerUtilities.GetSigner("SHA-256withECDSA");
        signer.Init(false, pubKey);
        signer.BlockUpdate(bytesData);

        return signer.VerifySignature(bytesSignature);
    }
.net cryptography sha ecdsa
1个回答
0
投票

验证失败的原因是签名格式不兼容以及(可能)您的遗留工具中的错误!

这可以通过十六进制编码签名最容易地解释:

enter image description here

此签名采用 ASN.1/DER 编码。大多数库支持 ASN.1/DER 编码签名,但没有与

OCTET STRING
关联的 ID/长度,即两个前导字节 0x0448。如果删除这些,结果是:

enter image description here

如果指定了正确的签名格式,则该签名应该现在实际上已成功验证。
由于 C# 默认使用 IEEE P1363 格式,因此必须使用

DSASignatureFormat.Rfc3279DerSequence
作为
VerifyData()
调用中的第三个参数或(如已使用的)BC 变体中的
SHA-256withECDSA
显式指定 ASN.1/DER 格式。

尽管如此,这个验证还是失败了!原因是遗留工具没有正确实现 ASN.1/DER 编码。
正确编码的解释可以在here找到:r和s的编码是最小尺寸的有符号大端。这意味着如果前导字节大于 0x7f,则前导 0x00 只能出现在 r 之前。对于 s 同样如此。
对于发布的签名,r (

0xFA9C...B019
) 属于这种情况,但 s (
0x47A2...807B
) 则不然。尽管如此,0x00字节也被设置为s。这是不正确的,必须修复:必须删除 0x00 字节并调整长度信息:

enter image description here

有了这个签名,验证就成功了:

...
var publicKey = "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEwhDZNphi4J1EiBCQwZ1WBCYbnf9Ne6UqPuLLGJPTj5++io9pxT3McYgFFFPb7HdfJCrPsKmTVwlw9Z1RpQru6g==";
var signature = "MEUCIQD6nAT40UTosEwVBUxedIg10gZ/nu9XN3yI/ZIH8VGwGQIgR6KM+u8D6TWtaKVdH8EjLxWJf4Vm/u2Z5YptKEjTgHs=";
var data = ";\"4399901879126\";\"302.1302.006\";\"4005249001296\";\"20240627151654328\";";
var value = VerifySignature(Convert.FromBase64String(publicKey), Encoding.UTF8.GetBytes(data), Convert.FromBase64String(signature));
Console.WriteLine(value); // true
...
public static bool VerifySignature(byte[] key, byte[] data, byte[] signature)
{
    ...
    return ecdsa.VerifyData(data, signature, HashAlgorithmName.SHA256, DSASignatureFormat.Rfc3279DerSequence); // apply ASN.1/DER format
}
...
© www.soinside.com 2019 - 2024. All rights reserved.