在C#中验证使用BER/DER编码的ASN.1格式的DSA签名

7

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

给定:

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

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

  • OpenSSL.NET使用该库时出现各种奇怪的错误;我在SourceForge上有一个开放的线程,但还没有解决这个问题。

  • Microsoft .NET API:无法解包DER签名以进行比较。DSA签名是40字节(两个20字节整数),但呈现为两个整数的DER编码序列,因此总长度可以从46到48字节不等(请参见此帖子,了解快速概述)。虽然.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规范以获取更多信息(如果MSB为1,则BER/DER会添加一个前导00来确认符号)

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签名”或类似的内容。这样可能会对搜索有所帮助。 - Greg
只是一条评论;我正在进行相反的操作(在Java中验证在.NET中创建的签名);我不得不编写一个从.NET DSA格式(仅为40个字节的r || s)转换为DER格式的转换器。根据我在这里找到的内容(http://www.mombu.com/microsoft/security-crypto/t-dsa-signature-format-674106.html),DER格式期望r和s为大端,但这根本不起作用,所以我将其保留为小端,并且它可以工作...我没有找到任何非常清晰的关于此的信息;它应该是大端还是小端...如果有人知道,请发表意见! - SonarJetLens
我曾经遇到过同样的问题,通过这篇文章的帮助,我决定手动进行ASN.1-DER解码;规范看起来很吓人,但实际上解密两个20字节整数序列并不难。一旦你弄清楚了.NET类期望以包含两个20字节整数连接而成的40字节数组形式接收签名,就不难了。 - Michael Kay
5个回答

2

1
另一个很好的例子是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的类
  • 还有一个用于读取X.509证书的assembly

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


.NET 实现的问题在于它无法解析签名的 ASN.1 格式 - 我无法生成上面示例中的 "signature" 对象。 - andersop
@andersop,你能发一个ASN.1签名的例子吗? - Luke Quinane
@Luke Quinane:好的,我已经将其编辑到原始帖子中,因为评论不支持格式。 - andersop
@Luke:谢谢,但是为了这个目的,Mono太庞大了。不过我会查看源代码,看看它是否能给我一些启示。 - andersop
1
@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。 - Luke Quinane

1

使用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


1
NSsh项目中可以找到一个验证DSA签名的代码片段。它可能不完全符合您的需求,因为公钥并非从X.509证书中获取,但它可以作为一个起点。

据我所见,它是在内部生成签名的: SignatureOfH = hostKey.Sign(H); 我需要读取一个外部生成的签名,这会导致ASN.1问题(在更新的帖子中有更详细的描述)。 - andersop

0
JFYI:有时我会看到由OpenSSL命令行工具生成的有效的45字节DER DSA签名(其中一个整数是19字节而不是20字节)。似乎也可以有44字节(两个都是19字节),因此您最好预期从6到48字节的任何内容,而不是从46到48字节。;-)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接