Encrypting data with Public Key in node.js

2019-01-16 04:55发布

问题:

I need to encrypt a string using a public key (pem file), and then sign it using a private key (also a pem).

I am loading the pem files fine:

publicCert = fs.readFileSync(publicCertFile).toString();

but after hours of scouring google I can't seem to find a way to encrypt data using the public key. In php I simply call openssl_public_encrypt, but I don't see any corresponding function in node or in any modules.

If anyone has any suggestions, let me know.

回答1:

No library necessary friends,

Enter crypto

Here's a janky little module you could use to encrypt/decrypt strings with RSA keys:

var crypto = require("crypto");
var path = require("path");
var fs = require("fs");

var encryptStringWithRsaPublicKey = function(toEncrypt, relativeOrAbsolutePathToPublicKey) {
    var absolutePath = path.resolve(relativeOrAbsolutePathToPublicKey);
    var publicKey = fs.readFileSync(absolutePath, "utf8");
    var buffer = new Buffer(toEncrypt);
    var encrypted = crypto.publicEncrypt(publicKey, buffer);
    return encrypted.toString("base64");
};

var decryptStringWithRsaPrivateKey = function(toDecrypt, relativeOrAbsolutePathtoPrivateKey) {
    var absolutePath = path.resolve(relativeOrAbsolutePathtoPrivateKey);
    var privateKey = fs.readFileSync(absolutePath, "utf8");
    var buffer = new Buffer(toDecrypt, "base64");
    var decrypted = crypto.privateDecrypt(privateKey, buffer);
    return decrypted.toString("utf8");
};

module.exports = {
    encryptStringWithRsaPublicKey: encryptStringWithRsaPublicKey,
    decryptStringWithRsaPrivateKey: decryptStringWithRsaPrivateKey
}

I would recommend not using synchronous fs methods where possible, and you could use Promises to make this better, but for simple use cases this is the approach that I have seen work and would take



回答2:

The updated public/private decrypt and encryption module is URSA. node-rsa module is outdated.

This Node module provides a fairly complete set of wrappers for the RSA public/private key crypto functionality of OpenSSL.

npm install ursa

See: https://github.com/Obvious/ursa



回答3:

How about this node-rsa module? Here's a link to the test.js file that demonstrates usage.



回答4:

TL;DR: Ursa is your best bet. Its really funky that this doesn't come standard with node crypto.

Every other solutions I found either doesn't work in windows or aren't actually encryption libraries. Ursa, recommended by Louie, looks like the best bet. If you don't care about windows, you're even more golden. Note on Ursa, I had to install Open SSL along with something called "Visual C++ 2008 Redistributables" in order to get the npm install to work. Get that junk here: http://slproweb.com/products/Win32OpenSSL.html

The breakdown:

  • Annoying additional manual installation steps for windows
    • https://github.com/Obvious/ursa - probably the best of the lot
  • Not compatible with windows
    • https://npmjs.org/package/rsautl - says BADPLATFORM
    • https://github.com/katyo/node-rsa - node-waf isn't available on windows
    • https://github.com/paspao/simple_rsa_encrypt - unistd.h isn't on windows
    • https://npmjs.org/package/pripub - large amounts of linker errors, also not on github
  • Not encryption libraries
    • https://github.com/substack/secure-peer
    • https://github.com/substack/rsa-json - just generates keys, doesn't use them
    • https://github.com/substack/rsa-unpack - just unpacks PEM strings

This is literally all I could find.



回答5:

This is not supported natively by node version v0.11.13 or below but it seems that next version of node ( a.k.a v0.12) will support this.

Here is the clue: https://github.com/joyent/node/blob/v0.12/lib/crypto.js#L358

see crypto.publicEncrypt and crypto.privateDecrypt

Here is the future documentation for this https://github.com/joyent/node/blob/7c0419730b237dbfa0ec4e6fb33a99ff01825a8f/doc/api/crypto.markdown#cryptopublicencryptpublic_key-buffer



回答6:

I tested this in Node 10, you can use encrypt/decrypt functions (small changes on Jacob's answer)

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

function encrypt(toEncrypt, relativeOrAbsolutePathToPublicKey) {
  const absolutePath = path.resolve(relativeOrAbsolutePathToPublicKey)
  const publicKey = fs.readFileSync(absolutePath, 'utf8')
  const buffer = Buffer.from(toEncrypt, 'utf8')
  const encrypted = crypto.publicEncrypt(publicKey, buffer)
  return encrypted.toString('base64')
}

function decrypt(toDecrypt, relativeOrAbsolutePathtoPrivateKey) {
  const absolutePath = path.resolve(relativeOrAbsolutePathtoPrivateKey)
  const privateKey = fs.readFileSync(absolutePath, 'utf8')
  const buffer = Buffer.from(toDecrypt, 'base64')
  const decrypted = crypto.privateDecrypt(
    {
      key: privateKey.toString(),
      passphrase,
    },
    buffer,
  )
  return decrypted.toString('utf8')
}

const enc = encrypt('hello', `public.pem`)
console.log('enc', enc)

const dec = decrypt(enc, `private.pem`)
console.log('dec', dec)

For the keys you can generate them with

const { writeFileSync } = require('fs')
const { generateKeyPairSync } = require('crypto')

function generateKeys() {
  const { privateKey, publicKey } = generateKeyPairSync('rsa', {
    modulusLength: 4096,
    publicKeyEncoding: {
      type: 'pkcs1',
      format: 'pem',
    },
    privateKeyEncoding: {
      type: 'pkcs1',
      format: 'pem',
      cipher: 'aes-256-cbc',
      passphrase,
    },
  })

  writeFileSync('private.pem', privateKey)
  writeFileSync('public.pem', publicKey)
}