Cannot decrypt C# encrypted message using node-rsa npm - c#

I've got a server written in node.js that I want to receive encrypted messages from a C# app. I tried to implement RSA firstly for this task, but with no avail, because I wasn't able to decrypt the message after being encrypted by the C# app using the keys generated by the server.
I've tried using the node-rsa npm for generating the keys on the server side and got this error when trying to decrypt the messages
"Error during decryption (probably incorrect key). Original error:
Error: Incorrect data or key".
I've also tried the crypto module for decrypting, but received this error instead
"error:0406506C:rsa routines:rsa_ossl_private_decrypt:data greater
than mod len".
The C# app is a console app as it is merely used for testing the encryption, which uses the RSAServiceProvider for importing the keys generated by the server(which are stored in the pem format), encrypting the message(or the key, if I am going to implement AES as well) and then sending it back to the server by writing it in a file.
This is the rsa-wrapper.js file which contains the rsa implementation:
const NodeRSA = require('node-rsa');
const fs = require('fs');
var key;
var generate = function () {
key = new NodeRSA();
key.generateKeyPair(2048, 65537);
fs.writeFileSync('./keys/public-key.pem', key.exportKey('pkcs8-public-pem'));
};
var encrypt = function (message) {
let encrypted = key.encrypt(message, 'base64');
return encrypted;
};
var decrypt = function (cipherText) {
let decrypted = key.decrypt(cipherText, 'utf8');
return decrypted;
}
module.exports.generate = generate;
module.exports.test = test;
module.exports.encrypt = encrypt;
module.exports.decrypt = decrypt;
This is the the app.js file which simply contains the GET functions for testing the encryption:
const express = require('express');
const rsaWrapper = require('./components/rsa-wrapper.js');
const app = express();
const fs = require('fs');
app.listen(3000);
app.get('/', function(req, res) {
console.log('Working');
res.render('index.ejs');
});
app.get('/generate', function(req, res) {
rsaWrapper.generate();
console.log('The keys have been generated successfully.');
res.render('index.ejs');
});
app.get('/decrypt', function(req, res) {
fs.readFile('./cipher-text.txt', (err, data) => {
if (err) throw err;
console.log('The original message is:' + rsaWrapper.decrypt(data));
});
res.render('index.ejs');
});
This is the console app written in C# for encrypting the message using the public key generated by the server:
static void Main(string[] args) {
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
RSAParameters rsaParam = rsa.ExportParameters(false);
String publicKey = File.ReadAllText(#"D:\NodeProjects\Test project\keys\public-key.pem").Replace("-----BEGIN PUBLIC KEY-----", "").Replace("-----END PUBLIC KEY-----", "");
publicKey = publicKey.Replace("\n", String.Empty);
publicKey = publicKey.Replace("\r", String.Empty);
publicKey = publicKey.Replace("\t", String.Empty);
rsaParam.Modulus = Convert.FromBase64String(publicKey);
byte[] intBytes = BitConverter.GetBytes(65537);
if (BitConverter.IsLittleEndian)
Array.Reverse(intBytes);
rsaParam.Exponent = intBytes;
rsa.ImportParameters(rsaParam);
string msg = ".";
byte[] encrypted = rsa.Encrypt(Encoding.UTF8.GetBytes(msg), true);
string cipherText = Convert.ToBase64String(encrypted);
File.WriteAllText(#"D:\NodeProjects\Test project\cipher-text.txt", cipherText);
Console.WriteLine(publicKey);
Console.WriteLine(cipherText);
Console.ReadLine();
}
And this is the second rsa-wrapper.js that I tried using, but also resulting in a failure:
const rsaWrapper = {};
const fs = require('fs');
const NodeRSA = require('node-rsa');
const crypto = require('crypto');
// load keys from file
rsaWrapper.initLoadServerKeys = () => {
rsaWrapper.serverPub = fs.readFileSync('./keys/public-key.pem');
rsaWrapper.serverPrivate = fs.readFileSync('./keys/private-key.pem');
};
rsaWrapper.generate = () => {
let key = new NodeRSA();
key.generateKeyPair(2048, 65537);
fs.writeFileSync('./keys/private-key.pem', key.exportKey('pkcs8-private-pem'));
fs.writeFileSync('./keys/public-key.pem', key.exportKey('pkcs8-public-pem'));
return true;
};
rsaWrapper.serverExampleEncrypt = () => {
console.log('Server public encrypting');
let enc = rsaWrapper.encrypt(rsaWrapper.serverPub, 'Server init hello');
console.log('Encrypted RSA string ', '\n', enc);
let dec = rsaWrapper.decrypt(rsaWrapper.serverPrivate, enc);
console.log('Decrypted RSA string ...');
console.log(dec);
};
rsaWrapper.encrypt = (publicKey, message) => {
let enc = crypto.publicEncrypt({
key: publicKey,
padding: crypto.RSA_PKCS1_OAEP_PADDING
}, Buffer.from(message));
return enc.toString('base64');
};
rsaWrapper.decrypt = (privateKey, message) => {
let enc = crypto.privateDecrypt({
key: privateKey,
padding: crypto.RSA_PKCS1_OAEP_PADDING
}, Buffer.from(message, 'base64'));
return enc.toString();
};
module.exports = rsaWrapper;

Related

Decrypt and verify hash for SHA256 - c#

I am trying to decrypt and validate the hash but at times of decrypting, it throws me the error 'Key does not exist' and at the time of validating the hash return False
https://payvyne.readme.io/docs/webhooks
Signature:
HEjoCsghC9X0slrE2DprptDLYdoA7jaw4Jl7vpJVxzx9GNJEiO3pYGLDPhLmVqk98QJJ/FuiS5J+fvp+msr3Y8aFzKqjRQXj5TBELT38N+A7I8y3Vc0mgeR0aDMx7I83yhfkcoyhdiGJibzqQ5SYFZ0nnEVHYXheLUlga45yg/McDICtMm6lhnrPWEuHzoZTQkhsrLN/1W1PtLjJ2DickWB78PmhpeflL2Cpe6qS3qCclqFGZ7HIl9OoxU4WXpTYgxw7eixAKB7apFdFqea4BnGravfENNl97pOBuU6fRof4KtMczVagQw3QnxFD3BBtpTepRaT+jHY8wStXUG1bxllH32WiA9CVcpY4mxKhpxzQ8YD0b+3OgkpzZYS+BVVAdVazMJeEAw7v/zaxpjbR+Zo5l9vOLdyatwM75qpwMoKnMeKJHeRytEOK54al49OHiaE+v1OkOhJA0zh5nLzEIZanIdf+hXHDz3Euecs/p0cABiFNmhzYY5fl8qEytK6j2CjXQOYgljG5dqPm7M9CW36ntZTDaIEVWql3jdi9frxc4/82w1jhROFL0pBG1zz8nimAEesB1AaxmNqW7BIxULweX7eaReeo/dIqDSbmFuT+TikPQo4XRtmpDqO37Y9P6q7ZXtHOFopSaykHUHs+NgrKlBJMM5ADg5bHWm2Qows=
Public key:
pA6ULfXWrIMq-qvxn_0CykoStq0ZMYm63lHsuXTsE4q4tgekLJDW2Lnf35ilbFU_vybBdyeJAphpsYc4P0eJBt_z2T62HAV3gnwp_GU6hWIo8faK31TSXIrLmGjZlAVynAxjFYZoNxMeZuwEXpxG4bRGs58P7XSx1fAzedX6oGIlcSLljKH4I1BHt6gJhPIHYNXQzq_a0hX54C1m1VDVP_kot8ui1YKZil_riROK_Xk4ktnOTAqXo9z4uNBqzzH2k0J2YNiCb8VOdbp7kjmH9sPLI-jb-ociy0wSkGZc1e8saGIkkSm4eUASvX_M_TTDD99OrgoIS2Vx07Tw4lK5yd28EMVBUzy2OypuPVf9PyoDGv_4241x5PpJsA9IKocD7AgwxJ3E7FBFhvuSP8c5wspkbQxBwv5nnk2zAxuZsiJeK0o3JSxjkZJEkeVY4mA3VV9SvSXEKAFg2h9J3CR9PTwrZoVBruycVtWJ4it5jroXff-aGlLoRAO0g3gtfjkJb3tw6SJTFOA49iJci76Mj8Adz3eeEEGxTxfDzh_lq0jXxTk7cQSaR2_ChYLHaoorrrFmAvWgDH_lSvlISIgey-SzUoJM9RAy4gVFdmg-XCQQlpMh_d1-IACO3EfBvYKWE-6uGIqx1nZhn9WIDdSqMp6940xRxl0vQy8vYCQ5q8U
Data for Sign in string:
{"type":"PAYMENT_STATUS_CHANGE","paymentId":"1c6e834f074ec941","status":"FAILED","timestamp":1652688286662,"amount":"164.69","currency":"GBP","description":"This is test payment","paymentType":"ONE_OFF","bankName":"Diamond bank","destinationAccount":"GBP2","createdAt":"2022-05-16T08:04:32.994","updatedAt":"2022-05-16T08:04:46.662","customerReference":"1199","refundedAmount":"0.00"}
Expo (exponent):
AQAB
Below is the code to Decrypt the signature using public key.
public static void DecryptUsingPublicKey(string publicKey, string expo, string signature)
{
var modulus = ConvertToBase64(publicKey);
var exponent = Convert.FromBase64String(expo);
RSACryptoServiceProvider csp = new RSACryptoServiceProvider(2048);
var _publicKey = csp.ExportParameters(false);
_publicKey.Modulus = modulus;
_publicKey.Exponent = exponent;
csp.ImportParameters(_publicKey);
var dataBytes = ConvertToBase64(signature);
var plainText = csp.Decrypt(dataBytes, false);
var returnData = Encoding.Unicode.GetString(plainText);
Console.WriteLine($"value: {returnData}");
}
Below is the code for Verify signature using public key
public static void VerifySignature(string signature, string pKey, string dataForSign)
{
string pKeyNew = pKey;
pKeyNew = pKeyNew.Replace("_", "/").Replace("-", "+");
string publicKey = $"<RSAKeyValue><Modulus>{pKeyNew}==</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
var encoder = new UTF8Encoding();
byte[] dataForSignAsBytes = encoder.GetBytes(dataForSign);
byte[] signatureAsBytes = ConvertToBase64(signature);
RSACryptoServiceProvider rsaCryptoServiceProvider = new RSACryptoServiceProvider();
rsaCryptoServiceProvider.FromXmlString(publicKey);
var hashData = SHA256.Create().ComputeHash(dataForSignAsBytes);
var result1 = rsaCryptoServiceProvider.VerifyData(dataForSignAsBytes, CryptoConfig.MapNameToOID("SHA256"), signatureAsBytes);
var result2 = rsaCryptoServiceProvider.VerifyHash(hashData, CryptoConfig.MapNameToOID("SHA256"), signatureAsBytes);
var result3 = rsaCryptoServiceProvider.VerifyHash(hashData, signatureAsBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
var result4 = rsaCryptoServiceProvider.VerifyData(dataForSignAsBytes, signatureAsBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
Console.WriteLine(result1);
Console.WriteLine(result2);
Console.WriteLine(result3);
Console.WriteLine(result4);
}
ConvertToBase64 function
public static byte[] ConvertToBase64(string data)
{
byte[] cyperBuffer;
string dataNew = data;
dataNew = dataNew.Replace("_", "/").Replace("-", "+");
try
{
if (dataNew.Substring(dataNew.Length - 1) != "=")
{
dataNew += "=";
}
cyperBuffer = Convert.FromBase64String(dataNew);
}
catch
{
dataNew += "=";
try
{
cyperBuffer = Convert.FromBase64String(dataNew);
}
catch
{
//If any error occured while convert to base64 then append '=' at the end.
dataNew += "=";
cyperBuffer = Convert.FromBase64String(dataNew);
}
}
return cyperBuffer;
}
This is a conversion mistake; you need to decode the base 64 signature, not encode the signature, so the following line is wrong:
byte[] signatureAsBytes = ConvertToBase64(signature);
it should be something like:
byte[] signatureAsBytes = ConvertFromBase64(signature);
Decryption is modular exponentiation with a private key. Furthermore, encryption normally uses a different padding scheme than signature generation, so you'd expect that the unpadding would fail if you try and decrypt. Only verification is possible.

Decrypt RSA encrypted value generated from .net in Android

I have gone through many posts here but did not find the right solution. I want to decrypt a value encrypted in c# .net from Android.
I have successfully decrypted in .net platform using the following code snippet
public static void Main()
{
string _privateKey = Base64Decode("myprivatekey");
var rsa = new RSACryptoServiceProvider();
string data = "198,47,144,175,154,47,194,175,242,41,212,150,220,177,198,161,236,36,197,62,18,111,21,244,232,245,90,234,195,169,141,195,139,199,131,163,26,124,246,50,102,229,73,148,18,110,170,145,112,237,23,123,226,135,158,206,71,116,9,219,56,96,140,19,180,192,80,29,63,160,43,127,204,135,155,67,46,160,225,12,85,161,107,214,104,218,6,220,252,73,252,92,152,235,214,126,245,126,129,150,49,68,162,120,237,246,27,25,45,225,106,201,251,128,243,213,250,172,26,28,176,219,198,194,7,202,34,210";
var dataArray = data.Split(new char[] { ',' });
byte[] dataByte = new byte[dataArray.Length];
for (int i = 0; i < dataArray.Length; i++)
{
dataByte[i] = Convert.ToByte(dataArray[i]);
}
rsa.FromXmlString(_privateKey);
var decryptedByte = rsa.Decrypt(dataByte, false);
Console.WriteLine(_encoder.GetString(decryptedByte));
}
Now I want to do the same process in Android app. Please can somebody guide me through this?
I have tried the following code but its throwing javax.crypto.IllegalBlockSizeException: input must be under 128 bytes exception
String modulusString = "hm2oRCtP6usJKYpq7o1K20uUuL11j5xRrbV4FCQhn/JeXLT21laKK9901P69YUS3bLo64x8G1PkCfRtjbbZCIaa1Ci/BCQX8nF2kZVfrPyzcmeAkq4wsDthuZ+jPInknzUI3TQPAzdj6gim97E731i6WP0MHFqW6ODeQ6Dsp8pc=";
String publicExponentString = "AQAB";
byte[] modulusBytes = Base64.decode(modulusString, DEFAULT);
byte[] exponentBytes = Base64.decode(publicExponentString, DEFAULT);
BigInteger modulus = new BigInteger(1, modulusBytes);
BigInteger publicExponent = new BigInteger(1, exponentBytes);
RSAPrivateKeySpec rsaPubKey = new RSAPrivateKeySpec(modulus, publicExponent);
KeyFactory fact = KeyFactory.getInstance("RSA");
PrivateKey pubKey = fact.generatePrivate(rsaPubKey);
Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1PADDING");
cipher.init(Cipher.ENCRYPT_MODE, pubKey);
byte[] plainBytes = result.getBytes("UTF-16LE");
byte[] cipherData = cipher.doFinal(plainBytes);
String encryptedStringBase64 = Base64.decode(cipherData, DEFAULT).toString();

How to write RSA encrypt function in C#

I have following nodejs code to encrypt RSA:
const encryptWithRSA = (PublicKey, selData) => {
let encrypted = crypto.publicEncrypt(
{
key: -----BEGIN PUBLIC KEY-----\n${PublicKey}\n-----END PUBLIC KEY-----,
padding: crypto.constants.RSA_PKCS1_PADDING,
},
Buffer.from(selData)
);
return encrypted.toString("base64");
};
Then I tried to convert this code block to C# :
using Org.BouncyCastle.Asn1;
using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Security;
public static string Encrypt(string publickey, string data)
{
string key = publickey;
Asn1Object obj = Asn1Object.FromByteArray(Convert.FromBase64String(key));
DerSequence publicKeySequence = (DerSequence)obj;
DerBitString encodedPublicKey = (DerBitString)publicKeySequence[1];
DerSequence publicKey = (DerSequence)Asn1Object.FromByteArray(encodedPublicKey.GetBytes());
DerInteger modulus = (DerInteger)publicKey[0];
DerInteger exponent = (DerInteger)publicKey[1];
RsaKeyParameters keyParameters = new RsaKeyParameters(false, modulus.PositiveValue, exponent.PositiveValue);
RSAParameters parameters = DotNetUtilities.ToRSAParameters(keyParameters);
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.ImportParameters(parameters);
byte[] dataToEncrypt = Encoding.UTF8.GetBytes(data);
byte[] encryptedData = rsa.Encrypt(dataToEncrypt, RSAEncryptionPadding.Pkcs1);
return Convert.ToBase64String(encryptedData);
}
Two code return 2 different results. Anyone can show me what wrong? Thanks!

Using Azure Key Vault RSA Key to encrypt and decrypt strings

I have setup Azure Key Vault to retrieve RSA Keys for encryption. Azure send me an object of type KeyBundle. This object contains a JsonWebKey of type RSA of size 2048. Looking at my RSA Key, it has 2 methods called Encrypt(byte[] data, RSAEncryptionPadding padding) and Decrypt(byte[] data, RSAEncryptionPadding padding). Now I am trying to encrypt and decrypt a simple string like this:
public EncryptionManager(KeyBundle encryptionKey)
{
string test = "Hello World!";
var key = encryptionKey.Key.ToRSA();
var encryptedString = key.Encrypt(Encoding.UTF8.GetBytes(test), RSAEncryptionPadding.OaepSHA256);
var decryptedString = key.Decrypt(encryptedString, RSAEncryptionPadding.OaepSHA256);
}
Encryption works, but decryption throws an exception with message:
Key does not exist.
Here is the StackTrace
at System.Security.Cryptography.RSAImplementation.RSACng.EncryptOrDecrypt(SafeNCryptKeyHandle
key, ReadOnlySpan`1 input, AsymmetricPaddingMode paddingMode, Void*
paddingInfo, Boolean encrypt) at
System.Security.Cryptography.RSAImplementation.RSACng.EncryptOrDecrypt(Byte[]
data, RSAEncryptionPadding padding, Boolean encrypt) at
System.Security.Cryptography.RSAImplementation.RSACng.Decrypt(Byte[]
data, RSAEncryptionPadding padding) at
NxtUtils.Security.EncryptionManager..ctor(KeyBundle encryptionKey) in
C:\Repos\Enigma\EnigmaPrototype\SharedLibaries\NxtUtils\Security\EncryptionManager.cs:line
26
I am really not familiar with cryptographic algorithms. My question is: How can I encrypt and decrypt a simple strig using this RSA Key provided by Azure?
Thanks!
I got the same issue, what I did is here although I searched from internet and got this from the Microsoft docs
so this is my working code below
public static class KeyVaultEncryptorDecryptor
{
public static string KeyDecryptText(this string textToDecrypt , KeyVaultClient keyVaultClient, string keyidentifier)
{
var kv = keyVaultClient;
try
{
var key = kv.GetKeyAsync(keyidentifier).Result;
var publicKey = Convert.ToBase64String(key.Key.N);
using var rsa = new RSACryptoServiceProvider();
var p = new RSAParameters() {
Modulus = key.Key.N, Exponent = key.Key.E
};
rsa.ImportParameters(p);
var encryptedTextNew = Convert.FromBase64String(textToDecrypt);
var decryptedData = kv.DecryptAsync(key.KeyIdentifier.Identifier.ToString(), JsonWebKeyEncryptionAlgorithm.RSAOAEP, encryptedTextNew).GetAwaiter().GetResult();
var decryptedText = Encoding.Unicode.GetString(decryptedData.Result);
return decryptedText;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return default;
}
}
public static string KeyEncryptText(this string textToEncrypt , KeyVaultClient keyVaultClient, string keyidentifier)
{
var kv = keyVaultClient;
try
{
var key = kv.GetKeyAsync(keyidentifier).GetAwaiter().GetResult();
var publicKey = Convert.ToBase64String(key.Key.N);
using var rsa = new RSACryptoServiceProvider();
var p = new RSAParameters() {
Modulus = key.Key.N, Exponent = key.Key.E
};
rsa.ImportParameters(p);
var byteData = Encoding.Unicode.GetBytes(textToEncrypt);
var encryptedText = rsa.Encrypt(byteData, true);
string encText = Convert.ToBase64String(encryptedText);
return encText;
}
catch (Exception ex)
{
Console.WriteLine(ex);
return default;
}
}
}
ToRSA has a default boolean parameter indicating if the private key should be available, or not.
Since you didn't explicitly say true it is implicitly false, therefore your key object is public-only. With a public RSA key you can encrypt data or verify a signature, but you cannot sign or decrypt.

'Error: Incorrect data or key' while decrypting ciphertext using Node-RSA library

I'm using Node-RSA library to decrypt following cyphertext.
AD2FrZBeg6gqxJu3KPZtyvBhYcgb3Gk/D1hVG6/1bheD+E3sRv07a4mV/9WDiB1N1om8mHRgikNEoRlecv5UsAU=
But following error occurs.
Error: Error during decryption (probably incorrect key). Original error: Error: Incorrect data or key
at NodeRSA.module.exports.NodeRSA.$$decryptKey
And my code is following.
var NodeRSA = require('node-rsa');
var key = new NodeRSA();
key.importKey({
n: new Buffer('0086fa9ba066685845fc03833a9699c8baefb53cfbf19052a7f10f1eaa30488cec1ceb752bdff2df9fad6c64b3498956e7dbab4035b4823c99a44cc57088a23783', 'hex'),
e: 65537,
d: new Buffer('5d2f0dd982596ef781affb1cab73a77c46985c6da2aafc252cea3f4546e80f40c0e247d7d9467750ea1321cc5aa638871b3ed96d19dcc124916b0bcb296f35e1', 'hex'),
p: new Buffer('00c59419db615e56b9805cc45673a32d278917534804171edcf925ab1df203927f', 'hex'),
q: new Buffer('00aee3f86b66087abc069b8b1736e38ad6af624f7ea80e70b95f4ff2bf77cd90fd', 'hex'),
dmp1: new Buffer('008112f5a969fcb56f4e3a4c51a60dcdebec157ee4a7376b843487b53844e8ac85', 'hex'),
dmq1: new Buffer('1a7370470e0f8a4095df40922a430fe498720e03e1f70d257c3ce34202249d21', 'hex'),
coeff: new Buffer('00b399675e5e81506b729a777cc03026f0b2119853dfc5eb124610c0ab82999e45', 'hex')
}, 'components');
var publicComponents = key.exportKey('components-public');
console.log(publicComponents);
var encrypted = 'AD2FrZBeg6gqxJu3KPZtyvBhYcgb3Gk/D1hVG6/1bheD+E3sRv07a4mV/9WDiB1N1om8mHRgikNEoRlecv5UsAU=';
console.log('encrypted: ', encrypted);
var decrypted = key.decrypt(encrypted,'utf8');
console.log('decrypted: ', decrypted);
I predict var encrypted string is not compatible with decrypt function.
UPDATE 11.16 :
I got var encrypted from c# code.
c# code is following
RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
string publicModulus = "0086fa9ba066685845fc03833a9699c8baefb53cfbf19052a7f10f1eaa30488cec1ceb752bdff2df9fad6c64b3498956e7dbab4035b4823c99a44cc57088a23783";
byte[] publicExponent = BitConverter.GetBytes(65537);
// public key generate
RSAParameters publicKey = new RSAParameters();
publicKey.Modulus = StringToByteArray(publicModulus);
publicKey.Exponent = publicExponent;
rsa.ImportParameters(publicKey);
publicKeyText = rsa.ToXmlString(false);
string test = RSAEncrypt("Hello RSA!", publicKeyText);
And Encrypt function is following
public string RSAEncrypt(string getValue, string pubKey)
{
System.Security.Cryptography.RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
rsa.FromXmlString(pubKey);
//encode into UFT8
byte[] inbuf = (new UTF8Encoding()).GetBytes(getValue);
//encrypt
byte[] encbuf = rsa.Encrypt(inbuf, false);
//encode into Base64
return Convert.ToBase64String(encbuf);
}

Categories

Resources