gpt4 book ai didi

c# - 是否可以确定远程 Windows 服务器上证书私钥的文件名?

转载 作者:太空狗 更新时间:2023-10-29 21:40:54 24 4
gpt4 key购买 nike

我正在尝试确定存储在远程 Windows (2k3/2k8) 机器上的证书私钥的文件名,但遇到了一些困难。我也不太熟悉 Microsoft 的 CryptAPI,因此我正在寻找您可以提供的任何帮助。

本练习的目的是找到安装在远程计算机上的私钥满足特定条件的证书,并确保为其私钥文件分配了正确的权限。尽管我可以在文件夹级别分配权限,但我更愿意仅在必要时(出于显而易见的原因)在私钥文件级别分配权限。

这是场景,假设具有类似管理权限的服务帐户正在访问证书存储:

  • 我使用 p/invoke 使用 C# 中的以下调用检索远程证书存储:

    [DllImport("CRYPT32", EntryPoint = "CertOpenStore", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr CertOpenStore(int storeProvider, int encodingType, int hcryptProv, int flags, string pvPara);

    IntPtr storeHandle = CertOpenStore(CERT_STORE_PROV_SYSTEM,
    0,
    0,
    CERT_SYSTEM_STORE_LOCAL_MACHINE,
    string.Format(@"\{0}{1}", serverName, name));
  • 然后我使用 CertEnumCertificatesInStore 来检索我想要评估的证书。

    [DllImport("CRYPT32", EntryPoint = "CertEnumCertificatesInStore", CharSet = CharSet.Unicode, SetLastError = true)]
    public static extern IntPtr CertEnumCertificatesInStore(IntPtr storeProvider, IntPtr prevCertContext);
    IntPtr certCtx = IntPtr.Zero;
    certCtx = CertEnumCertificatesInStore(storeHandle, certCtx);
  • 如果证书符合我的条件,我会从 CertEnumCertificatesInStore 调用返回的 IntPtr 创建一个 X509Certificate2 实例,例如:

    X509Certificate2 当前 = 新 X509Certificate2(certCtx);
  • 一旦我获得了我感兴趣的证书的 X509Certificate2 实例,我就会调用 CryptAcquireCertificatePrivateKey获取私钥提供者:

    [DllImport("crypt32", CharSet = CharSet.Unicode, SetLastError = true)]
    内部外部静态 bool CryptAcquireCertificatePrivateKey(IntPtr pCert, uint dwFlags, IntPtr pvReserved, ref IntPtr phCryptProv, ref int pdwKeySpec, ref bool pfCallerFreeProv);

    //cert 是 X509Certificate2

    CryptAcquireCertificatePrivateKey(cert.Handle,
    0,
    IntPtr.零,
    引用提供者,
    引用 _keyNumber,
    引用 freeProvider);
  • 为了检索私钥文件名,我尝试从 hProvider 请求作为 pData 的唯一容器名称,例如:

    [DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
    internal extern static bool CryptGetProvParam(IntPtr hCryptProv, CryptGetProvParamType dwParam, IntPtr pvData, ref int pcbData, uint dwFlags);

    IntPtr pData = IntPtr.Zero;
    CryptGetProvParam(hProvider, PP_UNIQUE_CONTAINER, pData, ref cbBytes, 0));

  • 到目前为止,上述所有步骤都在本地运行良好(服务器名称 == 本地机器名称);但是,为存储在远程计算机的本地计算机证书存储中的证书返回的唯一容器名称(私钥文件名)不会呈现为我在下面看到的实际私钥文件名:

    w2k3:\Documents and Settings\All Users\Application Data\Microsoft\Crypto\RSA\MachineKeys

    ws08:\ProgramData\Microsoft\Crypto\RSA\MachineKeys

    例如,如果我直接在远程机器上运行上述步骤,我会得到一个私钥文件名 AAAAAAA-111111,但是如果我远程运行它们,我会得到一个私钥 BBBBBBBB-2222222。此外,如果我在本地安装远程证书并在本地机器上运行这些步骤,我会得到相同的私钥名称 BBBBBBBB-2222222。

    我很可能觉得我可能在第 4 步中遗漏了一个警告,即调用 CryptAcquireCertificatePrivateKey。这个调用可能依赖于本地机器的身份来生成将用于存储私钥 blob 的唯一容器的名称。

    更新

    经过一些进一步的研究,我发现了一个博客,其中详细说明了私钥容器的文件名是如何创建的 here .

    除了使用 CryptAcquireCertificatePrivateKey,您可以使用该博客中描述的方法在任何机器上获取私钥容器名称,一旦您获得了由 CertGetCertificateContextProperty 获取的容器名称。此处的代码显示了如何获取私钥容器名称,以便您可以生成私钥文件名。 * 免责声明 - 我很确定这可能会发生变化,甚至可能不完整,但我将其发布,以防将来对其他人有所帮助*

    结构和 P/Invoke:
    [StructLayout(LayoutKind.Sequential)]
    public struct CryptKeyProviderInfo
    {
    [MarshalAs(UnmanagedType.LPWStr)]
    public String pwszContainerName;
    [MarshalAs(UnmanagedType.LPWStr)]
    public String pwszProvName;
    public uint dwProvType;
    public uint dwFlags;
    public uint cProvParam;
    public IntPtr rgProvParam;
    public uint dwKeySpec;
    }

    public const uint CERT_KEY_PROV_INFO_PROP_ID = 0x00000002;

    [DllImport("crypt32.dll", SetLastError = true)]
    internal extern static bool CertGetCertificateContextProperty(IntPtr pCertContext, uint dwPropId, IntPtr pvData, ref uint pcbData);

    IntPtr providerInfo = IntPtr.Zero;
    string containerName = string.Empty;
    try
    {

    //Win32 call w/IntPtr.Zero will get the size of our Cert_Key_Prov_Info_Prop_ID struct
    uint pcbProviderInfo = 0;
    if (!Win32.CertGetCertificateContextProperty(certificate.Handle, Win32.CERT_KEY_PROV_INFO_PROP_ID, IntPtr.Zero, ref pcbProviderInfo))
    {
    //if we can't get the certificate context, return string.empty
    return string.Empty;
    }

    //Allocate heap for Cert_Key_Prov_Info_Prop_ID struct
    providerInfo = Marshal.AllocHGlobal((int)pcbProviderInfo);

    //Request actual Cert_Key_Prov_Info_Prop_ID struct with populated data using our allocated heap
    if (Win32.CertGetCertificateContextProperty(certificate.Handle, Win32.CERT_KEY_PROV_INFO_PROP_ID, providerInfo, ref pcbProviderInfo))
    {
    //Cast returned pointer into managed structure so we can refer to it by it's structure layout
    Win32.CryptKeyProviderInfo keyInfo = (Win32.CryptKeyProviderInfo)Marshal.PtrToStructure(providerInfo, typeof(Win32.CryptKeyProviderInfo));

    //Get the container name
    containerName = keyInfo.pwszContainerName;
    }

    //Do clean-up immediately if possible
    if (providerInfo != IntPtr.Zero)
    {
    Marshal.FreeHGlobal(providerInfo);
    providerInfo = IntPtr.Zero;
    }
    }
    finally
    {
    //Do clean-up on finalizer if an exception cause early terminiation of try - after alloc, before cleanup
    if (providerInfo != IntPtr.Zero)
    Marshal.FreeHGlobal(providerInfo);
    }

    最佳答案

    使用上面的 CertGetCertificateContextProperty,我能够解决这个问题。因此,可以使用更新中提到的步骤在远程计算机上确定证书私钥的文件名。

    关于c# - 是否可以确定远程 Windows 服务器上证书私钥的文件名?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9610689/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com