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
опция)