librería - BouncyCastle RSAPrivateKey a.NET RSAPrivateKey
librería bouncycastle (5)
¡Lo encontré!
O al menos parte de eso :)
En cuanto a PrivateKey.ExportToParameters(true)
Todavía no funciona, pero esto tiene algo que ver con el hecho de que la clave era 2048 bit. Porque cuando lo cambié a 1024 bits funcionó. Entonces, si alguien alguna vez descubre por qué me mantiene informado.
Aquí vamos de nuevo.
//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);
Así que ahora puedo agregar el Certificado completo a mi tienda :)
Estoy creando un sistema de distribución de certificados para hacer un seguimiento de los clientes y esas cosas.
Lo que ocurre es:
- Cliente envía CSR al servidor
- Certificado de comprobaciones y firma del servidor
- El servidor envía el certificado firmado al cliente
- El cliente coloca el certificado firmado más la clave privada en la tienda de Windows.
Entonces en el cliente esto sucede:
//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, me saltearé el servidor. Solo confíen en que el servidor firma el certificado y se lo envía al cliente. Ahí es donde voy a retomar la acción.
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();
Ahora probablemente estés pensando que debe haber un problema en el lado del servidor. Bueno, eso es lo que yo también pensé, pero cuando hice un archivo pfx de este certificado y lo importé a mano, funcionó bien ...
De alguna manera hay una diferencia entre una clave privada .NET RSA y una clave privada BouncyCastle RSA y no puedo entenderlo.
Probablemente sugiera importar el pfx y luego obtener la clave privada a través del X509Store. Lo intenté. : S Y falló. Tan pronto como trato de ExportParameters(true)
lo cierto es que incluye los parámetros privados. Dice "La clave no es válida para usar en un estado específico". Consulte la excepción completa al final.
Espero que algunos de ustedes hayan matado a este cerdo antes o puedan ayudarme.
***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:
FYI, agregué esta funcionalidad a la clase Org.BouncyCastle.Security.DotNetUtilities; estará en la versión 1.6, próximamente.
Creo que encontré la solución a este problema. No tiene nada que ver con la clave per, sino con el objeto X509Certificate2 que debe crearse con el indicador X509KeyStorageFlags.Exportable.
En este caso, su X509Certificate2 se creó con este método: System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);
Así que asegúrese de pasar el indicador exportable en el constructor del X509Certificate2 en ese método. En mi situación, necesitaba firmar algunos datos con una clave privada ubicada en un archivo PFX, así que tuve que escribir esto:
X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable;
X509Certificate2 cert = new X509Certificate2("my.pfx", "somepass", flags);
Ahora puedo hacer
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);
HTH,
Stefan
Ninguna de las soluciones funcionó para mí. Pero me he dado cuenta de que la excepción siempre se produce cuando una de las siguientes matrices:
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();
tiene un tamaño diferente al de su vecino:
DP, DQ, InverseQ, P, Q
o doble tamaño:
D, Modulus
Para cada uno de estos dos grupos he calculado la longitud máxima y agregado ceros adicionales al comienzo de cada matriz para hacerlos de la misma longitud (lo mismo para cada grupo). Esto funciona, supongo que ImportParameters
comprueba que tienen la misma longitud (desafortunadamente no tengo acceso al código ImportParameters
, parece que llama a alguna biblioteca nativa).
Estoy usando BouncyCastle.Crypto.dll ver 1.7
La respuesta (desde el nombre de usuario) apunta a la dirección correcta: relleno .
La última versión de Bouncy-castle de git tiene el siguiente código:
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: Este código no está en la versión nuget (2011) del castillo hinchable, o en la mayoría de los ejemplos de código donde los parámetros RSA simplemente se copian.
Este código es diferente del código que puede ver en cualquier otro lugar, que básicamente copia / pega los parámetros clave, y no realiza el paso adicional de relleno.