6

如何在 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 编码的 sig 中检索 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 解析器真的不是一种选择,错误的空间太大,必须有一种方法来正确地做到这一点。

4

5 回答 5

2

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

于 2009-11-11T20:08:30.560 回答
1

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

于 2009-10-27T01:46:01.763 回答
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

于 2013-10-04T14:15:08.173 回答
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 库:

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

于 2009-10-27T01:51:50.987 回答
0

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

于 2013-09-19T09:14:27.753 回答