简体   繁体   中英

How to persist private key of RSAParameter in C#/.net

Starting with .Net 4.7.2 (.Net Standard 2.0) it's possible to create self-signed certificates and certificate signing requests with C#/.Net only, see the MS Documentation .

While it's straight forward to create a self-signed certificate which will assert HasPrivateKey (you just call CreateSelfSigned(notBefore, notAfter) ) I'm having a hard time to figure out how to get hold of the private key in general, eg if I want to create a certificate signed by a CA and then want to persist the certificate as a PFX file or want to persist the private key in a .PEM file or want to store it in the MS certificate store together with the private key, or when I just want to have in memory and also assert HasPrivateKey .

What I do have is a 'RSAParameters' instance which is in possession of the relevant private information, but I failed to figure out how to (easily) use that for the purpose in question (create a PFX file or PEM file or MS Certificate Store entry) without having to read through all the relevant RFCs and write a program for that on my own. (That RSAParameter instance contains the D , Exponent and Modulus , so I could try to patch this together (with the help of this answer , hopefully), but I was hoping for a C# method which will perform these tasks for me (which I could not find) by now).

Of course the idea is to do that with .Net functionality alone, as well.

Every hint on how to achieve this is appreciated.

If you only have Modulus , Exponent , and D you first have to recover the CRT parameters ( P , Q , DP , DQ , InverseQ ).

As your other questions, you're mainly missing the cert.CopyWithPrivateKey(key) extension methods and rsa.ImportParameters(RSAParameters) :

if I want to create a certificate signed by a CA and then want to persist the certificate as a PFX file

using (RSA rsa = RSA.Create())
{
    rsa.ImportParameters(rsaParameters);

    using (X509Certificate2 caSigned = GetCASignedCert(rsa))
    using (X509Certificate2 withKey = caSigned.CopyWithPrivateKey(rsa))
    {
        File.WriteAllBytes("some.pfx", withKey.Export(X509ContentType.Pkcs12, "and a password"));
    }
}

or want to persist the private key in a .PEM file

This one is available in .NET Core 3.0 daily builds:

RSAParameters rsaParameters = default(RSAParameters);

using (StreamWriter writer = new StreamWriter("rsa.key"))
using (RSA rsa = RSA.Create())
{
    rsa.ImportParameters(rsaParameters);

    writer.WriteLine("-----BEGIN RSA PRIVATE KEY-----");

    writer.WriteLine(
        Convert.ToBase64String(
            rsa.ExportRSAPrivateKey(),
            Base64FormattingOptions.InsertLineBreaks));

    writer.WriteLine("-----END RSA PRIVATE KEY-----");
}

PKCS#8 and encrypted PKCS#8 are also available.

On existing versions this requires using the RSAParameters and a ITU-T X.690 DER encoder.

or want to store it in the MS certificate store together with the private key

using (RSA rsa = RSA.Create())
{
    rsa.ImportParameters(rsaParameters);

    using (X509Certificate2 caSigned = GetCASignedCert(rsa))
    using (X509Certificate2 withKey = caSigned.CopyWithPrivateKey(rsa))
    using (X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser))
    {
        X509Certificate2 persisted = new X509Certificate2(
            withKey.Export(X509ContentType.Pkcs12, ""),
            "",
            X509KeyStorageFlags.PersistKeySet);

        using (persisted)
        {
            store.Open(OpenFlags.ReadWrite);
            store.Add(persisted);
        }
    }
}

or when I just want to have in memory and also assert HasPrivateKey.

using (RSA rsa = RSA.Create())
{
    rsa.ImportParameters(rsaParameters);

    using (X509Certificate2 caSigned = GetCASignedCert(rsa))
    {
        // Yes, this value can outlive both usings
        return caSigned.CopyWithPrivateKey(rsa);
    }
}

If you are using a certificate hardware security module (HSM) such as a USB key, for example, it is not possible to "get" the private key because the HSM only provides an interface for using the private key. This is for security as once a private key is in a file or memory it is potentially obtainable by a third party.

Also .NET has, historically, not presented a flexible enough interface, although it is improving. As such a many software vendors use the more complete and well-maintained, Bouncy Castle API ( http://www.bouncycastle.org/csharp/ ) and you will find a lot of documentation around the web. Generally if .NET can't do it - bouncy castle will. Ironically an HSM requires .NET crypto access it's private key functionality on windows, but you usually encapsulate that somehow.

It is a steep learning curve using crypto APIs in general and it's unlikely that you will get much assistance without having a code example you want to make work.

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