Инициализация сеанса 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 и запросы состояния в драйвер.
Связанные темы