forge icon indicating copy to clipboard operation
forge copied to clipboard

Encryption in python and decryption in node.js using node-forge

Open pverma12345 opened this issue 6 years ago • 9 comments

I am trying to decrypt a message in node.js using node-forge but it's throwing "Invalid RSAES-OAEP padding."

Code snippet:

var forge = require('node-forge');
var rsa = forge.pki.rsa;
var pkey = fs.readFileSync(__dirname + "private_key.pem",'utf8')
var privateKey = forge.pki.privateKeyFromPem(pkey);
var t = privateKey.decrypt(forge.util.decode64(val), 'RSA-OAEP', {
     md: forge.md.sha1.create(),
     mgf1: {
         md: forge.md.sha1.create()
     }
 });
 console.log('decrypt', t, '\n')

I then tried removing the default parameters since the decrypt function uses sha1 by deafult, even that threw same error.

var t = privateKey.decrypt(forge.util.decode64(val), 'RSA-OAEP');
 console.log('decrypt', t, '\n')

Since I have manually copy pasted both the private and public keys in this project from python's project, I tried the operation end to end in node.js- first encryption and then decryption and it worked absolutely fine.

var pKey1  = fs.readFileSync(__dirname + "private_key.pem",'utf8')
var pKey2  = fs.readFileSync(__dirname + "public_key.pub",'utf8')

var privateKey = forge.pki.privateKeyFromPem(pKey1);
var publicKey = forge.pki.publicKeyFromPem(pKey2);
var t = publicKey.encrypt("abc-123e-5", 'RSA-OAEP', {
    md: forge.md.sha1.create(),
    mgf1: {
        md: forge.md.sha1.create()
    }
});
console.log('encrypted', t, '\n')

var y = forge.util.encode64(t);
t = privateKey.decrypt(forge.util.decode64(y), 'RSA-OAEP', {
    md: forge.md.sha1.create(),
    mgf1: {
        md: forge.md.sha1.create()
    }
});
console.log('decrypted', t, '\n') 

The python encrypting logic, FYI

from Crypto.Cipher import  PKCS1_OAEP
key = RSA.importKey(open('private_key.pem').read())
cipher = PKCS1_OAEP.new(key)
Return(b64encode(cipher.encrypt(message.encode('utf-8'))))

Can you please point what am I doing wrong?

pverma12345 avatar Apr 11 '18 04:04 pverma12345

Can you please point what am I doing wrong?

No, unfortunately! Everything worked for me ... let me show you what I did.

I generated a fresh keypair:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA6m2AtxPEl7BslOeLMAXw
Gpm6BfLT9uqrlXBhHRwMoAnaXZgjZzpKJhtJjaYe8pqyU1EeYtH1VGYfagJRb/OX
HVv9DM8K3oHiHvGgA9RxMX2nXMVS0ovVyl4GL6yjIOqc8gBDfnjURsRpQ860kNSL
oMI9R6VcjKCwM5k6Q4wDlgSIyp+/J6axU+4qRTQWwY/YvlrIkATz06FLULBQjdnu
knX8qpi1gAOQX/a5o8qCtyk6PdWxg3isi0w7r4zF6gUfKQiobzB8jvdd8w5/O2yw
0dP1YwaCdua7LMWokgpLMYxGbF/Zi2YYykHzJEi1f2/hslxeVKoCZho4FmY/8ued
zQIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA6m2AtxPEl7BslOeLMAXwGpm6BfLT9uqrlXBhHRwMoAnaXZgj
ZzpKJhtJjaYe8pqyU1EeYtH1VGYfagJRb/OXHVv9DM8K3oHiHvGgA9RxMX2nXMVS
0ovVyl4GL6yjIOqc8gBDfnjURsRpQ860kNSLoMI9R6VcjKCwM5k6Q4wDlgSIyp+/
J6axU+4qRTQWwY/YvlrIkATz06FLULBQjdnuknX8qpi1gAOQX/a5o8qCtyk6PdWx
g3isi0w7r4zF6gUfKQiobzB8jvdd8w5/O2yw0dP1YwaCdua7LMWokgpLMYxGbF/Z
i2YYykHzJEi1f2/hslxeVKoCZho4FmY/8uedzQIDAQABAoIBAB6TkcpPRcETPdZe
LQVigOK3UlAcUBFQoninY8NMOUlWf6jdgyCftbQqsvlKxZaXHtNHLVNuVzVe7JID
I2QavYcSzHqzNpO3FSs8EMbvGqR94ypygk779jKi+OOW/N/6OeZGqocAc4+XZrN1
bo/FaUDalOuXYe7ltpXEUpkk1gLm7Ji78Kngbb+ecomfmo67jE7NBiNhmw0IZU3V
emWKaiD3jx/wEKnX/kJwFFqbZMcm4iqwNrklY1ifKKNN4609bKFzhKxQNy70u5J8
eHGgATCjhbQvJV3jQy8BtKZOipAZRsYBmjY8X4BtI/hBUFmUhuHsFUQZpAbiZE9M
NQA3XfECgYEA9Z5vt4t8+pEhNaaguyvxGH4lmnJhQvDeOw3rGWS4EotY9VEhAhLD
zN//Io/nUVPAxxrXFxRHCNeEkTT8+lBMANihbWuiN+wwLZ2vqy5OtU0a27VZT0kB
ptb528YDoVGjrT5mvNGA9kGj7+kA4U5+jbE5xIfplqO9h4DXCu455e8CgYEA9FX6
zJ3tWVHy3Z3pmqVesJtzceg4CwMAzqqrF+x9/bVc/vqdawOSLc3iEDqWqbSEzSy/
EfK5eKLQX4ifKNOM+mjWN04pLU0CDUHXu4zsiwM0TFww7U7wbYOH4xQL6JFUFmLK
gXpTpx8t35xlKQndEa9YxaOTQTdywYtchncw1AMCgYEA12SZk9w/PX0IIoR9b6aZ
R2FYmJi1sn6IZEFQanRo99o7qOX8jFToYEIO+8A2mptX80MWXKbrpFVba/uL3feM
rzFKXyqEl4YIufgOlY+GdnenlgTyzRS43haoBpMfm9h8MPEIJ42SCmKF4V3z56Fu
WdORVUfbEL+G9c2Gx48iNwECgYBGn5+Ji1Byk+ROOHmwUIJpK9WXpBam4CxYkC46
EQptzgFeVhQwPW0APlVMeZLY5KvKHlfwGSeQ/gaaxoOyqktCihD5FWeDh5HDQTfZ
JyesGrYAKFmgDkQic3VQppe2lmMmwVFlUXVihfmde7glv4sdtIBFSD2lCSDrXpvU
5hi//wKBgQCn3DkQ/7mIn4NgFrhoVzB4tCvlFfoBXr+IabeUOsD3T0mum9qVMhA+
4624nULk4B+gvCErIH9DX9U1f/IQf0qbgVn7lnLjU5yRFJ1DlmdJOhckX4ZsN4h3
FBRcUmeO4iUQF7vsCD8je8xPTGr/pRShIzvpeITQFkEZ3aVkf2gWXA==
-----END RSA PRIVATE KEY-----

Then ran this python code:

from Crypto.Cipher import  PKCS1_OAEP
from Crypto.PublicKey import RSA
from base64 import b64encode
message = 'foo'
key = RSA.importKey(open('/tmp/pri.pem').read())
cipher = PKCS1_OAEP.new(key)
output = b64encode(cipher.encrypt(message.encode('utf-8')))
print output

Got this output:

UI9+fcLybKqpBmULo9amdFMKmtHIUxbU/smuQZwDZ9WPa/AQImvRMd9ZW/3vkpKJRU8M7ZmjldUrjXyBPEHOdAe2jr1fLZjXo/McRrCY2TRZOkpC8pTwSWN6SDx82vklpjXBXgEMOuTW/7q20LHx5cNLx4JGgug00EdClj2JCIAMyXDWLpSx5l9ynb9l3uML3vMyHAhZ3S0P5RPZ2RVkJM4aTfN+0yga+rJt4F9BagNp/CAc5gNswxev9NNPGIi0hph7tDdbwogr1VLsph/KbDxWFYvhvUQLnXvJJcwO7KVsrnmtVFMe1RNAdUDqw9oKISeT/jp7kydPxrrAt4GqZQ==

Then I ran this node.js code:

const forge = require('node-forge');
const fs = require('fs');

const pem = fs.readFileSync('/tmp/pri.pem', 'utf8');
const privateKey = forge.pki.privateKeyFromPem(pem);

const y = 'UI9+fcLybKqpBmULo9amdFMKmtHIUxbU/smuQZwDZ9WPa/AQImvRMd9ZW/3vkpKJRU8M7ZmjldUrjXyBPEHOdAe2jr1fLZjXo/McRrCY2TRZOkpC8pTwSWN6SDx82vklpjXBXgEMOuTW/7q20LHx5cNLx4JGgug00EdClj2JCIAMyXDWLpSx5l9ynb9l3uML3vMyHAhZ3S0P5RPZ2RVkJM4aTfN+0yga+rJt4F9BagNp/CAc5gNswxev9NNPGIi0hph7tDdbwogr1VLsph/KbDxWFYvhvUQLnXvJJcwO7KVsrnmtVFMe1RNAdUDqw9oKISeT/jp7kydPxrrAt4GqZQ==';

const t = privateKey.decrypt(forge.util.decode64(y), 'RSA-OAEP', {
    md: forge.md.sha1.create(),
    mgf1: {
        md: forge.md.sha1.create()
    }
});

console.log(t);

And got this output:

foo

dlongley avatar Apr 20 '18 14:04 dlongley

Btw, if you run it with the defaults in forge:

const t = privateKey.decrypt(forge.util.decode64(y), 'RSA-OAEP');

It also works.

dlongley avatar Apr 20 '18 15:04 dlongley

Btw, five months ago I developed this package that does decryption and encryption natively in Nodejs. Kind of a wrapper package that makes RSA Keypair encryption and encryption easier in terms of coding syntax:

https://github.com/joshuaquek/QuickEncrypt https://www.npmjs.com/package/quick-encrypt

joshuaquek avatar Oct 24 '18 13:10 joshuaquek

However, it can't beat Forge on the whole as a package. Forge is much more versatile and has many more encryption algos for different use cases

joshuaquek avatar Oct 24 '18 13:10 joshuaquek

In above code, how to read the private key if the private key has a passphrase

daratha avatar Mar 21 '19 08:03 daratha

You should always encrypt using the public key and decrypt using the private key. As both the servers won't have private keys. Server will have private and clients will have public keys. Clients will encrypt the data and send request to server where server will decrypt using the private key.

ishangajera avatar Aug 02 '22 23:08 ishangajera

I am facing the same problem but with React Native. I don't understand.

mougis avatar Jan 01 '23 22:01 mougis

Ok I figured it out , with this random answer found on stackoverflow: image

Here is my js code :

const forge = require('node-forge');
const fs = require('fs');

const pem = fs.readFileSync('private.pem', 'utf8');

const encode = (data) => {
    let key = forge.pki.decryptRsaPrivateKey(pem, 'TFZu');
    key = forge.pki.setRsaPublicKey(key.n, key.e);
    let buff = '';
    for (let i = 0; i < Math.ceil(data.length / 430); i++)
        buff += key.encrypt(data.slice(i * 430, i * 430 + 430), 'RSA-OAEP', {
            md: forge.md.sha256.create(),
            mgf1: {
                md: forge.md.sha256.create(),
            },
        });

    return Buffer.from(buff, 'binary').toString('base64');
};
const decode = (data) => {
    let key = forge.pki.decryptRsaPrivateKey(pem, 'TFZu');
    const buffer = Buffer.from(data, 'base64').toString('binary');
    let cleartext = '';
    for (let i = 0; i < Math.ceil(buffer.length / 512); i++)
        cleartext += key.decrypt(
            buffer.slice(i * 512, i * 512 + 512),
            'RSA-OAEP',
            {
                md: forge.md.sha256.create(),
                mgf1: {
                    md: forge.md.sha256.create(),
                },
            },
        );
    return cleartext;
};

const input = process.argv[3];

if (process.argv[2] === 'encode') {
    console.log(encode(input));
} else if (process.argv[2] === 'decode') {
    console.log(decode(input));
}

And here is my python code:

from Crypto.Cipher import PKCS1_OAEP
from Crypto.PublicKey import RSA
from Crypto.Hash import SHA256
from Crypto.Signature.pss import MGF1
from math import ceil
from base64 import b64decode, b64encode
import sys

if __name__ == '__main__':
    text = sys.argv[2]

    with open("private.pem", "rb") as key_file:
        keypem = key_file.read()
    ciphertxt = b''
    if sys.argv[1] == 'encrypt':
        txt = text.encode('utf-8')
        key = RSA.import_key(keypem, passphrase='TFZu')
        key = key.public_key()
        cipher = PKCS1_OAEP.new(key, SHA256,
            lambda x,y: MGF1(x,y, SHA256))
        for i in range(ceil(len(txt) / 430)):
            ciphertxt = ciphertxt + cipher.encrypt(txt[i * 430:i * 430 + 430])
        cipher_b64 = b64encode(ciphertxt).decode('utf-8')
        print(cipher_b64)
    elif sys.argv[1] == 'decrypt':
        key = RSA.import_key(keypem, passphrase='TFZu')
        cipher = PKCS1_OAEP.new(key, SHA256,
            lambda x,y: MGF1(x,y, SHA256))
        ciphertxt = b64decode(text)
        cleartxt = b''
        for i in range(ceil(len(ciphertxt) / 512)):
            cleartxt = cleartxt + cipher.decrypt(ciphertxt[i * 512:i * 512 + 512])
        print(cleartxt)

mougis avatar Jan 02 '23 01:01 mougis

FYI : The buffer slices of 430 and 512 is because it is a RSA key of 4096-Bit. In case of large messages.

mougis avatar Jan 02 '23 01:01 mougis