BouncyCastle RSAPrivateKey to.NET RSAPrivateKey

Tworzę system dystrybucji certyfikatów, aby śledzić klientów i takie tam.

Co się dzieje to:

  • klient wysyła CSR na Serwer
  • serwer sprawdza i podpisuje certyfikat
  • serwer wysyła podpisany certyfikat do Klienta
  • Klient umieszcza podpisany certyfikat i klucz prywatny w Sklepie Windows.

Więc na kliencie dzieje się tak:

//Pseudo Server Object:
Server s = new Server();  

//Requested Certificate Name and things
X509Name name = new X509Name("CN=Client Cert, C=NL");  

//Key generation 2048bits
RsaKeyPairGenerator rkpg = new RsaKeyPairGenerator();
rkpg.Init(new KeyGenerationParameters(new SecureRandom(), 2048));
AsymmetricCipherKeyPair ackp = rkpg.GenerateKeyPair();  

//PKCS #10 Certificate Signing Request
Pkcs10CertificationRequest csr = new Pkcs10CertificationRequest("SHA1WITHRSA", name, ackp.Public, null, ackp.Private);  

//Make it a nice PEM thingie
StringBuilder sb = new StringBuilder();
PemWriter pemwrit = new PemWriter(new StringWriter(b));
pemwrit.WriteObject(csr);
pemwrit.Writer.Flush();
s.SendRequest(sb.ToSting());

Ok więc pominę serverside tylko zaufaj mi serwer podpisuje cert i odeślij go z powrotem do klient. To tam się zajmę akcją.

PemReader pr = new PemReader(new StringReader(b.ToString()));
X509Certificate cert = (X509Certificate)pr.ReadObject();  

//So lets asume I saved the AsymmetricCipherKeyPair (ackp) from before
//I have now the certificate and my private key;

//first I make it a "Microsoft" x509cert.
//This however does not have a PrivateKey thats in the AsymmetricCipherKeyPair (ackp)
System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);

//So here comes the RSACryptoServerProvider:
System.Security.Cryptography.RSACryptoServiceProvider rcsp = new System.Security.Cryptography.RSACryptoServiceProvider();  

//And the privateKeyParameters
System.Security.Cryptography.RSAParameters parms = new System.Security.Cryptography.RSAParameters();  

//now I have to translate ackp.PrivateKey to parms;
RsaPrivateCrtKeyParameters BCKeyParms = ((RsaPrivateCrtKeyParameters)ackp1.Private);  

//D is the private exponent
parms.Modulus   = BCKeyParms.Modulus.ToByteArray();
parms.P         = BCKeyParms.P.ToByteArray();
parms.Q         = BCKeyParms.Q.ToByteArray();
parms.DP        = BCKeyParms.DP.ToByteArray();
parms.DQ        = BCKeyParms.DQ.ToByteArray();
parms.InverseQ  = BCKeyParms.QInv.ToByteArray();
parms.D         = BCKeyParms.Exponent.ToByteArray();
parms.Exponent  = BCKeyParms.PublicExponent.ToByteArray();  

//Now I should be able to import the RSAParameters into the RSACryptoServiceProvider
rcsp.ImportParameters(parms);  

//<em><b>not really</b></em> This breaks says "Bad Data" and not much more. I'll Post the 
//stacktrace at the end  

//I open up the windows cert store because thats where I want to save it.
//Add it and save it this works fine without the privkey.
X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser);
store.Open(OpenFlags.MaxAllowed);
store.Add(netcert);
store.Close();

Teraz pewnie myślisz, że musi być coś nie tak po stronie serwera. Cóż, też tak myślałem, ale kiedy zrobiłem plik pfx z tego cert i zaimportowałem go ręcznie, działało dobrze ....

Jakoś jest różnica między. NET RSA privatekey a BouncyCastle RSA privatekey i nie mogę tego włożyć palcem.

Prawdopodobnie zasugerujesz zaimportowanie pfx, a następnie pobranie z niego klucza prywatnego poprzez X509Store. Próbowałem. : S i nie powiodło się. Tak szybko, jak spróbuję ExportParameters(true) prawdziwe oznacza w tym privateparameters. Mówi " klucz nie jest prawidłowy do użycia w określonym stanie.". Zobacz kompletny wyjątek na końcu.

Mam nadzieję, że niektórzy z was zabili tę świnię wcześniej lub może będą w stanie mi pomóc.

***Exceptions:***

System.Security.Cryptography.CryptographicException was unhandled
  Message="Key not valid for use in specified state.\r\n"
  Source="mscorlib"
  StackTrace:
       at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
       at System.Security.Cryptography.Utils._ExportKey(SafeKeyHandle hKey, Int32 blobType, Object cspObject)
       at System.Security.Cryptography.RSACryptoServiceProvider.ExportParameters(Boolean includePrivateParameters)
  InnerException: 

***And the other one:***

System.Security.Cryptography.CryptographicException was unhandled
  Message="Bad Data.\r\n"
  Source="mscorlib"
  StackTrace:
       at System.Security.Cryptography.CryptographicException.ThrowCryptogaphicException(Int32 hr)
       at System.Security.Cryptography.Utils._ImportKey(SafeProvHandle hCSP, Int32 keyNumber, CspProviderFlags flags, Object cspObject, SafeKeyHandle& hKey)
       at System.Security.Cryptography.RSACryptoServiceProvider.ImportParameters(RSAParameters parameters)
  InnerException: 
Author: albertjan, 2009-06-04

5 answers

ODPOWIEDŹ (od nazwy użytkownika) wskazuje właściwy kierunek: padding .

Bouncy-najnowsza wersja castle 'a z git' a ma następujący kod:

public static RSAParameters ToRSAParameters(RsaPrivateCrtKeyParameters privKey)
{
   RSAParameters rp = new RSAParameters();
   rp.Modulus = privKey.Modulus.ToByteArrayUnsigned();
   rp.Exponent = privKey.PublicExponent.ToByteArrayUnsigned();
   rp.P = privKey.P.ToByteArrayUnsigned();
   rp.Q = privKey.Q.ToByteArrayUnsigned();
   rp.D = ConvertRSAParametersField(privKey.Exponent, rp.Modulus.Length);
   rp.DP = ConvertRSAParametersField(privKey.DP, rp.P.Length);
   rp.DQ = ConvertRSAParametersField(privKey.DQ, rp.Q.Length);
   rp.InverseQ = ConvertRSAParametersField(privKey.QInv, rp.Q.Length);
   return rp;
}

private static byte[] ConvertRSAParametersField(BigInteger n, int size)
{
   byte[] bs = n.ToByteArrayUnsigned();
   if (bs.Length == size)
      return bs;
   if (bs.Length > size)
      throw new ArgumentException("Specified size too small", "size");
   byte[] padded = new byte[size];
   Array.Copy(bs, 0, padded, size - bs.Length, bs.Length);
   return padded;
}

Nb: ten kod Nie w wersji NuGet (2011) bouncy castle, lub w większości próbek kodu były parametry RSA są po prostu kopiowane.

Ten kod różni się od kodu, który można zobaczyć gdziekolwiek indziej, który zasadniczo kopiuje / wkleja kluczowe parametry i nie wykonuje dodatkowego kroku wypełniania.

 7
Author: JB.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-09 08:35:08

Dla twojej informacji, dodałem tę funkcjonalność do Org.BouncyCastle.Ochrona.Klasa DotNetUtilities; będzie w wydaniu 1.6, wkrótce.

 9
Author: Peter Dettman,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-02 15:11:02

Znalazłem!

Albo przynajmniej jej część:)

Jeśli chodzi o PrivateKey.ExportToParameters(true) nadal nie działa, ale ma to coś wspólnego z faktem, że klucz był 2048 bit. Bo kiedy zmieniłem go na 1024bit to działało. Więc jeśli ktoś się dowie, po co mnie informować.

Więc znowu to samo.
//BouncyCastle's Key objects
RsaPrivateCrtKeyParameters rpckp = ((RsaPrivateCrtKeyParameters)ackp.Private);

//.NET RSA Key objects
System.Security.Cryptography.RSACryptoServiceProvider rcsp = new System.Security.Cryptography.RSACryptoServiceProvider();
System.Security.Cryptography.RSAParameters parms = new System.Security.Cryptography.RSAParameters();

//So the thing changed is offcourse the ToByteArrayUnsigned() instead of
//ToByteArray()
parms.Modulus   = rpckp.Modulus.ToByteArrayUnsigned();
parms.P         = rpckp.P.ToByteArrayUnsigned();
parms.Q         = rpckp.Q.ToByteArrayUnsigned();
parms.DP        = rpckp.DP.ToByteArrayUnsigned();
parms.DQ        = rpckp.DQ.ToByteArrayUnsigned();
parms.InverseQ  = rpckp.QInv.ToByteArrayUnsigned();
parms.D         = rpckp.Exponent.ToByteArrayUnsigned();
parms.Exponent  = rpckp.PublicExponent.ToByteArrayUnsigned();

//So now this now appears to work.
rcsp.ImportParameters(parms);

Więc teraz mogę dodać pełny certyfikat do mojego sklepu:)

 6
Author: albertjan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-06-04 14:26:22

Chyba znalazłem rozwiązanie tego problemu. Nie ma to nic wspólnego z kluczem per, ale raczej z obiektem X509Certificate2, który musi być utworzony za pomocą x509keystorageflags./ Align = "left" /

W tym przypadku twój X509Certificate2 został utworzony tą metodą: System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);

Więc upewnij się, że przekazujesz flagę eksportowalną w konstruktorze X509Certificate2 w tej metodzie. W mojej sytuacji musiałem podpisać niektóre dane kluczem prywatnym znajdującym się w pliku PFX więc musiałem napisać to:

X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
X509Certificate2 cert = new X509Certificate2("my.pfx", "somepass", flags);

Teraz mogę zrobić
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);

HTH,

Stefan

 4
Author: stefann,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-13 22:51:52

Żadne z rozwiązań nie zadziałało. Ale zauważyłem, że wyjątek jest zawsze wyrzucany, gdy jedna z następujących tablic:

parms.Modulus   = rpckp.Modulus.ToByteArrayUnsigned();
parms.P         = rpckp.P.ToByteArrayUnsigned();
parms.Q         = rpckp.Q.ToByteArrayUnsigned();
parms.DP        = rpckp.DP.ToByteArrayUnsigned();
parms.DQ        = rpckp.DQ.ToByteArrayUnsigned();
parms.InverseQ  = rpckp.QInv.ToByteArrayUnsigned();
parms.D         = rpckp.Exponent.ToByteArrayUnsigned();
parms.Exponent  = rpckp.PublicExponent.ToByteArrayUnsigned();

Ma inny rozmiar niż jego sąsiad:

DP, DQ, InverseQ, P, Q

Lub podwójny rozmiar:

D, Modulus

Dla każdej z tych dwóch grup obliczyłem maksymalną długość i dodałem dodatkowe zera na początku każdej tablicy, aby uczynić je taką samą długością (taką samą dla każdej grupy). To działa, przypuszczam, że ImportParameters sprawdza, czy są tej samej długości (niestety nie mam dostępu do kodu ImportParameters, wygląda na to, że wywołuje jakąś natywną bibliotekę).

Używam BouncyCastle.Krypto.dll ver 1.7
 2
Author: username,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-05-28 16:00:56