By reading the article subject first question rose to the mind why you should sign PowerShell scripts? The answer is whenever we download powershell script or we receive it from another users, The digital signature allows the user to confirm the validity of the certificate used to sign the script. It also allows the user to ensure that the script hasn't been tampered with since it was signed. Also you can curb the uses of malicious or foreign scripts which are not validated by your company, It is a best practice all script should be. This article is a next part of Creating an internal PowerShell module repository, using the same commands modules can also be digitally signed. Here in this article I am using Self-Signed SSL certificate here for lab scripts.
Line No. 1: The first command I run is to configuring PowerShell execution policy for scripts, without correct policy, it will not verify the digital signature and still execute script even if script is modified, below are the policies which you need to set, By default RemoteSigned configured on Windows 10/ windows 2016 servers and above. I changed it and set it to AllSigned so all the .ps1 scripts are verified whether script source is authenticate.
- AllSigned: All script which you run are require to be digitally signed.
- RemoteSigned: All remote scripts (UNC) or downloaded need to be signed.
To set execution policy for a scope you can check article Powershell execution policy setting is overridden by a policy defined at a more specific scope, you can also use Group Policy to configure execution policy for your infrastructure.
- MachinePolicy: The execution policy set by a Group Policy for all users.
- UserPolicy: The execution policy set by a Group Policy for the current user.
- Process: The execution policy that is set for the current Windows PowerShell process.
- CurrentUser: The execution policy that is set for the current user.
- LocalMachine: The execution policy that is set for all users.
You can download complete script here, it is also available github.com/kunaludapi.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
Set-ExecutionPolicy AllSigned -Force #Configure script execution policy to all script must be signed $scriptPath = '\\192.168.34.16\RemoteScripts\NewScript.ps1' #This is share path, Where all scripts will be hosted $certStoreLocation = 'Cert:\CurrentUser\My' #This is local certification store $certificateName = '\\192.168.34.16\RemoteScripts\PSCodeCertifiate.cer' #This is certificate to give to users #Create a code-signing, self-signed certificate $selfSignedCertInfo = @{ Subject = 'vCloud-lab.com Code Signing' Type = 'CodeSigning' CertStoreLocation = $certStoreLocation } $cert = New-SelfSignedCertificate @selfSignedCertInfo #View the newly created certificate Get-ChildItem -Path $certStoreLocation -CodeSigningCert | Where-Object {$_.SubjectName.Name -Match $_.$selfSignedCertInfo.Subject} #Create a simple script $scriptCode = @" #Demo Script for Testing Write-Host "ComputerName: $env:COMPUTERNAME" -BackgroundColor Green ipconfig "@ $scriptCode | Out-File -FilePath $scriptPath #View the files Get-ChildItem -Path $scriptPath #Sign the Script $codeSignInfo = @{ Certificate = $Cert FilePath = $scriptPath } Set-AuthenticodeSignature @codeSignInfo #View the files Get-ChildItem -Path $scriptPath #Test the signature Get-AuthenticodeSignature -FilePath $scriptPath | Format-List * #Export certificate to file on sharepath Export-Certificate -Cert $cert -FilePath $certificateName #Import it to users trusted root certificate autorities Import-Certificate -FilePath $certificateName -CertStoreLocation 'Cert:\CurrentUser\Root' -Confirm:$false #Import certificate to Trusted publisher store location Import-Certificate -FilePath $certificateName -CertStoreLocation 'Cert:\CurrentUser\TrustedPublisher' -Confirm:$false #Re-sign with a trusted certificate Set-AuthenticodeSignature @codeSignInfo #Check the script's signature Get-AuthenticodeSignature -FilePath $scriptPath | Format-List |
Line No 03 to 05: Configure the variables, for Script file path (Share path), Certificate store location 'Cert:\CurrentUser\My' and Certificate name to export, All the files are store on shared path.
Line No 08 to 13: To sign a ps1 scripts, there is a special code-signing certificate is required, cert type (Enhanced key usage) must be CodeSinging. This command creates self-signed certificate to given certificate store location, CurrentUsers >> Personal, which is already defined in the variable on the line no 4. Since your can easily access your own CurrentUsers cert store no special privileges are required.
Line No 16: Verify self -signed certificate is created and its matching subject line.
Interesting Artcle
Generate new self-signed certificates for ESXi using OpenSSL
Push SSL certificates to client computers using Group Policy
Line No 19 to 24: This is a demo script I am creating, If you already have script these steps are not required. I will create a quick and small .ps1 script file with the few PowerShell cmdlets, on this created new file I can assign digital certificate key.
Line No 27: Verify the script file and list the file to check the length / size of file also Open it in notepad and check the content of Powershell script file.
Line no 30 to 34: I am using earlier created self-signed ssl certificate to apply code digital signature, This Adds an Authenticode (/windows-hardware/drivers/install/authenticode) signature to a PowerShell script or other file. New digital code is applied, but status shows UnknownError.
Line no 37: List the file again with dir command and check the length/ Size of file is increased, open the file in notepad digital signature is added to code in the bottom.
Line no 40: When you check complete information of assigned authenticode, verify certificate thumbprint. It also reveals the error status message. 'A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider'.
If I try to execute the script, it shows me the same error 'A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider'. In the next commands I will resolve this issue.
Line no 43: The Export-Certificate cmdlet exports a certificate from a certificate store to a file. All the variable are already mentioned on the top of the few lines, they will be used as parameters.
Line no 46: The Import-Certificate cmdlet imports one or more certificates into a certificate store. I am importing it to CurrentUser\Trusted root certificate authority certificate location. This popups security warning as below. Make sure sha1 thumbprint and subject is the matching, press Yes to continue.
You are about to install a certificate from a certification authority (CA) claiming to represent: vCloud-lab.com Code Signing Windows cannot validate that the certificate is actually from "vCloud-lab.com Code Signing". You confirm its origin by contacting "vCloud-lab.com Code Signing". The following number will assist you in this process. Thumbprint (sha1): 8043C9C1 73EE1222 0A5C08CA FB056C13 DCC18AEF Warning: If you install this root certificate, Windows will automatically trust any certificate issued by this CA. Installing a certificate with an unconfirmed thumbprint is a security risk. if you click "Yes" you acknowledge this risk. Do you want to install this certificate?
Line no 49: Import the same certificate to one more cert store location to CurrentUsers's Trusted Publisher. You can verify the same on MMC\Certificates GUI.
Verify the script again by executing, this time it will be successful. You can re-verify script signature again buy using Get-AuthenticodeSignature, it will show the digital signature status is valid now.
The next thing I will do is open script ps1 in notepad and modify the piece of code, and try executing the script. This time it shows some other error "ps1 file cannot be loaded. The contents of file 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. for more information, run Get-Help about_Signing."
If I check AuthenticodeSignature, it shows the status is HashMisMatch, It can be corrected again by running Line no 30 to 34.
Here instead of using self-signed digital SSL certificates, you can use and create one from Certificate Authority server, for this I have already written few articles earlier.
How to replace default vCenter VMCA certificate with Microsoft CA signed certificate
Replacing a default ESXi certificate with a CA-Signed certificate
Push SSL certificates to client computers using Group Policy
There are Certificate authority and Certificate Authority Web Enrollment features are used, Few changes you will need to make, use Code Signing Certificate template for cloning from CA. Using Group Policy to publish certificate use Trusted Publisher location as well.
Useful Articles
GUI - SETUP AND CONFIGURE POWERSHELL WEB ACCESS SERVER (GATEWAY)
USE POWERSHELL ON MOBILE - SETUP AND CONFIGURE POWERSHELL WEB ACCESS (PSWA)
Different ways to bypass Powershell execution policy :.ps1 cannot be loaded because running scripts is disabled
Powershell Trick : Execute or run any file as a script file
Set Powershell execution policy with Group Policy
Powershell execution policy setting is overridden by a policy defined at a more specific scope