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

时间:2009-10-27 01:33:44

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

如何在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 /中正确检索40个字节DER编码的sig。这个问题让我想到了下一个选择......

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

我已经在这个问题上投入了大约一个星期了。我知道有人必须先做过,但我没有找到任何完整/有效的例子。

我有三个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解析器实际上不是一个选项,错误的空间太大,而且必须有一种方法来正确地执行此操作。

5 个答案:

答案 0 :(得分:2)

看看这个:http://www.codeproject.com/KB/security/CryptoInteropSign.aspx解决了我遇到的与你的问题非常相似的问题。

答案 1 :(得分:1)

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

答案 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
  • 的课程
  • 还有一个assembly用于阅读X.509 cerficiates。

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

答案 3 :(得分: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

答案 4 :(得分:0)

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