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

发布于 2024-08-09 04:42:55 字数 1965 浏览 6 评论 0原文

如何在 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 信息。)< /p>

我 我已经为这个问题投入了大约一周的时间。我知道以前肯定有人做过,但我还没有找到任何完整/有效的示例。

我这里有三个 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 解析器确实不是一个选择,错误的空间太大,并且必须有一种方法可以正确地执行此操作。

How can I verify a DSA signature in C#?

Given:

  • the message text,
  • a signed digest (typically ASN.1 DER format),
  • the public key (in a signed X.509 certificate, PEM or DER format)

I've tried a number of approaches, but haven't had any success:

  • OpenSSL.NET: various strange errors with the library; I've got an open thread running with the author over on SourceForge but haven't been able to resolve this yet.

  • Microsoft .NET API: can't unpack the DER signature for comparison. A DSA signature is 40 bytes (two 20-byte integers), but is presented as an DER-encoded sequence of two integers, so the total length can range from 46 to 48 bytes (see this post for a quick overview.) While .NET includes code to parse ASN.1/DER (because it can read certificates in DER format), it's buried deep, and there's no way to access it so that you can correctly retrieve the 40 bytes from the ASN.1/DER encoded sig. This issue led me to the next option...

  • BouncyCastle: through the use of the Org.BouncyCastle.Asn1 functions, I can parse the ASN.1 signature and pull it into it's component R and S integer values. But when I pass these to the signature verification routines, it's failing with no explanation given. I'm not sure if I'm doing anything wrong, because the C# API is completely undocumented, and the Java version is barely documented (but there's no example or HOWTO information that I can find.)

I've been throwing myself at this problem for about a week now. I know someone must have done it before, but I haven't found any complete/working examples.

I've got three C# projects sitting here, each 95% complete but with one critical flaw that causes it to fail. Any working example code would be tremendously appreciated.

edit: here's an example of a signature I'm trying to verify, converted to Base64 and ASCII hex to make it postable. This particular one is 47 bytes but a proper parser must still accept it, read up on the DER spec for more info (BER/DER adds a leading 00 to confirm the sign if the MSB is 1)

Base64: MC0CFQCUgq2DEHWzp3O4nOdo38mDu8mStwIUadZmbin7BvXxpeoJh7dhquD2CTM=

ASCII Hex: 302d0215009482ad831075b3a773b89ce768dfc983bbc992b7021469d6666e29fb06f5f1a5ea0987b761aae0f60933

Structure is per the DER spec; it parses as follows:

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

Writing my own DER parser is really not an option, too much room for error and there's got to be a way to do this properly.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

放血 2024-08-16 04:42:55

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

Take a look at this: http://www.codeproject.com/KB/security/CryptoInteropSign.aspx which resolved the problem I was having that seems very similar to your's.

幸福%小乖 2024-08-16 04:42:55

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

A piece of code that verifies a DSA signature can be found in the NSsh project. It doesn't suit your exact needs since the public key is not pulled from an X.509 certificate but it may give you a starting point.

掩耳倾听 2024-08-16 04:42:55

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

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


Edit: basically you want something similar to this:

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

var signatureFormatter = new DSASignatureDeformatter(dsa);
signatureFormatter.SetHashAlgorithm("SHA1");
bool valid = signatureFormatter.VerifySignature(hash, signature); 

有关更多详细信息,请参阅 MSDN


Edit: Another option is the Mono.Security libraries:

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

Another good example is the DotNetAutoUpdate code. It uses RSA but it should be quite simple to switch over to DSA. In particular have a look at this file:

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


Edit: basically you want something similar to this:

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

var signatureFormatter = new DSASignatureDeformatter(dsa);
signatureFormatter.SetHashAlgorithm("SHA1");
bool valid = signatureFormatter.VerifySignature(hash, signature); 

More details on MSDN.


Edit: Another option is the Mono.Security libraries:

  • There is a class for reading ASN1
  • There is also an assembly for reading X.509 cerficiates.

I'm not sure if that helps...

淡莣 2024-08-16 04:42:55

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

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

Using BouncyCastle (v1.7), I can do this (don't forget error checking, of course):

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!
}

My signature.bin is generated using OpenSSL, e.g. openssl dgst -sha1 -sign private.key data.bin > signature.bin

浅听莫相离 2024-08-16 04:42:55

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

JFYI: Sometimes I see valid 45-bytes DER DSA signatures (one of INTEGERS was 19 bytes not 20), generated by OpenSSL command line tool. And it seems that there can be 44 bytes (both are 19 bytes), so you better expect anything from 6 to 48 bytes rather than from 46 to 48. ;-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文