Pergunta

Estou criando um sistema de distribuição de certificado para acompanhar os clientes e outras coisas.

O que acontece é:

  • Enviar Cliente CSR ao servidor
  • O servidor verifica e certificado de sinais
  • O servidor envia certificado assinado ao Cliente
  • puts cliente assinou o certificado além de chave privada na loja do Windows.

Assim, no cliente isso acontece:

//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 Então eu vou pular serverside Apenas confie em mim os sinais de servidor o certificado e enviá-lo de volta para o cliente. Isso é onde eu vou pegar a ação.

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

Agora você está pensando provavelmente deve haver algo acontecendo de errado no lado do servidor. Bem isso é o que eu pensei também, mas Quando eu fiz um arquivo pfx desta cert e importados-lo à mão funcionou muito bem ....

De alguma forma, há uma diferença bewteen um .NET RSA privatekey e uma privatekey BouncyCastle RSA e eu não posso colocar o dedo sobre ele.

Você provavelmente irá sugerir para importar o pfx e, em seguida, obter a chave privada a partir dele através da X509Store. Eu tentei. : S e falhou. Assim que eu tento ExportParameters(true) os verdadeiros estandes para incluir privateparameters. Ele diz "Chave inválida para uso no estado especificado.". Veja por exceção completa no final.

Espero que alguns de vocês mataram este porco antes ou pode ser capaz de me ajudar.

***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: 
Foi útil?

Solução

A resposta (de nome de usuário) aponta para a direção certa:. estofamento

versão mais recente do Bouncy-castelo do git tem o código a seguir:

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 não na versão NuGet (2011) de castelo insuflável, ou na maioria dos exemplos de código foram parâmetros RSA são simplesmente copiados

Este código é diferente do código que você pode ver em qualquer outro lugar que basicamente copiar / colar os parâmetros-chave, e não executa a etapa cobertura extra.

Outras dicas

FYI, eu adicionei essa funcionalidade para a classe Org.BouncyCastle.Security.DotNetUtilities; será em versão 1.6, devido em breve.

Eu encontrei-o!

ou pelo menos parte dela:)

Quanto ao PrivateKey.ExportToParameters(true) Ainda doens't trabalho, mas isso não tem TODO algo com o fato de que a chave era de 2048 bits. Porque quando eu mudei para 1024bit não funcionou. Então, se alguém descobre por que manter-me informado.

Então, aqui vamos nós de novo.

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

Então agora eu posso adicionar o certificado completo a minha loja :)

Acho que encontrei a solução para este problema. Não tem nada a ver com a chave por, mas sim com o objeto X509Certificate2 que deve ser criado com a bandeira X509KeyStorageFlags.Exportable.

Neste caso seu X509Certificate2 foi criado por este método: System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);

Assim, certifique-se de passar o sinalizador exportável no construtor do X509Certificate2 nesse método. I minha situação eu precisava assinar alguns dados com uma chave privada localizada em um arquivo PFX então eu tive que escrever este:

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

Agora que posso fazer
RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);

HTH,

Stefan

Nenhuma das soluções trabalharam para mim. Mas tenho notado que a exceção é sempre acionada quando um dos seguintes matrizes:

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

tem um tamanho diferente, então seu vizinho:

DP, DQ, InverseQ, P, Q

ou dupla dimensão:

D, Modulus

Para cada um destes dois grupos I ter calculado o comprimento máximo e adicionou zeros adicionais no início de cada matriz a torná-los do mesmo comprimento (o mesmo para cada grupo). Isso funciona, eu acho que os controlos ImportParameters que eles são do mesmo comprimento (infelizmente eu não tenho acesso ao código ImportParameters, parece que ele chama alguma biblioteca nativa).

Eu estou usando BouncyCastle.Crypto.dll ver 1.7

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top