I have a decryption method written in Java. Java method uses new RSAPrivateKeySpec(BigInteger modulus, BigInteger privateExponent)
to create a private key from hex string and then Security.getProvider("BC"); Cipher instance = Cipher.getInstance("RSA/ECB/PKCS1Padding");
to decrypt.
Now I'm trying to implement the same method but in C#. Why my code throws Key does not exist
exception in Decrypt
? Also I am not sure do I need SwapBytes
for Java hex string? My C# code is:
byte[] dataToDecrypt = Convert.FromBase64String("AJqdug3s19c6bi8FaQrz0Q+qLnX/7mFmaNL2ztFeZyx0yeg9+lI48qFBRoS000JHaljlhVJpjACLr/qYk4ZdZjJHD0ENJmv+lf4TmTGQZpv3uiUQpG7QbVP7c56yWK4MgyMd8EdhJLcviobShkW3lI1a46C4vF+37WzH6qvgMuuUvtPi8mi9GvhPHHuocAh05s+c5YN2R78+tpkrs05EU6fiSn3iqRnrXv48O9kxQs9S/w4xaFLgfu2wbcgyI62s2Xbri1WdfSPUIYaLmv1EvVnzBPIty4Wclun25RmB8UtH5EZXpFOGF5ER8OztCcUeTAQYP9n9W2aQOqfpIjcNMA==");
RSAParameters rsaparam = new RSAParameters();
rsaparam.Modulus =
XUtils.SwapBytes(
XUtils.HexStringToByteArray(
"E86763F6B409ECCA94818F7438385D6C12237B5458974C5FD8573A62383DF55B7A39F73F27D5069981FB045042374A123828016AAD4BA94A77609624E44BD83D6F8CD4751D9F3E546D9E56BC55D8554ACFDF28FE9F97B01E53F9A4CE6DADFACC9D4499C186D41B65426206AC55DCBEC7F658104405E911D11034B462B3279E731B1D3E44244D3C60300261A82A5B28F01AD58F4A2D3B230501B00528956D820E1AA27FB25DC483376390A3C26C97AD84B744BDBB8F9C65E6CDA2A66AD12AE660D6CE26DC44F0AB055E6A7F7A19161F1ACFA09A14E34A555F43AC00B5BFAA834CCDB00AE5953696E38003EBEF1CA2531DF354929047858BFB50409FFEC2A1BF33"));
rsaparam.D =
XUtils.SwapBytes(
XUtils.HexStringToByteArray(
"7DF673DEE62C2EF489D3432E89BE3B1C752458171A413EFAFFE61F55D707F5F153E9111261037C253DA710EA7349465363AE833E0F995FB365CAAAF669EA95D48E9E7514D92E53792D44D1DC18673DAB63C99F8D8A708BDB94464DC638FA406220A76AF2D2A22A0B1314D06B1DCA9C7903132E15C06257EEC96AD78E5B6E10D967F480538FB9933E120750CDD31DC004BB94D0CD293C47EBC98D783B17C575DEC75751F99F82C026026773534B213446B14D4978EC44CD290908CBB3AF5FF35055A5698B096F66109B60C1A39D2A34FA791E421914E0137CCA3C95872FB39DD0586F2F4EFD6D1027596199F259D76FA3727B752EE76B4FE9C4A1BE18A6E67EC1"));
rsaparam.Exponent = new byte[] { 0x1, 0x0, 0x1 };
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider();
RSA.ImportParameters(rsaparam);
byte[] decryptedData = RSA.Decrypt(dataToDecrypt, false);
string s = Encoding.ASCII.GetString(decryptedData);
The native C# implementation requires not only modulus and private exponent D for a private key, but also the remaining RSA parameters P, Q, DP, DQ, and InverseQ (see here for more details).
These parameters are missing in your code, which is most likely the cause of the "Key does not exist"-exception.
However, BouncyCastle allows you to import the key by specifying only modulus and private exponent without having to provide the rest of the RSA parameters (be aware that your Java reference code also uses BouncyCastle, as Security.getProvider("BC")
shows):
using Org.BouncyCastle.Crypto.Encodings;
using Org.BouncyCastle.Crypto.Engines;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using System;
...
// Import key
BigInteger n = new BigInteger("E86763F6B409ECCA94818F7438385D6C12237B5458974C5FD8573A62383DF55B7A39F73F27D5069981FB045042374A123828016AAD4BA94A77609624E44BD83D6F8CD4751D9F3E546D9E56BC55D8554ACFDF28FE9F97B01E53F9A4CE6DADFACC9D4499C186D41B65426206AC55DCBEC7F658104405E911D11034B462B3279E731B1D3E44244D3C60300261A82A5B28F01AD58F4A2D3B230501B00528956D820E1AA27FB25DC483376390A3C26C97AD84B744BDBB8F9C65E6CDA2A66AD12AE660D6CE26DC44F0AB055E6A7F7A19161F1ACFA09A14E34A555F43AC00B5BFAA834CCDB00AE5953696E38003EBEF1CA2531DF354929047858BFB50409FFEC2A1BF33", 16);
BigInteger d = new BigInteger("7DF673DEE62C2EF489D3432E89BE3B1C752458171A413EFAFFE61F55D707F5F153E9111261037C253DA710EA7349465363AE833E0F995FB365CAAAF669EA95D48E9E7514D92E53792D44D1DC18673DAB63C99F8D8A708BDB94464DC638FA406220A76AF2D2A22A0B1314D06B1DCA9C7903132E15C06257EEC96AD78E5B6E10D967F480538FB9933E120750CDD31DC004BB94D0CD293C47EBC98D783B17C575DEC75751F99F82C026026773534B213446B14D4978EC44CD290908CBB3AF5FF35055A5698B096F66109B60C1A39D2A34FA791E421914E0137CCA3C95872FB39DD0586F2F4EFD6D1027596199F259D76FA3727B752EE76B4FE9C4A1BE18A6E67EC1", 16);
RsaKeyParameters rsaKeyParameters = new RsaKeyParameters(true, n, d);
// Decrypt using BC
RsaEngine rsaEngine = new RsaEngine();
Pkcs1Encoding pkcs1Encoding = new Pkcs1Encoding(rsaEngine);
pkcs1Encoding.Init(false, rsaKeyParameters);
byte[] ciphertext = Convert.FromBase64String("AJqdug3s19c6bi8FaQrz0Q+qLnX/7mFmaNL2ztFeZyx0yeg9+lI48qFBRoS000JHaljlhVJpjACLr/qYk4ZdZjJHD0ENJmv+lf4TmTGQZpv3uiUQpG7QbVP7c56yWK4MgyMd8EdhJLcviobShkW3lI1a46C4vF+37WzH6qvgMuuUvtPi8mi9GvhPHHuocAh05s+c5YN2R78+tpkrs05EU6fiSn3iqRnrXv48O9kxQs9S/w4xaFLgfu2wbcgyI62s2Xbri1WdfSPUIYaLmv1EvVnzBPIty4Wclun25RmB8UtH5EZXpFOGF5ER8OztCcUeTAQYP9n9W2aQOqfpIjcNMA==");
byte[] decrypted = pkcs1Encoding.ProcessBlock(ciphertext, 0, ciphertext.Length);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(decrypted)); // aum5d6qxevl1psj4bgkfiynchtr3ozw2:099a515a5cba1b81
If you want to use the native C# decryption methods, you have to reconstruct the missing values from modulus, private and public exponent.
Here you can find a corresponding implementation for Java, which can easily be converted to C# (also using BouncyCastle):
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
...
private static BigInteger FindFactor(BigInteger e, BigInteger d, BigInteger n)
{
BigInteger edMinus1 = e.Multiply(d).Subtract(BigInteger.One);
int s = edMinus1.GetLowestSetBit();
BigInteger t = edMinus1.ShiftRight(s);
for (int aInt = 2; true; aInt++)
{
BigInteger aPow = BigInteger.ValueOf(aInt).ModPow(t, n);
for (int i = 1; i <= s; i++)
{
if (aPow.Equals(BigInteger.One))
{
break;
}
if (aPow.Equals(n.Subtract(BigInteger.One)))
{
break;
}
BigInteger aPowSquared = aPow.Multiply(aPow).Mod(n);
if (aPowSquared.Equals(BigInteger.One))
{
return aPow.Subtract(BigInteger.One).Gcd(n);
}
aPow = aPowSquared;
}
}
}
private static RsaPrivateCrtKeyParameters GetRsaPrivateCrtKeyParameters(BigInteger e, BigInteger d, BigInteger n)
{
BigInteger p = FindFactor(e, d, n);
BigInteger q = n.Divide(p);
if (p.CompareTo(q) > 0)
{
BigInteger t = p;
p = q;
q = t;
}
BigInteger exp1 = d.Mod(p.Subtract(BigInteger.One));
BigInteger exp2 = d.Mod(q.Subtract(BigInteger.One));
BigInteger coeff = q.ModInverse(p);
RsaPrivateCrtKeyParameters rsaPrivateCrtKeyParameters = new RsaPrivateCrtKeyParameters(n, e, d, p, q, exp1, exp2, coeff);
return rsaPrivateCrtKeyParameters;
}
With this, decryption with the native C# methods is then possible as follows:
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using System;
using System.Security.Cryptography;
...
// Import key
BigInteger n = new BigInteger("E86763F6B409ECCA94818F7438385D6C12237B5458974C5FD8573A62383DF55B7A39F73F27D5069981FB045042374A123828016AAD4BA94A77609624E44BD83D6F8CD4751D9F3E546D9E56BC55D8554ACFDF28FE9F97B01E53F9A4CE6DADFACC9D4499C186D41B65426206AC55DCBEC7F658104405E911D11034B462B3279E731B1D3E44244D3C60300261A82A5B28F01AD58F4A2D3B230501B00528956D820E1AA27FB25DC483376390A3C26C97AD84B744BDBB8F9C65E6CDA2A66AD12AE660D6CE26DC44F0AB055E6A7F7A19161F1ACFA09A14E34A555F43AC00B5BFAA834CCDB00AE5953696E38003EBEF1CA2531DF354929047858BFB50409FFEC2A1BF33", 16);
BigInteger d = new BigInteger("7DF673DEE62C2EF489D3432E89BE3B1C752458171A413EFAFFE61F55D707F5F153E9111261037C253DA710EA7349465363AE833E0F995FB365CAAAF669EA95D48E9E7514D92E53792D44D1DC18673DAB63C99F8D8A708BDB94464DC638FA406220A76AF2D2A22A0B1314D06B1DCA9C7903132E15C06257EEC96AD78E5B6E10D967F480538FB9933E120750CDD31DC004BB94D0CD293C47EBC98D783B17C575DEC75751F99F82C026026773534B213446B14D4978EC44CD290908CBB3AF5FF35055A5698B096F66109B60C1A39D2A34FA791E421914E0137CCA3C95872FB39DD0586F2F4EFD6D1027596199F259D76FA3727B752EE76B4FE9C4A1BE18A6E67EC1", 16);
BigInteger e = new BigInteger("010001", 16);
RsaPrivateCrtKeyParameters rsaPrivateCrtKeyParameters = GetRsaPrivateCrtKeyParameters(e, d, n);
// convert to .net native types
RSAParameters rsaParameters = DotNetUtilities.ToRSAParameters(rsaPrivateCrtKeyParameters);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(rsaParameters);
// Decrypt using MS
byte[] ciphertext = Convert.FromBase64String("AJqdug3s19c6bi8FaQrz0Q+qLnX/7mFmaNL2ztFeZyx0yeg9+lI48qFBRoS000JHaljlhVJpjACLr/qYk4ZdZjJHD0ENJmv+lf4TmTGQZpv3uiUQpG7QbVP7c56yWK4MgyMd8EdhJLcviobShkW3lI1a46C4vF+37WzH6qvgMuuUvtPi8mi9GvhPHHuocAh05s+c5YN2R78+tpkrs05EU6fiSn3iqRnrXv48O9kxQs9S/w4xaFLgfu2wbcgyI62s2Xbri1WdfSPUIYaLmv1EvVnzBPIty4Wclun25RmB8UtH5EZXpFOGF5ER8OztCcUeTAQYP9n9W2aQOqfpIjcNMA==");
byte[] decrypted = rsa.Decrypt(ciphertext, false);
Console.WriteLine(System.Text.Encoding.UTF8.GetString(decrypted)); // aum5d6qxevl1psj4bgkfiynchtr3ozw2:099a515a5cba1b81