Creating Self-Signing Certificates and assigning to Powershell scripts

If we take the assumption that you’ve just started out with powershell, you have probably seen this error when attempting to run a script:

File C:\scripts\test.ps1 cannot be loaded because the execution of scripts is 
disabled on this system. Please see "get-help about_signing" for more details.

Probably after some research and looking into about_signing you may have run the following in your Powershell editor:

Get-ExecutionPolicy

and found that your machine was set to be restricted. This policy prevents any scripts from being run in your environment. The next thing you probably did was set your execution policy to be Unrestricted, which then allows any scripts to be run without any security checks.

This would have solved your immediate problem of getting the script to run, however, to create more control over what can run and what cant, you can introduce certificates.

Self-Sign Certificates

To create Self-Signed ceriticates, the makecert.exe tool needs to be installed. This ships as part of the .NET Framework SDK (1.1 or higher) – i have 2.0 installed (found here)

To make the certificate:

C:\Program Files\Microsoft.NET\SDK\v2.0 64bit\Bin>makecert -n "CN=PowerShell Local Certificate Root" -a sha1 -eku 1.3.6.1.5.5.7.3.3 -r -sv root.pvk root.cer -ss Root -sr localMachine

Succeeded

C:\Program Files\Microsoft.NET\SDK\v2.0 64bit\Bin>makecert -pe -n "CN=PowerShell User" -ss MY -a sha1 -eku 1.3.6.1.5.5.7.3.3 -iv root.pvk -ic root.cer

Succeeded

After running these commands you will be prompted to provide passwords.  There are a number of ways to determine if the cert was created correctly including looking in the Certificated snap-in MMC (mmc.exe | add snap-in | certificates)

image

or by running the following PowerShell command (I have narrowed down gci to just return where the subject is Powershell as i have several different ceritifcates):

Get-ChildItem cert:\CurrentUser\My | Where-Object {$_.subject -match "Powershell"}

Directory: Microsoft.PowerShell.Security\Certificate::CurrentUser\My

Thumbprint                                Subject

———-                                ——————-

52F872D96D6A507846875CE83317C99F53E5C48F  CN=PowerShell User 

Sign the script

The last step is to sign the script using the newly created certificate:

$cert = @(Get-ChildItem cert:\CurrentUser\My -codesigning `
  | Where-Object {$_.subject -match "Powershell User"})[0]

  Set-AuthenticodeSignature "\\Path\myscript_New.ps1" $cert

SignerCertificate                         Status       Path

—————–                         ——       ——–

52F872D96D6A507846875CE83317C99F53E5C48F  Valid        myscript_New.ps1

Troubleshooting

If your having issues with signing the script then see this – UnknownError when using Powershell ISE to Set-AuthenticodeSignature

Advertisements

UnknownError when using Powershell ISE to Set-AuthenticodeSignature

This is discussed on Microsoft Connect here

When attempting to assign a certificate to a powershell script, it does not assign and gives a status of ‘UnknownError’

$cert = @(Get-ChildItem cert:\CurrentUser\My -codesigning | Where-Object {$_.subject -match "Powershell User"})[0]
Set-AuthenticodeSignature "\\path\myscript.ps1" $cert

SignerCertificate  Status          Path 
—————–  ——          —- 
                   UnknownError    myscript.ps1

This happens because when using Powershell ISE the default encoding is ‘Unicode Big Endian’

If the file is recreated using UTF-8 then the script is correctly assigned a certificate

#recreate the script into a new file
type "\\Path\myscript.ps1" | out-file "\\Path\myscript_New.ps1" -encoding utf8
$cert = @(Get-ChildItem cert:\CurrentUser\My -codesigning `
   | Where-Object {$_.subject -match "Powershell User"})[0]
#use the UTF-8 encoded file
Set-AuthenticodeSignature "\\Path\myscript_New.ps1" $cert

SignerCertificate  Status          Path 
—————–  ——          —-  
Cert_Thumbprint    Valid           myscript_New.ps1