From 48a71dbf4e3007a845aedcfa8c9ed6a7911cc2da Mon Sep 17 00:00:00 2001 From: Calvin Metcalf Date: Tue, 27 Jan 2015 14:58:22 -0500 Subject: [PATCH] crypto: allow password protected private keys with publicEncrypt Private keys may be used along with publicEncrypt since the private key includes the public one. This adds the ability to use encrypted private keys which previously threw an error. This commit also makes sure the user exposed functions have names. --- doc/api/crypto.markdown | 3 +++ lib/crypto.js | 27 ++++++++++++------------ test/parallel/test-crypto.js | 41 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/doc/api/crypto.markdown b/doc/api/crypto.markdown index b6834d0a9459..e93fc1b0b62b 100644 --- a/doc/api/crypto.markdown +++ b/doc/api/crypto.markdown @@ -678,10 +678,13 @@ Encrypts `buffer` with `public_key`. Only RSA is currently supported. `public_key` can be an object or a string. If `public_key` is a string, it is treated as the key with no passphrase and will use `RSA_PKCS1_OAEP_PADDING`. +Since RSA public keys may be derived from private keys you may pass a private +key to this method. `public_key`: * `key` : A string holding the PEM encoded private key +* `passphrase` : An optional string of passphrase for the private key * `padding` : An optional padding value, one of the following: * `constants.RSA_NO_PADDING` * `constants.RSA_PKCS1_PADDING` diff --git a/lib/crypto.js b/lib/crypto.js index f8efffda483e..84517f7397b2 100644 --- a/lib/crypto.js +++ b/lib/crypto.js @@ -336,16 +336,17 @@ Verify.prototype.verify = function(object, signature, sigEncoding) { return this._handle.verify(toBuf(object), toBuf(signature, sigEncoding)); }; -function rsaPublic(method, defaultPadding) { - return function(options, buffer) { +function makeRsaPublic(method, defaultPadding) { + return function rsaPublic(options, buffer) { var key = options.key || options; var padding = options.padding || defaultPadding; - return method(toBuf(key), buffer, padding); + var passphrase = options.passphrase || null; + return method(toBuf(key), buffer, padding, passphrase); }; } -function rsaPrivate(method, defaultPadding) { - return function(options, buffer) { +function makeRsaPrivate(method, defaultPadding) { + return function rsaPrivate(options, buffer) { var key = options.key || options; var passphrase = options.passphrase || null; var padding = options.padding || defaultPadding; @@ -353,14 +354,14 @@ function rsaPrivate(method, defaultPadding) { }; } -exports.publicEncrypt = rsaPublic(binding.publicEncrypt, - constants.RSA_PKCS1_OAEP_PADDING); -exports.publicDecrypt = rsaPublic(binding.publicDecrypt, - constants.RSA_PKCS1_PADDING); -exports.privateEncrypt = rsaPrivate(binding.privateEncrypt, - constants.RSA_PKCS1_PADDING); -exports.privateDecrypt = rsaPrivate(binding.privateDecrypt, - constants.RSA_PKCS1_OAEP_PADDING); +exports.publicEncrypt = makeRsaPublic(binding.publicEncrypt, + constants.RSA_PKCS1_OAEP_PADDING); +exports.publicDecrypt = makeRsaPublic(binding.publicDecrypt, + constants.RSA_PKCS1_PADDING); +exports.privateEncrypt = makeRsaPrivate(binding.privateEncrypt, + constants.RSA_PKCS1_PADDING); +exports.privateDecrypt = makeRsaPrivate(binding.privateDecrypt, + constants.RSA_PKCS1_OAEP_PADDING); exports.createDiffieHellman = exports.DiffieHellman = DiffieHellman; diff --git a/test/parallel/test-crypto.js b/test/parallel/test-crypto.js index 8198a6c9d601..f5001fd24a03 100644 --- a/test/parallel/test-crypto.js +++ b/test/parallel/test-crypto.js @@ -831,6 +831,28 @@ assert.equal(bad_dh.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); }, encryptedBuffer); assert.equal(input, decryptedBufferWithPassword.toString()); + encryptedBuffer = crypto.publicEncrypt({ + key: rsaKeyPemEncrypted, + passphrase: 'password' + }, bufferToEncrypt); + + decryptedBufferWithPassword = crypto.privateDecrypt({ + key: rsaKeyPemEncrypted, + passphrase: 'password' + }, encryptedBuffer); + assert.equal(input, decryptedBufferWithPassword.toString()); + + encryptedBuffer = crypto.privateEncrypt({ + key: rsaKeyPemEncrypted, + passphrase: new Buffer('password') + }, bufferToEncrypt); + + decryptedBufferWithPassword = crypto.publicDecrypt({ + key: rsaKeyPemEncrypted, + passphrase: new Buffer('password') + }, encryptedBuffer); + assert.equal(input, decryptedBufferWithPassword.toString()); + encryptedBuffer = crypto.publicEncrypt(certPem, bufferToEncrypt); decryptedBuffer = crypto.privateDecrypt(keyPem, encryptedBuffer); @@ -850,6 +872,25 @@ assert.equal(bad_dh.verifyError, constants.DH_NOT_SUITABLE_GENERATOR); crypto.privateDecrypt({ key: rsaKeyPemEncrypted, passphrase: 'wrong' + }, bufferToEncrypt); + }); + + assert.throws(function() { + crypto.publicEncrypt({ + key: rsaKeyPemEncrypted, + passphrase: 'wrong' + }, encryptedBuffer); + }); + + assert.throws(function() { + encryptedBuffer = crypto.privateEncrypt({ + key: rsaKeyPemEncrypted, + passphrase: new Buffer('password') + }, bufferToEncrypt); + + crypto.publicDecrypt({ + key: rsaKeyPemEncrypted, + passphrase: [].concat.apply([], new Buffer('password')) }, encryptedBuffer); }); })();