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

如何在PowerShell中用非托管函数获取签名文件的完整证书链?

获取文件的完整证书链(使用CertGetCertificateChain非托管函数)

问题背景

我有一个文件,其证书路径包含4个证书,但用常规PowerShell代码只能得到3个证书的最短链,文件资源管理器里能看到完整的4个证书链;还有其他文件有5个证书,但PowerShell里只能看到3个,因为返回的是最短链。常规代码如下:

$FilePath = '.\NordPassSetup_x86.exe'

# 从文件路径获取证书
$Cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $FilePath

# 构建证书链
$Chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
[void]$Chain.Build($Cert)

$Chain.ChainElements.count

foreach ($Element in $Chain.ChainElements) {
    $Element.Certificate | ft -AutoSize
}

解决方案:调用CertGetCertificateChain非托管函数

要获取完整的证书链,需要直接调用Windows的CertGetCertificateChain非托管API,以下是PowerShell实现步骤:

1. 定义API结构体和函数

通过Add-Type导入非托管代码,定义API所需的结构体和函数签名:

Add-Type @"
using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;

public class CertChainHelper {
    [StructLayout(LayoutKind.Sequential)]
    public struct CERT_CHAIN_PARA {
        public uint cbSize;
        public IntPtr RequestedUsage;
    }

    [DllImport("crypt32.dll", SetLastError = true)]
    public static extern bool CertGetCertificateChain(
        IntPtr hChainEngine,
        IntPtr pCertContext,
        IntPtr pTime,
        IntPtr hAdditionalStore,
        ref CERT_CHAIN_PARA pChainPara,
        uint dwFlags,
        IntPtr pvReserved,
        out IntPtr ppChainContext
    );

    [DllImport("crypt32.dll", SetLastError = true)]
    public static extern void CertFreeCertificateChain(IntPtr pChainContext);

    [StructLayout(LayoutKind.Sequential)]
    public struct CERT_CHAIN_CONTEXT {
        public uint cbSize;
        public IntPtr pCertChain;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CERT_SIMPLE_CHAIN {
        public uint cbSize;
        public IntPtr pCertContext;
        public uint TrustStatus;
        public IntPtr pTrustListInfo;
        public IntPtr pRevocationInfo;
        public IntPtr pIssuanceUsage;
        public IntPtr pApplicationUsage;
        public uint cElement;
        public IntPtr rgpElement;
    }

    [StructLayout(LayoutKind.Sequential)]
    public struct CERT_CHAIN_ELEMENT {
        public uint cbSize;
        public IntPtr pCertContext;
    }
}
"@

2. 编写完整链获取函数

实现PowerShell函数,加载文件证书并调用API解析完整链:

function Get-FullCertificateChain {
    param(
        [Parameter(Mandatory=$true)]
        [string]$FilePath
    )

    # 加载文件证书
    $cert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2 $FilePath
    $certContext = $cert.Handle

    # 初始化链参数
    $chainPara = New-Object CertChainHelper+CERT_CHAIN_PARA
    $chainPara.cbSize = [System.Runtime.InteropServices.Marshal]::SizeOf($chainPara)

    # 调用CertGetCertificateChain
    $chainContextPtr = [IntPtr]::Zero
    $success = [CertChainHelper]::CertGetCertificateChain(
        [IntPtr]::Zero,  # 使用默认链引擎
        $certContext,
        [IntPtr]::Zero,  # 使用当前时间
        [IntPtr]::Zero,  # 无额外存储
        [ref]$chainPara,
        0x00000001,  # 排除根证书的吊销检查,可按需调整
        [IntPtr]::Zero,
        [ref]$chainContextPtr
    )

    if (-not $success) {
        throw "调用CertGetCertificateChain失败,错误码: $([System.Runtime.InteropServices.Marshal]::GetLastWin32Error())"
    }

    try {
        # 解析链上下文
        $chainContext = [System.Runtime.InteropServices.Marshal]::PtrToStructure(
            $chainContextPtr,
            [type][CertChainHelper+CERT_CHAIN_CONTEXT]
        )

        # 获取第一个简单链(通常仅存在一个)
        $simpleChainPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($chainContext.pCertChain)
        $simpleChain = [System.Runtime.InteropServices.Marshal]::PtrToStructure(
            $simpleChainPtr,
            [type][CertChainHelper+CERT_SIMPLE_CHAIN]
        )

        # 遍历所有链元素
        $chainElements = @()
        for ($i=0; $i -lt $simpleChain.cElement; $i++) {
            $elementPtr = [System.Runtime.InteropServices.Marshal]::ReadIntPtr($simpleChain.rgpElement, $i * [IntPtr]::Size)
            $chainElement = [System.Runtime.InteropServices.Marshal]::PtrToStructure(
                $elementPtr,
                [type][CertChainHelper+CERT_CHAIN_ELEMENT]
            )

            # 将非托管证书上下文转为X509Certificate2对象
            $elementCert = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($chainElement.pCertContext)
            $chainElements += $elementCert
        }

        return $chainElements
    }
    finally {
        # 释放资源
        if ($chainContextPtr -ne [IntPtr]::Zero) {
            [CertChainHelper]::CertFreeCertificateChain($chainContextPtr)
        }
    }
}

3. 使用函数获取完整链

调用上述函数即可获取文件的完整证书链:

$fullChain = Get-FullCertificateChain -FilePath '.\NordPassSetup_x86.exe'
Write-Host "完整证书链长度: $($fullChain.Count)"
$fullChain | Format-Table -AutoSize Subject, Issuer, Thumbprint

说明

  • CertGetCertificateChain会构建包含所有中间证书的完整链,不会像X509Chain.Build()那样返回最短信任链(可能跳过系统信任存储中已存在的根/中间证书)。
  • 代码中的dwFlags参数可根据需求调整,例如禁用吊销检查可使用0x00000010(CERT_CHAIN_REVOCATION_CHECK_NONE)。

内容的提问来源于stack exchange,提问作者SpyNet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.11 12:52:51