代码签署可执行文件两次

简短的问题

如何从可执行文件(.EXE / .DLL)获取有关多个代码签名证书的信息?

预期答案

最终接受的答案应该提出一种获取C#所有证书的方法。 概念/伪代码是可以的,我不指望你写完整的源代码。

有关建议工具的中间答案,请参阅Security.StackExchange上的问题 。

很长的问题

我正在研究是否可以在插件(.DLL)上使用多个代码签名证书来检查它是否已经过官方测试。 这是程序:

  • 插件DLL由供应商签名,就像任何其他应用程序一样
  • 插件DLL进入测试实验室并进行一系列测试
  • 测试实验室再次对插件DLL进行签名,以便使用DLL的应用程序可以确定它是否使用了经过测试的插件

似乎可以使用第二次签署DLL

signtool /v /f  /as  

这可能有用的迹象:

  • 文件大小增加
  • 该工具打印成功消息

但是,有一些问题显示第二个签名:

  • 虽然Windows资源管理器说“签名列表”,但它只显示一个证书
  • C# X509Certificate.CreateFromSignedFile()方法只能返回一个证书

目前我实际上是在EXE文件而不是DLL文件上尝试我的代码,但这应该不重要。 EXE已使用受信任的根证书和时间戳签名。 第二个签名是使用我自己的证书创建的, 遵循这些步骤,目前没有时间戳。

在问这个问题之前我做了些什么:

  • 在Stackoverflow上搜索现有答案
  • 在Google上搜索工具

到目前为止我发现的唯一相关问题是如何使用时间戳正确地进行双重签名,但它没有答案。

我最近实现了自己执行此操作的代码。 我无法发布完整的解决方案,因为它嵌入在一个更大的静态分析工具中,但下面使用WinVerifyTrust() Windows提供了一个工作的简单C#控制台应用程序的代码,该应用程序枚举了指定文件路径中的Authenticode签名。 此知识库文章的帮助下的API函数。

注意事项:

  1. 仅在Windows 8和Windows Server 2012(或更高版本)上支持枚举多个证书。 早期版本的Windows只会报告零或一个证书。
  2. 此处提供的代码仅处理有效签名的文件和没有Authenticode签名的文件。 它无法正确处理带有无效签名的文件。 这留给读者作为练习。

这是代码:

 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, }; // NB 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; } } } 

针对SQL Server 2014 SP1安装程序运行应用程序在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. 

试试Mono吧。 它能够将所有文件的Authenticode证书拉到一行!

  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

用法示例: https : //github.com/mono/mono/blob/master/mcs/tools/security/chktrust.cs