【问题标题】:How to retrieve information from multiple/dual code signatures on an executable file如何从可执行文件上的多个/双重代码签名中检索信息
【发布时间】:2016-08-24 05:40:41
【问题描述】:

我一直在使用以下代码(取自KB323809 article)来检索有关可执行文件上的代码签名的信息。这适用于单个数字签名。

但是如何检索多个代码签名的信息?

在这种情况下,下面的 Microsoft 代码仅检索第一个签名的信息。

我的想法是用CMSG_SIGNER_COUNT_PARAM 调用CryptMsgGetParam 以获取签名数量,然后将每个签名索引传递给随后使用CMSG_SIGNER_INFO_PARAM 调用CryptMsgGetParam(在下面的代码中。)但是这种方法总是返回 1 个签名,即使我显然有更多签名,例如本例中的 3 个:

#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>

#pragma comment(lib, "crypt32.lib")

#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)

typedef struct {
    LPWSTR lpszProgramName;
    LPWSTR lpszPublisherLink;
    LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;

BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
                             PSPROG_PUBLISHERINFO Info);
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st);
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext);
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,
                            PCMSG_SIGNER_INFO *pCounterSignerInfo);

int _tmain(int argc, TCHAR *argv[])
{
    WCHAR szFileName[MAX_PATH]; 
    HCERTSTORE hStore = NULL;
    HCRYPTMSG hMsg = NULL; 
    PCCERT_CONTEXT pCertContext = NULL;
    BOOL fResult;   
    DWORD dwEncoding, dwContentType, dwFormatType;
    PCMSG_SIGNER_INFO pSignerInfo = NULL;
    PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
    DWORD dwSignerInfo;
    CERT_INFO CertInfo;     
    SPROG_PUBLISHERINFO ProgPubInfo;
    SYSTEMTIME st;

    ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
    __try
    {
        if (argc != 2)
        {
            _tprintf(_T("Usage: SignedFileInfo <filename>\n"));
            return 0;
        }

#ifdef UNICODE
        lstrcpynW(szFileName, argv[1], MAX_PATH);
#else
        if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1)
        {
            printf("Unable to convert to unicode.\n");
            __leave;
        }
#endif

        // Get message handle and store handle from the signed file.
        fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
                                   szFileName,
                                   CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
                                   CERT_QUERY_FORMAT_FLAG_BINARY,
                                   0,
                                   &dwEncoding,
                                   &dwContentType,
                                   &dwFormatType,
                                   &hStore,
                                   &hMsg,
                                   NULL);
        if (!fResult)
        {
            _tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
            __leave;
        }

        // Get signer information size.
        fResult = CryptMsgGetParam(hMsg, 
                                   CMSG_SIGNER_INFO_PARAM, 
                                   0, 
                                   NULL, 
                                   &dwSignerInfo);
        if (!fResult)
        {
            _tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
            __leave;
        }

        // Allocate memory for signer information.
        pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
        if (!pSignerInfo)
        {
            _tprintf(_T("Unable to allocate memory for Signer Info.\n"));
            __leave;
        }

        // Get Signer Information.
        fResult = CryptMsgGetParam(hMsg, 
                                   CMSG_SIGNER_INFO_PARAM, 
                                   0, 
                                   (PVOID)pSignerInfo, 
                                   &dwSignerInfo);
        if (!fResult)
        {
            _tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
            __leave;
        }

        // Get program name and publisher information from 
        // signer info structure.
        if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
        {
            if (ProgPubInfo.lpszProgramName != NULL)
            {
                wprintf(L"Program Name : %s\n",
                    ProgPubInfo.lpszProgramName);
            }

            if (ProgPubInfo.lpszPublisherLink != NULL)
            {
                wprintf(L"Publisher Link : %s\n",
                    ProgPubInfo.lpszPublisherLink);
            }

            if (ProgPubInfo.lpszMoreInfoLink != NULL)
            {
                wprintf(L"MoreInfo Link : %s\n",
                    ProgPubInfo.lpszMoreInfoLink);
            }
        }

        _tprintf(_T("\n"));

        // Search for the signer certificate in the temporary 
        // certificate store.
        CertInfo.Issuer = pSignerInfo->Issuer;
        CertInfo.SerialNumber = pSignerInfo->SerialNumber;

        pCertContext = CertFindCertificateInStore(hStore,
                                                  ENCODING,
                                                  0,
                                                  CERT_FIND_SUBJECT_CERT,
                                                  (PVOID)&CertInfo,
                                                  NULL);
        if (!pCertContext)
        {
            _tprintf(_T("CertFindCertificateInStore failed with %x\n"),
                GetLastError());
            __leave;
        }

        // Print Signer certificate information.
        _tprintf(_T("Signer Certificate:\n\n"));        
        PrintCertificateInfo(pCertContext);
        _tprintf(_T("\n"));

        // Get the timestamp certificate signerinfo structure.
        if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
        {
            // Search for Timestamp certificate in the temporary
            // certificate store.
            CertInfo.Issuer = pCounterSignerInfo->Issuer;
            CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;

            pCertContext = CertFindCertificateInStore(hStore,
                                                ENCODING,
                                                0,
                                                CERT_FIND_SUBJECT_CERT,
                                                (PVOID)&CertInfo,
                                                NULL);
            if (!pCertContext)
            {
                _tprintf(_T("CertFindCertificateInStore failed with %x\n"),
                    GetLastError());
                __leave;
            }

            // Print timestamp certificate information.
            _tprintf(_T("TimeStamp Certificate:\n\n"));
            PrintCertificateInfo(pCertContext);
            _tprintf(_T("\n"));

            // Find Date of timestamp.
            if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
            {
                _tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
                                            st.wMonth,
                                            st.wDay,
                                            st.wYear,
                                            st.wHour,
                                            st.wMinute);
            }
            _tprintf(_T("\n"));
        }
    }
    __finally
    {               
        // Clean up.
        if (ProgPubInfo.lpszProgramName != NULL)
            LocalFree(ProgPubInfo.lpszProgramName);
        if (ProgPubInfo.lpszPublisherLink != NULL)
            LocalFree(ProgPubInfo.lpszPublisherLink);
        if (ProgPubInfo.lpszMoreInfoLink != NULL)
            LocalFree(ProgPubInfo.lpszMoreInfoLink);

        if (pSignerInfo != NULL) LocalFree(pSignerInfo);
        if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);
        if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
        if (hStore != NULL) CertCloseStore(hStore, 0);
        if (hMsg != NULL) CryptMsgClose(hMsg);
    }
    return 0;
}

BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext)
{
    BOOL fReturn = FALSE;
    LPTSTR szName = NULL;
    DWORD dwData;

    __try
    {
        // Print Serial Number.
        _tprintf(_T("Serial Number: "));
        dwData = pCertContext->pCertInfo->SerialNumber.cbData;
        for (DWORD n = 0; n < dwData; n++)
        {
            _tprintf(_T("%02x "),
              pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);
        }
        _tprintf(_T("\n"));

        // Get Issuer name size.
        if (!(dwData = CertGetNameString(pCertContext, 
                                         CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                         CERT_NAME_ISSUER_FLAG,
                                         NULL,
                                         NULL,
                                         0)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // Allocate memory for Issuer name.
        szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
        if (!szName)
        {
            _tprintf(_T("Unable to allocate memory for issuer name.\n"));
            __leave;
        }

        // Get Issuer name.
        if (!(CertGetNameString(pCertContext, 
                                CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                CERT_NAME_ISSUER_FLAG,
                                NULL,
                                szName,
                                dwData)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // print Issuer name.
        _tprintf(_T("Issuer Name: %s\n"), szName);
        LocalFree(szName);
        szName = NULL;

        // Get Subject name size.
        if (!(dwData = CertGetNameString(pCertContext, 
                                         CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                         0,
                                         NULL,
                                         NULL,
                                         0)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // Allocate memory for subject name.
        szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
        if (!szName)
        {
            _tprintf(_T("Unable to allocate memory for subject name.\n"));
            __leave;
        }

        // Get subject name.
        if (!(CertGetNameString(pCertContext, 
                                CERT_NAME_SIMPLE_DISPLAY_TYPE,
                                0,
                                NULL,
                                szName,
                                dwData)))
        {
            _tprintf(_T("CertGetNameString failed.\n"));
            __leave;
        }

        // Print Subject Name.
        _tprintf(_T("Subject Name: %s\n"), szName);

        fReturn = TRUE;
    }
    __finally
    {
        if (szName != NULL) LocalFree(szName);
    }

    return fReturn;
}

LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
    LPWSTR outputString = NULL;

    outputString = (LPWSTR)LocalAlloc(LPTR,
        (wcslen(inputString) + 1) * sizeof(WCHAR));
    if (outputString != NULL)
    {
        lstrcpyW(outputString, inputString);
    }
    return outputString;
}

BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
                             PSPROG_PUBLISHERINFO Info)
{
    BOOL fReturn = FALSE;
    PSPC_SP_OPUS_INFO OpusInfo = NULL;  
    DWORD dwData;
    BOOL fResult;

    __try
    {
        // Loop through authenticated attributes and find
        // SPC_SP_OPUS_INFO_OBJID OID.
        for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
        {           
            if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID, 
                        pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
            {
                // Get Size of SPC_SP_OPUS_INFO structure.
                fResult = CryptDecodeObject(ENCODING,
                            SPC_SP_OPUS_INFO_OBJID,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                            0,
                            NULL,
                            &dwData);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                // Allocate memory for SPC_SP_OPUS_INFO structure.
                OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData);
                if (!OpusInfo)
                {
                    _tprintf(_T("Unable to allocate memory for Publisher Info.\n"));
                    __leave;
                }

                // Decode and get SPC_SP_OPUS_INFO structure.
                fResult = CryptDecodeObject(ENCODING,
                            SPC_SP_OPUS_INFO_OBJID,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                            pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                            0,
                            OpusInfo,
                            &dwData);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                // Fill in Program Name if present.
                if (OpusInfo->pwszProgramName)
                {
                    Info->lpszProgramName =
                        AllocateAndCopyWideString(OpusInfo->pwszProgramName);
                }
                else
                    Info->lpszProgramName = NULL;

                // Fill in Publisher Information if present.
                if (OpusInfo->pPublisherInfo)
                {

                    switch (OpusInfo->pPublisherInfo->dwLinkChoice)
                    {
                        case SPC_URL_LINK_CHOICE:
                            Info->lpszPublisherLink =
                                AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
                            break;

                        case SPC_FILE_LINK_CHOICE:
                            Info->lpszPublisherLink =
                                AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
                            break;

                        default:
                            Info->lpszPublisherLink = NULL;
                            break;
                    }
                }
                else
                {
                    Info->lpszPublisherLink = NULL;
                }

                // Fill in More Info if present.
                if (OpusInfo->pMoreInfo)
                {
                    switch (OpusInfo->pMoreInfo->dwLinkChoice)
                    {
                        case SPC_URL_LINK_CHOICE:
                            Info->lpszMoreInfoLink =
                                AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
                            break;

                        case SPC_FILE_LINK_CHOICE:
                            Info->lpszMoreInfoLink =
                                AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
                            break;

                        default:
                            Info->lpszMoreInfoLink = NULL;
                            break;
                    }
                }               
                else
                {
                    Info->lpszMoreInfoLink = NULL;
                }

                fReturn = TRUE;

                break; // Break from for loop.
            } // lstrcmp SPC_SP_OPUS_INFO_OBJID                 
        } // for 
    }
    __finally
    {
        if (OpusInfo != NULL) LocalFree(OpusInfo);      
    }

    return fReturn;
}

BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st)
{   
    BOOL fResult;
    FILETIME lft, ft;   
    DWORD dwData;
    BOOL fReturn = FALSE;

    // Loop through authenticated attributes and find
    // szOID_RSA_signingTime OID.
    for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
    {           
        if (lstrcmpA(szOID_RSA_signingTime, 
                    pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
        {               
            // Decode and get FILETIME structure.
            dwData = sizeof(ft);
            fResult = CryptDecodeObject(ENCODING,
                        szOID_RSA_signingTime,
                        pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
                        pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
                        0,
                        (PVOID)&ft,
                        &dwData);
            if (!fResult)
            {
                _tprintf(_T("CryptDecodeObject failed with %x\n"),
                    GetLastError());
                break;
            }

            // Convert to local time.
            FileTimeToLocalFileTime(&ft, &lft);
            FileTimeToSystemTime(&lft, st);

            fReturn = TRUE;

            break; // Break from for loop.

        } //lstrcmp szOID_RSA_signingTime
    } // for 

    return fReturn;
}

BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo)
{   
    PCCERT_CONTEXT pCertContext = NULL;
    BOOL fReturn = FALSE;
    BOOL fResult;       
    DWORD dwSize;   

    __try
    {
        *pCounterSignerInfo = NULL;

        // Loop through unathenticated attributes for
        // szOID_RSA_counterSign OID.
        for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
        {
            if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId, 
                         szOID_RSA_counterSign) == 0)
            {
                // Get size of CMSG_SIGNER_INFO structure.
                fResult = CryptDecodeObject(ENCODING,
                           PKCS7_SIGNER_INFO,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
                           0,
                           NULL,
                           &dwSize);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                // Allocate memory for CMSG_SIGNER_INFO.
                *pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize);
                if (!*pCounterSignerInfo)
                {
                    _tprintf(_T("Unable to allocate memory for timestamp info.\n"));
                    __leave;
                }

                // Decode and get CMSG_SIGNER_INFO structure
                // for timestamp certificate.
                fResult = CryptDecodeObject(ENCODING,
                           PKCS7_SIGNER_INFO,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
                           pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
                           0,
                           (PVOID)*pCounterSignerInfo,
                           &dwSize);
                if (!fResult)
                {
                    _tprintf(_T("CryptDecodeObject failed with %x\n"),
                        GetLastError());
                    __leave;
                }

                fReturn = TRUE;

                break; // Break from for loop.
            }           
        }
    }
    __finally
    {
        // Clean up.
        if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
    }

    return fReturn;
}

【问题讨论】:

  • 事实上,CryptMsgGetParam 为 CMSG_SIGNER_INFO_PARAM 指定:“要检索有关消息的所有签名者的签名者信息,请调用 CryptMsgGetParam 将 dwIndex 从 0 更改为签名者数减一”。您的代码似乎没有使用此选项,如果您将 CMSG_SIGNER_INFO_PARAM 调用的 dwIndex 设置为 1,返回值是多少(和 GetLastError())?
  • @TonPlooij:那不是我的代码。这是 KB323809 中的一个示例。我只是说如果我用CMSG_SIGNER_COUNT_PARAM 打电话给CryptMsgGetParam,似乎只有一个签名者。如果我用CMSG_SIGNER_INFO_PARAM 调用CryptMsgGetParam 以获取大于0 的索引,它将失败,错误代码为0x80091008,即The index value is not valid.
  • 文档中还列出了许多其他“计数”参数(CMSG_ATTR_CERT_COUNT_PARAMCMSG_CERT_COUNT_PARAM 等)。他们中的任何一个都返回 3 吗?
  • @JonathanPotter:我不想仅仅“戳”这些参数。我想了解应该怎么做。这就是我发布这个问题的原因。
  • 有时在 Windows 上你只需要尝试一下。

标签: c++ windows winapi certificate code-signing


【解决方案1】:

Authenticode 将辅助签名存储在主要签名者(索引 0)的 UnauthenticatedAttributes 中,而不是额外的 PKCS 7 签名者。

从主签名中,搜索 UauthenticatedAttribue 以获取以下内容:

//表示该属性是八位字节编码的PKCS7

定义 szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"

此属性的编码对象是完整的 PKCS 7 签名者。

谢谢。

【讨论】:

    【解决方案2】:

    除了 Daniel Sie 的回答。

    找到一个属性 (szOID_NESTED_SIGNATURE),其中包含需要通过以下步骤解码的 CMSG_SIGNER_INFO(这是 Delphi 代码,但意义很明确):

      LNestedMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, 0);
      CryptMsgUpdate(LNestedMsg, LFindedAttr.rgValue.pbData, LFindedAttr.rgValue.cbData, True);
      CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, @LSize);
      CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, LNestedSignerInfo, @LSize);
    

    获取的CMSG_SIGNER_INFO (LNestedSignerInfo) 是嵌套签名(在我们的例子中是 SHA2 签名)。

    要获取该签名的时间戳信息 (RFC3161) - 使用 pszObjId = szOID_RFC3161_counterSign (1.3.6.1.4.1.311.3.3.1) 搜索 Unauthenticated 属性。

    Found 属性将包含时间戳计数器签名的CMSG_SIGNER_INFO,它还需要通过前面描述的步骤(CryptMsgOpenToDecode、CryptMsgUpdate、CryptMsgGetParam)进行解码。

    嵌套签名或时间戳计数器签名的CERT_CONTEXT需要在store中查找,从对应的HCRYPTMSG中获取(CryptMsgOpenToDecode的结果)。

      LNestedStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LNestedMsg);
      LTimeStampStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LTimeStampMsg);
    

    解码 szOID_RFC3161_counterSign 属性的示例:

    LNestedSignerAttr := LNestedSigner.UnauthAttrs.rgAttr;
    for I := 0 to LNestedSigner.UnauthAttrs.cAttr - 1 do
    begin
      if SameText(string(LNestedSignerAttr.pszObjId), szOID_RFC3161_counterSign) then
      begin
        LNestedTimeStampMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, nil);
        if not Assigned(LNestedTimeStampMsg) then
          RaiseLastOSError;
        try
          if not CryptMsgUpdate(LNestedTimeStampMsg, LNestedSignerAttr.rgValue.pbData, LNestedSignerAttr.rgValue.cbData, True) then
            RaiseLastOSError;
    
          if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, @LSize) then
            RaiseLastOSError;
          GetMem(LTimeStampSigner, LSize);
          try
            if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, LTimeStampSigner, @LSize) then
              RaiseLastOSError;
    
            LAttr := LTimeStampSigner.AuthAttrs.rgAttr;
            for J := 0 to LTimeStampSigner.AuthAttrs.cAttr - 1 do
            begin
              if SameText(string(LAttr.pszObjId), szOID_RSA_signingTime) then
              begin
                LSize := SizeOf(LFileTime);
                if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
                  szOID_RSA_signingTime, LAttr.rgValue.pbData, LAttr.rgValue.cbData, 0, @LFileTime, @LSize) then
                  RaiseLastOSError;
    
                if FileTimeToLocalFileTime(@LFileTime, LLocalFileTime)
                  and FileTimeToSystemTime(@LLocalFileTime, LSystemTime) then
                    SHA2TimeStamp := SystemTimeToDateTime(LSystemTime)
                else
                  SHA2TimeStamp := 0;
    
              end;
              Inc(LAttr);
            end;
    
          finally
            FreeMem(LTimeStampSigner);
          end;
        finally
          if not CryptMsgClose(LNestedTimeStampMsg) then
            RaiseLastOSError;
        end;
      end;
    
      Inc(LNestedSignerAttr);
    end;
    

    【讨论】:

    猜你喜欢
    • 2018-12-01
    • 2012-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-16
    • 1970-01-01
    相关资源
    最近更新 更多