2

我正在使用 .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);
4

1 回答 1

2

你误解了你的任务的某些方面。

首先,您不需要使用您的私钥重新签署从 CA 收到的证书(步骤 4)。它已经由 CA 的私钥签名。

其次,PFX 只是一种可以存储证书的格式。PFX 用于将密码加密的证书和私钥保存在文件中;在智能卡上存储证书时不使用它。

第三,硬件令牌通常不允许导出私钥,也就是说,如果令牌上已生成密钥对,您将无法创建证书+密钥 PFX 文件。

现在回答你的问题。

据我所知,OpenSSL 不支持访问存储在 HSM 上的密钥,即您将无法获取私钥来签署请求。然而,如果令牌供应商提供适当的 CSP(实际上是 CryptoAPI 和低级令牌驱动程序之间的一层),则可以使用 CryptoAPI 访问此类密钥,就像通常的基于系统的密钥一样。但是,由于此类密钥不可导出,因此您只能将它们与 CryptoAPI 函数一起使用,并通过 CryptoAPI 密钥句柄引用它们。同时,CryptoAPI 的证书注册控制不支持从现有私钥生成证书请求(总是生成全新的密钥对)。

也就是说,您有以下三个选项:

  1. 基于 CryptoAPI。自己形成证书请求 BLOB (PKCS#10) 并使用 CryptSignHash() 方法对其进行签名;然后将创建的签名附加到 BLOB。可以在形成请求之前通过 CryptGenKey() 方法或通过其他方式(例如,某些 PKCS#11 组件或工具)生成密钥对。

  2. 基于 OpenSSL。生成一个全新的密钥对(您将被迫将其存储在文件中)和证书请求。将密钥对导入令牌。销毁存储在文件中的私钥。

  3. 第三。使用一些 PKCS#11 库来生成密钥对并对请求进行签名。与选项 1 一样,您需要自己提出请求。

于 2011-03-28T20:56:23.633 回答