【问题标题】:How to load a certificate with private key from PEM files in .NET standard如何从 .NET 标准中的 PEM 文件中加载带有私钥的证书
【发布时间】:2018-12-03 22:19:06
【问题描述】:

我正在尝试从 .NET 标准库中的 PEM 文件加载 X509Certificate2。

我使用 openssl 创建了一个自签名证书,如下所示:

openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -nodes -subj /CN=localhost -days 365

我将生成的 PEM 文件加载到项目内的嵌入式字符串资源中,并尝试使用以下代码加载它们:

private X509Certificate2 GetCertificate()
{
    try
    {
        byte[] pubPem = System.Text.Encoding.UTF8.GetBytes(Properties.Resources.DefaultPublicPem.Trim());
        var cert = new X509Certificate2(pubPem);
        var rsa = GetRSAFromPem(Properties.Resources.DefaultPrivatePem.Trim());
        cert.PrivateKey = rsa;
        return cert;
    }

    catch (Exception ex)
    {
        // ignore errors
        return null;
    }
}

public static RSA GetRSAFromPem(String pemstr)
{
    RSA rsaKey = RSA.Create();
    Func<RSA, RsaKeyParameters, RSA> MakePublicRCSP = (RSA rcsp, RsaKeyParameters rkp) =>
    {
        RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
        rcsp.ImportParameters(rsaParameters);
        return rsaKey;
    };

    Func<RSA, RsaPrivateCrtKeyParameters, RSA> MakePrivateRCSP = (RSA rcsp, RsaPrivateCrtKeyParameters rkp) =>
    {
        RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rkp);
        rcsp.ImportParameters(rsaParameters);
        return rsaKey;
    };

    PemReader reader = new PemReader(new StringReader(pemstr));
    object kp = reader.ReadObject();

    // If object has Private/Public property, we have a Private PEM
    var hasPrivate = kp.GetType().GetProperty("Private") != null;
    var isPrivate = kp is RsaPrivateCrtKeyParameters;
    return isPrivate ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)kp) : hasPrivate ? MakePrivateRCSP(rsaKey, (RsaPrivateCrtKeyParameters)(((AsymmetricCipherKeyPair)kp).Private)) : MakePublicRCSP(rsaKey, (RsaKeyParameters)kp);
}

我在 Android 上测试过,效果很好。

在 iOS 上我还没有测试,但在 UWP 上它失败了,我在尝试在证书上设置 PrivateKey 时收到 PlatformNotSupported 异常。

所以我想知道什么不受支持,以及我是否做得不对。

【问题讨论】:

    标签: x509certificate .net-standard


    【解决方案1】:

    根据this,.net core 不支持在现有证书上设置私钥:

    PrivateKey 属性将回到 netstandard2.0 (#12295),但是 它将为 .NET Core 设置。

    set_PrivateKey 在 .NET Framework 中有很多细微差别 (取决于你如何使用它,你最终可能会产生副作用 在机器重新启动后持续存在),并将该级别的细微差别反映到 Windows 以外的平台非常棘手,这就是为什么我们不 支持一下。

    在 .NET Core 上获得带有私钥的证书的唯一受支持方式 是通过 PFX/PKCS12 文件(或证书+密钥对已经是 通过 X509Store 关联)。

    因此解决此问题的一种方法是将公私对合并到 PFX 文件中,将其作为资源嵌入,并从该 PFX 初始化 X509Certificate2。

    我最终使用的另一种方法是在 UWP 上使用 RSACertificateExtensions.CopyWithPrivateKey 方法。

    所以基本上我最终构建了一个特定于平台的接口来加载证书。在 UWP 上它是这样实现的:

    public class UWPCertificateBuilder : ICertificateBuilder
    {
        public X509Certificate2 GetCertificate(X509Certificate2 cert, RSA key)
        {
            return cert.CopyWithPrivateKey(key);
        }
    }
    

    在 Android 上它是这样实现的:

    public class DroidCertificateBuilder : ICertificateBuilder
    {
        public X509Certificate2 GetCertificate(X509Certificate2 cert, RSA key)
        {
            cert.PrivateKey = key;
            return cert;
        }
    }
    

    【讨论】:

    • 是的,CopyWithPrivateKey 是要走的路。它适用于 .NET Core 和 .NET Framework,但显然不适用于 Mono。
    • CopyWithPrivateKey方法的定义是什么?
    • CopyWithPrivateKey() 将实例化一个新的 CSP。例如UseMachineKeyStore-flag 在操作过程中无法设置,导致其他进程无法访问密钥。
    【解决方案2】:

    您应该使用基类 RSA 而不是 RSACryptoServiceProvider。在 Windows 上的 .NET Standard 和 .NET Core 中,RSA 私钥解析为 RSACng,而不是旧的 RSACryptoServiceProvider

    更多详情请参阅此主题:X509AsymmetricSecurityKey.GetAsymmetricAlgorithm returns null after .Net 4.7.2 upgrade

    【讨论】:

    • 谢谢。因此,我按照您的建议更改了代码以使用 RSACng,但我仍然遇到相同的错误...我已编辑问题以反映此更改。
    猜你喜欢
    • 1970-01-01
    • 2012-01-16
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多