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);

このコードは問題なく動作しますが、1 つ問題があります。スマートカードでキーと PFX を生成したいので、証明書の生成手順は次のとおりです。

1.) スマート カードで公開鍵と秘密鍵のペアを生成します。

2.) OpenSLL で証明書署名要求を作成し、スマートカードの秘密鍵で署名します。

3.) 証明書署名要求を CA 機関サービスに送信し、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を使用。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 で生成された) 証明書要求に署名し、スマートカードに保存されている秘密鍵を使用して、独自の 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 の秘密鍵によってすでに署名されています。

第 2 に、PFX は証明書を格納できる形式にすぎません。PFX は、パスワードで暗号化された証明書と秘密鍵をファイルに保持するために使用されます。証明書をスマート カードに格納する場合は使用されません。

第 3 に、ハードウェア トークンは通常、秘密キーのエクスポートを許可しません。つまり、トークンでキー ペアが生成されている場合、証明書とキーの PFX ファイルを作成することはできません。

今あなたの質問に。

私の知る限り、OpenSSL は HSM に保存されたキーへのアクセスをサポートしていません。つまり、リクエストに署名するために秘密キーを取得することはできません。ただし、トークンのベンダーが適切な CSP (実際には、CryptoAPI と低レベルのトークン ドライバーの間のレイヤー) を出荷している場合、通常のシステム ベースのキーと同様に、CryptoAPI を使用してそのようなキーにアクセスできます。ただし、このようなキーはエクスポートできないため、CryptoAPI キー ハンドルで参照する CryptoAPI 関数でのみ使用できます。同時に、CryptoAPI の証明書登録制御は、既存の秘密鍵からの証明書要求の生成をサポートしていません (常に新しい鍵ペアが生成されます)。

つまり、次の 3 つのオプションがあります。

  1. CryptoAPI ベース。証明書要求 BLOB (PKCS#10) を自分で作成し、CryptSignHash() メソッドを使用して署名します。次に、作成した署名を BLOB に追加します。鍵ペアは、CryptGenKey() メソッドまたは他の手段 (PKCS#11 コンポーネントまたはツールなど) を介して、要求を形成する前に生成できます。

  2. OpenSSL ベース。新しい鍵ペア (ファイルに保存する必要があります) と証明書要求を生成します。キーペアをトークンにインポートします。ファイルに保存されている秘密鍵を破棄します。

  3. 第3。いくつかの PKCS#11 ライブラリを使用してキーペアを生成し、リクエストに署名します。オプション 1 と同様に、自分でリクエストを作成する必要があります。

于 2011-03-28T20:56:23.633 に答える