Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
426 views
in Technique[技术] by (71.8m points)

.net - Generating self-signed certificate without external libraries

I'm curious to know if there's a simplish way to create a self-signed certificate comparable to the below New-SelfSignedCertificate command (other providers are OK too, for instance). I want to use only the .NET libraries without P/Invoke or external libraries such as Bouncy Castle or without calling PowerShell from the application.

New-SelfSignedCertificate -DnsName $certificateName -CertStoreLocation $certificateStore -KeyExportPolicy Exportable -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -NotAfter $certificateNotAfter

I suppose the simplest alternative would be to call PowerShell or use a Nuget library such as Bouncy Castle, if this isn't doable without external facilities? Though it feels like that if I knew enough how to construct certificates, it'd be possible to create a byte array template or such and use that in the X509Certificate2 constructor.

It appears one would need to

public X509Certificate2 GenerateCertificate(string fileName, string password, string subjectName, StoreName storeName, DateTime endDate, DateTime notAfter, string provider = "Microsoft Enhanced RSA and AES Cryptographic Provider")
{
    //Could provider be taken from https://stackoverflow.com/questions/43474902/generate-self-signed-rsa-2048-sha-256-certificate-pfx-file-using-openssl?            
    var newCertificate = new X509Certificate2(fileName, password, X509KeyStorageFlags.Exportable);

     /*
      # The following creates a self-signed certificate with one year of running time.
     $currentDate = Get-Date
     $certificateEndDate = $currentDate.AddYears(1)
     $certificateNotAfter = $certificateEndDate.AddYears(1)

     $certificateName = "https://www.test.com/test"
     $certificateStore = "Cert:LocalMachineMy"
     New-SelfSignedCertificate -DnsName $certificateName -CertStoreLocation $certificateStore -KeyExportPolicy Exportable -Provider "Microsoft Enhanced RSA and AES Cryptographic Provider" -NotAfter $certificateNotAfter
     */
}

<edit: It quickly became apparent there isn't a good way to do this with plain .NET.

A few more options I found:

A blog post Creating Authority-Signed and Self-Signed Certificates in .NET by Steve Syfuhs and another SO post using Mono extensions, Mono.Security won't set multiple KeyUsages. Apart from the choices already discussed, it's "something like this".

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There's nothing to create certificates in any released version of .NET.

.NET Core 2.0 (which hasn't released yet, but should soon) has and .NET Framework 4.7.2 have added this functionality via CertificateRequest (an API which can do self-signed certs, chain-signed certs, or PKCS#10 certificate/certification signing requests).

The PowerShell command is combining three things:

  • Key Storage Parameters (the CSP)
  • Certificate Creation
  • Certificate Storage (the store to add the cert to)

To create a "few-frills" self-signed certificate good for TLS server authentication on localhost:

X509Certificate2 certificate = null;
var sanBuilder = new SubjectAlternativeNameBuilder();
sanBuilder.AddDnsName("localhost");

// New .NET Core Create(int) method.  Or use
// rsa = RSA.Create(), rsa.KeySize = newRsaKeySize,
// or (on .NET Framework) new RSACng(newRsaKeySize)
using (RSA rsa = RSA.Create(newRsaKeySize))
{
    var certRequest = new CertificateRequest(
        $"CN=localhost",
        rsa,
        HashAlgorithmName.SHA256,
        RSASignaturePadding.Pkcs1);

    // Explicitly not a CA.
    certRequest.CertificateExtensions.Add(
        new X509BasicConstraintsExtension(false, false, 0, false));

    certRequest.CertificateExtensions.Add(
        new X509KeyUsageExtension(
            X509KeyUsageFlags.DigitalSignature | X509KeyUsageFlags.KeyEncipherment,
            true));

    // TLS Server EKU
    certRequest.CertificateExtensions.Add(
        new X509EnhancedKeyUsageExtension(
            new OidCollection
            {
                new Oid("1.3.6.1.5.5.7.3.1"),
            },
            false));

    // Add the SubjectAlternativeName extension
    certRequest.CertificateExtensions.Add(sanBuilder.Build());

    DateTimeOffset now = DateTimeOffset.UtcNow;
    certificate = certRequest.CreateSelfSigned(now, now.AddDays(365.25));
}

The certificate this created has an ephemeral private key -- it's not currently written to disk. If you don't care what key storage provider it uses then at this point your best bet would be to export the certificate (and private key) as a PFX/PKCS#12, then re-import it with PersistKeySet (and Exportable, since you want that), and add the imported copy to the X509Store of your choosing.

If you care about the key storage provider (in your case, a CAPI CSP), or you want to avoid export/import, you can create it using a pre-persisted key. So you'd replace RSA.Create(newRsaKeySize) with

CAPI:

var cspParameters = new CspParameters(
    /* PROV_RSA_AES */ 24,
    "Microsoft Enhanced RSA and AES Cryptographic Provider",
    Guid.NewGuid().ToString());

using (RSA rsa = new RSACryptoServiceProvider(newRsaKeySize, cspParameters))

CNG:

var keyParams = new CngKeyCreationParameters();
keyParams.Parameters.Add(
    new CngProperty(
        "Length",
        BitConverter.GetBytes(newRsaKeySize),
        CngPropertyOptions.Persist));

using (CngKey rsaKey = CngKey.Create(CngAlgorithm.RSA, Guid.NewGuid().ToString(), keyParams)
using (RSA rsa = new RSACng(rsaKey))

And then add it to an open X509Store instance in the usual way.

it feels like that if I knew enough how to construct certificates, it'd be possible to create a byte array template or such and use that in the X509Certificate2 constructor.

True. But it is a tad bit tricky. You'd need to learn ASN.1 (ITU-T X.680), DER (ITU-T X.690) and X.509 (either RFC 5280 or ITU-T X.509). If you wanted to create the certificate mated with the private key you'd then need to learn PFX/PKCS#12 (RFC 7292, though limited to some of the older options unless you're Win10-only) and that has a bunch of prerequisite knowledge, too.

I think these are all fun things to learn about, and good things to know... but my colleagues give me strange looks when I start doodling DER on a whiteboard, so I probably don't represent the average developer opinion of "fun".


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...