如何在.NET Standard 2.0/Core 2.1中创建带有私钥的X509Certificate2?

5
我们正在使用BouncyCastle生成一些用于测试的自签名证书,但是当我们尝试将私钥添加到证书时,代码会抛出异常。以下是相关代码:
private static X509Certificate2 CreateCertificate(string subject, DateTimeOffset notBefore, DataTimeOffset notAfter, string issuer, AsymmetricKeyParamter issuerPrivateKey)
{
        // Setup
        X509V3CertificateGenerator certGenerator = new X509V3CertificateGenerator();
        SecureRandom random = new SecureRandom(new CryptoApiRandomGenerator());

        RsaKeyPairGenerator keyPairGenerator = new RsaKeyPairGenerator();
        keyPairGenerator.Init(new KeyGenerationParameters(random, KeyStrength));

        // Randomly generate a serial number
        BigInteger serialNumber = BigIntegers.CreateRandomInRange(BigInteger.One, BigInteger.ValueOf(long.MaxValue), random);
        certGenerator.SetSerialNumber(serialNumber);

        // Set the issuer and subject names
        X509Name issuerName = new X509Name(issuer);
        X509Name subjectName = new X509Name(subject);
        certGenerator.SetIssuerDN(issuerName);
        certGenerator.SetSubjectDN(subjectName);

        // Set the validity period
        certGenerator.SetNotBefore(notBefore.UtcDateTime);
        certGenerator.SetNotAfter(notAfter.UtcDateTime);

        // Randomly generate the public key
        AsymmetricCipherKeyPair subjectKeyPair = keyPairGenerator.GenerateKeyPair();
        certGenerator.SetPublicKey(subjectKeyPair.Public);

        // Generate the signed certificate
        ISignatureFactory signatureFactory = new Asn1SignatureFactory(SHA256RSASignatureAlgorithm, issuerPrivateKey ?? subjectKeyPair.Private, random);
        X509Certificate2 certificate = new X509Certificate2(certGenerator.Generate(signatureFactory).GetEncoded());

        // Include the private key with the response
        // ERROR HERE!
        certificate.PrivateKey = DotNetUtilities.ToRSA(subjectKeyPair.Private as RsaPrivateCrtKeyParameters);

        return certificate;
}

这段代码位于一个针对.NET Standard 2.0的库中,该库是两个不同应用程序的依赖项:一个针对.NET Core 2.1,另一个针对.NET Framework 4.7.2。我相信在.NET Framework应用程序中这可以正常工作,但在.NET Core应用程序中,我在上面指示的行收到了一个异常,其消息为:“此平台不支持该操作。”

显然,这是.NET Core的预期行为。我知道CopyWithPrivateKey方法,如this question中所述,理论上我应该使用它。然而,此方法不受.NET Standard 2.0支持(请注意页面顶部的错误指示重定向)。此外,由于其他一些依赖关系是.NET Framework,因此目前无法将.NET Framework应用程序转换为.NET Core。根据this matrix,.NET Standard 2.1根本不受.NET Framework支持,这意味着我不能升级到.NET Standard 2.1并使用CopyWithPrivateKey

我如何在.NET Standard 2.0中创建一个带有私钥的X509Certificate2,以一种与.NET Core兼容的方式?

1个回答

7

经过多方搜寻,我找到的唯一解决方案是将证书转换为PKCS12格式的字节数组,然后附加私钥,并将其重新读入X509Certificate2对象。虽然这种方法不太好,但据我所知,在 .NET Core 中获取带有私钥的证书的唯一方法就是调用CopyWithPrivateKey(如问题中所述,不适用于 .NET Standard 2.0)或加载包含私钥的PKCS12数据。以下代码可实现此操作:

    private static X509Certificate2 AddPrivateKeyPlatformIndependent(Org.BouncyCastle.X509.X509Certificate bouncyCastleCert, AsymmetricKeyParameter privateKey)
    {
        string alias = bouncyCastleCert.SubjectDN.ToString();
        Pkcs12Store store = new Pkcs12StoreBuilder().Build();

        X509CertificateEntry certEntry = new X509CertificateEntry(bouncyCastleCert);
        store.SetCertificateEntry(alias, certEntry);

        // TODO: This needs extra logic to support a certificate chain
        AsymmetricKeyEntry keyEntry = new AsymmetricKeyEntry(privateKey);
        store.SetKeyEntry(alias, keyEntry, new X509CertificateEntry[] { certEntry });

        byte[] certificateData;
        string password = GenerateRandomString();
        using (MemoryStream memoryStream = new MemoryStream())
        {
            store.Save(memoryStream, password.ToCharArray(), new SecureRandom());
            memoryStream.Flush();
            certificateData = memoryStream.ToArray();
        }

        return new X509Certificate2(certificateData, password, X509KeyStorageFlags.Exportable);
    }

请使用这行代替问题代码中的最后两行,而且不要使用certGenerator创建X509Certificate2,而是使用这个创建相应的Bouncy Castle类型以传递给它:Org.BouncyCastle.X509.X509Certificate bouncyCastleCert = certGenerator.Generate(signatureFactory);


由于关于Bouncy Castle和.NET证书的文档最多只能算是很少,因此在此过程中我发现了一些特殊情况:

  • PKCS12容器中证书条目和密钥条目的别名必须相同。否则,在尝试使用私钥时会出现“密钥集不存在”的异常。

  • 加载字节数组时必须使用X509KeyStorageFlags.Exportable,否则根据您如何使用证书,可能会出现“密钥无法用于指定状态”的异常。这还意味着您必须提供密码,因为没有不带密码的重载。但是,您可以使用任何字符串作为临时密码。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接