2

私が作成しているアプリケーションの一部として、プログラムがローカルマシンに証明書を一時的にインストールし、WinHTTPがWebサーバーに接続するときにそれをクライアント証明書として使用することを望んでいます。これの目的は、不正アクセスからWebサーバーを保護することです(この証明書はセキュリティの層にすぎません。誰かが.exeから抽出できることを知っています)。ユーザーが証明書をインストールする必要がないようにし、アプリケーションが実行されていないときに証明書をPCに残したくない。

現在、私はこれを試しています:

.p12ファイルから証明書を手動でインストールします

C ++アプリケーションを使用して、バイナリデータをローカル証明書からアプリケーションのC配列に取得します(CryptExportKeyおよびPCCERT_CONTEXT :: pbCertEncodedを使用)

証明書をアンインストールします

アプリケーションの起動時:

証明書の一時ストアを開きます

m_certificateStoreHandle = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, NULL, 0, NULL );

CertAddEncodedCertificateToStoreを呼び出して、証明書を追加します

CertAddEncodedCertificateToStore( m_certificateStoreHandle,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 
reinterpret_cast< const BYTE * >( certificateData ),
dataSize, 
CERT_STORE_ADD_REPLACE_EXISTING,
&m_clientCertificate )

CryptAcquireContextを呼び出して、秘密鍵を格納する場所を取得します(現時点では、実行ごとに鍵の名前を変更します。理想的には、CRYPT_VERIFYCONTEXTを使用して鍵を非永続化する予定ですが、今のところ無視する必要があります)

HCRYPTPROV cryptProvider = NULL;
HCRYPTKEY cryptKey = NULL;
CryptAcquireContext( &cryptProvider, "MyTestKeyNumber123", NULL, PROV_RSA_FULL, CRYPT_NEWKEYSET )

CryptImportKeyを呼び出して、秘密鍵を鍵ストアにロードします

CryptImportKey( cryptProvider, reinterpret_cast< BYTE * >( privateKey ), keySize, 0, CRYPT_EXPORTABLE, &cryptKey )

CertSetCertificateContextPropertyを呼び出して、証明書を秘密鍵にリンクします

char containerName[128];
DWORD containerNameSize = ARRAY_NUM_BYTES(containerName);
char providerName[128];
DWORD providerNameSize = ARRAY_NUM_BYTES(providerName);

CryptGetProvParam(cryptProvider, PP_CONTAINER, reinterpret_cast<byte *>(containerName), &containerNameSize, 0)
CryptGetProvParam(cryptProvider, PP_NAME, reinterpret_cast<byte *>(providerName), &providerNameSize, 0)

WCHAR containerNameWide[128];
convertCharToWChar(containerNameWide, containerName);
WCHAR providerNameWide[128];
convertCharToWChar(providerNameWide, providerName);

CRYPT_KEY_PROV_INFO privateKeyData;
neMemZero(&privateKeyData, sizeof(privateKeyData));
privateKeyData.pwszContainerName = containerNameWide;
privateKeyData.pwszProvName = providerNameWide;
privateKeyData.dwProvType = 0;
privateKeyData.dwFlags = CRYPT_SILENT;
privateKeyData.dwKeySpec = AT_KEYEXCHANGE;

if ( CertSetCertificateContextProperty( m_clientCertificate, CERT_KEY_PROV_INFO_PROP_ID, 0, &privateKeyData ) )

秘密鍵にアクセスできることを確認します(動作し、アプリケーションにハードコーディングしたものとまったく同じデータを出力します)

byte privateKeyBuffer[2048];
DWORD privateKeyBufferSize = ARRAY_NUM_BYTES(privateKeyBuffer);
memZero(privateKeyBuffer, privateKeyBufferSize);
if(CryptExportKey(cryptKey, 0, PRIVATEKEYBLOB, 0, privateKeyBuffer, &privateKeyBufferSize))
{
   TRACE("Got private key!");
   LOG_BUFFER(privateKeyBuffer, privateKeyBufferSize);
}

クライアント証明書が期待どおりに機能することを確認してください

char certNameBuffer[128] = "";
char certUrlBuffer[128] = "";
CertGetNameString(testValue, CERT_NAME_FRIENDLY_DISPLAY_TYPE, 0, NULL, certNameBuffer, ARRAY_NUM_BYTES(certNameBuffer));
CertGetNameString(testValue, CERT_NAME_URL_TYPE , 0, NULL, certUrlBuffer, ARRAY_NUM_BYTES(certUrlBuffer));
TRACE("SSL Certificate %s [%s]", certNameBuffer, certUrlBuffer);

HCRYPTPROV_OR_NCRYPT_KEY_HANDLE privateKey;
DWORD privateKeyType;
BOOL freeKeyAfter = false;
if(CryptAcquireCertificatePrivateKey(testValue, CRYPT_ACQUIRE_NO_HEALING, NULL, &privateKey, &privateKeyType, &freeKeyAfter))
{
    HCRYPTPROV privateKeyProvider = static_cast<HCRYPTPROV>(privateKey);
    HCRYPTKEY privateKeyHandle;
    if(CryptGetUserKey(privateKeyProvider, privateKeyType, &privateKeyHandle))
    {
        NEbyte privateKeyBuffer[2048];
        DWORD privateKeyBufferSize = NE_ARRAY_NUM_BYTES(privateKeyBuffer);
        neMemZero(privateKeyBuffer, privateKeyBufferSize);
        if(CryptExportKey(privateKeyHandle, 0, PRIVATEKEYBLOB, 0, privateKeyBuffer, &privateKeyBufferSize))
        {
            NE_TRACE("Got private key!");
            HTTP_LOG_BUFFER(neGetGlobalTraceLog(), "Key", "", privateKeyBuffer, privateKeyBufferSize);
        }

この段階で秘密鍵は検出されますが、CryptExportKeyの呼び出しはNTE_BAD_KEY_STATEで失敗します。WinHTTPでクライアント証明書を使用しようとすると、ERROR_WINHTTP_CLIENT_CERT_NO_ACCESS_PRIVATE_KEYが表示されます。誰かが疑問に思っている場合は、WinHTTPに次のコードでクライアント証明書を使用するように指示します。

if ( !WinHttpSetOption( handle,
                                WINHTTP_OPTION_CLIENT_CERT_CONTEXT,
                                const_cast<PCERT_CONTEXT>(m_clientCertificate),
                                sizeof( CERT_CONTEXT ) ) )
        {
            HTTP_LOG_ERROR( getLog(), "Setting the client certificate failed with error code %x", GetLastError() );
        }

私の見方では、秘密鍵と証明書を何らかの方法でリンクし、CryptAcquireCertificatePrivateKeyとCryptExportKeyを使用して鍵データを取り戻すことができるようになるまで、WinHTTPは成功する可能性がありません。

秘密鍵を使用するための証明書を取得できないように見える理由について何か考えはありますか?

4

1 に答える 1

1

私はこのアプローチをうまく機能させることができませんでした。代わりに、使用したい証明書と秘密鍵を含む生の.p12ファイルと一緒にPFXImportCertStoreを使用することになりました。

私が見たところ、PFXImportCertStoreはメモリ内に新しいストアを作成するようです。私が知ることができなかった唯一のことは、秘密鍵がメモリにも保存されているのか、それともPCのどこかに永久に保存されているのかということです。どちらかの方法が見つかったら、この回答を更新します。

m_clientCertificateStoreHandle = PFXImportCertStore(&pfxData, certificatePassword, 0);
if(NULL != m_clientCertificateStoreHandle)
{
m_clientCertificateHandle = CertFindCertificateInStore( m_clientCertificateStoreHandle, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL );
}
于 2011-09-02T12:53:02.793 に答える