4

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???

6
  • You need RSA-OAEP Commented Jan 15, 2019 at 11:36
  • @kelalaka RSA-OAEP is for encrypting and decrypting and actually supported and the default for crypto. The "problem" is with the signing and what is needed is RSA-PSS. See my answer for details. Commented Jan 15, 2019 at 14:20
  • Yes, if we talk about private encryption as signing. signing != private encryption. Commented Jan 15, 2019 at 14:23
  • @kelalaka I have updated my answer with an explanation about signing (as opposed to private encryption). This should make it more clear. With regard to your comment, I wonder if there is a reason to use privateEncrypt() for purposes other than signing. Commented Jan 15, 2019 at 15:12
  • 1
    @kelalaka Thanks, interesting read. And the problem is aggravated by some implementors. For example, the RSA_sign function that you refer to may invoke a function RSA_private_encrypt() :-) Commented Jan 15, 2019 at 15:47

1 Answer 1

9

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'))
Sign up to request clarification or add additional context in comments.

3 Comments

@cockroach54 My pleasure, your question was well posed and welcome to StackOverflow.
Um.. may I ask you one more??? I am planning to embed the PUBLIC KEY in client app and PRIVATE KEY in admin app. Admin app user encrypt some kind of code (encrypted code must be different randmly) and give it to the client app. client only decrypt code but should not know how to encrypt it. In this case, if admin use PUBLIC KEY and client use PRIVATE KEY, client can make PUBLIC KEY using PRIVATE KEY. this is a problem. Could I customize privateEncrypt() use RSASSA-PKCS1-v1_5 --> RSAES-PKCS1-v1_5 ???
@cockroach54 This is too big to respond to in a comment, please submit it as a new question to get a proper answer...

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.