2009-07-02 61 views
9

我正在实施Windows的进程提升助手。这是一个程序,它将以提升模式运行,并以管理员权限启动其他程序,而不显示其他UAC提示。出于安全原因,我想确保只能执行使用我公司的Authenticode密钥进行数字签名的二进制文件。WinVerifyTrust检查特定的签名?

WinVerifyTrust函数让我在中途得到了一半,但它只确保一个二进制文件被签名,其中一些是Microsoft信任链的一部分。是否有相对简单的方法来执行Authenticode验证并确保它是由我们的私钥签名的?

回答

8

我相信你要找的是CryptQueryObject

有了它,你应该能够从PE中拉出所涉及的证书,并执行所需的任何额外检查。


举例来说,这会让你到HCRYPTMSG。从那里你可以使用CryptMsgGetParam拉出任何你想要的。我希望能够做出更加“健壮”的东西,但这些API非常繁琐,因为它们需要大量分支来处理所有的返回情况。

所以,这里的AP /调用-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; 
    } 
+0

我想我会尝试和杜松子酒的一些代码,因为我有点对Windows安全感兴趣。 – 2009-07-02 15:07:10

+2

+1很好,这就是魔术词。谷歌搜索“CryptQueryObject”和“Authenticode”让我这个:http://support.microsoft.com/kb/323809 - 这正是医生的命令。我邀请你仍然添加你的代码,尽管:) – 2009-07-02 16:11:27

+0

Gah,这些是我曾经使用过的最差劲的API中的一些。不是我最好的工作。希望对未来出现这个问题的人有所帮助。 – 2009-07-02 16:37:21

7

为了得到签名的代码使用该证书信息:

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 
2

发现这里的解决方案:

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; 
    } 
} 
0

这些都是一些我曾经与

一个字的警告工作的最恶劣的API:它更糟糕比你想象的要多。

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

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

如果您使用主签名的详细信息来验证证书是否为您的软件信任的证书,那么您很容易受到WinVerifyTrust信任二级签名但您的代码正在检查主签名证书的情况的影响是你所期望的,你没有注意到来自主要证书的签名是无稽之谈。攻击者可以在不拥有私钥的情况下使用您的公共证书,并将其他一些代码签名证书与其他人签发的证书一起使用,从而绕过发行商的这种检查方式。

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

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

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