【问题标题】:How to create X509Certificate2 with PrivateKey in .NET Standard 2.0/Core 2.1?如何在 .NET Standard 2.0/Core 2.1 中使用 PrivateKey 创建 X509Certificate2?
【发布时间】:2020-05-04 21:08:47
【问题描述】:

我们正在生成一些自签名证书以使用 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 应用程序中,我在上面指示的行中收到一条异常消息:

此平台不支持操作。

显然是this is expected behavior in .NET Core。我知道this question 中提到的CopyWithPrivateKey 方法,理论上我应该使用它。但是,this method is not supported in .NET Standard 2.0(请注意页面顶部指示重定向的错误)。此外,由于 .NET Framework 等一些其他依赖项,目前无法将 .NET Framework 应用程序转换为 .NET Core。根据this matrix,.NET Framework 根本不支持 .NET Standard 2.1,这意味着 我无法升级到 .NET Standard 2.1 并使用CopyWithPrivateKey

如何以与 .NET Core 兼容的方式在 .NET Standard 2.0 中使用私钥创建 X509Certificate2

【问题讨论】:

    标签: c# .net-core .net-standard-2.0


    【解决方案1】:

    经过大量挖掘,我找到的唯一解决方案是将证书转换为 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,而是使用它来创建等效的充气城堡类型以传递给它:Org.BouncyCastle.X509.X509Certificate bouncyCastleCert = certGenerator.Generate(signatureFactory);


    由于有关 Bouncy Castle 和 .NET 证书的文档充其量很少,以下是我在此过程中发现的一些怪癖:

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

    • 加载字节数组时必须使用X509KeyStorageFlags.Exportable,否则根据您使用证书的方式,您可能会收到“Key not valid for use in specified state”异常。这也意味着您必须提供密码,因为没有密码就不会过载,但是您可以使用任何旧字符串,因为它是临时密码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-05-19
      • 1970-01-01
      • 1970-01-01
      • 2020-07-04
      • 2022-01-15
      • 1970-01-01
      • 1970-01-01
      • 2021-05-29
      相关资源
      最近更新 更多