Dot-Net

BouncyCastle RSAPrivateKey 到 .NET RSAPrivateKey

  • June 4, 2009

我正在創建一個證書分發系統來跟踪客戶和東西。

會發生什麼:

  • 客戶端向伺服器發送 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: 

答案(來自使用者名)指向正確的方向:padding

來自 git 的 Bouncy-castle 的最新版本具有以下程式碼:

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;
}

注意:此程式碼不在 bouncy castle 的 nuget 版本 (2011) 中,或者在大多數程式碼範例中只是簡單地複制了 RSA 參數。

此程式碼不同於您在其他任何地方看到的基本上複製/粘貼關鍵參數的程式碼,並且不執行額外的填充步驟。

引用自:https://stackoverflow.com/questions/949727