Skip to content
This repository was archived by the owner on Jul 9, 2023. It is now read-only.
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 93 additions & 8 deletions Titanium.Web.Proxy/Network/CertificateManager.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
Expand Down Expand Up @@ -30,7 +31,7 @@ public enum CertificateEngine
/// <summary>
/// A class to manage SSL certificates used by this proxy server
/// </summary>
public class CertificateManager : IDisposable
public sealed class CertificateManager : IDisposable
{
internal CertificateEngine Engine
{
Expand Down Expand Up @@ -147,7 +148,7 @@ private string GetRootCertificatePath()
return fileName;
}

internal X509Certificate2 LoadRootCertificate()
private X509Certificate2 LoadRootCertificate()
{
var fileName = GetRootCertificatePath();
if (!File.Exists(fileName)) return null;
Expand Down Expand Up @@ -218,6 +219,51 @@ public void TrustRootCertificate()
TrustRootCertificate(StoreLocation.LocalMachine);
}

/// <summary>
/// Puts the certificate to the local machine's certificate store.
/// Needs elevated permission. Works only on Windows.
/// </summary>
/// <returns></returns>
public bool TrustRootCertificateAsAdministrator()
{
if (RunTime.IsRunningOnMono())
{
return false;
}

var fileName = Path.GetTempFileName();
File.WriteAllBytes(fileName, RootCertificate.Export(X509ContentType.Pkcs12));

var info = new ProcessStartInfo
{
FileName = "certutil.exe",
Arguments = "-importPFX -p \"\" -f \"" + fileName + "\"",
CreateNoWindow = true,
UseShellExecute = true,
Verb = "runas",
ErrorDialog = false,
};

try
{
var process = Process.Start(info);
if (process == null)
{
return false;
}

process.WaitForExit();

File.Delete(fileName);
}
catch
{
return false;
}

return true;
}

/// <summary>
/// Removes the trusted certificates.
/// </summary>
Expand All @@ -230,13 +276,49 @@ public void RemoveTrustedRootCertificates()
RemoveTrustedRootCertificates(StoreLocation.LocalMachine);
}

/// <summary>
/// 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);
}

private bool FindRootCertificate(StoreLocation storeLocation)
{
string value = $"{RootCertificate.Issuer}";
return FindCertificates(StoreName.Root, storeLocation, value).Count > 0;
}

private X509Certificate2Collection FindCertificates(StoreName storeName, StoreLocation storeLocation, string findValue)
{
X509Store x509Store = new X509Store(storeName, storeLocation);
try
{
x509Store.Open(OpenFlags.OpenExistingOnly);
return x509Store.Certificates.Find(X509FindType.FindBySubjectDistinguishedName, findValue, false);
}
finally
{
x509Store.Close();
}
}

/// <summary>
/// Create an SSL certificate
/// </summary>
/// <param name="certificateName"></param>
/// <param name="isRootCertificate"></param>
/// <returns></returns>
internal virtual X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
internal X509Certificate2 CreateCertificate(string certificateName, bool isRootCertificate)
{
if (certificateCache.ContainsKey(certificateName))
{
Expand Down Expand Up @@ -317,7 +399,7 @@ internal async void ClearIdleCertificates(int certificateCacheTimeOutMinutes)
/// </summary>
/// <param name="storeLocation"></param>
/// <returns></returns>
internal void TrustRootCertificate(StoreLocation storeLocation)
private void TrustRootCertificate(StoreLocation storeLocation)
{
if (RootCertificate == null)
{
Expand All @@ -328,7 +410,7 @@ internal void TrustRootCertificate(StoreLocation storeLocation)
return;
}

X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation);

try
Expand Down Expand Up @@ -357,7 +439,7 @@ internal void TrustRootCertificate(StoreLocation storeLocation)
/// </summary>
/// <param name="storeLocation"></param>
/// <returns></returns>
internal void RemoveTrustedRootCertificates(StoreLocation storeLocation)
private void RemoveTrustedRootCertificates(StoreLocation storeLocation)
{
if (RootCertificate == null)
{
Expand All @@ -368,7 +450,7 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation)
return;
}

X509Store x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509RootStore = new X509Store(StoreName.Root, storeLocation);
var x509PersonalStore = new X509Store(StoreName.My, storeLocation);

try
Expand All @@ -382,7 +464,7 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation)
catch (Exception e)
{
exceptionFunc(
new Exception("Failed to make system trust root certificate "
new Exception("Failed to remove root certificate trust "
+ $" for {storeLocation} store location. You may need admin rights.", e));
}
finally
Expand All @@ -392,6 +474,9 @@ internal void RemoveTrustedRootCertificates(StoreLocation storeLocation)
}
}

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
}
Expand Down