PowerShell脚本数字签名实战:自签名证书、代码签名与信任链验证

本文详解PowerShell脚本数字签名机制,涵盖自签名证书创建、脚本签名与验证、证书存储区与信任链关系、签名完整性保护原理、时间戳作用及私钥安全管理。重点说明签名≠执行许可,需结合执行策略、证书信任链和系统安全控制综合排查NotTrusted、HashMismatch等常见错误。

PowerShell脚本数字签名实战:自签名证书、代码签名与信任链验证

在 Windows 上编写 PowerShell 自动化脚本时,我们经常会遇到一个问题:

脚本明明是自己编写的,但在另一台电脑上运行时,却出现了 PSSecurityException。

例如:

无法加载文件 xxx.ps1,因为此文件没有数字签名。

PSSecurityException
UnauthorizedAccess

如果将脚本放在 NAS、Windows 网络共享目录,或者通过 Git、压缩包等方式分发给其他电脑,还可能遇到:

  • 脚本已经签名,但 PowerShell 仍然拒绝执行。
  • Get-AuthenticodeSignature 显示 NotTrusted。
  • 脚本修改后,原来的签名失效。
  • 同一个脚本在电脑 A 可以运行,在电脑 B 却无法运行。
  • 签名证书过期后,历史脚本出现验证问题。
  • 已经设置 RemoteSigned,但依然提示 PSSecurityException。

这些问题涉及 PowerShell 的数字签名、证书信任链和执行策略。

本文通过一个完整的本地实验,介绍如何创建代码签名证书、为 PowerShell 脚本签名、验证签名完整性,以及理解证书信任和脚本执行权限之间的关系。

本章的最终目标不是简单地让某个脚本运行起来,而是理解:

如何确认脚本是谁签署的、签署后是否被修改,以及为什么某些电脑不信任这个签名。

适用环境:Windows 10/11、Windows PowerShell 5.1 和 Windows 上的 PowerShell 7.x。

本文使用自签名证书完成隔离实验。自签名证书主要适用于学习和测试,不建议直接用于正式的软件分发。涉及受信任根证书存储区的操作,仅应在经过授权的独立测试环境中完成。


一、为什么PowerShell脚本需要数字签名?

假设编写了一个 PowerShell 脚本:

Set-PrivateFolder.ps1

它负责设置 Windows 文件夹访问权限。

开发者完成测试后,将脚本保存到文件服务器,供其他计算机使用。

这时会出现三个问题:

第一,如何证明脚本确实来自预期的发布者?

第二,如何判断脚本在分发过程中有没有被修改?

第三,接收脚本的计算机是否信任发布者?

数字签名主要用于解决前两个问题,并为第三个问题提供可验证的身份依据。

1.1 数字签名的工作原理

PowerShell 使用 Windows Authenticode 机制对脚本进行签名。

简化过程如下:

通过

失败

PowerShell 脚本

计算脚本摘要

使用签名私钥生成签名

带有 Authenticode 签名的脚本

签名证书中的公钥

验证签名与脚本完整性

验证结果

签名完整性验证通过

签名无效或内容不匹配

图1:PowerShell Authenticode 数字签名的简化工作原理。

数字签名使用非对称密码技术:

组成部分主要用途
私钥(Private Key)生成数字签名,必须严格保护
公钥(Public Key)验证对应的数字签名
数字证书将公钥与证书主体信息关联
文件摘要用于验证文件内容完整性
证书信任链判断证书能否关联到受信任的信任锚
时间戳证明签名发生在特定时间

签名之后,PowerShell 脚本末尾通常会增加一个签名注释块。

例如:

# SIG # Begin signature block
#
# ...签名数据...
#
# SIG # End signature block

PowerShell 不会将这些注释作为普通脚本指令执行。

1.2 数字签名不等于脚本安全

需要明确:

签名只能提供身份与完整性验证能力,不能证明脚本没有漏洞或恶意行为。

例如,一个脚本即使由可信发布者签名,也可能存在危险操作。

因此,执行签名脚本之前,仍然需要进行源码审查、权限检查和必要的测试。


二、先区分四种不同的验证结果

在实际排查中,经常有人把“文件有签名”和“文件可以运行”理解成同一回事。

实际上,至少需要区分四个层面。

验证层面主要问题
签名存在文件是否包含 Authenticode 签名?
签名完整性文件是否在签名后被修改?
证书信任证书链和发布者是否满足信任要求?
执行授权PowerShell 执行策略和系统安全策略是否允许执行?

这几个判断不能互相替代。

例如:

某个脚本的签名完整性没有问题,但证书来自不受信任的颁发机构,仍可能无法在要求可信发布者签名的环境中运行。

反过来,即使某个签名证书受到信任,如果文件内容已经被修改,签名完整性验证也可能失败。

2.1 RemoteSigned与AllSigned的区别

先查看当前 PowerShell 的执行策略:

Get-ExecutionPolicy

再查看不同作用域:

Get-ExecutionPolicy -List

两种常见策略的区别如下:

策略基本要求
RemoteSigned对被识别为来自 Internet 的脚本通常要求可信的有效签名;本地创建的脚本一般可以不签名
AllSigned要求脚本及相关配置文件由可信发布者签名,包括本地创建的脚本

需要特别说明:

PowerShell 执行策略是一种纵深防御机制,不是完整安全边界。组织还可能通过 AppLocker、App Control for Business(WDAC)等其他安全机制控制脚本执行。

因此,本文实验不会擅自修改系统执行策略。


三、实验准备:检查系统和证书工具

本实验采用 Windows PowerShell 5.1 或 Windows 上的 PowerShell 7。

首先执行:

$PSVersionTable.PSVersion

Get-ExecutionPolicy -List

检查相关命令是否可用:

Get-Command `
    New-SelfSignedCertificate, `
    Set-AuthenticodeSignature, `
    Get-AuthenticodeSignature, `
    Export-Certificate `
    -ErrorAction SilentlyContinue |
    Select-Object Name, Source

这里使用的主要工具如下:

命令用途
New-SelfSignedCertificate创建测试证书
Get-ChildItem Cert:查看 Windows 证书存储区
Set-AuthenticodeSignature为脚本添加数字签名
Get-AuthenticodeSignature检查签名状态
Export-Certificate导出不含私钥的公钥证书
Import-Certificate在授权环境中导入证书

其中,创建自签名证书和导入证书主要依赖 Windows PKI 模块。

如果某个命令不存在,应先检查 Windows 版本、模块可用性和当前运行环境,不要立即修改执行策略。

3.1 创建独立实验目录

为了避免影响已有项目,先创建一个临时实验目录:

$labDir = Join-Path $env:TEMP (
    "PS-Signing-Lab-" + (Get-Date -Format "yyyyMMdd-HHmmss")
)

New-Item -ItemType Directory `
    -Path $labDir `
    -Force | Out-Null

$labDir

后续实验均在该目录中进行。

建议在同一个 PowerShell 窗口按顺序执行本文命令,以便复用 $labDir、$cert 等变量。


四、创建一个PowerShell代码签名证书

4.1 什么是代码签名证书?

数字证书可以具有不同用途。

例如:

  • HTTPS 服务器身份认证;
  • 客户端身份认证;
  • 邮件保护;
  • 代码签名。

用于 PowerShell Authenticode 签名的证书,需要具有相应的代码签名用途。

代码签名对应的增强型密钥用法(EKU)OID 是:

1.3.6.1.5.5.7.3.3

普通 HTTPS 服务器证书不应该被直接视为可用于代码签名的证书。

4.2 创建自签名测试证书

在 Windows PowerShell 中执行:

$cert = New-SelfSignedCertificate `
    -Type CodeSigning `
    -Subject "CN=PowerShell Signing Lab" `
    -CertStoreLocation "Cert:\CurrentUser\My" `
    -KeyAlgorithm RSA `
    -KeyLength 2048 `
    -KeyExportPolicy NonExportable `
    -HashAlgorithm SHA256 `
    -NotAfter (Get-Date).AddMonths(3)

这里采用以下配置:

  • CodeSigning:指定代码签名类型;
  • CurrentUser\My:保存到当前用户的个人证书存储区;
  • RSA 2048:用于本地兼容性实验;
  • SHA256:使用 SHA-256;
  • NonExportable:限制通过常规方式导出私钥;
  • NotAfter:将测试证书有效期设置为三个月。

注意,私钥不可导出并不意味着绝对无法被恶意程序滥用。生产环境仍需要更严格的私钥访问控制。

4.3 查看创建结果

$cert |
    Select-Object `
        Subject,
        Issuer,
        Thumbprint,
        NotBefore,
        NotAfter,
        HasPrivateKey

重点关注:

Subject:证书主体。

Issuer:证书颁发者。

Thumbprint:证书指纹,用于精确识别证书。

NotAfter:证书到期时间。

HasPrivateKey:当前证书对象是否关联私钥。

由于使用了自签名证书,Subject 和 Issuer 通常相同。

可以进一步检查代码签名证书:

Get-ChildItem Cert:\CurrentUser\My -CodeSigningCert |
    Where-Object {
        $_.Thumbprint -eq $cert.Thumbprint
    } |
    Select-Object Subject, Thumbprint, HasPrivateKey

这里通过指纹精确选择本次实验创建的证书。

不要在拥有多个代码签名证书的环境中,直接选取列表中的第一张证书进行签名。


五、创建测试脚本并完成第一次签名

5.1 创建无副作用的测试脚本

本次实验的脚本只输出一行文字。

$scriptPath = Join-Path $labDir "HelloSigned.ps1"

Set-Content `
    -LiteralPath $scriptPath `
    -Encoding ASCII `
    -Value "Write-Output 'SIGN_LAB_OK'"

这里故意采用纯 ASCII 内容。

原因是 Windows PowerShell 5.1 和 PowerShell 7 对文本编码的处理存在差异。使用简单 ASCII 测试脚本,可以先排除中文源文件编码对签名实验的干扰。

正式脚本如果包含中文字符,还需要统一文件编码并验证目标 PowerShell 版本。

5.2 签名前检查状态

Get-AuthenticodeSignature `
    -LiteralPath $scriptPath |
    Select-Object Status, StatusMessage

由于刚创建的文件尚未签名,预期状态是:

NotSigned

这是预期结果,实际以命令输出为准。

5.3 为脚本添加数字签名

执行:

$signResult = Set-AuthenticodeSignature `
    -LiteralPath $scriptPath `
    -Certificate $cert `
    -HashAlgorithm SHA256

$signResult |
    Select-Object Status, StatusMessage

如果签名操作成功,PowerShell 会将 Authenticode 签名块附加到脚本中。

可以查看脚本末尾:

Get-Content -LiteralPath $scriptPath -Tail 8

注意:

签名操作返回的状态不仅受到脚本内容影响,还可能受到证书链和本机信任配置影响。

因此,在尚未建立对自签名证书的信任时,不应直接假定状态一定是 Valid。

如果返回 NotTrusted,需要区分签名是否已经写入文件,以及证书是否未被系统信任。

5.4 重新验证签名

Get-AuthenticodeSignature `
    -LiteralPath $scriptPath |
    Format-List `
        Status,
        StatusMessage,
        SignerCertificate,
        TimeStamperCertificate

这里能够帮助我们回答:

  1. 当前文件是否存在签名?
  2. 使用的是哪张签名证书?
  3. 系统报告的验证状态是什么?
  4. 是否存在时间戳证书信息?

需要特别注意,Status = Valid 也不代表脚本内容经过安全审查,更不应替代对发布者身份和证书信任政策的检查。


六、理解证书存储区与信任链

这部分是整个实验中最容易产生误解的地方。

6.1 Windows证书存储区有哪些?

可以通过:

Get-ChildItem Cert:\CurrentUser

查看当前用户的证书存储区。

本文重点涉及三个位置:

证书存储区主要用途
CurrentUser\My保存当前用户的个人证书及关联私钥
CurrentUser\Root保存当前用户信任的根证书
CurrentUser\TrustedPublisher保存受信任的软件发布者证书

还存在对应的 LocalMachine 计算机级证书存储区。

两者作用范围不同,不应随意混用。

6.2 什么是证书信任链?

正式的代码签名证书通常由 CA 签发。

一个简化的证书链如下:

是

否

代码签名证书
签署 PowerShell 脚本

中间证书颁发机构
Intermediate CA

根证书颁发机构
Root CA

系统信任存储区

验证证书链

是否满足信任要求?

进一步检查发布者与执行策略

证书链验证失败

图2:代码签名证书信任链的简化验证流程。

在实际系统中,证书链验证还可能涉及:

  • 证书有效期;
  • 证书用途;
  • 信任锚;
  • 证书撤销状态;
  • 证书链构建规则;
  • 系统和组织安全策略。

Trusted Root 和 TrustedPublisher 不是同一个概念。

证书链受到信任,不等于系统已经自动将某个软件发布者批准为可信发布者。

6.3 查看实验自签名证书的信任状态

可以使用 Windows 的 X.509 证书链验证类:

$chain = New-Object `
    System.Security.Cryptography.X509Certificates.X509Chain

$chainOk = $chain.Build($cert)

Write-Host "Chain build result: $chainOk"

$chain.ChainStatus |
    Select-Object Status, StatusInformation

在尚未信任该自签名证书的环境中,通常可能看到类似:

UntrustedRoot

但实际结果取决于系统已经存在的证书、信任配置和验证环境。

这里也不应为了让结果通过而关闭证书撤销检查。


七、导出公钥证书,并理解如何建立信任

7.1 导出公钥证书

执行:

$cerPath = Join-Path $labDir "PS-Signing-Lab.cer"

Export-Certificate `
    -Cert $cert `
    -FilePath $cerPath |
    Out-Null

该命令导出的是不包含私钥的证书。

可以查看文件:

Get-Item -LiteralPath $cerPath |
    Select-Object FullName, Length

再次强调:

  • .cer 通常用于分发公钥证书;
  • .pfx 可能包含证书及私钥;
  • 私钥不应该随普通 PowerShell 脚本一起公开分发。

本文实验使用不可导出的测试私钥,因此不需要创建 PFX 文件。

7.2 为什么签名证书不一定被信任?

创建自签名证书时,它通常保存在:

Cert:\CurrentUser\My

但这并不意味着它已经成为受信任的根证书,也不意味着它已经成为受信任的软件发布者。

这就是“自己能签名,但另一台电脑不信任签名”的主要原因之一。

7.3 在隔离测试环境中验证信任变化

这一节会修改当前用户的证书信任存储区,仅适用于你自己管理且明确授权的测试虚拟机或可恢复的实验环境。

不要在日常办公电脑或生产服务器上直接照搬,更不要将来源不明的证书导入受信任根证书存储区。

首先确认准备导入的文件确实是刚才创建的证书,记录其指纹:

$cert.Thumbprint

Get-FileHash `
    -LiteralPath $cerPath `
    -Algorithm SHA256

在独立测试环境中,如果已经确认信任范围和风险,可以分别验证以下两个存储区的作用。

将本次自签名测试证书作为当前用户信任锚:

Import-Certificate `
    -FilePath $cerPath `
    -CertStoreLocation "Cert:\CurrentUser\Root"

将本次证书登记为当前用户受信任发布者:

Import-Certificate `
    -FilePath $cerPath `
    -CertStoreLocation "Cert:\CurrentUser\TrustedPublisher"

这两步不是正式环境的推荐配置方案。

将自签名证书加入 Root 会扩大该用户的证书信任范围,应严格限制在隔离实验中,并在实验结束后撤销。

完成后重新运行:

Get-AuthenticodeSignature `
    -LiteralPath $scriptPath |
    Format-List Status, StatusMessage

然后重新检查证书链:

$chain = New-Object `
    System.Security.Cryptography.X509Certificates.X509Chain

$chainOk = $chain.Build($cert)

Write-Host "Chain build result: $chainOk"

$chain.ChainStatus |
    Select-Object Status, StatusInformation

通过对比导入信任证书之前和之后的结果,就可以理解:

脚本签名本身并没有改变,改变的是验证计算机对证书及发布者的信任状态。

如果仍然不能执行脚本,应继续检查实际执行策略、证书用途以及其他系统安全控制,而不是反复导入证书。


八、实验:修改已签名脚本,签名会发生什么变化?

这是最能体现数字签名完整性保护的实验。

8.1 复制已签名脚本

先复制一份,保留原始签名文件:

$tamperedPath = Join-Path $labDir "HelloSigned-Tampered.ps1"

Copy-Item `
    -LiteralPath $scriptPath `
    -Destination $tamperedPath

先确认副本的签名状态:

Get-AuthenticodeSignature `
    -LiteralPath $tamperedPath |
    Select-Object Status, StatusMessage

8.2 修改脚本内容,但保留原签名块

执行:

$text = [System.IO.File]::ReadAllText(
    $tamperedPath,
    [System.Text.Encoding]::ASCII
)

if (-not $text.Contains("'SIGN_LAB_OK'")) {
    throw "Expected test content not found."
}

$text = $text.Replace(
    "'SIGN_LAB_OK'",
    "'SIGN_LAB_CHANGED'"
)

[System.IO.File]::WriteAllText(
    $tamperedPath,
    $text,
    [System.Text.Encoding]::ASCII
)

这里刻意保留签名注释块,只修改被签署的脚本内容。

随后再次检查:

Get-AuthenticodeSignature `
    -LiteralPath $tamperedPath |
    Format-List Status, StatusMessage

预期可能出现:

HashMismatch

表示文件内容与签名所记录的摘要不一致。

如果文件在编辑过程中被工具移除了签名块,则也可能表现为 NotSigned,需要检查实际文件内容。

8.3 为什么修改一点内容,签名就可能失效?

因为签名保护的是文件内容的完整性。

例如:

Write-Output 'SIGN_LAB_OK'

修改为:

Write-Output 'SIGN_LAB_CHANGED'

即使只有字符串发生变化,文件摘要也会改变。

这正是数字签名的重要作用。

它不能阻止别人修改文件,但可以让验证者发现文件与签署时的内容不一致。

因此,正式发布 PowerShell 脚本时,应该先完成代码修改、测试和审查,再执行签名。

签名后不要随意修改源码、换行符或编码。


九、为什么需要时间戳?证书过期后会怎样?

代码签名证书通常具有有效期。

例如:

NotBefore:2026-11-01
NotAfter :2027-11-01

假设开发者在证书有效期内完成了签名。

一年之后证书到期,历史签名是否还能继续验证?

这就涉及时间戳服务。

9.1 时间戳的作用

可信时间戳服务可以为签名提供关于签署时间的密码学证据。

在满足相应信任、算法和撤销状态要求时,即使签名证书后来到期,也可以依据可信时间戳判断签名是否在证书有效期内完成。

需要注意:

时间戳不等于签名证书,也不能解决所有证书撤销和信任问题。

9.2 PowerShell如何使用时间戳?

Set-AuthenticodeSignature 提供:

-TimestampServer

参数。

例如,在已经取得经过验证的组织时间戳服务地址后,可以使用:

$approvedTsaUrl = Read-Host "Approved timestamp server URL"

Set-AuthenticodeSignature `
    -LiteralPath $scriptPath `
    -Certificate $cert `
    -HashAlgorithm SHA256 `
    -TimestampServer $approvedTsaUrl

这里需要满足几个前提:

  • 时间戳服务是可信的;
  • 该服务支持当前签名工具所使用的时间戳协议;
  • 当前 PowerShell 版本支持相应参数和 URL 形式;
  • 证书、网络与组织安全策略允许使用该服务。

本文本地离线实验不需要时间戳,因此前面的基本签名过程没有使用这一参数。

在正式代码签名流程中,建议优先采用组织批准、可长期验证的时间戳服务,并明确工具与协议兼容性。

对于更复杂的 Windows 软件发布,可以进一步研究 Windows SDK 中的 SignTool 及 RFC 3161 时间戳。


十、正式项目如何管理签名证书?

前面的自签名证书实验主要用于理解原理。

真正用于团队开发、内部工具发布或者企业部署时,应该建立正式的证书管理流程。

10.1 测试证书与正式证书的区别

对比自签名测试证书正式代码签名证书
主要用途学习、隔离实验正式软件发布
信任来源需要显式建立信任由受信任的 CA 或组织 PKI 建立
身份验证自行声明按签发机构的身份审核流程
私钥管理本地实验管理应采用严格访问控制
跨机器使用默认通常不受信任取决于接收端信任和策略
时间戳可选实验建议纳入正式流程

这里不应认为“购买了正式证书,任何 Windows 电脑就必然允许运行脚本”。

系统还可能受到受信任发布者策略、证书撤销检查和应用控制策略约束。

10.2 私钥应该如何保护?

代码签名私钥非常重要。

一旦私钥泄露,攻击者就可能使用对应私钥生成签名。

因此,不建议:

  • 将私钥或包含私钥的 PFX 文件提交到 Git 仓库;
  • 将 PFX 与密码存放在同一个公开目录;
  • 在普通共享文件夹中放置可导出的生产私钥;
  • 让所有自动化进程使用同一个高权限签名账户;
  • 把测试证书当成长期生产证书。

正式项目可以考虑使用组织 PKI、专用签名服务、受控密钥存储、硬件安全模块或符合相关要求的签名设备。

10.3 推荐的签名发布流程

是

否

开发 PowerShell 脚本

代码审查

自动化测试

确定发布版本

受控签名

添加可信时间戳

独立验证签名

验证通过?

发布脚本与版本信息

停止发布并调查

图3:PowerShell 脚本从开发到签名发布的推荐流程。

这里有一个容易忽略的问题:

不要在签名之后,再通过自动格式化工具、编码转换工具或构建脚本修改已签署文件。

否则可能破坏签名。

如果需要修改文件,应重新进行审查、测试和签名。


十一、常见签名错误与排查方法

11.1 NotSigned:脚本没有签名

检查:

Get-AuthenticodeSignature `
    -LiteralPath $scriptPath |
    Select-Object Status, StatusMessage

如果是 NotSigned,首先确认是否确实完成了签名,以及文件是否被重新生成或覆盖。

11.2 NotTrusted:发布者或证书不受信任

这时不能只重复执行签名操作。

应检查:

  • 签名证书的指纹;
  • 证书链;
  • 受信任根证书;
  • 受信任发布者;
  • 证书有效期和用途;
  • 组织安全策略。

11.3 HashMismatch:脚本内容不匹配

可能原因包括:

  • 签名后修改了代码;
  • 自动格式化;
  • 换行符转换;
  • 文本编码改变;
  • 文件在分发中被修改。

如果文件包含中文或其他非 ASCII 字符,还应特别关注 Windows PowerShell 5.1 与其他 PowerShell 版本之间的编码兼容性。

对于跨区域、跨机器使用的旧版 Windows PowerShell 签名脚本,微软也记录过由编码和系统区域设置引发的哈希不匹配问题。

11.4 签名有效,但脚本仍然提示PSSecurityException

这种情况尤其值得注意。

签名验证只是脚本执行条件的一部分。

还需要检查:

Get-ExecutionPolicy -List

如果脚本来自 NAS 或 UNC 网络共享路径,还应该检查:

  • UNC 路径的安全区域判断;
  • 文件来源标记;
  • SMB 共享权限;
  • NTFS ACL;
  • 是否存在组织应用控制策略。

因此,PSSecurityException 不一定意味着签名本身无效。

这也是为什么前面的 UNC 脚本排查文章需要单独研究执行策略、文件来源和访问权限。


十二、实验结束后如何清理证书?

如果只完成创建证书和签名的基础实验,通常只需要处理实验目录和本次测试证书。

如果在第七节的隔离测试环境中导入了证书信任,还必须清理相应信任记录。

首先记录本次实验的证书指纹:

$thumbprint = $cert.Thumbprint

$thumbprint

然后检查三个存储区:

$stores = @(
    "My",
    "Root",
    "TrustedPublisher"
)

foreach ($store in $stores) {

    $entry = "Cert:\CurrentUser\$store\$thumbprint"

    if (Test-Path -LiteralPath $entry) {
        Get-Item -LiteralPath $entry |
            Select-Object Subject, Thumbprint
    }
}

确认每一项确实属于本次实验,并且已经不再需要后,才可以在对应存储区删除。

例如,对本次实验导入的受信任发布者记录:

$entry = "Cert:\CurrentUser\TrustedPublisher\$thumbprint"

if (Test-Path -LiteralPath $entry) {
    Remove-Item -LiteralPath $entry -Confirm
}

同样可以针对 CurrentUser\Root 中的本次测试证书进行清理。

最后再处理 CurrentUser\My 中的测试证书及其私钥关联。

不要使用通配符批量删除证书存储区内容。也不要删除任何不能确认属于本次实验的证书。

如果使用了测试虚拟机快照,恢复到实验前的快照通常是更容易审计和控制的清理方式。

实验脚本文件可以在确认不再需要后单独删除。


十三、完整实验验收表

建议在 Windows 上完成以下检查,并保存实际输出。

实验项目预期结果
创建代码签名证书成功获得证书对象
检查证书私钥HasPrivateKey = True
创建测试脚本文件存在
签名前检查NotSigned
执行代码签名脚本出现 Authenticode 签名块
检查签名状态返回实际验证状态
验证证书链可以观察链构建结果
隔离环境中建立信任验证结果按预期变化
修改已签名脚本签名完整性验证失败
清理实验信任测试证书不再作为信任锚
清理实验目录不影响其他项目

这里不应该强行把所有检查都写成 PASS。

例如,基础实验中自签名证书未被信任,返回 NotTrusted 可能正是希望观察到的结果。

真正重要的是理解结果发生的原因,而不是简单地让所有状态都显示为 Valid。


十四、总结

PowerShell 脚本数字签名涉及三个关键问题:

第一,脚本内容是否完整。

数字签名可以帮助验证脚本在签署之后是否被修改。

第二,签名者身份是否受到信任。

数字签名需要结合证书、私钥、公钥及信任链判断。自签名证书可以用于实验,但不应该直接取代正式 PKI 体系。

第三,系统是否允许脚本执行。

即使签名完整且证书受到信任,仍然可能受到执行策略、文件权限或其他安全策略限制。

因此,排查 PowerShell 脚本执行异常时,应该按照以下顺序思考:

检查签名 → 验证完整性 → 检查证书用途与信任链 → 检查发布者信任 → 检查执行策略和其他安全控制。

本篇通过创建自签名证书、签署测试脚本、验证证书链和修改已签名文件,建立了对 PowerShell 代码签名机制的完整理解。

对于企业内部脚本、Windows 自动化工具、Codex 辅助开发脚本和本地 AI Agent 运维脚本,建立可审计的签名与发布流程,比单纯修改执行策略更可靠。


参考资料

  1. Microsoft:about_Signing
    https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_signing

  2. Microsoft:New-SelfSignedCertificate
    https://learn.microsoft.com/powershell/module/pki/new-selfsignedcertificate

  3. Microsoft:Set-AuthenticodeSignature
    https://learn.microsoft.com/powershell/module/microsoft.powershell.security/set-authenticodesignature

  4. Microsoft:Get-AuthenticodeSignature
    https://learn.microsoft.com/powershell/module/microsoft.powershell.security/get-authenticodesignature

  5. Microsoft:about_Execution_Policies
    https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_execution_policies

  6. Microsoft:Import-Certificate
    https://learn.microsoft.com/powershell/module/pki/import-certificate

  7. Microsoft:PowerShell 签名脚本 HashMismatch 故障
    https://learn.microsoft.com/troubleshoot/windows-client/system-management-components/signed-powershell-script-fails-hash-mismatch

  8. Microsoft:Time Stamping Authenticode Signatures
    https://learn.microsoft.com/windows/win32/seccrypto/time-stamping-authenticode-signatures


本文属于《Windows 开发环境与 PowerShell 实战》系列。

上一篇介绍了 PowerShell 5.1 与 PowerShell 7 的安装共存、PATH、Profile 和 UTF-8 编码配置。

本篇重点介绍脚本数字签名机制。后续将继续讨论 RemoteSigned、PSSecurityException 与 UNC 网络共享脚本执行问题。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值