【问题标题】:WinVerifyTrust to check for a specific signature?WinVerifyTrust 检查特定签名?
【发布时间】:2010-11-07 12:52:42
【问题描述】:

我正在为 Windows 实现一个进程提升帮助程序。该程序将以提升模式运行并以管理员权限启动其他程序,而不会显示额外的 UAC 提示。出于安全原因,我想确保只能执行使用我公司的 Authenticode 密钥进行数字签名的二进制文件。

WinVerifyTrust 函数让我成功了一半,但它只确保二进制文件由 Microsoft 信任链中的 some 密钥签名。是否有一种相对简单的方法来执行 Authenticode 验证并确保它由我们的私钥签名?

【问题讨论】:

    标签: c windows digital-signature winverifytrust


    【解决方案1】:

    我相信您正在寻找的是CryptQueryObject

    有了它,您应该能够从 PE 中提取相关证书,并进行任何您想要的额外检查。


    例如,这将使您进入 HCRYPTMSG。从那里你可以使用CryptMsgGetParam 来提取你想要的任何东西。我希望做一些更“健壮”的东西,但是这些 API 非常麻烦,因为它们需要大量的分支来处理所有的返回案例。

    所以,这是一个 p/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;
        }
    

    【讨论】:

    • 我想我会尝试编写一些代码,因为我对 Windows 安全性有点兴趣。
    • +1 太好了,这就是神奇的词。谷歌搜索“CryptQueryObject”和“Authenticode”得到了这个:support.microsoft.com/kb/323809——这正是医生所要求的。不过,我邀请您仍然添加您的代码 :)
    • 哇,这些是我用过的一些最糟糕的 API。无论如何都不是我最好的作品。希望对以后遇到这个问题的人有所帮助。
    【解决方案2】:

    要从签名代码中获取证书信息,请使用:

    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
    

    【讨论】:

    • 这很好很简单,但是如果一个文件包含多个签名,它只会获得第一个证书
    【解决方案3】:

    这些是我用过的一些最糟糕的 API

    一个警告:它比你想象的更糟糕。

    至少自从引入 SHA-256 签名以来(一直都是这样吗?),Authenticode 有可能拥有多个签名。它们没有被编码为 PKCS-7 签名消息中的多个签名;相反,它们是 OID_NESTED_SIGNATURE 类型的未经身份验证的消息属性,每个都包含另一个完整的 PKCS-7 签名消息。

    如果任何签名有效并且来自受信任的证书链,WinVerifyTrust 将告诉您文件有效。但是,它不会告诉您哪些签名是有效的。如果您随后使用 CryptQueryObject 读取完整的 PKCS-7 消息,并且只查看主要签名的证书(如此处和 MSDN 上的代码示例中所示),您不一定查看经过验证的证书。关联的签名可能与可执行文件不匹配,和/或证书可能没有受信任的 CA 链。

    如果您使用主签名的详细信息来验证证书是否是您的软件信任的证书,那么您很容易遇到 WinVerifyTrust 信任辅助签名但您的代码正在检查主签名的证书的情况你所期望的,你没有注意到主证书的签​​名是无稽之谈。攻击者可以在不拥有其私钥的情况下使用您的公共证书,并结合其他一些颁发给其他人的代码签名证书,以这种方式绕过发布者检查。

    从 Win8 开始,WinVerifyTrust 可以选择性地验证特定签名,因此您应该能够迭代签名以找到一个有效的一个满足您要求的签名。

    但是,如果您必须与 Win7 兼容,据我所知,您可以管理的最好方法是 MsiGetFileSignatureInformation。从实验来看(至于这里的所有其他内容,实际文档令人沮丧),当 WinVerifyTrust 信任一个证书时,它似乎返回了受信任的证书。但如果没有受信任的签名,它无论如何都会返回主签名的证书,因此您仍然必须先使用 WinVerifyTrust 进行检查。

    当然这里也有很多可能的检查时间/使用时间问题。

    【讨论】:

    • “从 Win8 开始” -- 显然这也适用于打过补丁的 Win 7。
    【解决方案4】:

    在这里找到解决方案:

    http://www.ucosoft.com/how-to-program-to-retrieve-the-authenticode-information.html

    这里是缩进:

    #define _UNICODE 1
    #define UNICODE 1
    
    #include <windows.h>
    #include <tchar.h>
    #include <wincrypt.h>
    #include <Softpub.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    #pragma comment (lib, "Crypt32")
    
    // the Authenticode Signature is encode in PKCS7
    #define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
    
    // Information structure of authenticode sign
    typedef struct 
    {
        LPWSTR lpszProgramName; 
        LPWSTR lpszPublisherLink;
        LPWSTR lpszMoreInfoLink;
    
        DWORD cbSerialSize;
        LPBYTE lpSerialNumber;
        LPTSTR lpszIssuerName;
        LPTSTR lpszSubjectName;
    } 
    SPROG_SIGNATUREINFO, *PSPROG_SIGNATUREINFO;
    
    VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
    VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo);
    
    BOOL GetAuthenticodeInformation(LPCTSTR lpszFileName, PSPROG_SIGNATUREINFO pInfo)
    {
        HCERTSTORE hStore = NULL;
        HCRYPTMSG hMsg = NULL;
        PCMSG_SIGNER_INFO pSignerInfo = NULL;
        DWORD dwSignerInfo;
    
        BOOL bRet = FALSE;
    
        __try
        {
            // as CryptQueryObject() only accept WCHAR file name, convert first
            WCHAR wszFileName[MAX_PATH];
    #ifdef UNICODE
            if ( !lstrcpynW( wszFileName, lpszFileName, MAX_PATH))
                __leave;
    #else
            if ( mbstowcs( wszFileName, lpszFileName, MAX_PATH) == -1)
                __leave;
    #endif
            //Retrieve the Message Handle and Store Handle
            DWORD dwEncoding, dwContentType, dwFormatType;
            if ( !CryptQueryObject( CERT_QUERY_OBJECT_FILE, wszFileName,
                                    CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
                                    CERT_QUERY_FORMAT_FLAG_BINARY, 0, &dwEncoding,
                                    &dwContentType, &dwFormatType, &hStore,
                                    &hMsg, NULL))
                __leave;
    
            //Get the length of SignerInfo
            if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, NULL, &dwSignerInfo))
                __leave;
    
            // allocate the memory for SignerInfo
            if ( !(pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc( LPTR, dwSignerInfo)))
                __leave;
    
            // get the SignerInfo
            if ( !CryptMsgGetParam( hMsg, CMSG_SIGNER_INFO_PARAM, 0, (PVOID)pSignerInfo, &dwSignerInfo))
                __leave;
    
            //get the Publisher from SignerInfo
            GetProgAndPublisherInfo( pSignerInfo, pInfo);
    
            //get the Certificate from SignerInfo
            GetCertificateInfo( hStore, pSignerInfo, pInfo);
    
            bRet = TRUE;
        }
        __finally
        {
            // release the memory
            if (pSignerInfo != NULL) LocalFree(pSignerInfo);
            if (hStore != NULL) CertCloseStore(hStore, 0);
            if (hMsg != NULL) CryptMsgClose(hMsg);
        }
        return bRet;
    }
    
    
    LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
    {
        LPWSTR outputString = NULL;
    
        // allocate the memory
        outputString = (LPWSTR)VirtualAlloc(NULL, (wcslen(inputString) + 1) * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE);
    
        // copy
        if (outputString != NULL)
        {
            lstrcpyW(outputString, inputString);
        }
    
        return outputString;
    }
    
    
    VOID GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
    {
        PSPC_SP_OPUS_INFO OpusInfo = NULL;
        DWORD dwData;
    
        __try
        {
            // query SPC_SP_OPUS_INFO_OBJID OID in Authenticated Attributes
            for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
            {
                if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
                {
                    // get the length of SPC_SP_OPUS_INFO
                    if ( !CryptDecodeObject(ENCODING,
                                            SPC_SP_OPUS_INFO_OBJID,
                                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                                            0,
                                            NULL,
                                            &dwData))
                        __leave;
    
                    // allocate the memory for SPC_SP_OPUS_INFO
                    if ( !(OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData)))
                        __leave;
    
                    // get SPC_SP_OPUS_INFO structure
                    if ( !CryptDecodeObject(ENCODING,
                                            SPC_SP_OPUS_INFO_OBJID,
                                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                                            0,
                                            OpusInfo,
                                            &dwData))
                        __leave;
    
                    // copy the Program Name of SPC_SP_OPUS_INFO to the return variable
                    if (OpusInfo->pwszProgramName)
                    {
                        pInfo->lpszProgramName = AllocateAndCopyWideString(OpusInfo->pwszProgramName);
                    }
                    else
                        pInfo->lpszProgramName = NULL;
    
                    // copy the Publisher Info of SPC_SP_OPUS_INFO to the return variable
                    if (OpusInfo->pPublisherInfo)
                    {
                        switch (OpusInfo->pPublisherInfo->dwLinkChoice)
                        {
                            case SPC_URL_LINK_CHOICE:
                                pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
                                break;
    
                            case SPC_FILE_LINK_CHOICE:
                                pInfo->lpszPublisherLink = AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
                                break;
    
                            default:
                                pInfo->lpszPublisherLink = NULL;
                                break;
                        }
                    }
                    else
                    {
                        pInfo->lpszPublisherLink = NULL;
                    }
    
                    // copy the More Info of SPC_SP_OPUS_INFO to the return variable
                    if (OpusInfo->pMoreInfo)
                    {
                        switch (OpusInfo->pMoreInfo->dwLinkChoice)
                        {
                            case SPC_URL_LINK_CHOICE:
                                pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
                                break;
    
                            case SPC_FILE_LINK_CHOICE:
                                pInfo->lpszMoreInfoLink = AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
                                break;
    
                            default:
                                pInfo->lpszMoreInfoLink = NULL;
                                break;
                        }
                    }
                    else
                    {
                        pInfo->lpszMoreInfoLink = NULL;
                    }
    
                    break; // we have got the information, break
                }
            }
        }
        __finally
        {
            if (OpusInfo != NULL) LocalFree(OpusInfo);
        }
    }
    
    
    VOID GetCertificateInfo(HCERTSTORE hStore, PCMSG_SIGNER_INFO pSignerInfo, PSPROG_SIGNATUREINFO pInfo)
    {
        PCCERT_CONTEXT pCertContext = NULL;
    
        __try
        {
            CERT_INFO CertInfo;
            DWORD dwData;
    
            // query Signer Certificate in Certificate Store
            CertInfo.Issuer = pSignerInfo->Issuer;
            CertInfo.SerialNumber = pSignerInfo->SerialNumber;
    
            if ( !(pCertContext = CertFindCertificateInStore(   hStore,
                                                                ENCODING, 0, CERT_FIND_SUBJECT_CERT,
                                                                (PVOID)&CertInfo, NULL)))
                __leave;
    
            dwData = pCertContext->pCertInfo->SerialNumber.cbData;
    
            // SPROG_SIGNATUREINFO.cbSerialSize
            pInfo->cbSerialSize = dwData;
    
            // SPROG_SIGNATUREINFO.lpSerialNumber
            pInfo->lpSerialNumber = (LPBYTE)VirtualAlloc(NULL, dwData, MEM_COMMIT, PAGE_READWRITE);
            memcpy( pInfo->lpSerialNumber, pCertContext->pCertInfo->SerialNumber.pbData, dwData);
    
            // SPROG_SIGNATUREINFO.lpszIssuerName
            __try
            {
                // get the length of Issuer Name
                if (!(dwData = CertGetNameString(   pCertContext,
                                                    CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                                    CERT_NAME_ISSUER_FLAG, NULL, NULL, 0)))
                    __leave;
    
                // allocate the memory
                if ( !(pInfo->lpszIssuerName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
                    __leave;
    
                // get Issuer Name
                if (!(CertGetNameString(pCertContext,
                                        CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                        CERT_NAME_ISSUER_FLAG, NULL, pInfo->
                                        lpszIssuerName, dwData)))
                    __leave;
            }
            __finally
            {
            }
    
            // SPROG_SIGNATUREINFO.lpszSubjectName
            __try
            {
                //get the length of Subject Name
                if (!(dwData = CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 0)))
                    __leave;
    
                // allocate the memory
                if ( !(pInfo->lpszSubjectName = (LPTSTR)VirtualAlloc(NULL, dwData * sizeof(TCHAR), MEM_COMMIT, PAGE_READWRITE)))
                    __leave;
    
                // get Subject Name
                if (!(CertGetNameString( pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, pInfo->lpszSubjectName, dwData)))
                    __leave;
            }
            __finally
            {
            }
        }
        __finally
        {
            if (pCertContext != NULL)
                CertFreeCertificateContext(pCertContext);
        }
    }
    
    
    int _tmain(int argc, TCHAR *argv[])
    {
        if (argc != 2)
        {
            _tprintf(_T("Usage: SignedFileInfo \n"));
            return 0;
        }
        else
        {
            SPROG_SIGNATUREINFO SignInfo;
    
            ZeroMemory(&SignInfo, sizeof(SignInfo));
    
            GetAuthenticodeInformation( argv[1], &SignInfo);
    
            wprintf(L"Program Name: %s\n", SignInfo.lpszProgramName);
            wprintf(L"Publisher Link: %s\n", SignInfo.lpszPublisherLink);
            wprintf(L"More Info Link: %s\n", SignInfo.lpszMoreInfoLink);
    
            {
                _tprintf(_T("Serial Number: "));
                DWORD dwData = SignInfo.cbSerialSize;
                for (DWORD n = 0; n < dwData; n++)
                {
                    _tprintf(_T("%02x "),
                        SignInfo.lpSerialNumber[dwData - (n + 1)]);
                }
                _tprintf(_T("\n"));
            }
            _tprintf(_T("Issuer Name: %s\n"), SignInfo.lpszIssuerName);
            _tprintf(_T("Subject Name: %s\n"), SignInfo.lpszSubjectName);
            if ( SignInfo.lpszProgramName) VirtualFree(SignInfo.lpszProgramName, 0, MEM_RELEASE);
            if ( SignInfo.lpszPublisherLink) VirtualFree(SignInfo.lpszPublisherLink, 0, MEM_RELEASE);
            if ( SignInfo.lpszMoreInfoLink) VirtualFree(SignInfo.lpszMoreInfoLink, 0, MEM_RELEASE);
            if ( SignInfo.lpSerialNumber) VirtualFree(SignInfo.lpSerialNumber, 0, MEM_RELEASE);
            if ( SignInfo.lpszIssuerName) VirtualFree(SignInfo.lpszIssuerName, 0, MEM_RELEASE);
            if ( SignInfo.lpszSubjectName) VirtualFree(SignInfo.lpszSubjectName, 0, MEM_RELEASE);
    
            return 0;
        }
    }
    

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-15
    • 1970-01-01
    • 2013-07-27
    • 1970-01-01
    • 2016-08-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多