.NET Core 抛出 WindowsCryptographicException:访问在 X509Store.Add 上被拒绝

.NET Core throws WindowsCryptographicException: Access is denied on X509Store.Add

我写了一个安装证书的方法:

public void Install(StoreName storeName, StoreLocation storeLocation)
{
    using var store = new X509Store(storeName, storeLocation);
    store.Open(OpenFlags.ReadWrite);
    store.Add(cert);
}

并通过传递 StoreName.Root、StoreLocation.CurrentUser

来调用它
certificateManager.Install(StoreName.Root, StoreLocation.CurrentUser);

将导致:

Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: Access is denied.
   at Internal.Cryptography.Pal.StorePal.Add(ICertificatePal certificate)
   at System.Security.Cryptography.X509Certificates.X509Store.Add(X509Certificate2 certificate)
   at Pixeval.Persisting.WebApi.CertificateManager.Install(StoreName storeName, StoreLocation storeLocation) in C:\NotSystem\Coding\projects\visualstudio\csharp\Pixeval\Persisting\WebApi\CertificateManager.cs:line 99
   at Pixeval.App.InstallFakeCaCertificate() in C:\NotSystem\Coding\projects\visualstudio\csharp\Pixeval\App.xaml.cs:line 129
   at Pixeval.App.OnStartup(StartupEventArgs e) in C:\NotSystem\Coding\projects\visualstudio\csharp\Pixeval\App.xaml.cs:line 68
   at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_0(Object state)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.DispatcherOperation.InvokeImpl()
   at System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(Object state)
   at MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(Object obj)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location where exception was thrown ---
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at MS.Internal.CulturePreservingExecutionContext.Run(CulturePreservingExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Windows.Threading.DispatcherOperation.Invoke()
   at System.Windows.Threading.Dispatcher.ProcessQueue()
   at System.Windows.Threading.Dispatcher.WndProcHook(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndWrapper.WndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam, Boolean& handled)
   at MS.Win32.HwndSubclass.DispatcherCallbackOperation(Object o)
   at System.Windows.Threading.ExceptionWrapper.InternalRealCall(Delegate callback, Object args, Int32 numArgs)
   at System.Windows.Threading.ExceptionWrapper.TryCatchWhen(Object source, Delegate callback, Object args, Int32 numArgs, Delegate catchHandler)
   at System.Windows.Threading.Dispatcher.LegacyInvokeImpl(DispatcherPriority priority, TimeSpan timeout, Delegate method, Object args, Int32 numArgs)
   at MS.Win32.HwndSubclass.SubclassWndProc(IntPtr hwnd, Int32 msg, IntPtr wParam, IntPtr lParam)
   at MS.Win32.UnsafeNativeMethods.DispatchMessage(MSG& msg)
   at System.Windows.Threading.Dispatcher.PushFrameImpl(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.PushFrame(DispatcherFrame frame)
   at System.Windows.Threading.Dispatcher.Run()
   at System.Windows.Application.RunDispatcher(Object ignore)
   at System.Windows.Application.RunInternal(Window window)
   at System.Windows.Application.Run()
   at Pixeval.App.Main()

谷歌搜索后,我发现大多数相关问题都在询问 StoreLocation.LocalMachine,我没有在 StoreLocation.CurrentUser

找到安装证书的任何解决方案

注意:我无法重现此问题,因为它是由我的应用程序的一位用户发布的

有两个问题,一个与配置有关,另一个与代码本身有关。

问题 1:

引发异常是因为 CurrentUser.Root 存储是只读的。您的系统管理员似乎配置了 GPO 以限制用户写入此存储:

问题 2:

即使您修复了此问题,代码也可能以不同的方式失败:它可能 hang/freeze。这是因为当您将证书安装到 CurrentUser.Root 时,将调用弹出窗口(您无法避免此弹出窗口)以确认操作:

我没有测试过,但是如果在后台线程中调用代码,那么代码可能会失败,因为 UI 线程是从后台线程访问的。

如果应用程序作为 service/batch/imersonated 上下文运行(例如网络应用程序),弹出窗口将显示在单独的会话中,而不是在主屏幕上,因此没有人能够按确认按钮。

因此,您不得在 CurrentUser.Root 当前交互式记录的用户上下文之外安装证书,并确保任何人都可以按下确认按钮。并避免从背景任务中执行此代码。