问题描述
简短问题
如何从可执行文件(.EXE / .DLL)中获取有关多个代码签名证书的信息?
How can I get information about multiple code signing certificates from an executable (.EXE/.DLL)?
期望的答案
最终接受的答案应该提出一种获取所有证书的方法C#。概念/伪代码是可以的,我不希望您编写完整的源代码。
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.
常见问题
我正在研究是否可以在插件(.DLL)上使用多个代码签名证书来检查它是否是否经过正式测试。步骤如下:
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:
- 插件DLL就像其他任何应用程序一样由供应商签名
- 插件DLL进入测试实验室并进行了一系列测试
- 插件DLL被测试实验室再次签名,以便使用DLL的应用程序可以确定它是否正在使用
似乎有可能使用
signtool /v /f <pfx> /as <dll>
可能已奏效的指示:
- 文件大小增加
- 该工具会打印成功消息
但是,有些问题显示了第二个签名:
However, there are some issues showing the second signature:
- 尽管Windows资源管理器显示签名列表,但仅显示一个证书
- C#
X509Certificate.CreateFromSignedFile()
方法只能返回一个证书
- although Windows Explorer says "Signature list", it shows only one certificate
- the C#
X509Certificate.CreateFromSignedFile()
method can only return one certificate
此刻,我实际上是在EXE文件而不是DLL文件上尝试我的代码,但这无关紧要。 EXE已使用受信任的根证书和时间戳进行签名。第二个签名是通过,目前没有时间戳。
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:
- 在Stackoverflow上搜索现有答案
- 在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.
推荐答案
我最近实现了自己执行代码。我无法发布完整的解决方案,因为它已嵌入较大的静态分析工具中,但是下面使用 Windows API函数与。
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.
注意事项:
- 枚举多个证书仅在Windows 8和Windows Server 2012(或更高版本)上受支持。 Windows的早期版本将仅报告存在零个或一个证书。
- 此处提供的代码仅处理有效签名的文件和没有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,
};
// 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;
}
}
}
针对SQL运行应用程序Server 2014 SP1安装程序在Windows 8.1上提供以下输出:
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.
这篇关于如何使用Authenticode对二进制文件进行双重签名?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!