From 21e2413b2b661ac63e97f029b24fdc5c9a2793e7 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Sat, 12 Aug 2017 19:06:35 +0300 Subject: [PATCH 1/7] tls: multiple PFX in createSecureContext Add support for multiple PFX files in tls.createSecureContext. Also added support for object-style PFX pass. Fixes: https://github.com/nodejs/node/issues/14756 --- doc/api/tls.md | 10 ++++-- lib/_tls_common.js | 35 +++++++++++++------ test/fixtures/keys/ec-pfx.pem | Bin 0 -> 1006 bytes test/parallel/test-tls-multi-pfx.js | 50 ++++++++++++++++++++++++++++ 4 files changed, 82 insertions(+), 13 deletions(-) create mode 100644 test/fixtures/keys/ec-pfx.pem create mode 100644 test/parallel/test-tls-multi-pfx.js diff --git a/doc/api/tls.md b/doc/api/tls.md index e18bbb62b1908f..5e6b1199726ffd 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -934,10 +934,14 @@ changes: --> * `options` {Object} - * `pfx` {string|Buffer} Optional PFX or PKCS12 encoded private key and - certificate chain. `pfx` is an alternative to providing `key` and `cert` + * `pfx` {Buffer|Buffer[]|Object[]} Optional PFX or PKCS12 encoded private key + and certificate chain. `pfx` is an alternative to providing `key` and `cert` individually. PFX is usually encrypted, if it is, `passphrase` will be used - to decrypt it. + to decrypt it. Multiple PFX can be provided either as an array of unencrypted + PFX buffers, or an array of objects in the form `{buffer: [, + passphrase: ]}`. The object form can only occur in an array. + `object.passphrase` is optional. Encrypted PFX will be decrypted with + `object.passphrase` if provided, or `options.passphrase` if it is not. * `key` {string|string[]|Buffer|Buffer[]|Object[]} Optional private keys in PEM format. PEM allows the option of private keys being encrypted. Encrypted keys will be decrypted with `options.passphrase`. Multiple keys using diff --git a/lib/_tls_common.js b/lib/_tls_common.js index ed0bd4c23fe240..1d374b194ea13c 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -161,19 +161,34 @@ exports.createSecureContext = function createSecureContext(options, context) { } if (options.pfx) { - var pfx = options.pfx; - if (!crypto) crypto = require('crypto'); - - pfx = crypto._toBuf(pfx); - if (passphrase) - passphrase = crypto._toBuf(passphrase); - - if (passphrase) { - c.context.loadPKCS12(pfx, passphrase); + const Buffer = require('buffer').Buffer; + + if (Array.isArray(options.pfx)) { + for (i = 0; i < options.pfx.length; i++) { + const pfx = options.pfx[i]; + let buf; + if (pfx.buffer instanceof Buffer) { + buf = crypto._toBuf(pfx.buffer); + } else { + buf = crypto._toBuf(pfx); + } + const passphrase = pfx.passphrase || options.passphrase; + if (passphrase) { + c.context.loadPKCS12(buf, crypto._toBuf(passphrase)); + } else { + c.context.loadPKCS12(buf); + } + } } else { - c.context.loadPKCS12(pfx); + const buf = crypto._toBuf(options.pfx); + const passphrase = options.passphrase; + if (passphrase) { + c.context.loadPKCS12(buf, crypto._toBuf(passphrase)); + } else { + c.context.loadPKCS12(buf); + } } } diff --git a/test/fixtures/keys/ec-pfx.pem b/test/fixtures/keys/ec-pfx.pem new file mode 100644 index 0000000000000000000000000000000000000000..1478cafeb520a31ff4ad0ea642c8d37f714ae4c8 GIT binary patch literal 1006 zcmXqLVt&QM$ZXKWyn&5VtIebBJ1-+Uo4NjC=ns#blU)C7$enI0zT|s| zP?X5QZ>t{tQQfxgV%)5Q*Xy5zPmQ*kad%@Q^M|rz-Ln}*b0%9Z+1HT!&n$w~*+=!p z1Sz5H)d{(Iy1V<-9o4qZJM(MlLyvDi)qjZb?QEDMyiJSGFtM=vQhMA9Bgx_hkRn2+Sl6`gUt_g2HpZzi2h@DwWI8ULS zVV+1@;kJEEb571l(tMYE#k%)=it?p{YhO)C?u}W0mci(7pvuNA(|ja!uc*o$o~S;- zAjlsgval?`z z7bDYx#w`Yo8&T3FGbmlEG)ahVG88=mN|!8+6NF|tskcQL?0xvGdF#m~XTELDUY?V3 zd7;Js0KL0UFLI|pX|JrWG`{}7;~&Q=4%;_hC#c2U?a=ddTK}>5`qNAA{Ur)_n{WK} z_SB1t>}J-;+6(to6|&QOX8f0UaluSj>O|A7$?eSrADg~D;@0ChuE6^{!tI5@-$6_Hn`u K_xwK~&jJ89alIY@ literal 0 HcmV?d00001 diff --git a/test/parallel/test-tls-multi-pfx.js b/test/parallel/test-tls-multi-pfx.js new file mode 100644 index 00000000000000..6c27ab6dfb23f5 --- /dev/null +++ b/test/parallel/test-tls-multi-pfx.js @@ -0,0 +1,50 @@ +'use strict'; +const common = require('../common'); +if (!common.hasCrypto) + common.skip('missing crypto'); + +const assert = require('assert'); +const tls = require('tls'); +const fs = require('fs'); + +const options = { + pfx: [ + { + buffer: fs.readFileSync(`${common.fixturesDir}/keys/agent1-pfx.pem`), + passphrase: 'sample' + }, + fs.readFileSync(`${common.fixturesDir}/keys/ec-pfx.pem`) + ] +}; + +const ciphers = []; + +const server = tls.createServer(options, function(conn) { + conn.end('ok'); +}).listen(0, function() { + const ecdsa = tls.connect(this.address().port, { + ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384', + rejectUnauthorized: false + }, function() { + ciphers.push(ecdsa.getCipher()); + const rsa = tls.connect(server.address().port, { + ciphers: 'ECDHE-RSA-AES256-GCM-SHA384', + rejectUnauthorized: false + }, function() { + ciphers.push(rsa.getCipher()); + ecdsa.end(); + rsa.end(); + server.close(); + }); + }); +}); + +process.on('exit', function() { + assert.deepStrictEqual(ciphers, [{ + name: 'ECDHE-ECDSA-AES256-GCM-SHA384', + version: 'TLSv1/SSLv3' + }, { + name: 'ECDHE-RSA-AES256-GCM-SHA384', + version: 'TLSv1/SSLv3' + }]); +}); From a3897801ee2c13ed3323e5ef9a5eadd66dc6252b Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Wed, 23 Aug 2017 21:50:46 +0300 Subject: [PATCH 2/7] Fix buffer type check --- lib/_tls_common.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/_tls_common.js b/lib/_tls_common.js index 1d374b194ea13c..abd877a49b1e83 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -163,13 +163,12 @@ exports.createSecureContext = function createSecureContext(options, context) { if (options.pfx) { if (!crypto) crypto = require('crypto'); - const Buffer = require('buffer').Buffer; if (Array.isArray(options.pfx)) { for (i = 0; i < options.pfx.length; i++) { const pfx = options.pfx[i]; let buf; - if (pfx.buffer instanceof Buffer) { + if (ArrayBuffer.isView(pfx.buffer)) { buf = crypto._toBuf(pfx.buffer); } else { buf = crypto._toBuf(pfx); From f8fdff474f9e2d59e196eb92101fefda11960a6e Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Wed, 23 Aug 2017 21:56:59 +0300 Subject: [PATCH 3/7] Use fixtures API in test --- test/parallel/test-tls-multi-pfx.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/parallel/test-tls-multi-pfx.js b/test/parallel/test-tls-multi-pfx.js index 6c27ab6dfb23f5..a76e5f2708d12b 100644 --- a/test/parallel/test-tls-multi-pfx.js +++ b/test/parallel/test-tls-multi-pfx.js @@ -5,15 +5,15 @@ if (!common.hasCrypto) const assert = require('assert'); const tls = require('tls'); -const fs = require('fs'); +const fixtures = require('../common/fixtures'); const options = { pfx: [ { - buffer: fs.readFileSync(`${common.fixturesDir}/keys/agent1-pfx.pem`), + buffer: fixtures.readKey('agent1-pfx.pem'), passphrase: 'sample' }, - fs.readFileSync(`${common.fixturesDir}/keys/ec-pfx.pem`) + fixtures.readKey('ec-pfx.pem') ] }; From f2607b38522b3ba51ab24ac5b03af398b95cfd7d Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Wed, 23 Aug 2017 21:58:15 +0300 Subject: [PATCH 4/7] Use mustCall for test callbacks --- test/parallel/test-tls-multi-pfx.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/parallel/test-tls-multi-pfx.js b/test/parallel/test-tls-multi-pfx.js index a76e5f2708d12b..6a9ebb1554f711 100644 --- a/test/parallel/test-tls-multi-pfx.js +++ b/test/parallel/test-tls-multi-pfx.js @@ -25,18 +25,18 @@ const server = tls.createServer(options, function(conn) { const ecdsa = tls.connect(this.address().port, { ciphers: 'ECDHE-ECDSA-AES256-GCM-SHA384', rejectUnauthorized: false - }, function() { + }, common.mustCall(function() { ciphers.push(ecdsa.getCipher()); const rsa = tls.connect(server.address().port, { ciphers: 'ECDHE-RSA-AES256-GCM-SHA384', rejectUnauthorized: false - }, function() { + }, common.mustCall(function() { ciphers.push(rsa.getCipher()); ecdsa.end(); rsa.end(); server.close(); - }); - }); + })); + })); }); process.on('exit', function() { From 17a95222a55b3aec584df601c4d0976e1c1117a6 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Thu, 24 Aug 2017 00:41:04 +0300 Subject: [PATCH 5/7] Add EC PFX to fixtures Makefile --- test/fixtures/keys/Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/fixtures/keys/Makefile b/test/fixtures/keys/Makefile index c7390eda0eefc4..27fda1eef27c87 100644 --- a/test/fixtures/keys/Makefile +++ b/test/fixtures/keys/Makefile @@ -335,6 +335,14 @@ ec-cert.pem: ec-csr.pem ec-key.pem -signkey ec-key.pem \ -out ec-cert.pem +ec-pfx.pem: ec-cert.pem ec-key.pem + openssl pkcs12 -export \ + -descert \ + -in ec-cert.pem \ + -inkey ec-key.pem \ + -out ec-pfx.pem \ + -password pass: + dh512.pem: openssl dhparam -out dh512.pem 512 From be2756d99d450e705f209605f5dc1e00241922d8 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Thu, 24 Aug 2017 00:41:43 +0300 Subject: [PATCH 6/7] Fix EC PFX fixture to be FIPS-compatible --- test/fixtures/keys/ec-pfx.pem | Bin 1006 -> 1006 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/test/fixtures/keys/ec-pfx.pem b/test/fixtures/keys/ec-pfx.pem index 1478cafeb520a31ff4ad0ea642c8d37f714ae4c8..3a4aa7dd696085b04b9a169fb0bfaccb296ee714 100644 GIT binary patch delta 820 zcmV-41IzsG2kr-uSbqaB4g?63x#`L!tu`zI0tf(rf&xfWblzqeZOyd0ByGY#m*%a@ z2pdpcQ{4;`nFY}eh!-Os~LR4z>|J_AXgmt_|N^{!%8vFMk9>_%!qhBj_S|rRkeqFC5bQ zSDBwW#zt81coxvc;_e=6dE5BFFCyoV*SO-r9;iAeyOG;Oc?;^Q@*XOh6`iG~f=K(k zI>DjG_{VhBXnzlj+}-xP4^piZQ(nA6G2A1!duzb(x^T-KK$g=04h%oQ^nJeX-qS%f zo0OS&KnivNj&RmGTT_t8GP<(-J62iV6bJ+%Jw08)43fE?W?4rzs!Ojx(@y(rxXxp` z%Z=dV>NL61wn10XBa9eCntH4@`yPRV8Je0i+)VAva(_0I=P|yyS*m2UZyTZRiPdXF zT8>Wo<#}=JqHg0DmGe;SAL|JJa7E`4GCz958vZerp|oX{muGHOIDz4grcAcFPq7Etn&au?^i#;&_qXQLC8WZioVFF?p zfez8HL1Di*sjv|gXevn!LVS9trXA+*Xw=OYR0J(I38ec`&a+(ZyW3|_1Ab3g{^!{K zXy+^7$Z%bV7f;G`|F;ANs36_vs7&ta)7z|aceJ>?Q9>%4D)FT}jKylGpxb8Mv4l92 zwE{qY+m!@i9jv<60s;sC1c8u1Kb??xc-KM1Ok~)7r!mI5@Nlg|fK$ydcU6s8ZojWS z0PT76!CQ^@y6y(H9U~QbG@!w({$48#KWpAy`wx>uOn%cv>CG^)nmO97FK)OK`XHpV z-iIJsd`%td7#DV$k`x(|4N=}W#QmwG9ixFD#l~*PmQ=g^1wxgqx}GvX_3>r1?B_4z yIuuj15*NXWlavEEBGef`DVD~Q+jY|LUg9A3E!7;OvIGc73p4WY;j;h&0tf*44U`iA delta 820 zcmV-41IzsG2kr-uSbqjE4g?64_=If>9~<`q0tf(rf&xed2TQ2d68qyosJ0Px$vakt zCr_ntlq!tMB7u~I%>?}1+ae<6;Ctty2Toal9x!HP#=o09!q zo^#m|GN%~`1-CO&i<`vt;{P+>Jv9!!W}Un-15UmVdD%iOV1EcjFYyYUWoy0!dW5d< zU9(zrE@~M@kxGA{Oa+M#j0p&XYBz07 z51{Ea=9V-!|12zEj5p;tp#?qNuL(_QuhCWbNV$?l{IG48Jr|_+R1#Vg!S<@+{3W)o z(O#Ot*MHC)GVQWKg$m(rA=>VjGo1 From 501fe05cd1ec4056efb157b5d7a25d67bd70ebd2 Mon Sep 17 00:00:00 2001 From: Yury Popov Date: Tue, 29 Aug 2017 12:29:58 +0300 Subject: [PATCH 7/7] Rename buffer to buf, doc string and string[] support --- doc/api/tls.md | 16 ++++++++-------- lib/_tls_common.js | 8 ++------ test/parallel/test-tls-multi-pfx.js | 2 +- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/doc/api/tls.md b/doc/api/tls.md index 5e6b1199726ffd..ebcf85438fdeb5 100644 --- a/doc/api/tls.md +++ b/doc/api/tls.md @@ -934,14 +934,14 @@ changes: --> * `options` {Object} - * `pfx` {Buffer|Buffer[]|Object[]} Optional PFX or PKCS12 encoded private key - and certificate chain. `pfx` is an alternative to providing `key` and `cert` - individually. PFX is usually encrypted, if it is, `passphrase` will be used - to decrypt it. Multiple PFX can be provided either as an array of unencrypted - PFX buffers, or an array of objects in the form `{buffer: [, - passphrase: ]}`. The object form can only occur in an array. - `object.passphrase` is optional. Encrypted PFX will be decrypted with - `object.passphrase` if provided, or `options.passphrase` if it is not. + * `pfx` {string|string[]|Buffer|Buffer[]|Object[]} Optional PFX or PKCS12 + encoded private key and certificate chain. `pfx` is an alternative to + providing `key` and `cert` individually. PFX is usually encrypted, if it is, + `passphrase` will be used to decrypt it. Multiple PFX can be provided either + as an array of unencrypted PFX buffers, or an array of objects in the form + `{buf: [, passphrase: ]}`. The object form can only + occur in an array. `object.passphrase` is optional. Encrypted PFX will be + decrypted with `object.passphrase` if provided, or `options.passphrase` if it is not. * `key` {string|string[]|Buffer|Buffer[]|Object[]} Optional private keys in PEM format. PEM allows the option of private keys being encrypted. Encrypted keys will be decrypted with `options.passphrase`. Multiple keys using diff --git a/lib/_tls_common.js b/lib/_tls_common.js index abd877a49b1e83..563dc063bb48d9 100644 --- a/lib/_tls_common.js +++ b/lib/_tls_common.js @@ -167,12 +167,8 @@ exports.createSecureContext = function createSecureContext(options, context) { if (Array.isArray(options.pfx)) { for (i = 0; i < options.pfx.length; i++) { const pfx = options.pfx[i]; - let buf; - if (ArrayBuffer.isView(pfx.buffer)) { - buf = crypto._toBuf(pfx.buffer); - } else { - buf = crypto._toBuf(pfx); - } + const raw = pfx.buf ? pfx.buf : pfx; + const buf = crypto._toBuf(raw); const passphrase = pfx.passphrase || options.passphrase; if (passphrase) { c.context.loadPKCS12(buf, crypto._toBuf(passphrase)); diff --git a/test/parallel/test-tls-multi-pfx.js b/test/parallel/test-tls-multi-pfx.js index 6a9ebb1554f711..f750aec325c957 100644 --- a/test/parallel/test-tls-multi-pfx.js +++ b/test/parallel/test-tls-multi-pfx.js @@ -10,7 +10,7 @@ const fixtures = require('../common/fixtures'); const options = { pfx: [ { - buffer: fixtures.readKey('agent1-pfx.pem'), + buf: fixtures.readKey('agent1-pfx.pem'), passphrase: 'sample' }, fixtures.readKey('ec-pfx.pem')