I have created xml key with c#:
RSACryptoServiceProvider RSA = new RSACryptoServiceProvider (2048);
string str = RSA.ToXmlString (true);
System.IO.File.WriteAllText (@"c:\path\to\keypair.xml", str);
Then using this online tool, I converted XML key to pem private key. Then using open SSL, I extracted the public key from the private key:
openssl rsa -in ./private.pem -pubout -out public.pem
Using the public.pem I am encrypting the secret string in Node:
const encryptWithRsa = (secretStringToEncrypt) => {
const publicKeyPath = path.join(appRoot, `${process.env.PUBLIC_KEY_PATH}`);
const publicKeyString = fs.readFileSync(publicKeyPath, 'utf8');
const buffer = Buffer.from(secretStringToEncrypt, 'utf8');
const encrypted = crypto.publicEncrypt({
key: publicKeyString,
padding: crypto.constants.RSA_PKCS1_OAEP_PADDING,
oaepHash: "sha256",
}, buffer);
const encryptedString = encrypted.toString('base64');
console.log("encryptWithRsa -> encryptedString", encryptedString);
return encryptedString;
};
Then I am trying to decrypt the string in c#:
public string RSADecrypt (string encryptedStringFromNode) {
string keyPair = System.IO.File.ReadAllText (@"c:\path\to\keypair.xml");
using var csp = new RSACryptoServiceProvider ();
ImportKey (csp, keyPair);
var decrypted = csp.Decrypt (Convert.FromBase64String (encryptedStringFromNode), true);
return Encoding.UTF8.GetString (
decrypted
);
}
public void ImportKey (RSA rsa, string xmlKey) {
var parameters = new RSAParameters ();
var xmlDoc = new XmlDocument ();
xmlDoc.LoadXml (xmlKey);
if (xmlDoc.DocumentElement.Name.Equals ("RSAKeyValue")) {
foreach (XmlNode node in xmlDoc.DocumentElement.ChildNodes) {
var value = ToByteArray (node.InnerText);
switch (node.Name) {
case nameof (parameters.Modulus):
parameters.Modulus = value;
break;
case nameof (parameters.Exponent):
parameters.Exponent = value;
break;
case nameof (parameters.P):
parameters.P = value;
break;
case nameof (parameters.Q):
parameters.Q = value;
break;
case nameof (parameters.DP):
parameters.DP = value;
break;
case nameof (parameters.DQ):
parameters.DQ = value;
break;
case nameof (parameters.InverseQ):
parameters.InverseQ = value;
break;
case nameof (parameters.D):
parameters.D = value;
break;
}
}
} else {
throw new Exception ("");
}
rsa.ImportParameters (parameters);
}
public byte[] ToByteArray (string input) {
return string.IsNullOrEmpty (input) ? null : Convert.FromBase64String (input);
}
But when I try to decrypt the string which is encrypted by Node.js, I get below error:
Exception has occurred: CLR/Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException
An exception of type 'Internal.Cryptography.CryptoThrowHelper.WindowsCryptographicException' occurred in
System.Security.Cryptography.Csp.dll but was not handled in user code: 'The parameter is incorrect.'
However, if I try to encrypt with the same public key on this online tool and then decrypt with my C# code, it works and I get the original unencrypted string.
Is there any issue with the Padding I am using for the RSA encryption in Node.js?
RSA.FromXMLString()in ImportKey?RSACryptoServiceProvidersupports besides PKCS#1v1.5 only OAEP with SHA1. In the NodeJS code OAEP with SHA256 is used, therefore decryption with the C# code fails. To solve the problem, SHA1 can be used in the NodeJS Code. Alternatively, an RSA implementation that supports SHA256 can be applied in the C# code. HereRSACngwould be a possibility, provided this is available in the .NET version you are using. Since both sides also support PKCS#1 v1.5, this is another alternative (see Michael Fehr's answer).