.NET + OpenSSL + Смарт-карта - PullRequest
2 голосов
/ 28 марта 2011

Я использую .NET и OpenSSL для генерации сертификатов CA и клиентов.

Следующий фрагмент кода создает сертификат клиента и подписывает его сертификатом CA:

//Generating public/private key pair
var userRsa = new RSA();
userRsa.GenerateKeys(4096, 3, null, null);

var userCryptoKey = new CryptoKey(userRsa);


//Creating certificate signing request
var userSubjectName = new X509Name();
userSubjectName.Common = "Some Value";
userSubjectName.OrganizationUnit = "Some Value";
userSubjectName.Organization = "Some Value";
userSubjectName.StateOrProvince = "Some Value";
userSubjectName.StateOrProvince = "Some Value";
userSubjectName.Country = "US";

var userRequest = new X509Request(3, userSubjectName, userCryptoKey);
userRequest.Sign(userCryptoKey, MessageDigest.SHA1);


//Signing Certificate by authority (this part of code will be located in the separate CA Authority service)
X509Certificate userX509Certificate = signingX509CertificateAuthority.ProcessRequest(userRequest, DateTime.Now, DateTime.Now.AddYears(1), MessageDigest.SHA1);
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "basicConstraints", true, "CA:false"));
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "keyUsage", true, "nonRepudiation, digitalSignature, keyEncipherment, dataEncipherment, encipherOnly, decipherOnly, keyAgreement"));
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "extendedKeyUsage", true, "clientAuth"));
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "nsComment", true, "OpenSSL Generated Certificate"));
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "subjectKeyIdentifier", true, "hash"));
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "authorityKeyIdentifier", true, "keyid,issuer:always"));
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "issuerAltName", true, "issuer:copy"));
userX509Certificate.AddExtension(new X509Extension(signingX509Certificate, userX509Certificate, "crlDistributionPoints", true, "URI:http://ok/certEnroll/ok-ca.crl"));
userX509Certificate.Sign(signingCryptoKey, MessageDigest.SHA1);


//CA chain
var userX509Chain = new X509Chain();
userX509Chain.Add(rootX509Certificate);
userX509Chain.Add(signingX509Certificate);


//Creating Personal Information Exchange (PFX) Certificate
var userPkcs12 = new PKCS12("1", "Olexiy Kubliy", userCryptoKey, userX509Certificate, new Stack<X509Certificate>(), PKCS12.PBE.SHA1_3DES, PKCS12.PBE.SHA1_3DES, 1);

Этот код работает нормально, но есть одна проблема.Я хочу сгенерировать ключ и PFX на смарт-карте, поэтому шаги генерации сертификата должны быть следующими:

1.) Создать пару открытого / секретного ключей на смарт-карте;

2.) Создатьзапрос подписи сертификата через OpenSLL и подпись его с помощью закрытого ключа на смарт-карте;

3.) Отправьте запрос на подпись сертификата в нашу службу CA Authority и подпишите его сертификатом CA;

4.) Получить сертификат (подписанный CA Authority), подписать его закрытым ключом, хранящимся на смарт-карте, и сгенерировать pfx на смарт-карте;

Для работы со смарт-картами я использовал Microsoft CryptoAPI и NCryptoki,NET библиотека, но самое большее, что я мог сделать, это сгенерировать пару открытый / закрытый ключ на смарт-карте и импортировать сертификат pfx с цепочкой CA на смарт-карту.Я не могу подписать запрос сертификата и сертификат, полученный из собственного центра сертификации.

Использование Microsoft CryptoAPI:

const string provider = "etoken Base Cryptographic Provider";
const Int32 AT_KEYEXCHANGE = 1;
const Int32 PRIVATEKEYBLOB = 0x7;
const int KP_CERTIFICATE = 26;

const uint type = PROV_RSA_FULL;
IntPtr cardCryptoProvider = IntPtr.Zero;
if (!Win32.CryptAcquireContext(ref cardCryptoProvider, null, provider, type, Win32.CRYPT_NEWKEYSET))
    showWin32Error(Marshal.GetLastWin32Error());

const string fileName = @"D:\test.pfx";
var fileinfo = new FileInfo(fileName);
var br = new BinaryReader(fileinfo.OpenRead());
byte[] Bytes = new byte[fileinfo.Length];
br.Read(Bytes, 0, (int)fileinfo.Length);
br.Close();

IntPtr buffer = Marshal.AllocHGlobal(Bytes.Length);
Marshal.Copy(Bytes, 0, buffer, Bytes.Length);

var cryptDataBlob = new Win32.CRYPT_DATA_BLOB();
cryptDataBlob.pbData = buffer;
cryptDataBlob.cbData = Bytes.Length;

IntPtr store = Win32.PFXImportCertStore(ref cryptDataBlob, "1", Win32.CRYPT_EXPORTABLE | Win32.CRYPT_USER_KEYSET);

IntPtr currentCertContext = IntPtr.Zero;
X509Certificate2 c = null;

do
{
    currentCertContext = Win32.CertEnumCertificatesInStore(store, currentCertContext);
    if (currentCertContext == IntPtr.Zero)
        break;

    uint sizeOfData = 0;

    if (!Win32.CertGetCertificateContextProperty(currentCertContext, 2, IntPtr.Zero, ref sizeOfData))
    {
        showWin32Error(Marshal.GetLastWin32Error());
        continue;
    }

    IntPtr pData = Marshal.AllocHGlobal((int)sizeOfData);

    if (!Win32.CertGetCertificateContextProperty(currentCertContext, 2, pData, ref sizeOfData))
        showWin32Error(Marshal.GetLastWin32Error());

    var pKeyProvInfo = (Win32.CRYPT_KEY_PROV_INFO)Marshal.PtrToStructure(pData, typeof(Win32.CRYPT_KEY_PROV_INFO));

    int cryptAcquireCertificatePrivateKeyFlags = AT_KEYEXCHANGE;
    IntPtr cryptoProvider = IntPtr.Zero;
    bool callerFree = false;

    if (!Win32.CryptAcquireCertificatePrivateKey(currentCertContext, 0, IntPtr.Zero,
        ref cryptoProvider, ref cryptAcquireCertificatePrivateKeyFlags, ref callerFree))
        showWin32Error(Marshal.GetLastWin32Error());

    IntPtr cryptKeyHandle = IntPtr.Zero;
    if (!Win32.CryptGetUserKey(cryptoProvider, AT_KEYEXCHANGE, ref cryptKeyHandle))
        showWin32Error(Marshal.GetLastWin32Error());


    uint pkSize = 0;
    if (!Win32.CryptExportKey(cryptKeyHandle, IntPtr.Zero, PRIVATEKEYBLOB, 0, null, ref pkSize))
        showWin32Error(Marshal.GetLastWin32Error());

    byte[] pPk = new byte[pkSize];

    if (!Win32.CryptExportKey(cryptKeyHandle, IntPtr.Zero, PRIVATEKEYBLOB, 0, pPk, ref pkSize))
        showWin32Error(Marshal.GetLastWin32Error());

    IntPtr importedKey = IntPtr.Zero;
    if (!Win32.CryptImportKey(cardCryptoProvider, pPk, (uint)pPk.Length, IntPtr.Zero, 0, ref importedKey))
        showWin32Error(Marshal.GetLastWin32Error());

    var cert = new X509Certificate(currentCertContext);

    if (!Win32.CryptSetKeyParam(importedKey, KP_CERTIFICATE, cert.GetRawCertData(), 0))
        showWin32Error(Marshal.GetLastWin32Error());

} while (currentCertContext != IntPtr.Zero);
if (cardCryptoProvider != IntPtr.Zero)
    Win32.CryptReleaseContext(cardCryptoProvider, 0);

с использованием NCryptoki.Библиотека NET:

Cryptoki cryptoki = new Cryptoki(@"C:\Windows\System32\eTPKCS11.dll");
cryptoki.Initialize();

Slot slot = null;

foreach (Slot slot1 in slots)
{
    if (slot1.IsTokenPresent)
    {
        slot = slot1;
        break;
    }
}

Token token = slot.Token;
Session session = token.OpenSession(Session.CKF_SERIAL_SESSION | Session.CKF_RW_SESSION, null, null);

if (session.Login(Session.CKU_USER, "12345678") != 0)
{
    Console.WriteLine("Wrong PIN");
    return;
}

var certificate = new X509Certificate2(@"C:\certificate.pfx", "1", X509KeyStorageFlags.Exportable);            
var keyPair = certificate.PrivateKey as RSA;

var collection = new X509Certificate2Collection();
collection.Import(@"C:\certificate.pfx", "1", X509KeyStorageFlags.PersistKeySet);            

RSAParameters keyParams = keyPair.ExportParameters(true);

var template = new CryptokiCollection();
template.Add(new ObjectAttribute(ObjectAttribute.CKA_CLASS, CryptokiObject.CKO_PRIVATE_KEY));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_KEY_TYPE, Key.CKK_RSA));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_EXTRACTABLE, true));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_MODULUS, keyParams.Modulus));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_PUBLIC_EXPONENT, keyParams.Exponent));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_PRIVATE_EXPONENT, keyParams.D));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_PRIME_1, keyParams.P));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_PRIME_2, keyParams.Q));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_EXPONENT_1, keyParams.DP));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_EXPONENT_2, keyParams.DQ));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_COEFFICIENT, keyParams.InverseQ));            
template.Add(new ObjectAttribute(ObjectAttribute.CKA_LABEL, "dddd"));
template.Add(new ObjectAttribute(ObjectAttribute.CKA_TOKEN, true));            
session.Objects.Create(template);

foreach (X509Certificate2 x509Certificate2 in collection)
{
    var template1 = new CryptokiCollection();
    template1.Add(new ObjectAttribute(ObjectAttribute.CKA_CLASS, CryptokiObject.CKO_CERTIFICATE));
    template1.Add(new ObjectAttribute(ObjectAttribute.CKA_PRIVATE, false));
    template1.Add(new ObjectAttribute(ObjectAttribute.CKA_CERTIFICATE_CATEGORY, x509Certificate2.HasPrivateKey ? 0 : 2));
    template1.Add(new ObjectAttribute(ObjectAttribute.CKA_CERTIFICATE_TYPE, Certificate.CKC_X_509));
    template1.Add(new ObjectAttribute(ObjectAttribute.CKA_VALUE, x509Certificate2.RawData));
    template1.Add(new ObjectAttribute(ObjectAttribute.CKA_SUBJECT, x509Certificate2.SubjectName.RawData));
    template1.Add(new ObjectAttribute(ObjectAttribute.CKA_TOKEN, true));

    session.Objects.Create(template1);
}

Кто-нибудь может мне помочь с подписанием запроса сертификата (сгенерированного с помощью Open SLL) и подписью сертификата, полученного из собственного центра сертификации, с помощью закрытого ключа, сохраненного на смарт-карте?

ИспользованиеNCryptoki Sign метод для подписи текста:

Cryptoki cryptoki = new Cryptoki(@"C:\Windows\System32\eTPKCS11.dll");
cryptoki.Initialize();
Slot slot = _cryptoki.ActiveSlots.FirstOrDefault();
Session session = slot.Token.OpenSession(Session.CKF_SERIAL_SESSION | Session.CKF_RW_SESSION, null, null);
session.Login(Session.CKU_USER, "12345678")

CryptokiCollection templatePub = new CryptokiCollection();
templatePub.Add(new ObjectAttribute(ObjectAttribute.CKA_CLASS, CryptokiObject.CKO_PUBLIC_KEY));
templatePub.Add(new ObjectAttribute(ObjectAttribute.CKA_TOKEN, true));
templatePub.Add(new ObjectAttribute(ObjectAttribute.CKA_PRIVATE, true));
templatePub.Add(new ObjectAttribute(ObjectAttribute.CKA_LABEL, "My Key"));
templatePub.Add(new ObjectAttribute(ObjectAttribute.CKA_ID, "1"));
templatePub.Add(new ObjectAttribute(ObjectAttribute.CKA_MODULUS_BITS, 1024));

CryptokiCollection templatePri = new CryptokiCollection();
templatePri.Add(new ObjectAttribute(ObjectAttribute.CKA_CLASS, CryptokiObject.CKO_PRIVATE_KEY));
templatePri.Add(new ObjectAttribute(ObjectAttribute.CKA_TOKEN, true));
templatePri.Add(new ObjectAttribute(ObjectAttribute.CKA_PRIVATE, true));
templatePri.Add(new ObjectAttribute(ObjectAttribute.CKA_LABEL, "My Key"));
templatePri.Add(new ObjectAttribute(ObjectAttribute.CKA_ID, "1"));

Key[] keys = session.GenerateKeyPair(Mechanism.RSA_PKCS_KEY_PAIR_GEN, templatePub, templatePri);
var privateKey = (RSAPrivateKey)keys[1];

const string text = "Some Value";
byte[] textBytes = Encoding.ASCII.GetBytes(text);
int result = session.SignInit(Mechanism.SHA1_RSA_PKCS, privateKey);
byte[] signature = session.Sign(textBytes);

1 Ответ

2 голосов
/ 29 марта 2011

Вы неправильно понимаете некоторые аспекты своей задачи.

Во-первых, вам не нужно повторно подписывать сертификат, полученный от ЦС, своим закрытым ключом (шаг 4). Он будет уже подписан секретным ключом CA.

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

В-третьих, аппаратные токены обычно не позволяют экспортировать закрытые ключи, то есть вы не сможете создать файл PFX сертификат + ключ, если на токене была сгенерирована пара ключей.

Теперь к вашему вопросу.

Насколько мне известно, OpenSSL не поддерживает доступ к ключам, хранящимся в HSM, т. Е. Вы просто не сможете получить закрытый ключ для подписания запроса. Тем не менее, такие ключи могут быть доступны с помощью CryptoAPI точно так же, как и обычные системные ключи, если поставщик токена поставляет соответствующий CSP (который на самом деле является слоем между CryptoAPI и драйвером токена низкого уровня). Однако, поскольку такие ключи не экспортируются, вы сможете использовать их только с функциями CryptoAPI, ссылаясь на них с помощью ручек ключей CryptoAPI. В то же время элемент управления регистрацией сертификатов CryptoAPI не поддерживает генерацию запросов на сертификаты из существующих закрытых ключей (всегда создаются новые пары ключей).

То есть у вас есть три варианта:

  1. CryptoAPI основе. Сформируйте запрос на сертификат BLOB (PKCS # 10) самостоятельно и подпишите его с помощью метода CryptSignHash (); затем добавьте созданную подпись в BLOB. Пара ключей может быть сгенерирована до формирования запроса либо методом CryptGenKey (), либо другими способами (например, некоторым компонентом или инструментом PKCS # 11).

  2. OpenSSL основе. Сгенерируйте новую пару ключей (вам придется хранить ее в файлах) и запросите сертификат. Импортируйте пару ключей в токен. Уничтожить закрытый ключ, хранящийся в файле.

  3. Третий. Используйте некоторую библиотеку PKCS # 11, чтобы сгенерировать пару ключей и подписать запрос. Как и в варианте 1, вам нужно будет сформировать запрос самостоятельно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...