COPP セッションの開始
[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayer、IMFMediaEngine、および Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayer、IMFMediaEngine、Audio/Video Capture を使用することを強くお勧めします。 Microsoft は、従来の API を使用する既存のコードを、可能であれば新しい API を使用するように書き直すよう提案しています。]
認定出力保護プロトコル (COPP) セッションを開始するには、 署名 (次の数値の連結を含む配列) を準備する必要があります。
- ドライバーによって返される 128 ビットの乱数。 (この値は、ドライバーの証明書チェーンの取得に guidRandom として表示されます)。
- 128 ビット対称 AES キー。
- 状態要求の 32 ビット開始シーケンス番号。
- COPP コマンドの 32 ビット開始シーケンス番号。
次のように対称 AES キーを生成します。
DWORD dwFlag = 0x80; // Bit length: 128-bit AES.
dwFlag <<= 16; // Move this value to the upper 16 bits.
dwFlag |= CRYPT_EXPORTABLE; // We want to export the key.
CryptGenKey(
hCSP, // Handle to the CSP.
CALG_AES_128, // Use 128-bit AES block encryption algorithm.
dwFlag,
&m_hAESKey // Receives a handle to the AES key.
);
CryptGenKey 関数は対称キーを作成しますが、キーは引き続き CSP に保持されます。 キーをバイト配列にエクスポートするには、 CryptExportKey 関数を使用します。 これは、 CryptGenKey を呼び出すときに CRYPT_EXPORTABLE フラグを使用する理由です。 次のコードは、キーをエクスポートし、 にコピーします。
pData
配列。
DWORD cbData = 0;
BYTE *pData = NULL;
// Get the size of the blob.
CryptExportKey(hAESKey, 0, PLAINTEXTKEYBLOB, 0, NULL, &cbData);
// Allocate the array and call again.
pData = new BYTE[cbData];
CryptExportKey(hAESKey, 0, PLAINTEXTKEYBLOB, 0, pData, &cbData);
で返されるデータ
pData
には、次のレイアウトがあります。
BLOBHEADER header
DWORD cbSize
BYTE key[]
ただし、CryptoAPI ヘッダーでは、このレイアウトに一致する構造体は定義されていません。 1 つを定義するか、ポインターの算術演算を実行できます。 たとえば、キーのサイズを確認するには、次のようにします。
DWORD *pcbKey = (DWORD*)(pData + sizeof(BLOBHEADER));
if (*pcbKey != 16)
{
// Wrong size! Should be 16 bytes (128 bits).
}
キー自体へのポインターを取得するには:
BYTE *pKey = pData + sizeof(BLOBHEADER) + sizeof(DWORD);
次に、COPP 状態要求の開始シーケンスとして使用する 32 ビットの乱数を生成します。 乱数を作成する推奨される方法は、 CryptGenRandom 関数を呼び出す方法です。 C ランタイム ライブラリでは rand 関数を使用しないでください。これは、実際にはランダムではありません。 COPP コマンドの開始シーケンスとして使用する 2 番目の 32 ビット乱数を生成します。
UINT uStatusSeq; // Status sequence number.
UINT uCommandSeq; // Command sequence number.
CryptGenRandom(hCSP, sizeof(UINT), &uStatusSeq);
CryptGenRandom(hCSP, sizeof(UINT), &uCommandSeq);
これで、COPP 署名を準備できます。 これは、 AMCOPPSignature 構造体として定義された 256 バイトの配列です。 配列の内容を 0 に初期化します。 次に、ドライバーの乱数、AES キー、状態シーケンス番号、コマンド シーケンス番号の 4 つの番号を配列にコピーします。 最後に、配列全体のバイト順を入れ替えます。
CryptEncrypt のドキュメントによると、
- "RSA キーを使用して **CryptEncrypt** を呼び出して暗号化できるプレーンテキスト データの長さは、キーの剰余から 11 バイトを引いた長さです。 PKCS \#1 パディングで選択された最小値は 11 バイトです。
この場合、剰余は 256 バイトであるため、メッセージの最大長は 245 バイト (256 ~ 11) です。 AMCOPPSignature 構造体は 256 バイトですが、署名内の意味のあるデータは 40 バイトのみです。 次のコードは、署名を暗号化し、結果を提供します。
CoppSig
.
AMCOPPSignature CoppSig;
ZeroMemory(&CoppSig, sizeof(CoppSig));
// Copy the signature data into CoppSig. (Not shown.)
// Encrypt the signature:
const DWORD RSA_PADDING = 11; // 11-byte padding.
DWORD cbDataOut = sizeof(AMCOPPSignature);
DWORD cbDataIn = cbDataOut - RSA_PADDING;
CryptEncrypt(
hRSAKey,
NULL, // No hash object.
TRUE, // Final block to encrypt.
0, // Reserved.
&CoppSig, // COPP signature.
&cbDataOut,
cbDataIn
);
次に、暗号化された配列を IAMCertifiedOutputProtection::SessionSequenceStart に渡します。
hr = pCOPP->SessionSequenceStart(&CoppSig);
if (SUCCEEDED(hr))
{
// Ready to send COPP commands and status requests.
}
このメソッドが成功した場合は、COPP コマンドと状態要求をドライバーに送信する準備が整います。
関連トピック