From 280075beff08263a10cd5e42eb7354f2dcc175ef Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Wed, 14 Jun 2023 14:32:02 -0400 Subject: [PATCH 01/21] Replace `KeyInfoProvider` with plugable methods --- README.md | 19 +-- example/example.js | 3 +- index.d.ts | 140 +++++++----------- lib/file-key-info.js | 17 --- lib/signed-xml.js | 107 +++++++++---- lib/string-key-info.js | 40 ----- lib/utils.js | 44 ++++++ test/document-test.js | 4 +- test/hmac-tests.js | 6 +- test/key-info-tests.js | 19 +++ test/saml-response-test.js | 10 +- test/signature-integration-tests.js | 16 +- test/signature-unit-tests.js | 81 +++++----- test/static/client_public.pem | 24 +-- test/utils-tests.js | 29 ++++ .../XmlCryptoUtilities/Program.cs | 2 +- .../XmlCryptoUtilities/utilities.cs | 2 +- test/wsfed-metadata-test.js | 2 +- 18 files changed, 297 insertions(+), 268 deletions(-) delete mode 100644 lib/file-key-info.js delete mode 100644 lib/string-key-info.js create mode 100644 test/key-info-tests.js create mode 100644 test/utils-tests.js diff --git a/README.md b/README.md index 8f743d00..d6f6417b 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,6 @@ Example: var select = require("xml-crypto").xpath, dom = require("@xmldom/xmldom").DOMParser, SignedXml = require("xml-crypto").SignedXml, - FileKeyInfo = require("xml-crypto").FileKeyInfo, fs = require("fs"); var xml = fs.readFileSync("signed.xml").toString(); @@ -254,23 +253,9 @@ var SignedXml = require("xml-crypto").SignedXml, Now define the extension point you want to implement. You can choose one or more. A key info provider is used to extract and construct the key and the KeyInfo xml section. -Implement it if you want to create a signature with a KeyInfo section, or you want to read your key in a different way then the default file read option. +Implement it if you want to create a signature with a KeyInfo section, or you want to read your key in a different way then the built-in file-read or string methods. See the implementation in `string-key-info.js` for more information. -```javascript -function MyKeyInfo() { - this.getKeyInfo = function (key, prefix) { - prefix = prefix || ""; - prefix = prefix ? prefix + ":" : prefix; - return "<" + prefix + "X509Data>"; - }; - this.getKey = function (keyInfo) { - //you can use the keyInfo parameter to extract the key in any way you want - return fs.readFileSync("key.pem"); - }; -} -``` - -A custom hash algorithm is used to calculate digests. Implement it if you want a hash other than the default SHA1. +A custom hash algorithm is used to calculate digests. Implement it if you want a hash other than the built-in methods. ```javascript function MyDigest() { diff --git a/example/example.js b/example/example.js index cc2428c9..e693894b 100644 --- a/example/example.js +++ b/example/example.js @@ -3,7 +3,6 @@ var select = require("xml-crypto").xpath, dom = require("@xmldom/xmldom").DOMParser, SignedXml = require("xml-crypto").SignedXml, - FileKeyInfo = require("xml-crypto").FileKeyInfo, fs = require("fs"); function signXml(xml, xpath, key, dest) { @@ -21,7 +20,7 @@ function validateXml(xml, key) { doc )[0]; var sig = new SignedXml(); - sig.keyInfoProvider = new FileKeyInfo(key); + sig.signingCert = key; sig.loadSignature(signature.toString()); var res = sig.checkSignature(xml); if (!res) console.log(sig.validationErrors); diff --git a/index.d.ts b/index.d.ts index 736977d1..d9b95ff5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -129,6 +129,16 @@ export interface TransformAlgorithm { * - {@link SignedXml#checkSignature} * - {@link SignedXml#validationErrors} */ + +/** + * @param cert the certificate as a string or array of strings (see https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/#sec-X509Data) + * @param prefix an optional namespace alias to be used for the generated XML + */ +export interface ComposeKeyInfoContentArgs { + cert: string; + prefix: string; +} + export class SignedXml { // To add a new transformation algorithm create a new class that implements the {@link TransformationAlgorithm} interface, and register it here. More info: {@link https://github.com/node-saml/xml-crypto#customizing-algorithms|Customizing Algorithms} static CanonicalizationAlgorithms: { @@ -149,7 +159,7 @@ export class SignedXml { // One of the supported signature algorithms. See {@link SignatureAlgorithmType} signatureAlgorithm: SignatureAlgorithmType; // A {@link Buffer} or pem encoded {@link String} containing your private key - signingKey: Buffer | string; + privateKey: Buffer | string; // Contains validation errors (if any) after {@link checkSignature} method is called validationErrors: string[]; @@ -278,115 +288,79 @@ export class SignedXml { * @returns The signed XML. */ getSignedXml(): string; -} -/** - * KeyInfoProvider interface represents the structure for managing keys - * and KeyInfo section in XML data when dealing with XML digital signatures. - */ -export interface KeyInfoProvider { /** - * Method to return the key based on the contents of the specified KeyInfo. + * Builds the contents of a KeyInfo element as an XML string. * - * @param keyInfo - An optional array of XML Nodes. - * @return A string or Buffer representing the key. - */ - getKey(keyInfo?: Node[]): string | Buffer; - - /** - * Method to return an XML string representing the contents of a KeyInfo element. + * For example, if the value of the prefix argument is 'foo', then + * the resultant XML string will be "" * - * @param key - An optional string representing the key. - * @param prefix - An optional string representing the namespace alias. - * @return An XML string representation of the contents of a KeyInfo element. + * @return an XML string representation of the contents of a KeyInfo element, or `null` if no `KeyInfo` element should be included */ - getKeyInfo(key?: string, prefix?: string): string; + composeKeyInfoContent(args: ComposeKeyInfoContentArgs): string | null; /** - * An optional dictionary of attributes which will be added to the KeyInfo element. + * Returns the value of the signing certificate based on the contents of the + * specified KeyInfo. + * + * @param keyInfo an array with exactly one KeyInfo element (see https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/#sec-X509Data) + * @return the signing certificate as a string in PEM format */ - attrs?: { [key: string]: string }; + getCertFromKeyInfo(keyInfo: string): string | null; } -/** - * The FileKeyInfo class loads the certificate from the file provided in the constructor. - */ -export class FileKeyInfo implements KeyInfoProvider { +export interface Utils { /** - * The path to the file from which the certificate is to be read. + * @param pem The PEM-encoded base64 certificate to strip headers from */ - file: string; + static pemToDer(pem: string): string; /** - * Initializes a new instance of the FileKeyInfo class. - * - * @param file - An optional string representing the file path of the certificate. + * @param der The DER-encoded base64 certificate to add PEM headers too + * @param pemLabel The label of the header and footer to add */ - constructor(file?: string); + static derToPem( + der: string, + pemLabel: ["CERTIFICATE" | "PRIVATE KEY" | "RSA PUBLIC KEY"] + ): string; /** - * Return the loaded certificate. The certificate is read from the file specified in the constructor. - * The keyInfo parameter is ignored. (not implemented) + * -----BEGIN [LABEL]----- + * base64([DATA]) + * -----END [LABEL]----- * - * @param keyInfo - (not used) An optional array of XML Elements. - * @return A Buffer representing the certificate. - */ - getKey(keyInfo?: Node[]): Buffer; - - /** - * Builds the contents of a KeyInfo element as an XML string. + * Above is shown what PEM file looks like. As can be seen, base64 data + * can be in single line or multiple lines. * - * Currently, this returns exactly one empty X509Data element - * (e.g. ""). The resultant X509Data element will be - * prefaced with a namespace alias if a value for the prefix argument - * is provided. In example, if the value of the prefix argument is 'foo', then - * the resultant XML string will be "" + * This function normalizes PEM presentation to; + * - contain PEM header and footer as they are given + * - normalize line endings to '\n' + * - normalize line length to maximum of 64 characters + * - ensure that 'preeb' has line ending '\n' * - * @param key (not used) the signing/private key as a string - * @param prefix an optional namespace alias to be used for the generated XML - * @return an XML string representation of the contents of a KeyInfo element - */ - getKeyInfo(key?: string, prefix?: string): string; -} - -/** - * The StringKeyInfo class loads the certificate from the string provided in the constructor. - */ -export class StringKeyInfo implements KeyInfoProvider { - /** - * The certificate in string form. - */ - key: string; - - /** - * Initializes a new instance of the StringKeyInfo class. - * @param key - An optional string representing the certificate. - */ - constructor(key?: string); - - /** - * Returns the certificate loaded in the constructor. - * The keyInfo parameter is ignored. (not implemented) + * With couple of notes: + * - 'eol' is normalized to '\n' * - * @param keyInfo (not used) an array with exactly one KeyInfo element - * @return the signing certificate as a string + * @param pem The PEM string to normalize to RFC7468 'stricttextualmsg' definition */ - getKey(keyInfo?: Node[]): string; + static normalizePem(pem: string): string; /** - * Builds the contents of a KeyInfo element as an XML string. + * PEM format has wide range of usages, but this library + * is enforcing RFC7468 which focuses on PKIX, PKCS and CMS. * - * Currently, this returns exactly one empty X509Data element - * (e.g. ""). The resultant X509Data element will be - * prefaced with a namespace alias if a value for the prefix argument - * is provided. In example, if the value of the prefix argument is 'foo', then - * the resultant XML string will be "" + * https://www.rfc-editor.org/rfc/rfc7468 + * + * PEM_FORMAT_REGEX is validating given PEM file against RFC7468 'stricttextualmsg' definition. * - * @param key (not used) the signing/private key as a string - * @param prefix an optional namespace alias to be used for the generated XML - * @return an XML string representation of the contents of a KeyInfo element + * With few exceptions; + * - 'posteb' MAY have 'eol', but it is not mandatory. + * - 'preeb' and 'posteb' lines are limited to 64 characters, but + * should not cause any issues in context of PKIX, PKCS and CMS. */ - getKeyInfo(key?: string, prefix?: string): string; + PEM_FORMAT_REGEX: RegExp; + MULTI_PEM_SPLIT: RegExp; + BASE64_REGEX: RegExp; } /** diff --git a/lib/file-key-info.js b/lib/file-key-info.js deleted file mode 100644 index 63726233..00000000 --- a/lib/file-key-info.js +++ /dev/null @@ -1,17 +0,0 @@ -var StringKeyInfo = require("./string-key-info"), - fs = require("fs"); - -/** - * A key info provider implementation - * - * @param {string} file path to public certificate - */ -function FileKeyInfo(file) { - var key = fs.readFileSync(file); - StringKeyInfo.apply(this, [key]); -} - -FileKeyInfo.prototype = StringKeyInfo.prototype; -FileKeyInfo.prototype.constructor = FileKeyInfo; - -module.exports = FileKeyInfo; diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 523bfdf9..4d093423 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -4,8 +4,6 @@ var xpath = require("xpath"), c14n = require("./c14n-canonicalization"), execC14n = require("./exclusive-canonicalization"), EnvelopedSignature = require("./enveloped-signature").EnvelopedSignature, - StringKeyInfo = require("./string-key-info"), - FileKeyInfo = require("./file-key-info"), crypto = require("crypto"); /** @@ -302,7 +300,6 @@ function SignedXml(idMode, options) { this.signingCert = null; this.signatureAlgorithm = this.options.signatureAlgorithm || "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; - this.keyInfoProvider = null; this.canonicalizationAlgorithm = this.options.canonicalizationAlgorithm || "http://www.w3.org/2001/10/xml-exc-c14n#"; this.inclusiveNamespacesPrefixList = this.options.inclusiveNamespacesPrefixList || ""; @@ -316,6 +313,9 @@ function SignedXml(idMode, options) { this.idAttributes = ["Id", "ID", "id"]; if (this.options.idAttribute) this.idAttributes.splice(0, 0, this.options.idAttribute); this.implicitTransforms = this.options.implicitTransforms || []; + this.composeKeyInfoContent = SignedXml.composeKeyInfoContent; + this.getCertFromKeyInfo = SignedXml.getCertFromKeyInfo; + this.keyInfoAttributes = {}; } SignedXml.CanonicalizationAlgorithms = { @@ -359,6 +359,57 @@ SignedXml.defaultNsForPrefix = { SignedXml.findAncestorNs = findAncestorNs; +/** + * Builds the contents of a KeyInfo element as an XML string. + * + * For example, if the value of the prefix argument is 'foo', then + * the resultant XML string will be "" + * + * @param cert the certificate as a string or array of strings (see https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/#sec-X509Data) + * @param prefix an optional namespace alias to be used for the generated XML + * @return an XML string representation of the contents of a KeyInfo element + */ +SignedXml.composeKeyInfoContent = function ({ publicCert = null, prefix = null } = {}) { + if (publicCert == null) { + return null; + } + + prefix = prefix ? prefix + ":" : prefix; + + let x509Certs = ""; + if (Buffer.isBuffer(publicCert)) { + publicCert = publicCert.toString("latin1"); + } + + if (typeof publicCert === "string") { + publicCert = publicCert.match(utils.MULTI_PEM_SPLIT); + } + + if (Array.isArray(publicCert)) { + x509Certs = publicCert.map((c) => `${utils.pemToDer(c)}`); + } + + return `<${prefix}X509Data>${x509Certs}`; +}; + +/** + * Returns the value of the signing certificate based on the contents of the + * specified KeyInfo. + * + * @param keyInfo an array with exactly one KeyInfo element (see https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/#sec-X509Data) + * @return the signing certificate as a string in PEM format or `null` if none is found + */ +SignedXml.getCertFromKeyInfo = function (keyInfo) { + if (keyInfo != null && keyInfo.length > 0) { + const certs = xpath.select(".//*[local-name(.)='X509Certificate']", keyInfo[0]); + if (certs.length > 0) { + return utils.derToPem(certs[0].textContent.trim(), "CERTIFICATE"); + } + } + + return null; +}; + SignedXml.prototype.checkSignature = function (xml, callback) { if (callback != null && typeof callback !== "function") { throw new Error("Last parameter must be a callback function"); @@ -367,19 +418,13 @@ SignedXml.prototype.checkSignature = function (xml, callback) { this.validationErrors = []; this.signedXml = xml; - if (!this.keyInfoProvider) { - var err = new Error("cannot validate signature since no key info resolver was provided"); - if (!callback) { - throw err; - } else { - callback(err); - return; - } - } - - this.signingKey = this.keyInfoProvider.getKey(this.keyInfo); - if (!this.signingKey) { - var err2 = new Error("key info provider could not resolve key info " + this.keyInfo); + const signingCert = this.getCertFromKeyInfo(this.keyInfo) || this.signingCert; + if (!signingCert) { + var err2 = new Error( + "key info provider could not resolve key info " + + this.keyInfo + + "\n and no publicCert provided" + ); if (!callback) { throw err2; } else { @@ -467,7 +512,12 @@ SignedXml.prototype.getCanonReferenceXml = function (doc, ref, node) { SignedXml.prototype.validateSignatureValue = function (doc, callback) { var signedInfoCanon = this.getCanonSignedInfoXml(doc); var signer = this.findSignatureAlgorithm(this.signatureAlgorithm); - var res = signer.verifySignature(signedInfoCanon, this.signingKey, this.signatureValue, callback); + var res = signer.verifySignature( + signedInfoCanon, + this.getCertFromKeyInfo(this.keyInfo) || this.signingCert || this.signingKey, + this.signatureValue, + callback + ); if (!res && !callback) this.validationErrors.push( "invalid signature: the signature value " + this.signatureValue + " is incorrect" @@ -886,18 +936,21 @@ SignedXml.prototype.getKeyInfo = function (prefix) { currentPrefix = prefix || ""; currentPrefix = currentPrefix ? currentPrefix + ":" : currentPrefix; - if (this.keyInfoProvider) { - var keyInfoAttrs = ""; - if (this.keyInfoProvider.attrs) { - Object.keys(this.keyInfoProvider.attrs).forEach((name) => { - keyInfoAttrs += " " + name + '="' + this.keyInfoProvider.attrs[name] + '"'; - }); - } + var keyInfoAttrs = ""; + if (this.keyInfoAttributes) { + Object.keys(this.keyInfoAttributes).forEach((name) => { + keyInfoAttrs += " " + name + '="' + this.keyInfoAttributes[name] + '"'; + }); + } + const keyInfoContent = this.composeKeyInfoContent({ publicCert: this.signingCert, prefix }); + if (keyInfoAttrs !== "" || keyInfoContent != null) { res += "<" + currentPrefix + "KeyInfo" + keyInfoAttrs + ">"; - res += this.keyInfoProvider.getKeyInfo(this.signingCert || this.signingKey, prefix); + res += keyInfoContent; res += ""; + return res; + } else { + return ""; } - return res; }; /** @@ -1130,5 +1183,3 @@ SignedXml.prototype.getSignedXml = function () { }; exports.SignedXml = SignedXml; -exports.StringKeyInfo = StringKeyInfo; -exports.FileKeyInfo = FileKeyInfo; diff --git a/lib/string-key-info.js b/lib/string-key-info.js deleted file mode 100644 index 2387f0bc..00000000 --- a/lib/string-key-info.js +++ /dev/null @@ -1,40 +0,0 @@ -/** - * A basic string based implementation of a FileInfoProvider - * - * @param {string} key the string contents of a public certificate - */ -function StringKeyInfo(key) { - this.key = key; -} - -/** - * Builds the contents of a KeyInfo element as an XML string. - * - * Currently, this returns exactly one empty X509Data element - * (e.g. ""). The resultant X509Data element will be - * prefaced with a namespace alias if a value for the prefix argument - * is provided. In example, if the value of the prefix argument is 'foo', then - * the resultant XML string will be "" - * - * @param key (not used) the signing/private key as a string - * @param prefix an optional namespace alias to be used for the generated XML - * @return an XML string representation of the contents of a KeyInfo element - */ -StringKeyInfo.prototype.getKeyInfo = function (key, prefix) { - prefix = prefix || ""; - prefix = prefix ? prefix + ":" : prefix; - return "<" + prefix + "X509Data>"; -}; - -/** - * Returns the value of the signing certificate based on the contents of the - * specified KeyInfo. - * - * @param keyInfo (not used) an array with exactly one KeyInfo element - * @return the signing certificate as a string - */ -StringKeyInfo.prototype.getKey = function (keyInfo) { - return this.key; -}; - -module.exports = StringKeyInfo; diff --git a/lib/utils.js b/lib/utils.js index 1925e9b5..e9d6e324 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -77,8 +77,52 @@ function encodeSpecialCharactersInText(text) { }); } +const MULTI_PEM_SPLIT = /-----BEGIN [A-Z\x20]{1,48}-----[^-]*-----END [A-Z\x20]{1,48}-----/g; +const PEM_FORMAT_REGEX = + /^(-----BEGIN [A-Z\x20]{1,48}-----(\r\n|\r|\n){1}.*(\r\n|\r|\n){1}-----END [A-Z\x20]{1,48}-----(\r\n|\r|\n){0,1})$/s; +const BASE64_REGEX = + /^(?:[A-Za-z0-9\+\/]{4}\n{0,1})*(?:[A-Za-z0-9\+\/]{2}==|[A-Za-z0-9\+\/]{3}=)?$/s; // eslint-disable-line no-useless-escape + +function normalizePem(pem) { + return `${( + pem + .trim() + .replace(/(\r\n|\r)/g, "\n") + .match(/.{1,64}/g) ?? [] + ).join("\n")}\n`; +} + +function pemToDer(pem) { + return pem + .replace(/(\r\n|\r)/g, "\n") + .replace(/-----BEGIN [A-Z\x20]{1,48}-----\n?/, "") + .replace(/-----END [A-Z\x20]{1,48}-----\n?/, ""); +} + +function derToPem(der, pemLabel) { + const base64Der = Buffer.isBuffer(der) ? der.toString("latin1").trim() : der.trim(); + + if (PEM_FORMAT_REGEX.test(base64Der)) { + return normalizePem(base64Der); + } + + if (BASE64_REGEX.test(base64Der)) { + const pem = `-----BEGIN ${pemLabel}-----\n${base64Der}\n-----END ${pemLabel}-----`; + + return normalizePem(pem); + } + + throw new Error("Unknown DER format."); +} + exports.findAttr = findAttr; exports.findChilds = findChilds; exports.encodeSpecialCharactersInAttribute = encodeSpecialCharactersInAttribute; exports.encodeSpecialCharactersInText = encodeSpecialCharactersInText; exports.findFirst = findFirst; +exports.MULTI_PEM_SPLIT = MULTI_PEM_SPLIT; +exports.PEM_FORMAT_REGEX = PEM_FORMAT_REGEX; +exports.BASE64_REGEX = BASE64_REGEX; +exports.pemToDer = pemToDer; +exports.derToPem = derToPem; +exports.normalizePem = normalizePem; diff --git a/test/document-test.js b/test/document-test.js index 30faf049..f36bfaaa 100644 --- a/test/document-test.js +++ b/test/document-test.js @@ -17,7 +17,7 @@ describe("Document tests", function () { .toString() ); var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); + sig.signingCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -37,7 +37,7 @@ describe("Document tests", function () { ); var sig = new crypto.SignedXml(); var feidePublicCert = fs.readFileSync("./test/static/feide_public.pem"); - sig.keyInfoProvider = new crypto.StringKeyInfo(feidePublicCert); + sig.signingCert = feidePublicCert; sig.loadSignature(signature); var result = sig.checkSignature(xml); diff --git a/test/hmac-tests.js b/test/hmac-tests.js index c0c70055..d3f0c967 100644 --- a/test/hmac-tests.js +++ b/test/hmac-tests.js @@ -24,7 +24,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key"); + sig.signingCert = fs.readFileSync("./test/static/hmac.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -39,7 +39,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac-foobar.key"); + sig.signingCert = fs.readFileSync("./test/static/hmac-foobar.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -60,7 +60,7 @@ describe("HMAC tests", function () { doc )[0]; var verify = new crypto.SignedXml(); - verify.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key"); + verify.signingCert = fs.readFileSync("./test/static/hmac.key"); verify.loadSignature(signature); var result = verify.checkSignature(sig.getSignedXml()); diff --git a/test/key-info-tests.js b/test/key-info-tests.js new file mode 100644 index 00000000..f78d58da --- /dev/null +++ b/test/key-info-tests.js @@ -0,0 +1,19 @@ +const select = require("xpath").select; +const dom = require("@xmldom/xmldom").DOMParser; +const SignedXml = require("../lib/signed-xml.js").SignedXml; +const fs = require("fs"); +const expect = require("chai").expect; + +describe("KeyInfo tests", function () { + it("adds X509Certificate element during signature", function () { + const xml = ""; + const sig = new SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/client.pem"); + sig.signingCert = fs.readFileSync("./test/static/client_public.pem"); + sig.computeSignature(xml); + const signedXml = sig.getSignedXml(); + const doc = new dom().parseFromString(signedXml); + const x509 = select("//*[local-name(.)='X509Certificate']", doc.documentElement); + expect(x509.length, "X509Certificate element should exist").to.equal(1); + }); +}); diff --git a/test/saml-response-test.js b/test/saml-response-test.js index f7b2c98a..f63556c4 100644 --- a/test/saml-response-test.js +++ b/test/saml-response-test.js @@ -13,7 +13,7 @@ describe("SAML response tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); + sig.signingCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -29,7 +29,7 @@ describe("SAML response tests", function () { assertion )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); + sig.signingCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); expect(function () { sig.checkSignature(xml); @@ -46,7 +46,7 @@ describe("SAML response tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/saml_external_ns.pem"); + sig.signingCert = fs.readFileSync("./test/static/saml_external_ns.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); expect(result).to.be.true; @@ -61,7 +61,7 @@ describe("SAML response tests", function () { assertion )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); + sig.signingCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); expect(function () { sig.checkSignature(xml); @@ -76,7 +76,7 @@ describe("SAML response tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/feide_public.pem"); + sig.signingCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); // This doesn't matter, just want to make sure that we don't fail due to unknown algorithm diff --git a/test/signature-integration-tests.js b/test/signature-integration-tests.js index af493a70..5952d3ce 100644 --- a/test/signature-integration-tests.js +++ b/test/signature-integration-tests.js @@ -87,7 +87,7 @@ describe("Signature integration tests", function () { ""; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/client_public.pem"); + sig.signingCert = fs.readFileSync("./test/static/client_public.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -111,7 +111,7 @@ describe("Signature integration tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/windows_store_certificate.pem"); + sig.signingCert = fs.readFileSync("./test/static/windows_store_certificate.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -128,9 +128,7 @@ describe("Signature integration tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo( - "./test/static/signature_with_inclusivenamespaces.pem" - ); + sig.signingCert = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -150,9 +148,7 @@ describe("Signature integration tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo( - "./test/static/signature_with_inclusivenamespaces.pem" - ); + sig.signingCert = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -172,9 +168,7 @@ describe("Signature integration tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo( - "./test/static/signature_with_inclusivenamespaces.pem" - ); + sig.signingCert = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index 043993fb..16cdefbf 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1,7 +1,6 @@ var select = require("xpath").select, dom = require("@xmldom/xmldom").DOMParser, SignedXml = require("../lib/signed-xml.js").SignedXml, - FileKeyInfo = require("../lib/signed-xml.js").FileKeyInfo, fs = require("fs"), crypto = require("crypto"); var expect = require("chai").expect; @@ -15,11 +14,15 @@ describe("Signature unit tests", function () { )[0]; var sig = new SignedXml(mode); - sig.keyInfoProvider = new FileKeyInfo("./test/static/client_public.pem"); + sig.signingCert = fs.readFileSync("./test/static/client_public.pem"); sig.loadSignature(node); - var res = sig.checkSignature(xml); + try { + var res = sig.checkSignature(xml); - return res; + return res; + } catch (e) { + return false; + } } function passValidSignature(file, mode) { @@ -317,12 +320,6 @@ describe("Signature unit tests", function () { }); it("signer creates signature with correct structure", function () { - function DummyKeyInfo() { - this.getKeyInfo = function () { - return "dummy key info"; - }; - } - function DummyDigest() { this.getHash = function () { return "dummy digest"; @@ -372,7 +369,9 @@ describe("Signature unit tests", function () { SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; - sig.keyInfoProvider = new DummyKeyInfo(); + sig.composeKeyInfoContent = function () { + return "dummy key info"; + }; sig.canonicalizationAlgorithm = "http://DummyCanonicalization"; sig.addReference( @@ -475,12 +474,6 @@ describe("Signature unit tests", function () { it("signer creates signature with correct structure (with prefix)", function () { var prefix = "ds"; - function DummyKeyInfo() { - this.getKeyInfo = function () { - return "dummy key info"; - }; - } - function DummyDigest() { this.getHash = function () { return "dummy digest"; @@ -530,7 +523,9 @@ describe("Signature unit tests", function () { SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; - sig.keyInfoProvider = new DummyKeyInfo(); + sig.composeKeyInfoContent = function () { + return "dummy key info"; + }; sig.canonicalizationAlgorithm = "http://DummyCanonicalization"; sig.addReference( @@ -636,7 +631,7 @@ describe("Signature unit tests", function () { ''; var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; + sig.signingCert = null; sig.addReference("//*[local-name(.)='x']"); sig.addReference("//*[local-name(.)='y']"); @@ -698,7 +693,7 @@ describe("Signature unit tests", function () { var sig = new SignedXml(); sig.signatureAlgorithm = "http://dummySignatureAlgorithmAsync"; sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; + sig.signingCert = null; sig.addReference("//*[local-name(.)='x']"); sig.addReference("//*[local-name(.)='y']"); @@ -781,7 +776,7 @@ describe("Signature unit tests", function () { var xml = ""; var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; + sig.signingCert = null; sig.addReference( "//*[local-name(.)='root']", @@ -821,17 +816,15 @@ describe("Signature unit tests", function () { }); it("signer adds existing prefixes", function () { - function AssertionKeyInfo(assertionId) { - this.getKeyInfo = function () { - return ( - ' ' + - '' + - assertionId + - "" + - "" - ); - }; + function composeKeyInfoContentWithAssertionId({ assertionId }) { + return ( + ' ' + + '' + + assertionId + + "" + + "" + ); } var xml = @@ -846,7 +839,8 @@ describe("Signature unit tests", function () { ""; var sig = new SignedXml(); - sig.keyInfoProvider = new AssertionKeyInfo("_81d5fba5c807be9e9cf60c58566349b1"); + const assertionId = "_81d5fba5c807be9e9cf60c58566349b1"; + sig.composeKeyInfoContent = composeKeyInfoContentWithAssertionId.bind(this, { assertionId }); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.computeSignature(xml, { prefix: "ds", @@ -862,13 +856,14 @@ describe("Signature unit tests", function () { var result = sig.getSignedXml(); expect((result.match(/xmlns:wsu=/g) || []).length).to.equal(1); expect((result.match(/xmlns:wsse=/g) || []).length).to.equal(1); + expect(result.includes(assertionId)).to.be.true; }); it("creates InclusiveNamespaces element when inclusiveNamespacesPrefixList is set on Reference", function () { var xml = ""; var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; + sig.signingCert = null; sig.addReference( "//*[local-name(.)='root']", @@ -900,7 +895,7 @@ describe("Signature unit tests", function () { var xml = ""; var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; + sig.signingCert = null; sig.addReference( "//*[local-name(.)='root']", @@ -927,7 +922,7 @@ describe("Signature unit tests", function () { var xml = ""; var sig = new SignedXml(null, { inclusiveNamespacesPrefixList: "prefix1 prefix2" }); sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; + sig.signingCert = null; sig.addReference( "//*[local-name(.)='root']", @@ -960,7 +955,7 @@ describe("Signature unit tests", function () { var xml = ""; var sig = new SignedXml(null); // Omit inclusiveNamespacesPrefixList property sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = null; + sig.signingCert = null; sig.addReference( "//*[local-name(.)='root']", @@ -987,15 +982,11 @@ describe("Signature unit tests", function () { var xml = ""; var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); - sig.keyInfoProvider = { - attrs: { - CustomUri: "http://www.example.com/keyinfo", - CustomAttribute: "custom-value", - }, - getKeyInfo: function () { - return ""; - }, + sig.keyInfoAttributes = { + CustomUri: "http://www.example.com/keyinfo", + CustomAttribute: "custom-value", }; + sig.composeKeyInfoContent = () => ""; sig.computeSignature(xml); var signedXml = sig.getSignedXml(); diff --git a/test/static/client_public.pem b/test/static/client_public.pem index 430f46fa..d61698bd 100644 --- a/test/static/client_public.pem +++ b/test/static/client_public.pem @@ -1,12 +1,12 @@ ------BEGIN CERTIFICATE----- -MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW -MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy -MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG -SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd -Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x -O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf -z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU -MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN -AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5 -sT/txBnVJGziyO8DPYdu2fPMER8ajJfl ------END CERTIFICATE----- \ No newline at end of file +-----BEGIN CERTIFICATE----- +MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW +MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy +MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd +Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x +O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf +z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU +MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN +AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5 +sT/txBnVJGziyO8DPYdu2fPMER8ajJfl +-----END CERTIFICATE----- diff --git a/test/utils-tests.js b/test/utils-tests.js new file mode 100644 index 00000000..b28ca25f --- /dev/null +++ b/test/utils-tests.js @@ -0,0 +1,29 @@ +const fs = require("fs"); +const utils = require("../lib/utils"); +const expect = require("chai").expect; + +describe("Utils tests", function () { + describe("derToPem", function () { + it("will return a normalized PEM format when given an non-normalized PEM format", function () { + const normalizedPem = fs.readFileSync("./test/static/client_public.pem", "latin1"); + const pemAsArray = normalizedPem.trim().split("\n"); + const base64String = pemAsArray.slice(1, -1).join(""); + const nonNormalizedPem = + pemAsArray[0] + "\n" + base64String + "\n" + pemAsArray[pemAsArray.length - 1]; + + expect(utils.derToPem(nonNormalizedPem)).to.equal(normalizedPem); + }); + + it("will return a normalized PEM format when given a base64 string", function () { + const normalizedPem = fs.readFileSync("./test/static/client_public.pem", "latin1"); + const pemAsArray = normalizedPem.trim().split("\n"); + const base64String = pemAsArray.slice(1, -1).join(""); + + expect(utils.derToPem(base64String, "CERTIFICATE")).to.equal(normalizedPem); + }); + + it("will throw if the format is neither PEM nor DER", function () { + expect(() => utils.derToPem("not a pem")).to.throw(); + }); + }); +}); diff --git a/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/Program.cs b/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/Program.cs index 4b226b3d..6c4c48e6 100644 --- a/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/Program.cs +++ b/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/Program.cs @@ -1,4 +1,4 @@ -// +// // This example signs an XML file using an // envelope signature. It then verifies the // signed XML. diff --git a/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/utilities.cs b/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/utilities.cs index 75796a44..5ed16f80 100644 --- a/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/utilities.cs +++ b/test/validators/XmlCryptoUtilities/XmlCryptoUtilities/utilities.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; using System.Text; diff --git a/test/wsfed-metadata-test.js b/test/wsfed-metadata-test.js index e4cb7c6d..11982ba5 100644 --- a/test/wsfed-metadata-test.js +++ b/test/wsfed-metadata-test.js @@ -13,7 +13,7 @@ describe("WS-Fed Metadata tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/wsfederation_metadata.pem"); + sig.signingCert = fs.readFileSync("./test/static/wsfederation_metadata.pem"); sig.loadSignature(signature); var result = sig.checkSignature(xml); From 74f4a23171ca209f8c339a6fe48aca7c98443f23 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Wed, 14 Jun 2023 14:55:02 -0400 Subject: [PATCH 02/21] Address review comments --- .eslintrc.json | 2 +- index.d.ts | 6 +++--- lib/signed-xml.js | 6 +++--- lib/utils.js | 17 ++++++++++++----- test/signature-unit-tests.js | 10 +++++----- 5 files changed, 24 insertions(+), 17 deletions(-) diff --git a/.eslintrc.json b/.eslintrc.json index 0a0a638d..82029c51 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -6,7 +6,7 @@ }, "root": true, "parserOptions": { - "ecmaVersion": 6 + "ecmaVersion": 2020 }, "extends": ["eslint:recommended", "prettier"], "rules": { diff --git a/index.d.ts b/index.d.ts index d9b95ff5..843068b6 100644 --- a/index.d.ts +++ b/index.d.ts @@ -134,8 +134,8 @@ export interface TransformAlgorithm { * @param cert the certificate as a string or array of strings (see https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/#sec-X509Data) * @param prefix an optional namespace alias to be used for the generated XML */ -export interface ComposeKeyInfoContentArgs { - cert: string; +export interface GetKeyInfoContentArgs { + cert: string | string[]; prefix: string; } @@ -297,7 +297,7 @@ export class SignedXml { * * @return an XML string representation of the contents of a KeyInfo element, or `null` if no `KeyInfo` element should be included */ - composeKeyInfoContent(args: ComposeKeyInfoContentArgs): string | null; + getKeyInfoContent(args: GetKeyInfoContentArgs): string | null; /** * Returns the value of the signing certificate based on the contents of the diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 4d093423..6fa512d6 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -313,7 +313,7 @@ function SignedXml(idMode, options) { this.idAttributes = ["Id", "ID", "id"]; if (this.options.idAttribute) this.idAttributes.splice(0, 0, this.options.idAttribute); this.implicitTransforms = this.options.implicitTransforms || []; - this.composeKeyInfoContent = SignedXml.composeKeyInfoContent; + this.getKeyInfoContent = SignedXml.getKeyInfoContent; this.getCertFromKeyInfo = SignedXml.getCertFromKeyInfo; this.keyInfoAttributes = {}; } @@ -369,7 +369,7 @@ SignedXml.findAncestorNs = findAncestorNs; * @param prefix an optional namespace alias to be used for the generated XML * @return an XML string representation of the contents of a KeyInfo element */ -SignedXml.composeKeyInfoContent = function ({ publicCert = null, prefix = null } = {}) { +SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = {}) { if (publicCert == null) { return null; } @@ -942,7 +942,7 @@ SignedXml.prototype.getKeyInfo = function (prefix) { keyInfoAttrs += " " + name + '="' + this.keyInfoAttributes[name] + '"'; }); } - const keyInfoContent = this.composeKeyInfoContent({ publicCert: this.signingCert, prefix }); + const keyInfoContent = this.getKeyInfoContent({ publicCert: this.signingCert, prefix }); if (keyInfoAttrs !== "" || keyInfoContent != null) { res += "<" + currentPrefix + "KeyInfo" + keyInfoAttrs + ">"; res += keyInfoContent; diff --git a/lib/utils.js b/lib/utils.js index e9d6e324..7984f42e 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -77,11 +77,18 @@ function encodeSpecialCharactersInText(text) { }); } -const MULTI_PEM_SPLIT = /-----BEGIN [A-Z\x20]{1,48}-----[^-]*-----END [A-Z\x20]{1,48}-----/g; -const PEM_FORMAT_REGEX = - /^(-----BEGIN [A-Z\x20]{1,48}-----(\r\n|\r|\n){1}.*(\r\n|\r|\n){1}-----END [A-Z\x20]{1,48}-----(\r\n|\r|\n){0,1})$/s; -const BASE64_REGEX = - /^(?:[A-Za-z0-9\+\/]{4}\n{0,1})*(?:[A-Za-z0-9\+\/]{2}==|[A-Za-z0-9\+\/]{3}=)?$/s; // eslint-disable-line no-useless-escape +const MULTI_PEM_SPLIT = new RegExp( + "-----BEGIN [A-Z\x20]{1,48}-----[^-]*-----END [A-Z\x20]{1,48}-----", + "g" +); +const PEM_FORMAT_REGEX = new RegExp( + "^-----BEGIN [A-Z\x20]{1,48}-----([^-]*)-----END [A-Z\x20]{1,48}-----$", + "s" +); +const BASE64_REGEX = new RegExp( + "^(?:[A-Za-z0-9\\+\\/]{4}\\n{0,1})*(?:[A-Za-z0-9\\+\\/]{2}==|[A-Za-z0-9\\+\\/]{3}=)?$", + "s" +); function normalizePem(pem) { return `${( diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index 16cdefbf..604b221a 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -369,7 +369,7 @@ describe("Signature unit tests", function () { SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; - sig.composeKeyInfoContent = function () { + sig.getKeyInfoContent = function () { return "dummy key info"; }; sig.canonicalizationAlgorithm = "http://DummyCanonicalization"; @@ -523,7 +523,7 @@ describe("Signature unit tests", function () { SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; - sig.composeKeyInfoContent = function () { + sig.getKeyInfoContent = function () { return "dummy key info"; }; sig.canonicalizationAlgorithm = "http://DummyCanonicalization"; @@ -816,7 +816,7 @@ describe("Signature unit tests", function () { }); it("signer adds existing prefixes", function () { - function composeKeyInfoContentWithAssertionId({ assertionId }) { + function getKeyInfoContentWithAssertionId({ assertionId }) { return ( ' ' + @@ -840,7 +840,7 @@ describe("Signature unit tests", function () { var sig = new SignedXml(); const assertionId = "_81d5fba5c807be9e9cf60c58566349b1"; - sig.composeKeyInfoContent = composeKeyInfoContentWithAssertionId.bind(this, { assertionId }); + sig.getKeyInfoContent = getKeyInfoContentWithAssertionId.bind(this, { assertionId }); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.computeSignature(xml, { prefix: "ds", @@ -986,7 +986,7 @@ describe("Signature unit tests", function () { CustomUri: "http://www.example.com/keyinfo", CustomAttribute: "custom-value", }; - sig.composeKeyInfoContent = () => ""; + sig.getKeyInfoContent = () => ""; sig.computeSignature(xml); var signedXml = sig.getSignedXml(); From 8bd88c00672a701ffa1130fd7b750b5446b07a07 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Wed, 14 Jun 2023 15:25:20 -0400 Subject: [PATCH 03/21] Update readme and apply PR suggestions --- README.md | 32 +++++++++++++++----------------- lib/signed-xml.js | 15 --------------- 2 files changed, 15 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index d6f6417b..a202a10e 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,6 @@ _Signature Algorithm:_ RSA-SHA1 http://www.w3.org/2000/09/xmldsig#rsa-sha1 When signing a xml document you can specify the following properties on a `SignedXml` instance to customize the signature process: - `sign.signingKey` - **[required]** a `Buffer` or pem encoded `String` containing your private key -- `sign.keyInfoProvider` - **[optional]** a key info provider instance, see [customizing algorithms](#customizing-algorithms) for an implementation example - `sign.signatureAlgorithm` - **[optional]** one of the supported [signature algorithms](#signature-algorithms). Ex: `sign.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"` - `sign.canonicalizationAlgorithm` - **[optional]** one of the supported [canonicalization algorithms](#canonicalization-and-transformation-algorithms). Ex: `sign.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"` @@ -119,7 +118,9 @@ To generate a `` element in the signature you must provide When verifying a xml document you must specify the following properties on a ``SignedXml` instance: -- `sign.keyInfoProvider` - **[required]** a key info provider instance containing your certificate, see [customizing algorithms](#customizing-algorithms) for an implementation example +- `sign.signingCert` - **[optional]** your certificate, see [customizing algorithms](#customizing-algorithms) for an implementation example + +The certificate that will be used to check the signature will first be determined by calling `.getCertFromKeyInfo()`, which function you can customize as you see fit. If that returns `null`, then `.signingCert` is used. If that is `null`, then `.signingKey` is used (for symmetrical signing applications). You can use any dom parser you want in your code (or none, depending on your usage). This sample uses [xmldom](https://github.com/jindw/xmldom) so you should install it first: @@ -143,7 +144,7 @@ var signature = select( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']" )[0]; var sig = new SignedXml(); -sig.keyInfoProvider = new FileKeyInfo("client_public.pem"); +sig.signingCert = new FileKeyInfo("client_public.pem"); sig.loadSignature(signature); var res = sig.checkSignature(xml); if (!res) console.log(sig.validationErrors); @@ -178,7 +179,7 @@ If you keep failing verification, it is worth trying to guess such a hidden tran ```javascript var option = { implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"] }; var sig = new SignedXml(null, option); -sig.keyInfoProvider = new FileKeyInfo("client_public.pem"); +sig.signingCert = new FileKeyInfo("client_public.pem"); sig.loadSignature(signature); var res = sig.checkSignature(xml); ``` @@ -231,14 +232,6 @@ To verify xml documents: - `checkSignature(xml)` - validates the given xml document and returns true if the validation was successful, `sig.validationErrors` will have the validation errors if any, where: - `xml` - a string containing a xml document -### FileKeyInfo - -A basic key info provider implementation using `fs.readFileSync(file)`, is constructed using `new FileKeyInfo([file])` where: - -- `file` - a path to a pem encoded certificate - -See [verifying xml documents](#verifying-xml-documents) for an example usage - ## Customizing Algorithms The following sample shows how to sign a message using custom algorithms. @@ -252,8 +245,13 @@ var SignedXml = require("xml-crypto").SignedXml, Now define the extension point you want to implement. You can choose one or more. -A key info provider is used to extract and construct the key and the KeyInfo xml section. -Implement it if you want to create a signature with a KeyInfo section, or you want to read your key in a different way then the built-in file-read or string methods. See the implementation in `string-key-info.js` for more information. +To determine the inclusion and contents of a `` element, the function +`getKeyInfoContent()` is called. There is a default implementation of this. If you wish to change +this implementation, provide your own function assigned to the property `.getKeyInfoContent`. If +there are no attributes and no contents to the `` element, it won't be included in the +generated XML. + +To specify custom attributes on ``, add the properties to the `.keyInfoAttributes` property. A custom hash algorithm is used to calculate digests. Implement it if you want a hash other than the built-in methods. @@ -269,7 +267,7 @@ function MyDigest() { } ``` -A custom signing algorithm. The default is RSA-SHA1 +A custom signing algorithm. The default is RSA-SHA1. ```javascript function MySignatureAlgorithm() { @@ -335,7 +333,7 @@ function signXml(xml, xpath, key, dest) { /*configure the signature object to use the custom algorithms*/ sig.signatureAlgorithm = "http://mySignatureAlgorithm"; - sig.keyInfoProvider = new MyKeyInfo(); + sig.signingCert = fs.readFileSync("my_public_cert.pem", "latin1"); sig.canonicalizationAlgorithm = "http://MyCanonicalization"; sig.addReference( "//*[local-name(.)='x']", @@ -355,7 +353,7 @@ var xml = "" + "" + "Harry Potter" + ""; signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml"); ``` -You can always look at the actual code as a sample (or drop me a [mail](mailto:yaronn01@gmail.com)). +You can always look at the actual code as a sample. ## Asynchronous signing and verification diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 6fa512d6..c4e9f9b7 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -418,21 +418,6 @@ SignedXml.prototype.checkSignature = function (xml, callback) { this.validationErrors = []; this.signedXml = xml; - const signingCert = this.getCertFromKeyInfo(this.keyInfo) || this.signingCert; - if (!signingCert) { - var err2 = new Error( - "key info provider could not resolve key info " + - this.keyInfo + - "\n and no publicCert provided" - ); - if (!callback) { - throw err2; - } else { - callback(err2); - return; - } - } - var doc = new Dom().parseFromString(xml); if (!this.validateReferences(doc)) { From c09e37364d7583587df76bca13fd63f3e78a9ea8 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Wed, 14 Jun 2023 15:35:31 -0400 Subject: [PATCH 04/21] Update types --- index.d.ts | 2 +- lib/signed-xml.js | 17 ----------------- 2 files changed, 1 insertion(+), 18 deletions(-) diff --git a/index.d.ts b/index.d.ts index 843068b6..ec218c12 100644 --- a/index.d.ts +++ b/index.d.ts @@ -135,7 +135,7 @@ export interface TransformAlgorithm { * @param prefix an optional namespace alias to be used for the generated XML */ export interface GetKeyInfoContentArgs { - cert: string | string[]; + cert: string | string[] | Buffer; prefix: string; } diff --git a/lib/signed-xml.js b/lib/signed-xml.js index c4e9f9b7..96b043df 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -359,16 +359,6 @@ SignedXml.defaultNsForPrefix = { SignedXml.findAncestorNs = findAncestorNs; -/** - * Builds the contents of a KeyInfo element as an XML string. - * - * For example, if the value of the prefix argument is 'foo', then - * the resultant XML string will be "" - * - * @param cert the certificate as a string or array of strings (see https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/#sec-X509Data) - * @param prefix an optional namespace alias to be used for the generated XML - * @return an XML string representation of the contents of a KeyInfo element - */ SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = {}) { if (publicCert == null) { return null; @@ -392,13 +382,6 @@ SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = { return `<${prefix}X509Data>${x509Certs}`; }; -/** - * Returns the value of the signing certificate based on the contents of the - * specified KeyInfo. - * - * @param keyInfo an array with exactly one KeyInfo element (see https://www.w3.org/TR/2008/REC-xmldsig-core-20080610/#sec-X509Data) - * @return the signing certificate as a string in PEM format or `null` if none is found - */ SignedXml.getCertFromKeyInfo = function (keyInfo) { if (keyInfo != null && keyInfo.length > 0) { const certs = xpath.select(".//*[local-name(.)='X509Certificate']", keyInfo[0]); From 62fafe9a37a2692c30525dac2357284cf27650cf Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Wed, 14 Jun 2023 15:37:36 -0400 Subject: [PATCH 05/21] better documentation --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a202a10e..bcd9e77c 100644 --- a/README.md +++ b/README.md @@ -118,7 +118,7 @@ To generate a `` element in the signature you must provide When verifying a xml document you must specify the following properties on a ``SignedXml` instance: -- `sign.signingCert` - **[optional]** your certificate, see [customizing algorithms](#customizing-algorithms) for an implementation example +- `sign.signingCert` - **[optional]** your certificate as a string, a string of multiple certs in PEM format, or a Buffer, see [customizing algorithms](#customizing-algorithms) for an implementation example The certificate that will be used to check the signature will first be determined by calling `.getCertFromKeyInfo()`, which function you can customize as you see fit. If that returns `null`, then `.signingCert` is used. If that is `null`, then `.signingKey` is used (for symmetrical signing applications). From 84c8146af9f3dcd757791b103fcb102fe7fbb4f2 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Wed, 14 Jun 2023 16:12:08 -0400 Subject: [PATCH 06/21] Add test per PR review This test failed unless we go back and make sure to clear out any `getKeyInfoContent()` functions that may have been set. If they wish to use one, they should set it after enabling HMAC. --- lib/signed-xml.js | 3 ++- test/key-info-tests.js | 23 +++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 96b043df..deffeb42 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -347,10 +347,11 @@ SignedXml.SignatureAlgorithms = { * and digital signature algos enabled at the same time. * This enables HMAC and disables other signing algos. */ -SignedXml.enableHMAC = function () { +SignedXml.prototype.enableHMAC = function () { SignedXml.SignatureAlgorithms = { "http://www.w3.org/2000/09/xmldsig#hmac-sha1": HMACSHA1, }; + this.getKeyInfoContent = () => null; }; SignedXml.defaultNsForPrefix = { diff --git a/test/key-info-tests.js b/test/key-info-tests.js index f78d58da..1fe58435 100644 --- a/test/key-info-tests.js +++ b/test/key-info-tests.js @@ -1,7 +1,9 @@ const select = require("xpath").select; -const dom = require("@xmldom/xmldom").DOMParser; +const xmldom = require("@xmldom/xmldom"); const SignedXml = require("../lib/signed-xml.js").SignedXml; const fs = require("fs"); +const xpath = require("xpath"); +const crypto = require("../index.js"); const expect = require("chai").expect; describe("KeyInfo tests", function () { @@ -12,8 +14,25 @@ describe("KeyInfo tests", function () { sig.signingCert = fs.readFileSync("./test/static/client_public.pem"); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); + const doc = new xmldom.DOMParser().parseFromString(signedXml); const x509 = select("//*[local-name(.)='X509Certificate']", doc.documentElement); expect(x509.length, "X509Certificate element should exist").to.equal(1); }); + + it("make sure private hmac key is not leaked due to key confusion", function () { + const xml = "" + "" + "Harry Potter" + "" + ""; + const sig = new crypto.SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/hmac.key"); + sig.signingCert = fs.readFileSync("./test/static/hmac.key"); + sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; + sig.enableHMAC(); + sig.addReference("//*[local-name(.)='book']"); + sig.computeSignature(xml); + + const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); + + const keyInfo = xpath.select("//*[local-name(.)='KeyInfo']", doc)[0]; + + expect(keyInfo).to.be.undefined; + }); }); From 49b8e64ff6fda089eb4e9482c22f1fd9ff595beb Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 15 Jun 2023 01:44:44 -0400 Subject: [PATCH 07/21] private hmac --- test/hmac-tests.js | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/test/hmac-tests.js b/test/hmac-tests.js index d3f0c967..85afd6e5 100644 --- a/test/hmac-tests.js +++ b/test/hmac-tests.js @@ -2,21 +2,13 @@ var crypto = require("../index"); var xpath = require("xpath"); var xmldom = require("@xmldom/xmldom"); var fs = require("fs"); +const { sign } = require("crypto"); var expect = require("chai").expect; var sigAlgs; describe("HMAC tests", function () { - beforeEach(function () { - sigAlgs = crypto.SignedXml.SignatureAlgorithms; - crypto.SignedXml.enableHMAC(); - }); - - afterEach(function () { - crypto.SignedXml.SignatureAlgorithms = sigAlgs; - }); - - it("test validating HMAC signature", function () { + it("test validating HMAC signature", function () { var xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); var signature = xpath.select( @@ -24,6 +16,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); + sig.enableHMAC() sig.signingCert = fs.readFileSync("./test/static/hmac.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -39,6 +32,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); + sig.enableHMAC() sig.signingCert = fs.readFileSync("./test/static/hmac-foobar.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -49,6 +43,7 @@ describe("HMAC tests", function () { it("test create and validate HMAC signature", function () { var xml = "" + "" + "Harry Potter" + "" + ""; var sig = new crypto.SignedXml(); + sig.enableHMAC() sig.signingKey = fs.readFileSync("./test/static/hmac.key"); sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; sig.addReference("//*[local-name(.)='book']"); From 3cb410f7825a63143ed6898f4c9825826061c1f3 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 10:01:35 +0200 Subject: [PATCH 08/21] fix broken tests --- test/hmac-tests.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/test/hmac-tests.js b/test/hmac-tests.js index d3f0c967..9b66ed33 100644 --- a/test/hmac-tests.js +++ b/test/hmac-tests.js @@ -7,13 +7,15 @@ var expect = require("chai").expect; var sigAlgs; describe("HMAC tests", function () { - beforeEach(function () { + + before(function (done) { sigAlgs = crypto.SignedXml.SignatureAlgorithms; - crypto.SignedXml.enableHMAC(); + done(); }); - afterEach(function () { + after(function (done) { crypto.SignedXml.SignatureAlgorithms = sigAlgs; + done(); }); it("test validating HMAC signature", function () { @@ -24,6 +26,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); + sig.enableHMAC(); sig.signingCert = fs.readFileSync("./test/static/hmac.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -39,6 +42,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); + sig.enableHMAC(); sig.signingCert = fs.readFileSync("./test/static/hmac-foobar.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -49,6 +53,7 @@ describe("HMAC tests", function () { it("test create and validate HMAC signature", function () { var xml = "" + "" + "Harry Potter" + "" + ""; var sig = new crypto.SignedXml(); + sig.enableHMAC(); sig.signingKey = fs.readFileSync("./test/static/hmac.key"); sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; sig.addReference("//*[local-name(.)='book']"); @@ -60,6 +65,7 @@ describe("HMAC tests", function () { doc )[0]; var verify = new crypto.SignedXml(); + sig.enableHMAC(); verify.signingCert = fs.readFileSync("./test/static/hmac.key"); verify.loadSignature(signature); var result = verify.checkSignature(sig.getSignedXml()); From 40f1e8f6ac50d13163ae76b399d2e79a4ffd7157 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 10:10:57 +0200 Subject: [PATCH 09/21] fix bug --- lib/signed-xml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/signed-xml.js b/lib/signed-xml.js index deffeb42..cc8cfda5 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -377,7 +377,7 @@ SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = { } if (Array.isArray(publicCert)) { - x509Certs = publicCert.map((c) => `${utils.pemToDer(c)}`); + x509Certs = publicCert.map((c) => `${utils.pemToDer(c)}`).join(""); } return `<${prefix}X509Data>${x509Certs}`; From 530f34e11d94db5c5620bdac6dece38c86638932 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 10:12:07 +0200 Subject: [PATCH 10/21] fix bug --- lib/signed-xml.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/signed-xml.js b/lib/signed-xml.js index cc8cfda5..5196771f 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -365,7 +365,7 @@ SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = { return null; } - prefix = prefix ? prefix + ":" : prefix; + prefix = prefix ? prefix + ":" : ''; let x509Certs = ""; if (Buffer.isBuffer(publicCert)) { From 0a5cff58be8c7d821953c4639aa26a6b3cbfc1b0 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 10:15:56 +0200 Subject: [PATCH 11/21] add test (failing) to make sure private keys are not added to the KeyInfo --- test/signature-unit-tests.js | 279 ++++++++++++++++++----------------- 1 file changed, 147 insertions(+), 132 deletions(-) diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index 604b221a..f5ae283a 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1,23 +1,23 @@ -var select = require("xpath").select, - dom = require("@xmldom/xmldom").DOMParser, - SignedXml = require("../lib/signed-xml.js").SignedXml, - fs = require("fs"), - crypto = require("crypto"); -var expect = require("chai").expect; +const select = require("xpath").select; + const dom = require("@xmldom/xmldom").DOMParser; + const SignedXml = require("../lib/signed-xml.js").SignedXml; + const fs = require("fs"); + const crypto = require("crypto"); +const expect = require("chai").expect; describe("Signature unit tests", function () { function verifySignature(xml, mode) { - var doc = new dom().parseFromString(xml); - var node = select( + const doc = new dom().parseFromString(xml); + const node = select( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc )[0]; - var sig = new SignedXml(mode); + const sig = new SignedXml(mode); sig.signingCert = fs.readFileSync("./test/static/client_public.pem"); sig.loadSignature(node); try { - var res = sig.checkSignature(xml); + const res = sig.checkSignature(xml); return res; } catch (e) { @@ -26,19 +26,19 @@ describe("Signature unit tests", function () { } function passValidSignature(file, mode) { - var xml = fs.readFileSync(file).toString(); - var res = verifySignature(xml, mode); + const xml = fs.readFileSync(file).toString(); + const res = verifySignature(xml, mode); expect(res, "expected signature to be valid, but it was reported invalid").to.equal(true); } function passLoadSignature(file, toString) { - var xml = fs.readFileSync(file).toString(); - var doc = new dom().parseFromString(xml); - var node = select( + const xml = fs.readFileSync(file).toString(); + const doc = new dom().parseFromString(xml); + const node = select( "/*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc )[0]; - var sig = new SignedXml(); + const sig = new SignedXml(); sig.loadSignature(toString ? node.toString() : node); expect(sig.canonicalizationAlgorithm, "wrong canonicalization method").to.equal( @@ -53,7 +53,7 @@ describe("Signature unit tests", function () { "PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI=" ); - var keyInfo = select( + const keyInfo = select( "//*[local-name(.)='KeyInfo']/*[local-name(.)='dummyKey']", sig.keyInfo[0] )[0]; @@ -61,15 +61,15 @@ describe("Signature unit tests", function () { expect(sig.references.length).to.equal(3); - var digests = [ + const digests = [ "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=", "K4dI497ZCxzweDIrbndUSmtoezY=", "sH1gxKve8wlU8LlFVa2l6w3HMJ0=", ]; - for (var i = 0; i < sig.references.length; i++) { - var ref = sig.references[i]; - var expectedUri = "#_" + i; + for (let i = 0; i < sig.references.length; i++) { + const ref = sig.references[i]; + const expectedUri = "#_" + i; expect( ref.uri, "wrong uri for index " + i + ". expected: " + expectedUri + " actual: " + ref.uri @@ -82,35 +82,35 @@ describe("Signature unit tests", function () { } function failInvalidSignature(file, mode) { - var xml = fs.readFileSync(file).toString(); - var res = verifySignature(xml, mode); + const xml = fs.readFileSync(file).toString(); + const res = verifySignature(xml, mode); expect(res, "expected signature to be invalid, but it was reported valid").to.equal(false); } function verifyDoesNotDuplicateIdAttributes(mode, prefix) { - var xml = + const xml = ""; - var sig = new SignedXml(mode); + const sig = new SignedXml(mode); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='x']"); sig.computeSignature(xml); - var signedXml = sig.getOriginalXmlWithIds(); - var doc = new dom().parseFromString(signedXml); - var attrs = select("//@*", doc); + const signedXml = sig.getOriginalXmlWithIds(); + const doc = new dom().parseFromString(signedXml); + const attrs = select("//@*", doc); expect(attrs.length, "wrong number of attributes").to.equal(2); } function nodeExists(doc, xpath) { - if (!doc && !xpath) return; - var node = select(xpath, doc); + if (!doc && !xpath) {return;} + const node = select(xpath, doc); expect(node.length, "xpath " + xpath + " not found").to.equal(1); } function verifyAddsId(mode, nsMode) { - var xml = ''; - var sig = new SignedXml(mode); + const xml = ''; + const sig = new SignedXml(mode); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='x']"); @@ -118,12 +118,12 @@ describe("Signature unit tests", function () { sig.addReference("//*[local-name(.)='w']"); sig.computeSignature(xml); - var signedXml = sig.getOriginalXmlWithIds(); - var doc = new dom().parseFromString(signedXml); + const signedXml = sig.getOriginalXmlWithIds(); + const doc = new dom().parseFromString(signedXml); - var op = nsMode == "equal" ? "=" : "!="; + const op = nsMode == "equal" ? "=" : "!="; - var xpath = + const xpath = "//*[local-name(.)='{elem}' and '_{id}' = @*[local-name(.)='Id' and namespace-uri(.)" + op + "'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd']]"; @@ -135,9 +135,9 @@ describe("Signature unit tests", function () { } function verifyAddsAttrs() { - var xml = 'xml-cryptogithub'; - var sig = new SignedXml(); - var attrs = { + const xml = 'xml-cryptogithub'; + const sig = new SignedXml(); + const attrs = { Id: "signatureTest", data: "dataValue", xmlns: "http://custom-xmlns#", @@ -151,9 +151,9 @@ describe("Signature unit tests", function () { attrs: attrs, }); - var signedXml = sig.getSignatureXml(); - var doc = new dom().parseFromString(signedXml); - var signatureNode = doc.documentElement; + const signedXml = sig.getSignatureXml(); + const doc = new dom().parseFromString(signedXml); + const signatureNode = doc.documentElement; expect( attrs.Id, @@ -173,9 +173,9 @@ describe("Signature unit tests", function () { } function verifyReferenceNS() { - var xml = + const xml = 'xml-cryptogithub'; - var sig = new SignedXml("wssecurity"); + const sig = new SignedXml("wssecurity"); sig.signingKey = fs.readFileSync("./test/static/client.pem"); @@ -187,9 +187,9 @@ describe("Signature unit tests", function () { }, }); - var signedXml = sig.getSignatureXml(); - var doc = new dom().parseFromString(signedXml); - var references = select("//*[local-name(.)='Reference']", doc); + const signedXml = sig.getSignatureXml(); + const doc = new dom().parseFromString(signedXml); + const references = select("//*[local-name(.)='Reference']", doc); expect(references.length).to.equal(2); } @@ -212,14 +212,14 @@ describe("Signature unit tests", function () { }); it("signer appends signature to the root node by default", function () { - var xml = "xml-cryptogithub"; - var sig = new SignedXml(); + const xml = "xml-cryptogithub"; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='name']"); sig.computeSignature(xml); - var doc = new dom().parseFromString(sig.getSignedXml()); + const doc = new dom().parseFromString(sig.getSignedXml()); expect( doc.documentElement.lastChild.localName, @@ -228,8 +228,8 @@ describe("Signature unit tests", function () { }); it("signer appends signature to a reference node", function () { - var xml = "xml-cryptogithub"; - var sig = new SignedXml(); + const xml = "xml-cryptogithub"; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -241,8 +241,8 @@ describe("Signature unit tests", function () { }, }); - var doc = new dom().parseFromString(sig.getSignedXml()); - var referenceNode = select("/root/name", doc)[0]; + const doc = new dom().parseFromString(sig.getSignedXml()); + const referenceNode = select("/root/name", doc)[0]; expect( referenceNode.lastChild.localName, @@ -251,8 +251,8 @@ describe("Signature unit tests", function () { }); it("signer prepends signature to a reference node", function () { - var xml = "xml-cryptogithub"; - var sig = new SignedXml(); + const xml = "xml-cryptogithub"; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -264,8 +264,8 @@ describe("Signature unit tests", function () { }, }); - var doc = new dom().parseFromString(sig.getSignedXml()); - var referenceNode = select("/root/name", doc)[0]; + const doc = new dom().parseFromString(sig.getSignedXml()); + const referenceNode = select("/root/name", doc)[0]; expect( referenceNode.firstChild.localName, @@ -274,8 +274,8 @@ describe("Signature unit tests", function () { }); it("signer inserts signature before a reference node", function () { - var xml = "xml-cryptogithub"; - var sig = new SignedXml(); + const xml = "xml-cryptogithub"; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -287,8 +287,8 @@ describe("Signature unit tests", function () { }, }); - var doc = new dom().parseFromString(sig.getSignedXml()); - var referenceNode = select("/root/name", doc)[0]; + const doc = new dom().parseFromString(sig.getSignedXml()); + const referenceNode = select("/root/name", doc)[0]; expect( referenceNode.previousSibling.localName, @@ -297,8 +297,8 @@ describe("Signature unit tests", function () { }); it("signer inserts signature after a reference node", function () { - var xml = "xml-cryptogithub"; - var sig = new SignedXml(); + const xml = "xml-cryptogithub"; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -310,8 +310,8 @@ describe("Signature unit tests", function () { }, }); - var doc = new dom().parseFromString(sig.getSignedXml()); - var referenceNode = select("/root/name", doc)[0]; + const doc = new dom().parseFromString(sig.getSignedXml()); + const referenceNode = select("/root/name", doc)[0]; expect( referenceNode.nextSibling.localName, @@ -360,8 +360,8 @@ describe("Signature unit tests", function () { }; } - var xml = ''; - var sig = new SignedXml(); + const xml = ''; + const sig = new SignedXml(); SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; @@ -391,8 +391,8 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - var signature = sig.getSignatureXml(); - var expected = + const signature = sig.getSignatureXml(); + const expected = '' + "" + '' + @@ -427,8 +427,8 @@ describe("Signature unit tests", function () { expect(expected, "wrong signature format").to.equal(signature); - var signedXml = sig.getSignedXml(); - var expectedSignedXml = + const signedXml = sig.getSignedXml(); + const expectedSignedXml = '' + '' + "" + @@ -465,14 +465,14 @@ describe("Signature unit tests", function () { expect(expectedSignedXml, "wrong signedXml format").to.equal(signedXml); - var originalXmlWithIds = sig.getOriginalXmlWithIds(); - var expectedOriginalXmlWithIds = + const originalXmlWithIds = sig.getOriginalXmlWithIds(); + const expectedOriginalXmlWithIds = ''; expect(expectedOriginalXmlWithIds, "wrong OriginalXmlWithIds").to.equal(originalXmlWithIds); }); it("signer creates signature with correct structure (with prefix)", function () { - var prefix = "ds"; + const prefix = "ds"; function DummyDigest() { this.getHash = function () { @@ -514,8 +514,8 @@ describe("Signature unit tests", function () { }; } - var xml = ''; - var sig = new SignedXml(); + const xml = ''; + const sig = new SignedXml(); SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; @@ -545,9 +545,9 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml, { prefix: prefix }); - var signature = sig.getSignatureXml(); + const signature = sig.getSignatureXml(); - var expected = + const expected = '' + "" + '' + @@ -582,8 +582,8 @@ describe("Signature unit tests", function () { expect(expected, "wrong signature format").to.equal(signature); - var signedXml = sig.getSignedXml(); - var expectedSignedXml = + const signedXml = sig.getSignedXml(); + const expectedSignedXml = '' + '' + "" + @@ -620,16 +620,16 @@ describe("Signature unit tests", function () { expect(expectedSignedXml, "wrong signedXml format").to.equal(signedXml); - var originalXmlWithIds = sig.getOriginalXmlWithIds(); - var expectedOriginalXmlWithIds = + const originalXmlWithIds = sig.getOriginalXmlWithIds(); + const expectedOriginalXmlWithIds = ''; expect(expectedOriginalXmlWithIds, "wrong OriginalXmlWithIds").to.equal(originalXmlWithIds); }); it("signer creates correct signature values", function () { - var xml = + const xml = ''; - var sig = new SignedXml(); + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -638,8 +638,8 @@ describe("Signature unit tests", function () { sig.addReference("//*[local-name(.)='w']"); sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); - var expected = + const signedXml = sig.getSignedXml(); + const expected = '' + '' + "" + @@ -676,9 +676,9 @@ describe("Signature unit tests", function () { it("signer creates correct signature values using async callback", function () { function DummySignatureAlgorithm() { this.getSignature = function (signedInfo, signingKey, callback) { - var signer = crypto.createSign("RSA-SHA1"); + const signer = crypto.createSign("RSA-SHA1"); signer.update(signedInfo); - var res = signer.sign(signingKey, "base64"); + const res = signer.sign(signingKey, "base64"); //Do some asynchronous things here callback(null, res); }; @@ -687,10 +687,10 @@ describe("Signature unit tests", function () { }; } - var xml = + const xml = ''; SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm; - var sig = new SignedXml(); + const sig = new SignedXml(); sig.signatureAlgorithm = "http://dummySignatureAlgorithmAsync"; sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -700,8 +700,8 @@ describe("Signature unit tests", function () { sig.addReference("//*[local-name(.)='w']"); sig.computeSignature(xml, function () { - var signedXml = sig.getSignedXml(); - var expected = + const signedXml = sig.getSignedXml(); + const expected = '' + '' + "" + @@ -773,8 +773,8 @@ describe("Signature unit tests", function () { }); it("allow empty reference uri when signing", function () { - var xml = ""; - var sig = new SignedXml(); + const xml = ""; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -789,15 +789,15 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); - var doc = new dom().parseFromString(signedXml); - var URI = select("//*[local-name(.)='Reference']/@URI", doc)[0]; + const signedXml = sig.getSignedXml(); + const doc = new dom().parseFromString(signedXml); + const URI = select("//*[local-name(.)='Reference']/@URI", doc)[0]; expect(URI.value, "uri should be empty but instead was " + URI.value).to.equal(""); }); it("signer appends signature to a non-existing reference node", function () { - var xml = "xml-cryptogithub"; - var sig = new SignedXml(); + const xml = "xml-cryptogithub"; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -827,7 +827,7 @@ describe("Signature unit tests", function () { ); } - var xml = + const xml = ' ' + " " + " " + ""; - var sig = new SignedXml(); + const sig = new SignedXml(); const assertionId = "_81d5fba5c807be9e9cf60c58566349b1"; sig.getKeyInfoContent = getKeyInfoContentWithAssertionId.bind(this, { assertionId }); sig.signingKey = fs.readFileSync("./test/static/client.pem"); @@ -853,15 +853,15 @@ describe("Signature unit tests", function () { wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", }, }); - var result = sig.getSignedXml(); + const result = sig.getSignedXml(); expect((result.match(/xmlns:wsu=/g) || []).length).to.equal(1); expect((result.match(/xmlns:wsse=/g) || []).length).to.equal(1); expect(result.includes(assertionId)).to.be.true; }); it("creates InclusiveNamespaces element when inclusiveNamespacesPrefixList is set on Reference", function () { - var xml = ""; - var sig = new SignedXml(); + const xml = ""; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -875,16 +875,16 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); + const signedXml = sig.getSignedXml(); - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select( + const doc = new dom().parseFromString(signedXml); + const inclusiveNamespaces = select( "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); expect(inclusiveNamespaces.length, "InclusiveNamespaces element should exist").to.equal(1); - var prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); + const prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); expect( prefixListAttribute, "InclusiveNamespaces element should have the correct PrefixList attribute value" @@ -892,8 +892,8 @@ describe("Signature unit tests", function () { }); it("does not create InclusiveNamespaces element when inclusiveNamespacesPrefixList is not set on Reference", function () { - var xml = ""; - var sig = new SignedXml(); + const xml = ""; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -907,10 +907,10 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); + const signedXml = sig.getSignedXml(); - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select( + const doc = new dom().parseFromString(signedXml); + const inclusiveNamespaces = select( "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); @@ -919,8 +919,8 @@ describe("Signature unit tests", function () { }); it("creates InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is set on SignedXml options", function () { - var xml = ""; - var sig = new SignedXml(null, { inclusiveNamespacesPrefixList: "prefix1 prefix2" }); + const xml = ""; + const sig = new SignedXml(null, { inclusiveNamespacesPrefixList: "prefix1 prefix2" }); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -931,10 +931,10 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); + const signedXml = sig.getSignedXml(); - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select( + const doc = new dom().parseFromString(signedXml); + const inclusiveNamespaces = select( "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); @@ -944,7 +944,7 @@ describe("Signature unit tests", function () { "InclusiveNamespaces element should exist inside CanonicalizationMethod" ).to.equal(1); - var prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); + const prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); expect( prefixListAttribute, "InclusiveNamespaces element inside CanonicalizationMethod should have the correct PrefixList attribute value" @@ -952,8 +952,8 @@ describe("Signature unit tests", function () { }); it("does not create InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is not set on SignedXml options", function () { - var xml = ""; - var sig = new SignedXml(null); // Omit inclusiveNamespacesPrefixList property + const xml = ""; + const sig = new SignedXml(null); // Omit inclusiveNamespacesPrefixList property sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -964,10 +964,10 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); + const signedXml = sig.getSignedXml(); - var doc = new dom().parseFromString(signedXml); - var inclusiveNamespaces = select( + const doc = new dom().parseFromString(signedXml); + const inclusiveNamespaces = select( "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); @@ -979,8 +979,8 @@ describe("Signature unit tests", function () { }); it("adds attributes to KeyInfo element when attrs are present in keyInfoProvider", function () { - var xml = ""; - var sig = new SignedXml(); + const xml = ""; + const sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.keyInfoAttributes = { CustomUri: "http://www.example.com/keyinfo", @@ -989,22 +989,37 @@ describe("Signature unit tests", function () { sig.getKeyInfoContent = () => ""; sig.computeSignature(xml); - var signedXml = sig.getSignedXml(); + const signedXml = sig.getSignedXml(); - var doc = new dom().parseFromString(signedXml); - var keyInfoElement = select("//*[local-name(.)='KeyInfo']", doc.documentElement); + const doc = new dom().parseFromString(signedXml); + const keyInfoElement = select("//*[local-name(.)='KeyInfo']", doc.documentElement); expect(keyInfoElement.length, "KeyInfo element should exist").to.equal(1); - var algorithmAttribute = keyInfoElement[0].getAttribute("CustomUri"); + const algorithmAttribute = keyInfoElement[0].getAttribute("CustomUri"); expect( algorithmAttribute, "KeyInfo element should have the correct CustomUri attribute value" ).to.equal("http://www.example.com/keyinfo"); - var customAttribute = keyInfoElement[0].getAttribute("CustomAttribute"); + const customAttribute = keyInfoElement[0].getAttribute("CustomAttribute"); expect( customAttribute, "KeyInfo element should have the correct CustomAttribute attribute value" ).to.equal("custom-value"); }); + + it("does not add private keys to KeyInfo element", function () { + const xml = ""; + const sig = new SignedXml(); + sig.signingKey = fs.readFileSync("./test/static/client_bundle.pem"); + sig.signingCert = fs.readFileSync("./test/static/client_bundle.pem"); + sig.computeSignature(xml); + const signedXml = sig.getSignedXml(); + + const doc = new dom().parseFromString(signedXml); + const x509certificates = select("//*[local-name(.)='X509Certificate']", doc.documentElement); + + expect(x509certificates.length, + "There should be only one certificate (private key was added to X509Certificate)").to.equal(1); + }); }); From 04a4915fae4414cce5e76dee7afc5a927d748c3f Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 10:19:40 +0200 Subject: [PATCH 12/21] add pem file which containts both public and private key --- test/static/client_bundle.pem | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 test/static/client_bundle.pem diff --git a/test/static/client_bundle.pem b/test/static/client_bundle.pem new file mode 100644 index 00000000..c99c10e2 --- /dev/null +++ b/test/static/client_bundle.pem @@ -0,0 +1,28 @@ +-----BEGIN CERTIFICATE----- +MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW +MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy +MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG +SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd +Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x +O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf +z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU +MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN +AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5 +sT/txBnVJGziyO8DPYdu2fPMER8ajJfl +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL4vpoH3H3byehjj +7RAGxefGRATiq4mXtzc9Q91W7uT0DTaFEbjzVch9aGsNjmLs4QHsoZbuoUmi0st4 +x5z9SQpTAKC/dW8muzacT3E7dJJYh03MAO6RiH4LG34VRTq1SQN6qDt2rCK85eG4 +5NHI4jceptZNu6Zot1zyO5/PYuFpAgMBAAECgYAhspeyF3M/xB7WIixy1oBiXMLY +isESFAumgfhwU2LotkVRD6rgNl1QtMe3kCNWa9pCWQcYkxeI0IzA+JmFu2shVvoR +oL7eV4VCe1Af33z24E46+cY5grxNhHt/LyCnZKcitvCcrzXExUc5n6KngX0mMKgk +W7skZDwsnKzhyUV8wQJBAN2bQMeASQVOqdfqBdFgC/NPnKY2cuDi6h659QN1l+kg +X3ywdZ7KKftJo1G9l45SN9YpkyEd9zEO6PMFaufJvZUCQQDbtAWxk0i8BT3UTNWC +T/9bUQROPcGZagwwnRFByX7gpmfkf1ImIvbWVXSpX68/IjbjSkTw1nj/Yj1NwFZ0 +nxeFAkEAzPhRpXVBlPgaXkvlz7AfvY+wW4hXHyyi0YK8XdPBi25XA5SPZiylQfjt +Z6iN6qSfYqYXoPT/c0/QJR+orvVJNQJBANhRPNXljVTK2GDCseoXd/ZiI5ohxg+W +UaA/1fDvQsRQM7TQA4NXI7BO/YmSk4rW1jIeOxjiIspY4MFAIh+7UL0CQFL6zTg6 +wfeMlEZzvgqwCGoLuvTnqtvyg45z7pfcrg2cHdgCXIy9kErcjwGiu6BOevEA1qTW +Rk+bv0tknWvcz/s= +-----END PRIVATE KEY----- From 2acd2eeb587d7cc813cb9263a9b068fa6af57764 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 10:21:22 +0200 Subject: [PATCH 13/21] revert var to const change (eslint did it) --- test/signature-unit-tests.js | 274 +++++++++++++++++------------------ 1 file changed, 137 insertions(+), 137 deletions(-) diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index f5ae283a..05e269d7 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1,23 +1,23 @@ -const select = require("xpath").select; - const dom = require("@xmldom/xmldom").DOMParser; - const SignedXml = require("../lib/signed-xml.js").SignedXml; - const fs = require("fs"); - const crypto = require("crypto"); -const expect = require("chai").expect; +var select = require("xpath").select, + dom = require("@xmldom/xmldom").DOMParser, + SignedXml = require("../lib/signed-xml.js").SignedXml, + fs = require("fs"), + crypto = require("crypto"); +var expect = require("chai").expect; describe("Signature unit tests", function () { function verifySignature(xml, mode) { - const doc = new dom().parseFromString(xml); - const node = select( + var doc = new dom().parseFromString(xml); + var node = select( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc )[0]; - const sig = new SignedXml(mode); + var sig = new SignedXml(mode); sig.signingCert = fs.readFileSync("./test/static/client_public.pem"); sig.loadSignature(node); try { - const res = sig.checkSignature(xml); + var res = sig.checkSignature(xml); return res; } catch (e) { @@ -26,19 +26,19 @@ describe("Signature unit tests", function () { } function passValidSignature(file, mode) { - const xml = fs.readFileSync(file).toString(); - const res = verifySignature(xml, mode); + var xml = fs.readFileSync(file).toString(); + var res = verifySignature(xml, mode); expect(res, "expected signature to be valid, but it was reported invalid").to.equal(true); } function passLoadSignature(file, toString) { - const xml = fs.readFileSync(file).toString(); - const doc = new dom().parseFromString(xml); - const node = select( + var xml = fs.readFileSync(file).toString(); + var doc = new dom().parseFromString(xml); + var node = select( "/*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc )[0]; - const sig = new SignedXml(); + var sig = new SignedXml(); sig.loadSignature(toString ? node.toString() : node); expect(sig.canonicalizationAlgorithm, "wrong canonicalization method").to.equal( @@ -53,7 +53,7 @@ describe("Signature unit tests", function () { "PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI=" ); - const keyInfo = select( + var keyInfo = select( "//*[local-name(.)='KeyInfo']/*[local-name(.)='dummyKey']", sig.keyInfo[0] )[0]; @@ -61,15 +61,15 @@ describe("Signature unit tests", function () { expect(sig.references.length).to.equal(3); - const digests = [ + var digests = [ "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=", "K4dI497ZCxzweDIrbndUSmtoezY=", "sH1gxKve8wlU8LlFVa2l6w3HMJ0=", ]; - for (let i = 0; i < sig.references.length; i++) { - const ref = sig.references[i]; - const expectedUri = "#_" + i; + for (var i = 0; i < sig.references.length; i++) { + var ref = sig.references[i]; + var expectedUri = "#_" + i; expect( ref.uri, "wrong uri for index " + i + ". expected: " + expectedUri + " actual: " + ref.uri @@ -82,35 +82,35 @@ describe("Signature unit tests", function () { } function failInvalidSignature(file, mode) { - const xml = fs.readFileSync(file).toString(); - const res = verifySignature(xml, mode); + var xml = fs.readFileSync(file).toString(); + var res = verifySignature(xml, mode); expect(res, "expected signature to be invalid, but it was reported valid").to.equal(false); } function verifyDoesNotDuplicateIdAttributes(mode, prefix) { - const xml = + var xml = ""; - const sig = new SignedXml(mode); + var sig = new SignedXml(mode); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='x']"); sig.computeSignature(xml); - const signedXml = sig.getOriginalXmlWithIds(); - const doc = new dom().parseFromString(signedXml); - const attrs = select("//@*", doc); + var signedXml = sig.getOriginalXmlWithIds(); + var doc = new dom().parseFromString(signedXml); + var attrs = select("//@*", doc); expect(attrs.length, "wrong number of attributes").to.equal(2); } function nodeExists(doc, xpath) { - if (!doc && !xpath) {return;} - const node = select(xpath, doc); + if (!doc && !xpath) return; + var node = select(xpath, doc); expect(node.length, "xpath " + xpath + " not found").to.equal(1); } function verifyAddsId(mode, nsMode) { - const xml = ''; - const sig = new SignedXml(mode); + var xml = ''; + var sig = new SignedXml(mode); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='x']"); @@ -118,12 +118,12 @@ describe("Signature unit tests", function () { sig.addReference("//*[local-name(.)='w']"); sig.computeSignature(xml); - const signedXml = sig.getOriginalXmlWithIds(); - const doc = new dom().parseFromString(signedXml); + var signedXml = sig.getOriginalXmlWithIds(); + var doc = new dom().parseFromString(signedXml); - const op = nsMode == "equal" ? "=" : "!="; + var op = nsMode == "equal" ? "=" : "!="; - const xpath = + var xpath = "//*[local-name(.)='{elem}' and '_{id}' = @*[local-name(.)='Id' and namespace-uri(.)" + op + "'http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd']]"; @@ -135,9 +135,9 @@ describe("Signature unit tests", function () { } function verifyAddsAttrs() { - const xml = 'xml-cryptogithub'; - const sig = new SignedXml(); - const attrs = { + var xml = 'xml-cryptogithub'; + var sig = new SignedXml(); + var attrs = { Id: "signatureTest", data: "dataValue", xmlns: "http://custom-xmlns#", @@ -151,9 +151,9 @@ describe("Signature unit tests", function () { attrs: attrs, }); - const signedXml = sig.getSignatureXml(); - const doc = new dom().parseFromString(signedXml); - const signatureNode = doc.documentElement; + var signedXml = sig.getSignatureXml(); + var doc = new dom().parseFromString(signedXml); + var signatureNode = doc.documentElement; expect( attrs.Id, @@ -173,9 +173,9 @@ describe("Signature unit tests", function () { } function verifyReferenceNS() { - const xml = + var xml = 'xml-cryptogithub'; - const sig = new SignedXml("wssecurity"); + var sig = new SignedXml("wssecurity"); sig.signingKey = fs.readFileSync("./test/static/client.pem"); @@ -187,9 +187,9 @@ describe("Signature unit tests", function () { }, }); - const signedXml = sig.getSignatureXml(); - const doc = new dom().parseFromString(signedXml); - const references = select("//*[local-name(.)='Reference']", doc); + var signedXml = sig.getSignatureXml(); + var doc = new dom().parseFromString(signedXml); + var references = select("//*[local-name(.)='Reference']", doc); expect(references.length).to.equal(2); } @@ -212,14 +212,14 @@ describe("Signature unit tests", function () { }); it("signer appends signature to the root node by default", function () { - const xml = "xml-cryptogithub"; - const sig = new SignedXml(); + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='name']"); sig.computeSignature(xml); - const doc = new dom().parseFromString(sig.getSignedXml()); + var doc = new dom().parseFromString(sig.getSignedXml()); expect( doc.documentElement.lastChild.localName, @@ -228,8 +228,8 @@ describe("Signature unit tests", function () { }); it("signer appends signature to a reference node", function () { - const xml = "xml-cryptogithub"; - const sig = new SignedXml(); + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -241,8 +241,8 @@ describe("Signature unit tests", function () { }, }); - const doc = new dom().parseFromString(sig.getSignedXml()); - const referenceNode = select("/root/name", doc)[0]; + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; expect( referenceNode.lastChild.localName, @@ -251,8 +251,8 @@ describe("Signature unit tests", function () { }); it("signer prepends signature to a reference node", function () { - const xml = "xml-cryptogithub"; - const sig = new SignedXml(); + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -264,8 +264,8 @@ describe("Signature unit tests", function () { }, }); - const doc = new dom().parseFromString(sig.getSignedXml()); - const referenceNode = select("/root/name", doc)[0]; + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; expect( referenceNode.firstChild.localName, @@ -274,8 +274,8 @@ describe("Signature unit tests", function () { }); it("signer inserts signature before a reference node", function () { - const xml = "xml-cryptogithub"; - const sig = new SignedXml(); + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -287,8 +287,8 @@ describe("Signature unit tests", function () { }, }); - const doc = new dom().parseFromString(sig.getSignedXml()); - const referenceNode = select("/root/name", doc)[0]; + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; expect( referenceNode.previousSibling.localName, @@ -297,8 +297,8 @@ describe("Signature unit tests", function () { }); it("signer inserts signature after a reference node", function () { - const xml = "xml-cryptogithub"; - const sig = new SignedXml(); + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -310,8 +310,8 @@ describe("Signature unit tests", function () { }, }); - const doc = new dom().parseFromString(sig.getSignedXml()); - const referenceNode = select("/root/name", doc)[0]; + var doc = new dom().parseFromString(sig.getSignedXml()); + var referenceNode = select("/root/name", doc)[0]; expect( referenceNode.nextSibling.localName, @@ -360,8 +360,8 @@ describe("Signature unit tests", function () { }; } - const xml = ''; - const sig = new SignedXml(); + var xml = ''; + var sig = new SignedXml(); SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; @@ -391,8 +391,8 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - const signature = sig.getSignatureXml(); - const expected = + var signature = sig.getSignatureXml(); + var expected = '' + "" + '' + @@ -427,8 +427,8 @@ describe("Signature unit tests", function () { expect(expected, "wrong signature format").to.equal(signature); - const signedXml = sig.getSignedXml(); - const expectedSignedXml = + var signedXml = sig.getSignedXml(); + var expectedSignedXml = '' + '' + "" + @@ -465,14 +465,14 @@ describe("Signature unit tests", function () { expect(expectedSignedXml, "wrong signedXml format").to.equal(signedXml); - const originalXmlWithIds = sig.getOriginalXmlWithIds(); - const expectedOriginalXmlWithIds = + var originalXmlWithIds = sig.getOriginalXmlWithIds(); + var expectedOriginalXmlWithIds = ''; expect(expectedOriginalXmlWithIds, "wrong OriginalXmlWithIds").to.equal(originalXmlWithIds); }); it("signer creates signature with correct structure (with prefix)", function () { - const prefix = "ds"; + var prefix = "ds"; function DummyDigest() { this.getHash = function () { @@ -514,8 +514,8 @@ describe("Signature unit tests", function () { }; } - const xml = ''; - const sig = new SignedXml(); + var xml = ''; + var sig = new SignedXml(); SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; @@ -545,9 +545,9 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml, { prefix: prefix }); - const signature = sig.getSignatureXml(); + var signature = sig.getSignatureXml(); - const expected = + var expected = '' + "" + '' + @@ -582,8 +582,8 @@ describe("Signature unit tests", function () { expect(expected, "wrong signature format").to.equal(signature); - const signedXml = sig.getSignedXml(); - const expectedSignedXml = + var signedXml = sig.getSignedXml(); + var expectedSignedXml = '' + '' + "" + @@ -620,16 +620,16 @@ describe("Signature unit tests", function () { expect(expectedSignedXml, "wrong signedXml format").to.equal(signedXml); - const originalXmlWithIds = sig.getOriginalXmlWithIds(); - const expectedOriginalXmlWithIds = + var originalXmlWithIds = sig.getOriginalXmlWithIds(); + var expectedOriginalXmlWithIds = ''; expect(expectedOriginalXmlWithIds, "wrong OriginalXmlWithIds").to.equal(originalXmlWithIds); }); it("signer creates correct signature values", function () { - const xml = + var xml = ''; - const sig = new SignedXml(); + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -638,8 +638,8 @@ describe("Signature unit tests", function () { sig.addReference("//*[local-name(.)='w']"); sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); - const expected = + var signedXml = sig.getSignedXml(); + var expected = '' + '' + "" + @@ -676,9 +676,9 @@ describe("Signature unit tests", function () { it("signer creates correct signature values using async callback", function () { function DummySignatureAlgorithm() { this.getSignature = function (signedInfo, signingKey, callback) { - const signer = crypto.createSign("RSA-SHA1"); + var signer = crypto.createSign("RSA-SHA1"); signer.update(signedInfo); - const res = signer.sign(signingKey, "base64"); + var res = signer.sign(signingKey, "base64"); //Do some asynchronous things here callback(null, res); }; @@ -687,10 +687,10 @@ describe("Signature unit tests", function () { }; } - const xml = + var xml = ''; SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm; - const sig = new SignedXml(); + var sig = new SignedXml(); sig.signatureAlgorithm = "http://dummySignatureAlgorithmAsync"; sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -700,8 +700,8 @@ describe("Signature unit tests", function () { sig.addReference("//*[local-name(.)='w']"); sig.computeSignature(xml, function () { - const signedXml = sig.getSignedXml(); - const expected = + var signedXml = sig.getSignedXml(); + var expected = '' + '' + "" + @@ -773,8 +773,8 @@ describe("Signature unit tests", function () { }); it("allow empty reference uri when signing", function () { - const xml = ""; - const sig = new SignedXml(); + var xml = ""; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -789,15 +789,15 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); - const URI = select("//*[local-name(.)='Reference']/@URI", doc)[0]; + var signedXml = sig.getSignedXml(); + var doc = new dom().parseFromString(signedXml); + var URI = select("//*[local-name(.)='Reference']/@URI", doc)[0]; expect(URI.value, "uri should be empty but instead was " + URI.value).to.equal(""); }); it("signer appends signature to a non-existing reference node", function () { - const xml = "xml-cryptogithub"; - const sig = new SignedXml(); + var xml = "xml-cryptogithub"; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.addReference("//*[local-name(.)='repository']"); @@ -827,7 +827,7 @@ describe("Signature unit tests", function () { ); } - const xml = + var xml = ' ' + " " + " " + ""; - const sig = new SignedXml(); + var sig = new SignedXml(); const assertionId = "_81d5fba5c807be9e9cf60c58566349b1"; sig.getKeyInfoContent = getKeyInfoContentWithAssertionId.bind(this, { assertionId }); sig.signingKey = fs.readFileSync("./test/static/client.pem"); @@ -853,15 +853,15 @@ describe("Signature unit tests", function () { wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", }, }); - const result = sig.getSignedXml(); + var result = sig.getSignedXml(); expect((result.match(/xmlns:wsu=/g) || []).length).to.equal(1); expect((result.match(/xmlns:wsse=/g) || []).length).to.equal(1); expect(result.includes(assertionId)).to.be.true; }); it("creates InclusiveNamespaces element when inclusiveNamespacesPrefixList is set on Reference", function () { - const xml = ""; - const sig = new SignedXml(); + var xml = ""; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -875,16 +875,16 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); + var signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); - const inclusiveNamespaces = select( + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); expect(inclusiveNamespaces.length, "InclusiveNamespaces element should exist").to.equal(1); - const prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); + var prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); expect( prefixListAttribute, "InclusiveNamespaces element should have the correct PrefixList attribute value" @@ -892,8 +892,8 @@ describe("Signature unit tests", function () { }); it("does not create InclusiveNamespaces element when inclusiveNamespacesPrefixList is not set on Reference", function () { - const xml = ""; - const sig = new SignedXml(); + var xml = ""; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -907,10 +907,10 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); + var signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); - const inclusiveNamespaces = select( + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); @@ -919,8 +919,8 @@ describe("Signature unit tests", function () { }); it("creates InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is set on SignedXml options", function () { - const xml = ""; - const sig = new SignedXml(null, { inclusiveNamespacesPrefixList: "prefix1 prefix2" }); + var xml = ""; + var sig = new SignedXml(null, { inclusiveNamespacesPrefixList: "prefix1 prefix2" }); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -931,10 +931,10 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); + var signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); - const inclusiveNamespaces = select( + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); @@ -944,7 +944,7 @@ describe("Signature unit tests", function () { "InclusiveNamespaces element should exist inside CanonicalizationMethod" ).to.equal(1); - const prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); + var prefixListAttribute = inclusiveNamespaces[0].getAttribute("PrefixList"); expect( prefixListAttribute, "InclusiveNamespaces element inside CanonicalizationMethod should have the correct PrefixList attribute value" @@ -952,8 +952,8 @@ describe("Signature unit tests", function () { }); it("does not create InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is not set on SignedXml options", function () { - const xml = ""; - const sig = new SignedXml(null); // Omit inclusiveNamespacesPrefixList property + var xml = ""; + var sig = new SignedXml(null); // Omit inclusiveNamespacesPrefixList property sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; @@ -964,10 +964,10 @@ describe("Signature unit tests", function () { ); sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); + var signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); - const inclusiveNamespaces = select( + var doc = new dom().parseFromString(signedXml); + var inclusiveNamespaces = select( "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement ); @@ -979,8 +979,8 @@ describe("Signature unit tests", function () { }); it("adds attributes to KeyInfo element when attrs are present in keyInfoProvider", function () { - const xml = ""; - const sig = new SignedXml(); + var xml = ""; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.keyInfoAttributes = { CustomUri: "http://www.example.com/keyinfo", @@ -989,19 +989,19 @@ describe("Signature unit tests", function () { sig.getKeyInfoContent = () => ""; sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); + var signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); - const keyInfoElement = select("//*[local-name(.)='KeyInfo']", doc.documentElement); + var doc = new dom().parseFromString(signedXml); + var keyInfoElement = select("//*[local-name(.)='KeyInfo']", doc.documentElement); expect(keyInfoElement.length, "KeyInfo element should exist").to.equal(1); - const algorithmAttribute = keyInfoElement[0].getAttribute("CustomUri"); + var algorithmAttribute = keyInfoElement[0].getAttribute("CustomUri"); expect( algorithmAttribute, "KeyInfo element should have the correct CustomUri attribute value" ).to.equal("http://www.example.com/keyinfo"); - const customAttribute = keyInfoElement[0].getAttribute("CustomAttribute"); + var customAttribute = keyInfoElement[0].getAttribute("CustomAttribute"); expect( customAttribute, "KeyInfo element should have the correct CustomAttribute attribute value" @@ -1009,15 +1009,15 @@ describe("Signature unit tests", function () { }); it("does not add private keys to KeyInfo element", function () { - const xml = ""; - const sig = new SignedXml(); + var xml = ""; + var sig = new SignedXml(); sig.signingKey = fs.readFileSync("./test/static/client_bundle.pem"); sig.signingCert = fs.readFileSync("./test/static/client_bundle.pem"); sig.computeSignature(xml); - const signedXml = sig.getSignedXml(); + var signedXml = sig.getSignedXml(); - const doc = new dom().parseFromString(signedXml); - const x509certificates = select("//*[local-name(.)='X509Certificate']", doc.documentElement); + var doc = new dom().parseFromString(signedXml); + var x509certificates = select("//*[local-name(.)='X509Certificate']", doc.documentElement); expect(x509certificates.length, "There should be only one certificate (private key was added to X509Certificate)").to.equal(1); From eaceb0816255c8aab09022f851f14f2487772e78 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 11:16:13 +0200 Subject: [PATCH 14/21] make SignatureAlgorithms, HashAlgorithms and CanonicalizationAlgorithms instance members of SignedXml --- lib/signed-xml.js | 14 +++++++------- test/hmac-tests.js | 14 +------------- test/signature-unit-tests.js | 18 +++++++++--------- 3 files changed, 17 insertions(+), 29 deletions(-) diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 5196771f..9240931d 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -318,7 +318,7 @@ function SignedXml(idMode, options) { this.keyInfoAttributes = {}; } -SignedXml.CanonicalizationAlgorithms = { +SignedXml.prototype.CanonicalizationAlgorithms = { "http://www.w3.org/TR/2001/REC-xml-c14n-20010315": c14n.C14nCanonicalization, "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments": c14n.C14nCanonicalizationWithComments, @@ -328,13 +328,13 @@ SignedXml.CanonicalizationAlgorithms = { "http://www.w3.org/2000/09/xmldsig#enveloped-signature": EnvelopedSignature, }; -SignedXml.HashAlgorithms = { +SignedXml.prototype.HashAlgorithms = { "http://www.w3.org/2000/09/xmldsig#sha1": SHA1, "http://www.w3.org/2001/04/xmlenc#sha256": SHA256, "http://www.w3.org/2001/04/xmlenc#sha512": SHA512, }; -SignedXml.SignatureAlgorithms = { +SignedXml.prototype.SignatureAlgorithms = { "http://www.w3.org/2000/09/xmldsig#rsa-sha1": RSASHA1, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256": RSASHA256, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512": RSASHA512, @@ -348,7 +348,7 @@ SignedXml.SignatureAlgorithms = { * This enables HMAC and disables other signing algos. */ SignedXml.prototype.enableHMAC = function () { - SignedXml.SignatureAlgorithms = { + this.SignatureAlgorithms = { "http://www.w3.org/2000/09/xmldsig#hmac-sha1": HMACSHA1, }; this.getKeyInfoContent = () => null; @@ -501,19 +501,19 @@ SignedXml.prototype.calculateSignatureValue = function (doc, callback) { }; SignedXml.prototype.findSignatureAlgorithm = function (name) { - var algo = SignedXml.SignatureAlgorithms[name]; + var algo = this.SignatureAlgorithms[name]; if (algo) return new algo(); else throw new Error("signature algorithm '" + name + "' is not supported"); }; SignedXml.prototype.findCanonicalizationAlgorithm = function (name) { - var algo = SignedXml.CanonicalizationAlgorithms[name]; + var algo = this.CanonicalizationAlgorithms[name]; if (algo) return new algo(); else throw new Error("canonicalization algorithm '" + name + "' is not supported"); }; SignedXml.prototype.findHashAlgorithm = function (name) { - var algo = SignedXml.HashAlgorithms[name]; + var algo = this.HashAlgorithms[name]; if (algo) return new algo(); else throw new Error("hash algorithm '" + name + "' is not supported"); }; diff --git a/test/hmac-tests.js b/test/hmac-tests.js index 9b66ed33..a325ffdf 100644 --- a/test/hmac-tests.js +++ b/test/hmac-tests.js @@ -4,20 +4,8 @@ var xmldom = require("@xmldom/xmldom"); var fs = require("fs"); var expect = require("chai").expect; -var sigAlgs; - describe("HMAC tests", function () { - before(function (done) { - sigAlgs = crypto.SignedXml.SignatureAlgorithms; - done(); - }); - - after(function (done) { - crypto.SignedXml.SignatureAlgorithms = sigAlgs; - done(); - }); - it("test validating HMAC signature", function () { var xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); @@ -65,7 +53,7 @@ describe("HMAC tests", function () { doc )[0]; var verify = new crypto.SignedXml(); - sig.enableHMAC(); + verify.enableHMAC(); verify.signingCert = fs.readFileSync("./test/static/hmac.key"); verify.loadSignature(signature); var result = verify.checkSignature(sig.getSignedXml()); diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index 05e269d7..aa6123fb 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -363,10 +363,10 @@ describe("Signature unit tests", function () { var xml = ''; var sig = new SignedXml(); - SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; - SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; - SignedXml.HashAlgorithms["http://dummyDigest"] = DummyDigest; - SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; + sig.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; + sig.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; + sig.HashAlgorithms["http://dummyDigest"] = DummyDigest; + sig.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; sig.getKeyInfoContent = function () { @@ -517,10 +517,10 @@ describe("Signature unit tests", function () { var xml = ''; var sig = new SignedXml(); - SignedXml.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; - SignedXml.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; - SignedXml.HashAlgorithms["http://dummyDigest"] = DummyDigest; - SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; + sig.CanonicalizationAlgorithms["http://DummyTransformation"] = DummyTransformation; + sig.CanonicalizationAlgorithms["http://DummyCanonicalization"] = DummyCanonicalization; + sig.HashAlgorithms["http://dummyDigest"] = DummyDigest; + sig.SignatureAlgorithms["http://dummySignatureAlgorithm"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithm"; sig.getKeyInfoContent = function () { @@ -689,8 +689,8 @@ describe("Signature unit tests", function () { var xml = ''; - SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm; var sig = new SignedXml(); + sig.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithmAsync"; sig.signingKey = fs.readFileSync("./test/static/client.pem"); sig.signingCert = null; From 833645460e7f81e9cb316754fd1054a26a542175 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 11:20:06 +0200 Subject: [PATCH 15/21] only put actual x509 certificates in the X509Certificate element --- lib/signed-xml.js | 2 +- lib/utils.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 9240931d..495a6c60 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -373,7 +373,7 @@ SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = { } if (typeof publicCert === "string") { - publicCert = publicCert.match(utils.MULTI_PEM_SPLIT); + publicCert = publicCert.match(utils.EXTRACT_X509_CERTS); } if (Array.isArray(publicCert)) { diff --git a/lib/utils.js b/lib/utils.js index 7984f42e..48460762 100644 --- a/lib/utils.js +++ b/lib/utils.js @@ -77,8 +77,8 @@ function encodeSpecialCharactersInText(text) { }); } -const MULTI_PEM_SPLIT = new RegExp( - "-----BEGIN [A-Z\x20]{1,48}-----[^-]*-----END [A-Z\x20]{1,48}-----", +const EXTRACT_X509_CERTS = new RegExp( + "-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----", "g" ); const PEM_FORMAT_REGEX = new RegExp( @@ -127,7 +127,7 @@ exports.findChilds = findChilds; exports.encodeSpecialCharactersInAttribute = encodeSpecialCharactersInAttribute; exports.encodeSpecialCharactersInText = encodeSpecialCharactersInText; exports.findFirst = findFirst; -exports.MULTI_PEM_SPLIT = MULTI_PEM_SPLIT; +exports.EXTRACT_X509_CERTS = EXTRACT_X509_CERTS; exports.PEM_FORMAT_REGEX = PEM_FORMAT_REGEX; exports.BASE64_REGEX = BASE64_REGEX; exports.pemToDer = pemToDer; From 1669c681e760cb5ae46a73f42d65072e8cda6413 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 11:53:22 +0200 Subject: [PATCH 16/21] improve test --- test/signature-unit-tests.js | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index aa6123fb..00f5709d 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1019,7 +1019,15 @@ describe("Signature unit tests", function () { var doc = new dom().parseFromString(signedXml); var x509certificates = select("//*[local-name(.)='X509Certificate']", doc.documentElement); - expect(x509certificates.length, - "There should be only one certificate (private key was added to X509Certificate)").to.equal(1); + expect(x509certificates.length, "There should be only one certificate (private key was added to X509Certificate)").to.equal(1); + + const { textContent } = x509certificates[0]; + expect(textContent, "X509Certificate TextContent does not exist").to.exist; + + const trimmedTextContent = textContent.trim(); + expect(trimmedTextContent, "Empty certificate added").to.not.be.empty; + + // In case private key was added instead of the certificate + expect(trimmedTextContent.substring(0,4), "Private key was added to X509Certificate").to.equal("MIIB"); }); }); From c6a29f40643fe87c04b0845b20e3439cb0bb3299 Mon Sep 17 00:00:00 2001 From: Ivan Date: Thu, 15 Jun 2023 12:37:20 +0200 Subject: [PATCH 17/21] test with two public certificates --- test/signature-unit-tests.js | 26 +++++++---- test/static/client_bundle.pem | 88 +++++++++++++++++++++++++---------- 2 files changed, 80 insertions(+), 34 deletions(-) diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index 00f5709d..d9c88158 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1008,26 +1008,32 @@ describe("Signature unit tests", function () { ).to.equal("custom-value"); }); - it("does not add private keys to KeyInfo element", function () { + it("adds all certificates and does not add private keys to KeyInfo element", function () { var xml = ""; var sig = new SignedXml(); - sig.signingKey = fs.readFileSync("./test/static/client_bundle.pem"); - sig.signingCert = fs.readFileSync("./test/static/client_bundle.pem"); + var pemBuffer = fs.readFileSync("./test/static/client_bundle.pem") + sig.signingKey = pemBuffer; + sig.signingCert = pemBuffer; sig.computeSignature(xml); var signedXml = sig.getSignedXml(); var doc = new dom().parseFromString(signedXml); + var x509certificates = select("//*[local-name(.)='X509Certificate']", doc.documentElement); + expect(x509certificates.length, "There should be exactly two certificates").to.equal(2); - expect(x509certificates.length, "There should be only one certificate (private key was added to X509Certificate)").to.equal(1); + var cert1 = x509certificates[0]; + var cert2 = x509certificates[1]; + expect(cert1.textContent, "X509Certificate[0] TextContent does not exist").to.exist; + expect(cert2.textContent, "X509Certificate[1] TextContent does not exist").to.exist; - const { textContent } = x509certificates[0]; - expect(textContent, "X509Certificate TextContent does not exist").to.exist; + var trimmedTextContent1 = cert1.textContent.trim(); + var trimmedTextContent2 = cert2.textContent.trim(); + expect(trimmedTextContent1, "Empty certificate added [0]").to.not.be.empty; + expect(trimmedTextContent2, "Empty certificate added [1]").to.not.be.empty; - const trimmedTextContent = textContent.trim(); - expect(trimmedTextContent, "Empty certificate added").to.not.be.empty; + expect(trimmedTextContent1.substring(0,5), "Incorrect value for X509Certificate[0]").to.equal("MIIDC"); + expect(trimmedTextContent2.substring(0,5), "Incorrect value for X509Certificate[1]").to.equal("MIIDZ"); - // In case private key was added instead of the certificate - expect(trimmedTextContent.substring(0,4), "Private key was added to X509Certificate").to.equal("MIIB"); }); }); diff --git a/test/static/client_bundle.pem b/test/static/client_bundle.pem index c99c10e2..bd784097 100644 --- a/test/static/client_bundle.pem +++ b/test/static/client_bundle.pem @@ -1,28 +1,68 @@ -----BEGIN CERTIFICATE----- -MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW -MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy -MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG -SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd -Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x -O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf -z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU -MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN -AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5 -sT/txBnVJGziyO8DPYdu2fPMER8ajJfl +MIIDCzCCAfMCFDdl3bSiEFLCBC3akD+sPuSbRKnyMA0GCSqGSIb3DQEBCwUAMEIx +CzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAaBgNVBAoME0Rl +ZmF1bHQgQ29tcGFueSBMdGQwHhcNMjMwNjE1MTAyNjMwWhcNMzMwNjEyMTAyNjMw +WjBCMQswCQYDVQQGEwJYWDEVMBMGA1UEBwwMRGVmYXVsdCBDaXR5MRwwGgYDVQQK +DBNEZWZhdWx0IENvbXBhbnkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB +CgKCAQEArMLLZkjvJ/Kr3rfhR/77nJdjPumutJ7lJoDgQAwG2qBmse4oJmBDB6fY +XFTrwVH4DKYnJFOaPBAqp+BGpFEjKo/zghEcGxidnuM5Hc6NAfnK3YEmbspc1DGX +cLCfv0Mw3VV+XvDxfLpQdfTA4CM/lgPmO6lUF6er/WaLsLMfJc2+jLXYkIlj+x6b +KVNHC7SG/HkD0WSZAAsfW1RCOQgsgVi/b+TEPR7MqcXzS3R1WWd8dB9EC8VwpU3o +KBZ4EaYYvbEH+z2YW24jl+vxGHM9+UZaoYMzkBnDs+gtmpH35S/+YFbrro+qbRAs +Hy5FhhfQ0ZWbe9nAFUaID0CkemnQOwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQBf +gb5/f8Jv+zR5yD2VhaqZPgIc3lekCi1UxOrmSfnFZ1osSlgenf1dvJeCX9QEh2Lv +FHmp0TflcJ12qHsWdfZSSantFhG5jMFxYD9uARyTHCWtRtdfO0P/KeuORleDN5lE +p7wBCy6JpE5INQxoHYnhO0ujfo9SvZVxpBHRpdSnHrkKn+6UOr6HVFQ4RVyEns7B +oZ/GQ7HWj4qpRF98MUmtwtCCemWPnNSjSAAWuJZ8e4JStjFcx8Vw3xIZbNGCZflw +ECjO3qQDUQmzySBub8FaDZkG1d2ODZsL221ETto4c5DXlesgYBVcPIkQAy53IVoP +hupmcqhjnIejGHsgFAdF +-----END CERTIFICATE----- +-----BEGIN CERTIFICATE----- +MIIDZTCCAk2gAwIBAgIUD2FdlpUMPuX6W1A0OiMKu6g0XuYwDQYJKoZIhvcNAQEL +BQAwQjELMAkGA1UEBhMCWFgxFTATBgNVBAcMDERlZmF1bHQgQ2l0eTEcMBoGA1UE +CgwTRGVmYXVsdCBDb21wYW55IEx0ZDAeFw0yMzA2MTUxMDI2MDNaFw0zMzA2MTIx +MDI2MDNaMEIxCzAJBgNVBAYTAlhYMRUwEwYDVQQHDAxEZWZhdWx0IENpdHkxHDAa +BgNVBAoME0RlZmF1bHQgQ29tcGFueSBMdGQwggEiMA0GCSqGSIb3DQEBAQUAA4IB +DwAwggEKAoIBAQD/emWytGY9zUAJ8Jq++GgTieFkmdgwPq8QcHzfIhqs0n5Y28cS +CExFwoNZaJQiA1lobD2bgwAJPUb4j1zUmnaKeDuc2dq3RhcctJ2kbReqJVwzPW19 +DxWsvADYrjzE6UdgyWZfoanp7IBKjEj3xF13w0rYm1D3lrT7mE5roEA10oOVwErl +HgRcCO8nbWMxy6HnZmMiTY815xdWXVKZpbjNJaVybEEnW128BFafAy24XmMg5PLx +YGLEVExO2RHjEOibDb08/L91wJA8N8rSDoG8Akl1UesdH95VBcMvK4lA2e4Nn2Lu +vFkqtsey6YrNw4OD1uAnQ0hinuo7OlVMYiRLAgMBAAGjUzBRMB0GA1UdDgQWBBTD +b17H/nXO5ZUh74YhBn/X7zZuxTAfBgNVHSMEGDAWgBTDb17H/nXO5ZUh74YhBn/X +7zZuxTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQBsq1PBuJtg +t+EFpbGngOmlMW4YaI77N8+H8Tk9+AIfvv+Awya+8T8ToMyEd7WZXuCabpzFwc5u +TaZiHV0oyHNLLMt+QKrBZ9Ybu/RFJo0kjvbO2FEvtIiz9qqRqPUwD804HpXcyBvP +lcQvJXRUSsaoTmcLtaA6TOWUbzxgHiXIDyiNVaM9B8hgKdqKCDqwtKYSAd9dpQVt +Yq2yEXxLKrJjZRrRF0d5CesA4rOoUiRK7VnZSIo8aO8BtLH7UjOrFg5WFial/1kh +SEmjbsPp6oLNyDtg4eTWkS82polPsCWihm9gEu3+plBQNXH7X6xrPEsn5ZilscCc ++lxT6hClXBXJ -----END CERTIFICATE----- -----BEGIN PRIVATE KEY----- -MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBAL4vpoH3H3byehjj -7RAGxefGRATiq4mXtzc9Q91W7uT0DTaFEbjzVch9aGsNjmLs4QHsoZbuoUmi0st4 -x5z9SQpTAKC/dW8muzacT3E7dJJYh03MAO6RiH4LG34VRTq1SQN6qDt2rCK85eG4 -5NHI4jceptZNu6Zot1zyO5/PYuFpAgMBAAECgYAhspeyF3M/xB7WIixy1oBiXMLY -isESFAumgfhwU2LotkVRD6rgNl1QtMe3kCNWa9pCWQcYkxeI0IzA+JmFu2shVvoR -oL7eV4VCe1Af33z24E46+cY5grxNhHt/LyCnZKcitvCcrzXExUc5n6KngX0mMKgk -W7skZDwsnKzhyUV8wQJBAN2bQMeASQVOqdfqBdFgC/NPnKY2cuDi6h659QN1l+kg -X3ywdZ7KKftJo1G9l45SN9YpkyEd9zEO6PMFaufJvZUCQQDbtAWxk0i8BT3UTNWC -T/9bUQROPcGZagwwnRFByX7gpmfkf1ImIvbWVXSpX68/IjbjSkTw1nj/Yj1NwFZ0 -nxeFAkEAzPhRpXVBlPgaXkvlz7AfvY+wW4hXHyyi0YK8XdPBi25XA5SPZiylQfjt -Z6iN6qSfYqYXoPT/c0/QJR+orvVJNQJBANhRPNXljVTK2GDCseoXd/ZiI5ohxg+W -UaA/1fDvQsRQM7TQA4NXI7BO/YmSk4rW1jIeOxjiIspY4MFAIh+7UL0CQFL6zTg6 -wfeMlEZzvgqwCGoLuvTnqtvyg45z7pfcrg2cHdgCXIy9kErcjwGiu6BOevEA1qTW -Rk+bv0tknWvcz/s= +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCswstmSO8n8qve +t+FH/vucl2M+6a60nuUmgOBADAbaoGax7igmYEMHp9hcVOvBUfgMpickU5o8ECqn +4EakUSMqj/OCERwbGJ2e4zkdzo0B+crdgSZuylzUMZdwsJ+/QzDdVX5e8PF8ulB1 +9MDgIz+WA+Y7qVQXp6v9Zouwsx8lzb6MtdiQiWP7HpspU0cLtIb8eQPRZJkACx9b +VEI5CCyBWL9v5MQ9HsypxfNLdHVZZ3x0H0QLxXClTegoFngRphi9sQf7PZhbbiOX +6/EYcz35RlqhgzOQGcOz6C2akfflL/5gVuuuj6ptECwfLkWGF9DRlZt72cAVRogP +QKR6adA7AgMBAAECggEAQVcvfNEq+u3yiTr8zrEm0vQDCmFxvUi3nJdzuWWTFg9C +qBtOPi18TKHz2AAaZrSs34PcHAYuuHbY20OdFDrH1So6zD/SZIEr5FNGX/qmJFAo +pRxav95zu6HCCFIVKU6tZZkXQatZenYxRlu6s0tBmmiBJKGHd6boCuBFByDIMBB/ +P509g1TrRB70vAL8hqyd5wHJNhuwUvOOfLMfAMOXa5aAtwQlc0PXOUbUxwS3C4NW +acqraGpFFWAOwg+J5eBi5jfXHDyiGhS5p8T8HkcoVyI5WrEQJES3fPlmA2IM8CXj +4ipS329zJNM8SUJuluo1KIJeFMNN5cF1DZqtREb38QKBgQDywatu2mOIAoN+cpwf +VtTDH1qsGYxuCka+7tipt+DxTxIJB/1KTQRwdWb1leqszO3nNS6Q/UIDFMV+oszG +3UuoriOw+xuvYy/PrFdrDki3droipOEllSGmbXGk7rBalAbgswyF21ebgZi4moZr +YjqdQl+R+XN1YjqnOn39njyeKQKBgQC2L5F90HBBhp+1qU1hbEGof5oMkp+Thjx/ +PWbJsqt6s41yIemug3MP8QLlUOMG5X+QTCN4RNTmf60V5McF2TSuT8jbT8jby0w+ +ClnZ9lgGMSL7UjI26CHkw36xcDH2hzgXWGRxzttzlXttJqnbeATHC811yWaLDXGU +ecC7bG9/wwKBgQChgq8fgtdjv2BjObebtja6V1sJU7o14EpvcBPg30Ee65+xOIqR +66n/dGz7CjJno7TI9n4z4vwPdrtrZL9ftA5JfQqsDnW9+/zsa9qBlLBWt/xhXleZ +nJ4Vz40j0datfP0SdK3pRSUFhnTopY63VVRwGp/hTBlASQmDB4yZt7TW+QKBgF73 +eM2ug3WEqWfWcsGf3rHoofJ/07LgvFRPO29UNVLmmYqu5tLTLn1W0n2apl0H8HDV +X3/n0Vq9nwnUkXIZAP8EE91OP5Ni68FDQAcABG5l2qhK9mXspw5KYZY4t7KcVb7F +ksZIX9hmSUpiZxRCAauIGXeWnl9JiLUuqiqIoa5lAoGAMS2HObavBhdTPkN998lq +fnInmg5M466+PiPYZuQZh+Ea+8Gs0wv2wpFXw7Ds7hjo0hmiYGV/yw8etn45lpfA +buAIy50HQABwOWxH74AzddemsUSFEnRO4VgQ2Cu2dDaDGRjZbXcggRtOZ+ynuUJi +G9/7qOuw5oPSq0v7tEDbqpI= -----END PRIVATE KEY----- From bb508cd3f2a5741d9fe088737458de09e2e36def Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 15 Jun 2023 03:17:06 -0400 Subject: [PATCH 18/21] lint --- lib/signed-xml.js | 6 ++++-- test/hmac-tests.js | 8 ++++---- test/signature-unit-tests.js | 11 +++++++---- 3 files changed, 15 insertions(+), 10 deletions(-) diff --git a/lib/signed-xml.js b/lib/signed-xml.js index 495a6c60..550f90b9 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -365,7 +365,7 @@ SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = { return null; } - prefix = prefix ? prefix + ":" : ''; + prefix = prefix ? prefix + ":" : ""; let x509Certs = ""; if (Buffer.isBuffer(publicCert)) { @@ -377,7 +377,9 @@ SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = { } if (Array.isArray(publicCert)) { - x509Certs = publicCert.map((c) => `${utils.pemToDer(c)}`).join(""); + x509Certs = publicCert + .map((c) => `${utils.pemToDer(c)}`) + .join(""); } return `<${prefix}X509Data>${x509Certs}`; diff --git a/test/hmac-tests.js b/test/hmac-tests.js index 7a2270ce..8afff57b 100644 --- a/test/hmac-tests.js +++ b/test/hmac-tests.js @@ -6,7 +6,7 @@ const { sign } = require("crypto"); var expect = require("chai").expect; describe("HMAC tests", function () { - it("test validating HMAC signature", function () { + it("test validating HMAC signature", function () { var xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8"); var doc = new xmldom.DOMParser().parseFromString(xml); var signature = xpath.select( @@ -14,7 +14,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.enableHMAC() + sig.enableHMAC(); sig.signingCert = fs.readFileSync("./test/static/hmac.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -30,7 +30,7 @@ describe("HMAC tests", function () { doc )[0]; var sig = new crypto.SignedXml(); - sig.enableHMAC() + sig.enableHMAC(); sig.signingCert = fs.readFileSync("./test/static/hmac-foobar.key"); sig.loadSignature(signature); var result = sig.checkSignature(xml); @@ -41,7 +41,7 @@ describe("HMAC tests", function () { it("test create and validate HMAC signature", function () { var xml = "" + "" + "Harry Potter" + "" + ""; var sig = new crypto.SignedXml(); - sig.enableHMAC() + sig.enableHMAC(); sig.signingKey = fs.readFileSync("./test/static/hmac.key"); sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; sig.addReference("//*[local-name(.)='book']"); diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index d9c88158..2e8f8faf 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1011,7 +1011,7 @@ describe("Signature unit tests", function () { it("adds all certificates and does not add private keys to KeyInfo element", function () { var xml = ""; var sig = new SignedXml(); - var pemBuffer = fs.readFileSync("./test/static/client_bundle.pem") + var pemBuffer = fs.readFileSync("./test/static/client_bundle.pem"); sig.signingKey = pemBuffer; sig.signingCert = pemBuffer; sig.computeSignature(xml); @@ -1032,8 +1032,11 @@ describe("Signature unit tests", function () { expect(trimmedTextContent1, "Empty certificate added [0]").to.not.be.empty; expect(trimmedTextContent2, "Empty certificate added [1]").to.not.be.empty; - expect(trimmedTextContent1.substring(0,5), "Incorrect value for X509Certificate[0]").to.equal("MIIDC"); - expect(trimmedTextContent2.substring(0,5), "Incorrect value for X509Certificate[1]").to.equal("MIIDZ"); - + expect(trimmedTextContent1.substring(0, 5), "Incorrect value for X509Certificate[0]").to.equal( + "MIIDC" + ); + expect(trimmedTextContent2.substring(0, 5), "Incorrect value for X509Certificate[1]").to.equal( + "MIIDZ" + ); }); }); From abcf25e4896601432b287219f2d9bceb23c222c3 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 15 Jun 2023 03:43:03 -0400 Subject: [PATCH 19/21] lint --- example/example.js | 8 ++++---- lib/signed-xml.js | 14 +++++++------- test/signature-unit-tests.js | 10 +++++----- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/example/example.js b/example/example.js index e693894b..4b9ae927 100644 --- a/example/example.js +++ b/example/example.js @@ -1,9 +1,9 @@ /* eslint-disable no-console */ -var select = require("xml-crypto").xpath, - dom = require("@xmldom/xmldom").DOMParser, - SignedXml = require("xml-crypto").SignedXml, - fs = require("fs"); +var select = require("xml-crypto").xpath; +var dom = require("@xmldom/xmldom").DOMParser; +var SignedXml = require("xml-crypto").SignedXml; +var fs = require("fs"); function signXml(xml, xpath, key, dest) { var sig = new SignedXml(); diff --git a/lib/signed-xml.js b/lib/signed-xml.js index d64dc8b6..82fa542d 100644 --- a/lib/signed-xml.js +++ b/lib/signed-xml.js @@ -1,10 +1,10 @@ -var xpath = require("xpath"), - Dom = require("@xmldom/xmldom").DOMParser, - utils = require("./utils"), - c14n = require("./c14n-canonicalization"), - execC14n = require("./exclusive-canonicalization"), - EnvelopedSignature = require("./enveloped-signature").EnvelopedSignature, - crypto = require("crypto"); +var xpath = require("xpath"); +var Dom = require("@xmldom/xmldom").DOMParser; +var utils = require("./utils"); +var c14n = require("./c14n-canonicalization"); +var execC14n = require("./exclusive-canonicalization"); +var EnvelopedSignature = require("./enveloped-signature").EnvelopedSignature; +var crypto = require("crypto"); /** * Hash algorithm implementation diff --git a/test/signature-unit-tests.js b/test/signature-unit-tests.js index 2e8f8faf..8880d133 100644 --- a/test/signature-unit-tests.js +++ b/test/signature-unit-tests.js @@ -1,8 +1,8 @@ -var select = require("xpath").select, - dom = require("@xmldom/xmldom").DOMParser, - SignedXml = require("../lib/signed-xml.js").SignedXml, - fs = require("fs"), - crypto = require("crypto"); +var select = require("xpath").select; +var dom = require("@xmldom/xmldom").DOMParser; +var SignedXml = require("../lib/signed-xml.js").SignedXml; +var fs = require("fs"); +var crypto = require("crypto"); var expect = require("chai").expect; describe("Signature unit tests", function () { From 599ca5a24a69c4710d0ba125ffd278f466e7aef4 Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 15 Jun 2023 17:46:17 -0400 Subject: [PATCH 20/21] Update index.d.ts Co-authored-by: shunkica --- index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/index.d.ts b/index.d.ts index ec218c12..55a32c63 100644 --- a/index.d.ts +++ b/index.d.ts @@ -359,7 +359,7 @@ export interface Utils { * should not cause any issues in context of PKIX, PKCS and CMS. */ PEM_FORMAT_REGEX: RegExp; - MULTI_PEM_SPLIT: RegExp; + EXTRACT_X509_CERTS: RegExp; BASE64_REGEX: RegExp; } From aa2f62f23bac9c8622fee6958b2b1af000088a0b Mon Sep 17 00:00:00 2001 From: Chris Barth Date: Thu, 15 Jun 2023 07:54:44 -0400 Subject: [PATCH 21/21] remove static --- index.d.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.d.ts b/index.d.ts index 55a32c63..32861375 100644 --- a/index.d.ts +++ b/index.d.ts @@ -141,13 +141,13 @@ export interface GetKeyInfoContentArgs { export class SignedXml { // To add a new transformation algorithm create a new class that implements the {@link TransformationAlgorithm} interface, and register it here. More info: {@link https://github.com/node-saml/xml-crypto#customizing-algorithms|Customizing Algorithms} - static CanonicalizationAlgorithms: { + CanonicalizationAlgorithms: { [uri in TransformAlgorithmType]: new () => TransformAlgorithm; }; // To add a new hash algorithm create a new class that implements the {@link HashAlgorithm} interface, and register it here. More info: {@link https://github.com/node-saml/xml-crypto#customizing-algorithms|Customizing Algorithms} - static HashAlgorithms: { [uri in HashAlgorithmType]: new () => HashAlgorithm }; + HashAlgorithms: { [uri in HashAlgorithmType]: new () => HashAlgorithm }; // To add a new signature algorithm create a new class that implements the {@link SignatureAlgorithm} interface, and register it here. More info: {@link https://github.com/node-saml/xml-crypto#customizing-algorithms|Customizing Algorithms} - static SignatureAlgorithms: { [uri in SignatureAlgorithmType]: new () => SignatureAlgorithm }; + SignatureAlgorithms: { [uri in SignatureAlgorithmType]: new () => SignatureAlgorithm }; // Rules used to convert an XML document into its canonical form. canonicalizationAlgorithm: TransformAlgorithmType; // It specifies a list of namespace prefixes that should be considered "inclusive" during the canonicalization process.