I have a number of PowerShell files code signed during build.
I'm getting different signature status depending how I call Get-AuthenticodeSignature
:
PS> Get-ChildItem | ForEach { Get-AuthenticodeSignature $_.Name }
SignerCertificate Status Path
----------------- ------ ----
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid CleanUpdateParameters.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid CreateApplication.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid DeleteApplication.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid ProvisionApplicationType.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid UnprovisionApplicationType.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid UpdateParameters.ps1
Everything looks good. But here's the results when I ry to validate the files using either binary content:
PS> Get-ChildItem | ForEach { Get-AuthenticodeSignature -Content ([System.IO.File]::ReadAllBytes($_.FullName)) -SourcePathOrExtension $_.Name }
SignerCertificate Status Path
----------------- ------ ----
NotSigned CleanUpdateParameters.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid CreateApplication.ps1
NotSigned DeleteApplication.ps1
NotSigned ProvisionApplicationType.ps1
NotSigned UnprovisionApplicationType.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid UpdateParameters.ps1
or string content:
PS> Get-ChildItem | ForEach { Get-AuthenticodeSignature -Content ([System.Text.Encoding]::ASCII.GetBytes([System.IO.File]::ReadAllText($_.FullName))) -SourcePathOrExtension $_.Name }
SignerCertificate Status Path
----------------- ------ ----
NotSigned CleanUpdateParameters.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid CreateApplication.ps1
NotSigned DeleteApplication.ps1
NotSigned ProvisionApplicationType.ps1
NotSigned UnprovisionApplicationType.ps1
E36170335E3DD78B6CDF1594B0D164F7C8A7B324 Valid UpdateParameters.ps1
I spent a week already on this and cannot wrap my head around what's causing this discrepancy/inconsistency. And I do have to make it working as my application ('client') receives the scripts from a service ('server') as a string and has to validate the validity of the content.
Update: as of pwsh 7.4.0, this bug has been fixed (fix PR here). However, many people will be using prior versions for a while, and 5.1 is stuck with this bug, so the workarounds below will remain valuable.
Sadly, Get-AuthenticodeSignature -Content
only recognizes byte arrays if they represent "Unicode" (UTF-16LE) encoded characters - any other encoding is misrepresented as NotSigned
in the output.
See this GitHub issue.
The implication of your symptoms is that only scripts CreateApplication.ps1
and UpdateParameters.ps1
are UTF-16LE-encoded.
If you wanted to use -Content
with all of your scripts:
([Text.Encoding]::Unicode.GetPreamble() +[Text.Encoding]::Unicode.GetBytes((Get-Content -Raw $_.FullName)))
to -Content
, i.e., manually convert the file content to UTF-16LE bytes; note the need to explicitly prepend the BOM (preamble)).As you've observed, using (implied) parameter -FilePath
- i.e. passing a file path and letting Get-AuthenticodeSignature
itself read its contents - is not subject to this encoding restriction - as long as PowerShell can infer the script file's encoding per the usual rules, signature verification succeeds.