BouncyCastle RSAPrivateKey到.NET RSAPrivateKey

时间:2009-06-04 10:28:07

标签: .net cryptography rsa bouncycastle csr

我正在创建一个证书分发系统来跟踪客户和内容。

会发生什么:

  • 客户端将CSR发送到服务器
  • 服务器检查并签署证书
  • 服务器将签名证书发送到客户端
  • 客户端在Windows商店中放置签名证书和私钥。

所以在客户端会发生这种情况:

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

好的所以我会跳过服务器端只要相信我服务器签署证书并将其发送回客户端。那就是我会采取行动。

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

现在你可能认为服务器端肯定会出现问题。那就是我的想法,但当我从这个证书制作一个pfx文件并手工导入它时工作得很好....

不知何故,.NET RSA私钥和BouncyCastle RSA私钥之间存在差异,我不能指责它。

您可能会建议导入pfx,然后通过X509Store从中获取私钥。我试过了。 :S并且失败了。一旦我尝试ExportParameters(true)包含私人参数的真实立场。它说“密钥无法在指定状态下使用”。请参阅最后的完整例外情况。

我希望你们中的一些人以前杀过这头猪或者能够帮助我。

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

5 个答案:

答案 0 :(得分:9)

仅供参考,我已将此功能添加到Org.BouncyCastle.Security.DotNetUtilities类中;它将在1.6版即将推出。

答案 1 :(得分:9)

答案(来自用户名)指向正确的方向:填充

Bouncy-castle的最新版本来自git,其代码如下:

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:此代码不在nuget版本(2011)的充气城堡中,或者在大多数代码示例中都是简单复制的RSA参数。

此代码与您在其他任何地方可以看到的基本上复制/粘贴关键参数的代码不同,并且不执行额外的填充步骤。

答案 2 :(得分:6)

我找到了它!

或至少其中一部分:)

至于PrivateKey.ExportToParameters(true)仍然没有工作,但这有点事实,关键是2048位。因为当我把它改成1024bit它确实有效。所以,如果有人发现为什么让我发布。

所以我们再来一次。

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

所以现在我可以将完整的证书添加到我的商店:)

答案 3 :(得分:4)

我想我找到了解决这个问题的方法。它与key per无关,而是与X509Certificate2对象无关,该对象必须使用X509KeyStorageFlags.Exportable标志创建。

在这种情况下,您的X509Certificate2是通过此方法创建的: System.Security.Cryptography.X509Certificates.X509Certificate2 netcert = DotNetUtilities.ToX509Certificate(cert);

因此,请确保在该方法中传递X509Certificate2的构造函数中的可导出标志。我的情况我需要使用位于PFX文件中的私钥来签署一些数据,所以我不得不这样写:

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

现在我可以做到 RSACryptoServiceProvider rsa = (RSACryptoServiceProvider)cert.PrivateKey;
RSAParameters rsaParam = rsa.ExportParameters(true);

HTH,

的Stefan

答案 4 :(得分:2)

这两种解决方案都不适合我。但我注意到,当下列数组之一时,总是抛出异常:

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

与其邻居的大小不同:

DP, DQ, InverseQ, P, Q

或双倍大小:

D, Modulus

对于这两组中的每一组,我计算了最大长度,并在每个数组的开头添加了额外的零,以使它们具有相同的长度(每组相同)。 这是有效的,我想ImportParameters检查它们的长度是否相同(遗憾的是我无法访问ImportParameters代码,似乎它调用了一些本机库。)

我正在使用BouncyCastle.Crypto.dll ver 1.7