So I've been trying to use node with node-rsa and javascript with jsencrypt to create a website (for an assignment) where the javascript client gets the public key generated by the server (node-rsa), encrypts the message (jsencrypt) that the user has entered, sends it to the server and gets the server to decrypt it (node-rsa). The generation of the keys works, the encryption works however the decryption doesn't. When I start the node script I do the following for the encryption...
var NodeRSA = require('node-rsa');
var myDecrypter = new NodeRSA({b: 512});
When the client requests the key (I am using express) the following is ran.
app.get('/getPublicKey', function(req, res){
var publicKeyJson = {"Key": ""};
console.log(myDecrypter.exportKey('public'));
publicKeyJson.Key = myDecrypter.exportKey('public');
res.json(JSON.stringify(publicKeyJson));
});
The client then saves that key like this...
var myEncrypter = new JSEncrypt();
var myJson = "";
$.getJSON( "getPublicKey", function( data ) {
myJson = JSON.parse(data).Key;
setKey();
});
function setKey() {
myEncrypter.setPublicKey(myJson);
}
When I got to encrypt and send the message on the client I do this...
function messageEncrypt() {
message = document.getElementById("message").value;
var encrypted = myEncrypter.encrypt(message);
myMessage = {"username": "", "userId": 0.0, "message": ""};
myMessage.username = me.username;
myMessage.userId = me.userId;
myMessage.message = encrypted;
console.log(encrypted);
$.post("sendMessage", myMessage);
}
When the server receives a message this is what happens, this is where I get the errors.
app.post('/sendMessage', function(req, res){
var message = req.body;
var user = message.username;
var id = message.userId;
console.log("What a mess, " + user + " said " + message.message + " what on earth does that mean");
//This line below errors
var clearMessage = myDecrypter.decrypt(message.message, 'utf8');
console.log(user + " said " + clearMessage);
});
The error I get is ...
Error: Error during decryption (probably incorrect key). Original error: Error: error:040A1079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
at Error (native)
at NodeRSA.module.exports.NodeRSA.$$decryptKey (/home/node_modules/node-rsa/src/NodeRSA.js:295:19)
at NodeRSA.module.exports.NodeRSA.decrypt (/home/node_modules/node-rsa/src/NodeRSA.js:243:21)
at /home/securechat/securechat.js:36:36
at Layer.handle [as handle_request] (/home/node_modules/express/lib/router/layer.js:95:5)
at next (/home/node_modules/express/lib/router/route.js:131:13)
at Route.dispatch (/home/node_modules/express/lib/router/route.js:112:3)
at Layer.handle [as handle_request] (/home/node_modules/express/lib/router/layer.js:95:5)
at /home/node_modules/express/lib/router/index.js:277:22
at Function.process_params (/home/node_modules/express/lib/router/index.js:330:12)
Here however is where it gets interesting, to get that error message above I had a private key of...
-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAIhdx31QICGN1LKRW4WngeL3RtzPh7cEHmhFJB8m4bQUSTcSi4eg
sUvMeZkWyaF9gOxtZKzk5TI6q+8hg8TY6S8CAwEAAQJASds423cVH/c4NsqhXh8e
KvYwjBFeeNIjQegIq1KctbHmKNM5MMb4jnDqdY/S5XHHS22EGvLNheLgV8tlRjwG
UQIhANpNmbl215eOsGPJ0jqz1XPMBrO35V6I3P04kvr66R1JAiEAn+oL0jtAFETR
4PRfenye5MAu9US3V5MoDN8xUoEvKrcCIQDQT2ZWNNIrHAyzXB2QyJPxqInoqp1j
5QPDWl3ewtj5iQIgY3E1nKw/stsA8LTGUvMAFBv2l4r9wDXAaBC7KSUwYY0CIAj4
0gA9etDbPm3H/XDwK4WXs9mXkKroyxewkWoOoAw/
-----END RSA PRIVATE KEY-----
and the public key sent to the client was...
-----BEGIN PUBLIC KEY-----
MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAIhdx31QICGN1LKRW4WngeL3RtzPh7cE
HmhFJB8m4bQUSTcSi4egsUvMeZkWyaF9gOxtZKzk5TI6q+8hg8TY6S8CAwEAAQ==
-----END PUBLIC KEY-----
The encrypted messages (stackoverflow) was ...
XDViV0InCSnpyBxbNu5Herut0JYSsp87buvhzM4g2f9z3khIx2zA8Ou0Uq0TtmqtvBBVtZi5wZbcS6em/vB78g==
The interesting thing is that when I used the demo on jsencrypt website and enter my private key as well as the encrypted message I get the correct decrypted message.
So my question is...
What am I doing wrong with my node-rsa decryption???
If you need anymore information/code please put it in the comments below.
To answer your question #Curious_Programmer be default node-rsa uses pkcs1_oaep for encryption and decryption while jsencrypt uses pkcs1. Thankfully node lets you change the encryptionScheme, what you need to do is add ...
myDecrypter.setOptions({encryptionScheme: 'pkcs1'});
under
var myDecrypter = new NodeRSA({b: 512});
and all will work like a charm, I hoped I helped you ;)
It seems that the ciphertext is a buffer, i.e. binary data. Then it is transported using JSON, which consists of text. You need to use a text encoding over the binary data to transport it over a text based interface.
Check the following definition of the encrypt method:
key.encrypt(buffer, [encoding], [source_encoding]);
with the reminder that the default is 'buffer' for [encoding].
So you should be using:
var encrypted = myEncrypter.encrypt(message, 'base64', 'utf-8');
where 'base64' is for the ciphertext encoding and 'utf-8' is for the plaintext encoding.
The decryption routine should automatically use base64 decoding of the ciphertext:
var clearMessage = myDecrypter.decrypt(message.message, 'utf8');
should be just fine.
I had the same issue.
encrypt.setOptions({encryptingScheme:'pkcs1'}); //Can be 'pkcs1_oaep' or 'pkcs1'. Default 'pkcs1_oaep'.
But, it still failed.
I have changed the lib from node-rsa to ursa, like this:
privateKey.decrypt(thirdEncrypted, 'base64', 'utf8',ursa.RSA_PKCS1_PADDING);
The problem has been resolved in ursa.
Related
So, I have some encryption/decryption issues …
I encrypt data in javascript thanks to node-forge and I try to decrypt it into PHP thanks to openssl_private_decrypt.
On the PHP side, I use the «OPENSSL_PKCS1_OAEP_PADDING» padding. So, on the javascript side I tried to configure forge to encrypt data with RSA-OAEP.
And when I try to decsypt the message on the PHP side, I have these errors :
error:04099079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
error:04065072:rsa routines:rsa_ossl_private_decrypt:padding check failed
I've tried to configure the encryption with sha1 message digest and sha1 for mgf1 option. I've also tried without any option (if I remember, forge use SHA256 by default). But there is nothing to do, I always have the same error …
javascript
const pubkey = `-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
`;
const privkey = `-----BEGIN PRIVATE KEY-----
...
-----END PRIVATE KEY-----
`;
let publicKey = forge.pki.publicKeyFromPem(pubkey);
let key = forge.random.getBytesSync(32);
let encKey = publicKey.encrypt(key, 'RSA-OAEP', {
md: forge.md.sha1.create(),
mgf1: {
md: forge.md.sha1.create()
}
});
let b64Key = encodeURIComponent(btoa(enckey));
Next, I send the key in url with the "xcem" param, thanks to HttpClient. And I receive it in PHP.
php
$privKey = "";
$b64Key = urldecode($_GET['xcem']);
$encKey = base64_decode($b64Key);
$key = null;
if (!openssl_private_decrypt($encKey, $key, file_get_contents('/keys/openssl_private.key'), OPENSSL_PKCS1_OAEP_PADDING))
{
$errorssl = [];
while ($error = openssl_error_string()) {
$errorssl[] = $error;
}
throw new Exception("Erreur lors du décryptage du message ! " . json_encode($errorssl));
}
When I send Data between 2 PHP servers, there is no problem …
But I can't make it work between JS and PHP … I need some help ^^
Sooo …
After some tests, and headaches I found the problem … In my original code (not the one here) I sent the key, the init vector, the signature and the encrypted data …
But in my PHP, I tried to decode the signature … I didn't send data in the right order …
My bad …
I'm trying to create a script on scriptr.io that creates a JWT/JWS to send to google's token endpoint in order to get an auth_token for my service account. I'm using the CryptoJS library in order to do the encrypting. I'm able to generate all 3 parts of the JWT, but I'm doing something wrong when doing so. I believe it has something to do with the last of the three parts of the string (so, the signature part), but I could be wrong.
var cryptoJs = {};
cryptoJs['SHA256'] = require('CryptoJS/rollups/sha256.js').CryptoJS.SHA256
var pHeader = {"alg":"RS256","typ":"JWT"}
var sHeader = JSON.stringify(pHeader);
var encodedHeader = Base64EncodeUrl(btoa(sHeader));
console.log("encodedHeader: " + encodedHeader);
var now = new Date();
var oneHourExpiration = ((now.getTime()-now.getMilliseconds())/1000)+3000;//3000, not 3600 which is 1 hour
var pClaim = {};
pClaim.iss = "-------#---iam.gserviceaccount.com";
pClaim.scope = "https://www.googleapis.com/auth/spreadsheets";
pClaim.aud = "https://www.googleapis.com/oauth2/v3/token";
pClaim.exp = oneHourExpiration;
pClaim.iat = Math.floor(Date.now()/1000);
console.log("exp: " + pClaim.exp);
console.log("iat: " + pClaim.iat);
var sClaim = JSON.stringify(pClaim);
var encodedClaim = Base64EncodeUrl(btoa(sClaim));
console.log("encodedClaim: " + encodedClaim);
var byteArray = encodedHeader + "." + encodedClaim;
console.log("byteArray: " + byteArray);
var secret = "-----BEGIN PRIVATE KEY-----\n.....MIIE.....=\n-----END PRIVATE KEY-----\n";
var signature = cryptoJs.SHA256(byteArray, secret);
var encodedSignature = Base64EncodeUrl(btoa(signature));
console.log("Encoded Signature: " + encodedSignature);
var sJWS = byteArray + "." + encodedSignature;
console.log("JWT: " + sJWS);
function Base64EncodeUrl(str){
return str.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/, '');
}
var http = require("http");
var requestObject = {
"url": "https://www.googleapis.com/oauth2/v3/token",
"method": "POST",
"headers": {"Content-Type":"application/x-www-form-urlencoded"},
"params": {"grant_type":"urn:ietf:params:oauth:grant-type:jwt-bearer","assertion":sJWS}
}
var response = http.request(requestObject);
var responseBodyStr = response.body;
console.log(responseBodyStr);
var token = JSON.parse(responseBodyStr.access_token);
console.log(token);
When I send the request to the token endpoint with the JWT I get the following response
{
"error": "invalid_grant",
"error_description": "Invalid JWT Signature."
}
Any idea where I'm going wrong? Can someone help me correctly format the JWT so I can get a token?
The function used is doing a hash, not a digital signature
var signature = cryptoJs.SHA256(byteArray, secret);
Digital signature with a RSA private key is not supported . Take a look at the comment in the main repository of CryptoJS
Inactivity
CryptoJS is a project that I enjoy and work on in my spare time, but
unfortunately my 9-to-5 hasn't left me with as much free time as it
used to. I'd still like to continue improving it in the future, but I
can't say when that will be. If you find that CryptoJS doesn't meet
your needs, then I'd recommend you try Forge.
I suggest to move the code to use other Javascript library like recommended. For example forge support RSA signatures (https://github.com/digitalbazaar/forge#rsa)
Google OAuth2 server uses RS256. I have provided an snippet to convert the secret key (I assumed PEM format) to forge and sign data using RSA with SHA256
The only signing algorithm supported by the Google OAuth 2.0 Authorization Server is RSA using SHA-256 hashing algorithm. This is expressed as RS256 in the alg field in the JWT header.
// convert a PEM-formatted private key to a Forge private key
var privateKey = forge.pki.privateKeyFromPem(pem);
// sign data with a private key and output DigestInfo DER-encoded bytes (defaults to RSASSA PKCS#1 v1.5)
var md = forge.md.sha256.create();
md.update(byteArray, 'utf8');
var signature = privateKey.sign(md);
//convert signature to base64
var encodedSignature = Base64EncodeUrl(btoa(signature));
I'm trying to
generate sign/verification keys (RSA)
sign a value (using those keys) on a Java web application (lets call server-side)
in order to a web client to verify - public-key imported as RSASSA-PKCS1-v1_5 + SHA-256, (in a browser, using WebCrypto API / client-side)
I'm having problems verifying the signed value (signed in the Java server-side) even though the public sign/verify key is successfully imported as a JWK in the client side.
I was wondering if there is any algorithm compatibility issue in any of the steps (OpenSSL, Java or Javascript) that I may be encountering.
The OpenSSL commands used to generate the keys
openssl genrsa -out privatekey.pem 2048
openssl rsa -in privatekey.pem -pubout > publickey.pub
openssl pkcs8 -topk8 -inform PEM -outform DER -in privatekey.pem -out privatekey-pkcs8.pem
Importing keys with Java (server-side)
public static KeyPair generateSignKeyPair() throws ... {
byte[] privBytes = b64ToByteArray(PRIVATE_KEY_PEM_VALUE);
byte[] pubBytes = b64ToByteArray(PUBLIC_KEY_PEM_VALUE);
// private key
KeySpec keySpec = new PKCS8EncodedKeySpec(privBytes);
KeyFactory keyFactory = KeyFactory.getInstance("RSA");
PrivateKey privateKey = keyFactory.generatePrivate(keySpec);
// public key (javaPubSignKey)
X509EncodedKeySpec X509publicKey = new X509EncodedKeySpec(pubBytes);
PublicKey publicKey = keyFactory.generatePublic(X509publicKey);
return new KeyPair(publicKey, privateKey);
}
Signing a value with Java (server-side)
public static byte[] generateSignature(PrivateKey signPrivateKey, byte[] data) throws ... {
Signature dsa = Signature.getInstance("SHA256withRSA");
dsa.initSign(signPrivateKey);
dsa.update(data);
return dsa.sign();
}
Send them to a web-app for the WebCrypto API to verify as a client/browser (the client is aware of the publicKey generated in the first step).
// Import public sign/verify key (javaPubSignVerifyKey)
var signatureAlgorithm = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: 2048,
publicExponent: new Uint8Array([0x01, 0x00, 0x01]),
hash: {
name: 'SHA-256'
}
};
// JWK format (1)
crypto.subtle.importKey(
'jwk', javaPubSignVerifyKey, signatureAlgorithm, false, ['verify']
).then(success, error);
function success(key) {
signatureVerifyPublicKey = key;
}
Note (1): on the Java side, I'm using com.nimbusds.jose.jwk.JWK to export the publicKey to JWK format.
The sign key is successfully imported by WebCrypto. But when it comes to the verification, it fails (the verification boolean is false).
crypto.subtle.verify(
signatureAlgorithm,
signatureVerifyPublicKey,
signature, // bytes in Int8Array format (2)
data // bytes in Int8Array format
).then(
function (valid) {
// valid === false
}
)
Note (2): also note that every example I found on WebCrypto used Uint8Array to represent byte arrays, but since Java generates signed byte-arrays I need to use Int8Array so that the signature values are not contaminated (maybe this is an issue aswell).
EDIT: for reference, it turned out to be another unrelated issue - I was converting the expected data from base64 twice in Javascript without noticing it; naturally the verification failed.
Please, check this simple code based on yours to import a RSA public key (spki) and verify a signature. I have generated the keys and signature using similar Java code
var publicKeyB64 = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDVdZDEs6htb3oxWstz7q+e5IwIRcptMNJiyemuoNyyjtiOy+0tEodjgo7RVoyUcGU3MysEivqvKdswQZ4KfwQCBLAR8DRzp3biAge5utZcKsQoQaC1rCEplfmzEo5ovIlBcMq5x1BxnrnlwEPRmM7MefRa+OeAOQJcstHcrJFO7QIDAQAB";
var dataB64 = "aGVsbG8=";
var signatureB64 = "aEOmUA7YC5gvF6QgH+TMg0erY5pzr83nykZGFtyGOOe+6ld+MC4/Qdb608XiNud+pBpzh0wqd6aajOtJim5XEfCH8vUPsv45aSPtukUIQTX00Oc1frIFDQI6jGJ4Q8dQYIwpqsyE2rkGwTDzt1fTTGiw54pLsJXjtL/D5hUEKL8=";
var signatureAlgorithm = {name: 'RSASSA-PKCS1-v1_5',modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]),hash: { name: 'SHA-256' }};
//convert public key, data and signature to ArrayBuffer.
var publicKey = str2ab(atob(publicKeyB64));
var data = str2ab(atob(dataB64));
var signature = str2ab(atob(signatureB64));
crypto.subtle.importKey("spki", publicKey, signatureAlgorithm, false,["verify"]).
then(function(key){
console.log(key);
return crypto.subtle.verify( signatureAlgorithm, key, signature, data);
}).then( function (valid) {
console.log("Signature valid: "+valid);
}).catch(function(err) {
alert("Verification failed " + err );
});
I could not reproduce exactly the issue. Using the str2ab utility function you have linked, the code works perfectly.
//Utility function
function str2ab(str) {
var arrBuff = new ArrayBuffer(str.length);
var bytes = new Uint8Array(arrBuff);
for (var iii = 0; iii < str.length; iii++) {
bytes[iii] = str.charCodeAt(iii);
}
return bytes;
}
I suggest to compare both codes to find the differences
I am developing a P2P Infrastructure that will have data from a set of different applications, distributed through the network. This P2P overlay is composed by a set of Python Twisted Servers.
I need to guarantee the security and privacy of the stored data, for each user of each application. Consequently, I am generating pairs of RSA keys in the client side of the web app, using the Web Crypto API. The RSA key pairs will be stored in the P2P overlay as well. So, I cipher on the client side, the private keys with a derivation of the user password.
In addition, I am using jwk to pem module to convert the JWK public key into a PEM key, to be used in the Python Cryptography library (PyCrypt or m2Crypto).
Finally, I have to guarantee that the message containing those credentials, as well as the user data , maintain its integrity. Therefore, in the client side, I am signing this data with the user's private key.
I send the data, as well as the signature, both in ArrayBuffer type to the server, encoded in base64.
function signData(private_key, data, callback){
var dataForHash = str2ab(JSON.stringify(sortObject(data)));
computeSHA(dataForHash, "SHA-256", function(hash){
signRSA(private_key, hash, function(data){
callback(data.buffer.b64encode(), dataForHash.b64encode());
});
});
}
function computeSHA(data, mode, callback){
window.crypto.subtle.digest(
{
name: mode,
},
data
)
.then(function(hash){
callback(new Uint8Array(hash).buffer);
})
.catch(function(err){
console.error(err);
});
}
function signRSA(private_key, data, callback){
window.crypto.subtle.sign(
{
name: "RSASSA-PKCS1-v1_5",
},
private_key,
data
)
.then(function(signature){
callback(new Uint8Array(signature));
})
.catch(function(err){
console.error(err);
});
}
ArrayBuffer.prototype.b64encode = function(){
return btoa(String.fromCharCode.apply(null, new Uint8Array(this)));
};
Afterwards, when the Python Server receives this http request, it decodes data and signature from base64.
dataForHash = base64.b64decode(dataReceived['data'])
signature = base64.b64decode(dataReceived['signature'])
For validating the signature, the public key is needed. Consequently:
data = utils.byteify(json.loads(dataForHash.decode("utf-16")))
pub_key = base64.b64decode(data['pub_key']) # Get PEM Public Key
(utils.byteify() converts unicode string to regular strings)
Verifying signature:
Authentication.verifySignature(signature, dataForHash, pub_key)
Method definition:
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
from Crypto.PublicKey import RSA
def verifySignature(signature, data, pub_key):
key = RSA.importKey(pub_key)
h = SHA256.new(data)
verifier = PKCS1_v1_5.new(key)
return verifier.verify(h, signature)
However, the signature verification returns False. I have also tried to use the m2crypto library, but it returns 0.
I managed to find the problem.
Although in Python (PyCrypto) the sign function should receive the hash of the data to sign, using the Web Cryptography API, the sign method applies a hash function to the received data before signing it.
Consequently, the data in JS was being hashed twice, one before invoking the sign method and one in the sign method, before creating the signature.
function signData(private_key, data, callback){
var dataForHash = str2ab(JSON.stringify(sortObject(data)));
signRSA(private_key, dataForHash, function(data){
callback(data.buffer.b64encode(), dataForHash.b64encode());
});
}
ArrayBuffer.prototype.b64encode = function(){
return btoa(String.fromCharCode.apply(null, new Uint8Array(this)));
};
String.prototype.b64decode = function(){
var binary_string = window.atob(this);
var len = binary_string.length;
var bytes = new Uint8Array(new ArrayBuffer(len));
for (var i = 0; i < len; i++) {
bytes[i] = binary_string.charCodeAt(i);
}
return bytes;
};
With this modification, the verification in python returns True now.
In the browser on the client I encrypt a rand_key variable using jsencrypt.js and a public key.
The encrypted key is send via the body in a mail to the server. (This is the only way within my reach given the IT structure.)
In my browser I have:
<!DOCTYPE html>
<html lang="en">
<head>
<script type="application/javascript" src="jsencrypt.js"></script>
</head>
<body>
<script>
var pubkey = "MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBALDjeFwFNhMCjMwcRVVKG1VvfsntEVPR3lNTujJnNk1+iSqZ4Tl5Lwq9GbwO+qlYVwXHNmeqG7rkEhL9uyDIZVECAwEAAQ=="
var rand_key = 'vpeq91mckhntgldi';
// Encrypt rand_key
var encrypt = new JSEncrypt();
//console.log('encrypt obj', encrypt);
encrypt.setPublicKey(pubkey);
var encrypted_rand_key = encrypt.encrypt(rand_key, 'base64');
console.log('encrypted_rand_key', encrypted_rand_key);
</script>
</body>
</html>
On my PC running node (where I receive the email):
var CryptoJS = require("crypto-js");
// encrypted string copied from browser console
var encrypted = 'IuRaUfDHDIJsO0JZbEj7RS/1Sw0iSZPB267MN9lmF5Fn/kuMMRyKlAjplwvUJ9qvirajOcAQNnRZs9A+gVcWLQ=='
IuRaUfDHDIJsO0JZbEj7RS/1Sw0iSZPB267MN9lmF5Fn/kuMMRyKlAjplwvUJ9qvirajOcAQNnRZs9A+gVcWLQ==
var key_pri = new NodeRSA('-----BEGIN RSA PRIVATE KEY-----MIIBPAIBAAJBALDjeFwFNhMCjMwcRVVKG1VvfsntEVPR3lNTujJnNk1+iSqZ4Tl5Lwq9GbwO+qlYVwXHNmeqG7rkEhL9uyDIZVECAwEAAQJBAIS8vYX4FyLex/8mu9SLvsU23KL0dgs7MqW+77uA/hvZt5eb/C0EfUekap3LBuAF3XqVkOwIjsDyj74adrB6J1ECIQDfxT74mqu+xZjdlrfNZcchu/MrrW631aMF4rsRZccTbQIhAMpdneTSAATCwE8vt4bS6BBnv8Y8ZceNO6wGOvcW30b1AiAY2MEGP75kP3Ka4Dpmfy+eSk1VAzvxA7LHW4akBuYU/QIhAMk7gtGSCjaxuy6DUssdW2tE4C0uzj87sIUFxQkEk48pAiEAkHxKin7tcB4pVU2yurSbGkB+TbaCOfkIzR4griXq00k=-----END RSA PRIVATE KEY-----');
var decrypted = key_pri.decrypt(encrypted, 'utf8');
console.log('decrypted: ', decrypted);
Unfortunately I get an error message:
Error: Error during decryption (probably incorrect key). Original error: Error: error:040A1079:rsa routines:RSA_padding_check_PKCS1_OAEP_mgf1:oaep decoding error
How can I fix the decription on the server (pc) side ?
Thanks !
The error gives you the information that the padding is wrong. JSencrypt is based on JSBN and only supports PKCS#1 v1.5 padding, but not OAEP. You have to configure NodeRSA to use the appropriate padding:
var key_pri = new NodeRSA(privateKeyString, {
encryptionScheme: 'pkcs1'
});
Then, you're passing a Base64-encoded string to key_pri.decrypt, but it expects the actual data as a Buffer, so you first need to parse that:
var decrypted = key_pri.decrypt(new Buffer(encrypted, 'base64'), 'utf8');