How do I programmatically change the Enabled Purposes of a Certificate Authority, in the relevant Windows Certificate Store, using PowerShell?
This is possible to do in the Certificates MMC snap-in
Is this only possible using P/Invoke with CertSetCertificateContextProperty as per StackOverflow: How to set certificate purposes? {C#}
Ideally, I want to import a custom Trusted Root Certificate Authority and only enable it for the purpose of Client Authentication.
A PowerShell Cmdlet that uses CertSetCertificateContextProperty at it's core. Thank you to Crypt32 and their answer on another post for guidance.
Example Usage:
Set-CertificateEku -StoreLocation 'CurrentUser' -StoreName 'Root' -CertificateThumbprint 'ffffffffffffffffffffffffffffffffffffffff' -Oids #("1.3.6.1.5.5.7.3.2") # Client Authentication
Function Set-CertificateEku {
[CmdletBinding()]
Param(
[Parameter(Mandatory)]
[ValidateSet('CurrentUser', 'LocalMachine')]
$StoreLocation,
[Parameter(Mandatory)]
$StoreName,
[Parameter(Mandatory)]
$CertificateThumbprint,
[Parameter(Mandatory)]
$Oids
)
$StoreLocation = switch($StoreLocation) {
'CurrentUser' {
[System.Security.Cryptography.X509Certificates.StoreLocation]::CurrentUser
}
'LocalMachine' {
[System.Security.Cryptography.X509Certificates.StoreLocation]::LocalMachine
}
}
try {
$CertificateStore = [System.Security.Cryptography.X509Certificates.X509Store]::new($StoreName, $StoreLocation)
$CertificateStore.Open([System.Security.Cryptography.X509Certificates.OpenFlags]::ReadWrite -bor [System.Security.Cryptography.X509Certificates.OpenFlags]::OpenExistingOnly)
} catch {
Write-Error "Could not Open Certificate Store $StoreName in $StoreLocation"
return $false
}
$Certificates = $CertificateStore.Certificates.Find(
[System.Security.Cryptography.X509Certificates.X509FindType]::FindByThumbprint,
$CertificateThumbprint,
$false
)
if($Certificates.Count -eq 0) {
Write-Error "Could not find Certificate $CertificateThumbprint in $StoreName in $StoreLocation"
return $false
}
$Certificate = $Certificates[0]
$PKICrypt32 = #"
[DllImport("Crypt32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern bool CertSetCertificateContextProperty(
IntPtr pCertContext,
uint dwPropId,
uint dwFlags,
IntPtr pvData
);
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct CRYPTOAPI_BLOB {
public uint cbData;
public IntPtr pbData;
}
"#
Add-Type -MemberDefinition $PKICrypt32 -Namespace 'PKI' -Name 'Crypt32'
$OIDs = [Security.Cryptography.OidCollection]::new()
foreach($Oid in $Oids) {
[void]$OIDs.Add([Security.Cryptography.Oid]::new($Oid))
}
$EKU = [Security.Cryptography.X509Certificates.X509EnhancedKeyUsageExtension]::new($OIDs, $false)
$pbData = [Runtime.InteropServices.Marshal]::AllocHGlobal($EKU.RawData.Length)
[Runtime.InteropServices.Marshal]::Copy($EKU.RawData, 0, $pbData, $EKU.RawData.Length)
$Blob = New-Object PKI.Crypt32+CRYPTOAPI_BLOB -Property #{
cbData = $EKU.RawData.Length;
pbData = $pbData;
}
$pvData = [Runtime.InteropServices.Marshal]::AllocHGlobal([Runtime.InteropServices.Marshal]::SizeOf([type][PKI.Crypt32+CRYPTOAPI_BLOB]))
[Runtime.InteropServices.Marshal]::StructureToPtr($Blob, $pvData, $false)
$Result = [PKI.Crypt32]::CertSetCertificateContextProperty($Certificate.Handle, 9, 0, $pvData)
[Runtime.InteropServices.Marshal]::FreeHGlobal($pvData)
[Runtime.InteropServices.Marshal]::FreeHGlobal($pbData)
$CertificateStore.Close()
return $Result
}
Related
I am loocking for an option to disable the SSL cert validation for a single websocket connection. My server in question uses a self-signed certificate only.
This is the Powershell code that I have so far:
$server = 'server1'
$cred = Get-Credential -Message "Credentials to access '$server'"
# setting credentials for any required proxy-authentication:
[System.Net.WebRequest]::DefaultWebProxy.Credentials = [System.Net.CredentialCache]::DefaultNetworkCredentials
# define settings for http-client:
Add-Type -AssemblyName System.Net.Http
$handler = [System.Net.Http.HttpClientHandler]::new()
$ignoreCerts = [System.Net.Http.HttpClientHandler]::DangerousAcceptAnyServerCertificateValidator
$handler.ServerCertificateCustomValidationCallback = $ignoreCerts
$handler.Credentials = $cred
$handler.PreAuthenticate = $true
$client = [System.Net.Http.HttpClient]::new($handler)
# generate websocket-key:
$init = [guid]::NewGuid().Guid.Substring(0,16)
$bytes = [System.Text.Encoding]::UTF8.GetBytes($init)
$key = [Convert]::ToBase64String($bytes)
# request protocol switch:
$url = [string]::Concat('https://', $server)
$client.DefaultRequestHeaders.Add('Connection', 'Upgrade,Keep-Alive')
$client.DefaultRequestHeaders.Add('Upgrade', 'WebSocket')
$client.DefaultRequestHeaders.Add('Sec-WebSocket-Version', '13')
$client.DefaultRequestHeaders.Add('Sec-WebSocket-Key', $key)
$client.DefaultRequestHeaders.Add('Origin', "wss://$server")
$response = $client.GetAsync($url).result
if ($response.StatusCode -eq 'BadGateway'){break}
Here I need a better solution than this "big bang" approach. In best case I am able to disable the cert validation only for this single websocket client/connection like I could do it for the above http-handler:
# skip SSL-Validation for websocket communication:
add-type #"
using System.Net;
using System.Security.Cryptography.X509Certificates;
public class TrustAllCertsPolicy : ICertificatePolicy {
public bool CheckValidationResult(
ServicePoint srvPoint, X509Certificate certificate,
WebRequest request, int certificateProblem) {
return true;
}
}
"#
[System.Net.ServicePointManager]::CertificatePolicy = New-Object TrustAllCertsPolicy
If cert-validation is disabled, then we can do a quick connection-test to the server:
$ws = [System.Net.WebSockets.ClientWebSocket]::new()
$ct = [System.Threading.CancellationToken]::new($false)
$url = [string]::Concat('wss://', $server)
# connect to server:
$conn = $ws.ConnectAsync($url, $ct)
$null = [Threading.Tasks.Task]::WaitAll($conn)
# check websocket-status:
$ws | ft -AutoSize
I have a set of apps that I would like to open on startup. They require a username and a password in order to login. Once you feed those in and press LogIn, a confirmation message asks to confirm whether you want to login or not.
Can this be done using CMD or PowerShell ?
eg:
> Start <path>/App1 | Username ; Password | Press OK
> Start <path>/App2 | Username ; Password | Press OK
Maybe this is helpful for you:
Add-Type -AssemblyName System.Windows.Forms
#----------------------------------------------------------------------
function win32UserDLL {
<#
// set focus to window
#>
#----------------------------------------------------------------------
param(
[Parameter()]
$mainWindowHandle = (Get-Process -Id $pid).MainWindowHandle,
[Parameter()]
[ValidateSet('SetFocus', 'BringWindowToTop')]
[String]$action = 'SetFocus'
)
$win32UserDLL = Add-Type –memberDefinition #”
[DllImport("user32.dll", SetLastError = true)]
public static extern IntPtr SetFocus(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
public static extern bool BringWindowToTop(IntPtr hWnd);
“# -name “Win32SetFocus” -namespace Win32Functions –passThru
switch( $action ) {
'SetFocus' {
[void]$win32UserDLL::SetFocus($mainWindowHandle)
}
'BringWindowToTop' {
[void]$win32UserDLL::BringWindowToTop($mainWindowHandle)
}
}
}
$appName = "myApp"
$handle = (Get-Process | where { $_.ProcessName -like $appName }).MainWindowHandle | where { $_ -ne 0 }
$handle
win32UserDLL -mainWindowHandle $handle -action SetFocus
[System.Windows.Forms.SendKeys]::SendWait("Username")
[System.Windows.Forms.SendKeys]::SendWait("{TAB}")
[System.Windows.Forms.SendKeys]::SendWait("Password")
[System.Windows.Forms.SendKeys]::SendWait("{ENTER}")
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 }
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
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
When calling a unmanaged piece of code with pinvoke -- createprofile. The Powershell.exe process crashes after the call to the method in the unmanaged code. The profile is created successfully.
Why this would happen? My code is below:
function CreateProfile
{
param([String]$UserSid, [String]$UserName, [system.uint32]$ProfilePath)
Add-Type -TypeDefinition '
using System;
using System.Runtime.InteropServices;
public static class PInvoke {
[DllImport("userenv.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern int CreateProfile( [MarshalAs(UnmanagedType.LPWStr)] String pszUserSid, [MarshalAs(UnmanagedType.LPWStr)] String pszUserName, [Out][MarshalAs(UnmanagedType.LPWStr)] System.Text.StringBuilder pszProfilePath, uint cchProfilePath);
}
'
$pszProfilePath = new-object -typename System.Text.StringBuilder
[int]$results = [PInvoke]::CreateProfile($UserSid, $UserName, $pszProfilePath, $ProfilePath)
}
$stringbuff = new-object system.text.stringbuilder(260)
[system.uint32]$a =$stringbuff.capacity
$sid = ((get-aduser -id 'brtestlocaluser').sid.value)
CreateProfile -usersid $sid -username 'brtestlocaluser' -ProfilePath $a
Finally was able to figure this out. Calling it in another means seemed to fix this issue.
function Register-NativeMethod
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string]$dll,
# Param2 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=1)]
[string]
$methodSignature
)
$script:nativeMethods += [PSCustomObject]#{ Dll = $dll; Signature = $methodSignature; }
}
function Add-NativeMethods
{
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param($typeName = 'NativeMethods')
$nativeMethodsCode = $script:nativeMethods | ForEach-Object { "
[DllImport(`"$($_.Dll)`")]
public static extern $($_.Signature);
" }
Add-Type #"
using System;
using System.Text;
using System.Runtime.InteropServices;
public static class $typeName {
$nativeMethodsCode
}
"#
}
function New-ProfileFromSID {
[CmdletBinding()]
[Alias()]
[OutputType([int])]
Param
(
# Param1 help description
[Parameter(Mandatory=$true,
ValueFromPipelineByPropertyName=$true,
Position=0)]
[string]$UserName,
[string]$domain = ''
)
$methodname = 'UserEnvCP2'
$script:nativeMethods = #();
if (-not ([System.Management.Automation.PSTypeName]$methodname).Type)
{
Register-NativeMethod "userenv.dll" "int CreateProfile([MarshalAs(UnmanagedType.LPWStr)] string pszUserSid,`
[MarshalAs(UnmanagedType.LPWStr)] string pszUserName,`
[Out][MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszProfilePath, uint cchProfilePath)";
Add-NativeMethods -typeName $methodname;
}
$sb = new-object System.Text.StringBuilder(260);
$pathLen = $sb.Capacity;
Write-Verbose "Creating user profile for $Username";
#$SID= ((get-aduser -id $UserName -ErrorAction Stop).sid.value)
if($domain)
{
$objUser = New-Object System.Security.Principal.NTAccount($domain, $UserName)
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
$SID = $strSID.Value
}
else
{
$objUser = New-Object System.Security.Principal.NTAccount($UserName)
$strSID = $objUser.Translate([System.Security.Principal.SecurityIdentifier])
$SID = $strSID.Value
}
Write-Verbose "$UserName SID: $SID"
try
{
$result = [UserEnvCP2]::CreateProfile($SID, $Username, $sb, $pathLen)
if($result -eq '-2147024713')
{
$status = "$userName already exists"
write-verbose "$username Creation Result: $result"
}
elseif($result -eq '-2147024809')
{
$status = "$username Not Found"
write-verbose "$username creation result: $result"
}
elseif($result -eq 0)
{
$status = "$username Profile has been created"
write-verbose "$username Creation Result: $result"
}
else
{
$status = "$UserName unknown return result: $result"
}
}
catch
{
Write-Error $_.Exception.Message;
break;
}
$status
}
I am using signtool to sign my msi and setup.exe files.
Timestamping failed for most of the msi, Now i would like to timestamp them separately.
How to find whether timestamp is missing or not?
Following cmdlet helps me to find whether it is signed or not
$AuthStatus= (Get-AuthenticodeSignature $FILENAME)
If ($AuthStatus.status -ne "Valid") {
$SIGNTOOL sign /v /f $CERPFX /t $TimestampSRVR /p $PWD $FILENAME
}
Now i need to check whether the msi timestamp is missing or not, How to do it?
Finally i found answer by myself. There is a property named "TimeStamperCertificate " is there it seems. Following is the code snippet.
If the msi is not signed or timestamped , It will sign and timestamp again.
$MsiAuthInfo= (Get-AuthenticodeSignature $FILENAME)
If ($MsiAuthInfo.status -ne "Valid" -or $MsiAuthInfo.TimeStamperCertificate -eq $Null) {
$SIGNTOOL sign /v /f $CERPFX /t $TimestampSRVR /p $PWD $FILENAME
}
Here's a PowerShell solution courtesy of PowerShell MVP Vadims Podans. Get-AuthenticodeSignatureEx adds a SigningTime property to the result, the value is a datetime as generalized time (not local time), you can always call the ToLocalTime() on the datetime object to get the result in your time zone. You can use the following command to quickly test it:
dir $pshome\*.ps1xml | Get-AuthenticodeSignatureEx | ft SignerCertificate,Status,SigningTime,Path
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
}
process
{
Get-AuthenticodeSignature #PSBoundParameters | 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(
1,
$_.Path,
16382,
14,
$null,
[ref]$pdwMsgAndCertEncodingType,
[ref]$pdwContentType,
[ref]$pdwFormatType,
[ref]$phCertStore,
[ref]$phMsg,
[ref]$ppvContext
)
$pcbData = 0
$return = [PKI.Crypt32]::CryptMsgGetParam($phMsg,29,0,$null,[ref]$pcbData)
$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)
foreach ($Infos in $SignedCms.SignerInfos) {
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}
}
}
$Output | Add-Member -MemberType NoteProperty -Name SigningTime -Value $sTime.SigningTime -PassThru -Force
[void][PKI.Crypt32]::CryptMsgClose($phMsg)
[void][PKI.Crypt32]::CertCloseStore($phCertStore,0)
} else {
$Output
}
}
}
}