Я использую .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);