验证C#中的DSA签名是否为';使用BER/DER编码的ASN.1格式

验证C#中的DSA签名是否为';使用BER/DER编码的ASN.1格式,c#,.net,digital-signature,asn.1,C#,.net,Digital Signature,Asn.1,如何在C#中验证DSA签名? 鉴于: 信息文本 签名摘要(通常为ASN.1 DER格式) 公钥(采用签名的X.509证书、PEM或DER格式) 我尝试了很多方法,但都没有成功: :库中的各种奇怪错误;我有一个问题,但还没有解决 Microsoft.NET API:无法解压缩DER签名以进行比较。DSA签名是40字节(两个20字节的整数),但表示为两个整数的DER编码序列,因此总长度可以在46到48字节之间(请参阅快速概述)。虽然.NET包含解析ASN.1/DER的代码(因为它可以读取DER

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

鉴于:

  • 信息文本
  • 签名摘要(通常为ASN.1 DER格式)
  • 公钥(采用签名的X.509证书、PEM或DER格式)
我尝试了很多方法,但都没有成功:

  • 库中的各种奇怪错误;我有一个问题,但还没有解决

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

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

我花了大约一个星期的时间研究这个问题。我知道一定有人以前做过,但我还没有找到任何完整/有效的例子

我这里有三个C#项目,每一个都完成了95%,但有一个关键缺陷导致它失败。任何工作示例代码都将受到极大的赞赏

编辑:下面是一个我试图验证的签名示例,它被转换为Base64和ASCII十六进制,以使其可邮寄。这个特定的值是47字节,但正确的解析器仍然必须接受它,请阅读DER规范以了解更多信息(如果MSB为1,BER/DER会添加前导00以确认符号)

结构符合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解析器真的不是一个选项,有太多的错误空间,必须有一种方法来正确地做到这一点。

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

另一个很好的示例是代码。它使用RSA,但切换到DSA应该非常简单。请特别查看此文件:


编辑:基本上,您需要类似的内容:

var sha1 = new SHA1Managed();
var hash = sha1.ComputeHash(inputStream);

var signatureFormatter = new DSASignatureDeformatter(dsa);
signatureFormatter.SetHashAlgorithm("SHA1");
bool valid = signatureFormatter.VerifySignature(hash, signature); 
更多详细信息


编辑:另一个选项是Mono.Security库:

  • 有一节阅读课
  • 还有一个用于阅读X.509铜币的标签

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

看看这个:它解决了我遇到的问题,看起来与您的问题非常相似。

JFYI:有时我会看到OpenSSL命令行工具生成的DSA签名中有效的45字节(其中一个整数是19字节,而不是20字节)。而且似乎可以有44个字节(两个都是19个字节),所以最好是6到48个字节,而不是46到48个字节

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


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

。.NET实现的问题是它无法解析签名的ASN.1格式-我无法生成“签名”对象。@andersop您能发布一个ASN.1签名的示例吗?@Luke Quinane:当然,我把它编辑到了OP中,因为注释不支持格式。@Luke:谢谢,但是Mono太大了,无法用于此目的。我将查看源代码,看看它是否给了我一些想法,尽管Mono.Security.dll只有285KB,不依赖于任何其他Mono资源。你可以在这里找到它:C:\ProgramFiles(x86)\Mono-2.4.2.3\lib\Mono\gac\Mono.Security\2.0.0\uuu 0738EB9F132ED756据我所知,它正在内部生成签名:SignatureOfH=hostKey.Sign(H);我需要阅读一个外部生成的签名,这导致了ASN.1问题(在更新的帖子中有更多描述)。我想知道您是否可以帮我一个忙,将标题从“用C#验证DSA签名”更改为“用ASN.1格式验证C#DSA签名”之类的内容。可能有助于搜索人员。作为评论;我将换一种方式(验证在.NET中创建的Java签名);我必须编写一个从.NETDSA格式(40字节的r | | s)到DER格式的转换器。根据我在这里发现的()DER格式期望r和s是big-endian,但这根本不起作用,所以我把它作为little-endian,它起作用了…我还没有发现任何关于这方面的清晰信息;应该是大端还是小端……如果有人知道,请插话!我也遇到了同样的问题,在这篇文章的帮助下,我决定手工解码ASN.1-DER;该规范令人望而生畏,但实际上破译两个20字节整数序列并不困难。一旦计算出.NET类希望签名以包含两个20字节整数的concatation的40字节数组的形式到达,就不难了。
var sha1 = new SHA1Managed();
var hash = sha1.ComputeHash(inputStream);

var signatureFormatter = new DSASignatureDeformatter(dsa);
signatureFormatter.SetHashAlgorithm("SHA1");
bool valid = signatureFormatter.VerifySignature(hash, signature); 
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!
}