From f1e3741c096bedf3e60fbf39a9d1d2e20deca920 Mon Sep 17 00:00:00 2001 From: Honfika Date: Wed, 17 May 2017 16:48:13 +0200 Subject: [PATCH 1/2] Allow to query the root certificate status (IsRootCertificateTrusted and IsRootCertificateMachineTrusted) Allow to machine trust the root certificate (it will show the UAC dialog when required) --- .../Network/CertificateManager.cs | 101 ++++++++++++++++-- 1 file changed, 93 insertions(+), 8 deletions(-) diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index 828e4f8fa..befb2d91b 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -30,7 +31,7 @@ public enum CertificateEngine /// /// A class to manage SSL certificates used by this proxy server /// - public class CertificateManager : IDisposable + public sealed class CertificateManager : IDisposable { internal CertificateEngine Engine { @@ -147,7 +148,7 @@ private string GetRootCertificatePath() return fileName; } - internal X509Certificate2 LoadRootCertificate() + private X509Certificate2 LoadRootCertificate() { var fileName = GetRootCertificatePath(); if (!File.Exists(fileName)) return null; @@ -218,6 +219,51 @@ public void TrustRootCertificate() TrustRootCertificate(StoreLocation.LocalMachine); } + /// + /// Puts the certificate to the local machine's certificate store. + /// Needs elevated permission. Works only on Windows. + /// + /// + public bool TrustRootCertificateAsAdministrator() + { + if (RunTime.IsRunningOnMono()) + { + return false; + } + + var fileName = Path.GetTempFileName(); + File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12)); + + var info = new ProcessStartInfo + { + FileName = "certutil.exe", + Arguments = "-importPFX -Enterprise -p \"\" -f \"" + fileName + "\"", + CreateNoWindow = true, + UseShellExecute = true, + Verb = "runas", + ErrorDialog = false, + }; + + try + { + var process = Process.Start(info); + if (process == null) + { + return false; + } + + process.WaitForExit(); + + File.Delete(fileName); + } + catch + { + return false; + } + + return true; + } + /// /// Removes the trusted certificates. /// @@ -230,13 +276,49 @@ public void RemoveTrustedRootCertificates() RemoveTrustedRootCertificates(StoreLocation.LocalMachine); } + /// + /// Determines whether the root certificate is trusted. + /// + public bool IsRootCertificateTrusted() + { + return FindRootCertificate(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted(); + } + + /// + /// Determines whether the root certificate is machine trusted. + /// + public bool IsRootCertificateMachineTrusted() + { + return FindRootCertificate(StoreLocation.LocalMachine); + } + + private bool FindRootCertificate(StoreLocation storeLocation) + { + string value = $"{RootCertificate.Issuer}"; + return FindCertificates(StoreName.Root, storeLocation, value).Count > 0; + } + + private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue) + { + X509Store x509Store = new X509Store(storeName, storeLocation); + try + { + x509Store.Open(OpenFlags.OpenExistingOnly); + return x509Store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, findValue, false); + } + finally + { + x509Store.Close(); + } + } + /// /// Create an SSL certificate /// /// /// /// - internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) + internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) { if (certificateCache.ContainsKey(certificateName)) { @@ -317,7 +399,7 @@ internal async void ClearIdleCertificates(int certificateCacheTimeOutMinutes) /// /// /// - internal void TrustRootCertificate(StoreLocation storeLocation) + private void TrustRootCertificate(StoreLocation storeLocation) { if (RootCertificate == null) { @@ -328,7 +410,7 @@ internal void TrustRootCertificate(StoreLocation storeLocation) return; } - X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); + var x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation); try @@ -357,7 +439,7 @@ internal void TrustRootCertificate(StoreLocation storeLocation) /// /// /// - internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) + private void RemoveTrustedRootCertificates(StoreLocation storeLocation) { if (RootCertificate == null) { @@ -368,7 +450,7 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) return; } - X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); + var x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509PersonalStore = new X509Store(StoreName.My, storeLocation); try @@ -382,7 +464,7 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) catch (Exception e) { exceptionFunc( - new Exception("Failed to make system trust root certificate " + new Exception("Failed to remove root certificate trust " + $" for {storeLocation} store location. You may need admin rights.", e)); } finally @@ -392,6 +474,9 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) } } + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// public void Dispose() { } From 03e69d2e2c2f52f975b44ad56de7b7d538a184cd Mon Sep 17 00:00:00 2001 From: Honfika Date: Wed, 17 May 2017 17:51:26 +0200 Subject: [PATCH 2/2] Enterprise parameter removed --- Titanium.Web.Proxy/Network/CertificateManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index befb2d91b..ddc9cd2ed 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -237,7 +237,7 @@ public bool TrustRootCertificateAsAdministrator() var info = new ProcessStartInfo { FileName = "certutil.exe", - Arguments = "-importPFX -Enterprise -p \"\" -f \"" + fileName + "\"", + Arguments = "-importPFX -p \"\" -f \"" + fileName + "\"", CreateNoWindow = true, UseShellExecute = true, Verb = "runas",