How to create a pair private/public keys using Node.js crypto? - node.js

I have to generate two keys (private and public) to encrypt a text with the public and let the user with the private key decrypt the text.
Is it possible with the module Crypto?

nodejs v10.12 now supports this natively with crypto.generateKeyPair
const { generateKeyPair } = require('crypto');
generateKeyPair('rsa', {
modulusLength: 4096,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: 'top secret'
}
}, (err, publicKey, privateKey) => {
// Handle errors and use the generated key pair.
});

Use the crypto module from npm to generate KeyPair.
var crypto = require('crypto');
var prime_length = 60;
var diffHell = crypto.createDiffieHellman(prime_length);
diffHell.generateKeys('base64');
console.log("Public Key : " ,diffHell.getPublicKey('base64'));
console.log("Private Key : " ,diffHell.getPrivateKey('base64'));
console.log("Public Key : " ,diffHell.getPublicKey('hex'));
console.log("Private Key : " ,diffHell.getPrivateKey('hex'));
Above is a example snippet. To know more checkout documentation http://nodejs.org/api/crypto.html

The following code works, but I'm not a professional cryptographer, so some comments here would be useful.
I've used the ursa RSA module, instead of crypto.
I am concerned that if similar data were encrypted directly, without a pass of AES or similar, then it might be trivial to break this. Comments please...
var ursa = require('ursa');
var fs = require('fs');
// create a pair of keys (a private key contains both keys...)
var keys = ursa.generatePrivateKey();
console.log('keys:', keys);
// reconstitute the private key from a base64 encoding
var privPem = keys.toPrivatePem('base64');
console.log('privPem:', privPem);
var priv = ursa.createPrivateKey(privPem, '', 'base64');
// make a public key, to be used for encryption
var pubPem = keys.toPublicPem('base64');
console.log('pubPem:', pubPem);
var pub = ursa.createPublicKey(pubPem, 'base64');
// encrypt, with the public key, then decrypt with the private
var data = new Buffer('hello world');
console.log('data:', data);
var enc = pub.encrypt(data);
console.log('enc:', enc);
var unenc = priv.decrypt(enc);
console.log('unenc:', unenc);
After some further investigation http://en.wikipedia.org/w/index.php?title=RSA_%28cryptosystem%29&section=12#Attacks_against_plain_RSA it looks like ursa already does padding.

const crypto = require('crypto');
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
});

If you know how to get what you want from OpenSSL, I think it's perfectly reasonable to run OpenSSL using Node's child_process.
var cp = require('child_process')
, assert = require('assert')
;
var privateKey, publicKey;
publicKey = '';
cp.exec('openssl genrsa 2048', function(err, stdout, stderr) {
assert.ok(!err);
privateKey = stdout;
console.log(privateKey);
makepub = cp.spawn('openssl', ['rsa', '-pubout']);
makepub.on('exit', function(code) {
assert.equal(code, 0);
console.log(publicKey);
});
makepub.stdout.on('data', function(data) {
publicKey += data;
});
makepub.stdout.setEncoding('ascii');
makepub.stdin.write(privateKey);
makepub.stdin.end();
});

I dont know if this helps but I also was looking to do something along these lines.
Here is what I came up with :
As mentioned in the answer by Nelson Owalo you can use the crypto library, as follows :
//import the methods
const { generateKeyPair, createSign, createVerify } = require("crypto");
//generate the key pair
generateKeyPair(
"rsa",
{
modulusLength: 2048, // It holds a number. It is the key size in bits and is applicable for RSA, and DSA algorithm only.
publicKeyEncoding: {
type: "pkcs1", //Note the type is pkcs1 not spki
format: "pem",
},
privateKeyEncoding: {
type: "pkcs1", //Note again the type is set to pkcs1
format: "pem",
//cipher: "aes-256-cbc", //Optional
//passphrase: "", //Optional
},
},
(err, publicKey, privateKey) => {
// Handle errors and use the generated key pair.
if (err) console.log("Error!", err);
console.log({
publicKey,
privateKey,
});//Print the keys to the console or save them to a file.
/*
* At this point you will have to pem files,
* the public key which will start with
* '-----BEGIN RSA PUBLIC KEY-----\n' +
* and the private key which will start with
* '-----BEGIN RSA PRIVATE KEY-----\n' +
*/
//Verify it works by signing some data and verifying it.
//Create some sample data that we want to sign
const verifiableData = "this need to be verified";
// The signature method takes the data we want to sign, the
// hashing algorithm, and the padding scheme, and generates
// a signature in the form of bytes
const signature = require("crypto").sign("sha256", Buffer.from(verifiableData),
{
key: privateKey,
padding: require("crypto").constants.RSA_PKCS1_PSS_PADDING,
});
//Convert the signature to base64 for storage.
console.log(signature.toString("base64"));
// To verify the data, we provide the same hashing algorithm and
// padding scheme we provided to generate the signature, along
// with the signature itself, the data that we want to
// verify against the signature, and the public key
const isVerified = require("crypto").verify(
"sha256",
Buffer.from(verifiableData),
{
key: publicKey,
padding: require("crypto").constants.RSA_PKCS1_PSS_PADDING,
},
Buffer.from(signature.toString("base64"), "base64")
);
// isVerified should be `true` if the signature is valid
console.log("signature verified: ", isVerified);
}
);
I think the key points are which algorithm is used, as older versions of the pem use pkcs1 not pkcs8. The beginning of the key helps identify the version of the key and also includes information on wither it is encrypted or not. Hope this helps!

I have not used it, but this may be useful:
http://ox.no/posts/diffie-hellman-support-in-node-js
Documentation is severely lacking on this (no examples that I could find).

You can use this rsa-json module. It just spawns a openssl process, so it is pretty dependent on the OS (it does not work by default on windows).

child_process route is a terrible and non-scalable solution imo. Stay away.
I chose to go with keypair instead.

Related

Is it possible to generate RSA keys from a string?

I am using the node-rsa package to do asymmetric encryption with the RSA function.
Currently, I am generating my public and private keys like so:
generateKeys = function() {
const key = new NodeRSA({ b: 1024 });
return {
public: key.exportKey('public'),
private: key.exportKey('private'),
}
}
Is there any possible way to generate a key from a given string? I want to do this so that my users can easily write down their private key, which is important to my project. After all, it is nearly impossible to write down a private key that is 1024 characters long.
I am hoping for something like this:
const key = new NodeRSA({ b: 1024 }, "Secret goes here")
I think this may be possible because the sha256 function can accept any string to hash. I know RSA encryption isn't really a hashing function, so I am not sure if the same effect is possible.
Any help is appreciated!
It seems that there is a simple way of doing this. There is a package called cryptico
that can do the thing.
First install the package:
npm i cryptico
Example
const cryptico = require('cryptico');
const PassPhrase = "The Moon is a Harsh Mistress.";
// The length of the RSA key, in bits.
const Bits = 1024;
const RSAkey = cryptico.generateRSAKey(PassPhrase, Bits);
const PublicKeyString = cryptico.publicKeyString(RSAkey);
A better and complete example can be found here
Update
So, if you need an ASYMMETRIC encryption (As I know that works with a pair of keys which are called publickey and privatekey) you can simply use a pure Node.js implementation.
Example
const { generateKeyPairSync, publicEncrypt, privateDecrypt } = require('crypto');
const PassPhrase = "The Moon is a Harsh Mistress.";
const Bits = 1024;
const encryptWithRSA = (input, publickey) => {
const buffer = Buffer.from(input, 'utf-8');
const encrypted = publicEncrypt(publicKey, buffer);
return encrypted.toString("base64");
}
const decryptWithRSA = function (input, privatekey) {
const buffer = Buffer.from(input, 'base64');
const decrypted = privateDecrypt(
{
key: privatekey,
passphrase: PassPhrase,
},
buffer,
)
return decrypted.toString("utf8");
};
const { privateKey, publicKey } = generateKeyPairSync('rsa', {
modulusLength: Bits,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: PassPhrase
}
});
const encrypted = encryptWithRSA('yes i know', publicKey)
console.log(encrypted);
console.log(decryptWithRSA(encrypted, privateKey));
Output (The encrypted value is random as you know)
t3Gw+PlKn84gx2wkj99we345C6ZjIElpgDkzXjio2aWRwI28qTMev14H7o219P6Lw9STGnfK4FgTYO/cvLGlzplqRv6X5AgrstwsGQN88wmKHe5+6cxlpzPFNPWLUqtAvsIOPZe+ghaRGCkOT2ETUp4PiqwOTJ2EtmyVqQHt+f4=
yes i know

Generate Signature using Private Key and "SHA256 with RSA" algorithm in Node.js

Our system will be calling an API with Authentication server. This server is built in java and requires a lot of key encryption. One requirement is to generate a Signature with client's(it's us) private key using "SHA256 with RSA" algorithm. I have done this in Java but not sure if it's right. Rur server is written in Nodejs. How can I translate below Java code to Node.js?
public static String signSHA256RSA(String input, String strPk) throws Exception {
Security.addProvider(new BouncyCastleProvider());
// Remove markers and new line characters in private key
String realPK = strPk.replaceAll("-----END PRIVATE KEY-----", "")
.replaceAll("-----BEGIN PRIVATE KEY-----", "")
.replaceAll("\n", "");
byte[] b1 = Base64.getDecoder().decode(realPK);
PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(b1);
KeyFactory kf = KeyFactory.getInstance("RSA");
Signature privateSignature = Signature.getInstance("SHA256withRSA");
privateSignature.initSign(kf.generatePrivate(spec));
privateSignature.update(input.getBytes("UTF-8"));
byte[] s = privateSignature.sign();
return Base64.getEncoder().encodeToString(s);
}
NodeJS has a built in utilities in the crypto package that could help you with that
More on that here https://nodejs.org/api/crypto.html#crypto_class_sign
here's an example from the docs
const { privateKey, publicKey } = crypto.generateKeyPairSync('ec', {
namedCurve: 'sect239k1'
});
const sign = crypto.createSign('SHA256');
sign.write('some data to sign');
sign.end();
const signature = sign.sign(privateKey, 'hex');
const verify = crypto.createVerify('SHA256');
verify.write('some data to sign');
verify.end();
console.log(verify.verify(publicKey, signature, 'hex'));
// Prints: true
Here is the complete Flow
Generate Public and Private keys
const crypto = require('crypto');
const fs = require('fs');
const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048,
publicKeyEncoding: {
type: 'pkcs1',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs1',
format: 'pem'
}
});
// Writing keys to files.
fs.writeFileSync("./private.key", privateKey);
fs.writeFileSync("./public.key", publicKey);
Sign and Verify using Public and Private keys
const crypto = require('crypto');
const fs = require('fs');
// Reading keys from files.
const privateKey = fs.readFileSync('./private.key');
const publicKey = fs.readFileSync('./public.key');
const data = Buffer.from("My Name is MHamzaRajput");
const signature = crypto.sign('RSA-SHA256', data, privateKey).toString("base64");
console.log("Signing done", signature);
const verify = crypto.verify('RSA-SHA256', data, publicKey, Buffer.from(signature, "base64"));
console.log("verfy done", verify);

Getting invalidCiphertextException: null when decrypting data which was encrypted with kms public key

I am building a POC based on asymmetric encryption where the public key from KMS will be downloaded and used on the client side to encrypt sensitive data and once that data is received at the server end it needs to be decrypted using KMS decrypt function.
Encryption and Decryption using KMS works fine but when I encrypt with the downloaded public key and then decrypt with KMS I get invalidCiphertextException: null
CMK Cryptographic Configuration is
https://i.stack.imgur.com/0muAb.png
The code I use for encryption is
var encrypt_with_public_key = function (data) {
let fs = require('fs'),
path = require('path'),
absolutePath = path.join(__dirname, 'Publickey.pem');
let publicKey = fs.readFileSync(absolutePath, "utf8");
let encrypted = crypto.publicEncrypt({
key: Buffer.from(publicKey),
oaepHash: "sha256",
},Buffer.from(data)).toString("base64");
return encrypted;
}
Code used for Decryption is
var decrypt_data = function (data) {
try {
let params = {
KeyId: kmsConfig["KeyId"],
EncryptionAlgorithm: kmsConfig["EncryptionAlgorithm"] /* RSAES_OAEP_SHA_256*/
}
params.CiphertextBlob = Buffer.from(data)
return kms.decrypt(params).promise().then(data => data.Plaintext);
}
catch (ex) {
throw ex
}
}

how to deterministically generate a key from a passphrase with nodejs crypto module

I want to generate a key pair, in NodeJS v10.16.3, using the crypto module, from the hash of a passphrase or seed.
I'm getting a public and private key and I'm able to sign with it.
They look like this;
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaScBwOHQRRIDzPb3cnz3TZfHYOG7
pM7M8PkZFq7DjElFUrkavQf9NSopuJcIZ0EOmlQq8jPLbLdHoUO9M9a1Pw==
-----END PUBLIC KEY-----
The code to create this;
Basically from the doc found here;
https://nodejs.org/api/crypto.html#crypto_crypto_generatekeypair_type_options_callback
var seed = "a very long and random passphrase or seed";
let sha256 = require('js-sha256');
seed = sha256(seed);
const { generateKeyPair } = require('crypto');
generateKeyPair('ec', {
namedCurve: 'P-256',
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem',
cipher: 'aes-256-cbc',
passphrase: seed
}
}, (err, publicKey, privateKey) => {
if (err) { throw err; }
console.log('Node Private : ', privateKey);
console.log('Node public : ', publicKey);
const fs = require('fs');
fs.writeFile("test.dat", privateKey, function(err) {
if(err) {
return console.log(err);
}
console.log("The file was saved!");
});
});
How can I generate a keypair using a seed so I can reproduce that same key again? The seed I am using is not changing but the key is.
If I want to create addresses by hashing the public key. I wouldn't be able to reproduce the same hash.
I want to do something similar to what is done with crypto wallet right now.

create RSA SHA256 using crypto for jsonwebtoken on nodejs

I see how to create HMAC 256 encrypted JWTs in NodeJS using Crypto, and the jsonwebtoken library. It's pretty easy
//encrypt & sign with HS256
const jwt = require('jsonwebtoken');
const pass = crypto.randomBytes(256).toString('hex');
const A = {algorithm:'HS256'};
const token = jwt.sign({ foo: 'bar' }, pass, A);
//decrypt & verify
jwt.verify(token, pass, A, function(err, decoded)
{
console.log('decode ',decoded);
console.log('err ',err);
res.send({error:err, text:decoded});
});
I would like to replace 'pass' with an 'RSA 256 SHA', and A with {algorithm:'RS256'}
In node crypto JS's documentation I see the command
const sign = crypto.createSign('RSA-SHA256');
to create the rsa-sha256
However then the documentation has the function
getPrivateKeySomehow()
Which is not defined or part of crypto.getPrivateKeySomehow()
So I guess I just need help getting the RSA-SHA-256 string returned from crypto, so that I can pass that into jsonwebtoken to sign my JWT.
Note: I do not want to read a private.key from a static file on my server because i see having a single private key for all my users as too big a security risk, hence why i am generating my own 256 bytes passwords and storing them off-site (not included in this post)
also, i'm not sure if i should do something like this (without using something like openssl from command line?)
const begin = '-----BEGIN RSA PRIVATE KEY-----\n';
const enc = crypto.randomBytes(256).toString('base64') + '\n';
const end = '-----END RSA PRIVATE KEY-----'
const pass = sign.sign(begin + enc + end);
const A = {algorithm:'RS256'};
const token = jwt.sign({ foo: 'bar' }, pass, A);
Take a look at this example bellow:
signExample = (str) => {
crypto.generateKeyPair('rsa', {
modulusLength: 1024,
publicKeyEncoding: {
type: 'spki',
format: 'pem'
},
privateKeyEncoding: {
type: 'pkcs8',
format: 'pem'
}
}, (err, publicKey, privateKey) => {
// sign String
var signerObject = crypto.createSign("RSA-SHA256");
signerObject.update(str);
var signature = signerObject.sign({key:privateKey,padding:crypto.constants.RSA_PKCS1_PSS_PADDING}, "base64");
console.info("signature: %s", signature);
//verify String
var verifierObject = crypto.createVerify("RSA-SHA256");
verifierObject.update(str);
var verified = verifierObject.verify({key:publicKey, padding:crypto.constants.RSA_PKCS1_PSS_PADDING}, signature, "base64");
console.info("is signature ok?: %s", verified);
});
}
Steps:
First you create an key pair with crypto.generateKeyPair('rsa', { ... desired key options
Create a Sign object - crypto.createSign("RSA-SHA256")
The string wanted to be signed - SignerObject.update(str)
Sign the string with your private key - signerObject.sign(
Option to salt - padding:crypto.constants.RSA_PKCS1_PSS_PADDING

Resources