2009-07-02 4 views
9

Sto implementando un helper di elevazione del processo per Windows. È un programma che verrà eseguito in modalità elevata e avvierà altri programmi con privilegi di amministratore senza visualizzare ulteriori prompt UAC. Per motivi di sicurezza, voglio essere sicuro che solo i binari che sono firmati digitalmente con la chiave Authenticode della mia azienda possano essere eseguiti.WinVerifyTrust per verificare la presenza di una firma specifica?

La funzione WinVerifyTrust mi arriva a metà, ma garantisce solo che un codice binario sia firmato da della chiave che fa parte della catena di fiducia di Microsoft. Esiste un modo relativamente semplice per eseguire la verifica Authenticode E assicurarsi che sia firmato dalla nostra chiave privata?

risposta

8

Credo che quello che stai cercando è CryptQueryObject.

Con esso si dovrebbe essere in grado di estrarre il certificato coinvolto da un PE e fare eventuali controlli aggiuntivi desiderati.


A titolo di esempio, questo vi porterà a un HCRYPTMSG. Da lì è possibile utilizzare CryptMsgGetParam per estrarre tutto ciò che si desidera. Speravo di creare qualcosa di più "robusto", ma queste API sono piuttosto pelose in quanto richiedono molte ramificazioni per gestire tutti i casi di restituzione.

Quindi, ecco ap/invoke-rific C# esempio (ho iniziato in C, ma che era fondamentalmente illeggibile):

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

Penso cercherò di gin un po 'di codice, come io sono un po' interessato a protezione di Windows. –

+2

+1 Grande, questa è la parola magica. Googling per "CryptQueryObject" e "Authenticode" mi ha fatto questo: http://support.microsoft.com/kb/323809 - che è proprio quello che il medico ha ordinato. Vi invito a aggiungere ancora il codice, però :) –

+0

Gah, queste sono alcune delle API più brutte che io abbia mai lavorato. Non è il mio lavoro migliore con qualsiasi mezzo. Speriamo un po 'di aiuto per coloro che si imbatteranno in questa domanda in futuro. –

7

Per ottenere le informazioni sul certificato da un uso codice firmato questo:

using System.Security.Cryptography.X509Certificates; 
X509Certificate basicSigner = X509Certificate.CreateFromSignedFile(filename); 
X509Certificate2 cert = new X509Certificate2(basicSigner); 

Quindi è possibile ottenere i dettagli cert in questo modo:

Console.WriteLine(cert.IssuerName.Name); 
Console.WriteLine(cert.SubjectName.Name); 
// etc 
+0

Questo è bello e semplice, ma se un file contiene più firme si ottiene solo il primo certificato – Cocowalla

2

trovato qui la soluzione:

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

qui è con rientro:

#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; 
    } 
} 
+1

In realtà, il codice è dalla knowledge base di Microsoft ([Come ottenere informazioni da Authenticode Firmato eseguibili] (http://support.microsoft.com/kb/323809)), già debitamente rientrato e senza pubblicità inutili. – IInspectable

0

queste sono alcune delle API più brutte che io abbia mai lavorato

Una parola di avvertimento: è peggio di quanto pensassi.

Almeno da quando ha introdotto la firma SHA-256 (è sempre stato così?), È possibile che Authenticode abbia più firme. Non sono codificati come firme multiple nel messaggio di firma PKCS-7; invece, sono attributi di messaggi non autenticati di tipo OID_NESTED_SIGNATURE, ciascuno contenente un altro messaggio di firma PKCS-7 completo.

WinVerifyTrust indicherà che il file è valido se una delle firme è valida e proviene da una catena di certificati attendibili. Tuttavia non ti dirà quale delle firme era valida. Se quindi si utilizza CryptQueryObject per leggere il messaggio completo PKCS-7 e si guarda solo il certificato per la firma principale (come negli esempi di codice qui e su MSDN), non si sta necessariamente esaminando un certificato verificato. La firma associata potrebbe non corrispondere all'eseguibile e/o il certificato potrebbe non avere una catena CA attendibile.

Se si utilizzano i dettagli della firma principale per convalidare che il certificato è uno dei trust del software, si è vulnerabili a una situazione in cui WinVerifyTrust considera attendibile una firma secondaria, ma il codice sta controllando il certificato della firma principale è quello che ti aspettavi, e non hai notato che la firma del certificato primario non ha senso. Un utente malintenzionato potrebbe utilizzare il tuo certificato pubblico senza possedere la sua chiave privata, combinato con un altro certificato di firma del codice rilasciato a qualcun altro, per evitare che un editore controlli in questo modo.

Da Win8 in poi, WinVerifyTrust può opzionalmente convalidare le firme specifiche, così si dovrebbe essere in grado di scorrere le firme di trovare uno che è valida e quella che soddisfa le vostre esigenze.

Se dovete essere Win7-compatibile, anche se, per quanto ne so il migliore è possibile gestire è MsiGetFileSignatureInformation. Dalla sperimentazione (come per tutto il resto qui, la documentazione attuale è frustrantemente prolissa), sembra che restituisca il certificato attendibile quando WinVerifyTrust si fida di uno. Ma se non c'è una firma attendibile, restituisce comunque il certificato della firma principale, quindi è ancora necessario utilizzare WinVerifyTrust per verificarlo.

Naturalmente c'è anche un sacco di possibili tempo di controllo/tempo di utilizzo problemi qui.