2013-05-07 24 views
9

Potrzebuję wyeksportować i zaimportować wygenerowane certyfikaty z kluczami prywatnymi do i od tablic bajtowych i nie mam żadnych problemów, chyba że korzystam z .NET framework 4.0 i 4.5. Generuję samopodpisane certyfikaty z biblioteką BouncyCastle, a następnie konwertuję je do formatu .net (obiekt X509Certificate2). Niestety po uaktualnieniu do najnowszej wersji nie mogę eksportować kluczy prywatnych. Oto kod:Nie można wyeksportować wygenerowanego certyfikatu z kluczem prywatnym do tablicy bajtów w .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; 
     } 
    } 
} 

Po bliższym przyjrzeniu do wygenerowanego certyfikatu Zauważyłem, że PrivateKey.CspKeyContainerInfo.Exportable flaga jest prawdziwe dla .NET Framework 3.5, ale w późniejszych wersjach rzuca:

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

Jedyną różnicą, jaką widzę, jest parametr PrivateKey.CspKeyContainerInfo.m_parameters.Flags: .NET 3.5 - 'NoFlags'; .NET 4.5 - "CreateEphemeralKey". Dokumentacja stwierdza, że ​​"CreateEphemeralKey" tworzy klucz tymczasowy, który jest zwalniany, gdy powiązany obiekt RSA jest zamknięty. Został wprowadzony w wersji 4.0 i wcześniej nie istniał. Próbowałem pozbyć się tej flagi, tworząc jawnie CspParameters:

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

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

ale bez powodzenia. Dodaje się "CreateEphemeralKey", więc otrzymuję w rezultacie UseMachineKeyStore | CreateEphemeralKey flagi i nie widzę sposobu, w jaki mogę je usunąć. Czy istnieje sposób, w jaki mogę zignorować tę flagę i normalnie wyeksportować certyfikat z kluczem prywatnym?

+0

możliwe duplikat [Wkładanie certyfikatu (z PrivateKey) w Root, sklep certyfikat LocalMachine nie w .NET 4] (http://stackoverflow.com/questions/3625624/inserting-certificate-with-privatekey-in -root-localmachine-certificate-store) – albertjan

Odpowiedz

12

Nie zauważyłem, że CspKeyContainerInfo.CspParameters.KeyContainerName jest pusty po utworzeniu klucza w .NET 4.0 i .NET 4.5, ale został automatycznie wygenerowany w .NET 3.5. Ustawiłem unikalną nazwę dla kontenera, a teraz mogę wyeksportować klucz prywatny.

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

Jesteś __the__ man! Dziękuję Ci! – albertjan

+0

To fajne @ nazwa użytkownika, nazwa użytkownika. –

+0

To "Napraw" również rozwiązuje błąd: Nieudane dodanie certyfikatu SSL, błąd: 1312 Określona sesja logowania nie istnieje. Mogło już zostać zakończone. – RcMan