Importing the Drivers Public Key
[The feature associated with this page, DirectShow, is a legacy feature. It has been superseded by MediaPlayer, IMFMediaEngine, and Audio/Video Capture in Media Foundation. Those features have been optimized for Windows 10 and Windows 11. Microsoft strongly recommends that new code use MediaPlayer, IMFMediaEngine and Audio/Video Capture in Media Foundation instead of DirectShow, when possible. Microsoft suggests that existing code that uses the legacy APIs be rewritten to use the new APIs if possible.]
The driver's RSA public key is contained in the Modulus and Exponent tags of the certificate's leaf node. Both values are base64-encoded and must be decoded. If you are using Microsoft's CryptoAPI, you must import the key into a cryptographic service provider (CSP), which is the module that implements the cryptographic algorithms.
To convert the modulus and exponents from base64 encoding to binary arrays, use the CryptStringToBinary function, as shown in the following code. Call the function once to get the size of the byte array. Then allocate the buffer and call the function again.
DWORD cbLen = 0, dwSkip = 0, dwFlags = 0;
::CryptStringToBinary(
pszModulus, // String that contains the Base64-encoded modulus.
cchModulus, // Length of the string, not including the trailing NULL.
CRYPT_STRING_BASE64, // Base64 encoding.
NULL, // Do not convert yet. Just calculate the length.
&cbLen, // Receives the length of the buffer that is required.
&dwSkip, // Receives the number of skipped characters.
&dwFlags // Receives flags.
);
// Allocate a new buffer.
BYTE *pbBuffer = new BYTE [cbLen];
::CryptStringToBinary(pszModulus, cchModulus, CRYPT_STRING_BASE64,
pbBuffer, &cbLen, &dwSkip, &dwFlags);
// (Repeat these steps for the exponent.)
The base64-encoded array is in big-endian order, whereas the CryptoAPI expects the number in little-endian order, so you need to swap the byte order of the array that is returned from CryptStringToBinary. The modulus is 256 bytes, but the decoded byte array might be less than 256 bytes. If so, you will need to allocate a new array that is 256 bytes, copy the data into the new array, and pad the front of the array with zeros. The exponent is a DWORD (4-byte) value.
After you have the modulus and exponent values, you can import the key into the default cryptographic service provider (CSP), as shown in the following code:
// Assume the following values exist:
BYTE *pModulus; // Byte array that contains the modulus.
DWORD cbModulus; // Size of the modulus in bytes.
DWORD dwExponent; // Exponent.
// Create a new key container to hold the key.
::CryptAcquireContext(
&hCSP, // Receives a handle to the CSP.
NULL, // Use the default key container.
NULL, // Use the default CSP.
PROV_RSA_AES, // Use the AES provider (public-key algorithm).
CRYPT_SILENT | CRYPT_NEWKEYSET
);
// Move the key into the key container.
// The data format is: PUBLICKEYSTRUC + RSAPUBKEY + key
DWORD cbKeyBlob = cbModulus + sizeof(PUBLICKEYSTRUC) + sizeof(RSAPUBKEY)
BYTE *pBlob = new BYTE[cbKeyBlob];
// Fill in the data.
PUBLICKEYSTRUC *pPublicKey = (PUBLICKEYSTRUC*)pBlob;
pPublicKey->bType = PUBLICKEYBLOB;
pPublicKey->bVersion = CUR_BLOB_VERSION; // Always use this value.
pPublicKey->reserved = 0; // Must be zero.
pPublicKey->aiKeyAlg = CALG_RSA_KEYX; // RSA public-key key exchange.
// The next block of data is the RSAPUBKEY structure.
RSAPUBKEY *pRsaPubKey = (RSAPUBKEY*)(pBlob + sizeof(PUBLICKEYSTRUC));
pRsaPubKey->magic = RSA1; // Public key.
pRsaPubKey->bitlen = cbModulus * 8; // Number of bits in the modulus.
pRsaPubKey->pubexp = dwExponent; // Exponent.
// Copy the modulus into the blob. Put the modulus directly after the
// RSAPUBKEY structure in the blob.
BYTE *pKey = (BYTE*)(pRsaPubkey + sizeof(RSAPUBKEY));
CopyMemory(pKey, pModulus, cbModulus);
// Now import the key.
HCRYPTKEY hRSAKey; // Receives a handle to the key.
CryptImportKey(hCSP, pBlob, cbKeyBlob, 0, 0, &hRSAKey)
Now you can use the CryptoAPI to encrypt commands and status requests with the driver's public key.
Related topics