How to sign EXE files with an Authenticode certificate (VB.NET)

Hi, welcome back,

Sorry it took me so long to write a new post. I'm visiting my colleagues at Milano, and I'm horribly busy these days.

For Windows platforms, below are the only supported methods we can use to digitally sign Authenticode executables:

1) We can use the old SignCode.exe tool (shipped with Authenticode tool for IE or SDK or VS.NET) for signing. SignCode.exe is an old tool from Microsoft to sign Authenticode executables (EXE, DLL, MSI). Newer version of the tool is named SignTool.exe. SignTool.exe is shipped with Microsoft Platform SDK. It is preferred to use the newer SignTool.exe tool. Please note that the command line options for SignTool.exe are different from SignCode.exe.

2) We can programmatically use CryptUIWizDigitalSign API for signing Authenticode executables (MSI, EXE, DLL) (Note this API is only available on Windows XP or above). Additionally, CAPICOM.SignedCode essentially does the same thing.

The following sample shows how to use CryptUIWizDigitalSign in .NET 2.0 through P/Invoke:

<SAMPLE file="Crypto.vb">

 Imports System.Runtime.InteropServices
Imports System.Security.Cryptography
Imports System.ComponentModel
Imports System.Windows.Forms

Public Class Crypto

    ' #define CRYPTUI_WIZ_NO_UI     1
    Public Const CRYPTUI_WIZ_NO_UI As Int32 = 1

    ' #define CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE     0x01
    Public Const CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE As Int32 = 1

    ' #define CRYPTUI_WIZ_DIGITAL_SIGN_CERT                    0x01
    Public Const CRYPTUI_WIZ_DIGITAL_SIGN_CERT As Int32 = 1

    ' typedef struct _CRYPTUI_WIZ_DIGITAL_SIGN_INFO {  
    '   DWORD dwSize;  
    '   DWORD dwSubjectChoice;  
    '   union {    
    '       LPCWSTR pwszFileName;    
    '       PCCRYPTUI_WIZ_DIGITAL_SIGN_BLOB_INFO pSignBlobInfo;  
    '   };  
    '   DWORD dwSigningCertChoice;  
    '   union {    
    '       PCCERT_CONTEXT pSigningCertContext;    
    '       PCCRYPTUI_WIZ_DIGITAL_SIGN_STORE_INFO pSigningCertStore;    
    '       PCCRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pSigningCertPvkInfo;  
    '   };  
    '   LPCWSTR pwszTimestampURL;  
    '   DWORD dwAdditionalCertChoice;  
    '   PCCRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO pSignExtInfo;
    ' } CRYPTUI_WIZ_DIGITAL_SIGN_INFO;
    <StructLayout(LayoutKind.Sequential)> _
    Public Structure CRYPTUI_WIZ_DIGITAL_SIGN_INFO
        Public dwSize As Int32
        Public dwSubjectChoice As Int32
        <MarshalAs(UnmanagedType.LPWStr)> Public pwszFileName As String
        Public dwSigningCertChoice As Int32
        Public pSigningCertContext As IntPtr
        Public pwszTimestampURL As String
        Public dwAdditionalCertChoice As Int32
        Public pSignExtInfo As IntPtr
    End Structure

    ' typedef struct _CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT {  
    '      DWORD dwSize;  
    '      DWORD cbBlob;  
    '      BYTE* pbBlob;
    ' } CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT;
    <StructLayout(LayoutKind.Sequential)> _
    Public Structure CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
        Public dwSize As Int32
        Public cbBlob As Int32
        Public pbBlob As IntPtr
    End Structure

    ' BOOL WINAPI CryptUIWizDigitalSign(
    '      DWORD dwFlags,
    '      HWND hwndParent,
    '      LPCWSTR pwszWizardTitle,
    '      PCCRYPTUI_WIZ_DIGITAL_SIGN_INFO pDigitalSignInfo,
    '      PCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT* ppSignContext
    ' );
    <DllImport("Cryptui.dll", CharSet:=CharSet.Unicode, SetLastError:=True)> _
    Public Shared Function CryptUIWizDigitalSign( _
        ByVal dwFlags As Int32, _
        ByVal hwndParent As IntPtr, _
        ByVal pwszWizardTitle As String, _
        ByRef pDigitalSignInfo As CRYPTUI_WIZ_DIGITAL_SIGN_INFO, _
        ByRef ppSignContext As IntPtr _
    ) As Boolean
    End Function

    ' BOOL WINAPI CryptUIWizFreeDigitalSignContext(
    '   PCCRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT pSignContext
    ' );
    <DllImport("Cryptui.dll", CharSet:=CharSet.Auto, SetLastError:=True)> _
    Public Shared Function CryptUIWizFreeDigitalSignContext( _
        ByVal pSignContext As IntPtr _
    ) As Boolean
    End Function

End Class

</SAMPLE>

<SAMPLE file="Module1.vb">

 Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports SignExe.Crypto
Imports System.Security.Cryptography.X509Certificates
Imports System.IO

Module Module1

    Sub Main()
        ' Variables
        '
        Dim cert As X509Certificate2
        Dim digitalSignInfo As CRYPTUI_WIZ_DIGITAL_SIGN_INFO
        Dim pSignContext As IntPtr
        Dim pSigningCertContext As IntPtr
        Dim signContext As CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT
        Dim fileOut As FileStream
        Dim binWriter As BinaryWriter
        Dim blob() As Byte

        Try
            ' Get certificate context
            '
            cert = New X509Certificate2("C:\Test\MyCert.pfx", "")
            pSigningCertContext = cert.Handle

            ' Prepare signing info: exe and cert
            '
            digitalSignInfo = New CRYPTUI_WIZ_DIGITAL_SIGN_INFO
            digitalSignInfo.dwSize = Marshal.SizeOf(digitalSignInfo)
            digitalSignInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE
            digitalSignInfo.pwszFileName = "C:\Test\notepad.exe"
            digitalSignInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_CERT
            digitalSignInfo.pSigningCertContext = pSigningCertContext
            digitalSignInfo.pwszTimestampURL = vbNullString
            digitalSignInfo.dwAdditionalCertChoice = 0
            digitalSignInfo.pSignExtInfo = IntPtr.Zero

            ' Sign exe
            '
            If (Not CryptUIWizDigitalSign( _
                CRYPTUI_WIZ_NO_UI, _
                IntPtr.Zero, _
                vbNullString, _
                digitalSignInfo, _
                pSignContext _
            )) Then
                Throw New Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizDigitalSign")
            End If

            ' Get the blob with the signature
            '
            signContext = Marshal.PtrToStructure(pSignContext, GetType(CRYPTUI_WIZ_DIGITAL_SIGN_CONTEXT))
            blob = New Byte(signContext.cbBlob) {}
            Marshal.Copy(signContext.pbBlob, blob, 0, signContext.cbBlob)

            ' Store the signature in a new file
            '
            fileOut = File.Open("C:\Test\signature.sig", FileMode.Create)
            binWriter = New BinaryWriter(fileOut)
            binWriter.Write(blob)
            binWriter.Close()
            fileOut.Close()

            ' Free blob
            '
            If (Not CryptUIWizFreeDigitalSignContext(pSignContext)) Then
                Throw New Win32Exception(Marshal.GetLastWin32Error(), "CryptUIWizFreeDigitalSignContext")
            End If

            ' We are done
            Console.WriteLine("Done!!!")

        Catch ex As Win32Exception
            ' Any expected errors?
            '
            Console.WriteLine(ex.Message + " error#" + ex.NativeErrorCode.ToString)
        Catch ex As Exception
            ' Any unexpected errors?
            '
            Console.WriteLine(ex.Message)
        End Try

        ' We are done
        '
        Console.WriteLine("<< Press any key to continue >>")
        Console.ReadKey()

    End Sub

End Module

</SAMPLE>

 

I hope this helps.

Cheers,

 

Alex (Alejandro Campos Magencio)

 

PS: You may find more info and samples about this on How to sign EXE files with an Authenticode certificate (part 2)