Windows 8/10/11平台下如何在程序启动时校验自身EXE代码签名?
Windows 8/10/11程序自签名证书验证实现(WinAPI示例)
以下是直接用WinAPI实现程序启动时自验证签名证书的代码示例,完全覆盖你要求的四个逻辑步骤:
#include <Windows.h> #include <Wintrust.h> #include <Cryptography.h> #include <iostream> #pragma comment(lib, "Crypt32.lib") #pragma comment(lib, "Wintrust.lib") // 验证当前程序签名证书的核心函数 BOOL VerifySelfCertificate() { WCHAR szExePath[MAX_PATH] = {0}; // 1. 获取当前运行EXE的完整路径 if (!GetModuleFileNameW(NULL, szExePath, MAX_PATH)) { std::wcerr << L"获取程序路径失败,错误码: " << GetLastError() << std::endl; return FALSE; } // 2. 验证签名有效性及证书存在性 WINTRUST_FILE_INFO fileInfo = {0}; fileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO); fileInfo.pcwszFilePath = szExePath; fileInfo.hFile = NULL; fileInfo.pgKnownSubject = NULL; WINTRUST_DATA trustData = {0}; trustData.cbStruct = sizeof(WINTRUST_DATA); trustData.dwUIChoice = WTD_UI_NONE; // 不显示UI,后台验证 trustData.fdwRevocationChecks = WTD_REVOKE_NONE; // 根据需求选择是否检查吊销 trustData.dwUnionChoice = WTD_CHOICE_FILE; trustData.pFile = &fileInfo; trustData.dwStateAction = WTD_STATEACTION_VERIFY; trustData.hWVTStateData = NULL; trustData.pwszURLReference = NULL; trustData.dwProvFlags = WTD_REVOCATION_CHECK_NONE | WTD_CACHE_ONLY_URL_RETRIEVAL; GUID guidAction = WINTRUST_ACTION_GENERIC_VERIFY_V2; LONG lStatus = WinVerifyTrust(NULL, &guidAction, &trustData); if (lStatus != ERROR_SUCCESS) { std::wcerr << L"证书无效或未签名,错误码: " << lStatus << std::endl; return FALSE; } // 3. 从EXE中提取证书上下文 HCRYPTPROV hProv = NULL; HCRYPTMSG hMsg = NULL; PCCERT_CONTEXT pCertContext = NULL; DWORD dwEncoding = 0; DWORD dwContentType = 0; DWORD dwFormatType = 0; if (!CryptQueryObject(CERT_QUERY_OBJECT_FILE, szExePath, CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED, CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding, &dwContentType, &dwFormatType, &hProv, &hMsg, (const void**)&pCertContext)) { std::wcerr << L"提取证书失败,错误码: " << GetLastError() << std::endl; WinVerifyTrust(NULL, &guidAction, &trustData); // 清理验证状态 return FALSE; } // 4. 获取证书所有者名称并验证是否为"John Doe" WCHAR szSubjectName[256] = {0}; if (!CertGetNameStringW(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, szSubjectName, ARRAYSIZE(szSubjectName))) { std::wcerr << L"获取证书所有者名称失败,错误码: " << GetLastError() << std::endl; CertFreeCertificateContext(pCertContext); CryptMsgClose(hMsg); CryptReleaseContext(hProv, 0); WinVerifyTrust(NULL, &guidAction, &trustData); return FALSE; } if (_wcsicmp(szSubjectName, L"John Doe") != 0) { std::wcerr << L"证书所有者不符,当前所有者: " << szSubjectName << std::endl; CertFreeCertificateContext(pCertContext); CryptMsgClose(hMsg); CryptReleaseContext(hProv, 0); WinVerifyTrust(NULL, &guidAction, &trustData); return FALSE; } // 资源清理 CertFreeCertificateContext(pCertContext); CryptMsgClose(hMsg); CryptReleaseContext(hProv, 0); WinVerifyTrust(NULL, &guidAction, &trustData); // 结束验证状态 return TRUE; } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { if (!VerifySelfCertificate()) { // 验证失败,退出程序 return 1; } // 验证通过,执行程序主逻辑 MessageBoxW(NULL, L"证书验证通过,程序启动成功!", L"提示", MB_OK); return 0; }
关键说明:
- 证书存在性与有效性验证:通过
WinVerifyTrust函数完成,不仅检查是否存在签名,还会验证证书链的有效性、有效期等(可通过dwProvFlags调整验证策略)。 - 证书提取:使用
CryptQueryObject从EXE文件中直接提取嵌入的证书上下文。 - 所有者名称匹配:通过
CertGetNameString获取证书的简易显示名称,使用_wcsicmp进行不区分大小写的对比。 - 资源清理:所有WinAPI获取的句柄和上下文都需手动释放,避免内存泄漏。
- 编译依赖:需链接
Crypt32.lib和Wintrust.lib,代码中已通过#pragma comment声明。
注意事项:
- 若需要严格检查证书吊销状态,可将
fdwRevocationChecks设为WTD_REVOKE_WHOLECHAIN,但需确保程序能访问网络。 - 此代码仅适用于Windows 8及以上系统,API兼容性满足需求。
内容的提问来源于stack exchange,提问作者Peter Furz
相关产品推荐
相关产品推荐

