我正在使用 .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 授权签署),用存储在智能卡上的私钥对其进行签名,并在智能卡上生成 pfx;
为了使用智能卡,我使用了 Microsoft CryptoAPI 和 NCryptoki。NET 库,但我最多只能在智能卡上生成公钥/私钥对,并在智能卡上使用 CA 链导入 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。网络库:
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 生成)并使用存储在智能卡上的私钥签署从自己的 CA 机构检索到的证书吗?
使用 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);