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 机制对脚本进行签名。
简化过程如下:
图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
这里能够帮助我们回答:
- 当前文件是否存在签名?
- 使用的是哪张签名证书?
- 系统报告的验证状态是什么?
- 是否存在时间戳证书信息?
需要特别注意,Status = Valid 也不代表脚本内容经过安全审查,更不应替代对发布者身份和证书信任政策的检查。
六、理解证书存储区与信任链
这部分是整个实验中最容易产生误解的地方。
6.1 Windows证书存储区有哪些?
可以通过:
Get-ChildItem Cert:\CurrentUser
查看当前用户的证书存储区。
本文重点涉及三个位置:
| 证书存储区 | 主要用途 |
|---|---|
CurrentUser\My | 保存当前用户的个人证书及关联私钥 |
CurrentUser\Root | 保存当前用户信任的根证书 |
CurrentUser\TrustedPublisher | 保存受信任的软件发布者证书 |
还存在对应的 LocalMachine 计算机级证书存储区。
两者作用范围不同,不应随意混用。
6.2 什么是证书信任链?
正式的代码签名证书通常由 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 推荐的签名发布流程
图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 运维脚本,建立可审计的签名与发布流程,比单纯修改执行策略更可靠。
参考资料
-
Microsoft:about_Signing
https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_signing -
Microsoft:New-SelfSignedCertificate
https://learn.microsoft.com/powershell/module/pki/new-selfsignedcertificate -
Microsoft:Set-AuthenticodeSignature
https://learn.microsoft.com/powershell/module/microsoft.powershell.security/set-authenticodesignature -
Microsoft:Get-AuthenticodeSignature
https://learn.microsoft.com/powershell/module/microsoft.powershell.security/get-authenticodesignature -
Microsoft:about_Execution_Policies
https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_execution_policies -
Microsoft:Import-Certificate
https://learn.microsoft.com/powershell/module/pki/import-certificate -
Microsoft:PowerShell 签名脚本 HashMismatch 故障
https://learn.microsoft.com/troubleshoot/windows-client/system-management-components/signed-powershell-script-fails-hash-mismatch -
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 网络共享脚本执行问题。
本文详解PowerShell脚本数字签名机制,涵盖自签名证书创建、脚本签名与验证、证书存储区与信任链关系、签名完整性保护原理、时间戳作用及私钥安全管理。重点说明签名≠执行许可,需结合执行策略、证书信任链和系统安全控制综合排查NotTrusted、HashMismatch等常见错误。
374

被折叠的 条评论
为什么被折叠?



