如何使用PowerShell从签名的dll中提取摘要算法的所有列表?

时间:2019-09-09 07:04:59

标签: powershell hash digital-signature

我有一个经过数字签名的dll文件。我需要编写一个PowerShell命令,该命令将为我提供用于数字签名的摘要算法。

我的Dll同时具有SHA1和SHA256,并且我需要两个值。

我尝试了以下解决方案,但仅提供SHA1

How to extract digest algorithm from signed dll using PowerShell?

命令:

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

2 个答案:

答案 0 :(得分:0)

经过一番搜索,我发现this blogVadims Podāns具有称为Get-AuthenticodeSignatureEx的功能,如果文件具有此功能,则确实可以获取主要和辅助(嵌套)证书签名。

我在该代码中添加了一些内容,以从发行者的X500DistinghuishedName中解析出友好名称,并确保嵌套签名也具有SigningTime时间戳。

功能:

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 {}
}

有了该功能,您可以像这样使用它:

$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

我的机器上的输出:

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

答案 1 :(得分:0)

以下综合文章可能有一个起点:Reading multiple signatures from signed file with PowerShell

  

Get-AuthenticodeSignature cmdlet具有以下限制:

     
      
  • 仅获取第一个签名;
  •   
  • 如果签名带有时间戳,则不提供签名时间;
  •   
  • 未提供签名算法信息。
  •   
     

从技术上讲,Microsoft authenticode签名支持   一次只有一个签名。附加签名作为嵌套完成   签名

他们编写了Get-AuthenticodeSignature cmdlet的扩展版,作为根据Attribution-ShareAlike 4.0 International许可获得许可的功能。
不幸的是,当前的Get-AuthenticodeSignatureEx函数似乎不足以支持两个以上的签名。

但是,有SignTool.exe。该工具会随 Visual Studio 自动安装。

示例(使用/v开关:打印详细的成功和状态消息。这可能还会提供有关错误的更多信息。如果要查看有关签名者的信息,则应使用此选项。< / em>)

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

例如,以下.ps1脚本可以找到所有签名两次以上的文件:

$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
        }
    }

(当前使用Get-ChildItem C:\Windows\System32\nvh*.dll来限制运行时间和输出大小):

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