【问题标题】:Combining Public Certificate with Private RSA Key in C#在 C# 中将公共证书与私有 RSA 密钥相结合
【发布时间】:2021-03-03 13:03:34
【问题描述】:

我收到了来自客户端的公共证书和私有 RSA 密钥,如下(此处仅部分显示):

-----BEGIN CERTIFICATE-----
MIIFKDCCAxACAQEwDQYJKoZIhvcNAQELBQAwbjELMAkGA1UEBhMCVVMxETAPBgNV
BAgMCElsbGlub2lzMRAwDgYDVQQHDAdDaGljYWdvMRMwEQYDVQQKDApDbG91ZFF1
YW50MSUwIwYJKoZIhvcNAQkBFhZjcWFpb3BzQGNsb3VkcXVhbnQuY29tMB4XDTIw
MDkyMzIwMTgyN1oXDTQwMDkxODIwMTgyN1owRjELMAkGA1UEBhMCVVMxETAPBgNV
iESEKmYvylUwce7TcOuVnLtufyXxr8egu43jPvWDHsK0QvhMbx0q2KvyxGneQJ5E
...........
U0oIrq7M0qZTAf1BXEw9wgfQlIKfLzWDbIYKIg==
-----END CERTIFICATE-----

-----BEGIN RSA PRIVATE KEY-----
MIIJKQIBAAKCAgEAuxHk8lBZaqRTzhi/jvlj59pehTkK/u2fVHLuHWZPGOvPiAjq
HqrAgM1urPpmQPC2QuWObmhvQ1uluo/tq6V56WZNUEYALZnXhGvVNrvnYfoFooI0
F+1dJI2xQ8AC11Khinj+yAPtKymMhZFOHzQaFnGvjhKWTn/I0MaoJc+TQ8JKbDQ0
ycvopM2finujmIb62cxxhkhkEC5T+yMUJ8MbCnfmWpYux/oU2CXSHrnPympCbx7x
cReH0BeZEZPMI7+1Yi7U+XKGc7RP+zt9AoIBAQDE0BZb3WfertNqmyj9TF73VAwq
SDtSa6MTC8bOdtQewCz9/zC13MSI+jZGRDKSh7nxNL+bgM0OgmM6n/5B/61SIRjM
hswgOU9AHewIFSB/5C/ZcxWqM+PrgYXXYfOl9ZeWs1x+YRKuqk/CW/Z2rHJXykNx
.............
czG95J+2TWdGAjPuFLA596PRXT5KN2ITOWXUym3UksHmonbJ9om+k0ckPr4J
-----END RSA PRIVATE KEY-----

最初,我在这里浏览了许多帖子,并意识到将公共 PEM 证书与私有 RSA 密钥(PEM 格式)结合起来的最简单方法是使用 .NET 5:

// Needs .NET 5, that is .NET Core and VS2019 16.8 or higher
X509Certificate2 X509Cert = X509Certificate2.CreateFromPemFile("cert.cer", "cert.key");

X509Certificate2 cert = new X509Certificate2(mX509Cert.Export(X509ContentType.Pfx, "12345678"), "12345678");

这很好用。 但是,后来我意识到我所针对的项目类型(VSTO Excel 插件)还不支持 .NET 5... 因此,我将上面的代码更改为以下代码(这也有效),但没有意识到它也需要 .NET 5...

            X509Certificate2 pubOnly = new X509Certificate2("cert.cer");

            RSA rsa = RSA.Create();
            rsa.ImportRSAPrivateKey(keyBuffer, out _);
            X509Certificate2 pubPrivEphemeral = pubOnly.CopyWithPrivateKey(rsa);

            X509Certificate2 cert = new X509Certificate2(pubPrivEphemeral.Export(X509ContentType.Pfx, "12345678"), "12345678");

然后,当我最终切换到 .NET 4.7.2 和现在的 4.8 时,我正在寻找兼容的方式来做同样的事情,并且厌倦了这里提供的几种方法,但徒劳无功,没有一个不起作用 - 他们有复杂的例程,抛出例外...

所以我现在正在尝试 BouncyCastle,它在过去帮了很多忙,并想出了这个代码:

                string cerFilePath = Path.Combine(InstallDir, "cert.cer");
                X509Certificate2 dotnetCertificate2 = new X509Certificate2(cerFilePath);

                string keyFilePath = Path.Combine(InstallDir, "cert.key");
                var pemReader = new Org.BouncyCastle.OpenSsl.PemReader(File.OpenText(keyFilePath));
                var pemObject = pemReader.ReadObject();
                var rsa = DotNetUtilities.ToRSA((RsaPrivateCrtKeyParameters)pemObject);

不幸的是,最后一行抛出异常 - 这里有一些不兼容的东西,我不知道它是什么......异常消息指出:

“无法将 'Org.BouncyCastle.Crypto.AsymmetricCipherKeyPair' 类型的对象转换为 'Org.BouncyCastle.Crypto.Parameters.RsaPrivateCrtKeyParameters' 类型。”

因此,如果有人可以用上面的 BouncyCastle 代码纠正我或向我展示纯 .NET 4.6 - 4.8 代码 - 我将非常感激。

编辑: 所以我确实从here找到了另一个代码

private X509Certificate2 CreateCertWithKey()
    {
        using (Log.VerboseCall())
        {
            X509Certificate2 x509Cert = null;

            try
            {
                string cerFilePath = Path.Combine(InstallDir, "cert.cer");
                using (TextReader tr = new StreamReader(cerFilePath))
                {
                    publicPemCert = tr.ReadToEnd();
                }

                string keyFilePath = Path.Combine(InstallDir, "cert.key");
                using (TextReader tr = new StreamReader(keyFilePath))
                {
                    privatePemKey = tr.ReadToEnd();
                }

                var keyPair = (AsymmetricCipherKeyPair)new PemReader(new StringReader(privatePemKey)).ReadObject();
                var cert = (Org.BouncyCastle.X509.X509Certificate)new PemReader(new StringReader(publicPemCert)).ReadObject();

                var builder = new Pkcs12StoreBuilder();
                builder.SetUseDerEncoding(true);
                var store = builder.Build();

                var certEntry = new X509CertificateEntry(cert);
                store.SetCertificateEntry("", certEntry);
                store.SetKeyEntry("", new AsymmetricKeyEntry(keyPair.Private), new[] { certEntry });

                byte[] data;
                using (var ms = new MemoryStream())
                {
                    //store.Save(ms, Array.Empty<char>(), new SecureRandom());
                    data = ms.ToArray();

                    x509Cert = new X509Certificate2(data);
                }
            }
            catch(Exception ex)
            {
                Log.Verbose(ex);
            }

            return x509Cert;
        }
    }

它运行没有问题,但是使用生成的证书,我的 HttpWebRequest 不起作用,声称无法创建 TSL/SSL 连接(我尝试了 1.1、1.2 和 1.3,尽管我记得只是 Tls 自己协商版本。 .. ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

.NET 5 代码运行没有问题,与 HttpWebRequest 的代码完全相同...

【问题讨论】:

    标签: c# certificate rsa x509certificate bouncycastle


    【解决方案1】:

    在处理类似问题时遇到了这个问题,我找到了一种方法来创建包含私钥(我用于 XML 签名)的 X509Certificate2 对象。它要求您使用公钥/私钥创建 PFX 记录,我使用的是 OpenSSL:

    openssl pkcs12 -export -inkey private-key.pem -in cert.pem -out cert.pfx
    

    您可以直接使用文件名打开实例化实例:

    var cert = new X509Certificate2(filePath);
    

    或者,如果您已经在某处打开文件并以 base64 格式存储它,则可以使用字节:

    var bytes = Convert.FromBase64String(pfx_base64);
    var cert = new X509Certificate2(bytes);
    

    这要归功于阅读Scott Brady's 博客。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-03
      • 2012-03-14
      • 2011-06-18
      • 1970-01-01
      • 1970-01-01
      • 2022-12-16
      • 2014-09-27
      • 2014-07-07
      相关资源
      最近更新 更多