Commit 1405c3ab authored by justcoding121's avatar justcoding121

Merge branch 'develop' into beta

parents a988e27d 5d22fe00
...@@ -32,8 +32,10 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -32,8 +32,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
//generate root certificate without storing it in file system //generate root certificate without storing it in file system
//proxyServer.CertificateManager.CreateTrustedRootCertificate(false); //proxyServer.CertificateManager.CreateRootCertificate(false);
//proxyServer.CertificateManager.TrustRootCertificate(); //proxyServer.CertificateManager.TrustRootCertificate();
//proxyServer.CertificateManager.TrustRootCertificateAsAdmin();
proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message); proxyServer.ExceptionFunc = exception => Console.WriteLine(exception.Message);
proxyServer.ForwardToUpstreamGateway = true; proxyServer.ForwardToUpstreamGateway = true;
......
...@@ -88,11 +88,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -88,11 +88,8 @@ namespace Titanium.Web.Proxy.Examples.Wpf
////note : load now (if existed) ////note : load now (if existed)
//proxyServer.CertificateManager.LoadRootCertificate(@"C:\NameFolder\rootCert.pfx", "PfxPassword"); //proxyServer.CertificateManager.LoadRootCertificate(@"C:\NameFolder\rootCert.pfx", "PfxPassword");
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true);
{
ExcludedHttpsHostNameRegex = new[] { "ssllabs.com" },
//IncludedHttpsHostNameRegex = new string[0],
};
proxyServer.AddEndPoint(explicitEndPoint); proxyServer.AddEndPoint(explicitEndPoint);
//proxyServer.UpStreamHttpProxy = new ExternalProxy //proxyServer.UpStreamHttpProxy = new ExternalProxy
......
...@@ -15,25 +15,27 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -15,25 +15,27 @@ namespace Titanium.Web.Proxy.UnitTests
private readonly Random random = new Random(); private readonly Random random = new Random();
[TestMethod] [TestMethod]
public async Task Simple_Create_Certificate_Test() public async Task Simple_BC_Create_Certificate_Test()
{ {
var tasks = new List<Task>(); var tasks = new List<Task>();
var mgr = new CertificateManager(new Lazy<Action<Exception>>(() => (e => { })).Value); var mgr = new CertificateManager(new Lazy<Action<Exception>>(() => (e =>
{
//Console.WriteLine(e.ToString() + e.InnerException != null ? e.InnerException.ToString() : string.Empty);
})).Value);
mgr.CertificateEngine = CertificateEngine.BouncyCastle;
mgr.ClearIdleCertificates(); mgr.ClearIdleCertificates();
for (int i = 0; i < 5; i++)
foreach (string host in hostNames) foreach (string host in hostNames)
{
tasks.Add(Task.Run(async () =>
{ {
tasks.Add(Task.Run(() =>
//get the connection {
var certificate = await mgr.CreateCertificateAsync(host); //get the connection
var certificate = mgr.CreateCertificate(host, false);
Assert.IsNotNull(certificate); Assert.IsNotNull(certificate);
})); }));
} }
await Task.WhenAll(tasks.ToArray()); await Task.WhenAll(tasks.ToArray());
...@@ -42,30 +44,34 @@ namespace Titanium.Web.Proxy.UnitTests ...@@ -42,30 +44,34 @@ namespace Titanium.Web.Proxy.UnitTests
//uncomment this to compare WinCert maker performance with BC (BC takes more time for same test above) //uncomment this to compare WinCert maker performance with BC (BC takes more time for same test above)
//cannot run this test in build server since trusting the certificate won't happen successfully //cannot run this test in build server since trusting the certificate won't happen successfully
//[TestMethod] [TestMethod]
public async Task Simple_Create_Win_Certificate_Test() public async Task Simple_Create_Win_Certificate_Test()
{ {
var tasks = new List<Task>(); var tasks = new List<Task>();
var mgr = new CertificateManager(new Lazy<Action<Exception>>(() => (e => { })).Value); var mgr = new CertificateManager(new Lazy<Action<Exception>>(() => (e =>
{
//Console.WriteLine(e.ToString() + e.InnerException != null ? e.InnerException.ToString() : string.Empty);
})).Value);
mgr.CertificateEngine = CertificateEngine.DefaultWindows;
mgr.CreateRootCertificate(true); mgr.CreateRootCertificate(true);
mgr.TrustRootCertificate(); mgr.TrustRootCertificate(true);
mgr.ClearIdleCertificates(); mgr.ClearIdleCertificates();
mgr.CertificateEngine = CertificateEngine.DefaultWindows;
foreach (string host in hostNames) for (int i = 0; i < 5; i++)
{ foreach (string host in hostNames)
tasks.Add(Task.Run(async () =>
{ {
//get the connection tasks.Add(Task.Run(() =>
var certificate = await mgr.CreateCertificateAsync(host); {
//get the connection
Assert.IsNotNull(certificate); var certificate = mgr.CreateCertificate(host, false);
})); Assert.IsNotNull(certificate);
} }));
}
await Task.WhenAll(tasks.ToArray()); await Task.WhenAll(tasks.ToArray());
mgr.RemoveTrustedRootCertificate(true);
mgr.StopClearIdleCertificates(); mgr.StopClearIdleCertificates();
} }
} }
......
...@@ -75,42 +75,6 @@ namespace Titanium.Web.Proxy.Models ...@@ -75,42 +75,6 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public X509Certificate2 GenericCertificate { get; set; } public X509Certificate2 GenericCertificate { get; set; }
/// <summary>
/// List of host names to exclude using Regular Expressions.
/// </summary>
[Obsolete("ExcludedHttpsHostNameRegex is deprecated, please use BeforeTunnelConnect event instead.")]
public IEnumerable<string> ExcludedHttpsHostNameRegex
{
get { return ExcludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); }
set
{
if (IncludedHttpsHostNameRegex != null)
{
throw new ArgumentException("Cannot set excluded when included is set");
}
ExcludedHttpsHostNameRegexList = value?.Select(x => new Regex(x, RegexOptions.Compiled)).ToList();
}
}
/// <summary>
/// List of host names to exclude using Regular Expressions.
/// </summary>
[Obsolete("IncludedHttpsHostNameRegex is deprecated, please use BeforeTunnelConnect event instead.")]
public IEnumerable<string> IncludedHttpsHostNameRegex
{
get { return IncludedHttpsHostNameRegexList?.Select(x => x.ToString()).ToList(); }
set
{
if (ExcludedHttpsHostNameRegex != null)
{
throw new ArgumentException("Cannot set included when excluded is set");
}
IncludedHttpsHostNameRegexList = value?.Select(x => new Regex(x, RegexOptions.Compiled)).ToList();
}
}
/// <summary> /// <summary>
/// Return true if this HTTP connect request should'nt be decrypted and instead be relayed /// Return true if this HTTP connect request should'nt be decrypted and instead be relayed
/// Valid only for explicit endpoints /// Valid only for explicit endpoints
......
...@@ -17,6 +17,7 @@ using Org.BouncyCastle.Pkcs; ...@@ -17,6 +17,7 @@ using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security; using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509; using Org.BouncyCastle.X509;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Network.Certificate namespace Titanium.Web.Proxy.Network.Certificate
{ {
...@@ -25,8 +26,6 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -25,8 +26,6 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// </summary> /// </summary>
internal class BCCertificateMaker : ICertificateMaker internal class BCCertificateMaker : ICertificateMaker
{ {
public static readonly Regex CNRemoverRegex = new Regex(@"^CN\s*=\s*", RegexOptions.IgnoreCase | RegexOptions.Compiled);
private const int certificateValidDays = 1825; private const int certificateValidDays = 1825;
private const int certificateGraceDays = 366; private const int certificateGraceDays = 366;
...@@ -149,7 +148,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -149,7 +148,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
{ {
try try
{ {
x509Certificate.FriendlyName = CNRemoverRegex.Replace(subjectName, string.Empty); x509Certificate.FriendlyName = ProxyConstants.CNRemoverRegex.Replace(subjectName, string.Empty);
} }
catch (PlatformNotSupportedException) catch (PlatformNotSupportedException)
{ {
......
...@@ -250,17 +250,8 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -250,17 +250,8 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue); typeX509Enrollment.InvokeMember("InstallResponse", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
typeValue = new object[] { null, 0, 1 }; typeValue = new object[] { null, 0, 1 };
try string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
{ return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
string empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
}
catch (Exception)
{
// ignored
}
return null;
} }
private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot, private X509Certificate2 MakeCertificateInternal(string sSubjectCN, bool isRoot,
......
...@@ -9,6 +9,7 @@ using System.Security.Cryptography.X509Certificates; ...@@ -9,6 +9,7 @@ using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network.Certificate; using Titanium.Web.Proxy.Network.Certificate;
using Titanium.Web.Proxy.Shared;
namespace Titanium.Web.Proxy.Network namespace Titanium.Web.Proxy.Network
{ {
...@@ -70,14 +71,14 @@ namespace Titanium.Web.Proxy.Network ...@@ -70,14 +71,14 @@ namespace Titanium.Web.Proxy.Network
/// Note that this do not make the client trust the certificate! /// Note that this do not make the client trust the certificate!
/// This would import the root certificate to the certificate store of machine that runs this proxy server /// This would import the root certificate to the certificate store of machine that runs this proxy server
/// </summary> /// </summary>
internal bool TrustRoot { get; set; } = false; internal bool UserTrustRoot { get; set; } = false;
/// <summary> /// <summary>
/// Needs elevated permission. Works only on Windows. /// Needs elevated permission. Works only on Windows.
/// <para>Puts the certificate to the local machine's certificate store.</para> /// <para>Puts the certificate to the local machine's certificate store.</para>
/// <para>Certutil.exe is a command-line program that is installed as part of Certificate Services</para> /// <para>Certutil.exe is a command-line program that is installed as part of Certificate Services</para>
/// </summary> /// </summary>
internal bool TrustRootAsAdministrator { get; set; } = false; internal bool MachineTrustRootAsAdministrator { get; set; } = false;
/// <summary> /// <summary>
/// Select Certificate Engine /// Select Certificate Engine
...@@ -188,7 +189,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -188,7 +189,7 @@ namespace Titanium.Web.Proxy.Network
internal CertificateManager(Action<Exception> exceptionFunc) internal CertificateManager(Action<Exception> exceptionFunc)
{ {
this.exceptionFunc = exceptionFunc; this.exceptionFunc = exceptionFunc;
if(RunTime.IsWindows) if (RunTime.IsWindows)
{ {
//this is faster in Windows based on tests (see unit test project CertificateManagerTests.cs) //this is faster in Windows based on tests (see unit test project CertificateManagerTests.cs)
CertificateEngine = CertificateEngine.DefaultWindows; CertificateEngine = CertificateEngine.DefaultWindows;
...@@ -247,10 +248,17 @@ namespace Titanium.Web.Proxy.Network ...@@ -247,10 +248,17 @@ namespace Titanium.Web.Proxy.Network
return fileName; return fileName;
} }
private bool FindRootCertificate(StoreLocation storeLocation) /// <summary>
/// For CertificateEngine.DefaultWindows to work we need to also check in personal store
/// </summary>
/// <param name="storeLocation"></param>
/// <returns></returns>
private bool RootCertificateInstalled(StoreLocation storeLocation)
{ {
string value = $"{RootCertificate.Issuer}"; string value = $"{RootCertificate.Issuer}";
return FindCertificates(StoreName.Root, storeLocation, value).Count > 0; return FindCertificates(StoreName.Root, storeLocation, value).Count > 0
&& (CertificateEngine != CertificateEngine.DefaultWindows
|| FindCertificates(StoreName.My, storeLocation, value).Count > 0);
} }
private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue) private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue)
...@@ -267,53 +275,41 @@ namespace Titanium.Web.Proxy.Network ...@@ -267,53 +275,41 @@ namespace Titanium.Web.Proxy.Network
} }
} }
private X509Certificate2 MakeCertificate(string certificateName, bool isRootCertificate)
{
if (!isRootCertificate && RootCertificate == null)
{
CreateRootCertificate();
}
return certEngine.MakeCertificate(certificateName, isRootCertificate, RootCertificate);
}
/// <summary> /// <summary>
/// Make current machine trust the Root Certificate used by this proxy /// Make current machine trust the Root Certificate used by this proxy
/// </summary> /// </summary>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <returns></returns> /// <returns></returns>
private void TrustRootCertificate(StoreLocation storeLocation) private void InstallCertificate(StoreName storeName, StoreLocation storeLocation)
{ {
if (RootCertificate == null) if (RootCertificate == null)
{ {
exceptionFunc( exceptionFunc(
new Exception("Could not set root certificate" new Exception("Could not install certificate"
+ " as system proxy since it is null or empty.")); + " as it is null or empty."));
return; return;
} }
var x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509store = new X509Store(storeName, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation);
//TODO
//also it should do not duplicate if certificate already exists
try try
{ {
x509RootStore.Open(OpenFlags.ReadWrite); x509store.Open(OpenFlags.ReadWrite);
x509PersonalStore.Open(OpenFlags.ReadWrite); x509store.Add(RootCertificate);
x509RootStore.Add(RootCertificate);
x509PersonalStore.Add(RootCertificate);
} }
catch (Exception e) catch (Exception e)
{ {
exceptionFunc( exceptionFunc(
new Exception("Failed to make system trust root certificate " new Exception("Failed to make system trust root certificate "
+ $" for {storeLocation} store location. You may need admin rights.", e)); + $" for {storeName}\\{storeLocation} store location. You may need admin rights.", e));
} }
finally finally
{ {
x509RootStore.Close(); x509store.Close();
x509PersonalStore.Close();
} }
} }
...@@ -322,27 +318,24 @@ namespace Titanium.Web.Proxy.Network ...@@ -322,27 +318,24 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
/// <param name="storeLocation"></param> /// <param name="storeLocation"></param>
/// <returns></returns> /// <returns></returns>
private void RemoveTrustedRootCertificate(StoreLocation storeLocation) private void UninstallCertificate(StoreName storeName, StoreLocation storeLocation, X509Certificate2 certificate)
{ {
if (RootCertificate == null) if (certificate == null)
{ {
exceptionFunc( exceptionFunc(
new Exception("Could not set root certificate" new Exception("Could not remove certificate"
+ " as system proxy since it is null or empty.")); + " as it is null or empty."));
return; return;
} }
var x509RootStore = new X509Store(StoreName.Root, storeLocation); var x509Store = new X509Store(storeName, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation);
try try
{ {
x509RootStore.Open(OpenFlags.ReadWrite); x509Store.Open(OpenFlags.ReadWrite);
x509PersonalStore.Open(OpenFlags.ReadWrite);
x509RootStore.Remove(RootCertificate); x509Store.Remove(certificate);
x509PersonalStore.Remove(RootCertificate);
} }
catch (Exception e) catch (Exception e)
{ {
...@@ -352,9 +345,26 @@ namespace Titanium.Web.Proxy.Network ...@@ -352,9 +345,26 @@ namespace Titanium.Web.Proxy.Network
} }
finally finally
{ {
x509RootStore.Close(); x509Store.Close();
x509PersonalStore.Close(); }
}
private X509Certificate2 MakeCertificate(string certificateName, bool isRootCertificate)
{
if (!isRootCertificate && RootCertificate == null)
{
CreateRootCertificate();
}
var certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, RootCertificate);
if (CertificateEngine == CertificateEngine.DefaultWindows)
{
//prevent certificates getting piled up in User\Personal store
Task.Run(() => UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, certificate));
} }
return certificate;
} }
/// <summary> /// <summary>
...@@ -372,22 +382,32 @@ namespace Titanium.Web.Proxy.Network ...@@ -372,22 +382,32 @@ namespace Titanium.Web.Proxy.Network
if (!isRootCertificate && SaveFakeCertificates) if (!isRootCertificate && SaveFakeCertificates)
{ {
string path = GetCertificatePath(); string path = GetCertificatePath();
string subjectName = BCCertificateMaker.CNRemoverRegex.Replace(certificateName, string.Empty); string subjectName = ProxyConstants.CNRemoverRegex.Replace(certificateName, string.Empty);
subjectName = subjectName.Replace("*", "$x$"); subjectName = subjectName.Replace("*", "$x$");
subjectName = Path.Combine(path, subjectName + ".pfx"); var certificatePath = Path.Combine(path, subjectName + ".pfx");
if (!File.Exists(subjectName)) if (!File.Exists(certificatePath))
{ {
certificate = MakeCertificate(certificateName, isRootCertificate); certificate = MakeCertificate(certificateName, isRootCertificate);
File.WriteAllBytes(subjectName, certificate.Export(X509ContentType.Pkcs12));
//store as cache
Task.Run(() =>
{
try
{
File.WriteAllBytes(certificatePath, certificate.Export(X509ContentType.Pkcs12));
}
catch (Exception e) { exceptionFunc(new Exception("Failed to save fake certificate.", e)); }
});
} }
else else
{ {
try try
{ {
certificate = new X509Certificate2(subjectName, string.Empty, StorageFlag); certificate = new X509Certificate2(certificatePath, string.Empty, StorageFlag);
} }
catch /* (Exception e)*/ //if load failed create again
catch
{ {
certificate = MakeCertificate(certificateName, isRootCertificate); certificate = MakeCertificate(certificateName, isRootCertificate);
} }
...@@ -457,14 +477,6 @@ namespace Titanium.Web.Proxy.Network ...@@ -457,14 +477,6 @@ namespace Titanium.Web.Proxy.Network
} }
/// <summary>
/// Stops the certificate cache clear process
/// </summary>
internal void StopClearIdleCertificates()
{
clearCertificates = false;
}
/// <summary> /// <summary>
/// A method to clear outdated certificates /// A method to clear outdated certificates
/// </summary> /// </summary>
...@@ -486,62 +498,13 @@ namespace Titanium.Web.Proxy.Network ...@@ -486,62 +498,13 @@ namespace Titanium.Web.Proxy.Network
} }
} }
/// <summary>
/// Load or Create Certificate : after "Test Is the root certificate used by this proxy is valid?"
/// <param name="trustRootCertificate">"Make current machine trust the Root Certificate used by this proxy" ==> True or False</param>
/// </summary>
public void EnsureRootCertificate(bool trustRootCertificate, bool trustRootCertificateAsAdmin)
{
this.TrustRoot = trustRootCertificate;
this.TrustRootAsAdministrator = trustRootCertificateAsAdmin;
EnsureRootCertificate();
}
/// <summary> /// <summary>
/// Create/load root if required and ensure that the root certificate is trusted. /// Stops the certificate cache clear process
/// </summary> /// </summary>
public void EnsureRootCertificate() internal void StopClearIdleCertificates()
{
if (!CertValidated)
{
CreateRootCertificate();
if (TrustRoot)
{
TrustRootCertificate();
}
if (TrustRootAsAdministrator)
{
TrustRootCertificateAsAdmin();
}
}
}
public void ClearRootCertificate()
{
certificateCache.Clear();
rootCertificate = null;
}
public X509Certificate2 LoadRootCertificate()
{ {
string fileName = GetRootCertificatePath(); clearCertificates = false;
pfxFileExists = File.Exists(fileName);
if (!pfxFileExists)
{
return null;
}
try
{
return new X509Certificate2(fileName, PfxPassword, StorageFlag);
}
catch (Exception e)
{
exceptionFunc(e);
return null;
}
} }
/// <summary> /// <summary>
...@@ -603,7 +566,31 @@ namespace Titanium.Web.Proxy.Network ...@@ -603,7 +566,31 @@ namespace Titanium.Web.Proxy.Network
} }
/// <summary> /// <summary>
/// Manually load a Root certificate file(.pfx file) /// Loads root certificate from current executing assembly location with expected name rootCert.pfx
/// </summary>
/// <returns></returns>
public X509Certificate2 LoadRootCertificate()
{
string fileName = GetRootCertificatePath();
pfxFileExists = File.Exists(fileName);
if (!pfxFileExists)
{
return null;
}
try
{
return new X509Certificate2(fileName, PfxPassword, StorageFlag);
}
catch (Exception e)
{
exceptionFunc(e);
return null;
}
}
/// <summary>
/// Manually load a Root certificate file from give path (.pfx file)
/// </summary> /// </summary>
/// <param name="pfxFilePath">Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as "rootCert.pfx" (and will be saved in proxy dll directory)</param> /// <param name="pfxFilePath">Set the name(path) of the .pfx file. If it is string.Empty Root certificate file will be named as "rootCert.pfx" (and will be saved in proxy dll directory)</param>
/// <param name="password">Set a password for the .pfx file</param> /// <param name="password">Set a password for the .pfx file</param>
...@@ -625,42 +612,67 @@ namespace Titanium.Web.Proxy.Network ...@@ -625,42 +612,67 @@ namespace Titanium.Web.Proxy.Network
} }
/// <summary> /// <summary>
/// Trusts the root certificate. /// Trusts the root certificate in user store, optionally also in machine store
/// Machine trust would require elevated permissions (will silently fail otherwise)
/// </summary> /// </summary>
public void TrustRootCertificate() public void TrustRootCertificate(bool machineTrusted = false)
{ {
//current user //currentUser\personal
TrustRootCertificate(StoreLocation.CurrentUser); InstallCertificate(StoreName.My, StoreLocation.CurrentUser);
//current system if (!machineTrusted)
TrustRootCertificate(StoreLocation.LocalMachine); {
//currentUser\Root
InstallCertificate(StoreName.Root, StoreLocation.CurrentUser);
}
else
{
//current system
InstallCertificate(StoreName.My, StoreLocation.LocalMachine);
//this adds to both currentUser\Root & currentMachine\Root
InstallCertificate(StoreName.Root, StoreLocation.LocalMachine);
}
} }
/// <summary> /// <summary>
/// Puts the certificate to the local machine's certificate store. /// Puts the certificate to the user store, optionally also to machine store
/// Needs elevated permission. Works only on Windows. /// Prompts with UAC if elevated permissions are required. Works only on Windows.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool TrustRootCertificateAsAdmin() public bool TrustRootCertificateAsAdmin(bool machineTrusted = false)
{ {
if (!RunTime.IsWindows || RunTime.IsRunningOnMono) if (!RunTime.IsWindows || RunTime.IsRunningOnMono)
{ {
return false; return false;
} }
string fileName = Path.GetTempFileName(); //currentUser\Personal
File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword)); InstallCertificate(StoreName.My, StoreLocation.CurrentUser);
string pfxFileName = Path.GetTempFileName();
File.WriteAllBytes(pfxFileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword));
var info = new ProcessStartInfo //currentUser\Root, currentMachine\Personal & currentMachine\Root
var info = new ProcessStartInfo()
{ {
FileName = "certutil.exe", FileName = "certutil.exe",
Arguments = "-importPFX -p \"" + PfxPassword + "\" -f \"" + fileName + "\"",
CreateNoWindow = true, CreateNoWindow = true,
UseShellExecute = true, UseShellExecute = true,
Verb = "runas", Verb = "runas",
ErrorDialog = false, ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden
}; };
if (!machineTrusted)
{
info.Arguments = "-f -user -p \"" + PfxPassword + "\" -importpfx root \"" + pfxFileName + "\"";
}
else
{
info.Arguments = "-importPFX -p \"" + PfxPassword + "\" -f \"" + pfxFileName + "\"";
}
try try
{ {
var process = Process.Start(info); var process = Process.Start(info);
...@@ -670,11 +682,12 @@ namespace Titanium.Web.Proxy.Network ...@@ -670,11 +682,12 @@ namespace Titanium.Web.Proxy.Network
} }
process.WaitForExit(); process.WaitForExit();
File.Delete(pfxFileName);
File.Delete(fileName);
} }
catch catch (Exception e)
{ {
exceptionFunc(e);
return false; return false;
} }
...@@ -682,75 +695,167 @@ namespace Titanium.Web.Proxy.Network ...@@ -682,75 +695,167 @@ namespace Titanium.Web.Proxy.Network
} }
/// <summary> /// <summary>
/// Removes the trusted certificates. /// Ensure certificates are setup (creates root if required)
/// Also makes root certificate trusted based on initial setup from proxy constructor for user/machine trust.
/// </summary> /// </summary>
public void RemoveTrustedRootCertificate() public void EnsureRootCertificate(bool machineTrustRootCertificate = false)
{ {
//current user if (!CertValidated)
RemoveTrustedRootCertificate(StoreLocation.CurrentUser); {
CreateRootCertificate();
}
if (UserTrustRoot)
{
TrustRootCertificate(machineTrustRootCertificate);
}
if (MachineTrustRootAsAdministrator)
{
TrustRootCertificateAsAdmin();
}
//current system
RemoveTrustedRootCertificate(StoreLocation.LocalMachine);
} }
/// <summary> /// <summary>
/// Removes the trusted certificates from the local machine's certificate store. /// Ensure certificates are setup (creates root if required)
/// Needs elevated permission. Works only on Windows. /// Also makes root certificate trusted based on provided parameters
/// </summary>
public void EnsureRootCertificate(bool userTrustRootCertificate, bool machineTrustRootCertificate, bool machineTrustRootCertificateAsAdmin)
{
UserTrustRoot = userTrustRootCertificate;
MachineTrustRootAsAdministrator = machineTrustRootCertificateAsAdmin;
EnsureRootCertificate(machineTrustRootCertificate);
}
/// <summary>
/// Determines whether the root certificate is trusted.
/// </summary>
public bool IsRootCertificateUserTrusted()
{
return RootCertificateInstalled(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted();
}
/// <summary>
/// Determines whether the root certificate is machine trusted.
/// </summary>
public bool IsRootCertificateMachineTrusted()
{
return RootCertificateInstalled(StoreLocation.LocalMachine);
}
/// <summary>
/// Removes the trusted certificates from user store, optionally also from machine store
/// To remove from machine store elevated permissions are required (will fail silently otherwise)
/// </summary>
public void RemoveTrustedRootCertificate(bool machineTrusted = false)
{
//currentUser\personal
UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate);
if (!machineTrusted)
{
//currentUser\Root
UninstallCertificate(StoreName.Root, StoreLocation.CurrentUser, RootCertificate);
}
else
{
//current system
UninstallCertificate(StoreName.My, StoreLocation.LocalMachine, RootCertificate);
//this adds to both currentUser\Root & currentMachine\Root
UninstallCertificate(StoreName.Root, StoreLocation.LocalMachine, RootCertificate);
}
}
/// <summary>
/// Removes the trusted certificates from user store, optionally also from machine store
// Prompts with UAC if elevated permissions are required. Works only on Windows.
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool RemoveTrustedRootCertificateAsAdmin() public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false)
{ {
if (!RunTime.IsWindows || RunTime.IsRunningOnMono) if (!RunTime.IsWindows || RunTime.IsRunningOnMono)
{ {
return false; return false;
} }
//currentUser\Personal
UninstallCertificate(StoreName.My, StoreLocation.CurrentUser, RootCertificate);
var info = new ProcessStartInfo var infos = new List<ProcessStartInfo>();
if (!machineTrusted)
{ {
//currentUser\Root
infos.Add(new ProcessStartInfo()
{
FileName = "certutil.exe",
Arguments = "-delstore -user Root \"" + RootCertificateName + "\"",
CreateNoWindow = true,
UseShellExecute = true,
Verb = "runas",
ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden
});
}
else
{
infos.AddRange(
new List<ProcessStartInfo>() {
//currentMachine\Personal
new ProcessStartInfo(){
FileName = "certutil.exe",
Arguments = "-delstore My \"" + RootCertificateName + "\"",
CreateNoWindow = true,
UseShellExecute = true,
Verb = "runas",
ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden
},
//currentUser\Personal & currentMachine\Personal
new ProcessStartInfo(){
FileName = "certutil.exe", FileName = "certutil.exe",
Arguments = "-delstore Root \"" + RootCertificateName + "\"", Arguments = "-delstore Root \"" + RootCertificateName + "\"",
CreateNoWindow = true, CreateNoWindow = true,
UseShellExecute = true, UseShellExecute = true,
Verb = "runas", Verb = "runas",
ErrorDialog = false, ErrorDialog = false,
}; WindowStyle = ProcessWindowStyle.Hidden
}
});
}
var success = true;
try try
{ {
var process = Process.Start(info); foreach (var info in infos)
if (process == null)
{ {
return false; var process = Process.Start(info);
}
process.WaitForExit(); if (process == null)
{
success = false;
}
process.WaitForExit();
}
} }
catch catch
{ {
return false; success = false;
} }
return true; return success;
} }
/// <summary> public void ClearRootCertificate()
/// Determines whether the root certificate is trusted.
/// </summary>
public bool IsRootCertificateTrusted()
{
return FindRootCertificate(StoreLocation.CurrentUser) || IsRootCertificateMachineTrusted();
}
/// <summary>
/// Determines whether the root certificate is machine trusted.
/// </summary>
public bool IsRootCertificateMachineTrusted()
{ {
return FindRootCertificate(StoreLocation.LocalMachine); certificateCache.Clear();
rootCertificate = null;
} }
/// <summary> /// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary> /// </summary>
......
...@@ -245,8 +245,8 @@ namespace Titanium.Web.Proxy ...@@ -245,8 +245,8 @@ namespace Titanium.Web.Proxy
} }
CertificateManager = new CertificateManager(ExceptionFunc); CertificateManager = new CertificateManager(ExceptionFunc);
CertificateManager.TrustRoot = trustRootCertificate; CertificateManager.UserTrustRoot = trustRootCertificate;
CertificateManager.TrustRootAsAdministrator = trustRootCertificateAsAdmin; CertificateManager.MachineTrustRootAsAdministrator = trustRootCertificateAsAdmin;
if (rootCertificateName != null) if (rootCertificateName != null)
{ {
......
...@@ -67,16 +67,6 @@ namespace Titanium.Web.Proxy ...@@ -67,16 +67,6 @@ namespace Titanium.Web.Proxy
//filter out excluded host names //filter out excluded host names
bool excluded = false; bool excluded = false;
if (endPoint.ExcludedHttpsHostNameRegex != null)
{
excluded = endPoint.ExcludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname));
}
if (endPoint.IncludedHttpsHostNameRegex != null)
{
excluded = !endPoint.IncludedHttpsHostNameRegexList.Any(x => x.IsMatch(connectHostname));
}
if(endPoint.BeforeTunnelConnect!=null) if(endPoint.BeforeTunnelConnect!=null)
{ {
excluded = await endPoint.BeforeTunnelConnect(connectHostname); excluded = await endPoint.BeforeTunnelConnect(connectHostname);
......
namespace Titanium.Web.Proxy.Shared using System.Text.RegularExpressions;
namespace Titanium.Web.Proxy.Shared
{ {
/// <summary> /// <summary>
/// Literals shared by Proxy Server /// Literals shared by Proxy Server
...@@ -13,5 +15,7 @@ ...@@ -13,5 +15,7 @@
internal static readonly char[] EqualSplit = { '=' }; internal static readonly char[] EqualSplit = { '=' };
internal static readonly byte[] NewLine = {(byte)'\r', (byte)'\n' }; internal static readonly byte[] NewLine = {(byte)'\r', (byte)'\n' };
public static readonly Regex CNRemoverRegex = new Regex(@"^CN\s*=\s*", RegexOptions.IgnoreCase | RegexOptions.Compiled);
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment