Skip to content

Instantly share code, notes, and snippets.

@ahmedosama007
Created April 28, 2022 01:50
Show Gist options
  • Save ahmedosama007/87bbfe4ee3b10fee12c02ac111e1794a to your computer and use it in GitHub Desktop.
Save ahmedosama007/87bbfe4ee3b10fee12c02ac111e1794a to your computer and use it in GitHub Desktop.
Verifies the file digital signature embedded in the file or located in a Windows security catalog using WinVerifyTrust API
'Copyright (c) Smart PC Utilities, Ltd.
'All rights reserved.
Imports System.Runtime.InteropServices
Namespace OsUtils.WinTrust
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Public Structure CatalogInfo
Public cbStruct As Integer
<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=256)> Public wszCatalogFile As String
End Structure
End Namespace
'Copyright (c) Smart PC Utilities, Ltd.
'All rights reserved.
Namespace OsUtils.WinTrust
#Disable Warning BC40032 ' Underlying type of Enum is not CLS-compliant
''' <summary>
''' Certificate revocation check options
''' </summary>
Public Enum WinTrustDataRevocationCheck As UInteger
#Enable Warning BC40032 ' Underlying type of Enum is not CLS-compliant
''' <summary>
''' No revocation check
''' </summary>
None = &H0
''' <summary>
''' Revocation check will be done on the whole chain
''' </summary>
WholeChain = &H1
End Enum
#Disable Warning BC40032 ' Underlying type of Enum is not CLS-compliant
Public Enum WinVerifyTrustResult As UInteger
#Enable Warning BC40032 ' Underlying type of Enum is not CLS-compliant
''' <summary>
''' SUCCESS
''' </summary>
Success = 0
''' <summary>
''' Trust provider is not recognized on this system
''' </summary>
ProviderUnknown = 2148204545
''' <summary>
''' Trust provider does not support the specified action
''' </summary>
ActionUnknown = 2148204546
''' <summary>
''' Trust provider does not support the subject's form
''' </summary>
SubjectFormUnknown = 2148204547
''' <summary>
''' Subject failed the specified verification action
''' </summary>
SubjectNotTrusted = 2148204548
''' <summary>
''' File is not signed. (TRUST_E_NOSIGNATURE)
''' </summary>
FileNotSigned = 2148204800
''' <summary>
''' Signer's certificate is in the Untrusted Publishers store
''' </summary>
SubjectExplicitlyDistrusted = 2148204817
''' <summary>
''' File is probably corrupt. (TRUST_E_BAD_DIGEST)
''' </summary>
SignatureOrFileCorrupt = 2148098064
''' <summary>
''' Signer's certificate was expired. (CERT_E_EXPIRED)
''' </summary>
SubjectCertExpired = 2148204801
''' <summary>
''' Subject's certificate was revoked. (CERT_E_REVOKED)
''' </summary>
SubjectCertificateRevoked = 2148204812
''' <summary>
''' A certification chain processed correctly but terminated in a root certificate that is not trusted by the trust provider. (CERT_E_UNTRUSTEDROOT)
''' </summary>
UntrustedRoot = 2148204809
End Enum
Friend Enum WinTrustDataUIChoice As UInteger
''' <summary>
''' Display all UI (WTD_UI_ALL)
''' </summary>
All = 1
''' <summary>
''' Display no UI (WTD_UI_NONE)
''' </summary>
None = 2
''' <summary>
''' Do not display any negative UI. (WTD_UI_NOBAD)
''' </summary>
NoBad = 3
''' <summary>
''' Do not display any positive UI. (WTD_UI_NOGOOD)
''' </summary>
NoGood = 4
End Enum
''' <summary>
''' Specifies the union member to be used and, thus, the type of object for which trust will be verified.
''' </summary>
Friend Enum WinTrustDataUnionChoice As UInteger
''' <summary>
''' File (WTD_CHOICE_FILE)
''' </summary>
File = 1
''' <summary>
''' Windows Security Catalog (WTD_CHOICE_CATALOG)
''' </summary>
Catalog = 2
''' <summary>
''' Blob (WTD_CHOICE_BLOB)
''' </summary>
Blob = 3
''' <summary>
''' Use the WINTRUST_SGNR_INFO structure pointed to by pSgnr. (WTD_CHOICE_SIGNER)
''' </summary>
Signer = 4
''' <summary>
''' Certificate (WTD_CHOICE_CERT)
''' </summary>
Certificate = 5
End Enum
''' <summary>
''' Specifies the action to be taken
''' </summary>
Friend Enum WinTrustDataStateAction As UInteger
''' <summary>
''' Ignore the hWVTStateData member. (WTD_STATEACTION_IGNORE)
''' </summary>
Ignore = &H0
''' <summary>
''' WTD_STATEACTION_VERIFY <br />
''' Verify the trust of the object (typically a file) that is specified by the dwUnionChoice member. <br />
''' The hWVTStateData member will receive a handle to the state data. This handle must be freed by specifying the WTD_STATEACTION_CLOSE action in a subsequent call.
''' </summary>
Verify = &H1
''' <summary>
''' WTD_STATEACTION_CLOSE <br />
''' Free the hWVTStateData member previously allocated with the WTD_STATEACTION_VERIFY action.<br />
''' This action must be specified for every use of the WTD_STATEACTION_VERIFY action.
''' </summary>
Close = &H2
''' <summary>
''' WTD_STATEACTION_AUTO_CACHE <br />
''' Write the catalog data to a WINTRUST_DATA structure and then cache that structure. <br />
''' This action only applies when the dwUnionChoice member contains WTD_CHOICE_CATALOG.
''' </summary>
AutoCache = &H3
''' <summary>
''' WTD_STATEACTION_AUTO_CACHE_FLUSH <br />
''' Flush any cached catalog data. This action only applies when the dwUnionChoice member contains WTD_CHOICE_CATALOG.
''' </summary>
AutoCacheFlush = &H4
End Enum
''' <summary>
''' Trust provider settings
''' </summary>
<Flags> Friend Enum WinTrustDataProvFlags As UInteger
UseIe4TrustFlag = &H1
NoIe4ChainFlag = &H2
NoPolicyUsageFlag = &H4
''' <summary>
''' Revocation checking is not performed.
''' </summary>
RevocationCheckNone = &H10
''' <summary>
''' Revocation checking is performed on the end certificate only.
''' </summary>
RevocationCheckEndCert = &H20
''' <summary>
''' Revocation checking is performed on the entire certificate chain.
''' </summary>
RevocationCheckChain = &H40
''' <summary>
''' Revocation checking is performed on the entire certificate chain, excluding the root certificate.
''' </summary>
RevocationCheckChainExcludeRoot = &H80
SaferFlag = &H100
''' <summary>
''' Only the hash is verified.
''' </summary>
HashOnlyFlag = &H200
''' <summary>
''' The default operating system version checking is performed. This flag is only used for verifying catalog-signed files.
''' </summary>
UseDefaultOsverCheck = &H400
LifetimeSigningFlag = &H800
''' <summary>
''' Use only the local cache for revocation checks. Prevents revocation checks over the network. it affects CRL retrieval and AIA retrieval
''' </summary>
CacheOnlyUrlRetrieval = &H1000
End Enum
''' <summary>
''' Specifies the user interface context for the WinVerifyTrust function.
''' </summary>
Friend Enum WinTrustDataUIContext As UInteger
''' <summary>
''' Use when calling WinVerifyTrust for a file that is to be run. This is the default value. (WTD_UICONTEXT_EXECUTE)
''' </summary>
Execute = 0
''' <summary>
''' Use when calling WinVerifyTrust for a file that is to be installed. (WTD_UICONTEXT_INSTALL)
''' </summary>
Install = 1
End Enum
End Namespace
'Copyright (c) Smart PC Utilities, Ltd.
'All rights reserved.
Imports System.Runtime.InteropServices
Imports SmartPCUtilities.ServicesOptimizer.Core.OsUtils.WinTrust
Friend Class NativeMethods
<DllImport("wintrust.dll", ExactSpelling:=True, SetLastError:=True, CharSet:=CharSet.Unicode, EntryPoint:="WinVerifyTrustEx")>
Friend Shared Function WinVerifyTrustEx(<[In]> hwnd As IntPtr, <[In]> <MarshalAs(UnmanagedType.LPStruct)> pgActionID As Guid, <[In]> pWVTData As WinTrustData) As WinVerifyTrustResult
End Function
<DllImport("wintrust.dll", CharSet:=CharSet.Unicode, SetLastError:=True)>
Friend Shared Function CryptCATAdminAcquireContext2(ByRef phCatAdmin As IntPtr, pgSubsystem As IntPtr, <MarshalAs(UnmanagedType.LPWStr)> pwszHashAlgorithm As String, pStrongHashPolicy As IntPtr, dwFlags As Long) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("wintrust.dll", SetLastError:=True, CharSet:=CharSet.Unicode)>
Friend Shared Function CryptCATAdminCalcHashFromFileHandle2(hCatAdmin As IntPtr, hFile As IntPtr, <[In], Out> ByRef pcbHash As Integer, pbHash As Byte(), dwFlags As Long) As <MarshalAs(UnmanagedType.Bool)> Boolean
End Function
<DllImport("wintrust.dll", SetLastError:=True)>
Public Shared Function CryptCATAdminEnumCatalogFromHash(<[In]> hCatAdmin As IntPtr, <[In]> pbHash As Byte(), <[In]> cbHash As Integer, <[In]> dwFlags As Integer, <[In]> phPrevCatInfo As IntPtr) As IntPtr
End Function
<DllImport("wintrust.dll", SetLastError:=True)>
Public Shared Function CryptCATAdminReleaseContext(<[In]> hCatAdmin As IntPtr, <[In]> dwFlags As Integer) As Boolean
End Function
<DllImport("wintrust.dll", SetLastError:=True)>
Public Shared Function CryptCATAdminReleaseCatalogContext(<[In]> hCatAdmin As IntPtr, <[In]> hCatInfo As IntPtr, <[In]> dwFlags As Integer) As Boolean
End Function
<DllImport("wintrust.dll", SetLastError:=True)>
Public Shared Function CryptCATCatalogInfoFromContext(<[In]> hCatInfo As IntPtr, <Out> ByRef psCatInfo As CatalogInfo, <[In]> dwFlags As Integer) As Boolean
End Function
End Class
'Copyright (c) Smart PC Utilities, Ltd.
'All rights reserved.
Imports System.IO
Imports System.Text
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Security.Cryptography.X509Certificates
Namespace OsUtils.WinTrust
Public NotInheritable Class SignVerify
''' <summary>
''' Verify a file or object using the Authenticode policy provider
''' </summary>
Private Shared ReadOnly WinTrustActionGenericVerifyV2 As New Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}")
Private Shared ReadOnly InvalidHandleValue As New IntPtr(-1)
''' <summary>
''' Verifies the file digital signature embedded in the file or located in a Windows security catalog
''' </summary>
''' <param name="filePath">The target file path</param>
''' <param name="revokeCheck">Whether to check certificate revocation</param>
''' <param name="errInfo">Returns error info if any</param>
Public Shared Function VerifyFileSignature(filePath As String, revokeCheck As WinTrustDataRevocationCheck, ByRef errInfo As Exception) As WinVerifyTrustResult
Dim fs As FileStream = Nothing
Dim pCatAdmin = IntPtr.Zero
Dim pCatInfo = IntPtr.Zero
Dim signResult As WinVerifyTrustResult = WinVerifyTrustResult.FileNotSigned
Try
If Not File.Exists(filePath) Then Throw New FileNotFoundException("The specified file does not exist.", filePath)
Dim wtdFile = New WinTrustData(filePath) With {.fdwRevocationChecks = revokeCheck}
signResult = NativeMethods.WinVerifyTrustEx(InvalidHandleValue, WinTrustActionGenericVerifyV2, wtdFile)
If signResult <> WinVerifyTrustResult.FileNotSigned Then Return signResult
fs = Utils.CreateFileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, errInfo)
If fs Is Nothing Then Throw errInfo
If Not NativeMethods.CryptCATAdminAcquireContext2(pCatAdmin, Nothing, "SHA256", Nothing, 0) Then
Throw New Win32Exception(Marshal.GetLastWin32Error)
End If
Dim fileHandle As IntPtr = fs.SafeFileHandle.DangerousGetHandle
Dim hashLength As Integer = 256
Dim hash As Byte() = New Byte(256) {}
If Not NativeMethods.CryptCATAdminCalcHashFromFileHandle2(pCatAdmin, fileHandle, hashLength, hash, 0) Then
Throw New Win32Exception(Marshal.GetLastWin32Error)
End If
Dim memberTag = New StringBuilder(hashLength * 2)
For i As Integer = 0 To hashLength - 1
memberTag.Append(hash(i).ToString("X2"))
Next
pCatInfo = NativeMethods.CryptCATAdminEnumCatalogFromHash(pCatAdmin, hash, hashLength, 0, Nothing)
If pCatInfo = IntPtr.Zero Then Throw New Win32Exception(Marshal.GetLastWin32Error)
Dim catInfo As CatalogInfo = Nothing
If Not NativeMethods.CryptCATCatalogInfoFromContext(pCatInfo, catInfo, 0) Then
Throw New Win32Exception(Marshal.GetLastWin32Error)
End If
Dim wtCatInfo As New WintrustCatalogInfo With {
.pcwszCatalogFilePath = catInfo.wszCatalogFile,
.pcwszMemberFilePath = filePath,
.pcwszMemberTag = memberTag.ToString(),
.hMemberFile = fileHandle,
.hCatAdmin = pCatAdmin
}
Dim wtdCat As New WinTrustData() With {
.dwUnionChoice = WinTrustDataUnionChoice.Catalog,
.fdwRevocationChecks = revokeCheck}
wtdCat.dwProvFlags = wtdCat.dwProvFlags Or WinTrustDataProvFlags.UseDefaultOsverCheck
wtdCat.unionData = Marshal.AllocCoTaskMem(Marshal.SizeOf(GetType(WintrustCatalogInfo)))
Marshal.StructureToPtr(wtCatInfo, wtdCat.unionData, False)
signResult = NativeMethods.WinVerifyTrustEx(InvalidHandleValue, WinTrustActionGenericVerifyV2, wtdCat)
Return signResult
Catch ex As Exception
errInfo = ex
Return signResult
Finally
If pCatAdmin <> IntPtr.Zero Then
If pCatInfo <> IntPtr.Zero Then NativeMethods.CryptCATAdminReleaseCatalogContext(pCatAdmin, pCatInfo, 0)
NativeMethods.CryptCATAdminReleaseContext(pCatAdmin, 0)
End If
If fs IsNot Nothing Then fs.Close()
End Try
End Function
End Class
End Namespace
'Copyright (c) Smart PC Utilities, Ltd.
'All rights reserved.
Imports System.Runtime.InteropServices
Namespace OsUtils.WinTrust
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Friend Class WintrustCatalogInfo
''' <summary>
''' Size, in bytes, of this structure.
''' </summary>
Public cbStruct As Integer
''' <summary>
''' Optional. Catalog version number.
''' </summary>
Public dwCatalogVersion As Integer
''' <summary>
''' The full path and file name of the catalog file that contains the member to be verified.
''' </summary>
Public pcwszCatalogFilePath As String
''' <summary>
''' Tag of a member file to be verified.
''' </summary>
Public pcwszMemberTag As String
''' <summary>
''' The full path and file name of the catalog member file to be verified.
''' </summary>
Public pcwszMemberFilePath As String
''' <summary>
''' Optional. Handle of the open catalog member file to be verified. The handle must be to a file with at least read permissions.
''' </summary>
Public hMemberFile As IntPtr
''' <summary>
''' Optional. The calculated hash of the file that contains the file to be verified.
''' </summary>
Public pbCalculatedFileHash As Byte()
''' <summary>
''' The size, in bytes, of the value passed in the pbCalculatedFileHash member. cbCalculatedFileHash is used only if the calculated hash is being passed.
''' </summary>
Public cbCalculatedFileHash As Integer
''' <summary>
''' A pointer to a CTL_CONTEXT structure that represents a catalog context to be used instead of a catalog file.
''' </summary>
Public pcCatalogContext As IntPtr
''' <summary>
''' Handle to the catalog administrator context that was used when calculating the hash of the file. This value can be zero only for a SHA1 file hash.Windows 8 and Windows Server 2012: Support for this member begins.
''' </summary>
Public hCatAdmin As IntPtr
Public Sub New()
cbStruct = Marshal.SizeOf(GetType(WintrustCatalogInfo))
End Sub
End Class
End Namespace
'Copyright (c) Smart PC Utilities, Ltd.
'All rights reserved.
Imports System.Runtime.InteropServices
Namespace OsUtils.WinTrust
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Friend NotInheritable Class WinTrustData
Public cbStruct As Integer
Public pPolicyCallbackData As IntPtr = IntPtr.Zero
Public pSIPClientData As IntPtr = IntPtr.Zero
Public dwUIChoice As WinTrustDataUIChoice = WinTrustDataUIChoice.None
Public fdwRevocationChecks As WinTrustDataRevocationCheck = WinTrustDataRevocationCheck.WholeChain
Public dwUnionChoice As WinTrustDataUnionChoice
Public unionData As IntPtr
Public dwStateAction As WinTrustDataStateAction = WinTrustDataStateAction.Ignore
Public hWVTStateData As IntPtr = IntPtr.Zero
Public pwszURLReference As String = Nothing
Public dwProvFlags As WinTrustDataProvFlags = WinTrustDataProvFlags.CacheOnlyUrlRetrieval
Public dwUIContext As WinTrustDataUIContext = WinTrustDataUIContext.Execute
Public pSignatureSettings As Integer
Public Sub New()
cbStruct = Marshal.SizeOf(GetType(WinTrustData))
End Sub
Public Sub New(fileName As String)
cbStruct = Marshal.SizeOf(GetType(WinTrustData))
dwUnionChoice = WinTrustDataUnionChoice.File
Dim fileInfo = New WinTrustFileInfo(fileName)
unionData = Marshal.AllocCoTaskMem(Marshal.SizeOf(fileInfo))
Marshal.StructureToPtr(fileInfo, unionData, False)
End Sub
Protected Overrides Sub Finalize()
Marshal.FreeCoTaskMem(unionData)
End Sub
End Class
End Namespace
'Copyright (c) Smart PC Utilities, Ltd.
'All rights reserved.
Imports System.Runtime.InteropServices
Namespace OsUtils.WinTrust
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)>
Friend NotInheritable Class WinTrustFileInfo
''' <summary>
''' Count of bytes in this structure.
''' </summary>
Private ReadOnly cbStruct As Integer = Marshal.SizeOf(GetType(WinTrustFileInfo))
''' <summary>
''' Full path and file name of the file to be verified. This parameter cannot be NULL.
''' </summary>
Private ReadOnly pcwszFilePath As IntPtr
''' <summary>
''' Optional. File handle to the open file to be verified. This handle must be to a file that has at least read permission. This member can be set to NULL.
''' </summary>
Private ReadOnly hFile As IntPtr = IntPtr.Zero
''' <summary>
''' Optional. Pointer to a GUID structure that specifies the subject type. This member can be set to NULL.
''' </summary>
Private ReadOnly pgKnownSubject As IntPtr = IntPtr.Zero
Public Sub New(filePath As String)
pcwszFilePath = Marshal.StringToCoTaskMemAuto(filePath)
End Sub
Protected Overrides Sub Finalize()
Marshal.FreeCoTaskMem(pcwszFilePath)
End Sub
End Class
End Namespace
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment