显示数字签名/证书对话框

时间:2013-12-27 07:40:58

标签: c# c winapi

我从Authenticode签名的PE文件中获得PKCS #7 SignedData。我想在这样的对话框中显示它:

enter image description here

这是一个标准的Windows对话框,就像您点击PE文件的数字签名标签上的详细信息一样。

知道怎么做吗?

我更喜欢C#解决方案,但标准C API也可以工作(我可以创建一个C ++ / CLI接口。)

2 个答案:

答案 0 :(得分:3)

这需要大量的研究和一些逆向工程,但我最终还是让它发挥了作用。

魔术功能是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的重新提取。

答案 1 :(得分:1)

导致CryptUIDlgViewSignerInfo调用的大部分工作似乎已经由.NET类System.Security.Cryptography.Pkcs.SignedCms在内部完成。

填充CRYPTUI_VIEWSIGNERINFO_STRUCT所需的两部分已经存在,SignedCms的私有字段:

  • hMsgSignedCms.m_safeCryptMsgHandle
  • pSignerInfoSignerInfo.m_pbCmsgSignerInfo

enter image description here

如果我们可以调用一个假设的SignedCms.ShowSignerInfoDialog()函数,或以某种方式访问​​这些成员而不进行反思,那将是不可思议的。

以下黑客确实有效!

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 UInt32 dwReserved;
    public UInt32 cStores;
    public IntPtr rghStores;
    public UInt32 cPropSheetPages;
    public IntPtr rgPropSheetPages;
}