0

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;
6
  • So your decrypt method 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. Commented Jul 21, 2019 at 15:55
  • As a side note, this is a VERY strange way to encrypt things. Firstly, RSA by itself can only encrypt data up to keysize/8 (e.g. 2048 key would encrypt up to 214 bytes), which means this is only really well suited to things like passwords etc. Secondly, if you do have a PEM file, convert it to PKCS12 and just load the RSACryptoServiceProvider like so: var key = new X509Certificate2(@"C:\Wherever.pfx"); var rsa = (RSACryptoServiceProvider)key.PrivateKey; Commented Jul 21, 2019 at 15:57
  • Lastly, if you CAN change encryption, i would recommend switching to PKCS7 aka CMS - see e.g. EnvelopedCMS class: 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. Commented Jul 21, 2019 at 15:58
  • I have just checked and you are right. I forgot that the pem file contains the entire public key, not just the modulus, so I will need to address this problem and see if it will work. Also, as I have already said, I plan on implementing a symmetric encryption algorithm for the messages, but I'd still need to use the RSA for sending the key securely. Or am I wrong? I haven't delved too much into cryptography, so I don't know the details, just how it works on the surface. And I'll only send small pieces of text, so I thought that the length limitations wouldn't be a problem regardless. Commented Jul 21, 2019 at 17:35
  • Also, thanks for your help. I'll come with an update soon. Commented Jul 21, 2019 at 17:36

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.