Как подписать файл PowerShell скрипта (ps1) с помощью сертификата?

Как подписать файл PowerShell скрипта (ps1) с помощью сертификата?

Наличие цифровой подписи у скрипта или исполняемого файла позволяет пользователю удостовериться, что файл является оригинальным и его код не был изменен третьими лицами. В современных версиях PowerShell есть встроенные средства для подписывания кода файла скриптов *.ps1 с помощью цифровых сертификатов.

Для подписывания скриптов PowerShell нужно использовать специальный сертификат типа Code Signing. Этот сертификат может быть получен от внешнего коммерческого центра сертификации, внутреннего корпоративного Certificate Authority (CA) или можно даже самоподписанный сертификат.

Предположим, у нас в домене развернуты службы PKI — Active Directory Certificate Services. Запросите новый сертификат, перейдя на страницу
https://CA-server-name/certsrv
. Нужно запросить новый сертификат с шаблоном Code Signing (данный шаблон должен быть предварительно разрешен в консоли Certification Authority).

Шаблон сертификата code signing

Также пользователь может самостоятельно запросить сертификат для подписи PowerShell скриптов из
mmc
оснастки Certificates -> My user account -> Personal -> All task -> Request New Certificate.

консоль certificates запросить новый сертификат

Если вы запросили сертификат вручную, у вас должен получится файл сертификат x509 в виде файла с расширением .cer. Данный сертификат нужно установить в локальное хранилище сертификатов вашего компьютера.

Для добавления сертификата в доверенные корневые сертификаты компьютера можно использовать следующие команды PowerShell:

$certFile = Export-Certificate -Cert $cert -FilePath C:pscertname.cer
Import-Certificate -CertStoreLocation Cert:LocalMachineAuthRoot -FilePath $certFile.FullName

Если вы хотите использовать самоподписанный сертификат, то вы можете использовать командлета New-SelfSignedCertificate чтобы создать сертификат типа CodeSigning c DNS именем test1:

New-SelfSignedCertificate -DnsName test1 -Type CodeSigning
$cert = New-SelfSignedCertificate -Subject "Cert for Code Signing” -Type CodeSigningCert -DnsName test1 -CertStoreLocation cert:LocalMachineMy

После генерации сертификата, его нужно будет в консоли управления хранилищем сертификатов (
certmgr.msc
) перенести из контейнера Intermediate в Trusted Root.

После того, как сертификат получен, можно настроить политику исполнения скриптов PowerShell, разрешив запуск только подписанных скриптов. По умолчанию PowerShell Execution политика в Windows 10/Windows Server 2016 установлена в значение Restricted. Это режим блокирует запуск любых PowerShell скриптов:

File C:pstest_script.ps1 cannot be loaded because running scripts is disabled on this system.

Чтобы разрешить запуск только подписанных PS1 скриптов, можно изменить настройку политики исполнения скриптов на AllSigned или RemoteSigned (разница между ними в том, что RemoteSigned требует наличие подписи только для скриптов, полученных из интернета):

Set-ExecutionPolicy AllSigned –Force

В этом режиме при запуске неподписанных PowerShell скриптов появляется ошибка:

File C:pstest_script.ps1 cannot be loaded. The file .ps1 is not digitally signed. You cannot run this script on the current system.
Разрешить выполнение подписанных скриптов PowerShell также можно с помощью параметра групповых политик Включить выполнение сценариев (Turn on Script Execution) в разделе GPO Computer Configuration -> Policies -> Administrative Templates -> Windows Components -> Windows PowerShell. Измените значение параметра на ”
Разрешать только подписанные сценарии
” (Allow only signed scripts).

Теперь перейдем к подписыванию файла со скриптом PowerShell. В первую очередь вам нужно получить сертификат типа CodeSign из локального хранилища сертификатов текущего пользователя. Сначала выведем список всех сертификатов, которые можно использовать для подписывания кода:

Get-ChildItem cert:CurrentUsermy –CodeSigningCert

В нашем случае мы возьмем первый сертификат и сохраним его в переменную $cert.

$cert = (Get-ChildItem cert:CurrentUsermy –CodeSigningCert)[0]

Затем можно использовать данный сертификат, чтобы подписать файл PS1 с вашим скриптом PowerShell:

Set-AuthenticodeSignature -Certificate $cert -FilePath C:PStest_script.ps1

Также можно использовать такую команду (в данном случае мы вибираем самоподписанный сертификат созданный ранее по DnsName):

Set-AuthenticodeSignature C:PStest_script.ps1 @(gci Cert:LocalMachineMy -DnsName test1 -codesigning)[0]

Совет. У командлета Set-AuthenticodeSignature есть специальный параметр TimestampServer, в котором указывается URL адрес Timestamp службы. Если этот параметр оставить пустым, то PS скрипт перестанет запускаться после истечения срока действия сертификата. Например
-TimestampServer "http://timestamp.verisign.com/scripts/timstamp.dll"
.

Если вы попытаетесь использовать обычный сертификат для подписывания скрипта, появится ошибка:

Set-AuthenticodeSignature : Cannot sign code. The specified certificate is not suitable for code signing.
Можно подписать сразу все файлы PowerShell скриптов в папке:

Get-ChildItem c:ps*.ps1| Set-AuthenticodeSignature -Certificate $Cert

Теперь можно проверить, что скрипт подписан. Можно использовать командлет Get-AuthenticodeSignature или открыть свойства PS1 файла и перейдти на вкладку Digital Signatures.

Get-AuthenticodeSignature c:pstest_script.ps1 | ft -AutoSize

powershell скрипт с цифровой подписью

Если при выполнении команды Set-AuthenticodeSignature появится предупреждение UnknownError, значит этот сертификат недоверенный, т.к. находится в персональном хранилище сертификатов пользователя.

Set-AuthenticodeSignature UnknownError

Нужно переместить его в корневые сертификаты (не забывайте периодически проверять хранилище сертификатов Windows на наличие недоверенных сертфикатов и обновлять списки корневых сертификатов):

Move-Item -Path $cert.PSPath -Destination "Cert:LocalMachineRoot"

Теперь при проверке подписи PS1 файла должен возвращаться статус Valid.

используем командлет Set-AuthenticodeSignature чтобы подписать файл с powershell скриптом

При подписывании файла PowerShell скрипта, командлет Set-AuthenticodeSignature добавляет в конец текстового файла PS1 блок сигнатуры цифровой подписи, обрамленный специальными метками:

# SIG # Begin signature block
...........
...........
# SIG # End signature block

Блок сигнатуры содержит хэш скрипта, который зашифрован с помощью закрытого ключа.

PS1 файл с блоком цифровой подписи # SIG # Begin signature block

При первой попытке запустить скрипт появится предупреждение:

Do you want to run software from this untrusted publisher?
File C:PStest_script.ps1 is published by CN=test1 and is not trusted on your system. Only run scripts from trusted  publishers.

Если выбрать [A] Always run, то при запуске любых PowerShell скриптов, подписанных этим сертификатом, предупреждение появляться больше не будет.

ps1 is published by CN=test1 and is not trusted on your system. Only run scripts from trusted publishers

Чтобы это предупреждения не появлялось нужно скопировать сертификат также в раздел Trusted Publishers. С помощью обычной операции Copy-Paste в консоли Certificates скопируйте сертификат в раздел Trusted Publishers -> Certificates.

скопировать сертфика codesigning в Trusted Publishers

Теперь подписанный PowerShell скрипт будет запускаться без уведомления об untrusted publisher.

Совет. Корневой сертификат CA и сертификат, которым подписан скрипт, должен быть доверенным (иначе скрипт вообще не запустится). Вы можете централизованно установить сертификаты на все компьютеры домена с помощью GPO. Сертфикаты нужно поместить в следующие разделы GPO:

Computer Configuration -> Policies -> Windows Settings -> Security Settings -> Public Key Policies -> Trusted Root Certification Authorities и Trusted Publishers.

Если корневой сертификат недоверенный, то при запуске скрипта PowerShell будет появляться ошибка:

A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.

Что произойдет, если изменить код подписанного файла со скриптом PowerShell? Его запуск будет заблокирован, с ошибкой, что содержимое скрипта было изменено:

C:PStest_script.ps1 : File C:PStest_script.ps1 cannot be loaded. The contents of file C:PStest_script.ps1 might  have been changed by an unauthorized user or process, because the hash of the file does not match the hash stored in the digital signature. The script cannot run on the specified system.

ошибка при запуске модифицированного powershell скрипта The contents of file have been changed

Попробуйте проверить цифровую подпись скрипта с помощью командлета
Get-AuthenticodeSignature
. Если хэш не совпадает с хэшем в подписи, появится сообщение HashMismatch.

Get-AuthenticodeSignature ошибка HashMismatch

Таким образом, после любой модификации кода подписанного PS1 скрипта его нужно заново переподписать.

Windows 10
Как подписать файл PowerShell скрипта (ps1) с помощью сертификата?