diff --git a/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html b/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html
index 7e3573e19..3a80595f6 100644
--- a/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html
+++ b/docs/api/Titanium.Web.Proxy.Network.CertificateManager.html
@@ -132,7 +132,7 @@
Properties
Improve this Doc
- View Source
+ View Source
CertificateCacheTimeOutMinutes
@@ -163,7 +163,7 @@ Property Value
Improve this Doc
- View Source
+ View Source
CertificateEngine
@@ -191,17 +191,50 @@ Property Value
+
+ |
+ Improve this Doc
+
+
+ View Source
+
+
+ CertificateStorage
+ The fake certificate cache storage.
+The default cache storage implementation saves certificates in folder "crts" (will be created in proxy dll directory).
+Implement ICertificateCache interface and assign concrete class here to customize.
+
+
+ Declaration
+
+
public ICertificateCache CertificateStorage { get; set; }
+
+ Property Value
+
+
+
+ | Type |
+ Description |
+
+
+
+
+ | ICertificateCache |
+ |
+
+
+
|
Improve this Doc
- View Source
+ View Source
OverwritePfxFile
Overwrite Root certificate file.
-
true : replace an existing .pfx file if password is incorect or if RootCertificate = null.
+
true : replace an existing .pfx file if password is incorrect or if RootCertificate = null.
Declaration
@@ -228,7 +261,7 @@ Property Value
Improve this Doc
- View Source
+ View Source
PfxFilePath
@@ -263,7 +296,7 @@ Property Value
Improve this Doc
- View Source
+ View Source
PfxPassword
@@ -295,7 +328,7 @@ Property Value
Improve this Doc
- View Source
+ View Source
RootCertificate
@@ -326,7 +359,7 @@ Property Value
Improve this Doc
- View Source
+ View Source
RootCertificateIssuerName
@@ -358,7 +391,7 @@ Property Value
Improve this Doc
- View Source
+ View Source
RootCertificateName
@@ -393,11 +426,11 @@ Property Value
Improve this Doc
- View Source
+ View Source
SaveFakeCertificates
- Save all fake certificates in folder "crts" (will be created in proxy dll directory).
+
Save all fake certificates using CertificateStorage.
for can load the certificate and not make new certificate every time.
@@ -425,7 +458,7 @@
Property Value
Improve this Doc
- View Source
+ View Source
StorageFlag
@@ -458,7 +491,7 @@
Methods
Improve this Doc
- View Source
+ View Source
ClearRootCertificate()
@@ -474,7 +507,7 @@ Declaration
Improve this Doc
- View Source
+ View Source
CreateRootCertificate(Boolean)
@@ -524,7 +557,7 @@ Returns
Improve this Doc
- View Source
+ View Source
Dispose()
@@ -540,7 +573,7 @@ Declaration
Improve this Doc
- View Source
+ View Source
EnsureRootCertificate()
@@ -557,7 +590,7 @@ Declaration
Improve this Doc
- View Source
+ View Source
EnsureRootCertificate(Boolean, Boolean, Boolean)
@@ -607,7 +640,7 @@ Parameters
Improve this Doc
- View Source
+ View Source
IsRootCertificateMachineTrusted()
@@ -638,7 +671,7 @@ Returns
Improve this Doc
- View Source
+ View Source
IsRootCertificateUserTrusted()
@@ -669,7 +702,7 @@ Returns
Improve this Doc
- View Source
+ View Source
LoadRootCertificate()
@@ -700,7 +733,7 @@ Returns
Improve this Doc
- View Source
+ View Source
LoadRootCertificate(String, String, Boolean, X509KeyStorageFlags)
@@ -737,7 +770,7 @@ Parameters
| Boolean |
overwritePfXFile |
- true : replace an existing .pfx file if password is incorect or if
+ | true : replace an existing .pfx file if password is incorrect or if
RootCertificate==null.
|
@@ -769,7 +802,7 @@ Returns
Improve this Doc
- View Source
+ View Source
RemoveTrustedRootCertificate(Boolean)
@@ -804,7 +837,7 @@ Parameters
Improve this Doc
- View Source
+ View Source
RemoveTrustedRootCertificateAsAdmin(Boolean)
@@ -853,7 +886,7 @@ Returns
Improve this Doc
- View Source
+ View Source
TrustRootCertificate(Boolean)
@@ -887,7 +920,7 @@ Parameters
Improve this Doc
- View Source
+ View Source
TrustRootCertificateAsAdmin(Boolean)
@@ -947,7 +980,7 @@ Implements
Improve this Doc
- View Source
+ View Source
diff --git a/docs/index.json b/docs/index.json
index 07b473994..852c6dd56 100644
--- a/docs/index.json
+++ b/docs/index.json
@@ -267,7 +267,7 @@
"api/Titanium.Web.Proxy.Network.CertificateManager.html": {
"href": "api/Titanium.Web.Proxy.Network.CertificateManager.html",
"title": "Class CertificateManager | Titanium Web Proxy",
- "keywords": "Class CertificateManager A class to manage SSL certificates used by this proxy server. Inheritance Object CertificateManager Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Network Assembly : Titanium.Web.Proxy.dll Syntax public sealed class CertificateManager : IDisposable Properties | Improve this Doc View Source CertificateCacheTimeOutMinutes Minutes certificates should be kept in cache when not used. Declaration public int CertificateCacheTimeOutMinutes { get; set; } Property Value Type Description Int32 | Improve this Doc View Source CertificateEngine Select Certificate Engine. Optionally set to BouncyCastle. Mono only support BouncyCastle and it is the default. Declaration public CertificateEngine CertificateEngine { get; set; } Property Value Type Description CertificateEngine | Improve this Doc View Source OverwritePfxFile Overwrite Root certificate file. true : replace an existing .pfx file if password is incorect or if RootCertificate = null. Declaration public bool OverwritePfxFile { get; set; } Property Value Type Description Boolean | Improve this Doc View Source PfxFilePath Name(path) of the Root certificate file. 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) Declaration public string PfxFilePath { get; set; } Property Value Type Description String | Improve this Doc View Source PfxPassword Password of the Root certificate file. Set a password for the .pfx file Declaration public string PfxPassword { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificate The root certificate. Declaration public X509Certificate2 RootCertificate { get; set; } Property Value Type Description X509Certificate2 | Improve this Doc View Source RootCertificateIssuerName Name of the root certificate issuer. (This is valid only when RootCertificate property is not set.) Declaration public string RootCertificateIssuerName { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificateName Name of the root certificate. (This is valid only when RootCertificate property is not set.) If no certificate is provided then a default Root Certificate will be created and used. The provided root certificate will be stored in proxy exe directory with the private key. Root certificate file will be named as \"rootCert.pfx\". Declaration public string RootCertificateName { get; set; } Property Value Type Description String | Improve this Doc View Source SaveFakeCertificates Save all fake certificates in folder \"crts\" (will be created in proxy dll directory). for can load the certificate and not make new certificate every time. Declaration public bool SaveFakeCertificates { get; set; } Property Value Type Description Boolean | Improve this Doc View Source StorageFlag Adjust behaviour when certificates are saved to filesystem. Declaration public X509KeyStorageFlags StorageFlag { get; set; } Property Value Type Description X509KeyStorageFlags Methods | Improve this Doc View Source ClearRootCertificate() Clear the root certificate and cache. Declaration public void ClearRootCertificate() | Improve this Doc View Source CreateRootCertificate(Boolean) Attempts to create a RootCertificate. Declaration public bool CreateRootCertificate(bool persistToFile = true) Parameters Type Name Description Boolean persistToFile if set to true try to load/save the certificate from rootCert.pfx. Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source Dispose() Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. Declaration public void Dispose() | Improve this Doc View Source EnsureRootCertificate() Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on initial setup from proxy constructor for user/machine trust. Declaration public void EnsureRootCertificate() | Improve this Doc View Source EnsureRootCertificate(Boolean, Boolean, Boolean) Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on provided parameters. Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true. Declaration public void EnsureRootCertificate(bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? | Improve this Doc View Source IsRootCertificateMachineTrusted() Determines whether the root certificate is machine trusted. Declaration public bool IsRootCertificateMachineTrusted() Returns Type Description Boolean | Improve this Doc View Source IsRootCertificateUserTrusted() Determines whether the root certificate is trusted. Declaration public bool IsRootCertificateUserTrusted() Returns Type Description Boolean | Improve this Doc View Source LoadRootCertificate() Loads root certificate from current executing assembly location with expected name rootCert.pfx. Declaration public X509Certificate2 LoadRootCertificate() Returns Type Description X509Certificate2 | Improve this Doc View Source LoadRootCertificate(String, String, Boolean, X509KeyStorageFlags) Manually load a Root certificate file from give path (.pfx file). Declaration public bool LoadRootCertificate(string pfxFilePath, string password, bool overwritePfXFile = true, X509KeyStorageFlags storageFlag = X509KeyStorageFlags.Exportable) Parameters Type Name Description String 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). String password Set a password for the .pfx file. Boolean overwritePfXFile true : replace an existing .pfx file if password is incorect or if RootCertificate==null. X509KeyStorageFlags storageFlag Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source RemoveTrustedRootCertificate(Boolean) 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). Declaration public void RemoveTrustedRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Should also remove from machine store? | Improve this Doc View Source RemoveTrustedRootCertificateAsAdmin(Boolean) Removes the trusted certificates from user store, optionally also from machine store Declaration public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean Should also remove from machine store? | Improve this Doc View Source TrustRootCertificate(Boolean) Trusts the root certificate in user store, optionally also in machine store. Machine trust would require elevated permissions (will silently fail otherwise). Declaration public void TrustRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted | Improve this Doc View Source TrustRootCertificateAsAdmin(Boolean) Puts the certificate to the user store, optionally also to machine store. Prompts with UAC if elevated permissions are required. Works only on Windows. Declaration public bool TrustRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean True if success. Implements System.IDisposable"
+ "keywords": "Class CertificateManager A class to manage SSL certificates used by this proxy server. Inheritance Object CertificateManager Implements IDisposable Inherited Members Object.ToString() Object.Equals(Object) Object.Equals(Object, Object) Object.ReferenceEquals(Object, Object) Object.GetHashCode() Object.GetType() Object.MemberwiseClone() Namespace : Titanium.Web.Proxy.Network Assembly : Titanium.Web.Proxy.dll Syntax public sealed class CertificateManager : IDisposable Properties | Improve this Doc View Source CertificateCacheTimeOutMinutes Minutes certificates should be kept in cache when not used. Declaration public int CertificateCacheTimeOutMinutes { get; set; } Property Value Type Description Int32 | Improve this Doc View Source CertificateEngine Select Certificate Engine. Optionally set to BouncyCastle. Mono only support BouncyCastle and it is the default. Declaration public CertificateEngine CertificateEngine { get; set; } Property Value Type Description CertificateEngine | Improve this Doc View Source CertificateStorage The fake certificate cache storage. The default cache storage implementation saves certificates in folder \"crts\" (will be created in proxy dll directory). Implement ICertificateCache interface and assign concrete class here to customize. Declaration public ICertificateCache CertificateStorage { get; set; } Property Value Type Description ICertificateCache | Improve this Doc View Source OverwritePfxFile Overwrite Root certificate file. true : replace an existing .pfx file if password is incorrect or if RootCertificate = null. Declaration public bool OverwritePfxFile { get; set; } Property Value Type Description Boolean | Improve this Doc View Source PfxFilePath Name(path) of the Root certificate file. 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) Declaration public string PfxFilePath { get; set; } Property Value Type Description String | Improve this Doc View Source PfxPassword Password of the Root certificate file. Set a password for the .pfx file Declaration public string PfxPassword { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificate The root certificate. Declaration public X509Certificate2 RootCertificate { get; set; } Property Value Type Description X509Certificate2 | Improve this Doc View Source RootCertificateIssuerName Name of the root certificate issuer. (This is valid only when RootCertificate property is not set.) Declaration public string RootCertificateIssuerName { get; set; } Property Value Type Description String | Improve this Doc View Source RootCertificateName Name of the root certificate. (This is valid only when RootCertificate property is not set.) If no certificate is provided then a default Root Certificate will be created and used. The provided root certificate will be stored in proxy exe directory with the private key. Root certificate file will be named as \"rootCert.pfx\". Declaration public string RootCertificateName { get; set; } Property Value Type Description String | Improve this Doc View Source SaveFakeCertificates Save all fake certificates using CertificateStorage . for can load the certificate and not make new certificate every time. Declaration public bool SaveFakeCertificates { get; set; } Property Value Type Description Boolean | Improve this Doc View Source StorageFlag Adjust behaviour when certificates are saved to filesystem. Declaration public X509KeyStorageFlags StorageFlag { get; set; } Property Value Type Description X509KeyStorageFlags Methods | Improve this Doc View Source ClearRootCertificate() Clear the root certificate and cache. Declaration public void ClearRootCertificate() | Improve this Doc View Source CreateRootCertificate(Boolean) Attempts to create a RootCertificate. Declaration public bool CreateRootCertificate(bool persistToFile = true) Parameters Type Name Description Boolean persistToFile if set to true try to load/save the certificate from rootCert.pfx. Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source Dispose() Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. Declaration public void Dispose() | Improve this Doc View Source EnsureRootCertificate() Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on initial setup from proxy constructor for user/machine trust. Declaration public void EnsureRootCertificate() | Improve this Doc View Source EnsureRootCertificate(Boolean, Boolean, Boolean) Ensure certificates are setup (creates root if required). Also makes root certificate trusted based on provided parameters. Note:setting machineTrustRootCertificate to true will force userTrustRootCertificate to true. Declaration public void EnsureRootCertificate(bool userTrustRootCertificate, bool machineTrustRootCertificate, bool trustRootCertificateAsAdmin = false) Parameters Type Name Description Boolean userTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's user certificate store? Boolean machineTrustRootCertificate Should fake HTTPS certificate be trusted by this machine's certificate store? Boolean trustRootCertificateAsAdmin Should we attempt to trust certificates with elevated permissions by prompting for UAC if required? | Improve this Doc View Source IsRootCertificateMachineTrusted() Determines whether the root certificate is machine trusted. Declaration public bool IsRootCertificateMachineTrusted() Returns Type Description Boolean | Improve this Doc View Source IsRootCertificateUserTrusted() Determines whether the root certificate is trusted. Declaration public bool IsRootCertificateUserTrusted() Returns Type Description Boolean | Improve this Doc View Source LoadRootCertificate() Loads root certificate from current executing assembly location with expected name rootCert.pfx. Declaration public X509Certificate2 LoadRootCertificate() Returns Type Description X509Certificate2 | Improve this Doc View Source LoadRootCertificate(String, String, Boolean, X509KeyStorageFlags) Manually load a Root certificate file from give path (.pfx file). Declaration public bool LoadRootCertificate(string pfxFilePath, string password, bool overwritePfXFile = true, X509KeyStorageFlags storageFlag = X509KeyStorageFlags.Exportable) Parameters Type Name Description String 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). String password Set a password for the .pfx file. Boolean overwritePfXFile true : replace an existing .pfx file if password is incorrect or if RootCertificate==null. X509KeyStorageFlags storageFlag Returns Type Description Boolean true if succeeded, else false. | Improve this Doc View Source RemoveTrustedRootCertificate(Boolean) 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). Declaration public void RemoveTrustedRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Should also remove from machine store? | Improve this Doc View Source RemoveTrustedRootCertificateAsAdmin(Boolean) Removes the trusted certificates from user store, optionally also from machine store Declaration public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean Should also remove from machine store? | Improve this Doc View Source TrustRootCertificate(Boolean) Trusts the root certificate in user store, optionally also in machine store. Machine trust would require elevated permissions (will silently fail otherwise). Declaration public void TrustRootCertificate(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted | Improve this Doc View Source TrustRootCertificateAsAdmin(Boolean) Puts the certificate to the user store, optionally also to machine store. Prompts with UAC if elevated permissions are required. Works only on Windows. Declaration public bool TrustRootCertificateAsAdmin(bool machineTrusted = false) Parameters Type Name Description Boolean machineTrusted Returns Type Description Boolean True if success. Implements System.IDisposable"
},
"api/Titanium.Web.Proxy.Network.html": {
"href": "api/Titanium.Web.Proxy.Network.html",
diff --git a/docs/xrefmap.yml b/docs/xrefmap.yml
index f0aeaf495..119b7ae55 100644
--- a/docs/xrefmap.yml
+++ b/docs/xrefmap.yml
@@ -2970,6 +2970,19 @@ references:
isSpec: "True"
fullName: Titanium.Web.Proxy.Network.CertificateManager.CertificateEngine
nameWithType: CertificateManager.CertificateEngine
+- uid: Titanium.Web.Proxy.Network.CertificateManager.CertificateStorage
+ name: CertificateStorage
+ href: api/Titanium.Web.Proxy.Network.CertificateManager.html#Titanium_Web_Proxy_Network_CertificateManager_CertificateStorage
+ commentId: P:Titanium.Web.Proxy.Network.CertificateManager.CertificateStorage
+ fullName: Titanium.Web.Proxy.Network.CertificateManager.CertificateStorage
+ nameWithType: CertificateManager.CertificateStorage
+- uid: Titanium.Web.Proxy.Network.CertificateManager.CertificateStorage*
+ name: CertificateStorage
+ href: api/Titanium.Web.Proxy.Network.CertificateManager.html#Titanium_Web_Proxy_Network_CertificateManager_CertificateStorage_
+ commentId: Overload:Titanium.Web.Proxy.Network.CertificateManager.CertificateStorage
+ isSpec: "True"
+ fullName: Titanium.Web.Proxy.Network.CertificateManager.CertificateStorage
+ nameWithType: CertificateManager.CertificateStorage
- uid: Titanium.Web.Proxy.Network.CertificateManager.ClearRootCertificate
name: ClearRootCertificate()
href: api/Titanium.Web.Proxy.Network.CertificateManager.html#Titanium_Web_Proxy_Network_CertificateManager_ClearRootCertificate
diff --git a/src/Titanium.Web.Proxy/Network/CachedCertificate.cs b/src/Titanium.Web.Proxy/Network/CachedCertificate.cs
index 818bb812c..64efe0b9b 100644
--- a/src/Titanium.Web.Proxy/Network/CachedCertificate.cs
+++ b/src/Titanium.Web.Proxy/Network/CachedCertificate.cs
@@ -1,23 +1,19 @@
using System;
using System.Security.Cryptography.X509Certificates;
+using System.Threading.Tasks;
namespace Titanium.Web.Proxy.Network
{
///
/// An object that holds the cached certificate
///
- internal class CachedCertificate
+ internal sealed class CachedCertificate
{
- internal CachedCertificate()
- {
- LastAccess = DateTime.Now;
- }
-
internal X509Certificate2 Certificate { get; set; }
///
- /// last time this certificate was used
- /// Usefull in determining its cache lifetime
+ /// Last time this certificate was used.
+ /// Useful in determining its cache lifetime.
///
internal DateTime LastAccess { get; set; }
}
diff --git a/src/Titanium.Web.Proxy/Network/CertificateManager.cs b/src/Titanium.Web.Proxy/Network/CertificateManager.cs
index a940ed8b4..cf0aee98c 100644
--- a/src/Titanium.Web.Proxy/Network/CertificateManager.cs
+++ b/src/Titanium.Web.Proxy/Network/CertificateManager.cs
@@ -4,8 +4,8 @@
using System.Diagnostics;
using System.IO;
using System.Linq;
-using System.Reflection;
using System.Security.Cryptography.X509Certificates;
+using System.Threading;
using System.Threading.Tasks;
using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Network.Certificate;
@@ -30,7 +30,6 @@ public enum CertificateEngine
/// Bug #468 Reported.
///
DefaultWindows = 1
-
}
///
@@ -45,9 +44,21 @@ public sealed class CertificateManager : IDisposable
///
/// Cache dictionary
///
- private readonly ConcurrentDictionary certificateCache;
+ private readonly ConcurrentDictionary cachedCertificates
+ = new ConcurrentDictionary();
+
+ ///
+ /// A list of pending certificate creation tasks.
+ /// Usefull to prevent multiple threads working on same certificate generation
+ /// when burst certificate generation requests happen for same certificate.
+ ///
+ private readonly ConcurrentDictionary> pendingCertificateCreationTasks
+ = new ConcurrentDictionary>();
- private readonly ConcurrentDictionary> pendingCertificateCreationTasks;
+ private readonly CancellationTokenSource clearCertificatesTokenSource
+ = new CancellationTokenSource();
+
+ private readonly object rootCertCreationLock = new object();
private ICertificateMaker certEngine;
@@ -55,12 +66,12 @@ public sealed class CertificateManager : IDisposable
private string issuer;
- private bool pfxFileExists;
-
private X509Certificate2 rootCertificate;
private string rootCertificateName;
+ private ICertificateCache certificateCache = new DefaultCertificateDiskCache();
+
///
/// Initializes a new instance of the class.
///
@@ -98,13 +109,8 @@ internal CertificateManager(string rootCertificateName, string rootCertificateIs
}
CertificateEngine = CertificateEngine.BouncyCastle;
-
- certificateCache = new ConcurrentDictionary();
- pendingCertificateCreationTasks = new ConcurrentDictionary>();
}
- private bool clearCertificates { get; set; }
-
///
/// Is the root certificate used by this proxy is valid?
///
@@ -122,7 +128,7 @@ internal CertificateManager(string rootCertificateName, string rootCertificateIs
internal bool MachineTrustRoot { get; set; }
///
- /// Whether trust operations should be done with elevated privillages
+ /// Whether trust operations should be done with elevated privileges
/// Will prompt with UAC if required. Works only on Windows.
///
internal bool TrustRootAsAdministrator { get; set; }
@@ -215,14 +221,25 @@ public X509Certificate2 RootCertificate
}
///
- /// Save all fake certificates in folder "crts" (will be created in proxy dll directory).
+ /// Save all fake certificates using .
/// for can load the certificate and not make new certificate every time.
///
public bool SaveFakeCertificates { get; set; } = false;
+ ///
+ /// The fake certificate cache storage.
+ /// The default cache storage implementation saves certificates in folder "crts" (will be created in proxy dll directory).
+ /// Implement ICertificateCache interface and assign concrete class here to customize.
+ ///
+ public ICertificateCache CertificateStorage
+ {
+ get => certificateCache;
+ set => certificateCache = value ?? new DefaultCertificateDiskCache();
+ }
+
///
/// Overwrite Root certificate file.
- /// true : replace an existing .pfx file if password is incorect or if RootCertificate = null.
+ /// true : replace an existing .pfx file if password is incorrect or if RootCertificate = null.
///
public bool OverwritePfxFile { get; set; } = true;
@@ -241,52 +258,7 @@ public X509Certificate2 RootCertificate
///
public void Dispose()
{
- }
-
- private string getRootCertificateDirectory()
- {
- string assemblyLocation = Assembly.GetExecutingAssembly().Location;
-
- // dynamically loaded assemblies returns string.Empty location
- if (assemblyLocation == string.Empty)
- {
- assemblyLocation = Assembly.GetEntryAssembly().Location;
- }
-
- string path = Path.GetDirectoryName(assemblyLocation);
- if (path == null)
- {
- throw new NullReferenceException();
- }
-
- return path;
- }
-
- private string getCertificatePath()
- {
- string path = getRootCertificateDirectory();
-
- string certPath = Path.Combine(path, "crts");
- if (!Directory.Exists(certPath))
- {
- Directory.CreateDirectory(certPath);
- }
-
- return certPath;
- }
-
- private string getRootCertificatePath()
- {
- string path = getRootCertificateDirectory();
-
- string fileName = PfxFilePath;
- if (fileName == string.Empty)
- {
- fileName = Path.Combine(path, "rootCert.pfx");
- StorageFlag = X509KeyStorageFlags.Exportable;
- }
-
- return fileName;
+ clearCertificatesTokenSource.Dispose();
}
///
@@ -412,43 +384,37 @@ private X509Certificate2 makeCertificate(string certificateName, bool isRootCert
///
internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{
- X509Certificate2 certificate = null;
+ X509Certificate2 certificate;
try
{
if (!isRootCertificate && SaveFakeCertificates)
{
- string path = getCertificatePath();
- string subjectName = ProxyConstants.CNRemoverRegex.Replace(certificateName, string.Empty);
- subjectName = subjectName.Replace("*", "$x$");
- string certificatePath = Path.Combine(path, subjectName + ".pfx");
+ string subjectName = ProxyConstants.CNRemoverRegex
+ .Replace(certificateName, string.Empty)
+ .Replace("*", "$x$");
- if (!File.Exists(certificatePath))
+ try
+ {
+ certificate = certificateCache.LoadCertificate(subjectName, StorageFlag);
+ }
+ catch (Exception e)
+ {
+ ExceptionFunc(new Exception("Failed to load fake certificate.", e));
+ certificate = null;
+ }
+
+ if (certificate == null)
{
certificate = makeCertificate(certificateName, false);
- // store as cache
try
{
- var exported = certificate.Export(X509ContentType.Pkcs12);
- File.WriteAllBytes(certificatePath, exported);
+ certificateCache.SaveCertificate(subjectName, certificate);
}
catch (Exception e)
{
ExceptionFunc(new Exception("Failed to save fake certificate.", e));
}
-
- }
- else
- {
- try
- {
- certificate = new X509Certificate2(certificatePath, string.Empty, StorageFlag);
- }
- catch
- {
- // if load failed create again
- certificate = makeCertificate(certificateName, false);
- }
}
}
else
@@ -459,6 +425,7 @@ internal X509Certificate2 CreateCertificate(string certificateName, bool isRootC
catch (Exception e)
{
ExceptionFunc(e);
+ certificate = null;
}
return certificate;
@@ -472,7 +439,7 @@ internal X509Certificate2 CreateCertificate(string certificateName, bool isRootC
internal async Task CreateCertificateAsync(string certificateName)
{
// check in cache first
- if (certificateCache.TryGetValue(certificateName, out var cached))
+ if (cachedCertificates.TryGetValue(certificateName, out var cached))
{
cached.LastAccess = DateTime.Now;
return cached.Certificate;
@@ -491,7 +458,7 @@ internal async Task CreateCertificateAsync(string certificateN
var result = CreateCertificate(certificateName, false);
if (result != null)
{
- certificateCache.TryAdd(certificateName, new CachedCertificate
+ cachedCertificates.TryAdd(certificateName, new CachedCertificate
{
Certificate = result
});
@@ -513,20 +480,27 @@ internal async Task CreateCertificateAsync(string certificateN
///
internal async void ClearIdleCertificates()
{
- clearCertificates = true;
- while (clearCertificates)
+ var cancellationToken = clearCertificatesTokenSource.Token;
+ while (!cancellationToken.IsCancellationRequested)
{
var cutOff = DateTime.Now.AddMinutes(-1 * CertificateCacheTimeOutMinutes);
- var outdated = certificateCache.Where(x => x.Value.LastAccess < cutOff).ToList();
+ var outdated = cachedCertificates.Where(x => x.Value.LastAccess < cutOff).ToList();
foreach (var cache in outdated)
{
- certificateCache.TryRemove(cache.Key, out _);
+ cachedCertificates.TryRemove(cache.Key, out _);
}
// after a minute come back to check for outdated certificates in cache
- await Task.Delay(1000 * 60);
+ try
+ {
+ await Task.Delay(1000 * 60, cancellationToken);
+ }
+ catch (TaskCanceledException)
+ {
+ return;
+ }
}
}
@@ -535,7 +509,7 @@ internal async void ClearIdleCertificates()
///
internal void StopClearIdleCertificates()
{
- clearCertificates = false;
+ clearCertificatesTokenSource.Cancel();
}
///
@@ -547,53 +521,68 @@ internal void StopClearIdleCertificates()
///
public bool CreateRootCertificate(bool persistToFile = true)
{
- if (persistToFile && RootCertificate == null)
- {
- RootCertificate = LoadRootCertificate();
- }
-
- if (RootCertificate != null)
+ lock (rootCertCreationLock)
{
- return true;
- }
-
- if (!OverwritePfxFile && pfxFileExists)
- {
- return false;
- }
+ if (persistToFile && RootCertificate == null)
+ {
+ RootCertificate = LoadRootCertificate();
+ }
- try
- {
- RootCertificate = CreateCertificate(RootCertificateName, true);
- }
- catch (Exception e)
- {
- ExceptionFunc(e);
- }
+ if (RootCertificate != null)
+ {
+ return true;
+ }
- if (persistToFile && RootCertificate != null)
- {
- try
+ if (!OverwritePfxFile)
{
try
{
- Directory.Delete(getCertificatePath(), true);
+ var rootCert = certificateCache.LoadRootCertificate(PfxFilePath, PfxPassword,
+ X509KeyStorageFlags.Exportable);
+
+ if (rootCert != null)
+ {
+ return false;
+ }
}
catch
{
- // ignore
+ // root cert cannot be loaded
}
+ }
- string fileName = getRootCertificatePath();
- File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12, PfxPassword));
+ try
+ {
+ RootCertificate = CreateCertificate(RootCertificateName, true);
}
catch (Exception e)
{
ExceptionFunc(e);
}
- }
- return RootCertificate != null;
+ if (persistToFile && RootCertificate != null)
+ {
+ try
+ {
+ try
+ {
+ certificateCache.Clear();
+ }
+ catch
+ {
+ // ignore
+ }
+
+ certificateCache.SaveRootCertificate(PfxFilePath, PfxPassword, RootCertificate);
+ }
+ catch (Exception e)
+ {
+ ExceptionFunc(e);
+ }
+ }
+
+ return RootCertificate != null;
+ }
}
///
@@ -602,16 +591,9 @@ public bool CreateRootCertificate(bool persistToFile = true)
///
public X509Certificate2 LoadRootCertificate()
{
- string fileName = getRootCertificatePath();
- pfxFileExists = File.Exists(fileName);
- if (!pfxFileExists)
- {
- return null;
- }
-
try
{
- return new X509Certificate2(fileName, PfxPassword, StorageFlag);
+ return certificateCache.LoadRootCertificate(PfxFilePath, PfxPassword, X509KeyStorageFlags.Exportable);
}
catch (Exception e)
{
@@ -629,7 +611,7 @@ public X509Certificate2 LoadRootCertificate()
///
/// Set a password for the .pfx file.
///
- /// true : replace an existing .pfx file if password is incorect or if
+ /// true : replace an existing .pfx file if password is incorrect or if
/// RootCertificate==null.
///
///
@@ -861,7 +843,7 @@ public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false)
ErrorDialog = false,
WindowStyle = ProcessWindowStyle.Hidden
},
-
+
// currentUser\Personal & currentMachine\Personal
new ProcessStartInfo
{
@@ -905,6 +887,7 @@ public bool RemoveTrustedRootCertificateAsAdmin(bool machineTrusted = false)
public void ClearRootCertificate()
{
certificateCache.Clear();
+ cachedCertificates.Clear();
rootCertificate = null;
}
}
diff --git a/src/Titanium.Web.Proxy/Network/DefaultCertificateDiskCache.cs b/src/Titanium.Web.Proxy/Network/DefaultCertificateDiskCache.cs
new file mode 100644
index 000000000..0690c86d4
--- /dev/null
+++ b/src/Titanium.Web.Proxy/Network/DefaultCertificateDiskCache.cs
@@ -0,0 +1,123 @@
+using System;
+using System.IO;
+using System.Reflection;
+using System.Security.Cryptography.X509Certificates;
+
+namespace Titanium.Web.Proxy.Network
+{
+ internal sealed class DefaultCertificateDiskCache : ICertificateCache
+ {
+ private const string defaultCertificateDirectoryName = "crts";
+ private const string defaultCertificateFileExtension = ".pfx";
+ private const string defaultRootCertificateFileName = "rootCert" + defaultCertificateFileExtension;
+ private string rootCertificatePath;
+ private string certificatePath;
+
+ public X509Certificate2 LoadRootCertificate(string name, string password, X509KeyStorageFlags storageFlags)
+ {
+ string filePath = getRootCertificatePath(name);
+ return loadCertificate(filePath, password, storageFlags);
+ }
+
+ public void SaveRootCertificate(string name, string password, X509Certificate2 certificate)
+ {
+ string filePath = getRootCertificatePath(name);
+ byte[] exported = certificate.Export(X509ContentType.Pkcs12, password);
+ File.WriteAllBytes(filePath, exported);
+ }
+
+ ///
+ public X509Certificate2 LoadCertificate(string subjectName, X509KeyStorageFlags storageFlags)
+ {
+ string filePath = Path.Combine(getCertificatePath(), subjectName + defaultCertificateFileExtension);
+ return loadCertificate(filePath, string.Empty, storageFlags);
+ }
+
+ ///
+ public void SaveCertificate(string subjectName, X509Certificate2 certificate)
+ {
+ string filePath = Path.Combine(getCertificatePath(), subjectName + defaultCertificateFileExtension);
+ byte[] exported = certificate.Export(X509ContentType.Pkcs12);
+ File.WriteAllBytes(filePath, exported);
+ }
+
+ public void Clear()
+ {
+ try
+ {
+ Directory.Delete(getCertificatePath(), true);
+ }
+ catch (DirectoryNotFoundException)
+ {
+ // do nothing
+ }
+
+ certificatePath = null;
+ }
+
+ private X509Certificate2 loadCertificate(string filePath, string password, X509KeyStorageFlags storageFlags)
+ {
+ byte[] exported;
+ try
+ {
+ exported = File.ReadAllBytes(filePath);
+ }
+ catch (IOException)
+ {
+ // file or directory not found
+ return null;
+ }
+
+ return new X509Certificate2(exported, password, storageFlags);
+ }
+
+ private string getRootCertificatePath(string filePath)
+ {
+ if (Path.IsPathRooted(filePath))
+ {
+ return filePath;
+ }
+
+ return Path.Combine(getRootCertificateDirectory(),
+ string.IsNullOrEmpty(filePath) ? defaultRootCertificateFileName : filePath);
+ }
+
+ private string getCertificatePath()
+ {
+ if (certificatePath == null)
+ {
+ string path = getRootCertificateDirectory();
+
+ string certPath = Path.Combine(path, defaultCertificateDirectoryName);
+ if (!Directory.Exists(certPath))
+ {
+ Directory.CreateDirectory(certPath);
+ }
+
+ certificatePath = certPath;
+ }
+
+ return certificatePath;
+ }
+
+ private string getRootCertificateDirectory()
+ {
+ if (rootCertificatePath == null)
+ {
+ string assemblyLocation = GetType().Assembly.Location;
+
+ // dynamically loaded assemblies returns string.Empty location
+ if (assemblyLocation == string.Empty)
+ {
+ assemblyLocation = Assembly.GetEntryAssembly().Location;
+ }
+
+ string path = Path.GetDirectoryName(assemblyLocation);
+
+ rootCertificatePath = path ?? throw new NullReferenceException();
+ }
+
+ return rootCertificatePath;
+ }
+ }
+}
diff --git a/src/Titanium.Web.Proxy/Network/ICertificateCache.cs b/src/Titanium.Web.Proxy/Network/ICertificateCache.cs
new file mode 100644
index 000000000..bd62348f8
--- /dev/null
+++ b/src/Titanium.Web.Proxy/Network/ICertificateCache.cs
@@ -0,0 +1,32 @@
+using System.Security.Cryptography.X509Certificates;
+
+namespace Titanium.Web.Proxy.Network
+{
+ public interface ICertificateCache
+ {
+ ///
+ /// Loads the root certificate from the storage.
+ ///
+ X509Certificate2 LoadRootCertificate(string name, string password, X509KeyStorageFlags storageFlags);
+
+ ///
+ /// Saves the root certificate to the storage.
+ ///
+ void SaveRootCertificate(string name, string password, X509Certificate2 certificate);
+
+ ///
+ /// Loads certificate from the storage. Returns true if certificate does not exist.
+ ///
+ X509Certificate2 LoadCertificate(string subjectName, X509KeyStorageFlags storageFlags);
+
+ ///
+ /// Stores certificate into the storage.
+ ///
+ void SaveCertificate(string subjectName, X509Certificate2 certificate);
+
+ ///
+ /// Clears the storage.
+ ///
+ void Clear();
+ }
+}