IBackgroundCopyServerCertificateValidationCallback
COM公式ドキュメント
HTTPS 接続が開かれるときに、サーバー証明書が送信されます。それらのサーバー証明書を検証するために呼び出されるコールバックを実装するには、このメソッドを使用します。
メソッド 1
vtbl = vtable インデックス(0始まり)。HSP等からCOMメソッドをインデックス指定で呼ぶ際に使用します。0〜2 は IUnknown。
HTTPS 接続が開かれるときに送信されるサーバー証明書を検証できるように呼び出される、実装用のコールバックメソッドです。
| job | IBackgroundCopyJob* | in | ジョブです。 |
| file | IBackgroundCopyFile* | in | 転送中のファイルです。 |
| certLength | DWORD | in | 証明書データの長さ (バイト単位) です。 |
| certData | BYTE* | in | 証明書データを格納するバイト配列です。バイト数は certLength と一致している必要があります。 |
| certEncodingType | DWORD | in | 証明書のエンコード種別です。 |
| certStoreLength | DWORD | in | 証明書ストアデータの長さ (バイト単位) です。 |
| certStoreData | BYTE* | in | 証明書ストアデータを格納するバイト配列です。バイト数は certStoreLength と一致している必要があります。 |
戻り値
解説(Remarks)
証明書の検証は 2 つのフェーズで実行されます。第 1 フェーズはオペレーティングシステム (OS) のフェーズで、OS が証明書に対して標準的な検証チェックを実行します。その後、OS のフェーズで証明書が合格した場合に、追加の検証を行うためにコールバックが呼び出されます。
サーバー証明書に対して独自のチェックを行いたい場合に、この検証メソッドを実装します。独自のチェックは、通常の OS による証明書検証チェックに追加して行われます。
検証メソッドが証明書を拒否した場合、ジョブは BG_JOB_STATE_TRANSIENT_ERROR に遷移し、ジョブのエラーコンテキストは BG_ERROR_CONTEXT_SERVER_CERTIFICATE_CALLBACK、エラーはコールバックが返した HRESULT になります。コールバックを呼び出せなかった場合 (たとえば、プログラムの終了後に BITS がサーバー証明書を検証する必要があった場合など) は、ジョブのエラーコードは BG_E_SERVER_CERT_VALIDATION_INTERFACE_REQUIRED になります。アプリケーションが次に実行されたときに、検証コールバックを再度設定してジョブを再開することで、このエラーを解消できます。
BITS がこのコールバックメソッドを呼び出すのは、IBackgroundCopyServerCertificateValidationCallback インターフェイスを実装し、それを IBackgroundCopyJobHttpOptions3::SetServerCertificateValidationInterface に渡した場合のみです。
検証インターフェイスはアプリケーションの終了時に無効になります。BITS は検証インターフェイスの情報を保持しません。そのため、証明書の検証要求を受け取りたい既存のジョブに対しては、アプリケーションの初期化処理で SetServerCertificateValidationInterface を呼び出す必要があります。
複数のアプリケーションが SetServerCertificateValidationInterface を呼び出してジョブの通知インターフェイスを設定した場合、通知を受け取るのは最後に呼び出したアプリケーションです。他のアプリケーションは通知を受け取りません。
証明書を検証する一般的な手順は次のとおりです。これらの手順はあくまで一例であり、実際の検証内容は開発者の制御下にあります。また、手順 5 ~ 7 は、OS が OS 検証ステップで行う内容とほぼ同じです。
certEncodingType、certData、certLengthを指定して CertCreateCertificateContext を呼び出し、CERT_CONTEXT を取得します。certStoreLengthとcertStoreDataで渡されたシリアル化済みメモリブロブを使用して、CRYPT_DATA_BLOB 構造体 (wincrypt.hで定義) を宣言し、初期化します。
DATA_BLOB storeData{};
storeData.cbData = certStoreLength;
storeData.pbData = const_cast<PBYTE>(certStoreData);
- CERT_STORE_PROV_SERIALIZED、0、nullptr、フラグ、および手順 2 の CRYPT_DATA_BLOB へのポインターを指定して CertOpenStore を呼び出し、証明書チェーンへのハンドルを取得します。
- nullptr、
certContext、nullptr、手順 3 のハンドル、チェーンパラメーター、フラグ、nullptr を指定して CertGetCertificateChain を呼び出し、証明書チェーンコンテキストへのポインターを取得します。 - 証明書の検証ポリシーを作成します。
CERT_CHAIN_POLICY_PARA policyParams{};
policyParams.cbSize = sizeof(policyParams);
policyParams.dwFlags =
CERT_CHAIN_POLICY_IGNORE_NOT_TIME_VALID_FLAG |
CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG |
CERT_CHAIN_POLICY_IGNORE_INVALID_NAME_FLAG |
CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG;
- ポリシー種別、チェーンコンテキスト、ポリシーパラメーター、ポリシーステータスを指定して CertVerifyCertificateChainPolicy を呼び出します。
- Win32 エラー (
policyStatus.dwError) を HRESULT に変換して返します。
以下は、BITS の検証キャッシュの動作についての説明です。BITS は、カスタム検証に合格した証明書をジョブ単位でキャッシュします。これは、ジョブの存続期間中に冗長で処理コストの高い再検証が行われるのを避けるためです。キャッシュは <サーバーエンドポイント, 証明書ハッシュ> のタプルで構成され、エンドポイント は サーバー名:ポート として定義されます。特定のエンドポイントからの特定の証明書をジョブが既に許可している場合、コールバックは再度呼び出されません。
もちろん、証明書は接続を試行するたびに OS の検証ロジックを通過する必要があります (IBackgroundCopyJobHttpOptions::SetSecurityFlags を呼び出すことで OS の検証ロジックをカスタマイズできます)。これにより、証明書がごく最近 (秒単位で) は有効だったが現在は期限切れになっている、といった時間に依存するコーナーケースにも対応できます。
BITS は、アプリケーションが提供する検証コールバックによって無効と判断された証明書はキャッシュしません。アプリケーションレベルで悪意のある配置を検出できるように、失敗したすべての接続試行を把握しておくことが重要です。たとえば、単発の不正な証明書は、同一サーバーからの数千件の不正な証明書に比べれば、はるかに懸念度が低いと言えます。
ジョブの証明書キャッシュは、SetServerCertificateValidationInterface が呼び出されるたびにクリアされます。これは、アプリのサーバー証明書検証ロジックが変更されたことを示すためです。
Microsoft 公式リファレンス: 英語 (en-us) · 日本語 (ja-jp) · 原文ソース (GitHub)
HSP用 COM定義
#usecom / #comfunc によるHSPのCOM呼び出し定義。数字は vtbl インデックス(0始まり)。クラスIDが無い場合 #usecom の末尾は "{}"、ある場合は "{CLSID}"。
#define global IID_IBackgroundCopyServerCertificateValidationCallback "{4CEC0D02-DEF7-4158-813A-C32A46945FF7}" #usecom global IBackgroundCopyServerCertificateValidationCallback IID_IBackgroundCopyServerCertificateValidationCallback "{}" #comfunc global IBackgroundCopyServerCertificateValidationCallback_ValidateServerCertificate 3 sptr,sptr,int,var,int,int,var ; ※数字は vtbl インデックス(0始まり)。0/1/2 は IUnknown(QueryInterface/AddRef/Release)。 ; ※このインターフェースは直接 CoCreateInstance するクラスIDが無いため "{}"(他メソッド/アクティベーションで取得)。 ; ※出力/バッファ引数は var(変数直渡し)。varptr 方式にも切替可。 ; ※ハンドル/void*等の不透明ポインタは IronHSP では intptr 指定が可能。#define global IID_IBackgroundCopyServerCertificateValidationCallback "{4CEC0D02-DEF7-4158-813A-C32A46945FF7}" #usecom global IBackgroundCopyServerCertificateValidationCallback IID_IBackgroundCopyServerCertificateValidationCallback "{}" #comfunc global IBackgroundCopyServerCertificateValidationCallback_ValidateServerCertificate 3 sptr,sptr,int,sptr,int,int,sptr ; ※数字は vtbl インデックス(0始まり)。0/1/2 は IUnknown(QueryInterface/AddRef/Release)。 ; ※このインターフェースは直接 CoCreateInstance するクラスIDが無いため "{}"(他メソッド/アクティベーションで取得)。 ; ※出力/バッファ引数はポインタ方式(token=sptr / 呼び出しは varptr(変数))。 ; ※ハンドル/void*等の不透明ポインタは IronHSP では intptr 指定が可能。