gpt4 book ai didi

c# - .NET Standard - 以编程方式将证书和私钥合并到 .pfx 文件中

转载 作者:太空狗 更新时间:2023-10-29 20:12:40 32 4
gpt4 key购买 nike

有没有办法使用 C#/.NET Standard 以编程方式将证书和 key (均作为 Base64 字符串从服务中单独接收)组合到 .pfx 文件中?使用工具不是一种选择,因为我需要自动化它。

上下文:我需要将证书和私钥(没有页眉和页脚的单独 Base64 字符串)加载到 X509Certificate2 对象,以将其从 .NET Standard 1.6 库传递到应用程序.问题是,.NET Standard 中的 X509Certificate2 类中没有 PrivateKey 属性!因此,我将私钥实际加载到 X509Certificate2 对象中的唯一方法是将它与证书本身组合在一个 .pfx 文件中,然后像在构造函数中那样加载它。

最佳答案

对于框架类型,没有办法做到这一点。这可能与 BouncyCaSTLe 或其他库有关。

.NET Core 2.0 添加了通过扩展方法将证书和 key 对象合并在一起(合并到新的 X509Certificate2 对象中)的功能:

X509Certificate2 mergedCert = cert.CopyWithPrivateKey(rsaPrivateKey);
X509Certificate2 mergedCert = cert.CopyWithPrivateKey(dsaPrivateKey);
X509Certificate2 mergedCert = cert.CopyWithPrivateKey(ecdsaPrivateKey);

但这需要专门针对 netcoreapp20(而非 netstandard20)进行编译。

框架类型也没有办法从二进制表示中加载关键对象(CngKey.Import 除外,但它只适用于 Windows),只能从预解析的结构(RSAParametersDSAParametersECParameters)。

在 Linux 上实现此目标的最简单方法(如果 BouncyCaSTLe 无法帮助您)是使用 System.Process 生成类似于 openssl pkcs12 -export -out 的调用tmp.pfx -in tmp.cer -inkey tmp.key -password pass:"".

在 Windows 上,您可以使用 CngKey.Import 和 P/Invoke CertSetCertificateContextProperty (对于 CERT_NCRYPT_KEY_HANDLE_PROP_ID (78))然后在变异证书上调用 cert.Export


更新(2020-09-30):对于 .NET 3.0,这相当简单(不回答“使用 .NET Standard”的原始问题,因为它需要针对 netcoreapp3 进行编译。 0 或更高版本),并且还可以轻松地使用 .NET 5.0 添加对 PEM 编码 key 的支持。

此代码使用 .NET 5.0 PemEncoding 类检查 key 文件是否经过 PEM 编码(而不是二进制/DER 编码),然后使用支持的格式加载私钥,匹配私钥到证书,然后导出。 .NET Core 3.0 中新增了关键的导入方法。

private enum KeyFileKinds
{
None = 0,
Pkcs8,
EncryptedPkcs8,
RsaPrivateKey,
Any = -1,
}

public static byte[] MakePfx(string certPath, string keyPath, string exportPassword)
{
using X509Certificate2 cert = new X509Certificate2(certPath);
byte[] keyBytes;
KeyFileKinds kinds;
ReadOnlySpan<char> keyFileText = File.ReadAllText(keyPath).AsSpan();

// PemEncoding.TryFind requires net5.0+
if (PemEncoding.TryFind(keyFileText, out PemFields pemFields))
{
keyBytes = new byte[pemFields.DecodedDataLength];

if (!Convert.TryFromBase64Chars(keyFileText[pemFields.Base64Data], keyBytes, out int written) ||
written != keyBytes.Length)
{
Debug.Fail("PemEncoding.TryFind and Convert.TryFromBase64Chars disagree on Base64 encoding");
throw new InvalidOperationException();
}

ReadOnlySpan<char> label = keyFileText[pemFields.Label];

if (label.SequenceEqual("PRIVATE KEY"))
{
kinds = KeyFileKinds.Pkcs8;
}
else if (label.SequenceEqual("ENCRYPTED PRIVATE KEY"))
{
kinds = KeyFileKinds.EncryptedPkcs8;
}
else if (label.SequenceEqual("RSA PRIVATE KEY"))
{
kinds = KeyFileKinds.RsaPrivateKey;
}
else
{
throw new NotSupportedException($"The PEM file type '{label.ToString()}' is not supported.");
}
}
else
{
kinds = KeyFileKinds.Any;
keyBytes = File.ReadAllBytes(keyPath);
}

RSA rsa = null;
ECDsa ecdsa = null;
DSA dsa = null;

switch (cert.GetKeyAlgorithm())
{
case "1.2.840.113549.1.1.1":
rsa = RSA.Create();
break;
case "1.2.840.10045.2.1":
ecdsa = ECDsa.Create();
break;
case "1.2.840.10040.4.1":
dsa = DSA.Create();
break;
default:
throw new NotSupportedException($"The certificate key algorithm '{cert.GetKeyAlgorithm()}' is unknown");
}

AsymmetricAlgorithm anyAlg = rsa ?? ecdsa ?? (AsymmetricAlgorithm)dsa;
bool loaded = false;
int bytesRead;

using (rsa)
using (ecdsa)
using (dsa)
{
if (!loaded && rsa != null && kinds.HasFlag(KeyFileKinds.RsaPrivateKey))
{
try
{
rsa.ImportRSAPrivateKey(keyBytes, out bytesRead);
loaded = bytesRead == keyBytes.Length;
}
catch (CryptographicException)
{
}
}

if (!loaded && kinds.HasFlag(KeyFileKinds.Pkcs8))
{
try
{
anyAlg.ImportPkcs8PrivateKey(keyBytes, out bytesRead);
loaded = bytesRead == keyBytes.Length;
}
catch (CryptographicException)
{
}
}

if (!loaded && kinds.HasFlag(KeyFileKinds.EncryptedPkcs8))
{
try
{
// This assumes that the private key was already exported
// with the same password that the PFX will be exported with.
// Not true? Add a parameter :).
anyAlg.ImportEncryptedPkcs8PrivateKey(exportPassword, keyBytes, out bytesRead);
loaded = bytesRead == keyBytes.Length;
}
catch (CryptographicException)
{
}
}

if (!loaded)
{
throw new InvalidOperationException("Could not load the key as any known format.");
}

X509Certificate2 withKey;

if (rsa != null)
{
withKey = cert.CopyWithPrivateKey(rsa);
}
else if (ecdsa != null)
{
withKey = cert.CopyWithPrivateKey(ecdsa);
}
else
{
Debug.Assert(dsa != null);
withKey = cert.CopyWithPrivateKey(dsa);
}

using (withKey)
{
return withKey.Export(X509ContentType.Pfx, exportPassword);
}
}
}

更新(2020-10-09):之前的更新显示了 .NET Core 3.1 中更好的代码,但也显示了 .NET 5 中的一些前瞻性代码。​​如果证书文件位于PEM 格式(-----BEGIN CERTIFICIATE-----)并且 key 文件是 PEM 格式(BEGIN PRIVATE KEY/BEGIN RSA PRIVATE KEY/BEGIN EC PRIVATE KEY/BEGIN ENCRYPTED PRIVATE KEY) 那么 .NET 5 有一个更简单的方法:

using (X509Certificate2 certWithKey = X509Certificate2.CreateFromPemFile(certPath, keyPath))
{
return certWithKey.Export(X509ContentType.Pfx, exportPassword);
}

也可用作 CreateFromPem(loadedCertPem, loadedKeyPem)CreateFromEncryptedPem(loadedCertPem、loadedKeyPem、keyPassword)CreateFromEncryptedPemFile(certPath, keyPath, keyPassword).

关于c# - .NET Standard - 以编程方式将证书和私钥合并到 .pfx 文件中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44465574/

32 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com