【问题标题】:Get timestamp from Authenticode Signed files in .NET从 .NET 中的 Authenticode 签名文件中获取时间戳
【发布时间】:2011-03-17 21:36:39
【问题描述】:

我们需要验证二进制文件是否使用数字签名(Authenticode)正确签名。这可以通过signtool.exe 轻松实现。但是,我们需要一种自动方式来验证签名者姓名和时间戳。这在使用 CryptQueryObject() API 的本机 C++ 中是可行的,如以下精彩示例所示:How To Get Information from Authenticode Signed Executables

然而,我们生活在一个托管的世界中 :) 因此寻找相同问题的 C# 解决方案。直接的方法是 pInvoke Crypt32.dll,一切都完成了。但是System.Security.Cryptography.X509Certificates 命名空间中有类似的托管 API。 X509Certificate2 Class 似乎提供了一些信息,但没有时间戳。现在我们来到了最初的问题,我们如何在 C Sharp 中获取数字签名的时间戳?

【问题讨论】:

  • 托管的验证码类留下了很多!只需 p/Invoke 可能更容易。
  • 第一个原因是托管代码看起来更好。其次,在几篇 MSDN 文章中,有类似这样的可怕注释。 注意 WinVerifyTrust 函数可用于“要求”部分中列出的操作系统。在后续版本中可能会更改或不可用。其中 Windows Vista 是最新支持的系统。当然 WinVerifyTrust 和 CryptQueryObject 可以在 Windows 7 上工作,但是在所有这些警告下,使用其他一些 API 似乎是合乎逻辑的。似乎 Certificates 命名空间确实是半生不熟的。有趣,但现在在线 MSDN 中不存在此警告......
  • @StephenCleary .NET Core 解决这个问题也将是一个不错的奖励。

标签: c# .net certificate signature authenticode


【解决方案1】:

回到最初的问题,我找不到托管方式,所以最终使用 pInvoke 如下:

public static bool IsTimestamped(string filename)
{
    try
    {
        int encodingType;
        int contentType;
        int formatType;
        IntPtr certStore = IntPtr.Zero;
        IntPtr cryptMsg = IntPtr.Zero;
        IntPtr context = IntPtr.Zero;

        if (!WinCrypt.CryptQueryObject(
            WinCrypt.CERT_QUERY_OBJECT_FILE,
            Marshal.StringToHGlobalUni(filename),
            WinCrypt.CERT_QUERY_CONTENT_FLAG_ALL,
            WinCrypt.CERT_QUERY_FORMAT_FLAG_ALL,
            0,
            out encodingType,
            out contentType,
            out formatType,
            ref certStore,
            ref cryptMsg,
            ref context))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        //expecting contentType=10; CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED 
        //Logger.LogInfo(string.Format("Querying file '{0}':", filename));
        //Logger.LogInfo(string.Format("  Encoding Type: {0}", encodingType));
        //Logger.LogInfo(string.Format("  Content Type: {0}", contentType));
        //Logger.LogInfo(string.Format("  Format Type: {0}", formatType));
        //Logger.LogInfo(string.Format("  Cert Store: {0}", certStore.ToInt32()));
        //Logger.LogInfo(string.Format("  Crypt Msg: {0}", cryptMsg.ToInt32()));
        //Logger.LogInfo(string.Format("  Context: {0}", context.ToInt32()));


        // Get size of the encoded message.
        int cbData = 0;
        if (!WinCrypt.CryptMsgGetParam(
            cryptMsg,
            WinCrypt.CMSG_ENCODED_MESSAGE,//Crypt32.CMSG_SIGNER_INFO_PARAM,
            0,
            IntPtr.Zero,
            ref cbData))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        var vData = new byte[cbData];

        // Get the encoded message.
        if (!WinCrypt.CryptMsgGetParam(
            cryptMsg,
            WinCrypt.CMSG_ENCODED_MESSAGE,//Crypt32.CMSG_SIGNER_INFO_PARAM,
            0,
            vData,
            ref cbData))
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }

        var signedCms = new SignedCms();
        signedCms.Decode(vData);

        foreach (var signerInfo in signedCms.SignerInfos)
        {
            foreach (var unsignedAttribute in signerInfo.UnsignedAttributes)
            {
                if (unsignedAttribute.Oid.Value == WinCrypt.szOID_RSA_counterSign)
                {
                    //Note at this point we assume this counter signature is the timestamp
                    //refer to http://support.microsoft.com/kb/323809 for the origins

                    //TODO: extract timestamp value, if required
                    return true;
                }

            }
        }
    }
    catch (Exception)
    {
        // no logging
    }

    return false;
}

WinCrypt.cs 包含以下内容:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;

namespace MyNamespace.Win32
{
    static class WinCrypt
    {
        [StructLayout(LayoutKind.Sequential)]
        public struct BLOB
        {
            public int cbData;
            public IntPtr pbData;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CRYPT_ALGORITHM_IDENTIFIER
        {
            public String pszObjId;
            BLOB Parameters;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CERT_ID
        {
            public int dwIdChoice;
            public BLOB IssuerSerialNumberOrKeyIdOrHashId;
        }

        [StructLayoutAttribute(LayoutKind.Sequential)]
        public struct SIGNER_SUBJECT_INFO
        {
            /// DWORD->unsigned int
            public uint cbSize;

            /// DWORD*
            public System.IntPtr pdwIndex;

            /// DWORD->unsigned int
            public uint dwSubjectChoice;

            /// SubjectChoiceUnion
            public SubjectChoiceUnion Union1;
        }

        [StructLayoutAttribute(LayoutKind.Explicit)]
        public struct SubjectChoiceUnion
        {

            /// SIGNER_FILE_INFO*
            [FieldOffsetAttribute(0)]
            public System.IntPtr pSignerFileInfo;

            /// SIGNER_BLOB_INFO*
            [FieldOffsetAttribute(0)]
            public System.IntPtr pSignerBlobInfo;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CERT_NAME_BLOB
        {
            public uint cbData;
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
            public byte[] pbData;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CRYPT_INTEGER_BLOB
        {
            public UInt32 cbData;
            public IntPtr pbData;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CRYPT_ATTR_BLOB
        {
            public uint cbData;
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
            public byte[] pbData;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CRYPT_ATTRIBUTE
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public string pszObjId;
            public uint cValue;
            [MarshalAs(UnmanagedType.LPStruct)]
            public CRYPT_ATTR_BLOB rgValue;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct CMSG_SIGNER_INFO
        {
            public int dwVersion;
            private CERT_NAME_BLOB Issuer;
            CRYPT_INTEGER_BLOB SerialNumber;
            CRYPT_ALGORITHM_IDENTIFIER HashAlgorithm;
            CRYPT_ALGORITHM_IDENTIFIER HashEncryptionAlgorithm;
            BLOB EncryptedHash;
            CRYPT_ATTRIBUTE[] AuthAttrs;
            CRYPT_ATTRIBUTE[] UnauthAttrs;
        }

        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean 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);


        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CryptMsgGetParam(
            IntPtr hCryptMsg,
            int dwParamType,
            int dwIndex,
            IntPtr pvData,
            ref int pcbData
        );

        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern Boolean CryptMsgGetParam(
            IntPtr hCryptMsg,
            int dwParamType,
            int dwIndex,
            [In, Out] byte[] vData,
            ref int pcbData
        );

        [DllImport("crypt32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CryptDecodeObject(
          uint CertEncodingType,
          UIntPtr lpszStructType,
          byte[] pbEncoded,
          uint cbEncoded,
          uint flags,
          [In, Out] byte[] pvStructInfo,
          ref uint cbStructInfo);


        public const int CRYPT_ASN_ENCODING = 0x00000001;
        public const int CRYPT_NDR_ENCODING = 0x00000002;
        public const int X509_ASN_ENCODING = 0x00000001;
        public const int X509_NDR_ENCODING = 0x00000002;
        public const int PKCS_7_ASN_ENCODING = 0x00010000;
        public const int PKCS_7_NDR_ENCODING = 0x00020000;

        public static UIntPtr PKCS7_SIGNER_INFO = new UIntPtr(500);
        public static UIntPtr CMS_SIGNER_INFO = new UIntPtr(501);

        public static string szOID_RSA_signingTime = "1.2.840.113549.1.9.5";
        public static string szOID_RSA_counterSign = "1.2.840.113549.1.9.6";

        //+-------------------------------------------------------------------------
        //  Get parameter types and their corresponding data structure definitions.
        //--------------------------------------------------------------------------
        public const int CMSG_TYPE_PARAM = 1;
        public const int CMSG_CONTENT_PARAM = 2;
        public const int CMSG_BARE_CONTENT_PARAM = 3;
        public const int CMSG_INNER_CONTENT_TYPE_PARAM = 4;
        public const int CMSG_SIGNER_COUNT_PARAM = 5;
        public const int CMSG_SIGNER_INFO_PARAM = 6;
        public const int CMSG_SIGNER_CERT_INFO_PARAM = 7;
        public const int CMSG_SIGNER_HASH_ALGORITHM_PARAM = 8;
        public const int CMSG_SIGNER_AUTH_ATTR_PARAM = 9;
        public const int CMSG_SIGNER_UNAUTH_ATTR_PARAM = 10;
        public const int CMSG_CERT_COUNT_PARAM = 11;
        public const int CMSG_CERT_PARAM = 12;
        public const int CMSG_CRL_COUNT_PARAM = 13;
        public const int CMSG_CRL_PARAM = 14;
        public const int CMSG_ENVELOPE_ALGORITHM_PARAM = 15;
        public const int CMSG_RECIPIENT_COUNT_PARAM = 17;
        public const int CMSG_RECIPIENT_INDEX_PARAM = 18;
        public const int CMSG_RECIPIENT_INFO_PARAM = 19;
        public const int CMSG_HASH_ALGORITHM_PARAM = 20;
        public const int CMSG_HASH_DATA_PARAM = 21;
        public const int CMSG_COMPUTED_HASH_PARAM = 22;
        public const int CMSG_ENCRYPT_PARAM = 26;
        public const int CMSG_ENCRYPTED_DIGEST = 27;
        public const int CMSG_ENCODED_SIGNER = 28;
        public const int CMSG_ENCODED_MESSAGE = 29;
        public const int CMSG_VERSION_PARAM = 30;
        public const int CMSG_ATTR_CERT_COUNT_PARAM = 31;
        public const int CMSG_ATTR_CERT_PARAM = 32;
        public const int CMSG_CMS_RECIPIENT_COUNT_PARAM = 33;
        public const int CMSG_CMS_RECIPIENT_INDEX_PARAM = 34;
        public const int CMSG_CMS_RECIPIENT_ENCRYPTED_KEY_INDEX_PARAM = 35;
        public const int CMSG_CMS_RECIPIENT_INFO_PARAM = 36;
        public const int CMSG_UNPROTECTED_ATTR_PARAM = 37;
        public const int CMSG_SIGNER_CERT_ID_PARAM = 38;
        public const int CMSG_CMS_SIGNER_INFO_PARAM = 39;


        //-------------------------------------------------------------------------
        //dwObjectType for CryptQueryObject
        //-------------------------------------------------------------------------
        public const int CERT_QUERY_OBJECT_FILE = 0x00000001;
        public const int CERT_QUERY_OBJECT_BLOB = 0x00000002;

        //-------------------------------------------------------------------------
        //dwContentType for CryptQueryObject
        //-------------------------------------------------------------------------
        //encoded single certificate
        public const int CERT_QUERY_CONTENT_CERT = 1;
        //encoded single CTL
        public const int CERT_QUERY_CONTENT_CTL = 2;
        //encoded single CRL
        public const int CERT_QUERY_CONTENT_CRL = 3;
        //serialized store
        public const int CERT_QUERY_CONTENT_SERIALIZED_STORE = 4;
        //serialized single certificate
        public const int CERT_QUERY_CONTENT_SERIALIZED_CERT = 5;
        //serialized single CTL
        public const int CERT_QUERY_CONTENT_SERIALIZED_CTL = 6;
        //serialized single CRL
        public const int CERT_QUERY_CONTENT_SERIALIZED_CRL = 7;
        //a PKCS#7 signed message
        public const int CERT_QUERY_CONTENT_PKCS7_SIGNED = 8;
        //a PKCS#7 message, such as enveloped message.  But it is not a signed message,
        public const int CERT_QUERY_CONTENT_PKCS7_UNSIGNED = 9;
        //a PKCS7 signed message embedded in a file
        public const int CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED = 10;
        //an encoded PKCS#10
        public const int CERT_QUERY_CONTENT_PKCS10 = 11;
        //an encoded PKX BLOB
        public const int CERT_QUERY_CONTENT_PFX = 12;
        //an encoded CertificatePair (contains forward and/or reverse cross certs)
        public const int CERT_QUERY_CONTENT_CERT_PAIR = 13;

        //-------------------------------------------------------------------------
        //dwExpectedConentTypeFlags for CryptQueryObject
        //-------------------------------------------------------------------------
        //encoded single certificate
        public const int CERT_QUERY_CONTENT_FLAG_CERT = (1 << CERT_QUERY_CONTENT_CERT);

        //encoded single CTL
        public const int CERT_QUERY_CONTENT_FLAG_CTL = (1 << CERT_QUERY_CONTENT_CTL);

        //encoded single CRL
        public const int CERT_QUERY_CONTENT_FLAG_CRL = (1 << CERT_QUERY_CONTENT_CRL);

        //serialized store
        public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE = (1 << CERT_QUERY_CONTENT_SERIALIZED_STORE);

        //serialized single certificate
        public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT = (1 << CERT_QUERY_CONTENT_SERIALIZED_CERT);

        //serialized single CTL
        public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CTL);

        //serialized single CRL
        public const int CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL = (1 << CERT_QUERY_CONTENT_SERIALIZED_CRL);

        //an encoded PKCS#7 signed message
        public const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED);

        //an encoded PKCS#7 message.  But it is not a signed message
        public const int CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED = (1 << CERT_QUERY_CONTENT_PKCS7_UNSIGNED);

        //the content includes an embedded PKCS7 signed message
        public const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED);

        //an encoded PKCS#10
        public const int CERT_QUERY_CONTENT_FLAG_PKCS10 = (1 << CERT_QUERY_CONTENT_PKCS10);

        //an encoded PFX BLOB
        public const int CERT_QUERY_CONTENT_FLAG_PFX = (1 << CERT_QUERY_CONTENT_PFX);

        //an encoded CertificatePair (contains forward and/or reverse cross certs)
        public const int CERT_QUERY_CONTENT_FLAG_CERT_PAIR = (1 << CERT_QUERY_CONTENT_CERT_PAIR);

        //content can be any type
        public const int CERT_QUERY_CONTENT_FLAG_ALL =
            CERT_QUERY_CONTENT_FLAG_CERT |
            CERT_QUERY_CONTENT_FLAG_CTL |
            CERT_QUERY_CONTENT_FLAG_CRL |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_STORE |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_CERT |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_CTL |
            CERT_QUERY_CONTENT_FLAG_SERIALIZED_CRL |
            CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED |
            CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED |
            CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED |
            CERT_QUERY_CONTENT_FLAG_PKCS10 |
            CERT_QUERY_CONTENT_FLAG_PFX |
            CERT_QUERY_CONTENT_FLAG_CERT_PAIR;

        //-------------------------------------------------------------------------
        //dwFormatType for CryptQueryObject
        //-------------------------------------------------------------------------
        //the content is in binary format
        public const int CERT_QUERY_FORMAT_BINARY = 1;

        //the content is base64 encoded
        public const int CERT_QUERY_FORMAT_BASE64_ENCODED = 2;

        //the content is ascii hex encoded with "{ASN}" prefix
        public const int CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED = 3;

        //-------------------------------------------------------------------------
        //dwExpectedFormatTypeFlags for CryptQueryObject
        //-------------------------------------------------------------------------
        //the content is in binary format
        public const int CERT_QUERY_FORMAT_FLAG_BINARY = (1 << CERT_QUERY_FORMAT_BINARY);

        //the content is base64 encoded
        public const int CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED = (1 << CERT_QUERY_FORMAT_BASE64_ENCODED);

        //the content is ascii hex encoded with "{ASN}" prefix
        public const int CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED = (1 << CERT_QUERY_FORMAT_ASN_ASCII_HEX_ENCODED);

        //the content can be of any format
        public const int CERT_QUERY_FORMAT_FLAG_ALL =
            CERT_QUERY_FORMAT_FLAG_BINARY |
            CERT_QUERY_FORMAT_FLAG_BASE64_ENCODED |
            CERT_QUERY_FORMAT_FLAG_ASN_ASCII_HEX_ENCODED;

    }
}

【讨论】:

    【解决方案2】:

    谢谢你们,

    你帮了我很多:)

    顺便说一句:我找到了获取时间戳的更简单方法。

    这里是:

    foreach (var signerInfo in signedCms.SignerInfos)
    {
      foreach (var unsignedAttribute in signerInfo.UnsignedAttributes)
      {
        if (unsignedAttribute.Oid.Value == WinCrypt.szOID_RSA_counterSign)
        {
          foreach (var counterSignInfo in signerInfo.CounterSignerInfos)
          {
            foreach (var signedAttribute in counterSignInfo.SignedAttributes)
            {
              if (signedAttribute.Oid.Value == WinCrypt.szOID_RSA_signingTime)
              {
                Pkcs9SigningTime signingTime = (Pkcs9SigningTime)signedAttribute.Values[0];
                Console.Out.WriteLine("Signing Time UTC: " + signingTime.SigningTime);
              }
            }
          }
          return true;
        }
      }
    }
    

    【讨论】:

      【解决方案3】:

      感谢 OP 的工作。我添加了实现以获取证书的实际时间戳。

      foreach (var signerInfo in signedCms.SignerInfos)
                  {
                      foreach (var unsignedAttribute in signerInfo.UnsignedAttributes)
                      {
                          if (unsignedAttribute.Oid.Value == WinCrypt.szOID_RSA_counterSign)
                          {
                              foreach (var counterSignInfo in signerInfo.CounterSignerInfos)
                              {
                                  foreach (var signedAttribute in counterSignInfo.SignedAttributes)
                                  {
                                      if (signedAttribute.Oid.Value == WinCrypt.szOID_RSA_signingTime)
                                      {                                        
                                          System.Runtime.InteropServices.ComTypes.FILETIME fileTime = new System.Runtime.InteropServices.ComTypes.FILETIME();
                                          int fileTimeSize = Marshal.SizeOf(fileTime);
                                          IntPtr fileTimePtr = Marshal.AllocCoTaskMem(fileTimeSize);
                                          Marshal.StructureToPtr(fileTime, fileTimePtr, true);
      
                                          byte[] buffdata = new byte[fileTimeSize];
                                          Marshal.Copy(fileTimePtr, buffdata, 0, fileTimeSize);
      
                                          uint buffSize = (uint)buffdata.Length;
      
                                          uint encoding = WinCrypt.X509_ASN_ENCODING | WinCrypt.PKCS_7_ASN_ENCODING;
      
                                          UIntPtr rsaSigningTime = (UIntPtr)(uint)Marshal.StringToHGlobalAnsi(WinCrypt.szOID_RSA_signingTime);
      
                                          byte[] pbData = signedAttribute.Values[0].RawData;                                         
                                          uint ucbData = (uint)pbData.Length;
      
                                          bool workie = WinCrypt.CryptDecodeObject(encoding, rsaSigningTime.ToUInt32(), pbData, ucbData, 0, buffdata, ref buffSize);
      
                                          if (workie)
                                          {
                                              IntPtr fileTimePtr2 = Marshal.AllocCoTaskMem(buffdata.Length);
                                              Marshal.Copy(buffdata, 0, fileTimePtr2, buffdata.Length);
                                              System.Runtime.InteropServices.ComTypes.FILETIME fileTime2 = (System.Runtime.InteropServices.ComTypes.FILETIME)Marshal.PtrToStructure(fileTimePtr2, typeof(System.Runtime.InteropServices.ComTypes.FILETIME));
      
                                              long hFT2 = (((long)fileTime2.dwHighDateTime) << 32) + ((uint)fileTime2.dwLowDateTime);
      
                                              DateTime dte = DateTime.FromFileTime(hFT2);
                                              Console.WriteLine(dte.ToString());
                                          }
                                          else
                                          {
                                              throw new Win32Exception(Marshal.GetLastWin32Error());                                            
                                          }
      
                                      }    
                                  }
      
                              }                            
      
                              return true;
                          }
      
                      }
                  }
      

      【讨论】:

        【解决方案4】:

        我想从数字证书中删除主题,它是一个 OU 类型的字符串,例如

        • CN=Microsoft Corporation,OU=MOPR,O=Microsoft Corporation,L=Redmond,S=Washington,C=US

        我发现 X509Certificate 非常慢并将整个文件加载到内存中。我尝试读取一个 800Mb 的补丁文件,读取时内存增加了 800Mb,耗时 30 多秒!!

        我站在海报上方,设法调整了上面的代码,以比使用 X509 对象快数百倍。

        请阅读我的博客文章了解更多关于性能差异图片的详细信息。 X509Certificate object c# performance and memory issues alternative – fixed

        试试这个:

        public static X509Certificate2 GetDigitalCertificate(string filename)
            {
                X509Certificate2 cert = null;
        
                int encodingType;
                int contentType;
                int formatType;
                IntPtr certStore = IntPtr.Zero;
                IntPtr cryptMsg = IntPtr.Zero;
                IntPtr context = IntPtr.Zero;
        
                if (!WinCrypt.CryptQueryObject(
                    WinCrypt.CERT_QUERY_OBJECT_FILE,
                    Marshal.StringToHGlobalUni(filename),
                    (WinCrypt.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED
                    | WinCrypt.CERT_QUERY_CONTENT_FLAG_PKCS7_UNSIGNED
                    | WinCrypt.CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED), // <-- These are the attributes that makes it fast!!
                    WinCrypt.CERT_QUERY_FORMAT_FLAG_ALL,
                    0,
                    out encodingType,
                    out contentType,
                    out formatType,
                    ref certStore,
                    ref cryptMsg,
                    ref context))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
        
                // Get size of the encoded message.
                int cbData = 0;
                if (!WinCrypt.CryptMsgGetParam(
                    cryptMsg,
                    WinCrypt.CMSG_ENCODED_MESSAGE,
                    0,
                    IntPtr.Zero,
                    ref cbData))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
        
                var vData = new byte[cbData];
        
                // Get the encoded message.
                if (!WinCrypt.CryptMsgGetParam(
                    cryptMsg,
                    WinCrypt.CMSG_ENCODED_MESSAGE,
                    0,
                    vData,
                    ref cbData))
                {
                    throw new Win32Exception(Marshal.GetLastWin32Error());
                }
        
                var signedCms = new SignedCms();
                signedCms.Decode(vData);
        
                if (signedCms.SignerInfos.Count > 0)
                {
                    var signerInfo = signedCms.SignerInfos[0];
        
                    if (signerInfo.Certificate != null)
                    {
                        cert = signerInfo.Certificate;
                    }
                }
        
                return cert;
            }
        

        似乎如果您在 CryptQueryObject 调用上使用“WinCrypt.CERT_QUERY_CONTENT_FLAG_ALL”,它会遭受与 X509Certificate 对象相同的内存性能损失,但如果您将其缩减为仅 PKCS7 内容类型,它的表现就像做梦一样,而且似乎给了我需要的信息。

        【讨论】:

          【解决方案5】:

          我看你还是没有回复,让我提供一个。

          如果您不介意使用第三方组件,请查看我们 SecureBlackbox 产品的 TElAuthenticodeVerifier 组件。使用此组件,您可以验证签名并检查时间戳。

          【讨论】:

          • 课程看起来不错,谢谢。 VerifySignature 是否根据 CRL 在线检查证书有效性?顺便说一句,我已经通过 pinvoking CryptQueryObject 实现了时间戳检查,所以我只是出于好奇。
          • 嗨 SlavaGu,你能和我分享你的解决方案吗?或者可能会给我一些想法我该怎么做。我也有类似的问题。谢谢。
          【解决方案6】:

          在我的情况下,提供的答案不适用于 SHA256 签名方法。我更新到了那些嵌套的 foreach 循环中的底部。但是 Nuget AuthenticodeExaminer 接缝工作得很好。这是具有单个时间戳的单个证书的示例:

                  var extractor = new FileInspector(@"D:\Temp\file.exe");
                  var signTime = extractor.GetSignatures().FirstOrDefault()?.TimestampSignatures.FirstOrDefault()?.TimestampDateTime?.UtcDateTime;
          

          【讨论】:

            【解决方案7】:

            我喜欢通过使用 SignedCms 类避免一些讨厌的 p/invoke 代码的想法,但请注意,在某些网络环境下,SignedCms 构造函数可能会阻塞很长时间 --- 我看到大约 15 秒测试我目前正在运行。 Alejandro Campos Magencio 在他的 MSDN 博客上标题为 Big delay while calling EnvelopedCms constructor 的帖子中提供了一些相关信息。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2011-02-21
              • 1970-01-01
              • 2013-06-18
              • 2012-09-19
              • 2012-12-16
              • 2014-08-23
              相关资源
              最近更新 更多