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:
It seems possible to sign a DLL a second time using
 signtool /v /f <pfx> /as <dll>
Indications that this may have worked:
However, there are some issues showing the second signature:
X509Certificate.CreateFromSignedFile() method can only return one certificateAt 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:
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.
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:
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);
                            }
                        }
                        finally
                        {
                            Marshal.FreeHGlobal(pgActionID);
                        }
                    }
                    finally
                    {
                        if (pSignatureSettings != IntPtr.Zero)
                        {
                            Marshal.FreeHGlobal(pSignatureSettings);
                        }
                    }
                }
                finally
                {
                    Marshal.FreeHGlobal(ptrFile);
                }
            }
            finally
            {
                Marshal.FreeHGlobal(pcwszFilePath);
            }
            Console.WriteLine("Press enter to exit.");
            Console.ReadLine();
        }
        private const int SGNR_TYPE_TIMESTAMP = 0x00000010;
        private const int WTD_UI_NONE = 2;
        private const int WTD_CHOICE_FILE = 1;
        private const int WTD_REVOKE_NONE = 0;
        private const int WTD_REVOKE_WHOLECHAIN = 1;
        private const int WTD_STATEACTION_IGNORE = 0;
        private const int WTD_STATEACTION_VERIFY = 1;
        private const int WTD_STATEACTION_CLOSE = 2;
        private const int WTD_REVOCATION_CHECK_NONE = 16;
        private const int WTD_REVOCATION_CHECK_CHAIN = 64;
        private const int WTD_UICONTEXT_EXECUTE = 0;
        private const int WSS_VERIFY_SPECIFIC = 0x00000001;
        private const int WSS_GET_SECONDARY_SIG_COUNT = 0x00000002;
        [DllImport("wintrust.dll")]
        private static extern int WinVerifyTrust(IntPtr hWind, IntPtr pgActionID, IntPtr pWVTData);
        [DllImport("wintrust.dll")]
        private static extern IntPtr WTHelperProvDataFromStateData(IntPtr hStateData);
        [DllImport("wintrust.dll")]
        private static extern IntPtr WTHelperGetProvSignerFromChain(IntPtr pProvData, int idxSigner, bool fCounterSigner, int idxCounterSigner);
        [DllImport("wintrust.dll")]
        private static extern IntPtr WTHelperGetProvCertFromChain(IntPtr pSgnr, int idxCert);
        [StructLayout(LayoutKind.Sequential)]
        private struct WINTRUST_DATA
        {
            internal int cbStruct;
            internal IntPtr pPolicyCallbackData;
            internal IntPtr pSIPClientData;
            internal int dwUIChoice;
            internal int fdwRevocationChecks;
            internal int dwUnionChoice;
            internal IntPtr pFile;
            internal int dwStateAction;
            internal IntPtr hWVTStateData;
            internal IntPtr pwszURLReference;
            internal int dwProvFlags;
            internal int dwUIContext;
            internal IntPtr pSignatureSettings;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct WINTRUST_SIGNATURE_SETTINGS
        {
            internal int cbStruct;
            internal int dwIndex;
            internal int dwFlags;
            internal int cSecondarySigs;
            internal int dwVerifiedSigIndex;
            internal IntPtr pCryptoPolicy;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct WINTRUST_FILE_INFO
        {
            internal int cbStruct;
            internal IntPtr pcwszFilePath;
            internal IntPtr hFile;
            internal IntPtr pgKnownSubject;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_PROVIDER_DATA
        {
            internal int cbStruct;
            internal IntPtr pWintrustData;
            internal bool fOpenedFile;
            internal IntPtr hWndParent;
            internal IntPtr pgActionID;
            internal IntPtr hProv;
            internal int dwError;
            internal int dwRegSecuritySettings;
            internal int dwRegPolicySettings;
            internal IntPtr psPfns;
            internal int cdwTrustStepErrors;
            internal IntPtr padwTrustStepErrors;
            internal int chStores;
            internal IntPtr pahStores;
            internal int dwEncoding;
            internal IntPtr hMsg;
            internal int csSigners;
            internal IntPtr pasSigners;
            internal int csProvPrivData;
            internal IntPtr pasProvPrivData;
            internal int dwSubjectChoice;
            internal IntPtr pPDSip;
            internal IntPtr pszUsageOID;
            internal bool fRecallWithState;
            internal System.Runtime.InteropServices.ComTypes.FILETIME sftSystemTime;
            internal IntPtr pszCTLSignerUsageOID;
            internal int dwProvFlags;
            internal int dwFinalError;
            internal IntPtr pRequestUsage;
            internal int dwTrustPubSettings;
            internal int dwUIStateFlags;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_PROVIDER_SGNR
        {
            internal int cbStruct;
            internal System.Runtime.InteropServices.ComTypes.FILETIME sftVerifyAsOf;
            internal int csCertChain;
            internal IntPtr pasCertChain;
            internal int dwSignerType;
            internal IntPtr psSigner;
            internal int dwError;
            internal int csCounterSigners;
            internal IntPtr pasCounterSigners;
            internal IntPtr pChainContext;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_PROVIDER_CERT
        {
            internal int cbStruct;
            internal IntPtr pCert;
            internal bool fCommercial;
            internal bool fTrustedRoot;
            internal bool fSelfSigned;
            internal bool fTestCert;
            internal int dwRevokedReason;
            internal int dwConfidence;
            internal int dwError;
            internal IntPtr pTrustListContext;
            internal bool fTrustListSignerCert;
            internal IntPtr pCtlContext;
            internal int dwCtlError;
            internal bool fIsCyclic;
            internal IntPtr pChainElement;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_ALGORITHM_IDENTIFIER
        {
            internal IntPtr pszObjId;
            internal CRYPT_INTEGER_BLOB Parameters;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CMSG_SIGNER_INFO
        {
            internal int dwVersion;
            internal CRYPT_INTEGER_BLOB Issuer;
            internal CRYPT_INTEGER_BLOB SerialNumber;
            internal CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
            internal CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
            internal CRYPT_INTEGER_BLOB EncryptedHash;
            internal CRYPT_ATTRIBUTES AuthAttrs;
            internal CRYPT_ATTRIBUTES UnauthAttrs;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_INTEGER_BLOB
        {
            internal int cbData;
            internal IntPtr pbData;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_ATTRIBUTES
        {
            internal int cAttr;
            internal IntPtr rgAttr;
        }
    }
}
Running the application against the SQL Server 2014 SP1 installer gives the following output on Windows 8.1:
File: SQLServer2014SP1-KB3058865-x64-ENU.exe
Authenticode signatures: 2
Hash algorithm of signature 1: sha1.
Certificate thumbprint of signature 1: 67B1757863E3EFF760EA9EBB02849AF07D3A8080
Timestamp of signature 1: 22/04/2015 06:03:40
Hash algorithm of signature 2: sha256.
Certificate thumbprint of signature 2: 76DAF3E30F95B244CA4D6107E0243BB97F7DF965
Timestamp of signature 2: 22/04/2015 06:03:51
Press enter to exit.
                        Give Mono a try. It's able to pull all of the file's Authenticode certificates in one line!
    using Mono.Security.Authenticode;
    AuthenticodeDeformatter monoFileCert = new AuthenticodeDeformatter("System.Windows.dll");
    Console.WriteLine($"Found certificates {monoFileCert.Certificates.Count}");
https://github.com/mono/mono/blob/master/mcs/class/Mono.Security/Mono.Security.Authenticode/AuthenticodeDeformatter.cs
Usage example: https://github.com/mono/mono/blob/master/mcs/tools/security/chktrust.cs
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With