Commit a0a6e678 authored by Jehonathan Thomas's avatar Jehonathan Thomas Committed by GitHub

Merge pull request #212 from honfika/develop

Root certificate improvements (issue #121 and #149)
parents d386c2d1 9ee26d14
...@@ -5,7 +5,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -5,7 +5,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
public class Program public class Program
{ {
private static readonly ProxyTestController Controller = new ProxyTestController(); private static readonly ProxyTestController controller = new ProxyTestController();
public static void Main(string[] args) public static void Main(string[] args)
{ {
...@@ -15,13 +15,13 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -15,13 +15,13 @@ namespace Titanium.Web.Proxy.Examples.Basic
//Start proxy controller //Start proxy controller
Controller.StartProxy(); controller.StartProxy();
Console.WriteLine("Hit any key to exit.."); Console.WriteLine("Hit any key to exit..");
Console.WriteLine(); Console.WriteLine();
Console.Read(); Console.Read();
Controller.Stop(); controller.Stop();
} }
...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -30,7 +30,7 @@ namespace Titanium.Web.Proxy.Examples.Basic
if (eventType != 2) return false; if (eventType != 2) return false;
try try
{ {
Controller.Stop(); controller.Stop();
} }
catch catch
{ {
......
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Net; using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks; using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
...@@ -9,10 +10,10 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -9,10 +10,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
{ {
public class ProxyTestController public class ProxyTestController
{ {
private ProxyServer proxyServer; private readonly ProxyServer proxyServer;
//share requestBody outside handlers //share requestBody outside handlers
private Dictionary<Guid, string> requestBodyHistory; private readonly Dictionary<Guid, string> requestBodyHistory = new Dictionary<Guid, string>();
public ProxyTestController() public ProxyTestController()
{ {
...@@ -21,9 +22,10 @@ namespace Titanium.Web.Proxy.Examples.Basic ...@@ -21,9 +22,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
//optionally set the Certificate Engine //optionally set the Certificate Engine
//Under Mono only BouncyCastle will be supported //Under Mono only BouncyCastle will be supported
//proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle; //proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle;
requestBodyHistory = new Dictionary<Guid, string>(); //optionally set the Root Certificate
//proxyServer.RootCertificate = new X509Certificate2("myCert.pfx", string.Empty, X509KeyStorageFlags.Exportable);
} }
public void StartProxy() public void StartProxy()
......
using System.Linq; using System.Linq;
using System.Net; using System.Net;
using System.Net.Sockets; using System.Net.Sockets;
namespace Titanium.Web.Proxy.Helpers namespace Titanium.Web.Proxy.Helpers
{ {
internal class NetworkHelper internal class NetworkHelper
{ {
private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion) private static int FindProcessIdFromLocalPort(int port, IpVersion ipVersion)
{ {
var tcpRow = TcpHelper.GetExtendedTcpTable(ipVersion).FirstOrDefault( var tcpRow = TcpHelper.GetExtendedTcpTable(ipVersion).FirstOrDefault(
row => row.LocalEndPoint.Port == port); row => row.LocalEndPoint.Port == port);
return tcpRow?.ProcessId ?? 0; return tcpRow?.ProcessId ?? 0;
} }
internal static int GetProcessIdFromPort(int port, bool ipV6Enabled) internal static int GetProcessIdFromPort(int port, bool ipV6Enabled)
{ {
var processId = FindProcessIdFromLocalPort(port, IpVersion.Ipv4); var processId = FindProcessIdFromLocalPort(port, IpVersion.Ipv4);
if (processId > 0 && !ipV6Enabled) if (processId > 0 && !ipV6Enabled)
{ {
return processId; return processId;
} }
return FindProcessIdFromLocalPort(port, IpVersion.Ipv6); return FindProcessIdFromLocalPort(port, IpVersion.Ipv6);
} }
/// <summary> /// <summary>
/// Adapated from below link /// Adapated from below link
/// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost /// http://stackoverflow.com/questions/11834091/how-to-check-if-localhost
/// </summary> /// </summary>
/// <param name="address"></param> /// <param name="address"></param>
/// <returns></returns> /// <returns></returns>
internal static bool IsLocalIpAddress(IPAddress address) internal static bool IsLocalIpAddress(IPAddress address)
{ {
// get local IP addresses // get local IP addresses
...@@ -40,8 +40,8 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -40,8 +40,8 @@ namespace Titanium.Web.Proxy.Helpers
return IPAddress.IsLoopback(address) || localIPs.Contains(address); return IPAddress.IsLoopback(address) || localIPs.Contains(address);
} }
internal static bool IsLocalIpAddress(string hostName) internal static bool IsLocalIpAddress(string hostName)
{ {
bool isLocalhost = false; bool isLocalhost = false;
IPHostEntry localhost = Dns.GetHostEntry("127.0.0.1"); IPHostEntry localhost = Dns.GetHostEntry("127.0.0.1");
...@@ -74,7 +74,7 @@ namespace Titanium.Web.Proxy.Helpers ...@@ -74,7 +74,7 @@ namespace Titanium.Web.Proxy.Helpers
} }
} }
return isLocalhost; return isLocalhost;
} }
} }
} }
...@@ -53,8 +53,8 @@ namespace Titanium.Web.Proxy.Models ...@@ -53,8 +53,8 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class ExplicitProxyEndPoint : ProxyEndPoint public class ExplicitProxyEndPoint : ProxyEndPoint
{ {
private List<string> _excludedHttpsHostNameRegex; private List<string> excludedHttpsHostNameRegex;
private List<string> _includedHttpsHostNameRegex; private List<string> includedHttpsHostNameRegex;
internal bool IsSystemHttpProxy { get; set; } internal bool IsSystemHttpProxy { get; set; }
...@@ -65,7 +65,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -65,7 +65,7 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public List<string> ExcludedHttpsHostNameRegex public List<string> ExcludedHttpsHostNameRegex
{ {
get { return _excludedHttpsHostNameRegex; } get { return excludedHttpsHostNameRegex; }
set set
{ {
if (IncludedHttpsHostNameRegex != null) if (IncludedHttpsHostNameRegex != null)
...@@ -73,7 +73,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -73,7 +73,7 @@ namespace Titanium.Web.Proxy.Models
throw new ArgumentException("Cannot set excluded when included is set"); throw new ArgumentException("Cannot set excluded when included is set");
} }
_excludedHttpsHostNameRegex = value; excludedHttpsHostNameRegex = value;
} }
} }
...@@ -82,7 +82,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -82,7 +82,7 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public List<string> IncludedHttpsHostNameRegex public List<string> IncludedHttpsHostNameRegex
{ {
get { return _includedHttpsHostNameRegex; } get { return includedHttpsHostNameRegex; }
set set
{ {
if (ExcludedHttpsHostNameRegex != null) if (ExcludedHttpsHostNameRegex != null)
...@@ -90,7 +90,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -90,7 +90,7 @@ namespace Titanium.Web.Proxy.Models
throw new ArgumentException("Cannot set included when excluded is set"); throw new ArgumentException("Cannot set included when excluded is set");
} }
_includedHttpsHostNameRegex = value; includedHttpsHostNameRegex = value;
} }
} }
......
...@@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Models ...@@ -8,7 +8,7 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public class ExternalProxy public class ExternalProxy
{ {
private static readonly Lazy<NetworkCredential> DefaultCredentials = new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials); private static readonly Lazy<NetworkCredential> defaultCredentials = new Lazy<NetworkCredential>(() => CredentialCache.DefaultNetworkCredentials);
private string userName; private string userName;
private string password; private string password;
...@@ -27,12 +27,12 @@ namespace Titanium.Web.Proxy.Models ...@@ -27,12 +27,12 @@ namespace Titanium.Web.Proxy.Models
/// Username. /// Username.
/// </summary> /// </summary>
public string UserName { public string UserName {
get { return UseDefaultCredentials ? DefaultCredentials.Value.UserName : userName; } get { return UseDefaultCredentials ? defaultCredentials.Value.UserName : userName; }
set set
{ {
userName = value; userName = value;
if (DefaultCredentials.Value.UserName != userName) if (defaultCredentials.Value.UserName != userName)
{ {
UseDefaultCredentials = false; UseDefaultCredentials = false;
} }
...@@ -44,12 +44,12 @@ namespace Titanium.Web.Proxy.Models ...@@ -44,12 +44,12 @@ namespace Titanium.Web.Proxy.Models
/// </summary> /// </summary>
public string Password public string Password
{ {
get { return UseDefaultCredentials ? DefaultCredentials.Value.Password : password; } get { return UseDefaultCredentials ? defaultCredentials.Value.Password : password; }
set set
{ {
password = value; password = value;
if (DefaultCredentials.Value.Password != password) if (defaultCredentials.Value.Password != password)
{ {
UseDefaultCredentials = false; UseDefaultCredentials = false;
} }
......
...@@ -42,7 +42,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -42,7 +42,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
private readonly string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0"; private readonly string sProviderName = "Microsoft Enhanced Cryptographic Provider v1.0";
private object _SharedPrivateKey; private object sharedPrivateKey;
/// <summary> /// <summary>
/// Constructor. /// Constructor.
...@@ -105,7 +105,7 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -105,7 +105,7 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (!isRoot) if (!isRoot)
{ {
sharedPrivateKey = _SharedPrivateKey; sharedPrivateKey = this.sharedPrivateKey;
} }
if (sharedPrivateKey == null) if (sharedPrivateKey == null)
...@@ -130,52 +130,52 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -130,52 +130,52 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (!isRoot) if (!isRoot)
{ {
_SharedPrivateKey = sharedPrivateKey; this.sharedPrivateKey = sharedPrivateKey;
} }
} }
typeValue = new object[1]; typeValue = new object[1];
var OID = Activator.CreateInstance(typeOID); var oid = Activator.CreateInstance(typeOID);
typeValue[0] = "1.3.6.1.5.5.7.3.1"; typeValue[0] = "1.3.6.1.5.5.7.3.1";
typeOID.InvokeMember("InitializeFromValue", BindingFlags.InvokeMethod, null, OID, typeValue); typeOID.InvokeMember("InitializeFromValue", BindingFlags.InvokeMethod, null, oid, typeValue);
var OIDS = Activator.CreateInstance(typeOIDS); var oids = Activator.CreateInstance(typeOIDS);
typeValue[0] = OID; typeValue[0] = oid;
typeOIDS.InvokeMember("Add", BindingFlags.InvokeMethod, null, OIDS, typeValue); typeOIDS.InvokeMember("Add", BindingFlags.InvokeMethod, null, oids, typeValue);
var EKUExt = Activator.CreateInstance(typeEKUExt); var ekuExt = Activator.CreateInstance(typeEKUExt);
typeValue[0] = OIDS; typeValue[0] = oids;
typeEKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, EKUExt, typeValue); typeEKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, ekuExt, typeValue);
var RequestCert = Activator.CreateInstance(typeRequestCert); var requestCert = Activator.CreateInstance(typeRequestCert);
typeValue = new[] { 1, sharedPrivateKey, string.Empty }; typeValue = new[] { 1, sharedPrivateKey, string.Empty };
typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, RequestCert, typeValue); typeRequestCert.InvokeMember("InitializeFromPrivateKey", BindingFlags.InvokeMethod, null, requestCert, typeValue);
typeValue = new[] { x500CertDN }; typeValue = new[] { x500CertDN };
typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, RequestCert, typeValue); typeRequestCert.InvokeMember("Subject", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeValue[0] = x500RootCertDN; typeValue[0] = x500RootCertDN;
typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, RequestCert, typeValue); typeRequestCert.InvokeMember("Issuer", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeValue[0] = validFrom; typeValue[0] = validFrom;
typeRequestCert.InvokeMember("NotBefore", BindingFlags.PutDispProperty, null, RequestCert, typeValue); typeRequestCert.InvokeMember("NotBefore", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeValue[0] = validTo; typeValue[0] = validTo;
typeRequestCert.InvokeMember("NotAfter", BindingFlags.PutDispProperty, null, RequestCert, typeValue); typeRequestCert.InvokeMember("NotAfter", BindingFlags.PutDispProperty, null, requestCert, typeValue);
var KUExt = Activator.CreateInstance(typeKUExt); var kuExt = Activator.CreateInstance(typeKUExt);
typeValue[0] = 176; typeValue[0] = 176;
typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, KUExt, typeValue); typeKUExt.InvokeMember("InitializeEncode", BindingFlags.InvokeMethod, null, kuExt, typeValue);
var certificate = typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, RequestCert, null); var certificate = typeRequestCert.InvokeMember("X509Extensions", BindingFlags.GetProperty, null, requestCert, null);
typeValue = new object[1]; typeValue = new object[1];
if (!isRoot) if (!isRoot)
{ {
typeValue[0] = KUExt; typeValue[0] = kuExt;
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
} }
typeValue[0] = EKUExt; typeValue[0] = ekuExt;
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
if (!isRoot) if (!isRoot)
...@@ -203,12 +203,12 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -203,12 +203,12 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (!isRoot) if (!isRoot)
{ {
var SignerCertificate = Activator.CreateInstance(typeSignerCertificate); var signerCertificate = Activator.CreateInstance(typeSignerCertificate);
typeValue = new object[] { 0, 0, 12, signingCertificate.Thumbprint }; typeValue = new object[] { 0, 0, 12, signingCertificate.Thumbprint };
typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, SignerCertificate, typeValue); typeSignerCertificate.InvokeMember("Initialize", BindingFlags.InvokeMethod, null, signerCertificate, typeValue);
typeValue = new[] { SignerCertificate }; typeValue = new[] { signerCertificate };
typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, RequestCert, typeValue); typeRequestCert.InvokeMember("SignerCertificate", BindingFlags.PutDispProperty, null, requestCert, typeValue);
} }
else else
{ {
...@@ -220,24 +220,24 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -220,24 +220,24 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue); typeX509Extensions.InvokeMember("Add", BindingFlags.InvokeMethod, null, certificate, typeValue);
} }
OID = Activator.CreateInstance(typeOID); oid = Activator.CreateInstance(typeOID);
typeValue = new object[] { 1, 0, 0, hashAlg }; typeValue = new object[] { 1, 0, 0, hashAlg };
typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, OID, typeValue); typeOID.InvokeMember("InitializeFromAlgorithmName", BindingFlags.InvokeMethod, null, oid, typeValue);
typeValue = new[] { OID }; typeValue = new[] { oid };
typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, RequestCert, typeValue); typeRequestCert.InvokeMember("HashAlgorithm", BindingFlags.PutDispProperty, null, requestCert, typeValue);
typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, RequestCert, null); typeRequestCert.InvokeMember("Encode", BindingFlags.InvokeMethod, null, requestCert, null);
var X509Enrollment = Activator.CreateInstance(typeX509Enrollment); var x509Enrollment = Activator.CreateInstance(typeX509Enrollment);
typeValue[0] = RequestCert; typeValue[0] = requestCert;
typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, X509Enrollment, typeValue); typeX509Enrollment.InvokeMember("InitializeFromRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
if (isRoot) if (isRoot)
{ {
typeValue[0] = fullSubject; typeValue[0] = fullSubject;
typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, X509Enrollment, typeValue); typeX509Enrollment.InvokeMember("CertificateFriendlyName", BindingFlags.PutDispProperty, null, x509Enrollment, typeValue);
} }
...@@ -245,15 +245,15 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -245,15 +245,15 @@ namespace Titanium.Web.Proxy.Network.Certificate
typeValue[0] = 0; typeValue[0] = 0;
var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, X509Enrollment, typeValue); var createCertRequest = typeX509Enrollment.InvokeMember("CreateRequest", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
typeValue = new[] { 2, createCertRequest, 0, string.Empty }; typeValue = new[] { 2, createCertRequest, 0, string.Empty };
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 try
{ {
var empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, X509Enrollment, typeValue); var empty = (string)typeX509Enrollment.InvokeMember("CreatePFX", BindingFlags.InvokeMethod, null, x509Enrollment, typeValue);
return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable); return new X509Certificate2(Convert.FromBase64String(empty), string.Empty, X509KeyStorageFlags.Exportable);
} }
catch (Exception) catch (Exception)
......
...@@ -31,6 +31,10 @@ namespace Titanium.Web.Proxy.Network ...@@ -31,6 +31,10 @@ namespace Titanium.Web.Proxy.Network
/// </summary> /// </summary>
internal class CertificateManager : IDisposable internal class CertificateManager : IDisposable
{ {
private const string DefaultRootCertificateIssuer = "Titanium";
private const string DefaultRootRootCertificateName = "Titanium Root Certificate Authority";
private readonly ICertificateMaker certEngine; private readonly ICertificateMaker certEngine;
private bool clearCertificates { get; set; } private bool clearCertificates { get; set; }
...@@ -66,8 +70,8 @@ namespace Titanium.Web.Proxy.Network ...@@ -66,8 +70,8 @@ namespace Titanium.Web.Proxy.Network
certEngine = new WinCertificateMaker(); certEngine = new WinCertificateMaker();
} }
Issuer = issuer; Issuer = issuer ?? DefaultRootCertificateIssuer;
RootCertificateName = rootCertificateName; RootCertificateName = rootCertificateName ?? DefaultRootRootCertificateName;
certificateCache = new ConcurrentDictionary<string, CachedCertificate>(); certificateCache = new ConcurrentDictionary<string, CachedCertificate>();
} }
...@@ -88,7 +92,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -88,7 +92,7 @@ namespace Titanium.Web.Proxy.Network
return fileName; return fileName;
} }
internal X509Certificate2 GetRootCertificate() internal X509Certificate2 LoadRootCertificate()
{ {
var fileName = GetRootCertificatePath(); var fileName = GetRootCertificatePath();
if (!File.Exists(fileName)) return null; if (!File.Exists(fileName)) return null;
...@@ -108,11 +112,16 @@ namespace Titanium.Web.Proxy.Network ...@@ -108,11 +112,16 @@ namespace Titanium.Web.Proxy.Network
/// <returns>true if succeeded, else false</returns> /// <returns>true if succeeded, else false</returns>
internal bool CreateTrustedRootCertificate() internal bool CreateTrustedRootCertificate()
{ {
RootCertificate = GetRootCertificate(); if (RootCertificate == null)
{
RootCertificate = LoadRootCertificate();
}
if (RootCertificate != null) if (RootCertificate != null)
{ {
return true; return true;
} }
try try
{ {
RootCertificate = CreateCertificate(RootCertificateName, true); RootCertificate = CreateCertificate(RootCertificateName, true);
...@@ -121,6 +130,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -121,6 +130,7 @@ namespace Titanium.Web.Proxy.Network
{ {
exceptionFunc(e); exceptionFunc(e);
} }
if (RootCertificate != null) if (RootCertificate != null)
{ {
try try
...@@ -133,9 +143,23 @@ namespace Titanium.Web.Proxy.Network ...@@ -133,9 +143,23 @@ namespace Titanium.Web.Proxy.Network
exceptionFunc(e); exceptionFunc(e);
} }
} }
return RootCertificate != null; return RootCertificate != null;
} }
/// <summary>
/// Trusts the root certificate.
/// </summary>
/// <param name="exceptionFunc"></param>
internal void TrustRootCertificate(Action<Exception> exceptionFunc)
{
//current user
TrustRootCertificate(StoreLocation.CurrentUser, exceptionFunc);
//current system
TrustRootCertificate(StoreLocation.LocalMachine, exceptionFunc);
}
/// <summary> /// <summary>
/// Create an SSL certificate /// Create an SSL certificate
/// </summary> /// </summary>
...@@ -144,15 +168,13 @@ namespace Titanium.Web.Proxy.Network ...@@ -144,15 +168,13 @@ namespace Titanium.Web.Proxy.Network
/// <returns></returns> /// <returns></returns>
internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate) internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{ {
if (certificateCache.ContainsKey(certificateName)) if (certificateCache.ContainsKey(certificateName))
{ {
var cached = certificateCache[certificateName]; var cached = certificateCache[certificateName];
cached.LastAccess = DateTime.Now; cached.LastAccess = DateTime.Now;
return cached.Certificate; return cached.Certificate;
} }
X509Certificate2 certificate = null; X509Certificate2 certificate = null;
lock (string.Intern(certificateName)) lock (string.Intern(certificateName))
{ {
...@@ -160,6 +182,11 @@ namespace Titanium.Web.Proxy.Network ...@@ -160,6 +182,11 @@ namespace Titanium.Web.Proxy.Network
{ {
try try
{ {
if (!isRootCertificate && RootCertificate == null)
{
CreateTrustedRootCertificate();
}
certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, RootCertificate); certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, RootCertificate);
} }
catch (Exception e) catch (Exception e)
...@@ -168,7 +195,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -168,7 +195,7 @@ namespace Titanium.Web.Proxy.Network
} }
if (certificate != null && !certificateCache.ContainsKey(certificateName)) if (certificate != null && !certificateCache.ContainsKey(certificateName))
{ {
certificateCache.Add(certificateName, new CachedCertificate() { Certificate = certificate }); certificateCache.Add(certificateName, new CachedCertificate { Certificate = certificate });
} }
} }
else else
...@@ -182,10 +209,7 @@ namespace Titanium.Web.Proxy.Network ...@@ -182,10 +209,7 @@ namespace Titanium.Web.Proxy.Network
} }
} }
return certificate; return certificate;
} }
/// <summary> /// <summary>
......
...@@ -19,11 +19,10 @@ namespace Titanium.Web.Proxy ...@@ -19,11 +19,10 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
public partial class ProxyServer : IDisposable public partial class ProxyServer : IDisposable
{ {
/// <summary> /// <summary>
/// Is the root certificate used by this proxy is valid? /// Is the root certificate used by this proxy is valid?
/// </summary> /// </summary>
private bool certValidated { get; set; } private bool? certValidated { get; set; }
/// <summary> /// <summary>
/// Is the proxy currently running /// Is the proxy currently running
...@@ -45,6 +44,8 @@ namespace Titanium.Web.Proxy ...@@ -45,6 +44,8 @@ namespace Titanium.Web.Proxy
/// </summary> /// </summary>
private Action<Exception> exceptionFunc; private Action<Exception> exceptionFunc;
private bool trustRootCertificate;
/// <summary> /// <summary>
/// A object that creates tcp connection to server /// A object that creates tcp connection to server
/// </summary> /// </summary>
...@@ -66,27 +67,63 @@ namespace Titanium.Web.Proxy ...@@ -66,27 +67,63 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Buffer size used throughout this proxy /// Buffer size used throughout this proxy
/// </summary> /// </summary>
public int BUFFER_SIZE { get; set; } = 8192; public int BufferSize { get; set; } = 8192;
/// <summary>
/// The root certificate
/// </summary>
public X509Certificate2 RootCertificate
{
get { return certificateCacheManager.RootCertificate; }
set { certificateCacheManager.RootCertificate = value; }
}
/// <summary> /// <summary>
/// Name of the root certificate issuer /// Name of the root certificate issuer
/// </summary> /// </summary>
public string RootCertificateIssuerName { get; set; } public string RootCertificateIssuerName
{
get { return certificateCacheManager.Issuer; }
set
{
CreateCertificateCacheManager(certificateCacheManager.RootCertificateName, value);
certValidated = null;
}
}
/// <summary> /// <summary>
/// Name of the root certificate /// Name of the root certificate
/// If no certificate is provided then a default Root Certificate will be created and used /// If no certificate is provided then a default Root Certificate will be created and used
/// The provided root certificate has to be in the proxy exe directory with the private key /// The provided root certificate has to be in the proxy exe directory with the private key
/// The root certificate file should be named as "rootCert.pfx" /// The root certificate file should be named as "rootCert.pfx"
/// </summary> /// </summary>
public string RootCertificateName { get; set; } public string RootCertificateName
{
get { return certificateCacheManager.RootCertificateName; }
set
{
CreateCertificateCacheManager(value, certificateCacheManager.Issuer);
certValidated = null;
}
}
/// <summary> /// <summary>
/// Trust the RootCertificate used by this proxy server /// Trust the RootCertificate used by this proxy server
/// 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>
public bool TrustRootCertificate { get; set; } public bool TrustRootCertificate
{
get { return trustRootCertificate; }
set
{
trustRootCertificate = value;
if (value)
{
EnsureRootCertificate();
}
}
}
/// <summary> /// <summary>
/// Select Certificate Engine /// Select Certificate Engine
...@@ -211,7 +248,9 @@ namespace Titanium.Web.Proxy ...@@ -211,7 +248,9 @@ namespace Titanium.Web.Proxy
/// <summary> /// <summary>
/// Constructor /// Constructor
/// </summary> /// </summary>
public ProxyServer() : this(null, null) { } public ProxyServer() : this(null, null)
{
}
/// <summary> /// <summary>
/// Constructor. /// Constructor.
...@@ -220,9 +259,6 @@ namespace Titanium.Web.Proxy ...@@ -220,9 +259,6 @@ namespace Titanium.Web.Proxy
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param> /// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName) public ProxyServer(string rootCertificateName, string rootCertificateIssuerName)
{ {
RootCertificateName = rootCertificateName;
RootCertificateIssuerName = rootCertificateIssuerName;
//default values //default values
ConnectionTimeOutSeconds = 120; ConnectionTimeOutSeconds = 120;
CertificateCacheTimeOutMinutes = 60; CertificateCacheTimeOutMinutes = 60;
...@@ -233,8 +269,8 @@ namespace Titanium.Web.Proxy ...@@ -233,8 +269,8 @@ namespace Titanium.Web.Proxy
#if !DEBUG #if !DEBUG
new FireFoxProxySettingsManager(); new FireFoxProxySettingsManager();
#endif #endif
RootCertificateName = RootCertificateName ?? "Titanium Root Certificate Authority";
RootCertificateIssuerName = RootCertificateIssuerName ?? "Titanium"; CreateCertificateCacheManager(rootCertificateName, rootCertificateIssuerName);
} }
/// <summary> /// <summary>
...@@ -328,8 +364,10 @@ namespace Titanium.Web.Proxy ...@@ -328,8 +364,10 @@ namespace Titanium.Web.Proxy
.ToList() .ToList()
.ForEach(x => x.IsSystemHttpsProxy = false); .ForEach(x => x.IsSystemHttpsProxy = false);
EnsureRootCertificate();
//If certificate was trusted by the machine //If certificate was trusted by the machine
if (certValidated) if (certValidated == true)
{ {
systemProxySettingsManager.SetHttpsProxy( systemProxySettingsManager.SetHttpsProxy(
Equals(endPoint.IpAddress, IPAddress.Any) | Equals(endPoint.IpAddress, IPAddress.Any) |
...@@ -397,23 +435,6 @@ namespace Titanium.Web.Proxy ...@@ -397,23 +435,6 @@ namespace Titanium.Web.Proxy
throw new Exception("Proxy is already running."); throw new Exception("Proxy is already running.");
} }
certificateCacheManager = new CertificateManager(CertificateEngine,
RootCertificateIssuerName,
RootCertificateName, ExceptionFunc);
certValidated = certificateCacheManager.CreateTrustedRootCertificate();
if (TrustRootCertificate)
{
//current user
certificateCacheManager
.TrustRootCertificate(StoreLocation.CurrentUser, ExceptionFunc);
//current system
certificateCacheManager
.TrustRootCertificate(StoreLocation.LocalMachine, ExceptionFunc);
}
if (ForwardToUpstreamGateway && GetCustomUpStreamHttpProxyFunc == null if (ForwardToUpstreamGateway && GetCustomUpStreamHttpProxyFunc == null
&& GetCustomUpStreamHttpsProxyFunc == null) && GetCustomUpStreamHttpsProxyFunc == null)
{ {
...@@ -431,6 +452,25 @@ namespace Titanium.Web.Proxy ...@@ -431,6 +452,25 @@ namespace Titanium.Web.Proxy
proxyRunning = true; proxyRunning = true;
} }
private void CreateCertificateCacheManager(string rootCertificateName, string rootCertificateIssuerName)
{
certificateCacheManager = new CertificateManager(CertificateEngine,
rootCertificateIssuerName, rootCertificateName, ExceptionFunc);
}
private void EnsureRootCertificate()
{
if (!certValidated.HasValue)
{
certValidated = certificateCacheManager.CreateTrustedRootCertificate();
if (TrustRootCertificate)
{
certificateCacheManager.TrustRootCertificate(ExceptionFunc);
}
}
}
/// <summary> /// <summary>
/// Gets the system up stream proxy. /// Gets the system up stream proxy.
/// </summary> /// </summary>
......
...@@ -147,7 +147,7 @@ namespace Titanium.Web.Proxy ...@@ -147,7 +147,7 @@ namespace Titanium.Web.Proxy
//write back successfull CONNECT response //write back successfull CONNECT response
await WriteConnectResponse(clientStreamWriter, version); await WriteConnectResponse(clientStreamWriter, version);
await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
null, version, null, null, version, null,
false, SupportedSslProtocols, false, SupportedSslProtocols,
ValidateServerCertificate, ValidateServerCertificate,
...@@ -245,7 +245,7 @@ namespace Titanium.Web.Proxy ...@@ -245,7 +245,7 @@ namespace Titanium.Web.Proxy
args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy; args.CustomUpStreamHttpProxyUsed = customUpStreamHttpProxy;
args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy; args.CustomUpStreamHttpsProxyUsed = customUpStreamHttpsProxy;
connection = await tcpConnectionFactory.CreateClient(BUFFER_SIZE, ConnectionTimeOutSeconds, connection = await tcpConnectionFactory.CreateClient(BufferSize, ConnectionTimeOutSeconds,
args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion, args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.WebSession.Request.HttpVersion,
args.IsHttps, SupportedSslProtocols, args.IsHttps, SupportedSslProtocols,
ValidateServerCertificate, ValidateServerCertificate,
...@@ -378,7 +378,7 @@ namespace Titanium.Web.Proxy ...@@ -378,7 +378,7 @@ namespace Titanium.Web.Proxy
} }
var args = var args =
new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse) new SessionEventArgs(BufferSize, HandleHttpSessionResponse)
{ {
ProxyClient = {TcpClient = client}, ProxyClient = {TcpClient = client},
WebSession = {ConnectHeaders = connectHeaders} WebSession = {ConnectHeaders = connectHeaders}
...@@ -487,7 +487,7 @@ namespace Titanium.Web.Proxy ...@@ -487,7 +487,7 @@ namespace Titanium.Web.Proxy
//if upgrading to websocket then relay the requet without reading the contents //if upgrading to websocket then relay the requet without reading the contents
if (args.WebSession.Request.UpgradeToWebSocket) if (args.WebSession.Request.UpgradeToWebSocket)
{ {
await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port, await TcpHelper.SendRaw(BufferSize, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps, httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps,
SupportedSslProtocols, ValidateServerCertificate, SupportedSslProtocols, ValidateServerCertificate,
SelectClientCertificate, SelectClientCertificate,
...@@ -581,14 +581,14 @@ namespace Titanium.Web.Proxy ...@@ -581,14 +581,14 @@ namespace Titanium.Web.Proxy
//send the request body bytes to server //send the request body bytes to server
if (args.WebSession.Request.ContentLength > 0) if (args.WebSession.Request.ContentLength > 0)
{ {
await args.ProxyClient.ClientStreamReader.CopyBytesToStream(BUFFER_SIZE, postStream, args.WebSession.Request.ContentLength); await args.ProxyClient.ClientStreamReader.CopyBytesToStream(BufferSize, postStream, args.WebSession.Request.ContentLength);
} }
//Need to revist, find any potential bugs //Need to revist, find any potential bugs
//send the request body bytes to server in chunks //send the request body bytes to server in chunks
else if (args.WebSession.Request.IsChunked) else if (args.WebSession.Request.IsChunked)
{ {
await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(BUFFER_SIZE, postStream); await args.ProxyClient.ClientStreamReader.CopyBytesToStreamChunked(BufferSize, postStream);
} }
} }
} }
......
...@@ -108,7 +108,7 @@ namespace Titanium.Web.Proxy ...@@ -108,7 +108,7 @@ namespace Titanium.Web.Proxy
|| !args.WebSession.Response.ResponseKeepAlive) || !args.WebSession.Response.ResponseKeepAlive)
{ {
await args.WebSession.ServerConnection.StreamReader await args.WebSession.ServerConnection.StreamReader
.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, .WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked,
args.WebSession.Response.ContentLength); args.WebSession.Response.ContentLength);
} }
//write response if connection:keep-alive header exist and when version is http/1.0 //write response if connection:keep-alive header exist and when version is http/1.0
...@@ -116,7 +116,7 @@ namespace Titanium.Web.Proxy ...@@ -116,7 +116,7 @@ namespace Titanium.Web.Proxy
else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0) else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0)
{ {
await args.WebSession.ServerConnection.StreamReader await args.WebSession.ServerConnection.StreamReader
.WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, .WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked,
args.WebSession.Response.ContentLength); args.WebSession.Response.ContentLength);
} }
} }
......
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