Commit 64e96e09 authored by Honfika's avatar Honfika

Common BC Certificate mager

parent d852471a
...@@ -11,6 +11,7 @@ using Titanium.Web.Proxy.EventArguments; ...@@ -11,6 +11,7 @@ using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Http; using Titanium.Web.Proxy.Http;
using Titanium.Web.Proxy.Models; using Titanium.Web.Proxy.Models;
using Titanium.Web.Proxy.Network;
namespace Titanium.Web.Proxy.Examples.Wpf namespace Titanium.Web.Proxy.Examples.Wpf
{ {
...@@ -62,7 +63,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf ...@@ -62,7 +63,9 @@ namespace Titanium.Web.Proxy.Examples.Wpf
public MainWindow() public MainWindow()
{ {
proxyServer = new ProxyServer(); proxyServer = new ProxyServer();
proxyServer.CertificateEngine = CertificateEngine.DefaultWindows;
proxyServer.TrustRootCertificate = true; proxyServer.TrustRootCertificate = true;
proxyServer.CertificateManager.TrustRootCertificateAsAdministrator();
proxyServer.ForwardToUpstreamGateway = true; proxyServer.ForwardToUpstreamGateway = true;
var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true) var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
......
#if NET45 using System;
using System; using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Threading; using System.Threading;
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1;
...@@ -102,7 +103,6 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -102,7 +103,6 @@ namespace Titanium.Web.Proxy.Network.Certificate
// Self-sign the certificate // Self-sign the certificate
var certificate = certificateGenerator.Generate(signatureFactory); var certificate = certificateGenerator.Generate(signatureFactory);
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
// Corresponding private key // Corresponding private key
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
...@@ -118,13 +118,35 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -118,13 +118,35 @@ namespace Titanium.Web.Proxy.Network.Certificate
var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1, var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, rsa.Prime1, rsa.Prime2, rsa.Exponent1,
rsa.Exponent2, rsa.Coefficient); rsa.Exponent2, rsa.Coefficient);
#if NET45
// Set private key onto certificate instance // Set private key onto certificate instance
var x509Certificate = new X509Certificate2(certificate.GetEncoded());
x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams); x509Certificate.PrivateKey = DotNetUtilities.ToRSA(rsaparams);
x509Certificate.FriendlyName = subjectName; x509Certificate.FriendlyName = subjectName;
#else
var x509Certificate = WithPrivateKey(certificate, rsaparams);
x509Certificate.FriendlyName = subjectName;
#endif
return x509Certificate; return x509Certificate;
} }
private static X509Certificate2 WithPrivateKey(Org.BouncyCastle.X509.X509Certificate certificate, AsymmetricKeyParameter privateKey)
{
const string password = "password";
var store = new Pkcs12Store();
var entry = new X509CertificateEntry(certificate);
store.SetCertificateEntry(certificate.SubjectDN.ToString(), entry);
store.SetKeyEntry(certificate.SubjectDN.ToString(), new AsymmetricKeyEntry(privateKey), new[] { entry });
using (var ms = new MemoryStream())
{
store.Save(ms, password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator()));
return new X509Certificate2(ms.ToArray(), password, X509KeyStorageFlags.Exportable);
}
}
/// <summary> /// <summary>
/// Makes the certificate internal. /// Makes the certificate internal.
/// </summary> /// </summary>
...@@ -136,18 +158,52 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -136,18 +158,52 @@ namespace Titanium.Web.Proxy.Network.Certificate
/// <param name="signingCertificate">The signing certificate.</param> /// <param name="signingCertificate">The signing certificate.</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
/// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception> /// <exception cref="System.ArgumentException">You must specify a Signing Certificate if and only if you are not creating a root.</exception>
private X509Certificate2 MakeCertificateInternal(bool isRoot, string hostName, string subjectName, DateTime validFrom, DateTime validTo, private X509Certificate2 MakeCertificateInternal(bool isRoot,
X509Certificate2 signingCertificate) string hostName, string subjectName,
DateTime validFrom, DateTime validTo, X509Certificate2 signingCertificate)
{ {
if (isRoot != (null == signingCertificate)) if (isRoot != (null == signingCertificate))
{ {
throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(signingCertificate)); throw new ArgumentException("You must specify a Signing Certificate if and only if you are not creating a root.", nameof(signingCertificate));
} }
return isRoot if (isRoot)
? GenerateCertificate(null, subjectName, subjectName, validFrom, validTo) {
: GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, return GenerateCertificate(null, subjectName, subjectName, validFrom, validTo);
issuerPrivateKey: DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey).Private); }
else
{
#if NET45
var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey);
#else
var rsa = signingCertificate.GetRSAPrivateKey();
var kp = GetRsaKeyPair(rsa.ExportParameters(true));
#endif
return GenerateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, issuerPrivateKey: kp.Private);
}
}
static AsymmetricCipherKeyPair GetRsaKeyPair(RSAParameters rp)
{
BigInteger modulus = new BigInteger(1, rp.Modulus);
BigInteger pubExp = new BigInteger(1, rp.Exponent);
RsaKeyParameters pubKey = new RsaKeyParameters(
false,
modulus,
pubExp);
RsaPrivateCrtKeyParameters privKey = new RsaPrivateCrtKeyParameters(
modulus,
pubExp,
new BigInteger(1, rp.D),
new BigInteger(1, rp.P),
new BigInteger(1, rp.Q),
new BigInteger(1, rp.DP),
new BigInteger(1, rp.DQ),
new BigInteger(1, rp.InverseQ));
return new AsymmetricCipherKeyPair(pubKey, privKey);
} }
/// <summary> /// <summary>
...@@ -163,10 +219,10 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -163,10 +219,10 @@ namespace Titanium.Web.Proxy.Network.Certificate
bool switchToMtaIfNeeded, X509Certificate2 signingCert = null, bool switchToMtaIfNeeded, X509Certificate2 signingCert = null,
CancellationToken cancellationToken = default(CancellationToken)) CancellationToken cancellationToken = default(CancellationToken))
{ {
X509Certificate2 certificate = null; #if NET45
if (switchToMtaIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA) if (switchToMtaIfNeeded && Thread.CurrentThread.GetApartmentState() != ApartmentState.MTA)
{ {
X509Certificate2 certificate = null;
using (var manualResetEvent = new ManualResetEventSlim(false)) using (var manualResetEvent = new ManualResetEventSlim(false))
{ {
ThreadPool.QueueUserWorkItem(o => ThreadPool.QueueUserWorkItem(o =>
...@@ -184,10 +240,45 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -184,10 +240,45 @@ namespace Titanium.Web.Proxy.Network.Certificate
return certificate; return certificate;
} }
#endif
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert);
}
class CryptoApiRandomGenerator : IRandomGenerator
{
readonly RandomNumberGenerator rndProv;
public CryptoApiRandomGenerator()
{
rndProv = RandomNumberGenerator.Create();
}
public void AddSeedMaterial(byte[] seed) { }
public void AddSeedMaterial(long seed) { }
public void NextBytes(byte[] bytes)
{
rndProv.GetBytes(bytes);
}
return MakeCertificateInternal(isRoot, subject, $"CN={subject}", DateTime.UtcNow.AddDays(-certificateGraceDays), public void NextBytes(byte[] bytes, int start, int len)
DateTime.UtcNow.AddDays(certificateValidDays), isRoot ? null : signingCert); {
if (start < 0)
throw new ArgumentException("Start offset cannot be negative", "start");
if (bytes.Length < (start + len))
throw new ArgumentException("Byte array too small for requested offset and length");
if (bytes.Length == len && start == 0)
{
NextBytes(bytes);
}
else
{
byte[] tmpBuf = new byte[len];
NextBytes(tmpBuf);
Array.Copy(tmpBuf, 0, bytes, start, len);
}
}
} }
} }
} }
#endif
\ No newline at end of file
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