@bartojs answer is correct I have to use CertificateRequest.Create
.
I'm creating self signing certificate for my MITM Proxy Server application, so here working code for generating certificate in .Net Framework 4.7.2 without external .dll library like Bouncy Castle or CERTENROLLIB
public static X509Certificate2 CreateMyCertificate(string host, bool isCA)
{
var validFrom = DateTimeOffset.UtcNow;
var validUntil = DateTimeOffset.UtcNow.AddYears(8);
X509Certificate2 certificate;
RSA rsaKey = Program.rootRSA;
if (isCA)
{
// create exportable Root private key
// will be used later as global variable Program.rootRSA
CspParameters cspParams = new CspParameters
{
KeyContainerName = Guid.NewGuid().ToString()
};
rsaKey = new RSACryptoServiceProvider(2048, cspParams);
}
// Request a certificate with the common name as the host using the key pair.
// Common Name (AKA CN) represents the server name protected by the SSL certificate.
var request = new CertificateRequest(
$"CN={host}",
rsaKey,
HashAlgorithmName.SHA256,
RSASignaturePadding.Pkcs1
);
// Add Certificate Extensions
request.CertificateExtensions.Add(
new X509EnhancedKeyUsageExtension(
new OidCollection
{
new Oid("1.3.6.1.5.5.7.3.1")
}, true)
);
if (isCA)
{
request.CertificateExtensions.Add(new X509BasicConstraintsExtension(true, true, 1, false));
request.CertificateExtensions.Add(
new X509SubjectKeyIdentifierExtension(request.PublicKey, false));
request.CertificateExtensions.Add(
new X509KeyUsageExtension(X509KeyUsageFlags.CrlSign | X509KeyUsageFlags.KeyCertSign, false));
certificate = request.CreateSelfSigned(validFrom, validUntil);
}
else
{
SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName(host.Replace("*.", ""));
request.CertificateExtensions.Add(sanBuilder.Build());
request.CertificateExtensions.Add(new X509Extension("2.5.29.35", Program.authorityKeyIdentifer, false));
request.CertificateExtensions.Add(new X509KeyUsageExtension(
X509KeyUsageFlags.KeyEncipherment, false));
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
var unixTime = Convert.ToInt64((DateTime.UtcNow - epoch).TotalSeconds);
var serial = BitConverter.GetBytes(unixTime);
certificate = request.Create(Program.rootCert, validFrom, validUntil.AddDays(-5), serial);
certificate.PrivateKey = Program.rootCert.PrivateKey;
}
var certificateBytes = certificate.Export(X509ContentType.Pfx, "");
// return certificate with persisted private key
return new X509Certificate2(certificateBytes, "", X509KeyStorageFlags.Exportable);
}
Then use it like:
- create global variable and name it like
rootCert
and rootRSA
- on app start check Computer Certificate, if CA not stored generate using
rootCert = CreateMyCertificate("__MY_ROOT_CERTIFICATE", true);
- Export/Store
rootCert
to trusted Root location
- later if there are https
connect
request we can generate the cert using
CreateMyCertificate("stackoverflow.com", false);
- when chaining/creating
host
certificate make sure rootCert
is defined, because it will be used in CreateMyCertificate()
method, line below
if (isCA){...}
else
{
certificate = request.Create(Program.rootCert, validFrom, validUntil.AddDays(-5), serial);
}
note why you shoud not CERTENROLLIB:
- Slow because Its operation need to read directly from computer certificate
- RSA generation is also slower than built-in .net 4.7.2
- To get private key we need first to install the cert the we can export to PFX by doing alot installation to the computer certificate it can spamming your computer cert.