Skip to content

Commit 0b44384

Browse files
committed
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: #10294 Reviewed-By: Fedor Indutny <[email protected]> Reviewed-By: Ben Noordhuis <[email protected]>
1 parent 793d871 commit 0b44384

File tree

4 files changed

+97
-30
lines changed

4 files changed

+97
-30
lines changed

doc/api/tls.md

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

lib/_tls_common.js

+3-9
Original file line numberDiff line numberDiff line change
@@ -78,17 +78,11 @@ exports.createSecureContext = function createSecureContext(options, context) {
7878
if (Array.isArray(options.key)) {
7979
for (i = 0; i < options.key.length; i++) {
8080
const key = options.key[i];
81-
if (key.passphrase)
82-
c.context.setKey(key.pem, key.passphrase);
83-
else
84-
c.context.setKey(key);
81+
const passphrase = key.passphrase || options.passphrase;
82+
c.context.setKey(key.pem || key, passphrase);
8583
}
8684
} else {
87-
if (options.passphrase) {
88-
c.context.setKey(options.key, options.passphrase);
89-
} else {
90-
c.context.setKey(options.key);
91-
}
85+
c.context.setKey(options.key, options.passphrase);
9286
}
9387
}
9488

src/node_crypto.cc

+4-1
Original file line numberDiff line numberDiff line change
@@ -442,7 +442,10 @@ void SecureContext::SetKey(const FunctionCallbackInfo<Value>& args) {
442442
}
443443

444444
if (len == 2) {
445-
THROW_AND_RETURN_IF_NOT_STRING(args[1], "Pass phrase");
445+
if (args[1]->IsUndefined() || args[1]->IsNull())
446+
len = 1;
447+
else
448+
THROW_AND_RETURN_IF_NOT_STRING(args[1], "Pass phrase");
446449
}
447450

448451
BIO *bio = LoadBIO(env, args[0]);

test/parallel/test-tls-passphrase.js

+83-14
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,19 @@ server.listen(0, common.mustCall(function() {
5151
tls.connect({
5252
port: this.address().port,
5353
key: rawKey,
54-
passphrase: 'passphrase', // Ignored.
54+
passphrase: 'ignored',
5555
cert: cert,
5656
rejectUnauthorized: false
5757
}, common.mustCall(function() {}));
5858

5959
// Buffer[]
60-
/* XXX(sam) Should work, but its unimplemented ATM.
6160
tls.connect({
6261
port: this.address().port,
6362
key: [passKey],
6463
passphrase: 'passphrase',
6564
cert: [cert],
6665
rejectUnauthorized: false
6766
}, common.mustCall(function() {}));
68-
*/
6967

7068
tls.connect({
7169
port: this.address().port,
@@ -77,7 +75,7 @@ server.listen(0, common.mustCall(function() {
7775
tls.connect({
7876
port: this.address().port,
7977
key: [rawKey],
80-
passphrase: 'passphrase', // Ignored.
78+
passphrase: 'ignored',
8179
cert: [cert],
8280
rejectUnauthorized: false
8381
}, common.mustCall(function() {}));
@@ -101,21 +99,19 @@ server.listen(0, common.mustCall(function() {
10199
tls.connect({
102100
port: this.address().port,
103101
key: rawKey.toString(),
104-
passphrase: 'passphrase', // Ignored.
102+
passphrase: 'ignored',
105103
cert: cert.toString(),
106104
rejectUnauthorized: false
107105
}, common.mustCall(function() {}));
108106

109107
// String[]
110-
/* XXX(sam) Should work, but its unimplemented ATM.
111108
tls.connect({
112109
port: this.address().port,
113110
key: [passKey.toString()],
114111
passphrase: 'passphrase',
115112
cert: [cert.toString()],
116113
rejectUnauthorized: false
117114
}, common.mustCall(function() {}));
118-
*/
119115

120116
tls.connect({
121117
port: this.address().port,
@@ -127,7 +123,7 @@ server.listen(0, common.mustCall(function() {
127123
tls.connect({
128124
port: this.address().port,
129125
key: [rawKey.toString()],
130-
passphrase: 'passphrase', // Ignored.
126+
passphrase: 'ignored',
131127
cert: [cert.toString()],
132128
rejectUnauthorized: false
133129
}, common.mustCall(function() {}));
@@ -140,6 +136,22 @@ server.listen(0, common.mustCall(function() {
140136
rejectUnauthorized: false
141137
}, common.mustCall(function() {}));
142138

139+
tls.connect({
140+
port: this.address().port,
141+
key: [{pem: passKey, passphrase: 'passphrase'}],
142+
passphrase: 'ignored',
143+
cert: cert,
144+
rejectUnauthorized: false
145+
}, common.mustCall(function() {}));
146+
147+
tls.connect({
148+
port: this.address().port,
149+
key: [{pem: passKey}],
150+
passphrase: 'passphrase',
151+
cert: cert,
152+
rejectUnauthorized: false
153+
}, common.mustCall(function() {}));
154+
143155
tls.connect({
144156
port: this.address().port,
145157
key: [{pem: passKey.toString(), passphrase: 'passphrase'}],
@@ -149,31 +161,30 @@ server.listen(0, common.mustCall(function() {
149161

150162
tls.connect({
151163
port: this.address().port,
152-
key: [{pem: rawKey, passphrase: 'passphrase'}],
164+
key: [{pem: rawKey, passphrase: 'ignored'}],
153165
cert: cert,
154166
rejectUnauthorized: false
155167
}, common.mustCall(function() {}));
156168

157169
tls.connect({
158170
port: this.address().port,
159-
key: [{pem: rawKey.toString(), passphrase: 'passphrase'}],
171+
key: [{pem: rawKey.toString(), passphrase: 'ignored'}],
160172
cert: cert,
161173
rejectUnauthorized: false
162174
}, common.mustCall(function() {}));
163175

164-
/* XXX(sam) Should work, but unimplemented ATM
165176
tls.connect({
166177
port: this.address().port,
167178
key: [{pem: rawKey}],
168-
passphrase: 'passphrase',
179+
passphrase: 'ignored',
169180
cert: cert,
170181
rejectUnauthorized: false
171182
}, common.mustCall(function() {}));
172183

173184
tls.connect({
174185
port: this.address().port,
175186
key: [{pem: rawKey.toString()}],
176-
passphrase: 'passphrase',
187+
passphrase: 'ignored',
177188
cert: cert,
178189
rejectUnauthorized: false
179190
}, common.mustCall(function() {}));
@@ -191,9 +202,37 @@ server.listen(0, common.mustCall(function() {
191202
cert: cert,
192203
rejectUnauthorized: false
193204
}, common.mustCall(function() {}));
194-
*/
195205
})).unref();
196206

207+
// Missing passphrase
208+
assert.throws(function() {
209+
tls.connect({
210+
port: server.address().port,
211+
key: passKey,
212+
cert: cert,
213+
rejectUnauthorized: false
214+
});
215+
}, /bad password read/);
216+
217+
assert.throws(function() {
218+
tls.connect({
219+
port: server.address().port,
220+
key: [passKey],
221+
cert: cert,
222+
rejectUnauthorized: false
223+
});
224+
}, /bad password read/);
225+
226+
assert.throws(function() {
227+
tls.connect({
228+
port: server.address().port,
229+
key: [{pem: passKey}],
230+
cert: cert,
231+
rejectUnauthorized: false
232+
});
233+
}, /bad password read/);
234+
235+
// Invalid passphrase
197236
assert.throws(function() {
198237
tls.connect({
199238
port: server.address().port,
@@ -203,3 +242,33 @@ assert.throws(function() {
203242
rejectUnauthorized: false
204243
});
205244
}, /bad decrypt/);
245+
246+
assert.throws(function() {
247+
tls.connect({
248+
port: server.address().port,
249+
key: [passKey],
250+
passphrase: 'invalid',
251+
cert: cert,
252+
rejectUnauthorized: false
253+
});
254+
}, /bad decrypt/);
255+
256+
assert.throws(function() {
257+
tls.connect({
258+
port: server.address().port,
259+
key: [{pem: passKey}],
260+
passphrase: 'invalid',
261+
cert: cert,
262+
rejectUnauthorized: false
263+
});
264+
}, /bad decrypt/);
265+
266+
assert.throws(function() {
267+
tls.connect({
268+
port: server.address().port,
269+
key: [{pem: passKey, passphrase: 'invalid'}],
270+
passphrase: 'passphrase', // Valid but unused
271+
cert: cert,
272+
rejectUnauthorized: false
273+
});
274+
}, /bad decrypt/);

0 commit comments

Comments
 (0)