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
Bytes.

С помощью токена, использующего этот код для получения подписи, а затем ее проверки, я получаю только 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 битов (без токена - нам нужно проверить на машине без токена).

или же

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

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

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

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

в System.Security.Cryptography.CryptographicException.ThrowCryptographicException(Int32 ч.) в System.Security.Cryptography.Utils.SignValue(SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, объектный системный HH, байт-объект Hash).Security.Cryptography.Utils.SignValue (SafeKeyHandle hKey, Int32 keyNumber, Int32 calgKey, Int32 calgHash, Byte[] хеш) в System.Security.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHSash.Cryptography.RSACryptoServiceProvider.SignHash(Byte[] rgbHash, String str) в TE.Program.Main(String[] args) в z:\Work\compusolve\enctest\TE\TE\Program.cs: строка 77

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);
        }
    }
  }
}

Я должен оспорить ваше утверждение о том, что это на самом деле ключ контейнера ключей по умолчанию (возможно, вы вызвали его создание при первом запуске кода, поскольку вы не утверждали 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 опция)

Другие вопросы по тегам