powershellauthenticode

Inconsistency in results of calling Get-AuthenticodeSignature from PowerShell, often says NotSigned


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.


Solution

  • 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:

    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.