78

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?

undefined
  • 33,537
  • 22
  • 129
  • 198
Dail
  • 4,622
  • 16
  • 74
  • 109
  • I'm not sure how you're going to give the users their private keys securely. I would be better if they generated their key-pairs locally and gave you their public keys. – Bruno Dec 15 '11 at 13:42
  • 3
    @Bruno yes, no problem about it... my goal is to understand how to create a pair of keys(private,public) with Node.js, is this possible? – Dail Dec 15 '11 at 14:06

9 Answers9

78

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.
});
Nelson Owalo
  • 2,324
  • 18
  • 37
  • 1
    This produces PEM-encoded RSA Public key. What if I need OpenSSH (ssh-rsa) public key? Is there a native js way to do, without 3rd party modules? – Elias Goss Nov 19 '18 at 04:21
  • 1
    What do you mean? `ssh-keygen`? PEM is just a file format, they ( both functions) give you the same result. You will have both a public key and a private key which you can use. – Nelson Owalo Nov 19 '18 at 06:44
  • 1
    this code outputs to PKCS#8 format (-----BEGIN PUBLIC KEY-----) and I want ssh-rsa format, which can be used in .ssh/authorised_keys file - I know how to convert with ssh-gen or openssl, but I want to do it with JavaScript only – Elias Goss Nov 19 '18 at 06:58
  • So if I understand correctly, you need an `RSA PUBLIC KEY` instead of just a `PUBLIC KEY`? – Nelson Owalo Nov 19 '18 at 07:13
  • 2
    No, I need OpenSSH format, which starts with ssh-rsa. And RSA PUBLIC KEY is a different format, it's PKCS#1 – Elias Goss Nov 19 '18 at 07:49
  • I will try and check it out later, but you might get better response if you ask it as a new question – Nelson Owalo Nov 19 '18 at 08:45
  • What are the possible ciphers? – Master James Jan 22 '19 at 14:28
  • How do you generate new public givens with this private key? – SISYN Aug 18 '19 at 01:44
  • @EliasGoss did you find out? – David De Anda Oct 15 '19 at 23:49
  • 2
    @DavidDeAnda you can do this with the lib `node-forge`. After generating the `publicKey` and `privateKey` as above you can do `forge.ssh.publicKeyToOpenSSH(forge.pki.publicKeyFromPem(publicKey))` and `forge.ssh.privateKeyToOpenSSH(forge.pki.privateKeyFromPem(privateKey))`. Those functions do return the strings you need for OpenSSH. – maxarndt May 15 '20 at 12:30
28

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

Aks
  • 1,567
  • 13
  • 23
  • 1
    This answer didn't cover encrypting and decrypting. – fadedbee Feb 06 '14 at 22:05
  • 27
    The question didn't ask for encryption and decryption. It only asked for key pair generation. And encryption and decryption is very well explained in documentation. – Aks Feb 07 '14 at 06:44
  • 4
    I like this, but how to get a PEM formatted key pairs with crypto? – Daniele Cruciani Feb 07 '14 at 15:51
  • 3
    @Aks as someone fairly new to encryption, I didn't find it explained at all on http://nodejs.org/api/crypto.html#crypto_diffiehellman_computesecret_other_public_key_input_encoding_output_encoding - did I miss something? – fadedbee Feb 14 '14 at 09:17
  • 35
    This generates a DH keypair, very different to you normal RSA or DSA keypairs. You were warned. – jduncanator Apr 16 '14 at 04:21
  • public and private key encryption and decryption can be achieved using crypto library methods publicEncrypt and privateDecrypt. Link:- https://nodejs.org/api/crypto.html#cryptopublicencryptkey-buffer – badrik patel Mar 29 '22 at 07:44
17

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.

fadedbee
  • 42,671
  • 44
  • 178
  • 308
  • 6
    Chris: Your comment in the code says encrypt with private and decrypt with public, but the code does the opposite: encrypts with public and decrypts with private. When I do try encrypting with private, pub.decrypt(enc) tells me that the decrypt function on pub is undefined! Any thoughts. Thx – HarleyDave Jul 09 '15 at 13:28
17
const crypto = require('crypto');

  const { privateKey, publicKey } = crypto.generateKeyPairSync('rsa', {
    modulusLength: 2048,
    publicKeyEncoding: {
      type: 'spki',
      format: 'pem'
    },
    privateKeyEncoding: {
      type: 'pkcs8',
      format: 'pem'
    }
  }); 
Nats_Ayala
  • 199
  • 1
  • 5
13

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();  
});
kgilpin
  • 2,201
  • 18
  • 18
8

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!

  • 1
    Can it be that `require("crypto").sign` and `require("crypto").verify` doesn't accept/use `padding` as property in third key-Parameter object ? – ahe_borriglione Jan 07 '22 at 13:58
0

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).

Guido
  • 46,642
  • 28
  • 120
  • 174
0

child_process route is a terrible and non-scalable solution imo. Stay away.

I chose to go with keypair instead.

Kalle Richter
  • 8,008
  • 26
  • 77
  • 177
redditmerc
  • 125
  • 1
  • 6
  • 6
    `keypair` is much slower than OpenSSL and is also synchronous whereas the OpenSSL route is asynchronous. The child_process route is _more_ scalable. – Mark K Cowan May 29 '15 at 11:03
  • I second this. On my laptop, running `openssl` in shild process works ~80 times faster than `keypair` and infinitely faster than native crypto module. – AndroC May 02 '18 at 13:41
  • 2
    but the child_process has a dependency on native library. If the library is not there it can error out – rbansal Sep 25 '20 at 07:52
  • It's a pity nodejs doesn't provide this functionality directly, using native openssl when it's available for performance, and falling back to an in-the-box implementation if openssl isn't available. – Gershom Maes Oct 18 '22 at 06:04
0

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).

Steve Campbell
  • 3,385
  • 1
  • 31
  • 43