Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Short question

How can I get information about multiple code signing certificates from an executable (.EXE/.DLL)?

Expected answer

The final accepted answer should propose a way to get all certificates in C#. Concept / pseudo code is ok, I don't expect you to write the full source.

For an intermediate answer suggesting a tool, please see my question on Security.StackExchange.

Long question

I am researching whether we could use multiple code signing certificates on a plugin (.DLL) to check whether it has been officially tested or not. This is the procedure:

  • the plugin DLL is signed by the vendor just like any other application
  • the plugin DLL comes into a test lab and undergoes a set of tests
  • the plugin DLL gets signed again by the test lab so that the application using the DLL can find out whether it is using a tested plugin or not

It seems possible to sign a DLL a second time using

 signtool /v /f <pfx> /as <dll>

Indications that this may have worked:

  • the file increases in size
  • the tool prints a success message

However, there are some issues showing the second signature:

  • although Windows Explorer says "Signature list", it shows only one certificate
  • the C# X509Certificate.CreateFromSignedFile() method can only return one certificate

At the moment I'm actually trying my code on an EXE file rather than a DLL file, but that shouldn't matter. The EXE is already signed with a trusted root certificate and a timestamp. The second signature is created with my own certificate following these steps currently without a timestamp.

Things I did before asking the question:

  • search on Stackoverflow for existing answers
  • search for tools on Google

The only related question I found so far is How does one correctly dual-sign with a timestamp but it doesn't have an answer.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
204 views
Welcome To Ask or Share your Answers For Others

1 Answer

I have recently implemented code to do this myself. I can't post the full solution as it is embedded in a larger static analysis tool, but the code for a working bare-bones C# console application that enumerates the Authenticode signatures in a specified file path is provided below using the WinVerifyTrust() Windows API function with assistance from this Knowledge Base article.

Things to note:

  1. Enumerating more than one certificate is only supported on Windows 8 and Windows Server 2012 (or later). Earlier versions of Windows will only ever report there being zero or one certificates.
  2. The code as provided here only handles validly signed files and files with no Authenticode signature. It does not properly handle files with invalid signatures. This is left as an excercise for the reader.

Here's the code:

using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace ReadAuthenticodeSignatures
{
    internal static class Program
    {
        internal static void Main(string[] args)
        {
            string fileName = args[0];

            IntPtr hWind = IntPtr.Zero;

            Guid WINTRUST_ACTION_GENERIC_VERIFY_V2 = new Guid("{00AAC56B-CD44-11d0-8CC2-00C04FC295EE}");
            byte[] actionIdBytes = WINTRUST_ACTION_GENERIC_VERIFY_V2.ToByteArray();

            IntPtr pcwszFilePath = Marshal.StringToHGlobalAuto(fileName);

            try
            {
                WINTRUST_FILE_INFO File = new WINTRUST_FILE_INFO()
                {
                    cbStruct = Marshal.SizeOf(typeof(WINTRUST_FILE_INFO)),
                    pcwszFilePath = pcwszFilePath,
                    hFile = IntPtr.Zero,
                    pgKnownSubject = IntPtr.Zero,
                };

                IntPtr ptrFile = Marshal.AllocHGlobal(File.cbStruct);

                try
                {
                    Marshal.StructureToPtr(File, ptrFile, false);

                    WINTRUST_DATA WVTData = new WINTRUST_DATA()
                    {
                        cbStruct = Marshal.SizeOf(typeof(WINTRUST_DATA)),
                        pPolicyCallbackData = IntPtr.Zero,
                        pSIPClientData = IntPtr.Zero,
                        dwUIChoice = WTD_UI_NONE,
                        fdwRevocationChecks = WTD_REVOKE_NONE,
                        dwUnionChoice = WTD_CHOICE_FILE,
                        pFile = ptrFile,
                        dwStateAction = WTD_STATEACTION_IGNORE,
                        hWVTStateData = IntPtr.Zero,
                        pwszURLReference = IntPtr.Zero,
                        dwProvFlags = WTD_REVOCATION_CHECK_NONE,
                        dwUIContext = WTD_UICONTEXT_EXECUTE,
                        pSignatureSettings = IntPtr.Zero,
                    };

                    // N.B. Use of this member is only supported on Windows 8 and Windows Server 2012 (and later)
                    WINTRUST_SIGNATURE_SETTINGS signatureSettings = default(WINTRUST_SIGNATURE_SETTINGS);

                    bool canUseSignatureSettings = Environment.OSVersion.Version > new Version(6, 2, 0, 0);
                    IntPtr pSignatureSettings = IntPtr.Zero;

                    if (canUseSignatureSettings)
                    {
                        // Setup WINTRUST_SIGNATURE_SETTINGS to get the number of signatures in the file
                        signatureSettings = new WINTRUST_SIGNATURE_SETTINGS()
                        {
                            cbStruct = Marshal.SizeOf(typeof(WINTRUST_SIGNATURE_SETTINGS)),
                            dwIndex = 0,
                            dwFlags = WSS_GET_SECONDARY_SIG_COUNT,
                            cSecondarySigs = 0,
                            dwVerifiedSigIndex = 0,
                            pCryptoPolicy = IntPtr.Zero,
                        };

                        pSignatureSettings = Marshal.AllocHGlobal(signatureSettings.cbStruct);
                    }

                    try
                    {
                        if (pSignatureSettings != IntPtr.Zero)
                        {
                            Marshal.StructureToPtr(signatureSettings, pSignatureSettings, false);
                            WVTData.pSignatureSettings = pSignatureSettings;
                        }

                        IntPtr pgActionID = Marshal.AllocHGlobal(actionIdBytes.Length);

                        try
                        {
                            Marshal.Copy(actionIdBytes, 0, pgActionID, actionIdBytes.Length);

                            IntPtr pWVTData = Marshal.AllocHGlobal(WVTData.cbStruct);

                            try
                            {
                                Marshal.StructureToPtr(WVTData, pWVTData, false);

                                int hRESULT = WinVerifyTrust(hWind, pgActionID, pWVTData);

                                if (hRESULT == 0)
                                {
                                    if (pSignatureSettings != IntPtr.Zero)
                                    {
                                        // Read back the signature settings
                                        signatureSettings = (WINTRUST_SIGNATURE_SETTINGS)Marshal.PtrToStructure(pSignatureSettings, typeof(WINTRUST_SIGNATURE_SETTINGS));
                                    }

                                    int signatureCount = signatureSettings.cSecondarySigs + 1;

                                    Console.WriteLine("File: {0}", fileName);
                                    Console.WriteLine("Authenticode signatures: {0}", signatureCount);
                                    Console.WriteLine();

                                    for (int dwIndex = 0; dwIndex < signatureCount; dwIndex++)
                                    {
                                        if (pSignatureSettings != IntPtr.Zero)
                                        {
                                            signatureSettings.dwIndex = dwIndex;
                                            signatureSettings.dwFlags = WSS_VERIFY_SPECIFIC;

                                            Marshal.StructureToPtr(signatureSettings, pSignatureSettings, false);
                                        }

                                        WVTData.dwStateAction = WTD_STATEACTION_VERIFY;
                                        WVTData.hWVTStateData = IntPtr.Zero;

                                        Marshal.StructureToPtr(WVTData, pWVTData, false);

                                        hRESULT = WinVerifyTrust(hWind, pgActionID, pWVTData);

                                        try
                                        {
                                            if (hRESULT == 0)
                                            {
                                                WVTData = (WINTRUST_DATA)Marshal.PtrToStructure(pWVTData, typeof(WINTRUST_DATA));

                                                IntPtr ptrProvData = WTHelperProvDataFromStateData(WVTData.hWVTStateData);

                                                CRYPT_PROVIDER_DATA provData = (CRYPT_PROVIDER_DATA)Marshal.PtrToStructure(ptrProvData, typeof(CRYPT_PROVIDER_DATA));

                                                for (int idxSigner = 0; idxSigner < provData.csSigners; idxSigner++)
                                                {
                                                    IntPtr ptrProvSigner = WTHelperGetProvSignerFromChain(ptrProvData, idxSigner, false, 0);

                                                    CRYPT_PROVIDER_SGNR ProvSigner = (CRYPT_PROVIDER_SGNR)Marshal.PtrToStructure(ptrProvSigner, typeof(CRYPT_PROVIDER_SGNR));
                                                    CMSG_SIGNER_INFO Signer = (CMSG_SIGNER_INFO)Marshal.PtrToStructure(ProvSigner.psSigner, typeof(CMSG_SIGNER_INFO));

                                                    if (Signer.HashAlgorithm.pszObjId != IntPtr.Zero)
                                                    {
                                                        string objId = Marshal.PtrToStringAnsi(Signer.HashAlgorithm.pszObjId);

                                                        if (objId != null)
                                                        {
                                                            Oid hashOid = Oid.FromOidValue(objId, OidGroup.All);

                                                            if (hashOid != null)
                                                            {
                                                                Console.WriteLine("Hash algorithm of signature {0}: {1}.", dwIndex + 1, hashOid.FriendlyName);
                                                            }
                                                        }
                                                    }

                                                    IntPtr ptrCert = WTHelperGetProvCertFromChain(ptrProvSigner, idxSigner);

                                                    CRYPT_PROVIDER_CERT cert = (CRYPT_PROVIDER_CERT)Marshal.PtrToStructure(ptrCert, typeof(CRYPT_PROVIDER_CERT));

                                                    if (cert.cbStruct > 0)
                                                    {
                                                        X509Certificate2 certificate = new X509Certificate2(cert.pCert);
                                                        Console.WriteLine("Certificate thumbprint of signature {0}: {1}", dwIndex + 1, certificate.Thumbprint);
                                                    }

                                                    if (ProvSigner.sftVerifyAsOf.dwHighDateTime != provData.sftSystemTime.dwHighDateTime &&
                                                        ProvSigner.sftVerifyAsOf.dwLowDateTime != provData.sftSystemTime.dwLowDateTime)
                                                    {
                                                        DateTime timestamp = DateTime.FromFileTimeUtc(((long)ProvSigner.sftVerifyAsOf.dwHighDateTime << 32) | (uint)ProvSigner.sftVerifyAsOf.dwLowDateTime);
                                                        Console.WriteLine("Timestamp of signature {0}: {1}", dwIndex + 1, timestamp);
                                                    }
                                                }
                                            }
                                        }
                                        finally
                                        {
                                            WVTData.dwStateAction = WTD_STATEACTION_CLOSE;
                                            Marshal.StructureToPtr(WVTData, pWVTData, false);

                                            hRESULT = WinVerifyTrust(hWind, pgActionID, pWVTData);
                                        }

                                        Console.WriteLine();
                                    }
                                }
                                else if ((uint)hRESULT == 0x800b0100)
                                {
                                    Console.WriteLine("{0} has no Authenticode signatures.", fileName);
                                }
                            }
                            finally
                            {
                                Marshal.FreeHGlobal(pWVTData);
   

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...