启动 COPP 会话

若要启动认证输出保护协议 (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 函数会创建对称密钥,但密钥仍保留在云解决方案提供商中。 若要将键导出到字节数组中,请使用 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)