web-dev-qa-db-ja.com

スマートカードにある証明書を秘密鍵の所有権を使用してMicrosoftストアに追加する

スマートカードからローカルユーザーストアに証明書を追加するときにCryptAcquireCertificatePrivateKey0x8009200Bで失敗する問題を解決するにはどうすればよいですか?

ユーザーがCAに対してオンラインでスマートカード証明書要求を生成する登録システムでは、証明書はスマートカードに「オフライン」でロードされます。たとえば、要求が発行されてから数日後に、要求の作成に使用されるcertenrolllibオブジェクトは使用できません。カードとカードに証明書をインストールすると、秘密鍵が生成されます。秘密鍵は決して​​カードの外にエクスポートすることはできません。

カードに証明書をロードするとき、Minidriver APIを使用します。キーの生成に使用されたキーコンテナーの名前(通常はlr-e46f1586-7133-4284-895d-557e2261c24dのようなGUID)があり、cmapfileの読み取りを開始しますGUIDに対応するキーコンテナーインデックスXXを取得し、カードミニドライバーファイルシステムのmscpフォルダーにkscXX証明書を作成します。CAから取得したDERバイナリ証明書をzlibで圧縮します。ヘッダーを追加し、それをkscXXファイルにロードします。

カード上の証明書が適切な形式であり、さまざまなツールを使用してそれを確認できることがわかります。問題は、この証明書がMicrosoftユーザーストアに表示されないことです。実際、特にコンテナーがデフォルトのコンテナーである場合は、「表示される」ことがありますが、いずれにしてもすぐに表示されるわけではなく、すべてのOSに表示されるわけではありません。

証明書のコンテキストを取得し、次のコードで CryptAcquireCertificatePrivateKey を使用して秘密キーの所有権を証明するMSCAPIを使用してライブラリを作成しました(たとえば、addCardCertToStore.exeツールで使用されています)。

PINでのロギング、ユーザーキーの取得など:

 fStatus = CryptGetKeyParam(
    hKey,                  // HCRYPTKEY hKey,
    KP_CERTIFICATE,        // DWORD dwParam,
    pCertBlob,             // BYTE* pbData,
    &dwCertLen,            // DWORD* pdwDataLen,
    0                      // DWORD dwFlags
    );

if (!fStatus)
{
    return 1;
}


pCertContext = CertCreateCertificateContext(
    PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
    pCertBlob,
    dwCertLen);

    //trying to prove privatekey ownership by calling CryptAcquireCertificatePrivateKey


HCRYPTPROV_OR_NCRYPT_KEY_HANDLE  hCrypt;

    DWORD  hInfo;

fStatus=CryptAcquireCertificatePrivateKey(pCertContext,0,NULL,&hCrypt,&hInfo,NULL);

     if (!fStatus)
{
     return 1;
}



    hStoreHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, 0, CERT_SYSTEM_STORE_CURRENT_USER, L"My");

//保存する証明書コンテキストを保存します

CryptAcquireCertificatePrivateKeyが呼び出されない場合、証明書は常にストアにエクスポートされますが、秘密鍵の所有権がないため、署名などの操作には使用できません。

ただし、CryptAcquireCertificatePrivateKeyを使用すると、実際には「50%の頻度」で証明書を保存して秘密鍵の所有権で保存できますが、常にではありませんが、0x8009200Bエラー(CRYPT_E_NO_KEY_PROPERTY、たとえば、「証明書が見つかりません」および復号化のための秘密鍵。 ')CryptAcquireCertificatePrivateKeyを呼び出すとき。このエラーは、たとえば最初にaddCardCertToStore.exeを起動したときに発生し、次にaddCardCertToStore.exeを再起動すると機能する可能性があります。CryptAcquireCertificatePrivateKeyはOKを返し、証明書がストアにインストールされます。3回呼び出す必要がある場合もあります。場合によっては完了しないように見えることもあれば、カードを再挿入した後に機能することもあります。証明書がストアにしばらくすると自動的にインストールされ、この証明書を削除することによって失敗することもあります。CryptAcquireCertificatePrivateKeyが機能し、ストアに証明書を再度追加してください。

CSPがキャッシュを維持していること、およびカードを定期的にスキャンしてキャッシュを更新していること、およびカードの新しい証明書を検出してストアに単独で追加していること、または一部のMSCAPI操作が運用自体とは無関係に、CSPにカード証明書からストアを更新させる。

最後に問題は、なぜCryptAcquireCertificatePrivateKeyのこのような奇妙な動作と、そのコンテキストでエラーコード0x8009200B(アクセス拒否)をどのように解釈できるかということです。
そして、ミニドライバーAPIを使用するのではなく、MSCAPI関数を使用して(可能な場合)、DER証明書をカードにインストールする必要がありますか?
カード証明書をストアに安定的に追加する方法を教えてください。

6
user49920

パズルには多くのピースがあり、どこにでもキャッシュできるため、これらは少し複雑になる可能性があります。

理論は、 CertEnroll (OS提供)を使用して、すべての魔法を実行することです。 CertEnrollは、要求の生成と証明書のインポートの間の長い遅延(数日でも)を完全にサポートできます。ただし、要求の生成とそれに続く証明書のインポートは、同じアカウントで同じマシンで実行する必要があります。その理由は、CertEnrollは、要求自体のコピーをカード外の専用ストアに保存することで、進行中の登録操作を追跡し、証明書が戻ってきたときに、秘密鍵を見つけてすべてのリンクを設定できるようにするためです。

(理論的には、ローカルマシンストアではなく、彼のストアで「通常のユーザー」として操作が行われる場合、リクエストは最終的に「ローミングプロファイル」になり、同じマシンを使用する必要がなくなります。条件のみアカウントで維持されます。ただし、これにはいくつかのテストが必要になります。)

実践は異なる場合がありますいくつかの理由があります。たとえば、証明書のインストールは、いくつかの専用ハードウェアシステムで行われます。 CertEnrollを使用できない場合、または一部のキャッシュでエラーが発生した場合(発生した場合)、次の点を考慮する必要があります。

  • 証明書から秘密鍵へのリンクは、Windows側で発生するものです。 CryptAcquireCertificatePrivateKey() を呼び出すと、実際にはcertificateコンテキストのプロパティ(つまり、Windows側のオブジェクト)に従って、CSPとコンテナー名が検索されます。そのCSP内で。内部的には、そのプロパティはCSPnameとコンテナー名で構成されます。 CertSetCertificateContextProperty() で設定できます。 CERT_KEY_PROV_INFO_PROP_IDを使用します。

    このようなプロパティは通常は永続的です(ストア構造に書き込まれ、通常はユーザーのローミングプロファイル内の一部のファイルに書き込まれます)が、RAM CERT_SET_PROPERTY_INHIBIT_PERSIST_FLAGを使用した場合のみ)に保持できます。プロパティはRAM=のみで設定されます。その場合、特定のCERT_CONTEXT構造にのみ影響し、当然、単一のプロセスに制限されます。

  • MiniDriver API仕様 で説明されているように、カードでは、cmapfileファイルとkxc*ファイルを使用して、証明書と秘密鍵の関係が異なります。 Windowsシステムにカードを挿入すると、そのシステムはカードの証明書を検査し、ローカルユーザーのストアにプッシュして、秘密キーへのリンクを設定することになっています。 「証明書伝播サービス」がそれを行っているので、それがシステムで開始されていることを確認してください。そうしないと、証明書の自動抽出は機能しません。

    「証明書の伝播」は、カードが挿入されるとトリガーされます。カードの内容を変更する場合は、特に「直接」変更する場合は、ユーザーにカードの抜き差しを指示する必要があります。証明書の伝達をトリガーするために知っている唯一の他の方法は、証明書の伝達サービスを停止して再起動することです。これには管理者権限が必要です。

    証明書の伝達は非同期であり、特にリモートデスクトップと組み合わせて、複数のUSBカードリーダーを同時に使用する場合、かなりの時間がかかる場合があります(最大1分程度)。

  • キャッシュがあります。「スマートカードサービス」(SCardSvr.exe)がキャッシュを保持しています。キャッシュはRAMベースですが、サービスは停止時にキャッシュをレジストリに書き込むため、キャッシュは再起動に抵抗します(レジストリキーはHKLM\SOFTWARE\Microsoft\Cryptography\Calais\Cache\Cacheです)。

    キャッシュは物事をスピードアップすることを意図しています:カードからデータを読むことは遅いI/Oを招きます。証明書、コンテナ名、さらには公開鍵もキャッシュに保存されます。キャッシュの同期を維持することが重要です。それ以外の場合、さまざまな問題が発生します。これらの問題は、説明した症状と互換性があります(署名を計算するときに、他にも取得できます。たとえば、BaseCSPが検証計算されたばかりの署名。これは、カードからではなく、キャッシュからの公開鍵を使用して行います)。

    キャッシュが最新と見なされるかどうかは、カードのcardcfファイルの内容によって異なります。これについては、MiniDriver APIのセクション5.4.3で説明されています。基本的に、そのファイルには2つの16ビットカウンターが含まれます。1つはコンテナーのコンテンツ(公開キー)用で、もう1つはファイル用です。コンテナーを変更する場合(たとえば、新しいキーペアを生成またはインポートする場合)またはファイルを変更する場合(そして実行する!)、その場合は[〜#〜] [〜#〜]cardcfの関連するカウンタをインクリメントします。通常、BaseCSPは必要に応じて自動的にそれを行いますが、カードを再初期化したり、永久的に変更したりする場合は、手動で行う必要がある場合があります(個人的にSCardSvrキャッシュにいくつかのバグがあるので、強制的にcardcf update with card-specific APDU commands)。

    または、カードを構成して、その情報のためにキャッシュを維持する必要がないことを報告するようにしてください。 MiniDriver APIのルックアップCP_CARD_CACHE_MODE。ただし、すべてのカードがキャッシュモードの変更をサポートしているわけではありません。

  • すでに複雑な状況を悪化させるのは [〜#〜] cng [〜#〜] です。これは、まったく新しい暗号化APIです。 CryptoAPIとCNGの間にいくつかのブリッジがあります。例えばCryptAcquireCertificatePrivateKey()のCRYPT_ACQUIRE_ALLOW_NCRYPT_KEY_FLAGCERT_KEY_PROV_INFO_PROP_IDプロパティを設定する場合、CryptoAPI BaseCSP名( "Microsoft Base Smart Card Crypto Provider")を対応するCryptoNG名( "Microsoft Smart Card Key Storage Provider")に置き換えることができます。これにより、CNGを認識しない一部のアプリケーションが混乱する可能性があります。

    カードPIN=の処理に関しては、CryptoAPIおよびCNGでのプレイが必要になる場合があります。ユーザーの操作を容易にするために、CryptoAPIとCNGの両方がユーザーPINのプロセスごとのメモリ内キャッシュを維持します。ただし、2つのキャッシュは異なります!さらに、最近のカード(つまり、MiniDriver v5ではなくMiniDriver v7をサポートするカード)では、「セッションPIN」が自動的に生成され、キャッシュはセッションのコピーを保持しますPIN=人間のユーザーに知られているように。新しいセッションの生成をトリガーするPINこれはセッションを無効にしますPIN他のセッションでは。それについてめったに満足しない人。

    特に、Windows 7上のInternet Explorer、より一般的にはSSLクライアントコードは、証明書ベースのクライアント認証のために秘密鍵にアクセスするときに、CNGの使用を強制する傾向があります。同じアプリケーション(IEに読み込まれたActiveXコントロールなど)内で同じキーを使用する場合は、CNGも使用する必要があります-そうでない場合、PIN戦いは激怒します。

6
Thomas Pornin