【问题标题】:Verify a DSA signature in C# that's using BER/DER encoded ASN.1 format在 C# 中验证使用 BER/DER 编码的 ASN.1 格式的 DSA 签名
【发布时间】:2009-10-27 01:33:43
【问题描述】:

如何在 C# 中验证 DSA 签名?

给定:

  • 消息文本,
  • 签名摘要(通常为 ASN.1 DER 格式),
  • 公钥(采用签名的 X.509 证书、PEM 或 DER 格式)

我尝试了很多方法,但都没有成功:

  • OpenSSL.NET: 库的各种奇怪错误;我有一个open thread running with the author over on SourceForge,但还没有解决这个问题。

  • Microsoft .NET API: 无法解压缩 DER 签名进行比较。 DSA 签名为 40 字节(两个 20 字节整数),但呈现为两个整数的 DER 编码序列,因此总长度范围为 46 到 48 字节(请参阅this post 以获得快速概述。)虽然.NET 包含解析 ASN.1/DER 的代码(因为它可以读取 DER 格式的证书),它被埋得很深,无法访问它,因此您可以正确地从 ASN.1/DER 编码中检索 40 个字节签名。这个问题让我想到了下一个选择……

  • BouncyCastle: 通过使用Org.BouncyCastle.Asn1 函数,我可以解析ASN.1 签名并将其拉入它的组件R 和S 整数值中。但是当我将这些传递给签名验证例程时,它失败了,没有给出任何解释。我不确定我是否做错了什么,因为 C# API 完全没有文档记录,而 Java 版本几乎没有文档记录(但我找不到任何示例或 HOWTO 信息。)

我已经为这个问题苦苦挣扎了大约一个星期。我知道以前一定有人做过,但我还没有找到任何完整/有效的例子。

我这里有三个 C# 项目,每个项目都完成了 95%,但有一个导致它失败的严重缺陷。任何有效的示例代码都将不胜感激。

编辑:这是我正在尝试验证的签名示例,已转换为 Base64 和 ASCII 十六进制以使其可发布。这个特定的是 47 字节,但正确的解析器仍然必须接受它,阅读 DER 规范以获取更多信息(BER/DER 添加前导 00 以确认 MSB 为 1 的符号)

Base64: MC0CFQCUgq2DEHWzp3O4nOdo38mDu8mStwIUadZmbin7BvXxpeoJh7dhquD2CTM=

ASCII Hex: 302d0215009482ad831075b3a773b89ce768dfc983bbc992b7021469d6666e29fb06f5f1a5ea0987b761aae0f60933

结构符​​合 DER 规范;它解析如下:

30 2d: sequence, length 45 (may vary from 44 to 46)
 02 15: integer, length 21 (first byte is 00 to confirm sign)
  009482ad831075b3a773b89ce768dfc983bbc992b7
 02 14: integer, length 20 (leading 00 not necessary for this one)
  69d6666e29fb06f5f1a5ea0987b761aae0f60933

编写我自己的 DER 解析器确实不是一种选择,错误的空间太大,必须有一种方法来正确地做到这一点。

【问题讨论】:

  • 我想知道您是否可以帮我一个忙,将标题从“验证 C# 中的 DSA 签名”更改为“使用 ASN.1 格式验证 C# 中的 DSA 签名”或类似的东西。可能有助于搜索人。
  • 作为评论;我将采用另一种方式(验证在 .NET 中创建的 Java 签名);我必须编写一个从 .NET DSA 格式(仅 r||s 40 字节)到 DER 格式的转换器。根据我在此处找到的内容 (mombu.com/microsoft/security-crypto/…),DER 格式要求 r 和 s 为大端,但这根本不起作用,所以我将其保留为小端并且它可以工作......我还没有找到对此有什么非常清楚的;它应该是大端还是小端...如果有人知道,请插话!
  • 我遇到了同样的问题,在这篇文章的帮助下,我决定手动进行 ASN.1-DER 解码;规范令人生畏,但实际上破译两个 20 字节整数的序列并不困难。一旦您确定 .NET 类期望签名以包含两个 20 字节整数连接的 40 字节数组的形式到达,这并不难。

标签: c# .net digital-signature asn.1


【解决方案1】:

看看这个:http://www.codeproject.com/KB/security/CryptoInteropSign.aspx 它解决了我遇到的问题,看起来和你的很相似。

【讨论】:

    【解决方案2】:

    可以在NSsh 项目中找到一段验证 DSA 签名的代码。它不适合您的确切需求,因为公钥不是从 X.509 证书中提取的,但它可以为您提供一个起点。

    【讨论】:

    • 据我所知,它在内部生成签名: SignatureOfH = hostKey.Sign(H);我需要读取一个外部生成的签名,这会导致 ASN.1 问题(在更新的帖子中进行了更多描述。)
    【解决方案3】:

    另一个很好的例子是DotNetAutoUpdate 代码。它使用 RSA,但切换到 DSA 应该很简单。特别是看看这个文件:

    http://code.google.com/p/dotnetautoupdate/source/browse/trunk/source/DotNetAutoUpdate/UpdateKeys.cs


    编辑:基本上你想要类似这样的东西:
    var sha1 = new SHA1Managed();
    var hash = sha1.ComputeHash(inputStream);
    
    var signatureFormatter = new DSASignatureDeformatter(dsa);
    signatureFormatter.SetHashAlgorithm("SHA1");
    bool valid = signatureFormatter.VerifySignature(hash, signature); 
    

    更多详情请关注MSDN


    编辑: 另一种选择是 Mono.Security 库:
    • 有一个阅读类ASN1
    • 还有一个assembly 用于读取 X.509 证书。

    我不确定这是否有帮助......

    【讨论】:

    • .NET 实现的问题是它无法解析签名的 ASN.1 格式 - 我无法在上面的示例中生成“签名”对象。
    • @andersop 你能发布一个 ASN.1 签名的例子吗?
    • @Luke Quinane:当然,我将它编辑到 OP 中,因为 cmets 不支持格式化。
    • @Luke:谢谢,但 Mono 太大了,无法用于此目的。不过,我会看看源代码,看看它是否能给我任何想法。
    • @andersop Mono.Security.dll 只有 285KB,不依赖于任何其他 Mono 资源。你可以在这里找到它:C:\Program Files (x86)\Mono-2.4.2.3\lib\mono\gac\Mono.Security\2.0.0.0__0738eb9f132ed756
    【解决方案4】:

    使用 BouncyCastle (v1.7),我可以做到这一点(当然不要忘记错误检查):

    using System.Security.Cryptography;
    using System.Security.Cryptography.X509Certificates;
    using Org.BouncyCastle.Asn1;
    
    byte[] signature = ReadFile("signature.bin");
    byte[] dataToVerify = ReadFile("data.bin");
    byte[] rawPublicKey = KeyResources.publickey; // My public key is in a resource
    
    var x509 = new X509Certificate2(rawPublicKey);
    var dsa = x509.PublicKey.Key as DSACryptoServiceProvider;
    
    // extract signature components from ASN1 formatted signature
    DSASignatureDeformatter DSADeformatter = new DSASignatureDeformatter(dsa);
    DSADeformatter.SetHashAlgorithm("SHA1");
    
    Asn1InputStream bIn = new Asn1InputStream(new MemoryStream(signature));
    DerSequence seq = bIn.ReadObject() as DerSequence;
    
    var r11 = seq[0].GetEncoded();
    var r21 = seq[1].GetEncoded();
    
    byte[] p1363 = new byte[40];
    Array.Copy(r11, r11.Length - 20, p1363, 0, 20);
    Array.Copy(r21, r21.Length - 20, p1363, 20, 20);
    
    // and finally we can verify
    if (!DSADeformatter.VerifySignature(new SHA1CryptoServiceProvider().ComputeHash(dataToVerify), p1363))
    {
        // Noo, mismatch!
    }
    

    我的 signature.bin 是使用 OpenSSL 生成的,例如openssl dgst -sha1 -sign private.key data.bin > signature.bin

    【讨论】:

      【解决方案5】:

      JFYI:有时我会看到由 OpenSSL 命令行工具生成的有效 45 字节 DER DSA 签名(INTEGERS 之一是 19 字节而不是 20 字节)。而且似乎可以有 44 个字节(都是 19 个字节),所以你最好期望 6 到 48 个字节而不是 46 到 48 个字节。;-)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-06-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-03-20
        • 2013-11-17
        • 1970-01-01
        • 2022-01-22
        相关资源
        最近更新 更多