如何验证我的组织是不是签署了受信任的 Windows 二进制文件?

Posted

技术标签:

【中文标题】如何验证我的组织是不是签署了受信任的 Windows 二进制文件?【英文标题】:How to verify that my orgainization signed a trusted windows binary?如何验证我的组织是否签署了受信任的 Windows 二进制文件? 【发布时间】:2011-01-01 18:50:55 【问题描述】:

这是question 1072540, 'WinVerifyTrust to check for a specific signature?' 的后续问题。

我想写一个 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

【讨论】:

谢谢拉斯穆斯,但我还是有点困惑。 CMSG_SIGNER_INFO 结构不包含 SubjectPublicKeyInfol 成员。 抱歉,我混淆了 CMSG_SIGNER_INFO 和 CERT_INFO 结构。现在答案应该是正确的。 感谢 Rasmus,我还发现带有 CERT_NAME_SIMPLE_DISPLAY_TYPE 标志的 CertGetNameString api 对这个项目也有帮助。

以上是关于如何验证我的组织是不是签署了受信任的 Windows 二进制文件?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Click-Once 签署应用程序来满足 AppLocker?

验证可执行文件是不是已签名(用于签署该 exe 的signtool)

如何签署我的司机?有必要吗?

CWE-501: Trust Boundary Violation违反信任边界的代码漏洞缺陷

如何建立对中介颁发的 X.509 证书的信任?

如何验证客户端应用程序以信任从它发送的消息