在编写安全工具的过程中,有时要验证程序的数字签名,微软提供了一套API可供调用wintrust.h标头
WinVerifyTrust
https://learn.microsoft.com/zh-cn/windows/win32/api/wintrust/nf-wintrust-winverifytrust
1 2 3 4 5
| LONG WinVerifyTrust( [in] HWND hwnd, [in] GUID *pgActionID, [in] LPVOID pWVTData );
|
hwnd
:调用方窗口的句柄,一般为NULL
pgActionID
:指向GUID结构的指针,该结构指定信任提供程序。验证程序数字签名则传入WINTRUST_ACTION_GENERIC_VERIFY_V2
pWVTData
:指向WINTRUST_DATA
结构的指针,结构包括处理所需的信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| typedef struct _WINTRUST_DATA { DWORD cbStruct; LPVOID pPolicyCallbackData; LPVOID pSIPClientData; DWORD dwUIChoice; DWORD fdwRevocationChecks; DWORD dwUnionChoice; union { #if ... WINTRUST_FILE_INFO_ *pFile; #else struct WINTRUST_FILE_INFO_ *pFile; #endif #if ... WINTRUST_CATALOG_INFO_ *pCatalog; #else struct WINTRUST_CATALOG_INFO_ *pCatalog; #endif #if ... WINTRUST_BLOB_INFO_ *pBlob; #else struct WINTRUST_BLOB_INFO_ *pBlob; #endif #if ... WINTRUST_SGNR_INFO_ *pSgnr; #else struct WINTRUST_SGNR_INFO_ *pSgnr; #endif #if ... WINTRUST_CERT_INFO_ *pCert; #else struct WINTRUST_CERT_INFO_ *pCert; #endif #if ... WINTRUST_DETACHED_SIG_INFO_ *pDetachedSig; #else struct WINTRUST_DETACHED_SIG_INFO_ *pDetachedSig; #endif }; DWORD dwStateAction; HANDLE hWVTStateData; WCHAR *pwszURLReference; DWORD dwProvFlags; DWORD dwUIContext; struct WINTRUST_SIGNATURE_SETTINGS_ *pSignatureSettings; } WINTRUST_DATA, *PWINTRUST_DATA;
|
cbStruct
:结构大小,即sizeof(WINTRUST_DATA)
dwUIChoice
:指定要使用的UI的类型,一般为WTD_UI_NONE
(不显示UI)
fdwRevocationChecks
:证书吊销检查选项,可以是以下两个值
WTD_REVOKE_NONE
:不检查证书是否被吊销,哪怕证书已经被吊销,也会将其视为有效,可以提高验证的速度(安全换效率)
WTD_REVOKE_WHOLECHAIN
:检查整个证书链中的所有证书是否被吊销,任何一个证书被吊销,则整个证书链失效(效率换安全)
dwUnionChoice
:指定要使用的联合成员。验证文件则设置为WTD_CHOICE_FILE
pFile
:指向WINTRUST_FILE_INFO
结构的指针,包含要验证的文件信息
dwStateAction
:指定要执行的操作。如果不需要hWVTStateData
成员则设置为WTD_STATEACTION_IGNORE
,否则设置为WTD_STATEACTION_VERIFY
(hWVTStateData
成员在下文用于获取签名者姓名,所以我这里设置为WTD_STATEACTION_VERIFY
)(如果设置为WTD_STATEACTION_VERIFY
,则需要使用WTD_STATEACTION_CLOSE
关闭句柄以免造成资源泄露)
1 2 3 4 5 6
| typedef struct WINTRUST_FILE_INFO_ { DWORD cbStruct; LPCWSTR pcwszFilePath; HANDLE hFile; GUID *pgKnownSubject; } WINTRUST_FILE_INFO, *PWINTRUST_FILE_INFO;
|
cbStruct
:结构大小,即sizeof(WINTRUST_FILE_INFO)
pcwszFilePath
:要验证文件的完整路径
返回值
以下给出几个常用返回值,完整返回值列表可从 https://learn.microsoft.com/zh-cn/windows/win32/seccrypto/certificate-and-trust-return-values 获取
TRUST_E_NOSIGNATURE
:程序无签名
ERROR_SUCCESS
:签名有效
TRUST_E_BAD_DIGEST
:签名无效
CERT_E_EXPIRED
:签名过期
CERT_E_REVOKED
:签名被吊销
TRUST_E_EXPLICIT_DISTRUST
:签名不被信任
CertGetNameStringW
https://learn.microsoft.com/zh-cn/windows/win32/api/wincrypt/nf-wincrypt-certgetnamestringw
1 2 3 4 5 6 7 8
| DWORD CertGetNameStringW( [in] PCCERT_CONTEXT pCertContext, [in] DWORD dwType, [in] DWORD dwFlags, [in] void *pvTypePara, [out] LPWSTR pszNameString, [in] DWORD cchNameString );
|
dwType
:这里只是获取程序的签名者姓名,传入CERT_NAME_SIMPLE_DISPLAY_TYPE
即可
pszNameString
:指针,指向用于接收返回值的缓冲区
cchNameString
:缓冲区大小,防止返回数据时缓冲区溢出。传入0
则获取预期大小
pCertContext
:指向CERT_CONTEXT
结构的指针,CERT_CONTEXT
结构从CRYPT_PROVIDER_SGNR
结构中获取
1 2 3
| CRYPT_PROVIDER_DATA * WTHelperProvDataFromStateData( [in] HANDLE hStateData );
|
hStateData
:就是上文的hWVTStateData
成员
1 2 3 4 5 6
| CRYPT_PROVIDER_SGNR * WTHelperGetProvSignerFromChain( [in] CRYPT_PROVIDER_DATA *pProvData, [in] DWORD idxSigner, [in] BOOL fCounterSigner, [in] DWORD idxCounterSigner );
|
pProvData
:WTHelperProvDataFromStateData
函数的返回值,其中包含签名者和反签名者信息
idxSigner
:签名者索引,从0
开始
fCounterSigner
:这里传入FALSE
,检索idxSigner
指定的签名者
idxCounterSigner
:反签名器的索引,从0
开始
1 2 3 4 5 6 7 8 9 10 11 12
| typedef struct _CRYPT_PROVIDER_SGNR { DWORD cbStruct; FILETIME sftVerifyAsOf; DWORD csCertChain; struct _CRYPT_PROVIDER_CERT *pasCertChain; DWORD dwSignerType; CMSG_SIGNER_INFO *psSigner; DWORD dwError; DWORD csCounterSigners; struct _CRYPT_PROVIDER_SGNR *pasCounterSigners; PCCERT_CHAIN_CONTEXT pChainContext; } CRYPT_PROVIDER_SGNR, *PCRYPT_PROVIDER_SGNR;
|
唯一需要注意的是pasCertChain
成员,可通过pasCertChain[0].pCert获取签名上下文(就是上文提到的CERT_CONTEXT
)
示例代码
以下为示例代码,获取签名者姓名的同时验证签名是否有效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| #include <windows.h> #include <wintrust.h> #include <softpub.h> #include <stdio.h> #pragma comment(lib, "wintrust") #pragma comment(lib, "crypt32.lib")
int main() { WINTRUST_FILE_INFO fileData = { 0 }; fileData.cbStruct = sizeof(WINTRUST_FILE_INFO); fileData.pcwszFilePath = L"C:\\Users\\ADMIN\\Desktop\\test.exe"; fileData.hFile = NULL; fileData.pgKnownSubject = NULL;
GUID guidAction = WINTRUST_ACTION_GENERIC_VERIFY_V2; WINTRUST_DATA wintrustData = { 0 }; wintrustData.cbStruct = sizeof(WINTRUST_DATA); wintrustData.dwUIChoice = WTD_UI_NONE; wintrustData.fdwRevocationChecks = WTD_REVOKE_WHOLECHAIN; wintrustData.dwUnionChoice = WTD_CHOICE_FILE; wintrustData.pFile = &fileData; wintrustData.dwStateAction = WTD_STATEACTION_VERIFY;
LONG result = WinVerifyTrust(NULL, &guidAction, &wintrustData);
CRYPT_PROVIDER_DATA* providerData = WTHelperProvDataFromStateData(wintrustData.hWVTStateData); if (providerData == NULL) { return 1; }
CRYPT_PROVIDER_SGNR* signer = WTHelperGetProvSignerFromChain(providerData, 0, FALSE, 0); if (signer == NULL) { return 1; }
PCCERT_CONTEXT certContext = signer->pasCertChain[0].pCert; DWORD nameSize = CertGetNameStringA(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0);
char* nameBuffer = new char[nameSize]; CertGetNameStringA(certContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nameBuffer, nameSize);
printf("签名者姓名:%s\n",nameBuffer);
wintrustData.dwStateAction = WTD_STATEACTION_CLOSE; WinVerifyTrust(nullptr, &guidAction, &wintrustData); delete[]nameBuffer;
switch (result) { case ERROR_SUCCESS: printf("签名有效"); break; case TRUST_E_NOSIGNATURE: printf("无签名"); break; case CERT_E_EXPIRED: printf("签名过期"); break; case CERT_E_REVOKED: printf("签名被吊销"); break; case TRUST_E_EXPLICIT_DISTRUST: printf("签名不被信任"); break; case TRUST_E_BAD_DIGEST: printf("签名无效"); break; default: printf("0x%x", result); break; } }
|