如何签署 PowerShell 脚本(并有效地运行它)

你是否需要确保没有人修改您的脚本并将其传递为原始脚本?如果是这样,那么您需要学会如何签署 PowerShell 脚本。签署会将发布者的身份添加到脚本中,以便用户可以决定是否信任脚本的来源。

在本文中,了解如何通过学习如何签署 PowerShell 脚本来确保只有受信任的脚本在您的环境中运行。

先决条件

如果您要按照本文中的示例操作,您需要以下内容。

  • A computer running on a recent version of the Windows operating system. This article uses Windows 10 version 20H2.
  • Windows PowerShell 5.1 或 PowerShell 6+。本文中的示例将使用PowerShell v7.1.3
  • A sample PowerShell script for signing. Feel free to create a script with any name and in any folder you want. This article will use a sample script called C:\ATA\myscript.ps1 that contains the code below.
Write-Host "Script Execution - OK"

获取代码签名证书

在学习如何签署 PowerShell 脚本之前,您首先需要获取代码签名证书。在 Microsoft 的世界中,代码签名证书也被称为Authenticode证书。

A code signing certificate is one type of digital certificate whose purpose for signing files. Signing a file or code with a code signing certificate adds proof that the file came from the publisher who signed it.

您从哪里获取代码签名证书取决于您打算部署或分发已签名脚本的位置。而且,成本也一如既往地是一个重要因素。

  • 全球 / 公共 – 您需要一张由全球信任的证书颁发机构(CA)颁发的证书。此类CA的示例包括GeoTrustDigiCert。这些证书并非免费。例如,截至本文撰写时,DigiCert Authenticode证书的年费为474美元。
  • 内部 / 本地内部网 – 如果您拥有内部证书颁发机构(CA)服务器,您可以从内部CA服务器请求并下载签名证书
  • 个人 / 开发 – 对于个人测试或开发用途,自签名证书就足够了。本文中将使用这种类型的签名证书。

为代码签名创建自签名证书

在前一节中,您已经了解到在学习如何签署PowerShell脚本时,首先需要一张代码签名证书。由于本教程中您只会进行个人测试,因此自签名证书就足够了。那么,您应该从哪里获取它呢?

正如其名称所示,自签名意味着您的本地计算机将向自己颁发代码签名证书。要生成自签名证书,请按照以下步骤操作。

1. 在您的计算机上以管理员身份打开 PowerShell。

2. 复制下面的命令并在 PowerShell 中运行。此命令使用 New-SelfSignedCertificate cmdlet 来创建一个新的代码签名证书。证书的名称是 ATA Authenticode,存储在本地计算机的个人证书存储区中。

New-SelfSignedCertificate cmdlet 仅支持在当前用户的个人证书存储区(cert:\CurrentUser\My)或本地计算机的个人证书存储区(cert:\LocalMachine\My)中创建证书。cert:\LocalMachine\My 中的证书可在整个计算机上使用。

该命令还将证书对象存储到 $authenticode 变量中,以供下一步使用。

# 在本地计算机的个人证书存储区生成一个自签名的 Authenticode 证书。
 $authenticode = New-SelfSignedCertificate -Subject "ATA Authenticode" -CertStoreLocation Cert:\LocalMachine\My -Type CodeSigningCert

3. 接下来,为了使您的计算机信任您创建的新证书,将自签名证书添加到计算机的 受信任的根证书颁发机构受信任的发布者 证书存储区。为此,请复制下面的代码并在 PowerShell 中运行。

# 将自签名的 Authenticode 证书添加到计算机的根证书存储。
## 创建一个对象来表示 LocalMachine\Root 证书存储。
 $rootStore = [System.Security.Cryptography.X509Certificates.X509Store]::new("Root","LocalMachine")
## 以读写方式打开根证书存储。
 $rootStore.Open("ReadWrite")
## 添加存储在 $authenticode 变量中的证书。
 $rootStore.Add($authenticode)
## 关闭根证书存储。
 $rootStore.Close()
 
# 将自签名的 Authenticode 证书添加到计算机的受信任发布者证书存储。
## 创建一个对象来表示 LocalMachine\TrustedPublisher 证书存储。
 $publisherStore = [System.Security.Cryptography.X509Certificates.X509Store]::new("TrustedPublisher","LocalMachine")
## 以读写方式打开受信任发布者证书存储。
 $publisherStore.Open("ReadWrite")
## 添加存储在 $authenticode 变量中的证书。
 $publisherStore.Add($authenticode)
## 关闭受信任发布者证书存储。
 $publisherStore.Close()

将自签名证书安装到三个不同的证书存储中有三个主要原因。

  • 个人证书存储中创建的证书将用作代码签名证书。
  • 将相同的证书复制到受信任的发布者存储确保本地计算机信任签署脚本的发布者。PowerShell 在此存储中检查证书以验证脚本的签名。
  • 最后,将自签名证书添加到受信任的根证书颁发机构,确保您的本地计算机信任个人受信任的发布商存储中的证书。

4. 确认具有主题ATA Authenticode的证书是否位于个人受信任的发布商证书存储中,请在PowerShell中运行以下命令。

# 确认计算机的个人证书存储中是否存在自签名Authenticode证书
 Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -eq "CN=ATA Authenticode"}
# 确认计算机的根证书存储中是否存在自签名Authenticode证书
 Get-ChildItem Cert:\LocalMachine\Root | Where-Object {$_.Subject -eq "CN=ATA Authenticode"}
# 确认计算机的受信任的发布商证书存储中是否存在自签名Authenticode证书
 Get-ChildItem Cert:\LocalMachine\TrustedPublisher | Where-Object {$_.Subject -eq "CN=ATA Authenticode"}
Confirming the creation of the new self-signed certificate

5. 要以图形界面查看证书打开证书管理器,并在个人受信任的根证书颁发机构受信任的发布商证书存储中查找您创建的证书。

Viewing certificates in the Microsoft Management Console (MMC)

如何签署 PowerShell 脚本

现在,您已经创建并安装了代码签名证书到三个证书存储库中,您可以开始使用它来签署您的示例 PowerShell 脚本。当您需要签署脚本时,Set-AuthenticodeSignature cmdlet 是主角。

要签署 PowerShell 脚本,请在 PowerShell 中运行以下代码。第一条命令从本地计算机的个人证书存储库获取代码签名证书。第二条命令向 PowerShell 脚本文件添加数字签名。

# 从本地计算机的证书存储库中获取名称为 *ATA Authenticode* 的代码签名证书,并将其存储到 $codeCertificate 变量中。
$codeCertificate = Get-ChildItem Cert:\LocalMachine\My | Where-Object {$_.Subject -eq "CN=ATA Authenticode"}

# 签署 PowerShell 脚本
# 参数:
# FilePath - 指定要签署的 PowerShell 脚本的文件路径,例如 C:\ATA\myscript.ps1。
# Certificate - 指定签署脚本时要使用的证书。
# TimeStampServer - 指定可信的时间戳服务器,该服务器会向您的脚本数字签名添加时间戳。添加时间戳可确保您的代码在签名证书过期时不会过期。
Set-AuthenticodeSignature -FilePath C:\ATA\myscript.ps1 -Certificate $codeCertificate -TimeStampServer *<http://timestamp.digicert.com>*

大多数值得信赖的证书提供商都有一个时间戳服务器,您可以在这些提供商的网站上找到。例如,DigiCert的时间戳服务器是 http://timestamp.digicert.com,而Comodo的时间戳服务器是 http://timestamp.comodoca.com。

在签署脚本之后,您应该看到类似下面截图的输出。

How to Sign PowerShell Scripts

检查 PowerShell 脚本的数字签名

到目前为止,您已经使用您创建的自签名证书对 PowerShell 脚本进行了签名。但是,您如何知道脚本是否确实具有数字签名呢?

打开代码

确认脚本的数字签名的一种方法是打开脚本并在文本编辑器中查看代码。就像下面的示例一样,已签名的脚本在代码末尾有一个签名块。签名块以# SIG # Begin signature block开头,以# SIG # End signature block结尾。

Viewing the digital signature in the script’s content

从脚本代码中删除数字签名块将使脚本恢复为非签名状态。

打开脚本的文件属性

检查脚本的数字签名的另一种方法是在Windows资源管理器中打开脚本的文件属性。操作步骤如下:

  1. 在Windows资源管理器中,导航到PowerShell脚本的位置。在这个示例中,脚本位于C:\ATA\myscript.ps1
  2. 右键单击脚本,然后点击属性
  3. 在文件属性窗口中,点击数字签名选项卡,您应该在签名列表下看到一个数字签名。
Viewing the digital signature in the script’s file properties

使用Get-AuthenticodeSignature

您会惊讶地发现您也可以在PowerShell中检查脚本的签名吗?可能不会。您可以调用的cmdlet以检索文件签名的是Get-AuthenticodeSignature

要获取脚本的数字签名,请运行以下命令。此命令获取C:\ATA\myscript.ps1文件的签名。Select-Object -Property * cmdlet显示签名的所有详细信息。

Get-AuthenticodeSignature -FilePath C:\\ATA\\myscript.ps1 | Select-Object -Property *

运行命令后,您应该看到类似下面截图的结果。正如您所见,SignerCertificate属性显示签名证书的详细信息。而TimerStamperCertificate属性显示时间戳服务器的证书。

Viewing the digital signature in PowerShell

运行已签名的PowerShell脚本

到此为止,您已签署了一个PowerShell脚本并确认数字签名存在。但是,确认您是否已正确执行所有步骤的最终测试是执行脚本并确认其是否运行。

PowerShell具有一项安全功能,可防止用户意外运行脚本。这个安全功能称为执行策略根据执行策略的不同,PowerShell可能会阻止或允许脚本运行。

要了解不同的执行策略以及它们对脚本执行的影响,请参阅PowerShell执行策略:理解和管理

要运行已签名的PowerShell脚本,请按照以下步骤操作。

首先,将执行策略更改为AllSigned以确保只有已签名的脚本才能运行。如果不执行此步骤,则无法准确测试已签名脚本的运行情况。为此,请在PowerShell中以管理员身份运行以下命令,调用Set-ExecutionPolicy cmdlet。

Set-ExecutionPolicy AllSigned

接下来,执行已签名的PowerShell脚本。

C:\ATA\myscript.ps1

脚本应该会运行,而且没有错误或警告,正如下面的结果所示。

Running the signed script without errors

但是,如果脚本没有正确签名或者根本没有签名,你将会收到类似下面图片所示的错误。在这种情况下,请重新检查你的步骤,并尝试重新签名脚本。

Running a script with errors regarding the digital signature

如果你最终更新了你的脚本会怎样?数字签名还会有效吗?答案是否定的。对已签名脚本的任何修改都会使脚本的数字签名失效。运行修改后的脚本将会失败并显示错误。

按照以下步骤测试修改后的已签名脚本。

1. 在代码或文本编辑器中打开已签名的 myscript.ps1 脚本。

2. 修改代码以添加一个字符,例如在此示例中添加一个下划线。不要改变其他任何内容。

Editing the signed script

3. 修改代码后保存脚本。

4. 最后,通过运行以下命令在 PowerShell 中执行修改后的脚本。

C:\ATA\myscript.ps1

由于你修改了已签名的脚本,执行脚本将导致下面显示的错误。你需要重新签名脚本以更新和修复其数字签名。

Running a signed script with a broken digital signature

A digital signature does not guarantee that nobody modified the script from its original version. Any PowerShell script with malicious code may be digitally signed, too. Always practice caution when running scripts from sources you do not fully trust.

结论

通过本文,你了解了为什么签名 PowerShell 脚本可能是必要的,这取决于执行策略。你还学会了如何区分已签名和未签名的脚本。最后,你学会了如何对 PowerShell 脚本进行数字签名以及如何测试和运行它们。

现在你知道如何签署 PowerShell 脚本了,你会在分发或部署之前开始签署脚本吗?

Source:
https://adamtheautomator.com/how-to-sign-powershell-script/