Проверьте подпись DSA в C #, использующую формат ASN.1 в кодировке BER / DER. - PullRequest
6 голосов
/ 27 октября 2009

Как проверить подпись DSA в C #?

Дано:

  • текст сообщения,
  • подписанный дайджест (обычно формат 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), он глубоко скрыт, и нет способа получить к нему доступ, чтобы вы могли правильно извлечь 40 байтов из ASN .1 / кодированный DER сигнал Эта проблема привела меня к следующему варианту ...

  • BouncyCastle: с помощью функций Org.BouncyCastle.Asn1 я могу проанализировать сигнатуру ASN.1 и извлечь ее в целочисленные значения компонентов R и S. Но когда я передаю их процедурам проверки подписи, это не дает никакого объяснения. Я не уверен, что делаю что-то не так, потому что API C # полностью недокументирован , а версия Java едва документирована (но я не могу найти пример или информацию HOWTO).

Я занимаюсь этой проблемой уже около недели. Я знаю, что кто-то должен был сделать это раньше, но я не нашел полных / рабочих примеров.

У меня здесь три проекта на C #, каждый на 95% завершен, но с одним критическим недостатком, который приводит к сбою. Любой работающий пример кода будет очень полезен.

edit: вот пример подписи, которую я пытаюсь проверить, преобразованной в 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 на самом деле не вариант, слишком много места для ошибок, и должен быть способ сделать это правильно.

Ответы [ 5 ]

2 голосов
/ 11 ноября 2009

Взгляните на это: http://www.codeproject.com/KB/security/CryptoInteropSign.aspx, которое решило проблему, с которой я столкнулся, и которая очень похожа на вашу.

1 голос
/ 04 октября 2013

Используя 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 голос
/ 27 октября 2009

Другим хорошим примером является код 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.

Я не уверен, поможет ли это ...

1 голос
/ 27 октября 2009

Кусок кода, который проверяет подпись DSA, можно найти в проекте NSsh . Он не соответствует вашим точным потребностям, поскольку открытый ключ не извлекается из сертификата X.509, но может дать вам отправную точку.

0 голосов
/ 19 сентября 2013

JFYI: Иногда я вижу действительные 45-байтовые сигнатуры DER DSA (один из INTEGERS был 19 байтами, а не 20), сгенерированные инструментом командной строки OpenSSL. И кажется, что может быть 44 байта (оба - 19 байтов), поэтому лучше ожидать чего-то от 6 до 48 байтов, а не от 46 до 48.; -)

...