Hello S-1-1-0, it’s time for another blog post. Another PowerShell and CryptoAPI blog post.

Identifying the problem

Recently I had a trivial (or non-trivial?) challenge: read multiple signatures from signed files. Usually files have only one signature:

image

However, there are cases when files have two signatures (for example, one is SHA1 and second is SHA2) like this:

image

PowerShell has awesome Get-AuthenticodeSignature cmdlet that allows to retrieve authenticode signature status and relevant certificates:

PS C:\Users\vPodans> Get-AuthenticodeSignature C:\Windows\System32\msvcr120.dll


    Directory: C:\Windows\System32


SignerCertificate                         Status                                 Path
-----------------                         ------                                 ----
108E2BA23632620C427C570B6D9DB51AC31387FE  Valid                                  msvcr120.dll


PS C:\Users\vPodans> Get-AuthenticodeSignature C:\Windows\System32\msvcr120.dll | fl *


SignerCertificate      : [Subject]
                           CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Issuer]
                           CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Serial Number]
                           33000000B011AF0A8BD03B9FDD0001000000B0

                         [Not Before]
                           25.01.2013. 0:33:39

                         [Not After]
                           25.04.2014. 1:33:39

                         [Thumbprint]
                           108E2BA23632620C427C570B6D9DB51AC31387FE

TimeStamperCertificate : [Subject]
                           CN=Microsoft Time-Stamp Service, OU=nCipher DSE ESN:C0F4-3086-DEF8, OU=MOPR, O=Microsoft Cor
                         poration, L=Redmond, S=Washington, C=US

                         [Issuer]
                           CN=Microsoft Time-Stamp PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Serial Number]
                           330000002B393248C1B2C948F300000000002B

                         [Not Before]
                           05.09.2012. 0:12:34

                         [Not After]
                           04.12.2013. 23:12:34

                         [Thumbprint]
                           2F497C556F94E32731CF86ADD8629C9867C35A24

Status                 : Valid
StatusMessage          : Signature verified.
Path                   : C:\Windows\System32\msvcr120.dll



PS C:\Users\vPodans>

There is a bug in PowerShell 5.0 on Windows 10: TimeStamperCertificate property is always $null even if it is properly timestamped.

By using this cmdlet we can retrieve basic information about the signture. However, Get-AuthenticodeSignature cmdlet has the following limitations:

  • Only first signature is fetched;
  • If the signature is timestamped, no signing time is provided;
  • No signature algorithm information is provided.

what to do? Master the PowerShell and manage entire world.

Developing the solution

Ok, we have identified the problem, now we need to move forward and try to figure out how to fill above mentioned gaps.

For our purposes we will have to use some CryptoAPI native functions:

  • CryptQueryObject – this function will be used to read entire authenticode signature block
  • CryptMsgGetParam – this funtion will be used to read the retrieved authenticode signature block into a byte array
  • CryptMsgClose – will be used to clean unmanaged resources
  • CertCloseStore – will be used to clean unmanaged resources

We will write function signature definition (I will skip steps used to do this, as they are not very important here):

$signature = @"
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptQueryObject(
    int dwObjectType,
    [MarshalAs(UnmanagedType.LPWStr)]
    string pvObject,
    int dwExpectedContentTypeFlags,
    int dwExpectedFormatTypeFlags,
    int dwFlags,
    ref int pdwMsgAndCertEncodingType,
    ref int pdwContentType,
    ref int pdwFormatType,
    ref IntPtr phCertStore,
    ref IntPtr phMsg,
    ref IntPtr ppvContext
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptMsgGetParam(
    IntPtr hCryptMsg,
    int dwParamType,
    int dwIndex,
    byte[] pvData,
    ref int pcbData
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptMsgClose(
    IntPtr hCryptMsg
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CertCloseStore(
    IntPtr hCertStore,
    int dwFlags
);
"@
Add-Type -AssemblyName System.Security
Add-Type -MemberDefinition $signature -Namespace PKI -Name Crypt32

for CryptQueryObject function calls we will use the following parameters:

  • CERT_QUERY_OBJECT_FILE (0x1) for dwObjectType parameter;
  • path to a file for pvObject parameter;
  • CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED (1024) for dwExpectedContentTypeFlags parameter
  • CERT_QUERY_FORMAT_FLAG_BINARY (0x2) for dwExpectedFormatTypeFlags parameter.

These values can be found in the Wincrypt.h header file

Now, call the function:

$CERT_QUERY_OBJECT_FILE = 0x1
$CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = 0x400
$CERT_QUERY_FORMAT_FLAG_BINARY = 0x2
$pdwMsgAndCertEncodingType =  0
$pdwContentType =  0
$pdwFormatType =  0
[IntPtr]$phCertStore = [IntPtr]::Zero
[IntPtr]$phMsg = [IntPtr]::Zero
[IntPtr]$ppvContext = [IntPtr]::Zero
$return = [PKI.Crypt32]::CryptQueryObject(
    $CERT_QUERY_OBJECT_FILE,
    "C:\Windows\System32\msvcr120.dll",
    $CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
    $CERT_QUERY_FORMAT_FLAG_BINARY,
    0,
    [ref]$pdwMsgAndCertEncodingType,
    [ref]$pdwContentType,
    [ref]$pdwFormatType,
    [ref]$phCertStore,
    [ref]$phMsg,
    [ref]$ppvContext
)

and the function returns the following:

PS C:\Users\vPodans> $return
True
PS C:\Users\vPodans> $phMsg
852136756384

The function succeeded and returned a pointer to a PKCS#7 signature block (CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED). Now, we need to extract PKCS#7 signed message and extract managed bytes. We will use CryptMsgGetParam function for these purposes as follows:

$CMSG_ENCODED_MESSAGE = 29
$pcbData = 0
# call the function for the first time to determine the managed buffer size
[PKI.Crypt32]::CryptMsgGetParam($phMsg,$CMSG_ENCODED_MESSAGE,0,$null,[ref]$pcbData)
# allocate the buffer
$pvData = New-Object byte[] -ArgumentList $pcbData
# read the PKCS#7 signaed message to managed buffer
[PKI.Crypt32]::CryptMsgGetParam($phMsg,$CMSG_ENCODED_MESSAGE,0,$pvData,[ref]$pcbData)

and the code returns the following:

PS C:\Users\vPodans> $pcbData
16022

The whole signature block (encoded) is 16022 bytes (about 16kb). $pvData will store encoded bytes. Now, we leave unmanaged world and return to fully managed. We pass encoded PKCS#7 bytes to SignedCms class:

$SignedCms = New-Object Security.Cryptography.Pkcs.SignedCms
$SignedCms.Decode($pvData)

We will focus on a SignerInfos property, which stores signature information. SignerInfos property is a collection of SignerInfo objects. For multi-signed files you would expect that there will be two objects, where each of them represents each signature. It is very wrong assumption.

Technically speaking, Microsoft authenticode signature supports only one signature at a time. Additional signatures are done as nested signatures.

Look at these images:

imageimage

First picture represents SHA1 signature details and second representd SHA2 signature. We should explore them in more details. There are few fields and two type of attributes: authenticated and unauthenticated. Authenticated attributes are protected by signature. Any modifications there will invalidate the signature. Unauthenticated attributes are not protected by original signature and any modifications there will not invalidate the signature. To protect unauthenticated attributes from tampering, they should implement their own protection mechanisms. Usually, by externally signing these attributes.

First image observation tells us that signature timestamping (Counter Sign attribute) is unauthenticated. This means that we can attach or detach timestamp from the signature without changing original signature. This allows us to timestamp signed files after they are signed.

First image displays another unauthenticated attribute with OID=1.3.6.1.4.1.311.2.4.1 (szOID_NESTED_SIGNATURE). This is a nested SHA2 signature. Here it lies. Let’s go with retrieving general information from the SHA1 signature:

PS C:\Users\vPodans> $SignedCms.SignerInfos[0]


Version            : 1
Certificate        : [Subject]
                       CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                     [Issuer]
                       CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                     [Serial Number]
                       33000000B011AF0A8BD03B9FDD0001000000B0

                     [Not Before]
                       25.01.2013. 0:33:39

                     [Not After]
                       25.04.2014. 1:33:39

                     [Thumbprint]
                       108E2BA23632620C427C570B6D9DB51AC31387FE

SignerIdentifier   : System.Security.Cryptography.Pkcs.SubjectIdentifier
DigestAlgorithm    : System.Security.Cryptography.Oid
SignedAttributes   : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.
                     Oid, System.Security.Cryptography.Oid}
UnsignedAttributes : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid}
CounterSignerInfos : {System.Security.Cryptography.Pkcs.SignerInfo}



PS C:\Users\vPodans> $SignedCms.SignerInfos[0].DigestAlgorithm

Value                                                       FriendlyName
-----                                                       ------------
1.3.14.3.2.26                                               sha1

as we discovered, timestamp information is stored in the CounterSignerInfos property:

PS C:\Users\vPodans> $SignedCms.SignerInfos[0].CounterSignerInfos


Version            : 1
Certificate        : [Subject]
                       CN=Microsoft Time-Stamp Service, OU=nCipher DSE ESN:C0F4-3086-DEF8, OU=MOPR, O=Microsoft Corpora
                     tion, L=Redmond, S=Washington, C=US

                     [Issuer]
                       CN=Microsoft Time-Stamp PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                     [Serial Number]
                       330000002B393248C1B2C948F300000000002B

                     [Not Before]
                       05.09.2012. 0:12:34

                     [Not After]
                       04.12.2013. 23:12:34

                     [Thumbprint]
                       2F497C556F94E32731CF86ADD8629C9867C35A24

SignerIdentifier   : System.Security.Cryptography.Pkcs.SubjectIdentifier
DigestAlgorithm    : System.Security.Cryptography.Oid
SignedAttributes   : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.
                     Oid}
UnsignedAttributes : {}
CounterSignerInfos : {}



PS C:\Users\vPodans>

Okay, we see timestamping certificate, however, where is the signing time (timestamp)? It is stored in the signed attributes, and attribute OID is 1.2.840.113549.1.9.5 (Signing Time):

PS C:\Users\vPodans> $SignedCms.SignerInfos[0].CounterSignerInfos[0].SignedAttributes | ?{$_.Oid.Value -eq "1.2.840.1135
49.1.9.5"}

Oid                                                         Values
---                                                         ------
System.Security.Cryptography.Oid                            {System.Security.Cryptography.Oid}


PS C:\Users\vPodans> ($SignedCms.SignerInfos[0].CounterSignerInfos[0].SignedAttributes | ?{$_.Oid.Value -eq "1.2.840.113
549.1.9.5"}).values

SigningTime                             Oid                                     RawData
-----------                             ---                                     -------
05.10.2013. 6:49:07                     System.Security.Cryptography.Oid        {23, 13, 49, 51...}


PS C:\Users\vPodans>

Here it is!

Note that signing time is stored in UTC, so do not forget to convert it to your local time zone.

So, at this point we have retrieved everything we need from the SHA1 signature. How to retrieve SHA2 signature and relevant information? As we discussed, SHA2 is stored in the unauthenticated attribute with OID=1.3.6.1.4.1.311.2.4.1 (szOID_NESTED_SIGNATURE). Let’s go:

PS C:\Users\vPodans> $SignedCms.SignerInfos[0].UnsignedAttributes | ?{$_.Oid.Value -eq "1.3.6.1.4.1.311.2.4.1"}

Oid                                                         Values
---                                                         ------
System.Security.Cryptography.Oid                            {System.Security.Cryptography.Oid}


PS C:\Users\vPodans> ($SignedCms.SignerInfos[0].UnsignedAttributes | ?{$_.Oid.Value -eq "1.3.6.1.4.1.311.2.4.1"}).values


Oid                                                         RawData
---                                                         -------
System.Security.Cryptography.Oid                            {48, 130, 35, 244...}


PS C:\Users\vPodans>

Values[0].RawData is the same signed PKCS#7 message, so we instantiate another SignedCms object from Values[0].RawData and use the already used techniques to process the object:

PS C:\Users\vPodans> $nestedSigAttr = $SignedCms.SignerInfos[0].UnsignedAttributes | ?{$_.Oid.Value -eq "1.3.6.1.4.1.311
.2.4.1"}
PS C:\Users\vPodans> $SignedCms2 = New-Object Security.Cryptography.Pkcs.SignedCms
PS C:\Users\vPodans> $SignedCms2.Decode($nestedSigAttr.Values[0].RawData)
PS C:\Users\vPodans> $SignedCms2.SignerInfos[0]


Version            : 1
Certificate        : [Subject]
                       CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                     [Issuer]
                       CN=Microsoft Code Signing PCA 2011, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                     [Serial Number]
                       330000001A77BB74B307D116B800000000001A

                     [Not Before]
                       24.09.2013. 20:41:41

                     [Not After]
                       24.12.2014. 19:41:41

                     [Thumbprint]
                       6474839AF67AB79C91007FF62FE08E2ACF016B83

SignerIdentifier   : System.Security.Cryptography.Pkcs.SubjectIdentifier
DigestAlgorithm    : System.Security.Cryptography.Oid
SignedAttributes   : {System.Security.Cryptography.Oid, System.Security.Cryptography.Oid, System.Security.Cryptography.
                     Oid, System.Security.Cryptography.Oid...}
UnsignedAttributes : {System.Security.Cryptography.Oid}
CounterSignerInfos : {}



PS C:\Users\vPodans> $SignedCms2.SignerInfos[0].DigestAlgorithm

Value                                                       FriendlyName
-----                                                       ------------
2.16.840.1.101.3.4.2.1                                      sha256


PS C:\Users\vPodans>

if we combine all this information, we can write an extended version of Get-AuthenticodeSignature cmdlet:

function Get-AuthenticodeSignatureEx {
<#
.ForwardHelpTargetName Get-AuthenticodeSignature
#>
[CmdletBinding()]
    param(
        [Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
        [String[]]$FilePath
    )
    begin {
$signature = @"
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptQueryObject(
    int dwObjectType,
    [MarshalAs(UnmanagedType.LPWStr)]
    string pvObject,
    int dwExpectedContentTypeFlags,
    int dwExpectedFormatTypeFlags,
    int dwFlags,
    ref int pdwMsgAndCertEncodingType,
    ref int pdwContentType,
    ref int pdwFormatType,
    ref IntPtr phCertStore,
    ref IntPtr phMsg,
    ref IntPtr ppvContext
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptMsgGetParam(
    IntPtr hCryptMsg,
    int dwParamType,
    int dwIndex,
    byte[] pvData,
    ref int pcbData
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CryptMsgClose(
    IntPtr hCryptMsg
);
[DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool CertCloseStore(
    IntPtr hCertStore,
    int dwFlags
);
"@
        Add-Type -AssemblyName System.Security
        Add-Type -MemberDefinition $signature -Namespace PKI -Name Crypt32
        $CERT_QUERY_OBJECT_FILE = 0x1
        $CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = 0x400
        $CERT_QUERY_FORMAT_FLAG_BINARY = 0x2
        
        function getTimeStamps($SignerInfo) {
            $retValue = @()
            foreach ($CounterSignerInfos in $Infos.CounterSignerInfos) {                    
                $sTime = ($CounterSignerInfos.SignedAttributes | ?{$_.Oid.Value -eq "1.2.840.113549.1.9.5"}).Values | `
                Where-Object {$_.SigningTime -ne $null}
                $tsObject = New-Object psobject -Property @{
                    Certificate = $CounterSignerInfos.Certificate
                    SigningTime = $sTime.SigningTime.ToLocalTime()
                }
                $retValue += $tsObject
            }
            $retValue
        }
    }
    process {
        Get-AuthenticodeSignature $FilePath | ForEach-Object {
            $Output = $_
            if ($Output.SignerCertificate -ne $null) {              
                $pdwMsgAndCertEncodingType =  0
                $pdwContentType =  0
                $pdwFormatType =  0
                [IntPtr]$phCertStore = [IntPtr]::Zero
                [IntPtr]$phMsg = [IntPtr]::Zero
                [IntPtr]$ppvContext = [IntPtr]::Zero
                $return = [PKI.Crypt32]::CryptQueryObject(
                    $CERT_QUERY_OBJECT_FILE,
                    $Output.Path,
                    $CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
                    $CERT_QUERY_FORMAT_FLAG_BINARY,
                    0,
                    [ref]$pdwMsgAndCertEncodingType,
                    [ref]$pdwContentType,
                    [ref]$pdwFormatType,
                    [ref]$phCertStore,
                    [ref]$phMsg,
                    [ref]$ppvContext
                )
                if (!$return) {return}
                $pcbData = 0
                $return = [PKI.Crypt32]::CryptMsgGetParam($phMsg,29,0,$null,[ref]$pcbData)
                if (!$return) {return}
                $pvData = New-Object byte[] -ArgumentList $pcbData
                $return = [PKI.Crypt32]::CryptMsgGetParam($phMsg,29,0,$pvData,[ref]$pcbData)
                $SignedCms = New-Object Security.Cryptography.Pkcs.SignedCms
                $SignedCms.Decode($pvData)
                $Infos = $SignedCms.SignerInfos[0]
                $Output | Add-Member -MemberType NoteProperty -Name TimeStamps -Value $null
                $Output | Add-Member -MemberType NoteProperty -Name DigestAlgorithm -Value $Infos.DigestAlgorithm.FriendlyName
                $Output.TimeStamps = getTimeStamps $Infos
                $second = $Infos.UnsignedAttributes | ?{$_.Oid.Value -eq "1.3.6.1.4.1.311.2.4.1"}
                if ($second) {
                    $value = $second.Values | ?{$_.Oid.Value -eq "1.3.6.1.4.1.311.2.4.1"}
                    $SignedCms2 = New-Object Security.Cryptography.Pkcs.SignedCms
                    $SignedCms2.Decode($value.RawData)
                    $Output | Add-Member -MemberType NoteProperty -Name NestedSignature -Value $null
                    $Infos = $SignedCms2.SignerInfos[0]
                    $nested = New-Object psobject -Property @{
                        SignerCertificate = $Infos.Certificate
                        DigestAlgorithm = $Infos.DigestAlgorithm.FriendlyName
                        TimeStamps = getTimeStamps $Infos
                    }
                    $Output.NestedSignature = $nested
                }
                $Output
                [void][PKI.Crypt32]::CryptMsgClose($phMsg)
                [void][PKI.Crypt32]::CertCloseStore($phCertStore,0)
            } else {
                $Output
            }
        }
    }
    end {}
}

And the function usage:

PS C:\Users\vPodans> $sig = Get-AuthenticodeSignatureEx "C:\Windows\System32\msvcr120.dll"
PS C:\Users\vPodans> $sig | fl *


TimeStamps             : @{SigningTime=05.10.2013. 9:49:07; Certificate=[Subject]
                           CN=Microsoft Time-Stamp Service, OU=nCipher DSE ESN:C0F4-3086-DEF8, OU=MOPR, O=Microsoft Cor
                         poration, L=Redmond, S=Washington, C=US

                         [Issuer]
                           CN=Microsoft Time-Stamp PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Serial Number]
                           330000002B393248C1B2C948F300000000002B

                         [Not Before]
                           05.09.2012. 0:12:34

                         [Not After]
                           04.12.2013. 23:12:34

                         [Thumbprint]
                           2F497C556F94E32731CF86ADD8629C9867C35A24
                         }
DigestAlgorithm        : sha1
NestedSignature        : @{SignerCertificate=[Subject]
                           CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Issuer]
                           CN=Microsoft Code Signing PCA 2011, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Serial Number]
                           330000001A77BB74B307D116B800000000001A

                         [Not Before]
                           24.09.2013. 20:41:41

                         [Not After]
                           24.12.2014. 19:41:41

                         [Thumbprint]
                           6474839AF67AB79C91007FF62FE08E2ACF016B83
                         ; DigestAlgorithm=sha256; TimeStamps=}
SignerCertificate      : [Subject]
                           CN=Microsoft Corporation, OU=MOPR, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Issuer]
                           CN=Microsoft Code Signing PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Serial Number]
                           33000000B011AF0A8BD03B9FDD0001000000B0

                         [Not Before]
                           25.01.2013. 0:33:39

                         [Not After]
                           25.04.2014. 1:33:39

                         [Thumbprint]
                           108E2BA23632620C427C570B6D9DB51AC31387FE

TimeStamperCertificate : [Subject]
                           CN=Microsoft Time-Stamp Service, OU=nCipher DSE ESN:C0F4-3086-DEF8, OU=MOPR, O=Microsoft Cor
                         poration, L=Redmond, S=Washington, C=US

                         [Issuer]
                           CN=Microsoft Time-Stamp PCA, O=Microsoft Corporation, L=Redmond, S=Washington, C=US

                         [Serial Number]
                           330000002B393248C1B2C948F300000000002B

                         [Not Before]
                           05.09.2012. 0:12:34

                         [Not After]
                           04.12.2013. 23:12:34

                         [Thumbprint]
                           2F497C556F94E32731CF86ADD8629C9867C35A24

Status                 : Valid
StatusMessage          : Signature verified.
Path                   : C:\Windows\System32\msvcr120.dll



PS C:\Users\vPodans> $sig.TimeStamps

SigningTime                                                 Certificate
-----------                                                 -----------
05.10.2013. 9:49:07                                         [Subject]...


PS C:\Users\vPodans> $sig.NestedSignature

SignerCertificate                       DigestAlgorithm                         TimeStamps
-----------------                       ---------------                         ----------
[Subject]...                            sha256


PS C:\Users\vPodans>

as we can see, the function retrieves extended timestamping information (and fixes bug in Windows 10) and nested (secondary) signature information. At least, certificate and digest algorithms.


Share this article:

Comments:

Dennis

Hey,

Thanks so much for this. With the new signing requirements in 1607 I needed something like this to check our signing validity. :)

I'm guessing that the lack of license means that you are offering this as in the public domain?

Thanks again!

Dennis.

Vadims Podāns

The script is licensed under Attribution-ShareAlike 4.0 International license. Please, check Disclaimer page for more details.

Dennis

Thanks Vadims. Attribute and license properly included. :)

David

Thanks the script saved me the day

Theary

HI,

Is it normal the nested signature does not have timestamp? I get an empty list of CounterSignerInfos for the nested signerinfo. How can I retrieve the timestamp of the nested signature? Thanks

Vadims Podāns

Hi!

If you look at signature details images, you will see that CounterSigner attribute is available for the first signature only. There is no CounterSigner attribute in nested signature.

Anonymous

You are using an absolute (and personal ^^) path "c:\Users\vPodans\Desktop\s.bin" on line 94, would change that.

Vadims Podāns

> You are using an absolute (and personal ^^)

Thanks, removed this line.

Anonymous

Also on line 66, change the variable to $FilePath...

Eliad

Brilliant!.

Thanks alot!

Eliad

As an alternative, one can use sysinternals sigcheck.exe https://docs.microsoft.com/en-us/sysinternals/downloads/sigcheck

Vadims Podāns

There are various tools. The article is about managed PowerShell way.

Manikandan R

thanks a lot for this effort. Everytime i search about hash algorthim excercises with powershell, i get this in google. Much appreciated !!

Carsten Giese

Is there any solution doing the same with bcypt.dll?
According to Microsoft te WinCrypr-API is deprecated.

Vadims Podāns

No, there is no implementation in bcrypt. The code provided is still current.

Björn Kautler

At least in PS 5.1.19041.1320 this does not work as posted.
It complains with


Get-AuthenticodeSignature : Die Datei "System.Management.Automation.PSBoundParametersDictionary" wurde nicht gefunden.
In Zeile:66 Zeichen:9
+         Get-AuthenticodeSignature $PSBoundParameters | ForEach-Object ...
+         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : ObjectNotFound: (:) [Get-AuthenticodeSignature], FileNotFoundException
    + FullyQualifiedErrorId : SignatureCommandsBaseFileNotFound,Microsoft.PowerShell.Commands.GetAuthenticodeSignatureCommand

 

Doing as was suggested in the comment as of "30.04.2018 12:05 (GMT+2)" and replacing `$PSBoundParameters` by `$FilePath` makes it work.

 

Now it just has one more drawback, it only works for the first and last signature.
I have files with three or more signatures and can only retrieve the first one using

(Get-AuthenticodeSignatureEx foo.dll).SignerCertificate.Subject

and the last one using

(Get-AuthenticodeSignatureEx foo.dll).NestedSignature.SignerCertificate.Subject

Vadims Podāns

> Get-AuthenticodeSignature $PSBoundParameters

in fact, there should be @PSBoundParameters. It is blog rendering issue which renders "@" character as "$" in PS code snippets. If you replace "$" to "@", the script will work.

Rick S

The script is fine but I could not retrieve the timestamp of the signature of a Windows Update catalog. There is only one signature inside (sha256). In the propertypage of the file I can see the sigature is from 13th of December 2022, but with the script after "$sig | fl *" in the line "TimeStamps" no value is displayed.

If I opened the Details page of the signature on tab Advanced I see the same as in your screenshot above on the right "Digital Signature Details" image except the first line under "Authenticated attributes". Why I can not retrieve the date as displayed in the property page of the file on tab "Digital Signatures"? Do you have any idea?

 


Post your comment:

Please, solve this little equation and enter result below. Captcha