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.
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
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
How about this node-rsa module? Here's a link to the test.js file that demonstrates usage.
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.
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
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)
}