如何通过API用PFX证书签名EXE/DLL?加载证书遇0x80093102错误
问题分析与解决方案
错误根源
出现0x80093102(意外数据结束)错误的核心原因是API使用错误:CertCreateCertificateContext仅支持加载单个X.509证书文件(如.cer/.crt格式),而PFX是PKCS#12格式的容器,包含证书、私钥及可能的证书链,无法被该函数直接解析。
正确的PFX加载与签名流程
要实现PFX证书加载并完成文件签名,需遵循以下步骤:
- 读取PFX文件的二进制内容到内存
- 通过
PFXImportCertStore导入PFX文件,传入密码获取证书存储句柄 - 从证书存储中枚举并提取带私钥的目标证书上下文
- 使用
SignerSignEx2等API完成EXE/DLL文件签名
修正后的代码实现
1. 从PFX加载带私钥的证书上下文
#include <windows.h> #include <wincrypt.h> #include <stdio.h> #pragma comment(lib, "crypt32.lib") #define PFX_PATH L"你的PFX文件路径" #define PFX_PASSWORD L"PFX文件密码" HRESULT LoadCertificateFromPFX(PCCERT_CONTEXT* ppCertContext) { HRESULT hr = S_OK; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD dwFileSize = 0; BYTE* pPFXData = NULL; HCERTSTORE hCertStore = NULL; // 打开PFX文件 hFile = CreateFileW(PFX_PATH, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (hFile == INVALID_HANDLE_VALUE) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } // 获取文件大小 dwFileSize = GetFileSize(hFile, NULL); if (dwFileSize == INVALID_FILE_SIZE) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } // 分配内存读取PFX内容 pPFXData = (BYTE*)LocalAlloc(LPTR, dwFileSize); if (!pPFXData) { hr = E_OUTOFMEMORY; goto Cleanup; } DWORD dwBytesRead = 0; if (!ReadFile(hFile, pPFXData, dwFileSize, &dwBytesRead, NULL) || dwBytesRead != dwFileSize) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } // 导入PFX到证书存储 hCertStore = PFXImportCertStore( &CRYPT_DATA_BLOB{ dwFileSize, pPFXData }, PFX_PASSWORD, CRYPT_EXPORTABLE | CRYPT_USER_KEYSET ); if (!hCertStore) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } // 枚举存储中的证书,筛选带私钥的证书 *ppCertContext = CertFindCertificateInStore( hCertStore, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_PROPERTY, (LPVOID)CERT_KEY_PROV_INFO_PROP_ID, NULL ); if (!*ppCertContext) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } Cleanup: if (hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); if (pPFXData) LocalFree(pPFXData); // 证书存储句柄需保留,直到证书上下文被释放 return hr; }
2. 用证书签名目标文件
#pragma comment(lib, "mssign32.lib") HRESULT SignFile(LPCWSTR pwszFilePath, PCCERT_CONTEXT pCertContext) { HRESULT hr = S_OK; SIGNER_SUBJECT_INFO subjectInfo = { 0 }; SIGNER_CERT certInfo = { 0 }; SIGNER_SIGNATURE_INFO signatureInfo = { 0 }; // 初始化签名目标文件信息 subjectInfo.cbSize = sizeof(SIGNER_SUBJECT_INFO); subjectInfo.pwszFileName = pwszFilePath; // 初始化证书信息 certInfo.cbSize = sizeof(SIGNER_CERT); certInfo.dwCertPolicy = SIGNER_CERT_POLICY_CHAIN; certInfo.pCertContext = pCertContext; // 初始化签名算法(使用SHA256) signatureInfo.cbSize = sizeof(SIGNER_SIGNATURE_INFO); signatureInfo.algidHash = CALG_SHA256; // 执行签名 hr = SignerSignEx2( 0, &subjectInfo, &certInfo, &signatureInfo, NULL, NULL, NULL, NULL, NULL, NULL ); return hr; }
3. 主函数调用示例
int main() { PCCERT_CONTEXT pCertContext = NULL; HCERTSTORE hCertStore = NULL; // 加载PFX证书 HRESULT hr = LoadCertificateFromPFX(&pCertContext); if (FAILED(hr)) { wprintf(L"加载PFX失败: 0x%08X\n", hr); return 1; } hCertStore = pCertContext->hCertStore; // 签名目标文件 hr = SignFile(L"待签名的EXE/DLL路径", pCertContext); if (FAILED(hr)) { wprintf(L"签名失败: 0x%08X\n", hr); } else { wprintf(L"签名成功!\n"); } // 清理资源 if (pCertContext) CertFreeCertificateContext(pCertContext); if (hCertStore) CertCloseStore(hCertStore, 0); return 0; }
关键注意事项
- 确保PFX路径、密码及目标文件路径使用宽字符串,若为多字节编码需提前转换
- 编译时必须链接
crypt32.lib和mssign32.lib - 程序需以管理员权限运行,否则可能因权限不足导致签名失败
- 推荐使用SHA256及以上哈希算法,SHA1已被逐步淘汰
内容的提问来源于stack exchange,提问作者h.s.
相关产品推荐
相关产品推荐

