Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Singleline private keys #423

Merged
merged 3 commits into from
Feb 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 38 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,47 @@ The `generateServiceProviderMetadata` method is also available on the `MultiSaml

Passport-SAML uses the HTTP Redirect Binding for its `AuthnRequest`s (unless overridden with the `authnRequestBinding` parameter), and expects to receive the messages back via the HTTP POST binding.

Authentication requests sent by Passport-SAML can be signed using RSA-SHA1. To sign them you need to provide a private key in the PEM format via the `privateCert` configuration key. The certificate
should start with `-----BEGIN PRIVATE KEY-----` on its own line and end with `-----END PRIVATE KEY-----` on its own line.
Authentication requests sent by Passport-SAML can be signed using RSA signature with SHA1, SHA256 or SHA512 hashing algorithms.

For example:
To select hashing algorithm, use:

```js
...
signatureAlgorithm: 'sha1' // (default, but not recommended anymore these days)
signatureAlgorithm: 'sha256', // (preffered - your IDP should support it, otherwise think about upgrading it)
signatureAlgorithm: 'sha512' // (most secure - check if your IDP supports it)
...
```

To sign them you need to provide a private key in the PEM format via the `privateCert` configuration key.

Formats supported for `privateCert` field are,

1. Well formatted PEM:

```
-----BEGIN PRIVATE KEY-----
<private key contents here delimited at 64 characters per row>
-----END PRIVATE KEY-----

```
```
-----BEGIN RSA PRIVATE KEY-----
<private key contents here delimited at 64 characters per row>
-----END RSA PRIVATE KEY-----

```
(both versions work)
See example from tests of the first version of [well formatted private key](test/static/acme_tools_com.key).

2. Alternativelly a single line private key without start/end lines where all rows are joined into single line:

See example from tests of [singleline private key](test/static/singleline_acme_tools_com.key).

Add it to strategy options like this:

```javascript
privateCert: fs.readFileSync('./cert.pem', 'utf-8')
privateCert: fs.readFileSync('./privateCert.pem', 'utf-8')
```


Expand Down
17 changes: 16 additions & 1 deletion lib/passport-saml/saml.js
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ SAML.prototype.signRequest = function (samlMessage) {
samlMessageToSign.SigAlg = samlMessage.SigAlg;
}
signer.update(querystring.stringify(samlMessageToSign));
samlMessage.Signature = signer.sign(this.options.privateCert, 'base64');
samlMessage.Signature = signer.sign(this.keyToPEM(this.options.privateCert), 'base64');
};

SAML.prototype.generateAuthorizeRequest = function (req, isPassive, isHttpPostBinding, callback) {
Expand Down Expand Up @@ -1290,4 +1290,19 @@ SAML.prototype.generateServiceProviderMetadata = function( decryptionCert, signi
return xmlbuilder.create(metadata).end({ pretty: true, indent: ' ', newline: '\n' });
};

SAML.prototype.keyToPEM = function (key) {
if (!key || typeof key !== 'string') return key;

const lines = key.split('\n');
if (lines.length !== 1) return key;

const wrappedKey = [
'-----BEGIN PRIVATE KEY-----',
...key.match(/.{1,64}/g),
'-----END PRIVATE KEY-----',
''
].join('\n');
return wrappedKey;
};

exports.SAML = SAML;
40 changes: 38 additions & 2 deletions test/samlTests.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use strict';
var fs = require('fs');
var url = require('url');
var should = require('should');

var SAML = require('../lib/passport-saml/saml.js').SAML;
var should = require('should');
var url = require('url');

describe('SAML.js', function () {
describe('get Urls', function () {
Expand Down Expand Up @@ -232,5 +233,40 @@ describe('SAML.js', function () {
});
});
});

describe('keyToPEM', function () {
var [regular, singleline] = [
'acme_tools_com.key',
'singleline_acme_tools_com.key'
].map(keyFromFile);

it('formats singleline keys properly', function (done) {
var result = saml.keyToPEM(singleline);
result.should.equal(regular);
done();
});

it('passes all other multiline keys', function (done) {
var result = saml.keyToPEM(regular);
result.should.equal(regular);
done();
});

it('does nothing to falsy', function (done) {
var result = saml.keyToPEM(null);
should.equal(result, null);
done();
});

it('does nothing to non strings', function (done) {
var result = saml.keyToPEM(1);
should.equal(result, 1);
done();
});
});
});
});

function keyFromFile (file) {
return fs.readFileSync(`./test/static/${file}`).toString();
}
1 change: 1 addition & 0 deletions test/static/singleline_acme_tools_com.key
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCXJP47MTKaFpkU1/Hj4cXFlsE/d6C9xSA+8ZrvBYJXu/K4Jq4sGrA7UVCrNpEPGQSKGQeqZTPrcPbm/ZCLEvFfKpoVDxPEMUrBgBO1IvvHkbYbDDnofQvVcaBh01bVqQ/H3Q/5RKZWfVZv//sDBT5NX67iMiApnJNogVZ0WXAGGWng4EQcLJjCeAWAzJWgIGxKbBviJ/syLlPvjon1kxCkL62z6R/OV7Ln03Pnmlrl9EvC7irEVS/ahF1cB/Zku7EHsM9BtEfzb97Blnw3gFQaJDIDA6weCnSxv0kny4Qz+qHJLV1dfGOj6TGwjkdeHGng/lH1UAO46q5osijym0itAgMBAAECggEAOEGbotp/9BxfJAPwU5pwK0gea4oSOTAK/YKd4UNSrWcWQ3aaM25Hk/yDIwbvXQg5yVDhqol6lrJ4I6fTSgBl+L75lMcE22xhX75VF4GK51AT0ONvFlqcsfhV8rNfyazgY8qjLGth4iVKq+tCOXCVLOVlSWnQ0Mwnjak+TZYQfSQb9a6KZWwkTkoMf38q52iSkc3zbqUG0xYoGfpUNhdULrna8C6m6yGh+odG9kennHKBePf38M1sa3cCyriAm0TrKUG+OIAIJPV+PN2q14Vn/o9YW6uxj4GD/abBJZzFgt6aEGhJxSjwogEBT8U9SjhwBGwetL2A7T6NOoQkyWyKHQKBgQDGj4PMYtxUV9Cd50C5ekh2NQBLEsGd6DGrkJe2OrLuqDOmpxOdtGnAI7s0oiBNB2gYuTd8T+zL+FdN9vKJc9OJ9dZ0zgHKVoajff8ZB6QDkvGXGKZNbYjnsbuJYAuMg7fE0HZ7DZnBmArbRO1RtfbRf31Pfj2bpuaEWLPr3BbDewKBgQDC3g9XKKsaS+Eg3phgRYBfLL7j33vM5nGe1GrogVu3gNvirLoQLq60lSCzaWIwq0d9m+7nM9xgY9BeA6rxFWms0wWAxBW5Dj+UrRNjAa5KFav1IPVNBkzkoUfXc+WskgZBneDJt87Xif0NdnwvZPufvTzYLtgp7uf1tACOB0v39wKBgQCkvhglMPULiZyOFnuOKFYMtWbb4uD/ydHEPgCR8lvZRXctUzwkQbbn8v106vsPzhBhSvMtUgbvKMoO5tBOmOovcegQG4hCv82Pwo8vzjYXvQhzS8FXRoUrbzxg+245lGOZiTh0WlFy3VpMmQCqJeZ15Wgr11r4VN63ef01uPKuFwKBgDFTXlS0oaL1ZBYa4j00Ootc2zD5J/A0wLvwjuMto1au0nntOOfRuT1SpkVjvowNPvpnlzCE6xqnCV5S1VlTDz3E6Jawi8Mc/TEYIlkkWsa795wD7LPDjYEt5e5+krt89wJzASxuT3g1oI1g2YxxplPH5ffe2665n5ONLbrF1A//AoGAUKM5N826wiZqmYCa+eC1TVz12iprlIdW0wOoBL5hcDRDd7u0ninTSiv9mAE5RYJVHEF3PchmQFTs9i3OJXGXYDWU73kHhzgYnEC+vDxDq/6Q8BOhicRyBZ7faQ3oNnGTUzIioiBoQtmJ9axJZVNs3tfYUpxvwr43am35OSeC3aI=