| File: System\Security\Cryptography\X509Certificates\X509Certificate2UI.cs | Web Access |
| Project: src\runtime\src\libraries\System.Windows.Extensions\src\System.Windows.Extensions.csproj (System.Windows.Extensions) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; namespace System.Security.Cryptography.X509Certificates { public enum X509SelectionFlag { SingleSelection = 0x00, MultiSelection = 0x01 } public sealed class X509Certificate2UI { internal const int ERROR_SUCCESS = 0; internal const int ERROR_CANCELLED = 1223; public static void DisplayCertificate(X509Certificate2 certificate) { ArgumentNullException.ThrowIfNull(certificate); DisplayX509Certificate(certificate, IntPtr.Zero); } public static void DisplayCertificate(X509Certificate2 certificate, IntPtr hwndParent) { ArgumentNullException.ThrowIfNull(certificate); DisplayX509Certificate(certificate, hwndParent); } public static X509Certificate2Collection SelectFromCollection(X509Certificate2Collection certificates, string? title, string? message, X509SelectionFlag selectionFlag) { return SelectFromCollectionHelper(certificates, title, message, selectionFlag, IntPtr.Zero); } public static X509Certificate2Collection SelectFromCollection(X509Certificate2Collection certificates, string? title, string? message, X509SelectionFlag selectionFlag, IntPtr hwndParent) { return SelectFromCollectionHelper(certificates, title, message, selectionFlag, hwndParent); } private static unsafe void DisplayX509Certificate(X509Certificate2 certificate, IntPtr hwndParent) { using (SafeCertContextHandle safeCertContext = X509Utils.DuplicateCertificateContext(certificate)) { if (safeCertContext.IsInvalid) throw new CryptographicException(SR.Format(SR.Cryptography_InvalidHandle, nameof(safeCertContext))); int dwErrorCode = ERROR_SUCCESS; // Initialize view structure. Interop.CryptUI.CRYPTUI_VIEWCERTIFICATE_STRUCTW ViewInfo = default; #if NET ViewInfo.dwSize = (uint)sizeof(Interop.CryptUI.CRYPTUI_VIEWCERTIFICATE_STRUCTW.Marshaller.Native); #else ViewInfo.dwSize = (uint)Marshal.SizeOf<Interop.CryptUI.CRYPTUI_VIEWCERTIFICATE_STRUCTW>(); #endif ViewInfo.hwndParent = hwndParent; ViewInfo.dwFlags = 0; ViewInfo.szTitle = null; ViewInfo.pCertContext = safeCertContext.DangerousGetHandle(); ViewInfo.rgszPurposes = IntPtr.Zero; ViewInfo.cPurposes = 0; ViewInfo.pCryptProviderData = IntPtr.Zero; ViewInfo.fpCryptProviderDataTrustedUsage = false; ViewInfo.idxSigner = 0; ViewInfo.idxCert = 0; ViewInfo.fCounterSigner = false; ViewInfo.idxCounterSigner = 0; ViewInfo.cStores = 0; ViewInfo.rghStores = IntPtr.Zero; ViewInfo.cPropSheetPages = 0; ViewInfo.rgPropSheetPages = IntPtr.Zero; ViewInfo.nStartPage = 0; // View the certificate if (!Interop.CryptUI.CryptUIDlgViewCertificateW(ViewInfo, IntPtr.Zero)) dwErrorCode = Marshal.GetLastPInvokeError(); // CryptUIDlgViewCertificateW returns ERROR_CANCELLED if the user closes // the window through the x button or by pressing CANCEL, so ignore this error code if (dwErrorCode != ERROR_SUCCESS && dwErrorCode != ERROR_CANCELLED) throw new CryptographicException(dwErrorCode); } } private static X509Certificate2Collection SelectFromCollectionHelper(X509Certificate2Collection certificates, string? title, string? message, X509SelectionFlag selectionFlag, IntPtr hwndParent) { ArgumentNullException.ThrowIfNull(certificates); if (selectionFlag < X509SelectionFlag.SingleSelection || selectionFlag > X509SelectionFlag.MultiSelection) throw new ArgumentException(SR.Format(SR.Enum_InvalidValue, nameof(selectionFlag))); using (SafeCertStoreHandle safeSourceStoreHandle = X509Utils.ExportToMemoryStore(certificates)) using (SafeCertStoreHandle safeTargetStoreHandle = SelectFromStore(safeSourceStoreHandle, title, message, selectionFlag, hwndParent)) { return X509Utils.GetCertificates(safeTargetStoreHandle); } } private static unsafe SafeCertStoreHandle SelectFromStore(SafeCertStoreHandle safeSourceStoreHandle, string? title, string? message, X509SelectionFlag selectionFlags, IntPtr hwndParent) { int dwErrorCode = ERROR_SUCCESS; SafeCertStoreHandle safeCertStoreHandle = Interop.Crypt32.CertOpenStore( (IntPtr)Interop.Crypt32.CERT_STORE_PROV_MEMORY, Interop.Crypt32.X509_ASN_ENCODING | Interop.Crypt32.PKCS_7_ASN_ENCODING, IntPtr.Zero, 0, IntPtr.Zero); if (safeCertStoreHandle == null || safeCertStoreHandle.IsInvalid) { Exception e = new CryptographicException(Marshal.GetLastPInvokeError()); safeCertStoreHandle?.Dispose(); throw e; } Interop.CryptUI.CRYPTUI_SELECTCERTIFICATE_STRUCTW csc = default; // Older versions of CRYPTUI do not check the size correctly, // so always force it to the oldest version of the structure. #if NET // Declare a local for Native to enable us to get the managed byte offset // without having a null check cause a failure. Interop.CryptUI.CRYPTUI_SELECTCERTIFICATE_STRUCTW.Marshaller.Native native; Unsafe.SkipInit(out native); csc.dwSize = (uint)Unsafe.ByteOffset(ref Unsafe.As<Interop.CryptUI.CRYPTUI_SELECTCERTIFICATE_STRUCTW.Marshaller.Native, byte>(ref native), ref Unsafe.As<IntPtr, byte>(ref native.hSelectedCertStore)); #else csc.dwSize = (uint)Marshal.OffsetOf(typeof(Interop.CryptUI.CRYPTUI_SELECTCERTIFICATE_STRUCTW), "hSelectedCertStore"); #endif csc.hwndParent = hwndParent; csc.dwFlags = (uint)selectionFlags; csc.szTitle = title; csc.dwDontUseColumn = 0; csc.szDisplayString = message; csc.pFilterCallback = IntPtr.Zero; csc.pDisplayCallback = IntPtr.Zero; csc.pvCallbackData = IntPtr.Zero; csc.cDisplayStores = 1; IntPtr hSourceCertStore = safeSourceStoreHandle.DangerousGetHandle(); csc.rghDisplayStores = new IntPtr(&hSourceCertStore); csc.cStores = 0; csc.rghStores = IntPtr.Zero; csc.cPropSheetPages = 0; csc.rgPropSheetPages = IntPtr.Zero; csc.hSelectedCertStore = safeCertStoreHandle.DangerousGetHandle(); SafeCertContextHandle safeCertContextHandle = Interop.CryptUI.CryptUIDlgSelectCertificateW(ref csc); if (safeCertContextHandle != null && !safeCertContextHandle.IsInvalid) { // Single select, so add it to our hCertStore SafeCertContextHandle ppStoreContext = SafeCertContextHandle.InvalidHandle; if (!Interop.Crypt32.CertAddCertificateLinkToStore(safeCertStoreHandle, safeCertContextHandle, Interop.Crypt32.CERT_STORE_ADD_ALWAYS, ppStoreContext)) { dwErrorCode = Marshal.GetLastPInvokeError(); } } if (dwErrorCode != ERROR_SUCCESS) { safeCertContextHandle?.Dispose(); throw new CryptographicException(dwErrorCode); } return safeCertStoreHandle; } } }