【发布时间】: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