2013-05-07 90 views
9

我需要导出和导入生成的证书私钥与字节数组和我从来没有任何问题,除非我使用.NET Framework 4.0和4.5。我使用BouncyCastle库生成自签名证书,然后将它们转换为.net格式(X509Certificate2对象)。不幸的是,升级到最新的框架后,我无法导出私钥。下面是代码:无法导出生成的私钥证书到字节数组在.NET 4.0/4.5

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; 
     } 
    } 
} 

细看来生成的证书后,我注意到,PrivateKey.CspKeyContainerInfo.Exportable标志是.NET Framework 3.5的事实,但对于以后的版本,它抛出:

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

我看到的唯一区别是在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 = new CspParameters 
    { 
     Flags = CspProviderFlags.UseMachineKeyStore 
    }; 

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

但没有运气。 'CreateEphemeralKey'是无论如何都添加的,所以我得到UseMachineKeyStore | CreateEphemeralKey的标志,我看不出如何删除它。有没有什么办法可以忽略这个标志并正常用私钥导出证书?

+0

可能重复的[插入证书(用专用密钥)的根,LOCALMACHINE证书存储在.NET失败4](http://stackoverflow.com/questions/3625624/inserting-certificate-with-privatekey-in -root-localmachine-certificate-store) – albertjan 2014-08-04 13:56:33

回答

12

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

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); 
    // ... 
+0

你是__the__男人!谢谢! – albertjan 2014-08-04 13:58:18

+0

这是一个很酷的@username,用户名。 – 2016-01-13 06:55:11

+0

此“修复”还解决了错误: SSL证书添加失败,错误:1312 指定的登录会话不存在。它可能已经被终止。 – RcMan 2017-01-31 08:34:16