diff --git a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs index 6e1b67067..7a27bc17d 100644 --- a/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs +++ b/Examples/Titanium.Web.Proxy.Examples.Basic/ProxyTestController.cs @@ -17,6 +17,12 @@ public class ProxyTestController public ProxyTestController() { proxyServer = new ProxyServer(); + + //generate root certificate without storing it in file system + //proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle; + //proxyServer.CertificateManager.CreateTrustedRootCertificate(false); + //proxyServer.CertificateManager.TrustRootCertificate(); + proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message); proxyServer.TrustRootCertificate = true; @@ -92,6 +98,9 @@ public void Stop() proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection; proxyServer.Stop(); + + //remove the generated certificates + //proxyServer.CertificateManager.RemoveTrustedRootCertificates(); } //intecept & cancel redirect or update requests diff --git a/Titanium.Web.Proxy/Network/CertificateManager.cs b/Titanium.Web.Proxy/Network/CertificateManager.cs index 1d11444f2..d3e7547de 100644 --- a/Titanium.Web.Proxy/Network/CertificateManager.cs +++ b/Titanium.Web.Proxy/Network/CertificateManager.cs @@ -29,9 +29,9 @@ public enum CertificateEngine /// /// A class to manage SSL certificates used by this proxy server /// - internal class CertificateManager : IDisposable + public class CertificateManager : IDisposable { - public CertificateEngine Engine + internal CertificateEngine Engine { get { return engine; } set @@ -164,10 +164,13 @@ internal X509Certificate2 LoadRootCertificate() /// /// Attempts to create a RootCertificate /// - /// true if succeeded, else false - internal bool CreateTrustedRootCertificate() + /// if set to true try to load/save the certificate from rootCert.pfx. + /// + /// true if succeeded, else false + /// + public bool CreateTrustedRootCertificate(bool persistToFile = true) { - if (RootCertificate == null) + if (persistToFile && RootCertificate == null) { RootCertificate = LoadRootCertificate(); } @@ -186,7 +189,7 @@ internal bool CreateTrustedRootCertificate() exceptionFunc(e); } - if (RootCertificate != null) + if (persistToFile && RootCertificate != null) { try { @@ -205,7 +208,7 @@ internal bool CreateTrustedRootCertificate() /// /// Trusts the root certificate. /// - internal void TrustRootCertificate() + public void TrustRootCertificate() { //current user TrustRootCertificate(StoreLocation.CurrentUser); @@ -214,6 +217,18 @@ internal void TrustRootCertificate() TrustRootCertificate(StoreLocation.LocalMachine); } + /// + /// Removes the trusted certificates. + /// + public void RemoveTrustedRootCertificates() + { + //current user + RemoveTrustedRootCertificates(StoreLocation.CurrentUser); + + //current system + RemoveTrustedRootCertificates(StoreLocation.LocalMachine); + } + /// /// Create an SSL certificate /// @@ -336,6 +351,46 @@ internal void TrustRootCertificate(StoreLocation storeLocation) } } + /// + /// Remove the Root Certificate trust + /// + /// + /// + internal void RemoveTrustedRootCertificates(StoreLocation storeLocation) + { + if (RootCertificate == null) + { + exceptionFunc( + new Exception("Could not set root certificate" + + " as system proxy since it is null or empty.")); + + return; + } + + X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation); + var x509PersonalStore = new X509Store(StoreName.My, storeLocation); + + try + { + x509RootStore.Open(OpenFlags.ReadWrite); + x509PersonalStore.Open(OpenFlags.ReadWrite); + + x509RootStore.Remove(RootCertificate); + x509PersonalStore.Remove(RootCertificate); + } + catch (Exception e) + { + exceptionFunc( + new Exception("Failed to make system trust root certificate " + + $" for {storeLocation} store location. You may need admin rights.", e)); + } + finally + { + x509RootStore.Close(); + x509PersonalStore.Close(); + } + } + public void Dispose() { } diff --git a/Titanium.Web.Proxy/ProxyServer.cs b/Titanium.Web.Proxy/ProxyServer.cs index 75ee12fef..e0ce23bc3 100644 --- a/Titanium.Web.Proxy/ProxyServer.cs +++ b/Titanium.Web.Proxy/ProxyServer.cs @@ -24,11 +24,6 @@ public partial class ProxyServer : IDisposable /// private bool proxyRunning { get; set; } - /// - /// Manages certificates used by this proxy - /// - private CertificateManager certificateManager { get; set; } - /// /// An default exception log func /// @@ -64,13 +59,18 @@ private FireFoxProxySettingsManager firefoxProxySettingsManager /// public int BufferSize { get; set; } = 8192; + /// + /// Manages certificates used by this proxy + /// + public CertificateManager CertificateManager { get; } + /// /// The root certificate /// public X509Certificate2 RootCertificate { - get { return certificateManager.RootCertificate; } - set { certificateManager.RootCertificate = value; } + get { return CertificateManager.RootCertificate; } + set { CertificateManager.RootCertificate = value; } } /// @@ -79,8 +79,8 @@ public X509Certificate2 RootCertificate /// public string RootCertificateIssuerName { - get { return certificateManager.Issuer; } - set { certificateManager.RootCertificateName = value; } + get { return CertificateManager.Issuer; } + set { CertificateManager.Issuer = value; } } /// @@ -92,8 +92,8 @@ public string RootCertificateIssuerName /// public string RootCertificateName { - get { return certificateManager.RootCertificateName; } - set { certificateManager.Issuer = value; } + get { return CertificateManager.RootCertificateName; } + set { CertificateManager.RootCertificateName = value; } } /// @@ -121,8 +121,8 @@ public bool TrustRootCertificate /// public CertificateEngine CertificateEngine { - get { return certificateManager.Engine; } - set { certificateManager.Engine = value; } + get { return CertificateManager.Engine; } + set { CertificateManager.Engine = value; } } /// @@ -251,7 +251,7 @@ public ProxyServer(string rootCertificateName, string rootCertificateIssuerName) new FireFoxProxySettingsManager(); #endif - certificateManager = new CertificateManager(ExceptionFunc); + CertificateManager = new CertificateManager(ExceptionFunc); if (rootCertificateName != null) { RootCertificateName = rootCertificateName; @@ -356,7 +356,7 @@ public void SetAsSystemHttpsProxy(ExplicitProxyEndPoint endPoint) EnsureRootCertificate(); //If certificate was trusted by the machine - if (certificateManager.CertValidated) + if (CertificateManager.CertValidated) { systemProxySettingsManager.SetHttpsProxy( Equals(endPoint.IpAddress, IPAddress.Any) | @@ -435,7 +435,7 @@ public void Start() Listen(endPoint); } - certificateManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); + CertificateManager.ClearIdleCertificates(CertificateCacheTimeOutMinutes); proxyRunning = true; } @@ -468,7 +468,7 @@ public void Stop() ProxyEndPoints.Clear(); - certificateManager?.StopClearIdleCertificates(); + CertificateManager?.StopClearIdleCertificates(); proxyRunning = false; } @@ -483,7 +483,7 @@ public void Dispose() Stop(); } - certificateManager?.Dispose(); + CertificateManager?.Dispose(); } /// @@ -543,13 +543,13 @@ private Task GetSystemUpStreamProxy(SessionEventArgs sessionEvent private void EnsureRootCertificate() { - if (!certificateManager.CertValidated) + if (!CertificateManager.CertValidated) { - certificateManager.CreateTrustedRootCertificate(); + CertificateManager.CreateTrustedRootCertificate(); if (TrustRootCertificate) { - certificateManager.TrustRootCertificate(); + CertificateManager.TrustRootCertificate(); } } } diff --git a/Titanium.Web.Proxy/RequestHandler.cs b/Titanium.Web.Proxy/RequestHandler.cs index b4fc6fa0c..37e41d8e6 100644 --- a/Titanium.Web.Proxy/RequestHandler.cs +++ b/Titanium.Web.Proxy/RequestHandler.cs @@ -110,7 +110,7 @@ private async Task HandleClient(ExplicitProxyEndPoint endPoint, TcpClient tcpCli { sslStream = new SslStream(clientStream, true); - var certificate = endPoint.GenericCertificate ?? certificateManager.CreateCertificate(httpRemoteUri.Host, false); + var certificate = endPoint.GenericCertificate ?? CertificateManager.CreateCertificate(httpRemoteUri.Host, false); //Successfully managed to authenticate the client using the fake certificate await sslStream.AuthenticateAsServerAsync(certificate, false, @@ -177,7 +177,7 @@ private async Task HandleClient(TransparentProxyEndPoint endPoint, TcpClient tcp var sslStream = new SslStream(clientStream, true); //implement in future once SNI supported by SSL stream, for now use the same certificate - var certificate = certificateManager.CreateCertificate(endPoint.GenericCertificateName, false); + var certificate = CertificateManager.CreateCertificate(endPoint.GenericCertificateName, false); try {