Skip to content

Commit

Permalink
Add keyPassphrase option to signer (#115)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashpieboop committed Sep 10, 2020
1 parent 4fda5d0 commit 241725d
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 3 deletions.
3 changes: 2 additions & 1 deletion README.md
Expand Up @@ -36,7 +36,8 @@ var req = https.request(options, function(res) {

httpSignature.sign(req, {
key: key,
keyId: './cert.pem'
keyId: './cert.pem',
keyPassphrase: 'secret' // (optional)
});

req.end();
Expand Down
15 changes: 13 additions & 2 deletions lib/signer.js
Expand Up @@ -104,7 +104,10 @@ function RequestSigner(options) {
} else if (options.key !== undefined) {
var key = options.key;
if (typeof (key) === 'string' || Buffer.isBuffer(key))
key = sshpk.parsePrivateKey(key);
assert.optionalString(options.keyPassphrase, 'options.keyPassphrase');
key = sshpk.parsePrivateKey(key, 'auto', {
passphrase: options.keyPassphrase
});

assert.ok(sshpk.PrivateKey.isPrivateKey(key, [1, 2]),
'options.key must be a sshpk.PrivateKey');
Expand Down Expand Up @@ -266,6 +269,7 @@ module.exports = {
* - {String} keyId
* - {String|Buffer} key
* - {String} algorithm (optional, required for HMAC)
* - {String} keyPassphrase (optional, not for HMAC)
* or:
* - {Func} sign (data, cb)
* @return {RequestSigner}
Expand Down Expand Up @@ -302,6 +306,10 @@ module.exports = {
* - {int} expiresIn optional; defaults to 60. The
* seconds after which the signature should
* expire;
* - {String} keyPassphrase optional; The passphrase to
* pass to sshpk to parse the privateKey.
* This doesn't do anything if algorithm is
* HMAC.
* @return {Boolean} true if Authorization (and optionally Date) were added.
* @throws {TypeError} on bad parameter types (input).
* @throws {InvalidAlgorithmError} if algorithm was bad or incompatible with
Expand All @@ -319,6 +327,7 @@ module.exports = {
assert.optionalArrayOfString(options.headers, 'options.headers');
assert.optionalString(options.httpVersion, 'options.httpVersion');
assert.optionalNumber(options.expiresIn, 'options.expiresIn');
assert.optionalString(options.keyPassphrase, 'options.keyPassphrase');

if (!request.getHeader('Date'))
request.setHeader('Date', jsprim.rfc1123(new Date()));
Expand All @@ -340,7 +349,9 @@ module.exports = {
throw (new TypeError('options.key must be a string or Buffer'));
} else {
if (typeof (key) === 'string' || Buffer.isBuffer(key))
key = sshpk.parsePrivateKey(options.key);
key = sshpk.parsePrivateKey(options.key, 'auto', {
passphrase: options.keyPassphrase
});

assert.ok(sshpk.PrivateKey.isPrivateKey(key, [1, 2]),
'options.key must be a sshpk.PrivateKey');
Expand Down
18 changes: 18 additions & 0 deletions test/rsa_private_encrypted.pem
@@ -0,0 +1,18 @@
-----BEGIN ENCRYPTED PRIVATE KEY-----
MIIC3TBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQISkT2UHxhpiUCAggA
MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBBjoylMNYUwmXeqkoJYzJdOBIIC
gMi4cPwI9yUYb1MTSC067Qp8dlamntAD+T4Ol3rfx8qEU0hDawWkpmroZGzh8USb
uHpDTLx0yPlxEw262Ismx1xsrRH1nhu3PvDi0KC50Qgn3+W3tqYjUmNCt4dFSk9l
i7MgpvPHzVaUWryE5TK2iWKW5/QWfJ0FMXS+vzDOdmALaYhiQ2qAwZzyB4DLUFQE
zAy2k1EttkJjkXG/MHkGMJ0uJWD+SCfdi9PykFswNk2Ew4EvGVEKL8/1RTaLFezQ
3llS4o1TaLPsPawiAZcLNAeS1dvOpikBiDSidMb3dXatjBV19uYYXd5KzJQ1jlq6
hD6K6dStqekk+TGamsQr2XBf9nsn/n60c05IVogDBDhewaLpjh+tSew0iBHrG6z0
z7//kyjx5v8HzqTZ/7VErTkef4scnnWLQ+/uzgK2RgBIakgxWZz2/0zN/8MhoM/B
7Es8rkB1yjDFUwaNFcjaaTMkb2++CA8PuUxKNXiGEmQ3nylT3WL9kO+fllyNG4M+
WwaPDMQAsW02f+I51Jm3LB8QycQKsyHL6Ran35aAFrF3ANsf6dzp3oIcQpdt1j1y
pTWQwQ+8GlP5lTtQMdzyH+3HE/HpgkgaAMuYFfK6afByE9X8Br11S7pmZ60v0kzK
75RDwYo2m8zBLF3hQslSvNHJIDQ9u3KgdOz7xeqoMaKLSUF0JfoGNbT8qq09zE2A
2dtrwsNxA0O7jHURgvhl/SjlW0bg6fRqfKD6d8oDY085mq5N02wx1Rx9b3MLi6ok
wW6cELTZy3C+NdMgA6EqYAvn4iH+tXoLKKdAMlAPu5ari6wXF9TjctjYE6NNzGcp
B9AZhX+7Y9hSe5CFSIF6DE4=
-----END ENCRYPTED PRIVATE KEY-----
6 changes: 6 additions & 0 deletions test/rsa_public_encrypted.pem
@@ -0,0 +1,6 @@
-----BEGIN PUBLIC KEY-----
MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCoX3N2q0OzQjA5tu2LKKtBXbZZ
DJjqQzXnFUKOWJc5xo7D8xDWzETOirJLoR6qGf+1rYU+jAyR0YtW9km1wyluzeuM
TerUUioeaDGCo1Sq+euSqb6BQH6xdLFaTHlynuSafgjiEsKp0QNcFWyWFrCH+mJF
v0YyNiv5QX78asG8VwIDAQAB
-----END PUBLIC KEY-----
43 changes: 43 additions & 0 deletions test/signer.test.js
Expand Up @@ -17,6 +17,7 @@ var httpSignature = require('../lib/index');
var hmacKey = null;
var httpOptions = null;
var rsaPrivate = null;
var rsaPrivateEncrypted = null;
var dsaPrivate = null;
var ecdsaPrivate = null;
var signOptions = null;
Expand All @@ -30,9 +31,11 @@ var socket = null;

test('setup', function(t) {
rsaPrivate = fs.readFileSync(__dirname + '/rsa_private.pem', 'ascii');
rsaPrivateEncrypted = fs.readFileSync(__dirname + '/rsa_private_encrypted.pem', 'ascii');
dsaPrivate = fs.readFileSync(__dirname + '/dsa_private.pem', 'ascii');
ecdsaPrivate = fs.readFileSync(__dirname + '/ecdsa_private.pem', 'ascii');
t.ok(rsaPrivate);
t.ok(rsaPrivateEncrypted);
t.ok(dsaPrivate);
t.ok(ecdsaPrivate);

Expand Down Expand Up @@ -262,6 +265,27 @@ test('signing opaque param', function(t) {
req.end();
});

test('signing with key protected with passphrase', function(t) {
var req = http.request(httpOptions, function(res) {
t.end();
});
var opts = {
keyId: 'unit',
key: rsaPrivateEncrypted,
keyPassphrase: '123',
headers: ['date', '(algorithm)']
};

req._stringToSign = null;
t.ok(httpSignature.sign(req, opts));
t.ok(req.getHeader('Authorization'));
t.strictEqual(typeof (opts.algorithm), 'string');
t.strictEqual(typeof (req._stringToSign), 'string');
t.ok(req._stringToSign.match(/^date: [^\n]*\n\(algorithm\): [^\n]*$/));
console.log('> ' + req.getHeader('Authorization'));
req.end();
});

test('request-target with dsa key', function(t) {
var req = http.request(httpOptions, function(res) {
t.end();
Expand Down Expand Up @@ -348,6 +372,25 @@ test('createSigner with RSA key, auto algo', function(t) {
});
});

test('createSigner with RSA key, auto algo, passphrase', function(t) {
var s = httpSignature.createSigner({
keyId: 'foo',
key: rsaPrivateEncrypted,
keyPassphrase: '123'
});
s.writeTarget('get', '/');
var date = s.writeDateHeader();
s.sign(function (err, authz) {
t.error(err);
var req = http.request(httpOptions, function(res) {
t.end();
});
req.setHeader('date', date);
req.setHeader('authorization', authz);
req.end();
});
});

test('createSigner with HMAC key', function(t) {
var s = httpSignature.createSigner({
keyId: 'foo',
Expand Down

0 comments on commit 241725d

Please sign in to comment.