次の方法で共有


COPP セッションの開始

[このページに関連付けられている機能 DirectShow は、従来の機能です。 MediaPlayerIMFMediaEngine、および Media Foundation のオーディオ/ビデオ キャプチャに置き換わりました。 これらの機能は、Windows 10とWindows 11用に最適化されています。 新しいコードでは、可能であれば、DirectShow ではなく Media Foundation で MediaPlayerIMFMediaEngineAudio/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 コマンドと状態要求をドライバーに送信する準備が整います。

認定出力保護プロトコル (COPP) の使用