Browse Source

tls: allow obvious key/passphrase combinations

Passphrase is now used whether keys are provided singly, in an array of
string/buffer, or an array of object, where it used to be ignored in
some argument combinations. Specifically, these now work as expected:

  key: [encryptedPem],
  passphrase: 'passphrase'

and

  key: [{pem: encryptedPem}]
  passphrase: 'passphrase'

and

  key: [{pem: unencryptedPem}]

PR-URL: https://github.com/nodejs/node/pull/10294
Reviewed-By: Fedor Indutny <fedor.indutny@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
v6.x
Sam Roberts 8 years ago
committed by Myles Borins
parent
commit
df9d8ee6cb
No known key found for this signature in database GPG Key ID: 933B01F40B5CA946
  1. 13
      doc/api/tls.md
  2. 10
      lib/_tls_common.js
  3. 3
      src/node_crypto.cc
  4. 97
      test/parallel/test-tls-passphrase.js

13
doc/api/tls.md

@ -891,12 +891,13 @@ added: v0.11.13
individually. PFX is usually encrypted, if it is, `passphrase` will be used individually. PFX is usually encrypted, if it is, `passphrase` will be used
to decrypt it. to decrypt it.
* `key` {string|string[]|Buffer|Buffer[]|Object[]} Optional private keys in * `key` {string|string[]|Buffer|Buffer[]|Object[]} Optional private keys in
PEM format. Single keys will be decrypted with `passphrase` if necessary. PEM format. PEM allows the option of private keys being encrypted. Encrypted
Multiple keys, probably using different algorithms, can be provided either keys will be decrypted with `options.passphrase`. Multiple keys using
as an array of unencrypted key strings or buffers, or an array of objects in different algorithms can be provided either as an array of unencrypted key
the form `{pem: <string|buffer>, passphrase: <string>}`. The object form can strings or buffers, or an array of objects in the form `{pem:
only occur in an array, and it _must_ include a passphrase, even if key is <string|buffer>[, passphrase: <string>]}`. The object form can only occur in
not encrypted. an array. `object.passphrase` is optional. Encrypted keys will be decrypted
with `object.passphrase` if provided, or `options.passphrase` if it is not.
* `passphrase` {string} Optional shared passphrase used for a single private * `passphrase` {string} Optional shared passphrase used for a single private
key and/or a PFX. key and/or a PFX.
* `cert` {string|string[]|Buffer|Buffer[]} Optional cert chains in PEM format. * `cert` {string|string[]|Buffer|Buffer[]} Optional cert chains in PEM format.

10
lib/_tls_common.js

@ -78,17 +78,11 @@ exports.createSecureContext = function createSecureContext(options, context) {
if (Array.isArray(options.key)) { if (Array.isArray(options.key)) {
for (i = 0; i < options.key.length; i++) { for (i = 0; i < options.key.length; i++) {
const key = options.key[i]; const key = options.key[i];
if (key.passphrase) const passphrase = key.passphrase || options.passphrase;
c.context.setKey(key.pem, key.passphrase); c.context.setKey(key.pem || key, passphrase);
else
c.context.setKey(key);
} }
} else { } else {
if (options.passphrase) {
c.context.setKey(options.key, options.passphrase); c.context.setKey(options.key, options.passphrase);
} else {
c.context.setKey(options.key);
}
} }
} }

3
src/node_crypto.cc

@ -446,6 +446,9 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) {
} }
if (len == 2) { if (len == 2) {
if (args[1]->IsUndefined() || args[1]->IsNull())
len = 1;
else
THROW_AND_RETURN_IF_NOT_STRING(args[1], "Pass phrase"); THROW_AND_RETURN_IF_NOT_STRING(args[1], "Pass phrase");
} }

97
test/parallel/test-tls-passphrase.js

@ -51,13 +51,12 @@ server.listen(0, common.mustCall(function() {
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: rawKey, key: rawKey,
passphrase: 'passphrase', // Ignored. passphrase: 'ignored',
cert: cert, cert: cert,
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
// Buffer[] // Buffer[]
/* XXX(sam) Should work, but its unimplemented ATM.
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [passKey], key: [passKey],
@ -65,7 +64,6 @@ server.listen(0, common.mustCall(function() {
cert: [cert], cert: [cert],
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
*/
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
@ -77,7 +75,7 @@ server.listen(0, common.mustCall(function() {
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [rawKey], key: [rawKey],
passphrase: 'passphrase', // Ignored. passphrase: 'ignored',
cert: [cert], cert: [cert],
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
@ -101,13 +99,12 @@ server.listen(0, common.mustCall(function() {
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: rawKey.toString(), key: rawKey.toString(),
passphrase: 'passphrase', // Ignored. passphrase: 'ignored',
cert: cert.toString(), cert: cert.toString(),
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
// String[] // String[]
/* XXX(sam) Should work, but its unimplemented ATM.
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [passKey.toString()], key: [passKey.toString()],
@ -115,7 +112,6 @@ server.listen(0, common.mustCall(function() {
cert: [cert.toString()], cert: [cert.toString()],
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
*/
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
@ -127,7 +123,7 @@ server.listen(0, common.mustCall(function() {
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [rawKey.toString()], key: [rawKey.toString()],
passphrase: 'passphrase', // Ignored. passphrase: 'ignored',
cert: [cert.toString()], cert: [cert.toString()],
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
@ -140,6 +136,22 @@ server.listen(0, common.mustCall(function() {
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
tls.connect({
port: this.address().port,
key: [{pem: passKey, passphrase: 'passphrase'}],
passphrase: 'ignored',
cert: cert,
rejectUnauthorized: false
}, common.mustCall(function() {}));
tls.connect({
port: this.address().port,
key: [{pem: passKey}],
passphrase: 'passphrase',
cert: cert,
rejectUnauthorized: false
}, common.mustCall(function() {}));
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [{pem: passKey.toString(), passphrase: 'passphrase'}], key: [{pem: passKey.toString(), passphrase: 'passphrase'}],
@ -149,23 +161,22 @@ server.listen(0, common.mustCall(function() {
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [{pem: rawKey, passphrase: 'passphrase'}], key: [{pem: rawKey, passphrase: 'ignored'}],
cert: cert, cert: cert,
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [{pem: rawKey.toString(), passphrase: 'passphrase'}], key: [{pem: rawKey.toString(), passphrase: 'ignored'}],
cert: cert, cert: cert,
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
/* XXX(sam) Should work, but unimplemented ATM
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [{pem: rawKey}], key: [{pem: rawKey}],
passphrase: 'passphrase', passphrase: 'ignored',
cert: cert, cert: cert,
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
@ -173,7 +184,7 @@ server.listen(0, common.mustCall(function() {
tls.connect({ tls.connect({
port: this.address().port, port: this.address().port,
key: [{pem: rawKey.toString()}], key: [{pem: rawKey.toString()}],
passphrase: 'passphrase', passphrase: 'ignored',
cert: cert, cert: cert,
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
@ -191,9 +202,37 @@ server.listen(0, common.mustCall(function() {
cert: cert, cert: cert,
rejectUnauthorized: false rejectUnauthorized: false
}, common.mustCall(function() {})); }, common.mustCall(function() {}));
*/
})).unref(); })).unref();
// Missing passphrase
assert.throws(function() {
tls.connect({
port: server.address().port,
key: passKey,
cert: cert,
rejectUnauthorized: false
});
}, /bad password read/);
assert.throws(function() {
tls.connect({
port: server.address().port,
key: [passKey],
cert: cert,
rejectUnauthorized: false
});
}, /bad password read/);
assert.throws(function() {
tls.connect({
port: server.address().port,
key: [{pem: passKey}],
cert: cert,
rejectUnauthorized: false
});
}, /bad password read/);
// Invalid passphrase
assert.throws(function() { assert.throws(function() {
tls.connect({ tls.connect({
port: server.address().port, port: server.address().port,
@ -203,3 +242,33 @@ assert.throws(function() {
rejectUnauthorized: false rejectUnauthorized: false
}); });
}, /bad decrypt/); }, /bad decrypt/);
assert.throws(function() {
tls.connect({
port: server.address().port,
key: [passKey],
passphrase: 'invalid',
cert: cert,
rejectUnauthorized: false
});
}, /bad decrypt/);
assert.throws(function() {
tls.connect({
port: server.address().port,
key: [{pem: passKey}],
passphrase: 'invalid',
cert: cert,
rejectUnauthorized: false
});
}, /bad decrypt/);
assert.throws(function() {
tls.connect({
port: server.address().port,
key: [{pem: passKey, passphrase: 'invalid'}],
passphrase: 'passphrase', // Valid but unused
cert: cert,
rejectUnauthorized: false
});
}, /bad decrypt/);

Loading…
Cancel
Save