Windows OS Hub
  • Windows Server
    • Windows Server 2022
    • Windows Server 2019
    • Windows Server 2016
    • Windows Server 2012 R2
    • Windows Server 2008 R2
    • SCCM
  • Active Directory
    • Active Directory Domain Services (AD DS)
    • Group Policies
  • Windows Clients
    • Windows 11
    • Windows 10
    • Windows 8
    • Windows 7
    • Windows XP
    • MS Office
    • Outlook
  • Virtualization
    • VMWare
    • Hyper-V
    • KVM
  • PowerShell
  • Exchange
  • Cloud
    • Azure
    • Microsoft 365
    • Office 365
  • Linux
    • CentOS
    • RHEL
    • Ubuntu
  • Home
  • About

Windows OS Hub

  • Windows Server
    • Windows Server 2022
    • Windows Server 2019
    • Windows Server 2016
    • Windows Server 2012 R2
    • Windows Server 2008 R2
    • SCCM
  • Active Directory
    • Active Directory Domain Services (AD DS)
    • Group Policies
  • Windows Clients
    • Windows 11
    • Windows 10
    • Windows 8
    • Windows 7
    • Windows XP
    • MS Office
    • Outlook
  • Virtualization
    • VMWare
    • Hyper-V
    • KVM
  • PowerShell
  • Exchange
  • Cloud
    • Azure
    • Microsoft 365
    • Office 365
  • Linux
    • CentOS
    • RHEL
    • Ubuntu

 Windows OS Hub / PowerShell / How to Sign a PowerShell Script (PS1) with a Code Signing Certificate?

February 25, 2021 PowerShellWindows 10Windows Server 2016

How to Sign a PowerShell Script (PS1) with a Code Signing Certificate?

A script or an executable with a digital signature allows a user to make sure that a file is original and its code has not been changed by third parties. Current versions of PowerShell have built-in tools for code signing of *.ps1 script files using digital certificates.

You can sign a PowerShell script using a special type of certificate – Code Signing. This certificate can be obtained from an external commercial certification authority (AC), an internal enterprise CA or you can use a self-signed certificate.

Suppose, PKI services (Active Directory Certificate Services) are deployed in your domain. Let’s request a new certificate by going to https://CA-server-name/certsrv and requesting a new certificate with the Code Signing template (this template must first be enabled in Certification Authority console).

enterprise ca - create a cert from code signing template

Also, the user can request a certificate for signing PowerShell scripts from the mmc snap-in Certificates -> My Account -> Personal -> All Tasks -> Request a new certificate.

Request a new certificate code signing certificate on windows 10

If you manually requested a certificate, you should have an x509 certificate file with a .cer extension. This certificate must be installed in the local certificate store of your computer.

You can use the following PowerShell commands to add the certificate to the trusted root certificates of the computer:

$certFile = Export-Certificate -Cert $cert -FilePath C:\ps\certname.cer
Import-Certificate -CertStoreLocation Cert:\LocalMachine\AuthRoot -FilePath $certFile.FullName

If you want to use a self-signed certificate, use the New-SelfSignedCertificate cmdlet to create a CodeSigning certificate with the DNS name testPC1:

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

After the certificate has been generated, move it from Intermediate container to Trusted Root using Certificate Manager console (certmgr.msc).

After you got the certificate, you can configure the PowerShell Script Execution Policy to allow only signed scripts to run. By default, the PowerShell Execution Policy on Windows 10/ Windows Server 2016 is set to Restricted (blocks execution of any PowerShell scripts).

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

To allow only signed PS1 scripts to run, you can change the PowerShell Eecution Policy to AllSigned or RemoteSigned (with the only difference that RemoteSigned requires a signature only for the scripts downloaded from the Internet):

Set-ExecutionPolicy AllSigned –Force

In this mode, when running unsigned PowerShell scripts, an error appears:

File C:\script.ps1 cannot be loaded. The file script.ps1 is not digitally signed. You cannot run this script on the current system.
You can also can allow signed PowerShell scripts to run by using the Turn on Script Execution Group Policy parameter under Computer Configuration -> Policies -> Administrative Templates -> Windows Components -> Windows PowerShell. Change the parameter value to Allow only signed scripts.

Now let’s move on to signing the PowerShell script file. First of all, you need to get the CodeSign certificate from the current user’s local certificate store. First, let’s list all the certificates that can be used to sign code:

Get-ChildItem cert:\CurrentUser\my –CodeSigningCert

In our case, we will take the first certificate from personal user cert store and save it in the $cert variable:

$cert = (Get-ChildItem cert:\CurrentUser\my –CodeSigningCert)[0]

If you have moved your certificate to the trusted root certificate store, use the following command:

$cert = (Get-ChildItem Cert:\LocalMachine\AuthRoot –CodeSigningCert)[0]

You can then use this certificate to sign the PS1 file with your PowerShell script:

Set-AuthenticodeSignature -Certificate $cert -FilePath C:\PS\testscript.ps1

You can also use the following command (in this case, we select the self-signed certificate created earlier by DnsName):

Set-AuthenticodeSignature C:\PS\test_script.ps1 @(gci Cert:\LocalMachine\AuthRoot -DnsName testPC1 -codesigning)[0]

Hint. The Set-AuthenticodeSignature cmdlet has a special TimestampServer parameter that specifies the URL for the Timestamp of the service. If this parameter is left blank, the PS script will stop running after the certificate expires. For instance you can set a timestamp server as follows: -TimestampServer "http://timestamp.verisign.com/scripts/timstamp.dll"

If you try to use a common SSL/TLS certificate to sign the script, an error appears:

Set-AuthenticodeSignature: Cannot sign code. The specified certificate is not suitable for code signing.

You can sign all PowerShell script files at once in the folder:

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

Now you can check that the PowerShell script file is signed properly. You can use the Get-AuthenticodeSignature cmdlet or open the PS1 file properties and go to the Digital Signatures tab.

Get-AuthenticodeSignature c:\ps\test_script.ps1 | ft -AutoSize

Get-AuthenticodeSignature check signature of a signed powershell script

If an UnknownError warning appears while executing the Set-AuthenticodeSignature command, then this certificate is not trusted, because located in the user’s personal certificate store.

Set-AuthenticodeSignature UnknownError

You need to move it to the Trusted Root Certificates (do not forget to periodically check the Windows certificate store for suspicious certs and update trusted root certificates lists):

Move-Item -Path $cert.PSPath -Destination "Cert:\LocalMachine\Root"

Now when verifying the signature of a PS1 file, the Valid status should be returned.

powershell move certificate to trusted root

When signing a PowerShell script file, the Set-AuthenticodeSignature cmdlet adds a digital signature block to the end of the PS1 text file:

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

signature block in powershell script file

The signature block contains the hash of the script, which is encrypted using the private key.

The first time you try to run the script, a warning will appear:

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

If you select [A] Always run at the first run of the script, the next time you run the script, signed using this certificate, a warning will no longer appear.

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

To prevent this warning from appearing, you need to copy the certificate also to the Trusted Publishers certificate authority. Use the Copy-Paste operation in the Certificates console to copy the certificate to the Trusted Publishers -> Certificates.

copy code signing certificate to Trusted Publishers

The signed PowerShell script will now run without displaying untrusted publisher notification.

Tip. The CA root certificate and the certificate used to sign the script has to be trusted (otherwise, the script won’t run). You can centrally deploy certificates to domain computers using GPO. The certificates need to be placed in the following Public Key sections of GPO: Computer Configuration -> Policies -> Windows Settings -> Security Settings -> Public Key Policies -> Trusted Root Certification Authorities and Trusted Publishers.

If the root certificate is untrusted, then when you run the PowerShell script, an error will appear:

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

What will happen if you change the code of the signed PowerShell script file? The attempt of running it will be blocked with the notification that the contents of the script has been changed.

File xx.ps1 cannot be loaded. The contents of file xx.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.

The contents of powershell script file have been changed. the hash of the file does not match the hash stored in the digital signature

Try to verify the signature of the script using the Get-AuthenticodeSignature cmdlet. If the calculated hash doesn’t match the hash in the signature, the message HashMismatch appears.

Get-AuthenticodeSignature HashMismatch

Thus, any modification of the code of the signed PS1 script will require to re-signing it.

1 comment
4
Facebook Twitter Google + Pinterest
previous post
Change the Default Port Number (TCP/1433) for a MS SQL Server Instance
next post
Accessing USB Flash Drive from VMWare ESXi

Related Reading

Configuring Event Viewer Log Size on Windows

May 24, 2023

How to Detect Who Changed the File/Folder NTFS...

May 24, 2023

Enable Single Sign-On (SSO) Authentication on RDS Windows...

May 23, 2023

Allow Non-admin Users RDP Access to Windows Server

May 22, 2023

How to Create, Change, and Remove Local Users...

May 17, 2023

Categories

  • Active Directory
  • Group Policies
  • Exchange Server
  • Microsoft 365
  • Azure
  • Windows 11
  • Windows 10
  • Windows Server 2022
  • Windows Server 2019
  • Windows Server 2016
  • PowerShell
  • VMWare
  • Hyper-V
  • Linux
  • MS Office

Recent Posts

  • Configuring Event Viewer Log Size on Windows

    May 24, 2023
  • How to Detect Who Changed the File/Folder NTFS Permissions on Windows?

    May 24, 2023
  • Enable Single Sign-On (SSO) Authentication on RDS Windows Server

    May 23, 2023
  • Allow Non-admin Users RDP Access to Windows Server

    May 22, 2023
  • How to Create, Change, and Remove Local Users or Groups with PowerShell?

    May 17, 2023
  • Fix: BSOD Error 0x0000007B (INACCESSABLE_BOOT_DEVICE) on Windows

    May 16, 2023
  • View Success and Failed Local Logon Attempts on Windows

    May 2, 2023
  • Fix: “Something Went Wrong” Error When Installing Teams

    May 2, 2023
  • Querying Windows Event Logs with PowerShell

    May 2, 2023
  • Configure Windows LAPS (Local Administrator Passwords Solution) in AD

    April 25, 2023

Follow us

  • Facebook
  • Twitter
  • RSS
Popular Posts
  • Installing RSAT Administration Tools on Windows 10 and 11
  • Manage Windows Updates with PSWindowsUpdate PowerShell Module
  • Configuring Port Forwarding in Windows
  • Start Menu or Taskbar Search Not Working in Windows 10/11
  • Get-ADUser: Find Active Directory User Info with PowerShell
  • How to Hide Installed Programs in Windows 10 and 11?
  • Configuring SFTP (SSH FTP) Server on Windows
Footer Logo

@2014 - 2023 - Windows OS Hub. All about operating systems for sysadmins


Back To Top