2009-06-04 8 views
14

Je crée un système de distribution de certificats pour garder une trace des clients et d'autres choses.BouncyCastle RSAPrivateKey à .NET RSAPrivateKey

Qu'est-ce qui se passe est:

  • client envoyer la RSE au serveur
  • Server vérifie et signe le certificat
  • Server envoie un certificat signé au client
  • client met certificat signé plus de clé privée dans Windows Store.

donc sur le client cela se produit:

//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 donc je vais sauter Serverside-moi confiance Tout le serveur signe le cert et l'envoyer au client. C'est là que je vais prendre l'action.

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(); 

Maintenant, vous pensez probablement que quelque chose ne va pas du côté serveur. Eh bien c'est ce que je pensais aussi mais quand j'ai fait un fichier pfx de ce cert et l'importé à la main il a bien fonctionné ....

D'une certaine manière, il y a une différence entre une clé privée .NET RSA et une clé privée BouncyCastle RSA et je peux Ne mets pas mon doigt dessus.

Vous allez probablement suggérer d'importer le pfx, puis d'en récupérer la clé privée via le X509Store. J'ai essayé. : S Et échoué. Dès que j'essaie de ExportParameters(true) le vrai signifie pour inclure les paramètres privés. Il dit "clé non valide pour une utilisation dans un état spécifié.". Voir pour l'exception complète à la fin.

J'espère que certains d'entre vous ont tué ce cochon avant ou pourraient être en mesure de m'aider.

***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: 
+0

Exactement le même problème ici, certaines clés ne sont pas acceptées .. Je ne peux pas comprendre pourquoi: http://stackoverflow.com/questions/28370414/import-rsa-key-from-bouncycastle-sometimes-throws-bad-data –

Répondre

7

La réponse (de nom d'utilisateur) pointe vers la bonne direction: rembourrage.

Bouncy-château dernière version de git a le code suivant:

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: Ce code pas dans la version NuGet (2011) du château gonflable, ou dans la plupart des exemples de code sont les paramètres RSA sont tout simplement copié.

Ce code est différent du code que vous pouvez voir n'importe où ailleurs, qui consiste essentiellement à copier/coller les paramètres clés et à ne pas effectuer l'étape de remplissage supplémentaire.

+1

Merci d'avoir répondu à cette question une fois pour toutes. J'ai écrit ma réponse il y a ______ années. À l'époque, il n'y avait aucun moyen de copier les clés du château gonflable à .net du tout. – albertjan

6

Je l'ai trouvé!

ou du moins une partie de celui-ci :)

Quant au PrivateKey.ExportToParameters(true) doens't encore le travail, mais cela a quelque chose todo avec le fait que la clé était de 2048 bits. Parce que quand je l'ai changé en 1024bit ça a marché. Donc, si jamais quelqu'un découvre pourquoi me tenir au courant.

Alors c'est reparti.

//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); 

Alors maintenant, je peux ajouter le certificat complet à mon magasin :)

+1

Je cherchais cette solution particulière pour la dernière semaine. Votre solution a fonctionné pour moi. Merci du partage :) – AbrahamJP

+0

Merci beaucoup pour le partage :) – wandos

7

Pour votre information, j'ai ajouté cette fonctionnalité à la classe Org.BouncyCastle.Security.DotNetUtilities; ce sera dans la version 1.6, bientôt disponible.

+0

Bon de voir un développeur de château gonflable ici ... Je vais lire chacune de vos réponses ... – LamonteCristo

+0

Awesome. Et c'est dans. La méthode est 'DotNetUtilities.ToRSAParameters (bouncyRsaParameters)' – McKay

+1

Malheureusement, la version dans la version actuelle (1.7) n'est pas 100% correcte (bien que vous puissiez voir seulement les problèmes avec certains paramètres RSA). Il est cependant corrigé dans CVS, si vous voulez soit construire à partir de la source, soit copier l'implémentation corrigée. –

4

Je pense avoir trouvé la solution à ce problème. Cela n'a rien à voir avec la clé per, mais plutôt avec l'objet X509Certificate2 qui doit être créé avec l'indicateur X509KeyStorageFlags.Exportable.

Dans ce cas, votre X509Certificate2 a été créé par cette méthode: System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);

alors assurez-vous passer le drapeau exportables dans le constructeur de la X509Certificate2 dans cette méthode.Je ma situation que je devais signer des données avec une clé privée située dans un fichier PFX je devais donc écrire ceci:

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

Maintenant, je peux faire
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);

HTH,

Stefan

+0

Merci beaucoup! J'ai lutté ce problème. – kuy

+0

La définition du drapeau a fonctionné pour moi, X509KeyStorageFlags flags = X509KeyStorageFlags.Exportable; – vetti

2

Aucune des solutions n'a fonctionné pour moi. Mais je l'ai remarqué que l'exception est toujours levée lorsque l'un des tableaux suivants:

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(); 

a une taille différente, alors son voisin:

DP, DQ, InverseQ, P, Q 

ou double taille:

D, Modulus 

Pour chacun de ces deux groupes, j'ai calculé la longueur maximale et ajouté des zéros supplémentaires au début de chaque tableau pour leur donner la même longueur (la même pour chaque groupe). Cela fonctionne, je suppose que ImportParameters vérifie qu'ils sont de la même longueur (malheureusement, je n'ai pas accès au code ImportParameters, il semble qu'il appelle une bibliothèque native).

J'utilise BouncyCastle.Crypto.dll ver 1.7

+1

Ceci est en effet un bug dans BouncyCastle; voir http://www.bouncycastle.org/jira/browse/BMA-97 – EricLaw

+0

Je peux voir que vous avez posté votre solution ici: http://www.bouncycastle.org/jira/browse/BMA-97. Pouvez-vous me dire si vous avez ajouté le rembourrage nul au début ou à la fin? –

Questions connexes