我正在为Windows实现进程提升助手.这是一个将以提升模式运行并使用管理员权限启动其他程序而不显示其他UAC提示的程序.出于安全原因,我想确保只能执行使用我公司的Authenticode密钥进行数字签名的二进制文件.
该的WinVerifyTrust函数获取我中途有,但它只是确保了二进制通过签署一些关键的就是信任的微软链的一部分.是否有一种相对简单的方法来执行Authenticode验证并确保它是由我们的私钥签名的?
我相信你要找的是CryptQueryObject.
有了它,您应该能够从PE中提取相关证书,并进行所需的任何其他检查.
举例来说,这将使您获得HCRYPTMSG.从那里你可以使用CryptMsgGetParam提取你想要的任何东西.我希望能做出更"强大"的东西,但这些API非常多毛,因为它们需要大量的分支来处理所有的返回案例.
所以,这是ap/invoke-rific c#示例(我在C中开始,但这基本上是不可读的):
static class Crypt32 { //Omitting flag constants; you can look these up in WinCrypt.h [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)] public static extern bool CryptQueryObject( int dwObjectType, IntPtr pvObject, int dwExpectedContentTypeFlags, int dwExpectedFormatTypeFlags, int dwFlags, out int pdwMsgAndCertEncodingType, out int pdwContentType, out int pdwFormatType, ref IntPtr phCertStore, ref IntPtr phMsg, ref IntPtr ppvContext); } class Program { static void Main(string[] args) { //Path to executable here // I tested with MS-Office .exe's string path = ""; int contentType; int formatType; int ignored; IntPtr context = IntPtr.Zero; IntPtr pIgnored = IntPtr.Zero; IntPtr cryptMsg = IntPtr.Zero; if (!Crypt32.CryptQueryObject( Crypt32.CERT_QUERY_OBJECT_FILE, Marshal.StringToHGlobalUni(path), Crypt32.CERT_QUERY_CONTENT_FLAG_ALL, Crypt32.CERT_QUERY_FORMAT_FLAG_ALL, 0, out ignored, out contentType, out formatType, ref pIgnored, ref cryptMsg, ref context)) { int error = Marshal.GetLastWin32Error(); Console.WriteLine((new Win32Exception(error)).Message); return; } //expecting '10'; CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED Console.WriteLine("Context Type: " + contentType); //Which implies this is set Console.WriteLine("Crypt Msg: " + cryptMsg.ToInt32()); return; }
要从签名代码获取证书信息,请使用以下命令:
using System.Security.Cryptography.X509Certificates; X509Certificate basicSigner = X509Certificate.CreateFromSignedFile(filename); X509Certificate2 cert = new X509Certificate2(basicSigner);
然后您可以获得这样的证书详细信息:
Console.WriteLine(cert.IssuerName.Name); Console.WriteLine(cert.SubjectName.Name); // etc
这些是我曾经使用过的最讨厌的API
提个警告:它比您想像的还要糟糕。
至少由于引入了SHA-256签名(情况一直如此吗?),Authenticode可能具有多个签名。在PKCS-7签名消息中,它们没有被编码为多个签名。相反,它们是OID_NESTED_SIGNATURE类型的未经身份验证的消息属性,每个属性都包含另一个完整的PKCS-7签名消息。
如果任何签名有效并且来自受信任的证书链,则WinVerifyTrust会告诉您该文件有效。但是,它不会告诉您哪个签名是有效的。如果然后使用CryptQueryObject读取完整的PKCS-7消息,而仅查看主要签名的证书(如此处和MSDN上的代码示例中所示),则不一定要查看经过验证的证书。关联的签名可能与可执行文件不匹配,并且/或者证书可能没有受信任的CA链。
如果您使用主签名的详细信息来验证证书是您的软件信任的证书,则很容易出现WinVerifyTrust信任辅助签名,但您的代码正在检查主签名的证书是否符合您期望的情况。 ,您还没有注意到主证书的签名是胡说八道。攻击者可能会在不拥有您的私钥的情况下使用您的公共证书,再加上其他颁发给其他人的代码签名证书,从而绕过了发布者的这种检查方式。
从Win8开始,WinVerifyTrust可以选择验证特定的签名,因此您应该能够迭代签名以找到有效的签名和满足您要求的签名。
但是,如果必须与Win7兼容,据我所知,您可以管理的最好的方法是MsiGetFileSignatureInformation。从实验(对于这里的所有其他内容,实际文档令人沮丧地感到困惑),当WinVerifyTrust信任一个证书时,它似乎会返回受信任的证书。但是,如果没有受信任的签名,它无论如何都会返回主签名的证书,因此您仍然必须使用WinVerifyTrust对其进行首先检查。
当然,这里还有很多可能的检查时间/使用时间问题。