You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过API用PFX证书签名EXE/DLL?加载证书遇0x80093102错误

问题分析与解决方案

错误根源

出现0x80093102(意外数据结束)错误的核心原因是API使用错误:CertCreateCertificateContext仅支持加载单个X.509证书文件(如.cer/.crt格式),而PFX是PKCS#12格式的容器,包含证书、私钥及可能的证书链,无法被该函数直接解析。

正确的PFX加载与签名流程

要实现PFX证书加载并完成文件签名,需遵循以下步骤:

  1. 读取PFX文件的二进制内容到内存
  2. 通过PFXImportCertStore导入PFX文件,传入密码获取证书存储句柄
  3. 从证书存储中枚举并提取带私钥的目标证书上下文
  4. 使用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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.04 15:25:36