Поделиться через


Инициализация сеанса COPP

[Функция, связанная с этой страницей DirectShow, является устаревшей функцией. Он был заменен MediaPlayer, IMFMediaEngine, и аудио/ видео захвата в Media Foundation. Эти функции оптимизированы для Windows 10 и Windows 11. Корпорация Майкрософт настоятельно рекомендует, чтобы новый код использовал MediaPlayer, IMFMediaEngine и аудио- и видеозахват в Media Foundation вместо DirectShow, когда это возможно. Корпорация Майкрософт предлагает переписать существующий код, использующий устаревшие API, чтобы по возможности использовать новые API.]

Чтобы инициировать сеанс COPP, необходимо подготовить сигнатуру, которая представляет собой массив, содержащий объединение следующих чисел:

  • 128-битовое случайное число, возвращаемое драйвером. (Это значение отображается как guidRandom в разделе Получение цепочки сертификатов драйвера.)
  • 128-разрядный симметричный ключ AES.
  • 32-разрядный начальный порядковый номер для запросов о состоянии.
  • 32-разрядный начальный порядковый номер для команд COPP.

Создайте симметричный ключ 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 . Это причина использования флага CRYPT_EXPORTABLE при вызове CryptGenKey. Следующий код экспортирует ключ и копирует его в

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 . Не используйте функцию rand в библиотеке времени выполнения C, так как она не является действительно случайной. Создайте второе 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:

"Длина данных в виде открытого текста, которые можно зашифровать с помощью вызова **CryptEncrypt** с ключом RSA, составляет длину модуля ключа минус одиннадцать байтов. Одиннадцать байтов — это выбранный минимум для заполнения 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)