Geeks With Blogs
Running with Code Like scissors, only more dangerous

One of the neat things Microsoft incorporated into Windows with the release of Internet Explorer 4 (which was provided for Windows 95 and Windows NT 4.0 with Service Pack 3) was the CryptoAPI, which provided not only services for secure hashing and stream ciphers, but also implemented Microsoft's Authenticode (r) code-signing verification.  Authenticode is the technology that allows a Certification Authority (CA) such as Verisign to issue certificates to its clients in order to establish that software has been signed and is still authentic (that is, it has not been modified by any third parties).  This is beneficial for obvious reasons, of course - when you're installing the Flash plugin, for instance, having an Authenticode signature verifies to you that the code is actually from Adobe, and that it hasn't been modified for some malicious purpose by a third party (such as a hacker).

Windows Vista has incorporated this further by presenting User Account Control (UAC) dialogs based on the code-signing policy defined by the system.  When prompting for elevation, Windows Vista chooses one of four dialogs based on the security policy and code signature of the target executable.

As a software developer, verifying code integrity can be important; as a .NET developer, you of course have the Strong Name tool (sn.exe) to generate a private/public key pair with which to grant trust to assemblies (avoiding a trojan horse assembly in the process).  However, there may be times where we want to validate the digital signatures of non-CLR assemblies, macros, or some other entity that was digitally signed.  This will get you started by verifying the digital signature of a file; however, it can be used for other entities as described in the documentation.

For this we will use the WinVerifyTrust API.  WinVerifyTrust takes a handle and two pointers - I hope you're excited for some new adventures in Platform Invoke!  The first thing I put together quickly was an UnmanagedPointer class - by implementing IDisposable, it helps me to avoid memory leaks.  Here's the quick low-down:

   1:          #region UnmanagedPointer class
   2:          internal sealed class UnmanagedPointer : IDisposable
   3:          {
   4:              private IntPtr m_ptr;
   5:              private AllocMethod m_meth;
   6:              internal UnmanagedPointer(IntPtr ptr, AllocMethod method)
   7:              {
   8:                  m_meth = method;
   9:                  m_ptr = ptr;
  10:              }
  12:              ~UnmanagedPointer()
  13:              {
  14:                  Dispose(false);
  15:              }
  17:              #region IDisposable Members
  18:              private void Dispose(bool disposing)
  19:              {
  20:                  if (m_ptr != IntPtr.Zero)
  21:                  {
  22:                      if (m_meth == AllocMethod.HGlobal)
  23:                      {
  24:                          Marshal.FreeHGlobal(m_ptr);
  25:                      }
  26:                      else if (m_meth == AllocMethod.CoTaskMem)
  27:                      {
  28:                          Marshal.FreeCoTaskMem(m_ptr);
  29:                      }
  30:                      m_ptr = IntPtr.Zero;
  31:                  }
  33:                  if (disposing)
  34:                  {
  35:                      GC.SuppressFinalize(this);
  36:                  }
  37:              }
  39:              public void Dispose()
  40:              {
  41:                  Dispose(true);
  42:              }
  44:              #endregion
  46:              public static implicit operator IntPtr(UnmanagedPointer ptr)
  47:              {
  48:                  return ptr.m_ptr;
  49:              }
  50:          }

The AllocMethod enumeration simply specifies the HGlobal and CoTaskMem members, which indicates how Marshal originally allocated the memory.  With this class, I can now drop the memory blocks into using statements to have them automatically cleaned up, dodging a memory leak.

When using file lookups, you need to utilize the WINTRUST_FILE_INFO structure, which can be represented in C# as:

   1:          internal struct WINTRUST_FILE_INFO : IDisposable
   2:          {
   3:              public WINTRUST_FILE_INFO(string fileName, Guid subject)
   4:              {
   5:                  cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_FILE_INFO));
   6:                  pcwszFilePath = fileName;
   8:                  if (subject != Guid.Empty)
   9:                  {
  10:                      pgKnownSubject = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid)));
  11:                      Marshal.StructureToPtr(subject, pgKnownSubject, true);
  12:                  }
  13:                  else
  14:                  {
  15:                      pgKnownSubject = IntPtr.Zero;
  16:                  }
  17:                  hFile = IntPtr.Zero;
  18:              }
  19:              public uint cbStruct;
  20:              [MarshalAs(UnmanagedType.LPTStr)]
  21:              public string pcwszFilePath;
  22:              public IntPtr hFile;
  23:              public IntPtr pgKnownSubject;
  25:              #region IDisposable Members
  27:              public void Dispose()
  28:              {
  29:                  Dispose(true);
  30:              }
  32:              private void Dispose(bool disposing)
  33:              {
  34:                  if (pgKnownSubject != IntPtr.Zero)
  35:                  {
  36:                      Marshal.DestroyStructure(this.pgKnownSubject, typeof(Guid));
  37:                      Marshal.FreeHGlobal(this.pgKnownSubject);
  38:                  }
  39:              }
  41:              #endregion
  42:          }

Note that this class allocates unmanaged memory for the pgKnownSubject member - it is actually a pointer to a GUID structure, not the GUID structure itself.  Since we're not making a P/Invoke call, but a marshalable structure, we can't define a "ref" field.  I could have alternatively used a Guid* pointer type in place of that IntPtr, but I decided to stick with verifiable code.  (Note that this is also true in the upcoming code, where a WINTRUST_FILE_INFO pointer is one of the fields.

Finally, we can build out the actual structure used by the API call, the WINTRUST_DATA structure:

   1:          [StructLayout(LayoutKind.Sequential)]
   2:          internal struct WINTRUST_DATA : IDisposable
   3:          {
   4:              public WINTRUST_DATA(WINTRUST_FILE_INFO fileInfo)
   5:              {
   6:                  this.cbStruct = (uint)Marshal.SizeOf(typeof(WINTRUST_DATA));
   7:                  pInfoStruct = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)));
   8:                  Marshal.StructureToPtr(fileInfo, pInfoStruct, true);
   9:                  this.dwUnionChoice = UnionChoice.File;
  11:                  pPolicyCallbackData = IntPtr.Zero;
  12:                  pSIPCallbackData = IntPtr.Zero;
  14:                  dwUIChoice = UiChoice.NoUI;
  15:                  fdwRevocationChecks = RevocationCheckFlags.None;
  16:                  dwStateAction = StateAction.Ignore;
  17:                  hWVTStateData = IntPtr.Zero;
  18:                  pwszURLReference = IntPtr.Zero;
  19:                  dwProvFlags = TrustProviderFlags.Safer;
  21:                  dwUIContext = UIContext.Execute;
  22:              }
  24:              public uint cbStruct;
  25:              public IntPtr pPolicyCallbackData;
  26:              public IntPtr pSIPCallbackData;
  27:              public UiChoice dwUIChoice;
  28:              public RevocationCheckFlags fdwRevocationChecks;
  29:              public UnionChoice dwUnionChoice;
  30:              public IntPtr pInfoStruct;
  31:              public StateAction dwStateAction;
  32:              public IntPtr hWVTStateData;
  33:              private IntPtr pwszURLReference;
  34:              public TrustProviderFlags dwProvFlags;
  35:              public UIContext dwUIContext;
  37:              #region IDisposable Members
  39:              public void Dispose()
  40:              {
  41:                  Dispose(true);
  42:              }
  44:              private void Dispose(bool disposing)
  45:              {
  46:                  if (dwUnionChoice == UnionChoice.File)
  47:                  {
  48:                      WINTRUST_FILE_INFO info = new WINTRUST_FILE_INFO();
  49:                      Marshal.PtrToStructure(pInfoStruct, info);
  50:                      info.Dispose();
  51:                      Marshal.DestroyStructure(pInfoStruct, typeof(WINTRUST_FILE_INFO));
  52:                  }
  54:                  Marshal.FreeHGlobal(pInfoStruct);
  55:              }
  57:              #endregion
  58:          }

The WINTRUST_DATA structure defines a union where I defined the pInfoStruct field.  It's possible to create something similar to a union in C#, by setting StructLayout as Explicit, and by putting each of the fields in the union at the same field offset.  The drawback is that each field needs an explicit offset, which makes the structure not cross-platform (as 32-bit and 64-bit platforms will require diferrent field offsets).  I decided again to go the path of the IntPtr and do the custom marshaling myself.  You should note I use several enumerations - these are all defined on the API specification for WINTRUST_DATA. 

FINALLY!  We're at the point where we actually can define and call the method.  I do both of these within a class called Interop, and translate it in an external method.  Here are the definitions: 

   1:          internal static uint WinVerifyTrust(string fileName)
   2:          {
   3:              Guid wintrust_action_generic_verify_v2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}");
   4:              WINTRUST_FILE_INFO fileInfo = new WINTRUST_FILE_INFO(fileName, Guid.Empty);
   5:              WINTRUST_DATA data = new WINTRUST_DATA(fileInfo);
   7:              uint result = 0;
   9:              using (UnmanagedPointer guidPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Guid))), AllocMethod.HGlobal))
  10:              using (UnmanagedPointer wvtDataPtr = new UnmanagedPointer(Marshal.AllocHGlobal(Marshal.SizeOf(typeof(WINTRUST_DATA))), AllocMethod.HGlobal))
  11:              {
  12:                  IntPtr pGuid = guidPtr;
  13:                  IntPtr pData = wvtDataPtr;
  15:                  Marshal.StructureToPtr(wintrust_action_generic_verify_v2, pGuid, true);
  16:                  Marshal.StructureToPtr(data, pData, true);
  18:                  result = WinVerifyTrust(IntPtr.Zero, pGuid, pData);
  19:              }
  21:              return result;
  22:          }

The hardest part of utilizing this code is dealing with the myriad of return values (which I will not go over here).  Essentially, if the API returns 0, then the file is signed; if not, you can use Marshal.GetLastError() to determine the results.

Kudos go out to Microsoft for the example C program on which this article is based!

Posted on Friday, May 4, 2007 1:19 PM Toolkits | Back to top

Comments on this post: C#: Determining if a file has a valid digital signature

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
I tried testing the app but I'm missing the definitions for the following types:


What do I need to do to make it work?

Left by Igor on Oct 01, 2007 1:46 PM

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
I think he forgot to include some enum definitions. Adding the following at the top made it work.

enum AllocMethod { HGlobal, CoTaskMem };
enum UnionChoice { File = 1, Catalog, Blob, Signer, Cert };
enum UiChoice { All = 1, NoUI, NoBad, NoGood };
enum RevocationCheckFlags { None = 0, WholeChain };
enum StateAction { Ignore = 0, Verify, Close, AutoCache, AutoCacheFlush };
enum TrustProviderFlags
UseIE4Trust = 1,
NoIE4Chain = 2,
NoPolicyUsage = 4,
RevocationCheckNone = 16,
RevocationCheckEndCert = 32,
RevocationCheckChain = 64,
RecovationCheckChainExcludeRoot = 128,
Safer = 256,
HashOnly = 512,
UseDefaultOSVerCheck = 1024,
LifetimeSigning = 2048
enum UIContext { Execute = 0, Install };

You also need to add the dllimport attribute

[DllImport("Wintrust.dll", PreserveSig = true, SetLastError = false)]
internal static extern uint WinVerifyTrust(IntPtr hWnd, IntPtr pgActionID, IntPtr pWinTrustData);

However, even with all of this, I'm running into memory exceptions at the using (UnmanagedPointer...) calls. It never fails if the file sig is valid. But it always fails after the second call to an unsigned (or invalid signed) file.

Exception Details:

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

Stack Trace:

at System.Runtime.InteropServices.Marshal.StructureToPtr(Object structure, IntPtr ptr, Boolean fDeleteOld)

at CDShell.WINTRUST_DATA..ctor(WINTRUST_FILE_INFO fileInfo) in ...:line 140

at CDShell.WinTrust.WinVerifyTrust(String fileName) in ...:line 203

at CDShell.Form1.ValidHPSignature(String assembly) in ...:line 68

Any thoughts?
Left by Tony Camilli on Nov 09, 2007 5:02 PM

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
In line 8 of the WINTRUST_DATA structure code, changing the third parameter of StructureToPtr to false cleared up the exception. This could lead to a memory leak so I have manually added a call to fileInfo.Dispose() after line 18 of the WinVerifyTrust method.
Left by Tony on Nov 12, 2007 7:22 AM

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
Could you please make a sample code for downloading?
Thank you
Left by Steven on Jan 06, 2008 5:24 AM

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
Hi ,
I am writing managed wrapper for one of native library using p/invoke.
So we have lots of complex data structures in native library.
One of structure is very complex.It contains union of structures.As shown below

typedef struct
UInt32 cbSize; // size in bytes of this structure
TWWAConnectoidType type;
TWWADIALCONNECTOID dial; //another userdefined struct
TWWAACCESSCONNECTOID access;//another user defined struct
} Connectoid;

I am facing similar type of problem as you have solved in this blog.You have shown how to marshal WINTRUST_DATA's union,from managed code to un-managed code vice versa using IntPtr.

But there is subtle difference in my problem.
I have

TWWADIALCONNECTOID dial; //another userdefined struct
TWWAACCESSCONNECTOID access;//another user defined struct
} Connectoid;

in side struct.
But WINTRUST_DATA has union which in turn can contain pointer to struct.But in my case there is no pointer to struct in union.There is user defined type itself.

When I i try to user your IntPtr approach,My application crashes while returning from native code (where it passed as ref parameter).

To summerize My problem is how to marshal structure containg union though p/invoke.
Please could you suggest me ,How can I deal with this problem.

thanks in advance

Left by Abhijit on Jun 17, 2008 5:16 AM

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
Hi, great sample but I have 2 comments :

I've used :
instead of your code which give much more informations like issuer, public key...
Your sample check validity but not names (like displayed in your screenshot) :(

If you look at notepad.exe which is signed using the security catalog, your code return no certificate :/

Any idea to get more informations on both ways ?


Left by Vincent on May 20, 2009 3:48 PM

# davetiyeci
Requesting Gravatar...
güzel davetiye sözleri ve davetiye metinleri
Left by davetiye on Oct 31, 2009 3:24 AM

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
pls attach the method to detect the trojan horse using its signature
Left by leema on Jul 07, 2010 5:07 AM

# re: C#: Determining if a file has a valid digital signature
Requesting Gravatar...
From my experience, dealing with the Winverify Trust API from .Net's perspective is always tricky. This article certainly lends a helping hand.


Left by wesleyb on Oct 04, 2011 9:33 AM

Your comment:
 (will show your gravatar)

Copyright © Robert Paveza | Powered by: