From c996753c804472940d8347616f4894dc4b7e4072 Mon Sep 17 00:00:00 2001 From: Georg Bakken Idland Date: Tue, 3 Jan 2017 14:15:29 +0100 Subject: [PATCH] added support for 'sender-vouches' in subject confirmation method added test-case --- lib/saml11.js | 37 ++++++++++++++++++--------------- test/saml11.tests.js | 49 ++++++++++++++++++++++++++++++++++++-------- 2 files changed, 61 insertions(+), 25 deletions(-) diff --git a/lib/saml11.js b/lib/saml11.js index f359aa54..fa60a6aa 100644 --- a/lib/saml11.js +++ b/lib/saml11.js @@ -42,7 +42,7 @@ exports.create = function(options, callback) { algorithms.digest[options.digestAlgorithm]); sig.signingKey = options.key; - + sig.keyInfoProvider = { getKeyInfo: function () { return "" + cert + ""; @@ -68,7 +68,7 @@ exports.create = function(options, callback) { conditions[0].setAttribute('NotBefore', now.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); conditions[0].setAttribute('NotOnOrAfter', now.add(options.lifetimeInSeconds, 'seconds').format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); } - + if (options.audiences) { var audiences = options.audiences instanceof Array ? options.audiences : [options.audiences]; audiences.forEach(function (audience) { @@ -83,7 +83,7 @@ exports.create = function(options, callback) { var statement = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'AttributeStatement')[0]; Object.keys(options.attributes).forEach(function(prop) { if(typeof options.attributes[prop] === 'undefined') return; - + // // Foo Bar // @@ -110,15 +110,15 @@ exports.create = function(options, callback) { .setAttribute('AuthenticationInstant', now.format('YYYY-MM-DDTHH:mm:ss.SSS[Z]')); var nameID = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'NameIdentifier')[0]; - + if (options.nameIdentifier) { nameID.textContent = options.nameIdentifier; - + doc.getElementsByTagName('saml:AuthenticationStatement')[0] .getElementsByTagName('saml:NameIdentifier')[0] .textContent = options.nameIdentifier; } - + if (options.nameIdentifierFormat) { var nameIDs = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'NameIdentifier'); nameIDs[0].setAttribute('Format', options.nameIdentifierFormat); @@ -127,18 +127,18 @@ exports.create = function(options, callback) { if (!options.encryptionCert) return sign(options, sig, doc, callback); - // encryption is turned on, + // encryption is turned on, var proofSecret; async.waterfall([ function(cb) { - if (!options.subjectConfirmationMethod && options.subjectConfirmationMethod !== 'holder-of-key') + if (!options.subjectConfirmationMethod && (options.subjectConfirmationMethod !== 'holder-of-key' || options.subjectConfirmationMethod !== 'sender-vouches')) return cb(); - + crypto.randomBytes(32, function(err, randomBytes) { proofSecret = randomBytes; addSubjectConfirmation(options, doc, options.holderOfKeyProofSecret || randomBytes, cb); }); - + }, function(cb) { sign(options, sig, doc, function(err, signed) { @@ -150,7 +150,7 @@ exports.create = function(options, callback) { if (err) return callback(err); callback(null, result, proofSecret); }); -}; +}; function addSubjectConfirmation(options, doc, randomBytes, callback) { var encryptOptions = { @@ -159,7 +159,7 @@ function addSubjectConfirmation(options, doc, randomBytes, callback) { keyEncryptionAlgorighm: options.keyEncryptionAlgorighm || 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p' }; - xmlenc.encryptKeyInfo(randomBytes, encryptOptions, function(err, keyinfo) { + xmlenc.encryptKeyInfo(randomBytes, encryptOptions, function(err, keyinfo) { if (err) return cb(err); var subjectConfirmationNodes = doc.documentElement.getElementsByTagNameNS(NAMESPACE, 'SubjectConfirmation'); @@ -172,7 +172,12 @@ function addSubjectConfirmation(options, doc, randomBytes, callback) { } var method = subjectConfirmationNodes[i].getElementsByTagNameNS(NAMESPACE, 'ConfirmationMethod')[0]; - method.textContent = 'urn:oasis:names:tc:SAML:1.0:cm:holder-of-key'; + if (options.subjectConfirmationMethod == 'holder-of-key') { + method.textContent = 'urn:oasis:names:tc:SAML:1.0:cm:holder-of-key'; + + } else if (options.subjectConfirmationMethod == 'sender-vouches') { + method.textContent = 'urn:oasis:names:tc:SAML:1.0:cm:sender-vouches'; + } subjectConfirmationNodes[i].appendChild(keyinfoDom.documentElement); } @@ -185,9 +190,9 @@ function sign(options, sig, doc, callback) { var signed; try { - var opts = options.xpathToNodeBeforeSignature ? { - location: { - reference: options.xpathToNodeBeforeSignature, + var opts = options.xpathToNodeBeforeSignature ? { + location: { + reference: options.xpathToNodeBeforeSignature, action: 'after' } } : {}; diff --git a/test/saml11.tests.js b/test/saml11.tests.js index d924c278..fd197167 100644 --- a/test/saml11.tests.js +++ b/test/saml11.tests.js @@ -125,7 +125,7 @@ describe('saml 1.1', function () { var isValid = utils.isValidSignature(signedAssertion, options.cert); assert.equal(true, isValid); - + var attributes = utils.getAttributes(signedAssertion); assert.equal(3, attributes.length); assert.equal('emailaddress', attributes[0].getAttribute('AttributeName')); @@ -253,7 +253,7 @@ describe('saml 1.1', function () { }; var signedAssertion = saml11.create(options); var doc = new xmldom.DOMParser().parseFromString(signedAssertion); - + var signature = doc.documentElement.getElementsByTagName('Signature'); assert.equal('saml:Conditions', signature[0].previousSibling.nodeName); @@ -317,7 +317,7 @@ describe('saml 1.1', function () { saml11.create(options, function(err, encrypted) { if (err) return done(err); - + xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) { if (err) return done(err); var isValid = utils.isValidSignature(decrypted, options.cert); @@ -327,7 +327,7 @@ describe('saml 1.1', function () { }); }); - it('should support holder-of-key suject confirmationmethod', function (done) { + it('should support holder-of-key subject confirmationmethod', function (done) { var options = { cert: fs.readFileSync(__dirname + '/test-auth0.pem'), key: fs.readFileSync(__dirname + '/test-auth0.key'), @@ -338,10 +338,10 @@ describe('saml 1.1', function () { saml11.create(options, function(err, encrypted, proofSecret) { if (err) return done(err); - + xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) { if (err) return done(err); - + var doc = new xmldom.DOMParser().parseFromString(decrypted); var subjectConfirmationNodes = doc.documentElement.getElementsByTagName('saml:SubjectConfirmation'); assert.equal(2, subjectConfirmationNodes.length); @@ -358,6 +358,37 @@ describe('saml 1.1', function () { }); }); + it('should support sender-vouches subject confirmationmethod', function (done) { + var options = { + cert: fs.readFileSync(__dirname + '/test-auth0.pem'), + key: fs.readFileSync(__dirname + '/test-auth0.key'), + encryptionPublicKey: fs.readFileSync(__dirname + '/test-auth0_rsa.pub'), + encryptionCert: fs.readFileSync(__dirname + '/test-auth0.pem'), + subjectConfirmationMethod: 'sender-vouches' + }; + + saml11.create(options, function(err, encrypted, proofSecret) { + if (err) return done(err); + + xmlenc.decrypt(encrypted, { key: fs.readFileSync(__dirname + '/test-auth0.key')}, function(err, decrypted) { + if (err) return done(err); + + var doc = new xmldom.DOMParser().parseFromString(decrypted); + var subjectConfirmationNodes = doc.documentElement.getElementsByTagName('saml:SubjectConfirmation'); + assert.equal(2, subjectConfirmationNodes.length); + for (var i=0;i