COPP 세션 시작
[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngine 및 Media Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드에서 DirectShow 대신 MediaPlayer, IMFMediaEngine 및 오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]
COPP(Certified Output Protection Protocol) 세션을 시작하려면 다음 숫자의 연결이 포함된 배열인 서명을 준비해야 합니다.
- 드라이버에서 반환한 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);
다음으로 COPP 상태 요청의 시작 시퀀스로 사용할 32비트 난수를 생성합니다. 난수를 만드는 권장 방법은 CryptGenRandom 함수를 호출하는 것입니다. 실제로 임의가 아니므로 C 런타임 라이브러리에서 rand 함수를 사용하지 마세요. COPP 명령의 시작 시퀀스로 사용할 두 번째 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 키, 상태 시퀀스 번호 및 명령 시퀀스 번호 등 네 개의 숫자를 배열에 해당 순서로 복사합니다. 마지막으로 전체 배열의 바이트 순서를 교환합니다.
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 명령 및 상태 요청을 드라이버에 보낼 준비가 된 것입니다.
관련 항목