Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

nodejs crypto module privateEncrypt() always return same result

I am using RSA encryption with nodejs crypto module.

I want encrypt message with PRIVATE KEY and decrypt with PUBLIC KEY. also always make different result with same message using padding scheme like encryption using public key.

So I used basic crypto module like below

var crypto = require('crypto');
var fs = require('fs');
const path = require('path');


var PRIVKEY = fs.readFileSync(path.join(__dirname, 'private.key'), 'utf8');
var PUBKEY = fs.readFileSync(path.join(__dirname, 'pub.key'), 'utf8');

// RSA PRIVATE ENCRYPT -> PUBLIC DECRYPT //
myMSG = "apple";
console.log('myMSG SIZE:', myMSG.length);

function privENC_pubDEC(originMSG){
 encmsg = crypto.privateEncrypt(PRIVKEY, Buffer.from(originMSG, 'utf8') ).toString('base64');
 msg = crypto.publicDecrypt(PUBKEY, Buffer.from(encmsg, 'base64'));
 console.log("Encrypted with private key : "+encmsg);
 console.log(msg.toString());
}

// RSA PUBLIC ENCRYPT -> PRVATE DECRYPT //
function pubENC_privDEC(originMSG){
 encmsg = crypto.publicEncrypt({key:PUBKEY, padding:crypto.constants.RSA_PKCS1_PADDING}, Buffer.from(originMSG, 'utf8') ).toString('base64');
 msg = crypto.privateDecrypt({key:PRIVKEY, padding:crypto.constants.RSA_PKCS1_PADDING}, Buffer.from(encmsg, 'base64'));
 console.log("\nEncrypted with public key : "+encmsg);
 console.log(msg.toString());
}

privENC_pubDEC(myMSG);
pubENC_privDEC(myMSG);

Result

C:\Users\LSW>node crypto.js
myMSG SIZE: 5
Encrypted with private key : fbUZwj+UZP92HQYRc+EJTqSztJTY/Sit5axPZ0NVBuDAC8ZwvvC96pxxDGpra4Yg8MjcXyjvnT8rrrgHu0T0wA==
apple

Encrypted with public key : ze+5TdWtR8hkpNPIVa5HSasOxs3Pr8FA/1/zUGqDUQmIhs/miWt5pgU9kIAiryKfgGa0+p9RfHPMwZ1VMSA7Bw==
apple

C:\Users\LSW>node crypto.js
myMSG SIZE: 5
Encrypted with private key : fbUZwj+UZP92HQYRc+EJTqSztJTY/Sit5axPZ0NVBuDAC8ZwvvC96pxxDGpra4Yg8MjcXyjvnT8rrrgHu0T0wA==
apple

Encrypted with public key : OdEpjloUDWI8+YjWkE5cmBC/fJL2QnRLKBXfjaP5h5qyB1OMcm9JGGNSTiAAL2u8O5jjdQAavB9Rn+cdRDjLyA==
apple

C:\Users\LSW>node crypto.js
myMSG SIZE: 5
Encrypted with private key : fbUZwj+UZP92HQYRc+EJTqSztJTY/Sit5axPZ0NVBuDAC8ZwvvC96pxxDGpra4Yg8MjcXyjvnT8rrrgHu0T0wA==
apple

Encrypted with public key : INspxkyFu2AWGVYwSvOGOPH1fhE3qVVxiqz+SmyHU8wTDNKHj4gVVHqO+8AZOJvi4NfyekI2MMwpFDU4mUjEXA==
apple

PUBLIC ENCRYPT -> PRVATE DECRYPT is operated well I expected. it always return different result because of padding scheme.

But PRIVATE ENCRYPT -> PUBLIC DECRYPT is always return same message although used padding scheme.

Is there any solution make it different message with Nodejs crypto module???

like image 291
cockroach54 Avatar asked Jan 27 '23 21:01

cockroach54


1 Answers

This is expected behavior according to the padding schemes for RSA signing and encryption as implemented by OpenSSL, which crypto leverages.

I am not sure what you want to use the functions privateEncrypt() and publicDecrypt() for. If your intent is to sign data, then see my update below. Anyway, for these functions, the crypto documentation explains that it only exposes RSA_PKCS1_PADDING which OpenSSL maps to the deterministic RSASSA-PKCS1-v1_5 padding scheme under the hood. This means that for the same key and the same data, the resulting data will be the same.

For encryption and decryption, with publicEncrypt() and privateDecrypt(), you have selected the RSA_PKCS1_PADDING mode. This translates to the RSAES-PKCS1-v1_5, a scheme that includes random elements, which cause the different outputs that you observe in your repeated runs. According to the documentation, crypto uses RSA_PKCS1_OAEP_PADDING padding by default. This stands for Optimal asymmetric encryption padding, which is non-deterministic as well.

For a summary of the PKCS#1-defined schemes, see PKCS#1 Schemes.


Update: You may want to use the Sign class instead of the privateEncrypt() and publicDecrypt() functions. Its sign() function does support a probabilistic padding mode, which OpenSSL supports via RSASSA-PSS. Using your example code as a starting point, it would look something like this:

const sign = crypto.createSign('SHA256')
sign.update(Buffer.from(originMSG, 'utf8'))
signature = sign.sign({key:PRIVKEY, padding:crypto.constants.RSA_PKCS1_PSS_PADDING}).toString('base64')

The signature will be different every time. Note that you can not "decrypt" it, it is a one-way operation. You can only verify it, using the public key with the Verify class:

const verify = crypto.createVerify('SHA256')
verify.update(Buffer.from(originMSG, 'utf8'))
verifyRes = verify.verify({key:PUBKEY, padding:crypto.constants.RSA_PKCS1_PSS_PADDING}, Buffer.from(signature, 'base64'))
like image 101
Reinier Torenbeek Avatar answered Jan 31 '23 22:01

Reinier Torenbeek