You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

寻求cryptuiapi.h和wincrypt.h的官方C#包装器及证书迁移方案

Hey there! Let's tackle your problem of migrating those MFC certificate operations to a C# command-line app. First, let's address your core question about official NuGet wrappers for cryptuiapi.h and wincrypt.h, then break down your four proposed solutions to find the best fit.

Core Answer: Official .NET Support & NuGet Options

Most of the Win32 CryptoAPI functions you're using have first-class support in .NET's System.Security.Cryptography.X509Certificates namespace (no extra NuGet needed—it's part of the base class library). The big exception is CryptUIDlgSelectCertificateFromStore—since it's a UI-focused function, .NET doesn't include a managed wrapper for it out of the box.

There aren't any official Microsoft NuGet packages that wrap the full cryptuiapi.h or wincrypt.h surface area, but you don't need to copy the reference source code wholesale. Here's how to approach each of your options:


1. Use X509Store + Partial P/Invoke (Best Recommendation)

This is the cleanest path. Use .NET's built-in classes for 99% of your operations, and only write a small P/Invoke wrapper for the missing CryptUIDlgSelectCertificateFromStore function. Let's map your existing functions to .NET equivalents:

  • CertOpenStore/CertCloseStore → X509Store.Open() and X509Store.Close() (or use using statements for automatic disposal)
  • CertEnumCertificatesInStore → X509Store.Certificates (a collection you can iterate over directly)
  • CertVerifyRevocation → X509Chain.Build(X509Certificate2) (check the chain's ChainStatus property for revocation-related issues)
  • CertVerifyTimeValidity → Compare X509Certificate2.NotBefore and X509Certificate2.NotAfter against the current time
  • CryptAcquireCertificatePrivateKey → X509Certificate2.GetRSAPrivateKey(), GetDSAPrivateKey(), or GetECDsaPrivateKey() (typed, managed private key access)
  • CryptSignCertificate → Use the private key's SignData() method (e.g., rsa.SignData(data, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1))
  • CertFindCertificateInStore → X509Store.Certificates.Find(X509FindType.FindByIssuerName, issuerName, validOnly)
  • CertGetIntendedKeyUsage → Access X509Certificate2.Extensions to retrieve the KeyUsageExtension or EnhancedKeyUsageExtension
  • CertNameToStr → Use X509Certificate2.SubjectName.Name or X500DistinguishedName.Format() for human-readable distinguished names
  • CertDuplicateCertificateContext → Create a new X509Certificate2 instance from an existing one (e.g., new X509Certificate2(existingCert.RawData))
  • CertAddCertificateContextToStore → X509Store.Add(X509Certificate2)

For the missing CryptUIDlgSelectCertificateFromStore, here's a minimal P/Invoke implementation you can drop into your project (no need to wrap all CryptoAPI functions):

using System;
using System.Runtime.InteropServices;
using System.Security.Cryptography.X509Certificates;

public static class CryptUiHelper
{
    [DllImport("cryptui.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr CryptUIDlgSelectCertificateFromStore(
        IntPtr hCertStore,
        IntPtr hwndParent,
        string pszTitle,
        string pszDisplayString,
        uint dwDontUseColumn,
        uint dwFlags,
        IntPtr pvReserved);

    [DllImport("crypt32.dll", SetLastError = true)]
    private static extern bool CertFreeCertificateContext(IntPtr pCertContext);

    public static X509Certificate2 SelectCertificateFromStore(X509Store store)
    {
        IntPtr certContext = CryptUIDlgSelectCertificateFromStore(
            store.StoreHandle,
            IntPtr.Zero, // No parent window for console apps; pass a handle if needed
            "Select Certificate",
            "Choose a certificate from the store",
            0,
            0,
            IntPtr.Zero);

        if (certContext == IntPtr.Zero)
        {
            // Handle user cancellation or error (check Marshal.GetLastWin32Error() if needed)
            return null;
        }

        try
        {
            return new X509Certificate2(certContext);
        }
        finally
        {
            // Ensure the unmanaged certificate context is freed
            CertFreeCertificateContext(certContext);
        }
    }
}

Call this helper method after opening your X509Store to replicate the "select certificate" dialog from your MFC app.


Writing P/Invoke wrappers for every CryptoAPI function you use is technically possible, but it's tedious and error-prone. You'd have to manually handle memory management, type conversions, and error checking—all things .NET's managed classes already solve for you. Stick with option 1 instead.


3. Reuse Unmanaged C++ Code (Avoid If Possible)

If your main MFC project is huge and hard to extract code from, this path will likely create more headaches than it solves. You'd have to build a separate unmanaged DLL, handle interop between C# and C++, and deal with potential versioning or deployment issues. Not worth the effort when .NET covers almost all your needs.


4. C++/CLR Wrapper (Not Ideal)

The Unicode/ASCII encoding issues you're worried about are very real here. C++/CLR interop can get messy with string conversions, and you'd still be tied to unmanaged code. This middle ground doesn't offer much benefit over option 1.


Final Takeaway

Go with option 1: use .NET's built-in X509Store and related classes for all your core certificate operations, and add a small P/Invoke wrapper just for CryptUIDlgSelectCertificateFromStore. This gives you the productivity of C#, avoids the pain of full P/Invoke or unmanaged interop, and lets you reuse most of your existing logic in a managed way.

内容的提问来源于stack exchange,提问作者Lucian

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.11 07:50:43