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?
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