VYPR
Critical severity10.0NVD Advisory· Published May 2, 2024· Updated Apr 15, 2026

CVE-2024-32962

CVE-2024-32962

Description

xml-crypto is an xml digital signature and encryption library for Node.js. In affected versions the default configuration does not check authorization of the signer, it only checks the validity of the signature per section 3.2.2 of the w3 xmldsig-core-20080610 spec. As such, without additional validation steps, the default configuration allows a malicious actor to re-sign an XML document, place the certificate in a <KeyInfo /> element, and pass xml-crypto default validation checks. As a result xml-crypto trusts by default any certificate provided via digitally signed XML document's <KeyInfo />. xml-crypto prefers to use any certificate provided via digitally signed XML document's <KeyInfo /> even if library was configured to use specific certificate (publicCert) for signature verification purposes. An attacker can spoof signature verification by modifying XML document and replacing existing signature with signature generated with malicious private key (created by attacker) and by attaching that private key's certificate to <KeyInfo /> element. This vulnerability is combination of changes introduced to 4.0.0 on pull request 301 / commit c2b83f98 and has been addressed in version 6.0.0 with pull request 445 / commit 21201723d. Users are advised to upgrade. Users unable to upgrade may either check the certificate extracted via getCertFromKeyInfo against trusted certificates before accepting the results of the validation or set xml-crypto's getCertFromKeyInfo to () => undefined forcing xml-crypto to use an explicitly configured publicCert or privateKey for signature verification.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
xml-cryptonpm
>= 4.0.0, < 6.0.06.0.0

Patches

2
21201723d2ca

Set `getCertFromKeyInfo` to `noop` (#445)

https://github.com/node-saml/xml-cryptoChris BarthJan 26, 2024via ghsa
4 files changed · +5 5
  • src/signed-xml.ts+2 2 modified
    @@ -149,8 +149,8 @@ export class SignedXml {
         }
         this.implicitTransforms = implicitTransforms ?? this.implicitTransforms;
         this.keyInfoAttributes = keyInfoAttributes ?? this.keyInfoAttributes;
    -    this.getKeyInfoContent = getKeyInfoContent ?? SignedXml.noop;
    -    this.getCertFromKeyInfo = getCertFromKeyInfo ?? this.getCertFromKeyInfo;
    +    this.getKeyInfoContent = getKeyInfoContent ?? this.getKeyInfoContent;
    +    this.getCertFromKeyInfo = getCertFromKeyInfo ?? SignedXml.noop;
         this.CanonicalizationAlgorithms;
         this.HashAlgorithms;
         this.SignatureAlgorithms;
    
  • test/document-tests.spec.ts+3 0 modified
    @@ -47,6 +47,7 @@ describe("Validated node references tests", function () {
         const xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8");
         const doc = new xmldom.DOMParser().parseFromString(xml);
         const sig = new SignedXml();
    +    sig.getCertFromKeyInfo = SignedXml.getCertFromKeyInfo;
         sig.loadSignature(sig.findSignatures(doc)[0]);
         const validSignature = sig.checkSignature(xml);
         expect(validSignature).to.be.true;
    @@ -73,6 +74,7 @@ describe("Validated node references tests", function () {
         const xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8");
         const doc = new xmldom.DOMParser().parseFromString(xml);
         const sig = new SignedXml();
    +    sig.getCertFromKeyInfo = SignedXml.getCertFromKeyInfo;
         sig.loadSignature(sig.findSignatures(doc)[0]);
         const validSignature = sig.checkSignature(xml);
         expect(validSignature).to.be.true;
    @@ -86,6 +88,7 @@ describe("Validated node references tests", function () {
         const xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8");
         const doc = new xmldom.DOMParser().parseFromString(xml);
         const sig = new SignedXml();
    +    sig.getCertFromKeyInfo = SignedXml.getCertFromKeyInfo;
         sig.loadSignature(sig.findSignatures(doc)[0]);
         const validSignature = sig.checkSignature(xml);
         expect(validSignature).to.be.true;
    
  • test/key-info-tests.spec.ts+0 1 modified
    @@ -13,7 +13,6 @@ describe("KeyInfo tests", function () {
         sig.publicCert = fs.readFileSync("./test/static/client_public.pem");
         sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
         sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
    -    sig.getKeyInfoContent = SignedXml.getKeyInfoContent;
         sig.computeSignature(xml);
         const signedXml = sig.getSignedXml();
         const doc = new xmldom.DOMParser().parseFromString(signedXml);
    
  • test/signature-unit-tests.spec.ts+0 2 modified
    @@ -534,7 +534,6 @@ describe("Signature unit tests", function () {
         sig.signatureAlgorithm = "http://dummySignatureAlgorithm";
         sig.canonicalizationAlgorithm = "http://DummyCanonicalization";
         sig.privateKey = "";
    -    sig.getKeyInfoContent = SignedXml.getKeyInfoContent;
     
         sig.addReference({
           xpath: "//*[local-name(.)='x']",
    @@ -1237,7 +1236,6 @@ describe("Signature unit tests", function () {
         sig.publicCert = pemBuffer;
         sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#";
         sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1";
    -    sig.getKeyInfoContent = SignedXml.getKeyInfoContent;
         sig.computeSignature(xml);
         const signedXml = sig.getSignedXml();
     
    
c2b83f984049

Add support for <X509Certificate /> in <KeyInfo />; remove `KeyInfoProvider` (#301)

https://github.com/node-saml/xml-cryptoChris BarthJun 17, 2023via ghsa
20 files changed · +436 323
  • .eslintrc.json+1 1 modified
    @@ -6,7 +6,7 @@
       },
       "root": true,
       "parserOptions": {
    -    "ecmaVersion": 6
    +    "ecmaVersion": 2020
       },
       "extends": ["eslint:recommended", "prettier"],
       "rules": {
    
  • example/example.js+1 2 modified
    @@ -3,7 +3,6 @@
     const select = require("xml-crypto").xpath;
     const dom = require("@xmldom/xmldom").DOMParser;
     const SignedXml = require("xml-crypto").SignedXml;
    -const FileKeyInfo = require("xml-crypto").FileKeyInfo;
     const fs = require("fs");
     
     function signXml(xml, xpath, key, dest) {
    @@ -21,7 +20,7 @@ function validateXml(xml, key) {
         doc
       )[0];
       const sig = new SignedXml();
    -  sig.keyInfoProvider = new FileKeyInfo(key);
    +  sig.signingCert = key;
       sig.loadSignature(signature.toString());
       const res = sig.checkSignature(xml);
       if (!res) {
    
  • index.d.ts+60 86 modified
    @@ -129,15 +129,25 @@ 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 GetKeyInfoContentArgs {
    +  cert: string | string[] | Buffer;
    +  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: {
    +  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.
    @@ -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 "<foo:X509Data></foo:X509Data>"
        *
    -   * @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;
    +  getKeyInfoContent(args: GetKeyInfoContentArgs): 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. "<X509Data></X509Data>"). 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 "<foo:X509Data></foo:X509Data>"
    +   * 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. "<X509Data></X509Data>"). 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 "<foo:X509Data></foo:X509Data>"
    +   * 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;
    +  EXTRACT_X509_CERTS: RegExp;
    +  BASE64_REGEX: RegExp;
     }
     
     /**
    
  • lib/file-key-info.js+0 17 removed
    @@ -1,17 +0,0 @@
    -const StringKeyInfo = require("./string-key-info");
    -const fs = require("fs");
    -
    -/**
    - * A key info provider implementation
    - *
    - * @param {string} file path to public certificate
    - */
    -function FileKeyInfo(file) {
    -  const key = fs.readFileSync(file);
    -  StringKeyInfo.apply(this, [key]);
    -}
    -
    -FileKeyInfo.prototype = StringKeyInfo.prototype;
    -FileKeyInfo.prototype.constructor = FileKeyInfo;
    -
    -module.exports = FileKeyInfo;
    
  • lib/signed-xml.js+61 44 modified
    @@ -4,8 +4,6 @@ const utils = require("./utils");
     const c14n = require("./c14n-canonicalization");
     const execC14n = require("./exclusive-canonicalization");
     const EnvelopedSignature = require("./enveloped-signature").EnvelopedSignature;
    -const StringKeyInfo = require("./string-key-info");
    -const FileKeyInfo = require("./file-key-info");
     const crypto = require("crypto");
     
     /**
    @@ -317,7 +315,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 || "";
    @@ -333,9 +330,12 @@ function SignedXml(idMode, options) {
         this.idAttributes.splice(0, 0, this.options.idAttribute);
       }
       this.implicitTransforms = this.options.implicitTransforms || [];
    +  this.getKeyInfoContent = SignedXml.getKeyInfoContent;
    +  this.getCertFromKeyInfo = SignedXml.getCertFromKeyInfo;
    +  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,
    @@ -345,13 +345,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,
    @@ -364,10 +364,11 @@ SignedXml.SignatureAlgorithms = {
      * and digital signature algos enabled at the same time.
      * This enables HMAC and disables other signing algos.
      */
    -SignedXml.enableHMAC = function () {
    -  SignedXml.SignatureAlgorithms = {
    +SignedXml.prototype.enableHMAC = function () {
    +  this.SignatureAlgorithms = {
         "http://www.w3.org/2000/09/xmldsig#hmac-sha1": HMACSHA1,
       };
    +  this.getKeyInfoContent = () => null;
     };
     
     SignedXml.defaultNsForPrefix = {
    @@ -376,35 +377,50 @@ SignedXml.defaultNsForPrefix = {
     
     SignedXml.findAncestorNs = findAncestorNs;
     
    -SignedXml.prototype.checkSignature = function (xml, callback) {
    -  if (callback != null && typeof callback !== "function") {
    -    throw new Error("Last parameter must be a callback function");
    +SignedXml.getKeyInfoContent = function ({ publicCert = null, prefix = null } = {}) {
    +  if (publicCert == null) {
    +    return null;
       }
     
    -  this.validationErrors = [];
    -  this.signedXml = xml;
    +  prefix = prefix ? prefix + ":" : "";
     
    -  if (!this.keyInfoProvider) {
    -    const err = new Error("cannot validate signature since no key info resolver was provided");
    -    if (!callback) {
    -      throw err;
    -    } else {
    -      callback(err);
    -      return;
    -    }
    +  let x509Certs = "";
    +  if (Buffer.isBuffer(publicCert)) {
    +    publicCert = publicCert.toString("latin1");
       }
     
    -  this.signingKey = this.keyInfoProvider.getKey(this.keyInfo);
    -  if (!this.signingKey) {
    -    const err2 = new Error("key info provider could not resolve key info " + this.keyInfo);
    -    if (!callback) {
    -      throw err2;
    -    } else {
    -      callback(err2);
    -      return;
    +  if (typeof publicCert === "string") {
    +    publicCert = publicCert.match(utils.EXTRACT_X509_CERTS);
    +  }
    +
    +  if (Array.isArray(publicCert)) {
    +    x509Certs = publicCert
    +      .map((c) => `<X509Certificate>${utils.pemToDer(c)}</X509Certificate>`)
    +      .join("");
    +  }
    +
    +  return `<${prefix}X509Data>${x509Certs}</${prefix}X509Data>`;
    +};
    +
    +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");
    +  }
    +
    +  this.validationErrors = [];
    +  this.signedXml = xml;
    +
       const doc = new Dom().parseFromString(xml);
     
       if (!this.validateReferences(doc)) {
    @@ -488,7 +504,7 @@ SignedXml.prototype.validateSignatureValue = function (doc, callback) {
       const signer = this.findSignatureAlgorithm(this.signatureAlgorithm);
       const res = signer.verifySignature(
         signedInfoCanon,
    -    this.signingKey,
    +    this.getCertFromKeyInfo(this.keyInfo) || this.signingCert || this.signingKey,
         this.signatureValue,
         callback
       );
    @@ -507,7 +523,7 @@ SignedXml.prototype.calculateSignatureValue = function (doc, callback) {
     };
     
     SignedXml.prototype.findSignatureAlgorithm = function (name) {
    -  const algo = SignedXml.SignatureAlgorithms[name];
    +  const algo = this.SignatureAlgorithms[name];
       if (algo) {
         return new algo();
       } else {
    @@ -516,7 +532,7 @@ SignedXml.prototype.findSignatureAlgorithm = function (name) {
     };
     
     SignedXml.prototype.findCanonicalizationAlgorithm = function (name) {
    -  const algo = SignedXml.CanonicalizationAlgorithms[name];
    +  const algo = this.CanonicalizationAlgorithms[name];
       if (algo) {
         return new algo();
       } else {
    @@ -525,7 +541,7 @@ SignedXml.prototype.findCanonicalizationAlgorithm = function (name) {
     };
     
     SignedXml.prototype.findHashAlgorithm = function (name) {
    -  const algo = SignedXml.HashAlgorithms[name];
    +  const algo = this.HashAlgorithms[name];
       if (algo) {
         return new algo();
       } else {
    @@ -932,18 +948,21 @@ SignedXml.prototype.getKeyInfo = function (prefix) {
       currentPrefix = prefix || "";
       currentPrefix = currentPrefix ? currentPrefix + ":" : currentPrefix;
     
    -  if (this.keyInfoProvider) {
    -    let keyInfoAttrs = "";
    -    if (this.keyInfoProvider.attrs) {
    -      Object.keys(this.keyInfoProvider.attrs).forEach((name) => {
    -        keyInfoAttrs += " " + name + '="' + this.keyInfoProvider.attrs[name] + '"';
    -      });
    -    }
    +  let keyInfoAttrs = "";
    +  if (this.keyInfoAttributes) {
    +    Object.keys(this.keyInfoAttributes).forEach((name) => {
    +      keyInfoAttrs += " " + name + '="' + this.keyInfoAttributes[name] + '"';
    +    });
    +  }
    +  const keyInfoContent = this.getKeyInfoContent({ publicCert: this.signingCert, prefix });
    +  if (keyInfoAttrs !== "" || keyInfoContent != null) {
         res += "<" + currentPrefix + "KeyInfo" + keyInfoAttrs + ">";
    -    res += this.keyInfoProvider.getKeyInfo(this.signingCert || this.signingKey, prefix);
    +    res += keyInfoContent;
         res += "</" + currentPrefix + "KeyInfo>";
    +    return res;
    +  } else {
    +    return "";
       }
    -  return res;
     };
     
     /**
    @@ -1190,5 +1209,3 @@ SignedXml.prototype.getSignedXml = function () {
     };
     
     exports.SignedXml = SignedXml;
    -exports.StringKeyInfo = StringKeyInfo;
    -exports.FileKeyInfo = FileKeyInfo;
    
  • lib/string-key-info.js+0 40 removed
    @@ -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. "<X509Data></X509Data>"). 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 "<foo:X509Data></foo:X509Data>"
    - *
    - * @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></" + 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;
    
  • lib/utils.js+51 0 modified
    @@ -79,8 +79,59 @@ function encodeSpecialCharactersInText(text) {
       });
     }
     
    +const EXTRACT_X509_CERTS = new RegExp(
    +  "-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----",
    +  "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 `${(
    +    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.EXTRACT_X509_CERTS = EXTRACT_X509_CERTS;
    +exports.PEM_FORMAT_REGEX = PEM_FORMAT_REGEX;
    +exports.BASE64_REGEX = BASE64_REGEX;
    +exports.pemToDer = pemToDer;
    +exports.derToPem = derToPem;
    +exports.normalizePem = normalizePem;
    
  • README.md+15 32 modified
    @@ -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 `<X509Data></X509Data>` 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 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).
     
     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:
     
    @@ -133,7 +134,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();
    @@ -144,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);
    @@ -179,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);
     ```
    @@ -232,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.
    @@ -253,24 +245,15 @@ 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.
    +To determine the inclusion and contents of a `<KeyInfo />` 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 `<KeyInfo />` element, it won't be included in the
    +generated XML.
     
    -```javascript
    -function MyKeyInfo() {
    -  this.getKeyInfo = function (key, prefix) {
    -    prefix = prefix || "";
    -    prefix = prefix ? prefix + ":" : prefix;
    -    return "<" + prefix + "X509Data></" + 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");
    -  };
    -}
    -```
    +To specify custom attributes on `<KeyInfo />`, 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 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() {
    @@ -284,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() {
    @@ -350,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']",
    @@ -370,7 +353,7 @@ var xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>";
     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
     
    
  • test/document-test.js+2 2 modified
    @@ -17,7 +17,7 @@ describe("Document tests", function () {
             .toString()
         );
         const 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);
         const result = sig.checkSignature(xml);
     
    @@ -37,7 +37,7 @@ describe("Document tests", function () {
         );
         const sig = new crypto.SignedXml();
         const feidePublicCert = fs.readFileSync("./test/static/feide_public.pem");
    -    sig.keyInfoProvider = new crypto.StringKeyInfo(feidePublicCert);
    +    sig.signingCert = feidePublicCert;
         sig.loadSignature(signature);
         const result = sig.checkSignature(xml);
     
    
  • test/hmac-tests.js+8 14 modified
    @@ -2,20 +2,10 @@ const crypto = require("../index");
     const xpath = require("xpath");
     const xmldom = require("@xmldom/xmldom");
     const fs = require("fs");
    +const { sign } = require("crypto");
     const expect = require("chai").expect;
     
    -let 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 () {
         const xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8");
         const doc = new xmldom.DOMParser().parseFromString(xml);
    @@ -24,7 +14,8 @@ describe("HMAC tests", function () {
           doc
         )[0];
         const sig = new crypto.SignedXml();
    -    sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key");
    +    sig.enableHMAC();
    +    sig.signingCert = fs.readFileSync("./test/static/hmac.key");
         sig.loadSignature(signature);
         const result = sig.checkSignature(xml);
     
    @@ -39,7 +30,8 @@ describe("HMAC tests", function () {
           doc
         )[0];
         const sig = new crypto.SignedXml();
    -    sig.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac-foobar.key");
    +    sig.enableHMAC();
    +    sig.signingCert = fs.readFileSync("./test/static/hmac-foobar.key");
         sig.loadSignature(signature);
         const result = sig.checkSignature(xml);
     
    @@ -49,6 +41,7 @@ describe("HMAC tests", function () {
       it("test create and validate HMAC signature", function () {
         const xml = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
         const 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,7 +53,8 @@ describe("HMAC tests", function () {
           doc
         )[0];
         const verify = new crypto.SignedXml();
    -    verify.keyInfoProvider = new crypto.FileKeyInfo("./test/static/hmac.key");
    +    verify.enableHMAC();
    +    verify.signingCert = fs.readFileSync("./test/static/hmac.key");
         verify.loadSignature(signature);
         const result = verify.checkSignature(sig.getSignedXml());
     
    
  • test/key-info-tests.js+38 0 added
    @@ -0,0 +1,38 @@
    +const select = require("xpath").select;
    +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 () {
    +  it("adds X509Certificate element during signature", function () {
    +    const xml = "<root><x /></root>";
    +    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 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 = "<library>" + "<book>" + "<name>Harry Potter</name>" + "</book>" + "</library>";
    +    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;
    +  });
    +});
    
  • test/saml-response-test.js+5 5 modified
    @@ -13,7 +13,7 @@ describe("SAML response tests", function () {
           doc
         )[0];
         const 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);
         const result = sig.checkSignature(xml);
     
    @@ -29,7 +29,7 @@ describe("SAML response tests", function () {
           assertion
         )[0];
         const 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];
         const 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);
         const result = sig.checkSignature(xml);
         expect(result).to.be.true;
    @@ -61,7 +61,7 @@ describe("SAML response tests", function () {
           assertion
         )[0];
         const 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];
         const 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);
         const result = sig.checkSignature(xml);
         // This doesn't matter, just want to make sure that we don't fail due to unknown algorithm
    
  • test/signature-integration-tests.js+5 11 modified
    @@ -87,7 +87,7 @@ describe("Signature integration tests", function () {
           "</Signature>";
     
         const 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);
         const result = sig.checkSignature(xml);
     
    @@ -111,7 +111,7 @@ describe("Signature integration tests", function () {
           doc
         )[0];
         const 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);
         const result = sig.checkSignature(xml);
     
    @@ -128,9 +128,7 @@ describe("Signature integration tests", function () {
           doc
         )[0];
         const 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);
         const result = sig.checkSignature(xml);
     
    @@ -150,9 +148,7 @@ describe("Signature integration tests", function () {
           doc
         )[0];
         const 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);
         const result = sig.checkSignature(xml);
     
    @@ -172,9 +168,7 @@ describe("Signature integration tests", function () {
           doc
         )[0];
         const 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);
         const result = sig.checkSignature(xml);
     
    
  • test/signature-unit-tests.js+77 54 modified
    @@ -1,7 +1,6 @@
     const select = require("xpath").select;
     const dom = require("@xmldom/xmldom").DOMParser;
     const SignedXml = require("../lib/signed-xml.js").SignedXml;
    -const FileKeyInfo = require("../lib/signed-xml.js").FileKeyInfo;
     const fs = require("fs");
     const crypto = require("crypto");
     const expect = require("chai").expect;
    @@ -15,11 +14,15 @@ describe("Signature unit tests", function () {
         )[0];
     
         const 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);
    -    const res = sig.checkSignature(xml);
    +    try {
    +      const res = sig.checkSignature(xml);
     
    -    return res;
    +      return res;
    +    } catch (e) {
    +      return false;
    +    }
       }
     
       function passValidSignature(file, mode) {
    @@ -319,12 +322,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";
    @@ -368,13 +365,15 @@ describe("Signature unit tests", function () {
         const xml = '<root><x xmlns="ns"></x><y attr="value"></y><z><w></w></z></root>';
         const 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.keyInfoProvider = new DummyKeyInfo();
    +    sig.getKeyInfoContent = function () {
    +      return "dummy key info";
    +    };
         sig.canonicalizationAlgorithm = "http://DummyCanonicalization";
     
         sig.addReference(
    @@ -477,12 +476,6 @@ describe("Signature unit tests", function () {
       it("signer creates signature with correct structure (with prefix)", function () {
         const prefix = "ds";
     
    -    function DummyKeyInfo() {
    -      this.getKeyInfo = function () {
    -        return "<ds:dummy>dummy key info</ds:dummy>";
    -      };
    -    }
    -
         function DummyDigest() {
           this.getHash = function () {
             return "dummy digest";
    @@ -526,13 +519,15 @@ describe("Signature unit tests", function () {
         const xml = '<root><x xmlns="ns"></x><y attr="value"></y><z><w></w></z></root>';
         const 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.keyInfoProvider = new DummyKeyInfo();
    +    sig.getKeyInfoContent = function () {
    +      return "<ds:dummy>dummy key info</ds:dummy>";
    +    };
         sig.canonicalizationAlgorithm = "http://DummyCanonicalization";
     
         sig.addReference(
    @@ -638,7 +633,7 @@ describe("Signature unit tests", function () {
           '<root><x xmlns="ns" Id="_0"></x><y attr="value" Id="_1"></y><z><w Id="_2"></w></z></root>';
         const 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']");
    @@ -696,11 +691,11 @@ describe("Signature unit tests", function () {
     
         const xml =
           '<root><x xmlns="ns" Id="_0"></x><y attr="value" Id="_1"></y><z><w Id="_2"></w></z></root>';
    -    SignedXml.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm;
         const sig = new SignedXml();
    +    sig.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm;
         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']");
    @@ -783,7 +778,7 @@ describe("Signature unit tests", function () {
         const xml = "<root><x /></root>";
         const sig = new SignedXml();
         sig.signingKey = fs.readFileSync("./test/static/client.pem");
    -    sig.keyInfoProvider = null;
    +    sig.signingCert = null;
     
         sig.addReference(
           "//*[local-name(.)='root']",
    @@ -823,17 +818,15 @@ describe("Signature unit tests", function () {
       });
     
       it("signer adds existing prefixes", function () {
    -    function AssertionKeyInfo(assertionId) {
    -      this.getKeyInfo = function () {
    -        return (
    -          '<wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" wsu:Id="0" ' +
    -          'xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"> ' +
    -          '<wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">' +
    -          assertionId +
    -          "</wsse:KeyIdentifier>" +
    -          "</wsse:SecurityTokenReference>"
    -        );
    -      };
    +    function getKeyInfoContentWithAssertionId({ assertionId }) {
    +      return (
    +        '<wsse:SecurityTokenReference wsse11:TokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1" wsu:Id="0" ' +
    +        'xmlns:wsse11="http://docs.oasis-open.org/wss/oasis-wss-wssecurity-secext-1.1.xsd"> ' +
    +        '<wsse:KeyIdentifier ValueType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.0#SAMLAssertionID">' +
    +        assertionId +
    +        "</wsse:KeyIdentifier>" +
    +        "</wsse:SecurityTokenReference>"
    +      );
         }
     
         const xml =
    @@ -848,7 +841,8 @@ describe("Signature unit tests", function () {
           "</SOAP-ENV:Envelope>";
     
         const sig = new SignedXml();
    -    sig.keyInfoProvider = new AssertionKeyInfo("_81d5fba5c807be9e9cf60c58566349b1");
    +    const assertionId = "_81d5fba5c807be9e9cf60c58566349b1";
    +    sig.getKeyInfoContent = getKeyInfoContentWithAssertionId.bind(this, { assertionId });
         sig.signingKey = fs.readFileSync("./test/static/client.pem");
         sig.computeSignature(xml, {
           prefix: "ds",
    @@ -864,13 +858,14 @@ describe("Signature unit tests", function () {
         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 () {
         const xml = "<root><x /></root>";
         const sig = new SignedXml();
         sig.signingKey = fs.readFileSync("./test/static/client.pem");
    -    sig.keyInfoProvider = null;
    +    sig.signingCert = null;
     
         sig.addReference(
           "//*[local-name(.)='root']",
    @@ -902,7 +897,7 @@ describe("Signature unit tests", function () {
         const xml = "<root><x /></root>";
         const sig = new SignedXml();
         sig.signingKey = fs.readFileSync("./test/static/client.pem");
    -    sig.keyInfoProvider = null;
    +    sig.signingCert = null;
     
         sig.addReference(
           "//*[local-name(.)='root']",
    @@ -929,7 +924,7 @@ describe("Signature unit tests", function () {
         const xml = "<root><x /></root>";
         const 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']",
    @@ -962,7 +957,7 @@ describe("Signature unit tests", function () {
         const xml = "<root><x /></root>";
         const 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']",
    @@ -989,15 +984,11 @@ describe("Signature unit tests", function () {
         const xml = "<root><x /></root>";
         const 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 "<dummy/>";
    -      },
    +    sig.keyInfoAttributes = {
    +      CustomUri: "http://www.example.com/keyinfo",
    +      CustomAttribute: "custom-value",
         };
    +    sig.getKeyInfoContent = () => "<dummy/>";
     
         sig.computeSignature(xml);
         const signedXml = sig.getSignedXml();
    @@ -1018,4 +1009,36 @@ describe("Signature unit tests", function () {
           "KeyInfo element should have the correct CustomAttribute attribute value"
         ).to.equal("custom-value");
       });
    +
    +  it("adds all certificates and does not add private keys to KeyInfo element", function () {
    +    const xml = "<root><x /></root>";
    +    const sig = new SignedXml();
    +    const pemBuffer = fs.readFileSync("./test/static/client_bundle.pem");
    +    sig.signingKey = pemBuffer;
    +    sig.signingCert = pemBuffer;
    +    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 exactly two certificates").to.equal(2);
    +
    +    const cert1 = x509certificates[0];
    +    const 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 trimmedTextContent1 = cert1.textContent.trim();
    +    const trimmedTextContent2 = cert2.textContent.trim();
    +    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"
    +    );
    +  });
     });
    
  • test/static/client_bundle.pem+68 0 added
    @@ -0,0 +1,68 @@
    +-----BEGIN CERTIFICATE-----
    +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-----
    +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-----
    
  • test/static/client_public.pem+12 12 modified
    @@ -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-----
    
  • test/utils-tests.js+29 0 added
    @@ -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();
    +    });
    +  });
    +});
    
  • test/validators/XmlCryptoUtilities/XmlCryptoUtilities/Program.cs+1 1 modified
    @@ -1,4 +1,4 @@
    -//
    +//
     // This example signs an XML file using an
     // envelope signature. It then verifies the 
     // signed XML.
    
  • test/validators/XmlCryptoUtilities/XmlCryptoUtilities/utilities.cs+1 1 modified
    @@ -1,4 +1,4 @@
    -using System;
    +using System;
     using System.Collections.Generic;
     using System.Linq;
     using System.Text;
    
  • test/wsfed-metadata-test.js+1 1 modified
    @@ -13,7 +13,7 @@ describe("WS-Fed Metadata tests", function () {
           doc
         )[0];
         const 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);
         const result = sig.checkSignature(xml);
     
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.