【发布时间】:2014-01-14 19:38:17
【问题描述】:
我有来自 Authenticode 签名的 PE 文件的 PKCS #7 SignedData。我想在这样的对话框中显示它:
这是一个标准的 Windows 对话框,如果您在 PE 文件的 数字签名 选项卡上单击 详细信息,就会看到该对话框。
知道怎么做吗?
我更喜欢 C# 解决方案,但标准 C API 也可以工作(我可以制作 C++/CLI 接口。)
【问题讨论】:
我有来自 Authenticode 签名的 PE 文件的 PKCS #7 SignedData。我想在这样的对话框中显示它:
这是一个标准的 Windows 对话框,如果您在 PE 文件的 数字签名 选项卡上单击 详细信息,就会看到该对话框。
知道怎么做吗?
我更喜欢 C# 解决方案,但标准 C API 也可以工作(我可以制作 C++/CLI 接口。)
【问题讨论】:
CryptUIDlgViewSignerInfo 调用之前的大部分工作似乎已经由 .NET 类 System.Security.Cryptography.Pkcs.SignedCms 在内部完成。
填充CRYPTUI_VIEWSIGNERINFO_STRUCT 所需的两个部分已经存在,作为SignedCms 的私有字段:
hMsg 是 SignedCms.m_safeCryptMsgHandle
pSignerInfo 是 SignerInfo.m_pbCmsgSignerInfo
如果我们可以调用一个假设的 SignedCms.ShowSignerInfoDialog() 函数,或者以某种方式无需反射就可以访问这些成员,那将是不可思议的。
以下 hack 确实有效!
class Program
{
static void Main(string[] args) {
var data = ...;
var cms = new SignedCms();
cms.Decode(data);
var pbCmsgSignerInfo = typeof(SignerInfo).GetField("m_pbCmsgSignerInfo", BindingFlags.NonPublic | BindingFlags.Instance);
var si = (SafeHandle)pbCmsgSignerInfo.GetValue(cms.SignerInfos[0]);
var safeCryptMessageHandle = typeof(SignedCms).GetField("m_safeCryptMsgHandle", BindingFlags.NonPublic | BindingFlags.Instance);
var hMsg = (SafeHandle)safeCryptMessageHandle.GetValue(cms);
var vsi = new CRYPTUI_VIEWSIGNERINFO_STRUCT {
dwSize = (uint)Marshal.SizeOf(typeof(CRYPTUI_VIEWSIGNERINFO_STRUCT)),
pSignerInfo = si,
hMsg = hMsg,
};
CryptUIDlgViewSignerInfo(ref vsi);
}
[DllImport("Cryptui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern bool CryptUIDlgViewSignerInfo(ref CRYPTUI_VIEWSIGNERINFO_STRUCT pcvsi);
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
struct CRYPTUI_VIEWSIGNERINFO_STRUCT
{
public UInt32 dwSize;
public IntPtr hwndParent;
public UInt32 dwFlags;
public string szTitle;
public SafeHandle pSignerInfo;
public SafeHandle hMsg;
[MarshalAs(UnmanagedType.LPStr)]
public string pszOID;
public IntPtr dwReserved;
public UInt32 cStores;
public IntPtr rghStores;
public UInt32 cPropSheetPages;
public IntPtr rgPropSheetPages;
}
【讨论】:
这需要大量的研究和一些逆向工程,但我最终让它发挥了作用。
魔法函数是CryptUIDlgViewSignerInfo()。
CryptUIDlgViewSignerInfo 函数显示一个对话框,其中包含已签名消息的签名者信息。
不幸的是,连同其唯一参数的定义,CRYPTUI_VIEWSIGNERINFO_STRUCT 不在任何头文件中。所以首先你需要声明:
CryptUI.h
#ifdef __cplusplus
extern "C" {
#endif
typedef struct tagCRYPTUI_VIEWSIGNERINFO_STRUCT {
DWORD dwSize;
HWND hwndParent;
DWORD dwFlags;
LPCTSTR szTitle;
CMSG_SIGNER_INFO *pSignerInfo;
HCRYPTMSG hMsg;
LPCSTR pszOID;
DWORD_PTR dwReserved;
DWORD cStores;
HCERTSTORE *rghStores;
DWORD cPropSheetPages;
LPCPROPSHEETPAGE rgPropSheetPages;
} CRYPTUI_VIEWSIGNERINFO_STRUCT, *PCRYPTUI_VIEWSIGNERINFO_STRUCT;
#ifdef UNICODE
#define CryptUIDlgViewSignerInfo CryptUIDlgViewSignerInfoW
#else
#define CryptUIDlgViewSignerInfo CryptUIDlgViewSignerInfoA
#endif
BOOL WINAPI CryptUIDlgViewSignerInfo(
_In_ CRYPTUI_VIEWSIGNERINFO_STRUCT *pcvsi
);
#ifdef __cplusplus
} // extern "C"
#endif
现在,这是一个 C++ CLI 函数,但如果您调整开头,它应该很容易在纯 C 中工作。当然,您还需要做一些更好的错误检查,但这只是一个概念验证:
// Link against these libraries
#pragma comment (lib, "Crypt32")
#pragma comment (lib, "Cryptui")
void CertHelper::DoStuff(array<Byte>^ data) {
// http://stackoverflow.com/questions/17689154
pin_ptr<Byte> pData = &data[0];
CERT_BLOB blob;
blob.cbData = data->Length;
blob.pbData = pData;
BOOL res;
DWORD MsgAndCertEncodingType;
DWORD ContentType;
DWORD FormatType;
HCERTSTORE hCertStore;
HCRYPTMSG hMsg;
res = CryptQueryObject(
CERT_QUERY_OBJECT_BLOB, // dwObjectType [in]
&blob, // pvObject [in]
CERT_QUERY_CONTENT_FLAG_ALL, // dwExpectedContentTypeFlags [in]
CERT_QUERY_FORMAT_FLAG_BINARY, // dwExpectedFormatTypeFlags [in]
0, // dwFlags [in]
&MsgAndCertEncodingType, // pdwMsgAndCertEncodingType [out]
&ContentType, // pdwContentType [out]
&FormatType, // pdwFormatType [out]
&hCertStore, // phCertStore [out]
&hMsg, // phMsg [out]
NULL // ppvContext [out]
);
// Get the SignerInfo - call once to get size
DWORD cb;
res = CryptMsgGetParam(
hMsg, // hCryptMsg [in]
CMSG_SIGNER_INFO_PARAM, // dwParamType [in]
0, // dwIndex [in]
NULL, // pvData [out]
&cb // pcbData [in, out]
);
CMSG_SIGNER_INFO* signerinfo = (CMSG_SIGNER_INFO*)LocalAlloc(LPTR, cb);
res = CryptMsgGetParam(
hMsg, // hCryptMsg [in]
CMSG_SIGNER_INFO_PARAM, // dwParamType [in]
0, // dwIndex [in]
signerinfo, // pvData [out]
&cb // pcbData [in, out]
);
// Initialize the View Signer Info structure
CRYPTUI_VIEWSIGNERINFO_STRUCT vsi;
memset(&vsi, 0, sizeof(vsi));
vsi.dwSize = sizeof(vsi);
vsi.hwndParent = NULL; // TODO
vsi.dwFlags = 0; // SHDocvw.dll passes 0x14
vsi.szTitle = NULL;
vsi.pSignerInfo = signerinfo;
vsi.hMsg = hMsg;
vsi.pszOID = "1.3.6.1.5.5.7.3.3"; // XCN_OID_PKIX_KP_CODE_SIGNING
// Show the dialog already!
res = CryptUIDlgViewSignerInfo(&vsi);
// Free resources
LocalFree(signerinfo);
if (hCertStore) CertCloseStore(hCertStore, 0);
if (hMsg) CryptMsgClose(hMsg);
}
为了参考起见,这是从ViewCertProperties() 在shdocvw.dll 中回复的。
【讨论】: