如何在 C#/.net 中保留 RSAParameter 的私钥

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

从 .Net 4.7.2(.Net Standard 2.0)开始,可以仅使用 C#/.Net 创建自签名证书和证书签名请求,请参阅 MS Documentation

虽然创建一个声明 HasPrivateKey 的自签名证书很简单(您只需调用 CreateSelfSigned(notBefore, notAfter)),但我很难弄清楚如何掌握一般的私钥,例如如果我想创建一个由 CA 签名的证书,然后想将证书保存为 PFX 文件,或者想将私钥保存在 .PEM 文件中,或者想将它与私钥,或者当我只想在内存中并断言 HasPrivateKey.

我拥有的是一个 'RSAParameters' 实例,它拥有相关的私人信息,但我没有弄清楚如何(轻松地)将其用于相关目的(创建 PFX 文件或 PEM 文件或 MS 证书存储条目),而无需通读所有相关的 RFC 并自行编写程序。 (该 RSAParameter 实例包含 DExponentModulus,因此我可以尝试将其修补在一起(希望在 this answer 的帮助下),但我希望对于一个 C# 方法,它将为我执行这些任务(我现在找不到)。

当然,我们的想法也是单独使用 .Net 功能来做到这一点。

感谢您提供有关如何实现此目标的所有提示。

如果您使用的是证书硬件安全模块 (HSM),例如 USB 密钥,则无法 "get" 私钥,因为 HSM 仅提供使用私钥的接口钥匙。这是为了安全起见,因为一旦私钥在文件或内存中,第三方就有可能获得它。

此外,从历史上看,.NET 没有提供足够灵活的界面,尽管它正在改进。由于许多软件供应商使用更完整且维护良好的 Bouncy Castle API (http://www.bouncycastle.org/csharp/),您会在网上找到大量文档。一般来说,如果 .NET 做不到 - 充气城堡会。具有讽刺意味的是,HSM 需要 .NET 加密访问它在 windows 上的私钥功能,但您通常会以某种方式封装它。

一般来说,使用加密 APIs 是一个陡峭的学习曲线,如果没有您想要的代码示例,您不太可能获得很多帮助。

如果您只有 ModulusExponentD,您首先必须恢复 CRT 参数(PQDP, DQ, InverseQ).

作为您的其他问题,您主要缺少 cert.CopyWithPrivateKey(key) 扩展方法和 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

这个在 .NET Core 3.0 每日构建中可用:

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 和加密的 PKCS#8 也可用。

在现有版本中,这需要使用 RSAParameters 和 ITU-T X.690 DER 编码器。

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);
    }
}