我有一个用Java编写的解密方法。 Java 方法使用
new RSAPrivateKeySpec(BigInteger modulus, BigInteger privateExponent)
从十六进制字符串创建私钥,然后使用 Security.getProvider("BC"); Cipher instance = Cipher.getInstance("RSA/ECB/PKCS1Padding");
进行解密。
现在我正在尝试用 C# 实现相同的方法。为什么我的代码在
Key does not exist
中抛出 Decrypt
异常?另外我不确定 Java 十六进制字符串是否需要 SwapBytes
?我的 C# 代码是:
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);
据我所知,原生 C# 实现不仅需要私钥情况下的模数和私有指数 D,还需要其余参数 P、Q、DP、DQ 和 InverseQ(另请参阅here 和 for参数概述此处)。
但是你可以使用C#/BouncyCastle进行解密(在Java代码中BouncyCastle显然也适用,因为
Security.getProvider("BC")
):
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);// new BigInteger(1, nHex);
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
如果您想使用native C# 解密方法,您必须根据模数、私有和公共指数重建缺失值。 在这里你可以找到对应的Java实现,它可以很容易地转换为C#(但是,这个解决方案还需要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;
}
这样,就可以使用本机 C# 方法进行解密,如下所示:
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);// new BigInteger(1, nHex);
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