Reputation: 21
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
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
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