Commit 24c5332d authored by Honfika's avatar Honfika

fix after #795

parent c54c7c1c
using System; using System;
using System.IO; using System.IO;
using System.Net; using System.Net;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.Pkcs;
using Org.BouncyCastle.Asn1.X509; using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto; using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators; using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Operators; using Org.BouncyCastle.Crypto.Operators;
using Org.BouncyCastle.Crypto.Parameters; using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng; using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math; using Org.BouncyCastle.Math;
using Org.BouncyCastle.OpenSsl; using Org.BouncyCastle.OpenSsl;
using Org.BouncyCastle.Pkcs; using Org.BouncyCastle.Pkcs;
using Org.BouncyCastle.Security; using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities; using Org.BouncyCastle.Utilities;
using Org.BouncyCastle.X509; using Org.BouncyCastle.X509;
using Titanium.Web.Proxy.Helpers; using Titanium.Web.Proxy.Helpers;
using Titanium.Web.Proxy.Shared; using Titanium.Web.Proxy.Shared;
using X509Certificate = Org.BouncyCastle.X509.X509Certificate; using X509Certificate = Org.BouncyCastle.X509.X509Certificate;
namespace Titanium.Web.Proxy.Network.Certificate namespace Titanium.Web.Proxy.Network.Certificate
{ {
/// <summary> /// <summary>
/// Implements certificate generation operations. /// Implements certificate generation operations.
/// </summary> /// </summary>
internal class BCCertificateMaker : ICertificateMaker internal class BCCertificateMaker : ICertificateMaker
{ {
private readonly int certificateValidDays; private readonly int certificateValidDays;
private const int certificateGraceDays = 366; private const int certificateGraceDays = 366;
// The FriendlyName value cannot be set on Unix. // The FriendlyName value cannot be set on Unix.
// Set this flag to true when exception detected to avoid further exceptions // Set this flag to true when exception detected to avoid further exceptions
private static bool doNotSetFriendlyName; private static bool doNotSetFriendlyName;
private readonly ExceptionHandler exceptionFunc; private readonly ExceptionHandler exceptionFunc;
internal BCCertificateMaker(ExceptionHandler exceptionFunc, int certificateValidDays) internal BCCertificateMaker(ExceptionHandler exceptionFunc, int certificateValidDays)
{ {
this.certificateValidDays = certificateValidDays; this.certificateValidDays = certificateValidDays;
this.exceptionFunc = exceptionFunc; this.exceptionFunc = exceptionFunc;
} }
/// <summary> /// <summary>
/// Makes the certificate. /// Makes the certificate.
/// </summary> /// </summary>
/// <param name="sSubjectCn">The s subject cn.</param> /// <param name="sSubjectCn">The s subject cn.</param>
/// <param name="signingCert">The signing cert.</param> /// <param name="signingCert">The signing cert.</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
public X509Certificate2 MakeCertificate(string sSubjectCn, X509Certificate2? signingCert = null) public X509Certificate2 MakeCertificate(string sSubjectCn, X509Certificate2? signingCert = null)
{ {
return makeCertificateInternal(sSubjectCn, true, signingCert); return makeCertificateInternal(sSubjectCn, true, signingCert);
} }
/// <summary> /// <summary>
/// Generates the certificate. /// Generates the certificate.
/// </summary> /// </summary>
/// <param name="subjectName">Name of the subject.</param> /// <param name="subjectName">Name of the subject.</param>
/// <param name="issuerName">Name of the issuer.</param> /// <param name="issuerName">Name of the issuer.</param>
/// <param name="validFrom">The valid from.</param> /// <param name="validFrom">The valid from.</param>
/// <param name="validTo">The valid to.</param> /// <param name="validTo">The valid to.</param>
/// <param name="keyStrength">The key strength.</param> /// <param name="keyStrength">The key strength.</param>
/// <param name="signatureAlgorithm">The signature algorithm.</param> /// <param name="signatureAlgorithm">The signature algorithm.</param>
/// <param name="issuerPrivateKey">The issuer private key.</param> /// <param name="issuerPrivateKey">The issuer private key.</param>
/// <param name="hostName">The host name</param> /// <param name="hostName">The host name</param>
/// <returns>X509Certificate2 instance.</returns> /// <returns>X509Certificate2 instance.</returns>
/// <exception cref="PemException">Malformed sequence in RSA private key</exception> /// <exception cref="PemException">Malformed sequence in RSA private key</exception>
private static X509Certificate2 generateCertificate(string? hostName, private static X509Certificate2 generateCertificate(string? hostName,
string subjectName, string subjectName,
string issuerName, DateTime validFrom, string issuerName, DateTime validFrom,
DateTime validTo, int keyStrength = 2048, DateTime validTo, int keyStrength = 2048,
string signatureAlgorithm = "SHA256WithRSA", string signatureAlgorithm = "SHA256WithRSA",
AsymmetricKeyParameter? issuerPrivateKey = null) AsymmetricKeyParameter? issuerPrivateKey = null)
{ {
// Generating Random Numbers // Generating Random Numbers
var randomGenerator = new CryptoApiRandomGenerator(); var randomGenerator = new CryptoApiRandomGenerator();
var secureRandom = new SecureRandom(randomGenerator); var secureRandom = new SecureRandom(randomGenerator);
// The Certificate Generator // The Certificate Generator
var certificateGenerator = new X509V3CertificateGenerator(); var certificateGenerator = new X509V3CertificateGenerator();
// Serial Number // Serial Number
var serialNumber = var serialNumber =
BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom); BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), secureRandom);
certificateGenerator.SetSerialNumber(serialNumber); certificateGenerator.SetSerialNumber(serialNumber);
// Issuer and Subject Name // Issuer and Subject Name
var subjectDn = new X509Name(subjectName); var subjectDn = new X509Name(subjectName);
var issuerDn = new X509Name(issuerName); var issuerDn = new X509Name(issuerName);
certificateGenerator.SetIssuerDN(issuerDn); certificateGenerator.SetIssuerDN(issuerDn);
certificateGenerator.SetSubjectDN(subjectDn); certificateGenerator.SetSubjectDN(subjectDn);
certificateGenerator.SetNotBefore(validFrom); certificateGenerator.SetNotBefore(validFrom);
certificateGenerator.SetNotAfter(validTo); certificateGenerator.SetNotAfter(validTo);
if (hostName != null) if (hostName != null)
{ {
// add subject alternative names // add subject alternative names
var nameType = GeneralName.DnsName; var nameType = GeneralName.DnsName;
if (IPAddress.TryParse(hostName, out _)) if (IPAddress.TryParse(hostName, out _))
{ {
nameType = GeneralName.IPAddress; nameType = GeneralName.IPAddress;
} }
var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(nameType, hostName) };
var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(nameType, hostName) };
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
subjectAlternativeNamesExtension); certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false,
} subjectAlternativeNamesExtension);
}
// Subject Public Key
var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength); // Subject Public Key
var keyPairGenerator = new RsaKeyPairGenerator(); var keyGenerationParameters = new KeyGenerationParameters(secureRandom, keyStrength);
keyPairGenerator.Init(keyGenerationParameters); var keyPairGenerator = new RsaKeyPairGenerator();
var subjectKeyPair = keyPairGenerator.GenerateKeyPair(); keyPairGenerator.Init(keyGenerationParameters);
var subjectKeyPair = keyPairGenerator.GenerateKeyPair();
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
certificateGenerator.SetPublicKey(subjectKeyPair.Public);
// Set certificate intended purposes to only Server Authentication
certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false, // Set certificate intended purposes to only Server Authentication
new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth)); certificateGenerator.AddExtension(X509Extensions.ExtendedKeyUsage.Id, false,
if (issuerPrivateKey == null) new ExtendedKeyUsage(KeyPurposeID.IdKPServerAuth));
{ if (issuerPrivateKey == null)
certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true)); {
} certificateGenerator.AddExtension(X509Extensions.BasicConstraints.Id, true, new BasicConstraints(true));
}
var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm,
issuerPrivateKey ?? subjectKeyPair.Private, secureRandom); var signatureFactory = new Asn1SignatureFactory(signatureAlgorithm,
issuerPrivateKey ?? subjectKeyPair.Private, secureRandom);
// Self-sign the certificate
var certificate = certificateGenerator.Generate(signatureFactory); // Self-sign the certificate
var certificate = certificateGenerator.Generate(signatureFactory);
// Corresponding private key
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private); // Corresponding private key
var privateKeyInfo = PrivateKeyInfoFactory.CreatePrivateKeyInfo(subjectKeyPair.Private);
var seq = (Asn1Sequence)Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded());
var seq = (Asn1Sequence)Asn1Object.FromByteArray(privateKeyInfo.ParsePrivateKey().GetDerEncoded());
if (seq.Count != 9)
{ if (seq.Count != 9)
throw new PemException("Malformed sequence in RSA private key"); {
} throw new PemException("Malformed sequence in RSA private key");
}
var rsa = RsaPrivateKeyStructure.GetInstance(seq);
var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent, var rsa = RsaPrivateKeyStructure.GetInstance(seq);
rsa.Prime1, rsa.Prime2, rsa.Exponent1, var rsaparams = new RsaPrivateCrtKeyParameters(rsa.Modulus, rsa.PublicExponent, rsa.PrivateExponent,
rsa.Exponent2, rsa.Coefficient); rsa.Prime1, rsa.Prime2, rsa.Exponent1,
rsa.Exponent2, rsa.Coefficient);
// Set private key onto certificate instance
var x509Certificate = withPrivateKey(certificate, rsaparams); // Set private key onto certificate instance
var x509Certificate = withPrivateKey(certificate, rsaparams);
if (!doNotSetFriendlyName)
{ if (!doNotSetFriendlyName)
try {
{ try
x509Certificate.FriendlyName = ProxyConstants.CNRemoverRegex.Replace(subjectName, string.Empty); {
} x509Certificate.FriendlyName = ProxyConstants.CNRemoverRegex.Replace(subjectName, string.Empty);
catch (PlatformNotSupportedException) }
{ catch (PlatformNotSupportedException)
doNotSetFriendlyName = true; {
} doNotSetFriendlyName = true;
} }
}
return x509Certificate;
} return x509Certificate;
}
private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
{ private static X509Certificate2 withPrivateKey(X509Certificate certificate, AsymmetricKeyParameter privateKey)
const string password = "password"; {
Pkcs12Store store; const string password = "password";
Pkcs12Store store;
if (RunTime.IsRunningOnMono)
{ if (RunTime.IsRunningOnMono)
var builder = new Pkcs12StoreBuilder(); {
builder.SetUseDerEncoding(true); var builder = new Pkcs12StoreBuilder();
store = builder.Build(); builder.SetUseDerEncoding(true);
} store = builder.Build();
else }
{ else
store = new Pkcs12Store(); {
} store = new Pkcs12Store();
}
var entry = new X509CertificateEntry(certificate);
store.SetCertificateEntry(certificate.SubjectDN.ToString(), entry); 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.SetKeyEntry(certificate.SubjectDN.ToString(), new AsymmetricKeyEntry(privateKey), new[] { entry });
{ using (var ms = new MemoryStream())
store.Save(ms, password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator())); {
store.Save(ms, password.ToCharArray(), new SecureRandom(new CryptoApiRandomGenerator()));
return new X509Certificate2(ms.ToArray(), password, X509KeyStorageFlags.Exportable);
} return new X509Certificate2(ms.ToArray(), password, X509KeyStorageFlags.Exportable);
} }
}
/// <summary>
/// Makes the certificate internal. /// <summary>
/// </summary> /// Makes the certificate internal.
/// <param name="hostName">hostname for certificate</param> /// </summary>
/// <param name="subjectName">The full subject.</param> /// <param name="hostName">hostname for certificate</param>
/// <param name="validFrom">The valid from.</param> /// <param name="subjectName">The full subject.</param>
/// <param name="validTo">The valid to.</param> /// <param name="validFrom">The valid from.</param>
/// <param name="signingCertificate">The signing certificate.</param> /// <param name="validTo">The valid to.</param>
/// <returns>X509Certificate2 instance.</returns> /// <param name="signingCertificate">The signing certificate.</param>
/// <exception cref="System.ArgumentException"> /// <returns>X509Certificate2 instance.</returns>
/// You must specify a Signing Certificate if and only if you are not creating a /// <exception cref="System.ArgumentException">
/// root. /// You must specify a Signing Certificate if and only if you are not creating a
/// </exception> /// root.
private X509Certificate2 makeCertificateInternal(string hostName, string subjectName, /// </exception>
DateTime validFrom, DateTime validTo, X509Certificate2? signingCertificate) private X509Certificate2 makeCertificateInternal(string hostName, string subjectName,
{ DateTime validFrom, DateTime validTo, X509Certificate2? signingCertificate)
if (signingCertificate == null) {
{ if (signingCertificate == null)
return generateCertificate(null, subjectName, subjectName, validFrom, validTo); {
} return generateCertificate(null, subjectName, subjectName, validFrom, validTo);
}
var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey);
return generateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo, var kp = DotNetUtilities.GetKeyPair(signingCertificate.PrivateKey);
issuerPrivateKey: kp.Private); return generateCertificate(hostName, subjectName, signingCertificate.Subject, validFrom, validTo,
} issuerPrivateKey: kp.Private);
}
/// <summary>
/// Makes the certificate internal. /// <summary>
/// </summary> /// Makes the certificate internal.
/// <param name="subject">The s subject cn.</param> /// </summary>
/// <param name="switchToMtaIfNeeded">if set to <c>true</c> [switch to MTA if needed].</param> /// <param name="subject">The s subject cn.</param>
/// <param name="signingCert">The signing cert.</param> /// <param name="switchToMtaIfNeeded">if set to <c>true</c> [switch to MTA if needed].</param>
/// <returns>X509Certificate2.</returns> /// <param name="signingCert">The signing cert.</param>
private X509Certificate2 makeCertificateInternal(string subject, /// <returns>X509Certificate2.</returns>
bool switchToMtaIfNeeded, X509Certificate2? signingCert = null) private X509Certificate2 makeCertificateInternal(string subject,
{ bool switchToMtaIfNeeded, X509Certificate2? signingCert = null)
return makeCertificateInternal(subject, $"CN={subject}", {
DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays), return makeCertificateInternal(subject, $"CN={subject}",
signingCert); DateTime.UtcNow.AddDays(-certificateGraceDays), DateTime.UtcNow.AddDays(certificateValidDays),
} signingCert);
} }
} }
}
using System; using System;
using System.IO; using System.IO;
using System.Net;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1; using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Asn1.Pkcs; using Org.BouncyCastle.Asn1.Pkcs;
...@@ -99,7 +100,13 @@ namespace Titanium.Web.Proxy.Network.Certificate ...@@ -99,7 +100,13 @@ namespace Titanium.Web.Proxy.Network.Certificate
if (hostName != null) if (hostName != null)
{ {
// add subject alternative names // add subject alternative names
var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(GeneralName.DnsName, hostName) }; var nameType = GeneralName.DnsName;
if (IPAddress.TryParse(hostName, out _))
{
nameType = GeneralName.IPAddress;
}
var subjectAlternativeNames = new Asn1Encodable[] { new GeneralName(nameType, hostName) };
var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames); var subjectAlternativeNamesExtension = new DerSequence(subjectAlternativeNames);
certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false, certificateGenerator.AddExtension(X509Extensions.SubjectAlternativeName.Id, false,
......
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