Как проверить, что моя организация подписала доверенный двоичный файл Windows?
Это дополнительный вопрос к вопросу 1072540: "WinVerifyTrust для проверки конкретной подписи?",
Я хочу написать функцию C++ Позволяет вызвать ее TrustedByUs
формы:
bool TrustedByUs(std::string pathToBinary, std::string pathToPublicKey)
Идея состоит в том, что мы даем этой функции путь к двоичному файлу.dll или.exe, который был подписан цифровой подписью. pathToPublicKey
Строка - это путь к открытому ключу нашего конкретного сертификата подписи.
Используя код в http://support.microsoft.com/kb/323809, довольно просто проверить, что pathToBinary
файл на самом деле доверяет операционной системе.
Сейчас я нахожусь в том же месте, что и автор вопроса 1072540, я знаю, что ОС доверяет подписывающему лицу этого двоичного файла, но я хочу знать, является ли ключ RSA моей организации тем, который подписал двоичный файл.
KB323809 показывает, как извлечь строки из сертификата, встроенного в наш двоичный файл. В этом примере показано, как извлечь строки из сертификата подписи в его GetProgAndPublisherInfo
функции, но мне неудобно использовать совпадение строк для проверки сертификата.
То, что я хотел бы сделать, это извлечь открытый ключ из встроенной подписи и сравнить его с открытым ключом, который в первую очередь соответствует закрытому ключу, подписавшему мой двоичный файл.
Документация для CryptMsgGetParam
говорит, что CMSG_SIGNER_CERT_ID_PARAM
параметр "Возвращает информацию о подписавшем сообщения, необходимую для идентификации открытого ключа подписавшего". Мне удалось получить серийный номер сертификата с этим ключом. Мой код выглядит так:
// Get message handle and store handle from the signed file.
fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
L"C:\\Program Files\\MySignedProgram.exe",
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0, &dwEncoding, &dwContentType, &dwFormatType, &hStore, &hMsg, NULL);
// Get the public key information about the signer
// First get the size
DWORD dwCertIdSize(0);
fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_ID_PARAM,
0, NULL, &dwCertIdSize);
BYTE* pCertId = new BYTE(dwCertIdSize);
::ZeroMemory(pCertId,dwCertIdSize);
// Now get the cert info
fResult = CryptMsgGetParam(hMsg, CMSG_SIGNER_CERT_ID_PARAM,
0, (PVOID)pCertId, &dwCertIdSize);
if(fResult)
{
CERT_ID* pId = (CERT_ID*)pCertId;
pId->HashId;
pId->dwIdChoice;
pId->IssuerSerialNumber; // Valid serial number (reversed)
pId->KeyId;
_tprintf("pid\n");
}
Это близко к тому, что я хочу, но на самом деле я хотел бы использовать открытый ключ сертификата подписи, чтобы убедиться, что целевой двоичный файл с подписью на самом деле был создан с моей конкретной парой открытого / закрытого ключа.
С использованием CMSG_ENCRYPTED_DIGEST
пометить этот код успешно:
// Get digest which was encrypted with the private key
DWORD digestSize(0);
fResult = CryptMsgGetParam(hMsg, CMSG_ENCRYPTED_DIGEST, 0, NULL, &digestSize);
BYTE* pDigest = new BYTE[digestSize];
// Next CryptMsgGetParam call succeds,
// pDigest looks valid, can I use this to confirm my public key
// was used to sign MySignedProgram.exe ?
fResult = CryptMsgGetParam(hMsg, CMSG_ENCRYPTED_DIGEST, 0, pDigest, &digestSize);
Суть вопроса: учитывая информацию о сертификате, обнаруженную CryptQueryObject
Какой метод я должен использовать, чтобы убедиться, что целевой файл был действительно подписан с использованием закрытого ключа, который соответствует общедоступному ключу, доступному мне при выполнении вышеуказанного кода?
1 ответ
Вы хотите CMSG_SIGNER_INFO_PARAM
вместо.
Это можно использовать для получения всего сертификата, просмотрев сертификат в хранилище сертификатов, возвращенном CryptQueryObject
:
CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
PCMSG_SIGNER_INFO pSignerInfo = (PCMSG_SIGNER_INFO) malloc(dwSignerInfo);
CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
pSignerInfo,
&dwSignerInfo);
PCCERT_CONTEXT pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)pSignerInfo,
NULL);
// Compare with your certificate:
// - check pCertContext->pbCertEncoded (length is pCertContext->cbCertEncoded)
// *OR*
// Compare with your public-key:
// - check pCertContext->pCertInfo->SubjectPublicKeyInfo.Algorithm and
// pCertContext->pCertInfo->SubjectPublicKeyInfo.PublicKey