Reputation: 2091
I'm trying to manually set the properties of a RSAParameters
, but I'm getting all sorts of errors when I try to encrypt, decrypt or even instantiate the RSACryptoServiceProvider
.
If I set only the Exponent and the Modulus, I'm able to encrypt a message; but I when I try to decrypt it I get a Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException: 'Key does not exist
If I set all the properties, I get a System.Security.Cryptography.CryptographicException: 'The specified RSA parameters are not valid; both Exponent and Modulus are required fields.'
My question is why? What's wrong with my parameters?
I'm obviously not using random values for the fields; I copy-pasted their string representations from another project.
Below is the code I wrote, it's targeting .Net Core 2.1. I updated it to include this length restrictions
namespace PlayingWithCryptography {
using System;
using System.Linq;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
public static class Program {
private static void Main(string[] args) {
var msg = "lol";
var encodedMessage = Encoding.ASCII.GetBytes(msg);
var parameters = new RSAParameters();
parameters.P = BigInteger.Parse("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113").ToByteArray();
parameters.Q = BigInteger.Parse("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101").ToByteArray();
parameters.D = BigInteger.Parse("46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073").ToByteArray();
parameters.Modulus = BigInteger.Parse("109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413").ToByteArray();
parameters.Exponent = BigInteger.Parse("65537").ToByteArray();
parameters.DP = BigInteger.Parse("11141736698610418925078406669215087697114858422461871124661098818361832856659225315773346115219673296375487744032858798960485665997181641221483584094519937").ToByteArray();
parameters.DQ = BigInteger.Parse("4886309137722172729208909250386672706991365415741885286554321031904881408516947737562153523770981322408725111241551398797744838697461929408240938369297973").ToByteArray();
parameters.InverseQ = BigInteger.Parse("5610960212328996596431206032772162188356793727360507633581722789998709372832546447914318965787194031968482458122348411654607397146261039733584248408719418").ToByteArray();
Console.WriteLine($"M[0]={parameters.Modulus[0]}");
Console.WriteLine($"D.Length={parameters.D.Length}");
Console.WriteLine($"M.Length={parameters.Modulus.Length}");
Console.WriteLine($"E={parameters.Exponent[0]}");
Console.WriteLine($"P.Length={ parameters.P.Length}");
Console.WriteLine($"Q.Length={ parameters.Q.Length}");
Console.WriteLine($"DP.Length={ parameters.DP.Length}");
Console.WriteLine($"DQ.Length={ parameters.DQ.Length}");
Console.WriteLine($"InverseQ.Length={ parameters.InverseQ.Length}");
// Adding zeros coz https://stackoverflow.com/questions/42098493/decrypting-with-rsa-encryption-in-vb-net/42117655#42117655
parameters.D = new byte[] { 0 }.Concat(parameters.D).ToArray();
parameters.DQ = new byte[] { 0 }.Concat(parameters.DQ).ToArray();
parameters.InverseQ = new byte[] { 0 }.Concat(parameters.InverseQ).ToArray();
Console.WriteLine();
Console.WriteLine($"M[0]={parameters.Modulus[0]}");
Console.WriteLine($"D.Length={parameters.D.Length}");
Console.WriteLine($"M.Length={parameters.Modulus.Length}");
Console.WriteLine($"E[0]={parameters.Exponent[0]}");
Console.WriteLine($"P.Length={ parameters.P.Length}");
Console.WriteLine($"Q.Length={ parameters.Q.Length}");
Console.WriteLine($"DP.Length={ parameters.DP.Length}");
Console.WriteLine($"DQ.Length={ parameters.DQ.Length}");
Console.WriteLine($"InverseQ.Length={ parameters.InverseQ.Length}");
var csp = RSACryptoServiceProvider.Create();
csp.ImportParameters(parameters);
var encrypted = csp.Encrypt(
data: encodedMessage,
padding: RSAEncryptionPadding.OaepSHA256);
var decrypted = csp.Decrypt(
data: encrypted,
padding: RSAEncryptionPadding.OaepSHA256);
var decoded = Encoding.ASCII.GetString(decrypted);
Console.WriteLine(decoded);
Console.WriteLine("Done!");
}
}
}
Answered To anyone in a similar situation, please read (and vote) both James K Polk's answers and then the answer for this question
Upvotes: 0
Views: 4224
Reputation: 41967
I'm going to leave my other answer up for now, but I have reason to doubt the restrictions quoted therein. For the parameters in the question, my other answer suggests that RSAParameters.P
must be exactly 64 bytes. However that can't possibly work, as the P in the question requires at least 65 bytes to represent.
Instead, I'll suggest you use the RSA.FromXMLString()
method to import RSA public and private keys. That seems to be based on an external standard called XML Signature Syntax and Processing Version 1.1, and in particular this section with further details here. The basic idea is to encode the integers as big-endian byte arrays of minimal length -- no leading zeros -- and then base64-encode that.
Here is some lightly tested code to accomplish it. Note that C# and .NET are not my strong suit, so feel free to improve this. Note that, on my platform, OAEPSha1 was the only supported OAEP padding.
using System;
using System.Numerics;
using System.Security.Cryptography;
using System.Text;
namespace PlayingWithCryptography
{
public static class ConvertToRSAParameters
{
public static string ConvertXML(BigInteger e, BigInteger n)
{
var xml = new StringBuilder();
xml.AppendLine(WrapTags(BigToBase64(n), "Modulus"));
xml.AppendLine(WrapTags(BigToBase64(e), "Exponent"));
WrapTags(xml, "RSAKeyValue");
return xml.ToString();
}
public static string ConvertXML(BigInteger e, BigInteger n, BigInteger p, BigInteger q,
BigInteger d, BigInteger dp, BigInteger dq, BigInteger inverseQ)
{
var xml = new StringBuilder();
xml.AppendLine(WrapTags(BigToBase64(n), "Modulus"));
xml.AppendLine(WrapTags(BigToBase64(e), "Exponent"));
xml.AppendLine(WrapTags(BigToBase64(p), "P"));
xml.AppendLine(WrapTags(BigToBase64(q), "Q"));
xml.AppendLine(WrapTags(BigToBase64(d), "D"));
xml.AppendLine(WrapTags(BigToBase64(dp), "DP"));
xml.AppendLine(WrapTags(BigToBase64(dq), "DQ"));
xml.AppendLine(WrapTags(BigToBase64(inverseQ), "InverseQ"));
WrapTags(xml, "RSAKeyValue");
return xml.ToString();
}
private static string BigToBase64(BigInteger val)
{
var valBytes = val.ToByteArray();
int len = valBytes.Length;
while (valBytes[len - 1] == 0)
{
--len;
if (len == 0)
{
break;
}
}
Array.Resize(ref valBytes, len);
Array.Reverse(valBytes);
return System.Convert.ToBase64String(valBytes);
}
private static string WrapTags(string target, string tag)
{
return String.Format("<{0}>{1}</{0}>", tag, target);
}
private static StringBuilder WrapTags(StringBuilder target, string tag)
{
return target.Insert(0, String.Format("<{0}>", tag)).AppendFormat("</{0}>", tag);
}
private static void Main(string[] args)
{
var msg = "lol";
var encodedMessage = Encoding.ASCII.GetBytes(msg);
Console.WriteLine();
var publicRsa = RSA.Create();
publicRsa.FromXmlString(
ConvertToRSAParameters.ConvertXML(
BigInteger.Parse("65537"),
BigInteger.Parse("109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413")
)
);
var privateRsa = RSA.Create();
privateRsa.FromXmlString(
ConvertToRSAParameters.ConvertXML(
BigInteger.Parse("65537"),
BigInteger.Parse("109120132967399429278860960508995541528237502902798129123468757937266291492576446330739696001110603907230888610072655818825358503429057592827629436413108566029093628212635953836686562675849720620786279431090218017681061521755056710823876476444260558147179707119674283982419152118103759076030616683978566631413"),
BigInteger.Parse("14299623962416399520070177382898895550795403345466153217470516082934737582776038882967213386204600674145392845853859217990626450972452084065728686565928113"),
BigInteger.Parse("7630979195970404721891201847792002125535401292779123937207447574596692788513647179235335529307251350570728407373705564708871762033017096809910315212884101"),
BigInteger.Parse("46730330223584118622160180015036832148732986808519344675210555262940258739805766860224610646919605860206328024326703361630109888417839241959507572247284807035235569619173792292786907845791904955103601652822519121908367187885509270025388641700821735345222087940578381210879116823013776808975766851829020659073"),
BigInteger.Parse("11141736698610418925078406669215087697114858422461871124661098818361832856659225315773346115219673296375487744032858798960485665997181641221483584094519937"),
BigInteger.Parse("4886309137722172729208909250386672706991365415741885286554321031904881408516947737562153523770981322408725111241551398797744838697461929408240938369297973"),
BigInteger.Parse("5610960212328996596431206032772162188356793727360507633581722789998709372832546447914318965787194031968482458122348411654607397146261039733584248408719418")
)
);
var encrypted = publicRsa.Encrypt(
data: encodedMessage,
padding: RSAEncryptionPadding.OaepSHA1);
var decrypted = privateRsa.Decrypt(
data: encrypted,
padding: RSAEncryptionPadding.OaepSHA1);
var decoded = Encoding.ASCII.GetString(decrypted);
Console.WriteLine(decoded);
Console.WriteLine("Done!");
}
}
}
Upvotes: 2
Reputation: 41967
The details of what the fields of RSAParameters
must look like are given at the bottom of the page in the Feedback section by user bartonjs (who is also a stackoverflow user of the same name). Here is a copy of his comment:
The full rules of the arrays for RSAParameters values are as follows:
The other fields must either all be null (public key parameters), or all non-null (private key parameters). When the values are non-null:
Example: For a RSA key with a KeySize value of 2056 and having the standard public exponent value 0x010001, the array lengths are:
Public Key:
Private Key:
Note that BigInteger.ToByteArray()
returns the little-endian representation. This must be reversed to get the big-endian representation required by RSAParameters
.
Upvotes: 3