简体   繁体   中英

Is it possible to generate on the fly SSL certificates from a root CA in C#?

I'm setting up a server under my own root CA to generate SSL certificates on the fly under .NET Core.

I'm able to generate self-signed certificates using the CertificateRequest class. However, these certs obviously aren't trusted by clients with my own root CA. I'm using the CertificateRequest.CreateSelfSigned() method to do this. I cannot use my root CA to sign these new certs, however. Using the CertificateRequest.Create() method will generate my new cert, but it will not provide a private key.

public static X509Certificate2 CreateSelfSignedCertificate(string domain)
{
    SubjectAlternativeNameBuilder sanBuilder = new SubjectAlternativeNameBuilder();
    sanBuilder.AddDnsName(domain);

    X500DistinguishedName distinguishedName = new X500DistinguishedName($"CN=On-The-Fly Generated Cert");

    using (RSA rsa = RSA.Create(2048))
    {
        var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);

        request.CertificateExtensions.Add(
            new X509KeyUsageExtension(X509KeyUsageFlags.DataEncipherment | X509KeyUsageFlags.KeyEncipherment | X509KeyUsageFlags.DigitalSignature, false));


        request.CertificateExtensions.Add(
            new X509EnhancedKeyUsageExtension(
                new OidCollection { new Oid("1.3.6.1.5.5.7.3.1") }, false));

        request.CertificateExtensions.Add(sanBuilder.Build());

        var ca = new X509Certificate2(File.ReadAllBytes(@"E:\testing_ca_certificate.pfx"), "password"); //Open my root CA cert, generated in OpenSSL

        //Generates a cert, but does not provide a private key.
        var certificate = request.Create(ca, new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(365)), new byte[] { 0, 1, 2, 3 }); 

        //Generates a usable cert, but is not under my root CA
        //var certificate = request.CreateSelfSigned(new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(365)));

        return new X509Certificate2(certificate.Export(X509ContentType.Pfx, "password"), "password", X509KeyStorageFlags.DefaultKeySet);
    }

}

Using the CertificateRequest.Create() method, I get a valid cert without a private key. I should have this private key so I can encrypt SSL traffic.

Given the certificate request was created using your rsa instance, you should be able to export the private key from it. RSA.ToXmlString() allows for exporting the key to a format that later that can be later imported using RSA.FromXmlString() .

Using the CertificateRequest.Create() method, I get a valid cert without a private key.

Real certificate authorities should not have the private key of the CA and the private key matching the certificate to be created in the same place at the same time. Aside from the fact that there is currently no way to decode a Certification Request (CSR), this method assumes it's being used with a public-only key provided to the CertificateRequest constructor.

Following the flow, the "expected" model is:

  • Client determines the need for a new certificate
  • Client generates public/private keypair
  • Client sends public key and other necessary information to CA (PKCS#10 CertificationRequest, or other means)
  • CA validates request
  • CA builds up the CertificateRequest object using the client public key
  • CA produces a certfificate with CertificateRequest.Create()
  • CA sends the certificate back to the client ( cert.RawData )
  • Client instantiates X509Certificate2 instance (only has public key)
  • Client uses the CopyWithPrivateKey (extension) method to associate the private key to a new certificate object.
  • Client does whatever client wants now.

So, after

//Generates a cert, but does not provide a private key.
var certificate = request.Create(ca, new DateTimeOffset(DateTime.UtcNow.AddDays(-1)), new DateTimeOffset(DateTime.UtcNow.AddDays(365)), new byte[] { 0, 1, 2, 3 }); 

you should add

certificate = certificate.CopyWithPrivateKey(rsa);

(Though, really, you should have your certificate objects in using statements so they get disposed when no longer needed, in which case you'd need a second variable to hold the cert-with-key)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM