CryptQueryObject失败,托管代码中出现CRYPT_E_NO_MATCH错误,但在非托管代码中运行良好

本文关键字:错误 运行 MATCH 非托管代码 托管代码 失败 CryptQueryObject CRYPT NO | 更新日期: 2023-09-27 18:26:53

我已经和这个bug斗争了一天多了,如果有人能帮我了解一下,我将不胜感激。一切都始于这个问题。我的目标是检索签名的.js文件上的数字签名信息。(该文件最初由微软的签名工具签名。)

由于我的托管代码似乎失败了,我决定在C++中尝试一种非托管方法,它出奇地工作得很好。所以我决定用PInvoke在C#中写一个类似的东西。但无论我在托管代码中做了什么,这都不起作用。

所以我做了一些挖掘,下面是似乎失败的部分。

如果我从32位或64位非托管C++代码中执行此操作,它运行良好:

HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL; 
DWORD dwEncoding = 0, dwContentType = 0, dwFormatType = 0;
// Get message handle and store handle from the signed file.
if(CryptQueryObject(CERT_QUERY_OBJECT_FILE,
                            L"D:''Test''DataStore''Downloads''en-US''test02_1.js",
                            CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
                            CERT_QUERY_FORMAT_FLAG_BINARY,
                            0,
                            &dwEncoding,
                            &dwContentType,
                            &dwFormatType,
                            &hStore,
                            &hMsg,
                            NULL))
{
    //All good
    TRACE("Got it!'n");
}
else
{
    //Failed
    TRACE("Error: 0x%x'n", ::GetLastError());
}

但是,如果我使用PInvoke从用C#编写的ASP.NET web应用程序(在64位Windows 8.1操作系统上作为64位进程运行)中执行同样的操作,它会给我0x80092009错误代码,或CRYPT_E_NO_MATCH:

IntPtr hStore = IntPtr.Zero;
IntPtr hMsg = IntPtr.Zero;
IntPtr dwEncoding = IntPtr.Zero, dwContentType = IntPtr.Zero, dwFormatType = IntPtr.Zero;
IntPtr DummyNull = IntPtr.Zero;
// Get message handle and store handle from the signed file.
if (!CryptQueryObject(CERT_QUERY_OBJECT_FILE,
    "D:''Test''DataStore''Downloads''en-US''test02_1.js",
    CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
    CERT_QUERY_FORMAT_FLAG_BINARY,
    0,
    dwEncoding,
    dwContentType,
    dwFormatType,
    hStore,
    hMsg,
    ref DummyNull))
{
    //Failed
    int nOSError = Marshal.GetLastWin32Error();
    throw new Exception("Failed with error " + nOSError);
}

这些是它的PInvoke声明:

        [DllImport("CRYPT32.DLL", EntryPoint = "CryptQueryObject", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern Boolean CryptQueryObject(
            Int32 dwObjectType,
            [MarshalAs(UnmanagedType.LPWStr)]string pvObject,
            Int32 dwExpectedContentTypeFlags,
            Int32 dwExpectedFormatTypeFlags,
            Int32 dwFlags,
            IntPtr pdwMsgAndCertEncodingType,
            IntPtr pdwContentType,
            IntPtr pdwFormatType,
            IntPtr phCertStore,
            IntPtr phMsg,
            ref IntPtr ppvContext
        );
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_OBJID_BLOB
        {
            public uint cbData;
            [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)]
            public byte[] pbData;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_ALGORITHM_IDENTIFIER
        {
            [MarshalAs(UnmanagedType.LPStr)]
            public String pszObjId;
            public CRYPT_OBJID_BLOB Parameters;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CRYPT_BIT_BLOB
        {
            public uint cbData;
            public IntPtr pbData;
            public uint cUnusedBits;
        }
        [StructLayout(LayoutKind.Sequential)]
        private struct CERT_PUBLIC_KEY_INFO
        {
            public CRYPT_ALGORITHM_IDENTIFIER Algorithm;
            public CRYPT_BIT_BLOB PublicKey;
        }
    #pragma warning disable 0618
        [StructLayout(LayoutKind.Sequential)]
        private struct CERT_INFO
        {
            public uint dwVersion;
            public CRYPT_OBJID_BLOB SerialNumber;
            public CRYPT_ALGORITHM_IDENTIFIER SignatureAlgorithm;
            public CRYPT_OBJID_BLOB Issuer;
            public FILETIME NotBefore;
            public FILETIME NotAfter;
            public CRYPT_OBJID_BLOB Subject;
            public CERT_PUBLIC_KEY_INFO SubjectPublicKeyInfo;
            public CRYPT_OBJID_BLOB IssuerUniqueId;
            public CRYPT_OBJID_BLOB SubjectUniqueId;
            public uint cExtension;
            public IntPtr rgExtension;
        }
    #pragma warning restore 0618
        private const int CERT_QUERY_OBJECT_FILE = 0x00000001;
        private const int CERT_QUERY_OBJECT_BLOB = 0x00000002;
        private const int CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED = 10;
        private const int CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED = (1 << CERT_QUERY_CONTENT_PKCS7_SIGNED_EMBED);
        private const int CERT_QUERY_FORMAT_BINARY = 1;
        private const int CERT_QUERY_FORMAT_FLAG_BINARY = (1 << CERT_QUERY_FORMAT_BINARY);

知道为什么吗?

PS。请注意,如果我用数字签名的.exe文件代替数字签名的.js文件,则托管代码和非托管代码似乎都可以正常工作。这真的让我很困惑!

CryptQueryObject失败,托管代码中出现CRYPT_E_NO_MATCH错误,但在非托管代码中运行良好

由于我还没有得到答案,让我分享一下我目前的发现。首先,由于缺乏回复,甚至一些"大师"投了反对票,在我看来,人们似乎并没有真正意识到,不仅可以使用SignTool对.exe.dll文件进行数字签名,还可以使用.js.vbs.ps1或PowerShell脚本进行数字签名。在我看来,CryptQueryObject API还依赖于数字签名文件的扩展名来正确地衡量签名的格式。由于某种原因,托管代码似乎重命名了它所使用的文件,或者以某种方式使CryptQueryObject API无法访问该文件。

我在这个帖子中分享了更多我对此的想法。

当您调用CryptQueryObject而不是exe文件时,您不应该使用CERT_QUERY_FORMAT_FLAG_BINARY标志。您可以将CERT_QUERY_FORMAT_FLAG_ASN_ASCII_EX_ENCODED标志用于文本文件或CERT_QUERY_FORMAT_FLAG_ALL用于任何文件类型;

相关文章: