无法将生成的带有私钥的证书导出到.NET 4.0 / 4.5中的字节数组(Cannot export generated certificate with a private key to byte array in .NET 4.0/4.5)

10 IT屋

I need to export and import generated certificates with private keys to and from byte arrays, and I don't have any problems unless I use .NET framework 4.0 and 4.5. I'm generating self-signed certificates with BouncyCastle library and then converting them to .NET format (X509Certificate2 object). Unfortunately with the upgrade to a newest framework I cannot export private keys. Here is the code:

using System;
using System.Diagnostics;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Org.BouncyCastle.Asn1.X509;
using Org.BouncyCastle.Crypto;
using Org.BouncyCastle.Crypto.Generators;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Crypto.Prng;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.X509;

namespace X509CertificateExport
{
    class Program
    {
        static void Main(string[] args)
        {
            var certificate = Generate();
            var exported = certificate.Export(X509ContentType.Pfx);
            var imported = new X509Certificate2(exported, (string)null, X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

            Console.WriteLine("Certificate has private key: " + imported.HasPrivateKey);
            Console.ReadKey();
        }

        public static X509Certificate2 Generate()
        {
            var keyPairGenerator = new RsaKeyPairGenerator();
            var secureRandom = new SecureRandom(new CryptoApiRandomGenerator());
            keyPairGenerator.Init(new KeyGenerationParameters(secureRandom, 1024));
            var keyPair = keyPairGenerator.GenerateKeyPair();
            var publicKey = keyPair.Public;
            var privateKey = (RsaPrivateCrtKeyParameters)keyPair.Private;

            var generator = new X509V3CertificateGenerator();
            generator.SetSerialNumber(BigInteger.ProbablePrime(120, new Random()));
            generator.SetSubjectDN(new X509Name("CN=Test"));
            generator.SetIssuerDN(new X509Name("CN=Test"));
            generator.SetNotAfter(DateTime.Now + new TimeSpan(10, 10, 10, 10));
            generator.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7, 0, 0, 0)));
            generator.SetSignatureAlgorithm("MD5WithRSA");
            generator.SetPublicKey(publicKey);

            var newCert = generator.Generate(privateKey);
            var dotNetPrivateKey = ToDotNetKey(privateKey);
            var dotNetCert = new X509Certificate2(DotNetUtilities.ToX509Certificate(newCert));
            dotNetCert.PrivateKey = dotNetPrivateKey;

            return dotNetCert;
        }

        public static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
        {
            var rsaProvider = new RSACryptoServiceProvider();
            var parameters = new RSAParameters
            {
                Modulus = privateKey.Modulus.ToByteArrayUnsigned(),
                P = privateKey.P.ToByteArrayUnsigned(),
                Q = privateKey.Q.ToByteArrayUnsigned(),
                DP = privateKey.DP.ToByteArrayUnsigned(),
                DQ = privateKey.DQ.ToByteArrayUnsigned(),
                InverseQ = privateKey.QInv.ToByteArrayUnsigned(),
                D = privateKey.Exponent.ToByteArrayUnsigned(),
                Exponent = privateKey.PublicExponent.ToByteArrayUnsigned()
            };

            rsaProvider.ImportParameters(parameters);
            return rsaProvider;
        }
    }
}

After a closer look to the generated certificate I've noticed that PrivateKey.CspKeyContainerInfo.Exportable flag is true for .NET framework 3.5, but for later versions it throws:

'Exportable' threw an exception of type
'System.Security.Cryptography.CryptographicException' / Key does not exist

The only difference I see is in PrivateKey.CspKeyContainerInfo.m_parameters.Flags: .NET 3.5 - 'NoFlags'; .NET 4.5 - 'CreateEphemeralKey'. Documentation states that 'CreateEphemeralKey' creates a temporary key that is released when the associated RSA object is closed. It was introduced with 4.0 framework and didn't exist before. I've tried to get rid off this flag by creating CspParameters explicitly:

public static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
{
    var cspParams = new CspParameters
    {
        Flags = CspProviderFlags.UseMachineKeyStore
    };

    var rsaProvider = new RSACryptoServiceProvider(cspParams);
    // ...

but with no luck. 'CreateEphemeralKey' is added anyway, so I'm getting as a result UseMachineKeyStore | CreateEphemeralKey flags and I don't see how I can remove it. Is there a way I can ignore this flag and export the certificate with private key normally?

解决方案

I haven't noticed that CspKeyContainerInfo.CspParameters.KeyContainerName is empty after key creation in .NET 4.0 and .NET 4.5, but it was autogenerated in .NET 3.5. I've set a unique name for container and now I'm able to export the private key.

public static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
{
    var cspParams = new CspParameters
    {
          KeyContainerName = Guid.NewGuid().ToString(),
          KeyNumber = (int)KeyNumber.Exchange,
          Flags = CspProviderFlags.UseMachineKeyStore
    };

    var rsaProvider = new RSACryptoServiceProvider(cspParams);
    // ...

我需要将生成的带有私钥的证书导入和导出到字节数组中,除非使用.NET Framework 4.0和4.5,否则我没有任何问题。我正在使用 BouncyCastle 库生成自签名证书,然后将其转换为.NET格式(X509Certificate2目的)。不幸的是,升级到最新框架后,我无法导出私钥。这是代码:



 使用系统; 
使用System.Diagnostics;
使用System.Security.Cryptography;
使用System.Security.Cryptography.X509Certificates;
使用Org.BouncyCastle.Asn1.X509;
使用Org.BouncyCastle.Crypto;
使用Org.BouncyCastle.Crypto.Generators;
使用Org.BouncyCastle.Crypto.Parameters;
使用Org.BouncyCastle.Crypto.Prng;
使用Org.BouncyCastle.Math;
使用Org.BouncyCastle.Security;
使用Org.BouncyCastle.X509;

命名空间X509CertificateExport
{
class Program
{
static void Main(string [] args)
{
var certificate = Generate();
var export = certificate.Export(X509ContentType.Pfx);
var import = new X509Certificate2(exported,(string)null,X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet);

Console.WriteLine("证书具有私钥:" + import.HasPrivateKey);
Console.ReadKey();
}

公共静态X509Certificate2 Generate()
{
var keyPairGenerator = new RsaKeyPairGenerator();
var secureRandom = new SecureRandom(new CryptoApiRandomGenerator());
keyPairGenerator.Init(new KeyGenerationParameters(secureRandom,1024));
var keyPair = keyPairGenerator.GenerateKeyPair();
var publicKey = keyPair.Public;
var privateKey =(RsaPrivateCrtKeyParameters)keyPair.Private;

var generator = new X509V3CertificateGenerator();
generator.SetSerialNumber(BigInteger.ProbablePrime(120,new Random()));
generator.SetSubjectDN(new X509Name(" CN = Test"));
generator.SetIssuerDN(new X509Name(" CN = Test"));
generator.SetNotAfter(DateTime.Now + new TimeSpan(10,10,10,10));
generator.SetNotBefore(DateTime.Now.Subtract(new TimeSpan(7,0,0,0)));
generator.SetSignatureAlgorithm(" MD5WithRSA");
generator.SetPublicKey(publicKey);

var newCert = generator.Generate(privateKey);
var dotNetPrivateKey = ToDotNetKey(privateKey);
var dotNetCert = new X509Certificate2(DotNetUtilities.ToX509Certificate(newCert));
dotNetCert.PrivateKey = dotNetPrivateKey;

返回dotNetCert;
}

公共静态AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
{
var rsaProvider = new RSACryptoServiceProvider();
var参数=新的RSAParameters
{
模数= privateKey.Modulus.ToByteArrayUnsigned(),
P = privateKey.P.ToByteArrayUnsigned(),
Q =私钥。 Q.ToByteArrayUnsigned(),
DP = privateKey.DP.ToByteArrayUnsigned(),
DQ = privateKey.DQ.ToByteArrayUnsigned(),
InverseQ = privateKey.QInv.ToByteArrayUnsigned(),
D = privateKey.Exponent.ToByteArrayUnsigned(),
Exponent = privateKey.PublicExponent.ToByteArrayUnsigned()
};

rsaProvider.ImportParameters(参数);
return rsaProvider;
}
}
}


生成的证书我注意到PrivateKey.CspKeyContainerInfo.Exportable标志对于.NET Framework 3.5是正确的,但对于更高版本,它会抛出:



 "可导出"引发了类型为
的异常'System.Security.Cryptography.CryptographicException'/键不存在


我看到的唯一区别是在PrivateKey.CspKeyContainerInfo.m_parameters.Flags:
.NET 3.5-'NoFlags';
.NET 4.5-" CreateEphemeralKey"。
文档指出," CreateEphemeralKey"创建了一个临时密钥,该密钥在关联的RSA对象关闭时释放。它是在4.0框架中引入的,以前不存在。我试图通过显式创建CspParameters来摆脱此标志:



  public static AsymmetricAlgorithm ToDotNetKey(RsaPrivateCrtKeyParameters privateKey)
{
var cspParams =新的CspParameters
{
标志= CspProviderFlags.UseMachineKeyStore
};

var rsaProvider =新的RSACryptoServiceProvider(cspParams);
// ...


但没有运气。无论如何都添加了" CreateEphemeralKey",因此我得到 UseMachineKeyStore | CreateEphemeralKey 标志,但看不到如何删除它。有没有一种方法可以忽略该标志并正常导出带有私钥的证书?


解决方案

我没有注意到<在.NET 4.0和.NET 4.5中创建键后,code> CspKeyContainerInfo.CspParameters.KeyContainerName 为空,但在.NET 3.5中是自动生成的。我为容器设置了唯一的名称,现在我可以导出私钥了。



 公共静态AsymmetricAlgorithm ToDotNetKey( RsaPrivateCrtKeyParameters privateKey)
{
var cspParams = new CspParameters
{
KeyContainerName = Guid.NewGuid()。ToString(),
KeyNumber =(int)KeyNumber.Exchange ,
标志= CspProviderFlags.UseMachineKeyStore
} ;,

var rsaProvider =新的RSACryptoServiceProvider(cspParams);
// ...

本文地址:IT屋 » 无法将生成的带有私钥的证书导出到.NET 4.0 / 4.5中的字节数组