启动 COPP 会话

[与此页面关联的功能 DirectShow 是一项旧功能。 它已被 MediaPlayerIMFMediaEngine媒体基金会中的音频/视频捕获取代。 这些功能已针对Windows 10和Windows 11进行了优化。 Microsoft 强烈建议新代码尽可能使用 MediaPlayerIMFMediaEngineMedia Foundation 中的音频/视频捕获 ,而不是 DirectShow。 如果可能,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 标头中未定义与此布局匹配的结构。 可以定义一个或执行一些指针算术。 例如,若要验证密钥的大小,请执行以下操作:

DWORD *pcbKey = (DWORD*)(pData + sizeof(BLOBHEADER));
if (*pcbKey != 16)
{
    // Wrong size! Should be 16 bytes (128 bits).
}

若要获取指向键本身的指针,请执行以下操作:

BYTE *pKey = pData + sizeof(BLOBHEADER) + sizeof(DWORD);

接下来,生成一个 32 位随机数,用作 COPP 状态请求的起始序列。 创建随机数的建议方法是调用 CryptGenRandom 函数。 请勿在 C 运行时库中使用 rand 函数,因为它不是真正随机的。 生成第二个 32 位随机数,用作 COPP 命令的起始序列。

UINT uStatusSeq;     // Status sequence number.
UINT uCommandSeq;    // Command sequence number.
CryptGenRandom(hCSP, sizeof(UINT), &uStatusSeq);
CryptGenRandom(hCSP, sizeof(UINT), &uCommandSeq);

现在可以准备 COPP 签名。 这是一个 256 字节数组,定义为 AMCOPPSignature 结构。 将数组的内容初始化为零。 然后,将四个数字复制到数组中-驱动程序的随机数、AES 键、状态序列号和命令序列号(按该顺序排列)。 最后,交换整个数组的字节顺序。

根据 CryptEncrypt 的文档:

“通过调用具有 RSA 密钥的 CryptEncrypt** 可以加密的纯文本数据的长度是密钥取模减去 11 个字节的长度。 11 个字节是 PKCS \#1 填充选择的最小值。”

在这种情况下,取模为 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)