C # USB eToken подпись и проблема проверки

У меня есть сертификат x509 с открытым и закрытым ключом, который хранится на USB-токене safenet.

У меня есть данные, которые я хочу подписать. Мне нужно использовать открытый ключ сертификата для проверки подписи.

Окончательный код, подписывающий мой собственный самоподписанный сертификат:

RSACryptoServiceProvider rsa1 = (RSACryptoServiceProvider)useCertificate.PrivateKey;
byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));

И код для проверки с использованием открытого ключа сертификата:

RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);

С самоподписанным сертификатом это работает нормально. Подпись, которую я получаю, составляет 256 байтов.

С токеном, использующим этот код для получения подписи, а затем для ее проверки, я получаю только 128-байтовую подпись, и проверка не выполняется:

CspParameters csp = new CspParameters(1, "SafeNet RSA CSP");
csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
csp.KeyNumber = (int)KeyNumber.Signature;
RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(csp);

Проверьте код так же, как указано выше.

Замечу, что сертификат, который я хочу использовать, является сертификатом по умолчанию в токене. Почему я получаю только 128-байтовую подпись вместо 256? Подозреваю, поэтому не проверяет.

Нужны ли мне другие параметры и настройки в моем csp?

Спасибо

* Обновление на основе комментариев *

Понятно, что я использую 1024 бита, когда указываю csp.keyNumber = (int) KeyNumber.Signature, но это единственный способ, которым токен действительно что-то возвращает. Несмотря на то, что размер ключа токена составляет 2048 бит, а спецификация ключа - AT_KEYEXCHANGE. Когда я использую номер ключа обмена, который, по моему мнению, действительно правильный, тогда, когда я пытаюсь вычислить подпись, мне предлагается войти в систему, но затем я получаю исключение «Параметр недействителен». Итак, насколько я могу судить, мне нужна одна из двух вещей:

1 - как использовать открытый ключ для проверки подписи с использованием 1024 бит (без токена - нам нужно проверить на машине без токена).

or

2 - как установить все, что неверно, чтобы мы могли передать исключение - что, на мой взгляд, является лучшей идеей.

Есть ли у кого-нибудь совет о том, что я могу сделать с этим исключением или что может его вызывать?

Полная информация об исключении ниже:

HResult = -2147024809 Сообщение = Неверный параметр. Трассировки стека

в System.Security.Cryptography.CryptographicException.ThrowCryptographicException (Int32 hr) в System.Security.Cryptography.Utils.SignValue (SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Int32 [] хэш .Security.Cryptography.Utils.SignValue (SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte [] hash) в System.Security.Cryptography.RSACryptoServiceProvider.SignHash (Bytecurity [] rgbHash). Cryptography.RSACryptoServiceProvider.SignHash (Byte [] rgbHash, String str) в TE.Program.Main (String [] args) в z: \ Work \ compusolve \ enctest \ TE \ TE \ Program.cs: строка 77


person user2709214    schedule 30.03.2017    source источник
comment
Если данные не потеряны, 128-байтовая подпись предназначена для 1024-битного ключа вместо 2048-битного; т.е. неправильная пара ключей.   -  person Maarten Bodewes    schedule 31.03.2017
comment
Думаю, это помогает. Я знаю, что мой ключ для проверки - это 2048-битный ключ. Итак, я расскажу, как получить токен для использования правильной пары ключей. Я доложу здесь.   -  person user2709214    schedule 31.03.2017
comment
Я изменил код подписи на этот: RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(2048, csp); Но это не повлияло на результат - мне вернули только 128 байт. Это должно было потребовать использования 2048-битного ключа.   -  person user2709214    schedule 31.03.2017
comment
Независимо от того, что я указываю при создании RsaCryptoServiceProvider, внутренний размер ключа равен 1024. Если я изменю csp.KeyNumber на (Int)KeyNumber.Exchange вместо Signature, тогда я получу желаемый размер ключа 2048, однако, когда я прошу устройство вычислить подпись, я получаю исключение Параметр недействителен. KeySize на устройстве составляет 2048 бит.   -  person user2709214    schedule 31.03.2017
comment
Вы уверены, что вообще можете использовать ключ для генерации подписи? Поскольку AT_KEYEXCHANGE не является генерацией подписи.   -  person Maarten Bodewes    schedule 31.03.2017
comment
AT_KEYEXCHANGE можно использовать для генерации подписи, но не ограничивается этим.   -  person user2709214    schedule 31.03.2017


Ответы (2)


Ответ на этот вопрос двоякий. Если вы используете одно из этих устройств, я обнаружил, что в реестре в разделе HKEY_LOCAL_MACHINE \ SOFTWARE \ Microsoft \ Cryptography \ Defaults \ Provider

Есть 3 разных провайдера. Каждый с идентичными настройками для типа и даже изображения - используемой dll. Но выбор другого, в моем случае Datakey RSP CSP, предоставил 256-байтовую подпись на основе 2048-битного ключа. Вы также должны убедиться, что используемый вами сертификат является сертификатом по умолчанию в токене. В моем случае было два разных сертификата. Я проверял, используя один, но подписывал с помощью другого.

Полный исходный код для тестового клиента приведен ниже:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Security.Cryptography.X509Certificates;
using System.Security.Cryptography;

namespace TE
{
class Program
{
    static void Main(string[] args)
    {
        try
        {

            // these variables should be changed to math your installation

            // find CSP's in this windows registry key:  HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography\Defaults\Provider
            string TokenCSPName = "Datakey RSA CSP";
            string TokenCertificateName = "ACME Inc";
            string NonTokenCertificateName = "SelfSigned";
            string certLocation = "Token"; // change to something else to use self signed "Token" for token
            // the certificate on the token should be installed into the local users certificate store
            // tokens will not store or export the private key, only the public key


            // find the certificate we want to use - there's no recovery if the certificate is not found

            X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
            store.Open(OpenFlags.OpenExistingOnly);
            X509Certificate2Collection certificates = store.Certificates;
            X509Certificate2 certificate = new X509Certificate2();
            X509Certificate2 useCertificate = new X509Certificate2();
            if (certLocation == "Token")
            {
                for (int i = 0; i < certificates.Count; i++)
                {
                    certificate = certificates[i];
                    string subj = certificate.Subject;
                    List<X509KeyUsageExtension> extensions = certificate.Extensions.OfType<X509KeyUsageExtension>().ToList();
                    if (certificate.GetNameInfo(X509NameType.SimpleName, false).ToString() == TokenCertificateName)
                    {
                        for (int j = 0; j < extensions.Count; j++)
                        {
                            if ((extensions[j].KeyUsages & X509KeyUsageFlags.DigitalSignature) == X509KeyUsageFlags.DigitalSignature)
                            {
                                useCertificate = certificate;
                                j = extensions.Count + 1;
                            }

                        }

                    }
                }
            } else
            {
                for (int i = 0; i < certificates.Count; i++)
                {
                    certificate = certificates[i];
                    string subj = certificate.Subject;
                    List<X509KeyUsageExtension> extensions = certificate.Extensions.OfType<X509KeyUsageExtension>().ToList();
                    if (certificate.GetNameInfo(X509NameType.SimpleName, false).ToString() == NonTokenCertificateName)
                        useCertificate = certificate;
                }

            }
            CspParameters csp = new CspParameters(1, TokenCSPName);

            csp.Flags = CspProviderFlags.UseDefaultKeyContainer;
            csp.KeyNumber = (int)KeyNumber.Exchange;
            RSACryptoServiceProvider rsa1 = new RSACryptoServiceProvider(csp);
            string SignatureString = "Data that is to be signed";
            byte[] plainTextBytes = Encoding.ASCII.GetBytes(SignatureString);
            bool Verified = false;
            using (SHA1CryptoServiceProvider shaM = new SHA1CryptoServiceProvider())
            {
                // hash the data to be signed - you can use signData and avoid the hashing if you like

                byte[] hash = shaM.ComputeHash(plainTextBytes);
                // sign the hash
                byte[] digitalSignature = rsa1.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));
                // check your signature size here - if not 256 bytes then you may not be using the proper
                // crypto provider

                // Verify the signature with the hash

                RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)useCertificate.PublicKey.Key;
                Verified = rsa.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), digitalSignature);
                if (Verified)
                {
                    Console.WriteLine("Signature Verified");
                }
                else
                {
                    Console.WriteLine("Signature Failed Verification");
                }
            }
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message);
        }
    }
  }
}
person user2709214    schedule 31.03.2017
comment
Спасибо за ответ, вы можете принять свой ответ через 2 дня. - person Maarten Bodewes; 31.03.2017
comment
@ user2709214 Я получил ошибку "Набор ключей не определен" - person Fighter; 17.09.2020

Я должен оспорить ваше утверждение о том, что на самом деле это ключ контейнера ключей по умолчанию (возможно, вы вызвали создание этого ключа при первом запуске кода, поскольку вы не утверждали флаг UseExistingKey).

Предполагая, что сертификат находится в вашем хранилище сертификатов, запустите certutil -user -silent store my, найдите запись сертификата и проверьте значение Key Container:

================ Certificate 11 ================
Serial Number: 0123456789abcdeffedcba9876543210
Issuer: CN=Intermediate Certificate Authority
 NotBefore: 10/21/2016 7:26 AM
 NotAfter: 10/21/2017 7:26 AM
Subject: CN=bartonjs
Non-root Certificate
Template:
Cert Hash(sha1): 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14
  Key Container = le-Smartcard-987abcdf-6332-43-16531
  Provider = Microsoft Base Smart Card Crypto Provider

Если вы скопируете / вставите любое имеющееся значение и используете его в качестве имени контейнера ключей, ваши подписи, вероятно, начнут иметь правильный размер.

(Если ваш сертификат находится в хранилище машины, а не в хранилище пользователя, опустите параметр -user)

person bartonjs    schedule 31.03.2017
comment
Контейнер для закрытого ключа находится на токене, а не в локальном хранилище. Я убедился, что это тоже сертификат по умолчанию - на токене USB их два. - person user2709214; 31.03.2017