Commit e27ba5bb authored by Honfika's avatar Honfika

#121: Allow to set the root dertificate, #149:

 do not create root certificate when it is not needed
parent 88593feb
using System;
using System.Collections.Generic;
using System.Net;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;
......@@ -9,10 +10,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
{
public class ProxyTestController
{
private ProxyServer proxyServer;
private readonly ProxyServer proxyServer;
//share requestBody outside handlers
private Dictionary<Guid, string> requestBodyHistory;
private readonly Dictionary<Guid, string> requestBodyHistory = new Dictionary<Guid, string>();
public ProxyTestController()
{
......@@ -21,9 +22,10 @@ namespace Titanium.Web.Proxy.Examples.Basic
//optionally set the Certificate Engine
//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()
......
......@@ -31,6 +31,10 @@ namespace Titanium.Web.Proxy.Network
/// </summary>
internal class CertificateManager : IDisposable
{
private const string DefaultRootCertificateIssuer = "Titanium";
private const string DefaultRootRootCertificateName = "Titanium Root Certificate Authority";
private readonly ICertificateMaker certEngine;
private bool clearCertificates { get; set; }
......@@ -66,8 +70,8 @@ namespace Titanium.Web.Proxy.Network
certEngine = new WinCertificateMaker();
}
Issuer = issuer;
RootCertificateName = rootCertificateName;
Issuer = issuer ?? DefaultRootCertificateIssuer;
RootCertificateName = rootCertificateName ?? DefaultRootRootCertificateName;
certificateCache = new ConcurrentDictionary<string, CachedCertificate>();
}
......@@ -88,7 +92,7 @@ namespace Titanium.Web.Proxy.Network
return fileName;
}
internal X509Certificate2 GetRootCertificate()
internal X509Certificate2 LoadRootCertificate()
{
var fileName = GetRootCertificatePath();
if (!File.Exists(fileName)) return null;
......@@ -108,11 +112,16 @@ namespace Titanium.Web.Proxy.Network
/// <returns>true if succeeded, else false</returns>
internal bool CreateTrustedRootCertificate()
{
RootCertificate = GetRootCertificate();
if (RootCertificate == null)
{
RootCertificate = LoadRootCertificate();
}
if (RootCertificate != null)
{
return true;
}
try
{
RootCertificate = CreateCertificate(RootCertificateName, true);
......@@ -121,6 +130,7 @@ namespace Titanium.Web.Proxy.Network
{
exceptionFunc(e);
}
if (RootCertificate != null)
{
try
......@@ -133,9 +143,23 @@ namespace Titanium.Web.Proxy.Network
exceptionFunc(e);
}
}
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>
/// Create an SSL certificate
/// </summary>
......@@ -144,15 +168,13 @@ namespace Titanium.Web.Proxy.Network
/// <returns></returns>
internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{
if (certificateCache.ContainsKey(certificateName))
{
var cached = certificateCache[certificateName];
cached.LastAccess = DateTime.Now;
return cached.Certificate;
}
X509Certificate2 certificate = null;
lock (string.Intern(certificateName))
{
......@@ -160,6 +182,11 @@ namespace Titanium.Web.Proxy.Network
{
try
{
if (!isRootCertificate && RootCertificate == null)
{
CreateTrustedRootCertificate();
}
certificate = certEngine.MakeCertificate(certificateName, isRootCertificate, RootCertificate);
}
catch (Exception e)
......@@ -168,7 +195,7 @@ namespace Titanium.Web.Proxy.Network
}
if (certificate != null && !certificateCache.ContainsKey(certificateName))
{
certificateCache.Add(certificateName, new CachedCertificate() { Certificate = certificate });
certificateCache.Add(certificateName, new CachedCertificate { Certificate = certificate });
}
}
else
......@@ -182,10 +209,7 @@ namespace Titanium.Web.Proxy.Network
}
}
return certificate;
}
/// <summary>
......
......@@ -19,11 +19,10 @@ namespace Titanium.Web.Proxy
/// </summary>
public partial class ProxyServer : IDisposable
{
/// <summary>
/// Is the root certificate used by this proxy is valid?
/// </summary>
private bool certValidated { get; set; }
private bool? certValidated { get; set; }
/// <summary>
/// Is the proxy currently running
......@@ -45,6 +44,8 @@ namespace Titanium.Web.Proxy
/// </summary>
private Action<Exception> exceptionFunc;
private bool trustRootCertificate;
/// <summary>
/// A object that creates tcp connection to server
/// </summary>
......@@ -68,25 +69,61 @@ namespace Titanium.Web.Proxy
/// </summary>
public int BUFFER_SIZE { get; set; } = 8192;
/// <summary>
/// The root certificate
/// </summary>
public X509Certificate2 RootCertificate
{
get { return certificateCacheManager.RootCertificate; }
set { certificateCacheManager.RootCertificate = value; }
}
/// <summary>
/// Name of the root certificate issuer
/// </summary>
public string RootCertificateIssuerName { get; set; }
public string RootCertificateIssuerName
{
get { return certificateCacheManager.Issuer; }
set
{
CreateCertificateCacheManager(certificateCacheManager.RootCertificateName, value);
certValidated = null;
}
}
/// <summary>
/// Name of the root certificate
/// 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 root certificate file should be named as "rootCert.pfx"
/// The root certificate file should be named as "rootCert.pfx"
/// </summary>
public string RootCertificateName { get; set; }
public string RootCertificateName
{
get { return certificateCacheManager.RootCertificateName; }
set
{
CreateCertificateCacheManager(value, certificateCacheManager.Issuer);
certValidated = null;
}
}
/// <summary>
/// Trust the RootCertificate used by this proxy server
/// 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
/// </summary>
public bool TrustRootCertificate { get; set; }
public bool TrustRootCertificate
{
get { return trustRootCertificate; }
set
{
trustRootCertificate = value;
if (value)
{
EnsureRootCertificate();
}
}
}
/// <summary>
/// Select Certificate Engine
......@@ -211,7 +248,9 @@ namespace Titanium.Web.Proxy
/// <summary>
/// Constructor
/// </summary>
public ProxyServer() : this(null, null) { }
public ProxyServer() : this(null, null)
{
}
/// <summary>
/// Constructor.
......@@ -220,9 +259,6 @@ namespace Titanium.Web.Proxy
/// <param name="rootCertificateIssuerName">Name of root certificate issuer.</param>
public ProxyServer(string rootCertificateName, string rootCertificateIssuerName)
{
RootCertificateName = rootCertificateName;
RootCertificateIssuerName = rootCertificateIssuerName;
//default values
ConnectionTimeOutSeconds = 120;
CertificateCacheTimeOutMinutes = 60;
......@@ -233,8 +269,8 @@ namespace Titanium.Web.Proxy
#if !DEBUG
new FireFoxProxySettingsManager();
#endif
RootCertificateName = RootCertificateName ?? "Titanium Root Certificate Authority";
RootCertificateIssuerName = RootCertificateIssuerName ?? "Titanium";
CreateCertificateCacheManager(rootCertificateName, rootCertificateIssuerName);
}
/// <summary>
......@@ -328,8 +364,10 @@ namespace Titanium.Web.Proxy
.ToList()
.ForEach(x => x.IsSystemHttpsProxy = false);
EnsureRootCertificate();
//If certificate was trusted by the machine
if (certValidated)
if (certValidated == true)
{
systemProxySettingsManager.SetHttpsProxy(
Equals(endPoint.IpAddress, IPAddress.Any) |
......@@ -397,23 +435,6 @@ namespace Titanium.Web.Proxy
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
&& GetCustomUpStreamHttpsProxyFunc == null)
{
......@@ -431,6 +452,25 @@ namespace Titanium.Web.Proxy
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>
/// Gets the system up stream proxy.
/// </summary>
......
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