2014-07-22 32 views
11

我正在嘗試編寫從DLL或EXE讀取簽名(證書)的代碼。大多數DLL或EXE只有一個簽名,我的代碼正確讀取與此簽名關聯的所有證書。更具體地說,它讀取簽名證書,它是頒發者(不是根),簽署證書(帶有時間戳)及其頒發者(不是根)。我在C++和C#中有2個示例程序,它們都返回相同的證書。這是C#代碼,C++是長:)從可執行文件讀取多個簽名

static void Main(string[] args) 
{ 
    X509Certificate2Collection collection = new X509Certificate2Collection(); 
    collection.Import(args[0]); 
} 

100倍但也有有2個簽名,如圖文件屬性的DLL /數字簽名,例如C:\ Program Files文件(x86)的\微軟SQL服務器\ 80個\ TOOLS \ BINN \ MSVCR71.DLL:

File properties and digital signatures for msvcr71.dll

對於此DLL我的代碼讀取僅與第一簽名相關聯的證書。

我也嘗試過使用signtool,並且它返回與我的代碼相同的信息:第一個cert(與它的路徑)和countersignature(與它的路徑)。但最後還要注意錯誤。

C:\Windows>signtool verify /d /v "C:\Program Files (x86)\Microsoft SQL Server\80\Tools\Binn\msvcr71.dll" 

Verifying: C:\Program Files (x86)\Microsoft SQL Server\80\Tools\Binn\msvcr71.dll 
Signature Index: 0 (Primary Signature) 
Hash of file (sha1): 33BBCCF6326276B413A1ECED1BF7842A6D1DDA07 

Signing Certificate Chain: 
Issued to: Microsoft Root Certificate Authority 
Issued by: Microsoft Root Certificate Authority 
Expires: Sun May 09 19:28:13 2021 
SHA1 hash: CDD4EEAE6000AC7F40C3802C171E30148030C072 

    Issued to: Microsoft Code Signing PCA 
    Issued by: Microsoft Root Certificate Authority 
    Expires: Wed Jan 25 19:32:32 2017 
    SHA1 hash: FDD1314ED3268A95E198603BA8316FA63CBCD82D 

     Issued to: Microsoft Corporation 
     Issued by: Microsoft Code Signing PCA 
     Expires: Fri Feb 01 18:49:17 2013 
     SHA1 hash: 8849D1C0F147A3C8327B4038783AEC3E06C76F5B 

The signature is timestamped: Sat Feb 11 14:03:12 2012 
Timestamp Verified by: 
Issued to: Microsoft Root Certificate Authority 
Issued by: Microsoft Root Certificate Authority 
Expires: Sun May 09 19:28:13 2021 
SHA1 hash: CDD4EEAE6000AC7F40C3802C171E30148030C072 

    Issued to: Microsoft Time-Stamp PCA 
    Issued by: Microsoft Root Certificate Authority 
    Expires: Sat Apr 03 09:03:09 2021 
    SHA1 hash: 375FCB825C3DC3752A02E34EB70993B4997191EF 

     Issued to: Microsoft Time-Stamp Service 
     Issued by: Microsoft Time-Stamp PCA 
     Expires: Thu Oct 25 16:42:17 2012 
     SHA1 hash: FC33104FAE31FB538749D5F2D17FA0ECB819EAE5 

SignTool Error: The signing certificate is not valid for the requested usage. 
    This error sometimes means that you are using the wrong verification 
    policy. Consider using the /pa option. 

Number of files successfully Verified: 0 
Number of warnings: 0 
Number of errors: 1 

我有2個問題: - 什麼是第二個簽名 的目的 - 如何讀它(到目前爲止只有Windows資源管理器文件屬性對話框可以顯示它)。

謝謝!

+0

你看那些雙簽名的原因是[微軟將棄用](http://social.technet.microsoft.com/wiki/contents/articles/32288.windows-enforcement-of-authenticode-code -signing-and-timestamping.aspx)由於SHA-1的[不足的碰撞抵抗]而產生的SHA-1簽名(http://crypto.stackexchange.com/questions/845/what-is-wrong-with-using- SHA1功能於數字簽名 - 爲什麼 - 是 - 一 - 魯棒哈希functi)。他們今天離開後向兼容。 – ahmd0

回答

0

看着

The signature is timestamped: Sat Feb 11 14:03:12 2012 

Issued to: Microsoft Time-Stamp Service 

我認爲第二個簽名/證書用於time-stamping文件。 MS可能有兩個不同的組織單位,其中一個簽署代碼來承認其完整性,另一個(稍後)再次使用自己的證書籤署代碼,專門用於安全地爲文件加時間戳。

證書可以創建並分配給某些用途。意圖用於時間戳的證書可以被標記爲這樣,因此當遇到時間戳證書時,有可能因爲默認它期望用於代碼真實性/完整性驗證的證書而給出錯誤,沒有一個用於時間戳。

+0

不,數字簽名選項卡中的第二個證書不僅僅是時間戳。每個簽名(總共有兩個簽名)有6個證書關聯:簽名的路徑爲3個證書,signtool的輸出顯示爲3個時間戳。我同意兩個或多個簽名應該是可能的(問題是誰來閱讀他們兩個?),但時間戳簽名必須在簽名時應用,時間戳證書的全部目的是確保簽名的時間。 – Dima

+0

不太確定你的觀點。我們似乎都明白,有兩個簽名,其中一個用於驗證二進制文件的作者身份(「頒發給:Microsoft代碼簽名PCA」),另一個用於建立受信任的時間戳(「頒發給:Microsoft Time-郵票服務「)。這兩個簽名有不同的用途,可能由MS組織內的不同實體創建。當然,每個簽名都帶有一個時間戳,但由時間戳簽發機構發佈的可信時間戳與「正常」簽名的含義不同,請參閱維基百科鏈接。 – JimmyB

+0

在sceen shot的第二個標籤上有2個signautes,一個是sha1,另一個是sha256。 signtool和我的代碼只能讀取其中的一個,sha1。這個簽名(sha1)有6個與它關聯的證書,它們顯示在signtool輸出中。我可以在文件屬性對話框中查看與sha256相關聯的另外6個證書(如果您深入瞭解使用細節按鈕,但是signtool和我的代碼都不能顯示它們)換句話說:如果您將時間戳簽名(聯署簽名)作爲單獨簽名處理,這個DLL總共有4個簽名,但signtool只能顯示2. – Dima

13

經過大量的挖掘和嘗試不同的事情,我發現功能WinVerifyTrust可以讀取多個嵌入式證書。忽略功能名稱,它可以用於很多的東西,這是一個通用功能。

WinVerifyTrust以結構WINTRUST_DATA作爲其輸入/輸出參數之一。 Docs表示它是IN,但它也用於返回信息。

WINTRUST_DATA有字段pSignatureSettings,這是一個指向另一個結構的指針,WINTRUST_SIGNATURE_SETTINGS。這個stuct有dwFlags,控制什麼信息將由WinVerifyTrust返回。

首先調用的WinVerifyTrust與WINTRUST_SIGNATURE_SETTINGS::dwFlags = WSS_GET_SECONDARY_SIG_COUNT找回二次簽名,這是在該領域WINTRUST_SIGNATURE_SETTINGS::cSecondarySigs返回的數量。請注意,如果你的文件有2個簽名,cSecondarySigs將爲1

然後在循環for (int i = 0; i <= cSecondarySigs; i++)你叫的WinVerifyTrust與WINTRUST_SIGNATURE_SETTINGS::dwFlags = WSS_VERIFY_SPECIFICWINTRUST_SIGNATURE_SETTINGS::dwIndex = i

此調用序列的每個的WinVerifyTrust電話就可以得到證書信息(包括聯署)從WINTRUST_DATA::hWVTStateData後:

WTHelperProvDataFromStateData(hWVTStateData); 
WTHelperGetProvSignerFromChain(...); 
WTHelperGetProvCertFromChain(...); 

我並沒有太多深入到.NET API,但似乎它可以讀取只有第一個簽名。請注意,WINTRUST_SIGNATURE_SETTINGS似乎是讀取多個簽名的關鍵,在Windows 8中添加了,因此在較舊的操作系統中,您將無法讀取它,至少不能使用MS API。

+1

這正是我一直在尋找的。謝謝! –

0

最新版本的SignTool.exe可以處理多個簽名。

其中之一就是使用/ ds開關。這使您可以選擇簽名索引。

更棒的是,這裏有一個很棒的C#示例,它將讀取和驗證多個簽名。 Code signing an executable twice

4

擴展Dima的答案,我想提供一個演示如何檢查所有嵌入(和嵌套)葉(不在證書鏈中間)證書的示例代碼。

BOOL CheckCertificateIssuer(HANDLE hWVTStateData, const std::set<CString> &stValidIssuers) 
{ 
    CRYPT_PROVIDER_DATA *pCryptProvData = WTHelperProvDataFromStateData(hWVTStateData); 
    CRYPT_PROVIDER_SGNR *pSigner = WTHelperGetProvSignerFromChain(pCryptProvData, 0, FALSE, 0); 
    CRYPT_PROVIDER_CERT *pCert = WTHelperGetProvCertFromChain(pSigner, 0); 

    CString sIssuer; 
    int nLength = CertGetNameString(pCert->pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, NULL, 0); 
    if (!nLength) 
    { 
     ASSERT(FALSE && "Cannot get the length of the Issuer string"); 
     return FALSE; 
    } 

    if (!CertGetNameString(pCert->pCert, CERT_NAME_SIMPLE_DISPLAY_TYPE, CERT_NAME_ISSUER_FLAG, NULL, sIssuer.GetBuffer(nLength), nLength)) 
    { 
     ASSERT(FALSE && "Cannot get the Issuer string"); 
     return FALSE; 
    } 
    sIssuer.ReleaseBuffer(nLength); 
    if (stValidIssuers.find(sIssuer) == stValidIssuers.end()) 
    { 
     ASSERT(FALSE && "Certificate issuer is invalid"); 
     return FALSE; 
    } 
    return TRUE; 
} 
BOOL CheckCertificate(CString filename) 
{ 
    std::set<CString> stValidIssuers; 
    stValidIssuers.insert(L"VeriSign Class 3 Code Signing 2010 CA"); 
    stValidIssuers.insert(L"Symantec Class 3 SHA256 Code Signing CA"); 

    bool UseStrongSigPolicy = false; 

    DWORD Error = ERROR_SUCCESS; 
    bool WintrustCalled = false; 
    GUID GenericActionId = WINTRUST_ACTION_GENERIC_VERIFY_V2; 
    WINTRUST_DATA WintrustData = {}; 
    WINTRUST_FILE_INFO FileInfo = {}; 
    WINTRUST_SIGNATURE_SETTINGS SignatureSettings = {}; 
    CERT_STRONG_SIGN_PARA StrongSigPolicy = {}; 

    // Setup data structures for calling WinVerifyTrust 
    WintrustData.cbStruct = sizeof(WINTRUST_DATA); 
    WintrustData.dwStateAction = WTD_STATEACTION_VERIFY; 
    WintrustData.dwUIChoice = WTD_UI_NONE; 
    WintrustData.fdwRevocationChecks = WTD_REVOKE_NONE; 
    WintrustData.dwUnionChoice = WTD_CHOICE_FILE; 

    FileInfo.cbStruct = sizeof(WINTRUST_FILE_INFO_); 
    FileInfo.pcwszFilePath = filename; 
    WintrustData.pFile = &FileInfo; 

    // 
    // First verify the primary signature (index 0) to determine how many secondary signatures 
    // are present. We use WSS_VERIFY_SPECIFIC and dwIndex to do this, also setting 
    // WSS_GET_SECONDARY_SIG_COUNT to have the number of secondary signatures returned. 
    // 
    SignatureSettings.cbStruct = sizeof(WINTRUST_SIGNATURE_SETTINGS); 
    SignatureSettings.dwFlags = WSS_GET_SECONDARY_SIG_COUNT | WSS_VERIFY_SPECIFIC; 
    SignatureSettings.dwIndex = 0; 
    WintrustData.pSignatureSettings = &SignatureSettings; 

    if (UseStrongSigPolicy != false) 
    { 
     StrongSigPolicy.cbSize = sizeof(CERT_STRONG_SIGN_PARA); 
     StrongSigPolicy.dwInfoChoice = CERT_STRONG_SIGN_OID_INFO_CHOICE; 
     StrongSigPolicy.pszOID = szOID_CERT_STRONG_SIGN_OS_CURRENT; 
     WintrustData.pSignatureSettings->pCryptoPolicy = &StrongSigPolicy; 
    } 
    BOOL bResult = E_NOT_SET; 
    TRACE(L"Verifying primary signature... "); 
    Error = WinVerifyTrust(NULL, &GenericActionId, &WintrustData); 
    WintrustCalled = true; 
    if (Error == ERROR_SUCCESS) 
    { 
     if (CheckCertificateIssuer(WintrustData.hWVTStateData, stValidIssuers)) 
     { 
      if (bResult == E_NOT_SET) 
       bResult = TRUE; 
     } 
     else 
     { 
      bResult = FALSE; 
     } 

     TRACE(L"Success!\n"); 

     TRACE(L"Found %d secondary signatures\n", WintrustData.pSignatureSettings->cSecondarySigs); 

     // Now attempt to verify all secondary signatures that were found 
     for (DWORD x = 1; x <= WintrustData.pSignatureSettings->cSecondarySigs; x++) 
     { 
      TRACE(L"Verify secondary signature at index %d... ", x); 

      // Need to clear the previous state data from the last call to WinVerifyTrust 
      WintrustData.dwStateAction = WTD_STATEACTION_CLOSE; 
      Error = WinVerifyTrust(NULL, &GenericActionId, &WintrustData); 
      if (Error != ERROR_SUCCESS) 
      { 
       //No need to call WinVerifyTrust again 
       WintrustCalled = false; 
       TRACE(L"%s", utils::error::getText(Error)); 
       ASSERT(FALSE); 
       break; 
      } 

      WintrustData.hWVTStateData = NULL; 

      // Caller must reset dwStateAction as it may have been changed during the last call 
      WintrustData.dwStateAction = WTD_STATEACTION_VERIFY; 
      WintrustData.pSignatureSettings->dwIndex = x; 
      Error = WinVerifyTrust(NULL, &GenericActionId, &WintrustData); 
      if (Error != ERROR_SUCCESS) 
      { 
       TRACE(L"%s", utils::error::getText(Error)); 
       ASSERT(FALSE); 
       break; 
      } 

      if (CheckCertificateIssuer(WintrustData.hWVTStateData, stValidIssuers)) 
      { 
       if (bResult == E_NOT_SET) 
        bResult = TRUE; 
      } 
      else 
      { 
       bResult = FALSE; 
      } 


      TRACE(L"Success!\n"); 
     } 
    } 
    else 
    { 
     TRACE(utils::error::getText(Error)); 
     ASSERT(FALSE); 
    } 

    // 
    // Caller must call WinVerifyTrust with WTD_STATEACTION_CLOSE to free memory 
    // allocate by WinVerifyTrust 
    // 
    if (WintrustCalled != false) 
    { 
     WintrustData.dwStateAction = WTD_STATEACTION_CLOSE; 
     WinVerifyTrust(NULL, &GenericActionId, &WintrustData); 
    } 

    return bResult; 

}