获取已安装的 Windows 安装程序包的证书和产品代码

Get certificate of an installed Windows Installer package with product code

我有几个从 MsiEnumProducts 中筛选出来的产品代码,需要获取它们分配的证书。 应该可以使用 MsiOpenDatabase 获取证书,但我不知道如何可靠地获取 .msi 文件的路径。

我宁愿避免使用外部程序集(如 wix),只使用 pinvoke。

给定产品代码,调用 MsiGetProductInfo 传递 属性 名称 INSTALLPROPERTY_LOCALPACKAGE returns C:[=19= 中缓存的安装程序包的位置].

获得缓存包后,调用 MsiGetFileSignatureInformation 即可检索证书。

使用 pInvoke 的示例:

using System;
using System.Text;
using System.Runtime.InteropServices;

class Program
{
    [DllImport("msi.dll", CharSet = CharSet.Ansi, SetLastError = false)]
    static extern int MsiEnumProducts(int iProductIndex, StringBuilder lpProductBuf);

    [DllImport("msi.dll", CharSet = CharSet.Ansi, SetLastError = false)]
    static extern int MsiGetProductInfo(string product, string property, [Out] StringBuilder valueBuf, ref Int32 len);

    [DllImport("msi.dll", CharSet = CharSet.Ansi, SetLastError = false)]
    static extern int MsiGetFileSignatureInformation(string fileName, int flags, out IntPtr certContext, IntPtr hashData, ref int hashDataLength);

    [DllImport("Crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    static extern int CertFreeCertificateContext( IntPtr certContext );

    [DllImport("Crypt32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
    static extern int CertGetNameString(IntPtr certContext, UInt32 type, UInt32 flags, IntPtr typeParameter, StringBuilder stringValue, UInt32 stringLength );

    static void Main(string[] args)
    {
        int index = 0;
        StringBuilder productCode = new StringBuilder();
        int result = MsiEnumProducts(index, productCode);
        while (result == 0)
        {
            Console.WriteLine("{0}", productCode);

            Int32 length = 1024;
            StringBuilder fileName = new StringBuilder();
            result = MsiGetProductInfo(
                        productCode.ToString(),
                        "LocalPackage",
                        fileName,
                        ref length );
            if (result == 0)
            {
                Console.WriteLine("{0}", fileName);

                IntPtr certContext = IntPtr.Zero;
                IntPtr hashData = IntPtr.Zero;
                int hashDataLength = 0;
                result = MsiGetFileSignatureInformation(
                            fileName.ToString(),
                            0,
                            out certContext,
                            hashData,
                            ref hashDataLength);
                if ( result == 0 )
                {
                    Console.WriteLine("Got Cert");

                    StringBuilder simpleDisplayType = new StringBuilder();
                    int ok = CertGetNameString(
                                certContext,
                                4,                      // == CERT_NAME_SIMPLE_DISPLAY_TYPE
                                0,
                                IntPtr.Zero,
                                simpleDisplayType,
                                1024 );
                    if (ok != 0)
                    {
                        Console.WriteLine("{0}", simpleDisplayType);
                    }

                    CertFreeCertificateContext(certContext);
                }
            }

            ++index;
            result = MsiEnumProducts(index, productCode);
        }
    }
}