CryptQueryObject 在托管代码中因 CRYPT_E_NO_MATCH 错误而失败,但在非托管代码中工作正常
CryptQueryObject fails with CRYPT_E_NO_MATCH error in managed code but works fine in unmanaged one
我已经为这个错误苦苦挣扎了一天多,如果有人能帮助我阐明它,我将不胜感激。这一切都从 . My goal was to retrieve digital signature information on a signed .js
file. (The file was originally signed by Microsoft's signtool 开始。)
由于我的托管代码似乎失败了,我决定尝试 unmanaged approach,在 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());
}
但是,如果我从用 C# 编写的 ASP.NET Web 应用程序(和 运行 作为 64 位进程在 64 位 Windows 8.1 OS) 使用 PInvoke,它给了我 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
文件,托管代码和非托管代码似乎都可以正常工作。这让我很困惑!
由于我还没有得到答案,让我分享一下我目前的发现。首先,由于缺乏回复甚至一些 "guru" 的反对票,在我看来人们并没有真正意识到人们不仅可以对 .exe
和 .dll
文件进行数字签名使用 SignTool,还有 .js
、.vbs
和 .ps1
或 PowerShell 脚本,仅举几例。在我看来,CryptQueryObject
API 依赖于数字签名文件的扩展名来正确判断签名的格式。由于某种原因,托管代码似乎要么重命名它使用的文件,要么以某种方式使 CryptQueryObject
API.
无法访问它
我在 中分享了我对此的更多想法。
我已经为这个错误苦苦挣扎了一天多,如果有人能帮助我阐明它,我将不胜感激。这一切都从 .js
file. (The file was originally signed by Microsoft's signtool 开始。)
由于我的托管代码似乎失败了,我决定尝试 unmanaged approach,在 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());
}
但是,如果我从用 C# 编写的 ASP.NET Web 应用程序(和 运行 作为 64 位进程在 64 位 Windows 8.1 OS) 使用 PInvoke,它给了我 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
文件,托管代码和非托管代码似乎都可以正常工作。这让我很困惑!
由于我还没有得到答案,让我分享一下我目前的发现。首先,由于缺乏回复甚至一些 "guru" 的反对票,在我看来人们并没有真正意识到人们不仅可以对 .exe
和 .dll
文件进行数字签名使用 SignTool,还有 .js
、.vbs
和 .ps1
或 PowerShell 脚本,仅举几例。在我看来,CryptQueryObject
API 依赖于数字签名文件的扩展名来正确判断签名的格式。由于某种原因,托管代码似乎要么重命名它使用的文件,要么以某种方式使 CryptQueryObject
API.
我在