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;
decryptmethod simply specifies the key and padding; and your SERVER side encrypt method uses KEY as modulues and a BitConverter provided array taken from a static constant of 65537 as Exponent. Since you are not constructing the RSA parameters the same way in decrypt, it is no wonder that you can't decrypt anything.var key = new X509Certificate2(@"C:\Wherever.pfx"); var rsa = (RSACryptoServiceProvider)key.PrivateKey;EnvelopedCMSclass: learn.microsoft.com/en-us/dotnet/api/… This is industry standard and i am pretty sure there'd be a plugin for node to read this. If you need a cipher, pick DES3-CBC.