Rohan Basankar
Rohan Basankar

Reputation: 21

How to extract all the list of digest algorithm from signed dll using PowerShell?

I have a dll file which is digitally signed. I need to write a PowerShell command which would get me the Digest Algorithm that are used for the Digital Signature.

The Dll I have has both SHA1 and SHA256 and I need both values.

I tried the below solution but it gives SHA1 only

How to extract digest algorithm from signed dll using PowerShell?

command :

Get-AuthenticodeSignature $file.Filename | 
    %{ $_.SignerCertificate.SignatureAlgorithm.friendlyname } 

Upvotes: 2

Views: 2847

Answers (2)

JosefZ
JosefZ

Reputation: 30123

There is a potential starting point in the following comprehensive article: Reading multiple signatures from signed file with PowerShell.

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.

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

They wrote an extended version of Get-AuthenticodeSignature cmdlet as a function licensed under Attribution-ShareAlike 4.0 International license.
Unfortunately, the current Get-AuthenticodeSignatureEx function appears insufficient for more than two signatures.

However, there is SignTool.exe. This tool is automatically installed with Visual Studio.

Example (with /v switch: Print verbose success and status messages. This may also provide slightly more information on error. If you want to see information about the signer, you should use this option.)

d:\bat> 2>NUL "c:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe" verify /pa /all C:\WINDOWS\system32\OpenCL.dll
File: C:\Windows\System32\OpenCL.dll
Index  Algorithm  Timestamp
========================================
0      sha1       Authenticode
1      sha256     RFC3161
2      sha256     RFC3161

Successfully verified: C:\Windows\System32\OpenCL.dll

For instance, the following .ps1 script could find all files signed more than twice:

$signtool="c:\Program Files (x86)\Windows Kits\10\bin\10.0.17763.0\x64\signtool.exe"
Get-ChildItem -File |
    ForEach-Object {
        $aux = . "$signtool" verify /pa /all $_.FullName 2>$null
        if ( $aux -match "^2|^3|^4|^5|^6|^7|^8|^9" ) {
            $aux
        }
    }

(currently used Get-ChildItem C:\Windows\System32\nvh*.dll to limit run time as well as output size):

D:\PShell\tests\AuthenticodeSignTool.ps1
File: C:\Windows\System32\nvhdagenco6420103.dll
Index  Algorithm  Timestamp    
========================================
0      sha1       Authenticode 
1      sha256     RFC3161      
2      sha256     RFC3161      
3      sha256     RFC3161      

Successfully verified: C:\Windows\System32\nvhdagenco6420103.dll

File: C:\Windows\System32\nvhdap64.dll
Index  Algorithm  Timestamp    
========================================
0      sha1       Authenticode 
1      sha256     RFC3161      
2      sha256     RFC3161      
3      sha256     RFC3161      

Successfully verified: C:\Windows\System32\nvhdap64.dll

Upvotes: 2

Theo
Theo

Reputation: 61093

After some searching around, I found this blog by Vadims Podāns with a function called Get-AuthenticodeSignatureEx that indeed gets both the primary and the secondary (nested) certificate signatures if the file has this.

I have added a little to that code to parse the friendly name out of the X500DistinghuishedName of the issuer and to ensure the nested signature also has a SigningTime timestamp.

The function:

function Get-AuthenticodeSignatureEx {
    [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 | 
                          Where-Object{$_.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
        }

        function getIssuerName($SignerInfo) {
            # helper function to parse the name out of the X500DistinghuishedName formatted 'Issuer' string
            if ($SignerInfo.Certificate.Issuer -match 'O=([^,]+)') { $matches[1] }
            elseif ($SignerInfo.Certificate.Issuer -match 'CN=([^,]+)')  { $matches[1] }
            else { $SignerInfo.Certificate.Issuer }
        }
    }
    process {
        Get-AuthenticodeSignature -FilePath $FilePath | ForEach-Object {
            $Output = $_
            if ($Output.SignerCertificate) {              
                $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 IssuerName -Value (getIssuerName $Infos)
                $Output | Add-Member -MemberType NoteProperty -Name DigestAlgorithm -Value $Infos.DigestAlgorithm.FriendlyName
                $Output | Add-Member -MemberType NoteProperty -Name TimeStamps -Value (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
                        IssuerName        = getIssuerName $Infos
                        DigestAlgorithm   = $Infos.DigestAlgorithm.FriendlyName
                        TimeStamps        = getTimeStamps $Infos
                    }
                    # the nested certificate usually does not have a this, so use from the primary certificate
                    if (!$nested.TimeStamps) { $nested.TimeStamps = $Output.Timestamps }
                    $Output.NestedSignature = $nested
                }
                $Output
                [void][PKI.Crypt32]::CryptMsgClose($phMsg)
                [void][PKI.Crypt32]::CertCloseStore($phCertStore,0)
            } 
            else {
                $Output
            }
        }
    }
    end {}
}

With that function in place, you can use it like this:

$sig = Get-AuthenticodeSignatureEx "C:\Windows\Microsoft.NET\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll"
$result = @($sig | Select-Object IssuerName, DigestAlgorithm, @{Name = 'TimeStamp'; Expression = {$_.TimeStamps.SigningTime}})
if ($sig.NestedSignature) {
    $result += $sig.NestedSignature | Select-Object IssuerName, DigestAlgorithm, @{Name = 'TimeStamp'; Expression = {$_.TimeStamps.SigningTime}}
}
$result

Output on my machine:

IssuerName            DigestAlgorithm TimeStamp        
----------            --------------- ---------        
Microsoft Corporation sha1            25-7-2019 4:18:14
Microsoft Corporation sha256          25-7-2019 4:18:14

Upvotes: 1

Related Questions