脚本签名全攻略:从获取证书到验证签名
在当今数字化的时代,脚本签名是保障代码完整性和安全性的重要手段。本文将详细介绍脚本签名的各个方面,包括获取代码签名证书、安装证书、签名过程、防止签名过期、验证签名以及如何在多台机器上部署签名脚本等内容。
1. 获取代码签名证书
获取代码签名证书有两种常见的方式:
-
从商业证书提供商处逐个订购
:可以选择像 Digicert、GlobalSign、Sectigo 等商业证书提供商。这种方式相对便宜、快速且简单,但每颁发一个证书都需要付费。如果计划颁发大量证书,这种方式可能不是最具成本效益的选择。
-
注册托管 PKI 服务
:这是一种全面的 PKI 服务,具有很高的灵活性,而且无需担心基础设施的管理。许多安全公司都提供 PKI 即服务,例如 SecureW2、HydrantID、Entrust 等。
2. 正确安装代码签名证书
为了使整个签名过程正常工作,代码签名证书及其证书链必须正确安装在系统中。具体安装要求如下:
2.1 签名者计算机
-
证书本身
:必须存储在两个证书存储区中:
- 个人(用户或计算机) :供签名者使用该证书对代码进行签名。
- 受信任的发布者 :如果证书不在此位置,将无法验证签名。
- 证书链的根证书 :必须存储在“受信任的根证书颁发机构”存储区中,通常是在证书的“证书路径”选项卡中看到的最顶层证书。
2.2 用户计算机
- 代码签名证书 :必须存储在“受信任的发布者”存储区中。
- 根证书 :必须存储在“受信任的根证书颁发机构”存储区中。
- 任何中间证书 :必须存储在“中间证书颁发机构”存储区中。
PowerShell 证书提供程序对这些存储区有不同的名称,具体如下表所示:
| GUI 名称 | 证书提供程序名称 |
| — | — |
| 个人 | My |
| 受信任的发布者 | TrustedPublisher |
| 受信任的根证书颁发机构 | Root |
以下是安装证书的示例代码:
# 保存代码签名证书到变量
$Cert = Get-ChildItem -Path 'Cert:\LocalMachine\My' -CodeSigningCert
# 安装为受信任的发布者证书
$SigningCert = $Cert
$TrustPubStor = [System.Security.Cryptography.X509Certificates.X509Store]::new(
[System.Security.Cryptography.X509Certificates.StoreName]::TrustedPublisher,
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
)
$TrustPubStor.Open(
[System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite
)
$TrustPubStor.Add($SigningCert)
$TrustPubStor.Close()
# 安装为根证书
$RootCert = $Cert
$RootStore = [System.Security.Cryptography.X509Certificates.X509Store]::new(
[System.Security.Cryptography.X509Certificates.StoreName]::Root,
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
)
$RootStore.Open(
[System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite
)
$RootStore.Add($RootCert)
$RootStore.Close()
3. 签名过程
无论通过何种方式获取代码签名证书,签名过程都是相同的。具体步骤如下:
1. 从存储区中检索代码签名证书。
2. 使用
Set-AuthenticodeSignature
cmdlet,传入证书和要签名的脚本的路径。
以下是签名脚本的示例代码:
$ScriptPath = Join-Path -Path $env:Temp -ChildPath 'test1.ps1'
Set-Content -Path $ScriptPath -Value 'Write-Host "Signed Script"'
$Cert = Get-ChildItem -Path 'Cert:\CurrentUser\My' -CodeSigningCert
Set-AuthenticodeSignature -Certificate $Cert -FilePath $ScriptPath
Set-AuthenticodeSignature
支持文件大小不小于 4 字节。对于超小脚本,可以使用空格和换行符来满足要求。
4. 防止签名过期
代码签名证书有有限的有效期,证书过期后,使用该证书签名的代码将被视为未签名。为了解决这个问题,可以使用时间戳服务器。时间戳的目的是证明在代码签名证书仍然有效的时候对代码进行了签名,这样即使证书过期,代码仍然可以保持签名状态。
以下是一些公开可用的时间戳服务器:
- http://timestamp.digicert.com
- http://timestamp.sectigo.com
- http://timestamp.verisign.com/scripts/timstamp.dll
- https://www.freetsa.org/
在签名脚本时包含时间戳的示例代码如下:
$Params = @{
Certificate = $Cert
FilePath = $ScriptPath
TimestampServer = 'http://timestamp.digicert.com'
}
Set-AuthenticodeSignature @Params
5. 其他可签名的内容
5.1 函数
在 PowerShell 中,不能直接对函数进行签名,因为 PowerShell 只支持对文件进行签名。可以将每个函数放在一个
.ps1
文件中,对该文件进行签名,从而间接对函数进行签名。
.ps1
文件的内容示例如下:
function Do-Stuff {
'stuff done'
}
# SIG # Begin signature block
# ...
# SIG # End signature block
5.2 模块
对模块进行签名很简单,只需使用
Set-AuthenticodeSignature
cmdlet 对每个
.ps1
、
.psm1
、
.psd1
和
.ps1xml
文件进行签名。之所以要对所有这些文件进行签名,是为了防止篡改。
6. 验证签名
为了确保 PowerShell 文件已正确签名,可以使用以下几种工具:
-
PowerShell(Get-AuthenticodeSignature)
-
Sysinternals sigcheck
-
signtool.exe
-
在资源管理器中打开文件属性,查看“数字签名”选项卡
6.1 Get-AuthenticodeSignature
以下是使用
Get-AuthenticodeSignature
显示签名的示例代码及输出:
Get-AuthenticodeSignature -FilePath 'C:\test1' | Select-Object -Property *
输出示例:
SignerCertificate
: [Subject]
CN=MySelfCodeSigningCert
[Issuer]
CN=MySelfCodeSigningCert
[Serial Number]
1B599557458628A54D3D9EA33D15C160
[Not Before]
13/06/2021 23:38:18
[Not After]
13/06/2022 23:58:18
[Thumbprint]
F45E5297DA01A97E527F5AF262F29B4A8CCF2083
TimeStamperCertificate :
Status
: Valid
StatusMessage
: Signature verified.
Path
: C:\test.ps1
SignatureType
: Authenticode
IsOSBinary
: False
当证书链的根证书不在“受信任的根证书颁发机构”存储区中时,输出示例如下:
SignerCertificate
: [Subject]
CN=MySelfCodeSigningCert
[Issuer]
CN=MySelfCodeSigningCert
[Serial Number]
1B599557458628A54D3D9EA33D15C160
[Not Before]
13/06/2021 23:38:18
[Not After]
13/06/2022 23:58:18
[Thumbprint]
F45E5297DA01A97E527F5AF262F29B4A8CCF2083
TimeStamperCertificate :
Status
: UnknownError
StatusMessage
: A certificate chain processed, but terminated in
a root certificate which is not trusted by the trust provider.
Path
: C:\test.ps1
SignatureType
: Authenticode
IsOSBinary
: False
6.2 Sigcheck
以下是使用
sigcheck
显示签名的示例代码及输出:
sigcheck64.exe C:\test.ps1
输出示例:
Sigcheck v2.82 - File version and signature viewer
Copyright (C) 2004-2021 Mark Russinovich
Sysinternals - www.sysinternals.com
c:\test.ps1:
Verified:
Signed
Signing date:
00:03 14/06/2021
Publisher:
MySelfCodeSigningCert
Company:
n/a
Description:
n/a
Product:
n/a
Prod version:
n/a
File version:
n/a
MachineType:
n/a
当证书链的根证书不在“受信任的根证书颁发机构”存储区中时,输出示例如下:
Sigcheck v2.82 - File version and signature viewer
Copyright (C) 2004-2021 Mark Russinovich
Sysinternals - www.sysinternals.com
c:\test.ps1:
Verified:
A certificate chain processed, but terminated in a root
certificate which is not trusted by the trust provider.
File date:
00:03 14/06/2021
Publisher:
MySelfCodeSigningCert
Company:
n/a
Description:
n/a
Product:
n/a
Prod version:
n/a
File version:
n/a
MachineType:
n/a
6.3 Signtool
以下是使用
signtool
显示签名的示例代码及输出:
$Params = @{
Path = ${Env:ProgramFiles(x86)}
ChildPath = 'Windows Kits\10\bin\10.0.22000.0\x64\signtool.exe'
}
$SignToolPath = Join-Path @Params
& $SignToolPath verify /pa C:\test.ps1
输出示例:
File: C:\test.ps1
Index
Algorithm
Timestamp
========================================
0
sha1
None
Successfully verified: C:\test.ps1
当证书链的根证书不在“受信任的根证书颁发机构”存储区中时,输出示例如下:
File: C:\test.ps1
Index
Algorithm
Timestamp
========================================
SignTool Error: A certificate chain processed, but terminated in a root
certificate which is not trusted by the trust provider.
Number of errors: 1
6.4 执行错误
如果用于签名代码的证书在机器的“受信任的发布者”存储区中,这些工具将不会提示任何信息。只有当证书不存在时,才会出现提示。尝试运行使用不受信任证书签名的脚本时,会看到如下提示:
C:\test.ps1
Do you want to run software from this untrusted publisher?
File C:\test.ps1 is published by CN=MySelfCodeSigningCert and is not trusted on
your system. Only run scripts from trusted publishers.
[V] Never run
[D] Do not run
[R] Run once
[A] Always run
[?] Help (default
is "D"):
如果系统中未安装正确的根证书,运行签名脚本时会收到如下错误:
C:\test.ps1
.\test.ps1 : File C:\test.ps1 cannot be loaded. A certificate chain processed,
but terminated in a root certificate which is not trusted by the trust provider
At line:1 char:1
+ .\test.ps1
+ ~~~~~~~~~~~~~~
+ CategoryInfo
: SecurityError: (:) [], PSSecurityException
+ FullyQualifiedErrorId : UnauthorizedAccess
7. 扩展部署
当需要在多台机器上运行签名脚本时,可以考虑使用自己的 PKI 来颁发签名证书。以下是构建自己的 PKI 的详细步骤:
7.1 为什么需要自己的 PKI
- 合规性 :某些法规可能要求将所有敏感/安全信息存储在本地。
- 安全性 :可能不信任服务提供商对 PKI 的管理,因此选择自己管理。
- 可用性 :使用自己的服务器可以实现任何可用性级别,而托管服务并不总是能满足这一需求。
- 灵活性 :可以自行定义颁发哪些证书、如何颁发以及颁发给谁,SaaS 服务并不总是具有这样的灵活性。
- 成本 :一般来说,自己动手做更便宜,但这并不是普遍适用的规则,因为支持自己的基础设施需要优秀的工程师和硬件,这些都需要成本。
7.2 仅使用 PowerShell 构建 PKI
7.2.1 根 CA
构建自己的 PKI 的第一步是配置根证书颁发机构。为了遵循最佳实践,根 CA 应该是一个可以正常离线(关机)的虚拟机或可以关闭的物理机器。
以下是配置根 CA 的详细步骤:
1.
安装 Active Directory 证书服务 CA 功能
:
Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools
- 执行证书颁发机构的初始配置 :
$Params = @{
CACommonName = 'My Root CA'
ValidityPeriod = 'Years'
ValidityPeriodUnits = 10
CryptoProviderName = 'ECDSA_P521#Microsoft Software Key Storage Provider'
KeyLength = 521
HashAlgorithmName = 'SHA512'
CAType = 'StandaloneRootCA'
Force = $true
}
Install-AdcsCertificationAuthority @Params
在配置根 CA 时,还需要了解两个重要概念:CDP(CRL 分发点)和 AIA(权威信息访问)。
-
CDP
:是证书颁发机构存储证书吊销列表(CRL)的位置,客户端会定期下载该列表,以检查呈现给它们的证书是否已被吊销。
-
AIA
:是客户端可以从中下载证书颁发机构证书的位置。
以下是获取根 CA 的 CDP 和 AIA 位置列表的示例代码:
# 获取 CDP 列表
Get-CACrlDistributionPoint
# 获取 AIA 位置列表
Get-CAAuthorityInformationAccess
由于根 CA 是离线服务器,默认定义的 CDP 路径将不可用,因此需要移除默认的 CDP:
Get-CACrlDistributionPoint | Remove-CACrlDistributionPoint -Force
接下来,需要创建新的 AIA 和 CDP 位置。以下是添加新 CDP 和 AIA 位置的示例代码:
# 添加新的自定义 CDP
$Params = @{
Uri = 'http://pki.example.com/CDP/MyRootCA.crl'
AddToCertificateCdp = $true
Force = $true
}
Add-CACrlDistributionPoint @Params
# 添加文件系统 CDP
$Params = @{
Path = $env:SystemRoot
ChildPath = 'system32\CertSrv\CertEnroll\MyRootCA.crl'
}
$LocalCRLPath = Join-Path @Params
Add-CACrlDistributionPoint -Uri $LocalCRLPath -PublishToServer -Force
# 添加自定义 AIA 位置
$Params = @{
Uri = 'http://pki.example.com/AIA/MyRootCA.crt'
AddToCertificateAia = $true
Force = $true
}
Add-CAAuthorityInformationAccess @Params
默认情况下,根 CA 颁发的 CRL 的 NextUpdate 属性设置为大约 7 天后,为了减少手动干预,可以将 CRL 刷新间隔增加到更合理的六个月:
# 调整 CRL 更新周期从天改为月
certutil -setreg CA\CRLPeriod Months
# 设置 CRL 更新间隔为 6 个月
certutil -setreg CA\CRLPeriodUnits 6
默认情况下,根 CA 颁发的证书有效期最长为一年,可以将其增加到五年:
# 设置 CA 证书有效期间隔为 5 年
certutil -setreg CA\ValidityPeriodUnits 5
# 确认 CA 证书有效期以年为单位
certutil -getreg CA\ValidityPeriod
应用所有这些更改后,需要重启 CA 服务,并颁发更新后的 CRL:
# 重启证书服务
Restart-Service -Name CertSvc
# 颁发新的 CRL
certutil -CRL
7.2.2 颁发 CA
颁发 CA 是向端点(计算机和用户)颁发证书(包括代码签名证书)的服务器。以下是配置颁发 CA 的详细步骤:
1.
导入根 CA 的证书
:
$JPParams = @{
Path = $env:SystemDrive
ChildPath = 'ROOTCA01_My Root CA.crt'
}
$RootCRTPath = Join-Path @JPParams
$ICParams = @{
FilePath = $RootCRTPath
CertStoreLocation = 'Cert:\LocalMachine\Root\'
}
Import-Certificate @ICParams
- 安装 AD CS 角色 :
Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools
- 安装 CA 本身 :
$Params = @{
CACommonName = 'My Issuing CA'
CryptoProviderName = 'ECDSA_P256#Microsoft Software Key Storage Provider'
KeyLength = 256
HashAlgorithmName = 'SHA256'
CAType = 'EnterpriseSubordinateCA'
Force = $true
}
Install-AdcsCertificationAuthority @Params
安装完成后,需要手动从根 CA 获取该 CA 的证书。以下是提交颁发 CA 的证书请求、批准请求并导出证书的示例代码:
# 提交颁发 CA 的证书请求
$CA = Connect-CertificationAuthority
$Params = @{
Path = $env:SystemDrive
ChildPath = 'CA02.ad.example.net_My Issuing CA.req'
}
$Path = Join-Path @Params
Submit-CertificateRequest -Path $Path -CertificationAuthority $CA
# 检查提交请求的状态
Get-PendingRequest -CertificationAuthority $CA
# 批准颁发 CA 的证书请求
$Request = Get-PendingRequest -CertificationAuthority $CA
Approve-CertificateRequest -Request $Request
# 显示批准的证书请求
$IssuedRequest = Get-IssuedRequest -CertificationAuthority $CA -RequestID 2
$IssuedRequest
# 导出颁发的证书
$Item = Get-Item -Path $Env:SystemDrive
Receive-Certificate -RequestRow $IssuedRequest -Path $Item | Format-List
将导出的证书复制到颁发 CA 计算机后,使用以下命令导入该证书:
certutil -installcert "$Env:SystemDrive\RequestID_2.cer"
最后,启动“CertSvc”服务:
Start-Service -Name 'CertSvc'
与根 CA 类似,需要对颁发 CA 的 CDP 和 AIA 位置进行配置,移除不需要的位置,并添加需要使用的位置:
# 移除默认的 CDP
Get-CACrlDistributionPoint | Remove-CACrlDistributionPoint -Force
# 移除除本地文件系统外的所有 AIA 位置
Get-CAAuthorityInformationAccess |
Where-Object -FilterScript {$_.Uri -notlike ('{0}*' -f $env:SystemRoot)} |
Remove-CAAuthorityInformationAccess -Force
# 添加自定义 CDP
$Params = @{
Uri = 'http://pki.example.com/CDP/MyIssuingCA.crl'
AddToCertificateCdp = $true
Force = $true
}
Add-CACrlDistributionPoint @Params
# 添加文件系统 CDP
$Params = @{
Path = $env:SystemRoot
ChildPath = 'system32\CertSrv\CertEnroll\MyIssuingCA.crl'
}
$Path = Join-Path @Params
Add-CACrlDistributionPoint -Uri $Path -PublishToServer -Force
# 添加自定义 AIA 位置
$Params = @{
Uri = 'http://pki.example.com/AIA/MyIssuingCA.crt'
AddToCertificateAia = $true
Force = $true
}
Add-CAAuthorityInformationAccess @Params
检查颁发 CA 的 CRL 更新周期和间隔是否设置为一周:
# 确认颁发 CA 的 CRL 更新周期为周
certutil -getreg CA\CRLPeriod
# 确认颁发 CA 的 CRL 更新间隔为 1 周
certutil -getreg CA\CRLPeriodUnits
最后,重启 CA 服务并重新颁发 CRL:
# 重启证书服务
Restart-Service -Name CertSvc
# 重新颁发 CRL
certutil -CRL
7.2.3 创建签名模板
在 Windows PKI 中,证书模板是一组属性,定义了如何颁发证书、用于何种目的、谁可以颁发证书、颁发给谁以及证书可以存储在何处。
以下是创建签名模板的详细步骤:
1.
获取现有证书模板列表
:
Get-CertificateTemplate
- 创建新的证书模板 :
$JSONRaw = @'
{
"name": "MyCodeSigning",
"displayName": "My Code Signing",
"objectClass": "pKICertificateTemplate",
"flags": 131616,
"revision": 100,
"msPKI-Cert-Template-OID": "1.3.6.1.4.1.311.21.8.6625186.9886736.8734392.15429154.6687822.90.4158550.10744840",
"msPKI-Certificate-Application-Policy": ["1.3.6.1.5.5.7.3.3"],
"msPKI-Certificate-Name-Flag": -2113929216,
"msPKI-Enrollment-Flag": 32,
"msPKI-Minimal-Key-Size": 256,
"msPKI-Private-Key-Flag": 101056512,
"msPKI-RA-Application-Policies": [
"msPKI-Asymmetric-Algorithm`PZPWSTR`ECDSA_P256`msPKI-Hash-Algorithm`PZPWSTR`SHA256`msPKI-Key-Usage`DWORD`2`msPKI-Symmetric-Algorithm`PZPWSTR`3DES`msPKI-Symmetric-Key-Length`DWORD`168`"
],
"msPKI-RA-Signature": 0,
"msPKI-Template-Minor-Revision": 2,
"msPKI-Template-Schema-Version": 4,
"pKICriticalExtensions": ["2.5.29.15"],
"pKIDefaultKeySpec": 2,
"pKIExpirationPeriod": [0, 64, 57, 135, 46, 225, 254, 255],
"pKIExtendedKeyUsage": ["1.3.6.1.5.5.7.3.3"],
"pKIKeyUsage": [128, 0],
"pKIMaxIssuingDepth": 0,
"pKIOverlapPeriod": [0, 128, 166, 10, 255, 222, 255, 255]
}
'@
$JSONRaw = $JSONRaw -replace '\r?\n'
New-ADCSTemplate -DisplayName 'My Code Signing' -JSON $JSONRaw
- 手动添加缺失的属性 :
$JSON = ConvertFrom-Json -InputObject $JSONRaw
$DN = (Get-ADCSTemplate -DisplayName 'My Code Signing').DistinguishedName
Set-ADObject -Identity $DN -Add @{
'msPKI-RA-Application-Policies' = $JSON.'msPKI-RA-Application-Policies'
}
- 发布模板 :
Get-CertificationAuthority | Get-CATemplate |
Add-CATemplate -DisplayName 'My Code Signing' | Set-CATemplate
7.2.4 根证书部署
在使用 PKI 中的证书之前,需要确保其根证书颁发机构的证书已分发给组织中所有可能使用该证书的用户以及所有签名脚本的消费者。可以使用以下几种方法进行根证书部署:
-
手动导入
:可以在任何机器上手动执行命令来导入证书,对于非域客户端尤其有用。示例代码如下:
$Path = Join-Path $env:SystemDrive -ChildPath 'ROOTCA01_My Root CA.crt'
$Params = @{
FilePath = $Path
CertStoreLocation = 'Cert:\LocalMachine\Root\'
}
Import-Certificate @Params
- AD DS 证书容器 :这是将根 CA 证书部署到加入域的机器的首选方法。使用 AD DS 证书容器可以使证书同时对所有域客户端可用。示例代码如下:
$Path = Join-Path -Path $env:SystemDrive -ChildPath 'ROOTCA01_My Root CA.crt'
$Cert = [Security.Cryptography.X509Certificates.X509Certificate2]::new($Path)
$RCAADContainer = Get-AdPkiContainer -ContainerType RootCA
$AAParams = @{
AdContainer = $RCAADContainer
Certificate = $Cert
Dispose = $true
}
Add-AdCertificate @AAParams | Format-List
- DSC(期望状态配置) :DSC 是管理非域计算机系统配置的理想方法,也可以很好地补充基于 GPO 的管理。可以使用 DSC 来安装证书。示例代码如下:
Configuration DeployMyRootCertificate
{
Import-DscResource -ModuleName CertificateDsc
Node localhost
{
CertificateImport MyRootCertificate
{
Thumbprint = '0000000000000000000000000000000000000000'
Location = 'LocalMachine'
Store = 'Root'
Path = '\\ad.example.net\NETLOGON\ROOTCA01_My Root CA.crt'
}
}
}
- Intune :对于连接到 Intune 的工作站,可以使用受信任的证书配置文件来导入根证书。
7.2.5 颁发签名证书
完成上述所有步骤后,就可以颁发新的签名证书了:
$Params = @{
Template = 'MyCodeSigning'
CertStoreLocation = 'Cert:\CurrentUser\My'
}
Get-Certificate @Params
通过以上步骤,你可以全面掌握脚本签名的各个方面,从获取证书到验证签名,并能够在多台机器上部署签名脚本,确保代码的安全性和完整性。
以下是脚本签名的整体流程 mermaid 流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(获取代码签名证书):::process
B --> C(安装代码签名证书):::process
C --> D(签名过程):::process
D --> E{是否需要防止签名过期?}:::decision
E -- 是 --> F(使用时间戳服务器):::process
E -- 否 --> G(其他可签名内容):::process
F --> G
G --> H(验证签名):::process
H --> I(扩展部署):::process
I --> J([结束]):::startend
7.2.6 总结 PKI 构建流程
为了更清晰地展示构建 PKI 的整个过程,以下是一个详细的步骤总结表格:
|步骤|操作内容|代码示例|
| ---- | ---- | ---- |
|根 CA 配置|安装 Active Directory 证书服务 CA 功能|
Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools
|
| |执行证书颁发机构的初始配置|
powershell<br>$Params = @{<br> CACommonName = 'My Root CA'<br> ValidityPeriod = 'Years'<br> ValidityPeriodUnits = 10<br> CryptoProviderName = 'ECDSA_P521#Microsoft Software Key Storage Provider'<br> KeyLength = 521<br> HashAlgorithmName = 'SHA512'<br> CAType = 'StandaloneRootCA'<br> Force = $true<br>}<br>Install-AdcsCertificationAuthority @Params<br>
|
| |移除默认 CDP|
Get-CACrlDistributionPoint | Remove-CACrlDistributionPoint -Force
|
| |添加新 CDP 和 AIA 位置|
powershell<br># 添加新的自定义 CDP<br>$Params = {@<br> Uri = 'http://pki.example.com/CDP/MyRootCA.crl'<br> AddToCertificateCdp = $true<br> Force = $true<br>}<br>Add-CACrlDistributionPoint @Params<br><br># 添加文件系统 CDP<br>$Params = {@<br> Path = $env:SystemRoot<br> ChildPath = 'system32\CertSrv\CertEnroll\MyRootCA.crl'<br>}<br>$LocalCRLPath = Join-Path @Params<br>Add-CACrlDistributionPoint -Uri $LocalCRLPath -PublishToServer -Force<br><br># 添加自定义 AIA 位置<br>$Params = {@<br> Uri = 'http://pki.example.com/AIA/MyRootCA.crt'<br> AddToCertificateAia = $true<br> Force = $true<br>}<br>Add-CAAuthorityInformationAccess @Params<br>
|
| |调整 CRL 更新周期和有效期|
powershell<br># 调整 CRL 更新周期从天改为月<br>certutil -setreg CA\CRLPeriod Months<br><br># 设置 CRL 更新间隔为 6 个月<br>certutil -setreg CA\CRLPeriodUnits 6<br><br># 设置 CA 证书有效期间隔为 5 年<br>certutil -setreg CA\ValidityPeriodUnits 5<br><br># 确认 CA 证书有效期以年为单位<br>certutil -getreg CA\ValidityPeriod<br>
|
| |重启 CA 服务并颁发新 CRL|
powershell<br># 重启证书服务<br>Restart-Service -Name CertSvc<br><br># 颁发新的 CRL<br>certutil -CRL<br>
|
|颁发 CA 配置|导入根 CA 的证书|
powershell<br>$JPParams = {@<br> Path = $env:SystemDrive<br> ChildPath = 'ROOTCA01_My Root CA.crt'<br>}<br>$RootCRTPath = Join-Path @JPParams<br>$ICParams = {@<br> FilePath = $RootCRTPath<br> CertStoreLocation = 'Cert:\LocalMachine\Root\'<br>}<br>Import-Certificate @ICParams<br>
|
| |安装 AD CS 角色|
Install-WindowsFeature -Name ADCS-Cert-Authority -IncludeManagementTools
|
| |安装 CA 本身|
powershell<br>$Params = {@<br> CACommonName = 'My Issuing CA'<br> CryptoProviderName = 'ECDSA_P256#Microsoft Software Key Storage Provider'<br> KeyLength = 256<br> HashAlgorithmName = 'SHA256'<br> CAType = 'EnterpriseSubordinateCA'<br> Force = $true<br>}<br>Install-AdcsCertificationAuthority @Params<br>
|
| |提交、批准并导出证书|
powershell<br># 提交颁发 CA 的证书请求<br>$CA = Connect-CertificationAuthority<br>$Params = {@<br> Path = $env:SystemDrive<br> ChildPath = 'CA02.ad.example.net_My Issuing CA.req'<br>}<br>$Path = Join-Path @Params<br>Submit-CertificateRequest -Path $Path -CertificationAuthority $CA<br><br># 检查提交请求的状态<br>Get-PendingRequest -CertificationAuthority $CA<br><br># 批准颁发 CA 的证书请求<br>$Request = Get-PendingRequest -CertificationAuthority $CA<br>Approve-CertificateRequest -Request $Request<br><br># 显示批准的证书请求<br>$IssuedRequest = Get-IssuedRequest -CertificationAuthority $CA -RequestID 2<br>$IssuedRequest<br><br># 导出颁发的证书<br>$Item = Get-Item -Path $Env:SystemDrive<br>Receive-Certificate -RequestRow $IssuedRequest -Path $Item | Format-List<br>
|
| |导入导出的证书并启动服务|
powershell<br>certutil -installcert "$Env:SystemDrive\RequestID_2.cer"<br>Start-Service -Name 'CertSvc'<br>
|
| |配置颁发 CA 的 CDP 和 AIA 位置|
powershell<br># 移除默认的 CDP<br>Get-CACrlDistributionPoint | Remove-CACrlDistributionPoint -Force<br><br># 移除除本地文件系统外的所有 AIA 位置<br>Get-CAAuthorityInformationAccess |<br>Where-Object -FilterScript {$_.Uri -notlike ('{0}*' -f $env:SystemRoot)} |<br>Remove-CAAuthorityInformationAccess -Force<br><br># 添加自定义 CDP<br>$Params = {@<br> Uri = 'http://pki.example.com/CDP/MyIssuingCA.crl'<br> AddToCertificateCdp = $true<br> Force = $true<br>}<br>Add-CACrlDistributionPoint @Params<br><br># 添加文件系统 CDP<br>$Params = {@<br> Path = $env:SystemRoot<br> ChildPath = 'system32\CertSrv\CertEnroll\MyIssuingCA.crl'<br>}<br>$Path = Join-Path @Params<br>Add-CACrlDistributionPoint -Uri $Path -PublishToServer -Force<br><br># 添加自定义 AIA 位置<br>$Params = {@<br> Uri = 'http://pki.example.com/AIA/MyIssuingCA.crt'<br> AddToCertificateAia = $true<br> Force = $true<br>}<br>Add-CAAuthorityInformationAccess @Params<br>
|
| |检查并重启服务颁发 CRL|
powershell<br># 确认颁发 CA 的 CRL 更新周期为周<br>certutil -getreg CA\CRLPeriod<br><br># 确认颁发 CA 的 CRL 更新间隔为 1 周<br>certutil -getreg CA\CRLPeriodUnits<br><br># 重启证书服务<br>Restart-Service -Name CertSvc<br><br># 重新颁发 CRL<br>certutil -CRL<br>
|
|创建签名模板|获取现有证书模板列表|
Get-CertificateTemplate
|
| |创建新的证书模板|
powershell<br>$JSONRaw = @'<br>{<br> "name": "MyCodeSigning",<br> "displayName": "My Code Signing",<br> "objectClass": "pKICertificateTemplate",<br> "flags": 131616,<br> "revision": 100,<br> "msPKI-Cert-Template-OID": "1.3.6.1.4.1.311.21.8.6625186.9886736.8734392.15429154.6687822.90.4158550.10744840",<br> "msPKI-Certificate-Application-Policy": ["1.3.6.1.5.5.7.3.3"],<br> "msPKI-Certificate-Name-Flag": -2113929216,<br> "msPKI-Enrollment-Flag": 32,<br> "msPKI-Minimal-Key-Size": 256,<br> "msPKI-Private-Key-Flag": 101056512,<br> "msPKI-RA-Application-Policies": [<br> "msPKI-Asymmetric-Algorithm`PZPWSTR`ECDSA_P256`msPKI-Hash-Algorithm`PZPWSTR`SHA256`msPKI-Key-Usage`DWORD`2`msPKI-Symmetric-Algorithm`PZPWSTR`3DES`msPKI-Symmetric-Key-Length`DWORD`168`"<br> ],<br> "msPKI-RA-Signature": 0,<br> "msPKI-Template-Minor-Revision": 2,<br> "msPKI-Template-Schema-Version": 4,<br> "pKICriticalExtensions": ["2.5.29.15"],<br> "pKIDefaultKeySpec": 2,<br> "pKIExpirationPeriod": [0, 64, 57, 135, 46, 225, 254, 255],<br> "pKIExtendedKeyUsage": ["1.3.6.1.5.5.7.3.3"],<br> "pKIKeyUsage": [128, 0],<br> "pKIMaxIssuingDepth": 0,<br> "pKIOverlapPeriod": [0, 128, 166, 10, 255, 222, 255, 255]<br>}<br>'@<br>$JSONRaw = $JSONRaw -replace '\r?\n'<br>New-ADCSTemplate -DisplayName 'My Code Signing' -JSON $JSONRaw<br>
|
| |手动添加缺失的属性|
powershell<br>$JSON = ConvertFrom-Json -InputObject $JSONRaw<br>$DN = (Get-ADCSTemplate -DisplayName 'My Code Signing').DistinguishedName<br>Set-ADObject -Identity $DN -Add @{<br> 'msPKI-RA-Application-Policies' = $JSON.'msPKI-RA-Application-Policies'<br>}<br>
|
| |发布模板|
powershell<br>Get-CertificationAuthority | Get-CATemplate |<br>Add-CATemplate -DisplayName 'My Code Signing' | Set-CATemplate<br>
|
|根证书部署|手动导入|
powershell<br>$Path = Join-Path $env:SystemDrive -ChildPath 'ROOTCA01_My Root CA.crt'<br>$Params = {@<br> FilePath = $Path<br> CertStoreLocation = 'Cert:\LocalMachine\Root\'<br>}<br>Import-Certificate @Params<br>
|
| |AD DS 证书容器|
powershell<br>$Path = Join-Path -Path $env:SystemDrive -ChildPath 'ROOTCA01_My Root CA.crt'<br>$Cert = [Security.Cryptography.X509Certificates.X509Certificate2]::new($Path)<br>$RCAADContainer = Get-AdPkiContainer -ContainerType RootCA<br>$AAParams = {@<br> AdContainer = $RCAADContainer<br> Certificate = $Cert<br> Dispose = $true<br>}<br>Add-AdCertificate @AAParams | Format-List<br>
|
| |DSC|
powershell<br>Configuration DeployMyRootCertificate<br>{<br> Import-DscResource -ModuleName CertificateDsc<br><br> Node localhost<br> {<br> CertificateImport MyRootCertificate<br> {<br> Thumbprint = '0000000000000000000000000000000000000000'<br> Location = 'LocalMachine'<br> Store = 'Root'<br> Path = '\\ad.example.net\NETLOGON\ROOTCA01_My Root CA.crt'<br> }<br> }<br>}<br>
|
| |Intune|使用受信任的证书配置文件导入根证书|无代码示例,参考 Intune 文档操作|
|颁发签名证书|颁发新的签名证书|
powershell<br>$Params = {@<br> Template = 'MyCodeSigning'<br> CertStoreLocation = 'Cert:\CurrentUser\My'<br>}<br>Get-Certificate @Params<br>
|
8. 常见问题及解决方案
在脚本签名和 PKI 构建过程中,可能会遇到一些常见问题,以下是这些问题及对应的解决方案:
|问题|可能原因|解决方案|
| ---- | ---- | ---- |
|安装证书时提示“CRYPT_E_REVOCATION_OFFLINE”|颁发 CA 机器无法访问根 CA 配置中定义的 CDP,可能是防火墙问题、未添加 DNS 记录或未配置 Web 服务器|确保 CDP 对颁发 CA 和所有客户端可用,检查防火墙设置、添加 DNS 记录并配置 Web 服务器|
|
New-ADCSTemplate
未导入
msPKI-RA-Application-Policies
属性|该 cmdlet 在编写时存在此问题|手动添加该属性,示例代码如下:
powershell<br>$JSON = ConvertFrom-Json -InputObject $JSONRaw<br>$DN = (Get-ADCSTemplate -DisplayName 'My Code Signing').DistinguishedName<br>Set-ADObject -Identity $DN -Add @{<br> 'msPKI-RA-Application-Policies' = $JSON.'msPKI-RA-Application-Policies'<br>}<br>
|
|运行签名脚本时提示证书不受信任|证书不在“受信任的发布者”存储区或系统中未安装正确的根证书|将证书添加到“受信任的发布者”存储区,并确保根证书安装在“受信任的根证书颁发机构”存储区|
9. 最佳实践建议
为了确保脚本签名和 PKI 系统的安全性和可靠性,以下是一些最佳实践建议:
-
证书管理
:
- 定期检查证书的有效期,提前规划证书的续订,避免证书过期导致签名失效。
- 妥善保管根 CA 的私钥,建议使用智能卡或 USB HSM 存储,以提高安全性。
- 定期更新根 CA 的 CRL,并将其复制到 CDP 上,确保客户端能够及时获取最新的吊销信息。
-
模板使用
:
- 不要直接使用内置的证书模板,而是创建自定义模板,以便根据实际需求进行配置和调整。
- 定期审查和更新证书模板的设置,确保其符合最新的安全要求。
-
系统配置
:
- 对根 CA 和颁发 CA 进行严格的访问控制,仅授权必要的人员进行操作。
- 定期对 PKI 系统进行安全审计,检查是否存在异常活动和潜在的安全漏洞。
10. 总结
脚本签名是保障代码完整性和安全性的重要手段,通过本文的介绍,我们详细了解了从获取代码签名证书到验证签名的整个过程,以及如何构建自己的 PKI 系统来支持脚本签名的扩展部署。
在实际应用中,我们需要根据具体的需求和环境选择合适的方法和工具,遵循最佳实践建议,确保脚本签名和 PKI 系统的安全性和可靠性。同时,要注意及时处理遇到的问题,不断优化和完善系统配置,以适应不断变化的安全需求。
希望本文能够帮助你更好地理解和应用脚本签名技术,为你的代码安全保驾护航。
以下是 PKI 构建过程的 mermaid 流程图:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始]):::startend --> B(根 CA 配置):::process
B --> C(颁发 CA 配置):::process
C --> D(创建签名模板):::process
D --> E(根证书部署):::process
E --> F(颁发签名证书):::process
F --> G([结束]):::startend
超级会员免费看
11

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



