簡體   English   中英

從可執行文件中讀取多個簽名

[英]Reading multiple signatures from executable file

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

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

但是有些DLL有2個簽名,如文件屬性/數字簽名所示,例如C:\\ Program Files(x86)\\ Microsoft SQL Server \\ 80 \\ Tools \\ Binn \\ msvcr71.dll:

msvcr71.dll的文件屬性和數字簽名

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

我也嘗試使用signtool,它返回與我的代碼相同的信息:first cert(帶有它的路徑)和counterignature(帶有它的路徑)。 但最后還要注意錯誤。

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

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

謝謝!

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

WinVerifyTrust將struct WINTRUST_DATA作為其輸入/輸出參數之一。 Docs說它是IN ,但它也用於返回信息。

WINTRUST_DATA具有字段pSignatureSettings ,它是指向另一個結構WINTRUST_SIGNATURE_SETTINGS的指針。 此stuct具有字段dwFlags ,用於控制WinVerifyTrust將返回的信息。

首先使用WINTRUST_SIGNATURE_SETTINGS::dwFlags = WSS_GET_SECONDARY_SIG_COUNT調用WinVerifyTrust以獲取輔助簽名的數量,該簽名在WINTRUST_SIGNATURE_SETTINGS::cSecondarySigs字段中返回。 請注意,如果您的文件有2個簽名,則cSecondarySigs將為1。

然后在for (int i = 0; i <= cSecondarySigs; i++)的循環中for (int i = 0; i <= cSecondarySigs; i++)使用WINTRUST_SIGNATURE_SETTINGS::dwFlags = WSS_VERIFY_SPECIFICWINTRUST_SIGNATURE_SETTINGS::dwIndex = i調用WinVerifyTrust

在每次WinVerifyTrust調用之后,您可以通過此調用序列從WINTRUST_DATA::hWVTStateData獲取證書信息(包括WINTRUST_DATA::hWVTStateData ):

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

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

擴展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;

}

看着

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

Issued to: Microsoft Time-Stamp Service

我假設第二個簽名/證書用於時間戳文件。 很可能MS有兩個不同的組織單元,其中一個簽署代碼以確認其完整性,另一個(稍后)使用自己的證書再次簽署代碼,專門用於對文件進行安全時間戳。

可以創建證書並將其分配給某些用法。 用於時間戳的證書可以這樣標記,因此signtool在遇到時間戳證書時可能會出錯,因為默認情況下它需要證書來進行代碼真實性/完整性驗證。不是時間戳。

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

一個是使用/ ds開關。 這允許您選擇簽名索引。

更好的是,這是一個很好的C#示例,它將讀取和驗證多個簽名。 代碼簽署可執行文件兩次

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM