Repository: yaronn/xml-crypto Branch: master Commit: b673581ba070 Files: 118 Total size: 528.7 KB Directory structure: gitextract_8drkv3mt/ ├── .eslintignore ├── .eslintrc.json ├── .gitattributes ├── .github/ │ ├── dependabot.yml │ └── workflows/ │ ├── ci.yml │ └── codeql-analysis.yml ├── .gitignore ├── .grenrc.js ├── .markdownlint.json ├── .mocharc.json ├── .npmignore ├── .nycrc.json ├── .prettierignore ├── .prettierrc.json ├── .release-it.json ├── .vscode/ │ └── settings.json ├── CHANGELOG.md ├── LICENSE ├── README.md ├── example/ │ ├── client.pem │ ├── client_public.pem │ └── example.js ├── package.json ├── src/ │ ├── c14n-canonicalization.ts │ ├── enveloped-signature.ts │ ├── exclusive-canonicalization.ts │ ├── hash-algorithms.ts │ ├── index.ts │ ├── signature-algorithms.ts │ ├── signed-xml.ts │ ├── types.ts │ └── utils.ts ├── test/ │ ├── c14n-non-exclusive-unit-tests.spec.ts │ ├── c14nWithComments-unit-tests.spec.ts │ ├── canonicalization-unit-tests.spec.ts │ ├── document-tests.spec.ts │ ├── hmac-tests.spec.ts │ ├── key-info-tests.spec.ts │ ├── saml-response-tests.spec.ts │ ├── signature-integration-tests.spec.ts │ ├── signature-object-tests.spec.ts │ ├── signature-unit-tests.spec.ts │ ├── static/ │ │ ├── client.pem │ │ ├── client_bundle.pem │ │ ├── client_public.der │ │ ├── client_public.pem │ │ ├── empty_uri.pem │ │ ├── feide_public.pem │ │ ├── hmac-foobar.key │ │ ├── hmac.key │ │ ├── hmac_signature.xml │ │ ├── id_with_quotes.xml │ │ ├── idp_certificate.pem │ │ ├── idp_private_key.pem │ │ ├── integration/ │ │ │ ├── expectedVerify.xml │ │ │ └── expectedVerifyComplex.xml │ │ ├── invalid_saml_no_signed_info.xml │ │ ├── invalid_saml_sha256_rsa_mgf1.xml │ │ ├── invalid_signature - changed content.xml │ │ ├── invalid_signature - hash.xml │ │ ├── invalid_signature - non existing reference.xml │ │ ├── invalid_signature - signature value.xml │ │ ├── invalid_signature - wsu - changed content.xml │ │ ├── invalid_signature - wsu - hash.xml │ │ ├── invalid_signature - wsu - invalid signature value.xml │ │ ├── invalid_signature - wsu - non existing reference.xml │ │ ├── invalid_signature_without_transforms_element.xml │ │ ├── keyinfo - pretty-printed.xml │ │ ├── keyinfo.pem │ │ ├── saml_external_ns.pem │ │ ├── saml_external_ns.xml │ │ ├── saml_multiple_signed_info_nodes.xml │ │ ├── saml_wrapped_signed_info_node.xml │ │ ├── signature_with_inclusivenamespaces.pem │ │ ├── signature_with_inclusivenamespaces.xml │ │ ├── signature_with_inclusivenamespaces_lines.xml │ │ ├── signature_with_inclusivenamespaces_lines_windows.xml │ │ ├── unsigned_saml_response.xml │ │ ├── valid_saml.xml │ │ ├── valid_saml_sha256_rsa_mgf1.xml │ │ ├── valid_saml_signature_wrapping.xml │ │ ├── valid_saml_with_digest_comment.xml │ │ ├── valid_saml_withcomments.xml │ │ ├── valid_signature wsu.xml │ │ ├── valid_signature.xml │ │ ├── valid_signature_utf8.xml │ │ ├── valid_signature_with_lowercase_id_attribute.xml │ │ ├── valid_signature_with_reference_keyInfo.xml │ │ ├── valid_signature_with_root_level_sig_namespace.xml │ │ ├── valid_signature_with_unused_prefixes.xml │ │ ├── valid_signature_with_whitespace_in_digestvalue.xml │ │ ├── valid_signature_without_transforms_element.xml │ │ ├── windows_store_certificate.pem │ │ ├── windows_store_signature.xml │ │ ├── wsfederation_metadata.pem │ │ └── wsfederation_metadata.xml │ ├── utils-tests.spec.ts │ ├── validators/ │ │ ├── XmlCryptoJava/ │ │ │ ├── pom.xml │ │ │ └── src/ │ │ │ └── test/ │ │ │ ├── java/ │ │ │ │ └── org/ │ │ │ │ └── nodejs/ │ │ │ │ └── xmlcrypto/ │ │ │ │ └── HMACTest.java │ │ │ └── resources/ │ │ │ └── log4j.xml │ │ └── XmlCryptoUtilities/ │ │ ├── XmlCryptoUtilities/ │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── ValidateSignature.csproj │ │ │ ├── bin/ │ │ │ │ └── Debug/ │ │ │ │ ├── ClientPrivate.pfx │ │ │ │ ├── Example.xml │ │ │ │ ├── XmlCryptoUtilities.pdb │ │ │ │ ├── XmlCryptoUtilities.vshost.exe.manifest │ │ │ │ └── signedExample.xml │ │ │ ├── obj/ │ │ │ │ └── x86/ │ │ │ │ └── Debug/ │ │ │ │ ├── DesignTimeResolveAssemblyReferencesInput.cache │ │ │ │ ├── ValidateSignature.csproj.FileListAbsolute.txt │ │ │ │ └── XmlCryptoUtilities.pdb │ │ │ ├── program-repro-misc-validation-and-canon.cs │ │ │ └── utilities.cs │ │ ├── XmlCryptoUtilities.sln │ │ └── XmlCryptoUtilities.suo │ └── wsfed-metadata-tests.spec.ts ├── tsconfig.eslint.json └── tsconfig.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .eslintignore ================================================ ================================================ FILE: .eslintrc.json ================================================ { "env": { "browser": false, "node": true, "mocha": true, "es2020": true }, "root": true, "parser": "@typescript-eslint/parser", "plugins": ["@typescript-eslint", "deprecation"], "parserOptions": { "project": "./tsconfig.eslint.json", "ext": ".ts" }, "extends": [ "eslint:recommended", "plugin:@typescript-eslint/eslint-recommended", "plugin:@typescript-eslint/recommended", "prettier" ], "rules": { "no-console": "error", "no-prototype-builtins": "error", "one-var": ["error", "never"], "no-duplicate-imports": "error", "no-use-before-define": "error", "curly": "error", "eqeqeq": ["error", "smart"], "no-var": "error", "prefer-const": "error", "prefer-template": "error", "deprecation/deprecation": "error", "@typescript-eslint/no-non-null-assertion": "error", "@typescript-eslint/no-unused-vars": "error", "@typescript-eslint/no-this-alias": "error" } } ================================================ FILE: .gitattributes ================================================ # Git to autodetect text files and normalise their line endings to LF when they are checked into your repository. * text=auto # git won't try to convert this files *.pem -text *.der -text test/static/**/*.xml -text # These files are text and should be normalized (Convert crlf => lf) *.gitattributes text .gitignore text ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates version: 2 updates: - package-ecosystem: "npm" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "monthly" # Always increase the version requirement # to match the new version. versioning-strategy: increase - package-ecosystem: "github-actions" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "monthly" ================================================ FILE: .github/workflows/ci.yml ================================================ name: Test Status on: workflow_dispatch: push: branches: [master] pull_request: branches: [master] jobs: test: name: Test Code env: CI: true strategy: matrix: os: [ubuntu-latest] node-version: [16, 18, 20] experimental: [false] include: - os: ubuntu-latest node-version: latest experimental: true runs-on: ${{ matrix.os }} continue-on-error: ${{ matrix.experimental }} steps: - uses: actions/checkout@v4 - name: Use Node.js ${{ matrix.node-version }} on ${{ matrix.os }} uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - run: npm ci - run: npm test - run: npm update - run: npm ci - run: npm test - name: Codecov uses: codecov/codecov-action@v3.1.4 with: verbose: true lint: name: Lint Code env: CI: true runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: node-version: "lts/*" - run: | npm ci npm run lint ================================================ FILE: .github/workflows/codeql-analysis.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [master] pull_request: # The branches below must be a subset of the branches above branches: [master] schedule: - cron: "21 12 * * 1" jobs: analyze: name: Analyze runs-on: ubuntu-latest permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: ["javascript"] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # https://docs.github.com/en/free-pro-team@latest/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#changing-the-languages-that-are-analyzed steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines # and modify them (or add more) to build your code if your project # uses a compiled language #- run: | # make bootstrap # make release - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 ================================================ FILE: .gitignore ================================================ lib/ node_modules/ p.js p.txt *.svclog .DS_Store *.*~ *.swp npm-debug.log /test/validators/XmlCryptoJava/target/ .eslintcache .nyc_output/ coverage/ .idea ================================================ FILE: .grenrc.js ================================================ module.exports = { dataSource: "prs", prefix: "", onlyMilestones: false, ignoreTagsWith: [], ignoreLabels: [], tags: "all", groupBy: { "Major Changes": ["semver-major", "breaking-change"], "Minor Changes": ["semver-minor", "enhancement", "new-feature"], Dependencies: ["dependencies"], "Bug Fixes": ["semver-patch", "bug", "security"], Documentation: ["documentation"], "Technical Tasks": ["chore"], Other: ["..."], }, changelogFilename: "CHANGELOG.md", username: "node-saml", repo: "xml-crypto", template: { issue: function (placeholders) { const parts = [ "-", placeholders.labels, placeholders.name, `[${placeholders.text}](${placeholders.url})`, ]; return parts .filter((_) => _) .join(" ") .replace(" ", " "); }, release: function (placeholders) { placeholders.body = placeholders.body.replace( "*No changelog for this release.*", "\n_No changelog for this release._", ); return `## ${placeholders.release} (${placeholders.date})\n${placeholders.body}`; }, group: function (placeholders) { const iconMap = { Enhancements: "🚀", "Minor Changes": "🚀", "Bug Fixes": "🐛", Documentation: "📚", "Technical Tasks": "⚙️", "Major Changes": "💣", Dependencies: "🔗", }; const icon = iconMap[placeholders.heading] || "🙈"; return "\n### " + icon + " " + placeholders.heading + "\n"; }, }, }; ================================================ FILE: .markdownlint.json ================================================ { "MD013": false, "MD024": false } ================================================ FILE: .mocharc.json ================================================ { "diff": true, "extension": "spec.ts", "package": "./package.json", "recursive": true, "reporter": "spec", "require": ["choma", "ts-node/register"], "spec": "test/*.spec.ts", "watch-files": "test/*.spec.ts", "colors": true } ================================================ FILE: .npmignore ================================================ test/ example/ ================================================ FILE: .nycrc.json ================================================ { "extends": "@istanbuljs/nyc-config-typescript", "all": true, "check-coverage": false, "reporter": ["lcov", "text"], "include": ["src"] } ================================================ FILE: .prettierignore ================================================ # Ignore artifacts: lib node_modules package-lock.json .eslintcache .prettierignore test/static/* test/validators/* .nyc_output/* coverage/* ================================================ FILE: .prettierrc.json ================================================ { "printWidth": 100 } ================================================ FILE: .release-it.json ================================================ { "github": { "release": true, "releaseName": "v${version}" }, "hooks": { "after:bump": "npm run changelog" } } ================================================ FILE: .vscode/settings.json ================================================ { "cSpell.words": [ "Canonicalization", "canonicalize", "canonicalized", "codecov", "feide", "HMAC", "posteb", "preeb", "reserialization", "stricttextualmsg", "wsfederation", "wssecurity", "xades" ] } ================================================ FILE: CHANGELOG.md ================================================ # Changelog ## 6.0.0 (2024-01-26) ### 💣 Major Changes - [**breaking-change**] Set `getCertFromKeyInfo` to `noop` [#445](https://github.com/node-saml/xml-crypto/pull/445) ### 🔗 Dependencies - [**dependencies**] [**github_actions**] Bump github/codeql-action from 2 to 3 [#434](https://github.com/node-saml/xml-crypto/pull/434) ### 📚 Documentation - [**documentation**] Chore: Update README.md [#432](https://github.com/node-saml/xml-crypto/pull/432) --- ## v5.1.1 (2024-01-17) ### 🐛 Bug Fixes - [**bug**] fix: template literal [#443](https://github.com/node-saml/xml-crypto/pull/443) --- ## v5.1.0 (2024-01-07) ### 🚀 Minor Changes - [**enhancement**] Enhance derToPem to support XML pretty-print [#439](https://github.com/node-saml/xml-crypto/pull/439) ### 🔗 Dependencies - [**dependencies**] [**javascript**] Bump @typescript-eslint/parser from 6.13.0 to 6.18.1 [#442](https://github.com/node-saml/xml-crypto/pull/442) - [**dependencies**] [**javascript**] Bump @typescript-eslint/eslint-plugin from 6.13.0 to 6.18.1 [#441](https://github.com/node-saml/xml-crypto/pull/441) - [**dependencies**] [**javascript**] Bump follow-redirects from 1.15.3 to 1.15.4 [#440](https://github.com/node-saml/xml-crypto/pull/440) - [**dependencies**] [**javascript**] Bump eslint from 8.54.0 to 8.56.0 [#436](https://github.com/node-saml/xml-crypto/pull/436) - [**dependencies**] [**javascript**] Bump @types/node from 16.18.65 to 16.18.69 [#435](https://github.com/node-saml/xml-crypto/pull/435) - [**dependencies**] [**javascript**] Bump release-it from 16.2.1 to 16.3.0 [#428](https://github.com/node-saml/xml-crypto/pull/428) --- ## v5.0.0 (2023-11-27) ### 💣 Major Changes - [**breaking-change**] Mark `getKeyInfo()` private as it has no public consumers [#412](https://github.com/node-saml/xml-crypto/pull/412) - [**breaking-change**] Remove the default for `getKeyInfoContent` forcing a consumer to choose [#411](https://github.com/node-saml/xml-crypto/pull/411) - [**documentation**] [**breaking-change**] Remove default for transformation algorithm [#410](https://github.com/node-saml/xml-crypto/pull/410) - [**breaking-change**] Remove default for signature algorithm [#408](https://github.com/node-saml/xml-crypto/pull/408) - [**breaking-change**] Remove default for digest algorithm [#406](https://github.com/node-saml/xml-crypto/pull/406) - [**breaking-change**] Remove default canonicalization algorithm [#405](https://github.com/node-saml/xml-crypto/pull/405) - [**chore**] [**breaking-change**] Improve code clarity; remove unused functions [#397](https://github.com/node-saml/xml-crypto/pull/397) - [**breaking-change**] Move validation messages to each reference [#396](https://github.com/node-saml/xml-crypto/pull/396) - [**breaking-change**] Make references accessible only via get/set [#395](https://github.com/node-saml/xml-crypto/pull/395) - [**chore**] [**breaking-change**] Reduce public interface by making some methods private [#394](https://github.com/node-saml/xml-crypto/pull/394) - [**chore**] [**breaking-change**] Update build to support Node@16 [#385](https://github.com/node-saml/xml-crypto/pull/385) ### 🚀 Minor Changes - [**enhancement**] Add support for directly querying a node to see if it has passed validation [#389](https://github.com/node-saml/xml-crypto/pull/389) - [**enhancement**] Add method for checking if element is signed [#368](https://github.com/node-saml/xml-crypto/pull/368) ### 🔗 Dependencies - [**dependencies**] [**javascript**] Bump @typescript-eslint/eslint-plugin from 5.62.0 to 6.13.0 [#422](https://github.com/node-saml/xml-crypto/pull/422) - [**dependencies**] [**javascript**] Bump @prettier/plugin-xml from 3.2.1 to 3.2.2 [#423](https://github.com/node-saml/xml-crypto/pull/423) - [**dependencies**] [**javascript**] Bump @types/mocha from 10.0.2 to 10.0.6 [#421](https://github.com/node-saml/xml-crypto/pull/421) - [**dependencies**] [**javascript**] Bump @types/chai from 4.3.6 to 4.3.11 [#419](https://github.com/node-saml/xml-crypto/pull/419) - [**dependencies**] [**javascript**] Bump prettier from 3.0.3 to 3.1.0 [#418](https://github.com/node-saml/xml-crypto/pull/418) - [**dependencies**] [**javascript**] Bump typescript from 5.2.2 to 5.3.2 [#415](https://github.com/node-saml/xml-crypto/pull/415) - [**dependencies**] [**javascript**] Bump eslint from 8.51.0 to 8.54.0 [#414](https://github.com/node-saml/xml-crypto/pull/414) - [**dependencies**] [**github_actions**] Bump actions/setup-node from 3 to 4 [#413](https://github.com/node-saml/xml-crypto/pull/413) - [**dependencies**] [**javascript**] Bump @babel/traverse from 7.22.4 to 7.23.2 [#407](https://github.com/node-saml/xml-crypto/pull/407) - [**dependencies**] [**github_actions**] Bump actions/checkout from 3 to 4 [#392](https://github.com/node-saml/xml-crypto/pull/392) - [**dependencies**] [**javascript**] Bump eslint-plugin-deprecation from 1.4.1 to 2.0.0 [#390](https://github.com/node-saml/xml-crypto/pull/390) - [**dependencies**] [**javascript**] Bump typescript from 5.1.6 to 5.2.2 [#383](https://github.com/node-saml/xml-crypto/pull/383) - [**dependencies**] [**javascript**] Bump eslint-config-prettier from 8.8.0 to 9.0.0 [#381](https://github.com/node-saml/xml-crypto/pull/381) - [**dependencies**] Update dependencies; move to @xmldom-scoped is-dom-node package [#402](https://github.com/node-saml/xml-crypto/pull/402) ### 🐛 Bug Fixes - [**bug**] Ensure the X509Certificate tag is properly prefixed [#377](https://github.com/node-saml/xml-crypto/pull/377) - [**bug**] Fix transform processing regression [#379](https://github.com/node-saml/xml-crypto/pull/379) - [**bug**] Enforce consistent transform processing [#380](https://github.com/node-saml/xml-crypto/pull/380) ### 📚 Documentation - [**documentation**] Clarify use of in signature validation [#401](https://github.com/node-saml/xml-crypto/pull/401) ### ⚙️ Technical Tasks - [**chore**] Use is-dom-node for DOM node checking and narrowing [#388](https://github.com/node-saml/xml-crypto/pull/388) - [**chore**] Improve and simplify validation logic [#373](https://github.com/node-saml/xml-crypto/pull/373) - [**chore**] Add additional type checking [#369](https://github.com/node-saml/xml-crypto/pull/369) --- ## v4.1.0 (2023-07-28) ### 💣 Major Changes - [**bug**] [**breaking-change**] Fix `pemToDer()` return type [#364](https://github.com/node-saml/xml-crypto/pull/364) ### ⚙️ Technical Tasks - [**chore**] Improve exported typings [#367](https://github.com/node-saml/xml-crypto/pull/367) - [**chore**] Use stricter typing in tests [#366](https://github.com/node-saml/xml-crypto/pull/366) - [**chore**] Consistently reference `xmldom` [#365](https://github.com/node-saml/xml-crypto/pull/365) - [**chore**] Rename `findChilds()` to `findChildren()` [#363](https://github.com/node-saml/xml-crypto/pull/363) --- ## v4.0.1 (2023-07-22) ### 🐛 Bug Fixes - [**bug**] Use correct type for options [#360](https://github.com/node-saml/xml-crypto/pull/360) - [**bug**] Fix validationErrors type [#361](https://github.com/node-saml/xml-crypto/pull/361) --- ## v4.0.0 (2023-07-21) ### 💣 Major Changes - [**documentation**] [**breaking-change**] Expand the options, move idmode into options, fix types [#323](https://github.com/node-saml/xml-crypto/pull/323) - [**breaking-change**] Refactor classes into their own files [#318](https://github.com/node-saml/xml-crypto/pull/318) - [**breaking-change**] Prefer ES6 classes to prototypical inheritance [#316](https://github.com/node-saml/xml-crypto/pull/316) - [**breaking-change**] Rename `signingCert` -> `publicCert` and `signingKey` -> `privateKey` [#315](https://github.com/node-saml/xml-crypto/pull/315) - [**semver-major**] [**breaking-change**] Add support for in ; remove `KeyInfoProvider` [#301](https://github.com/node-saml/xml-crypto/pull/301) - [**semver-major**] Target an LTS version of Node [#299](https://github.com/node-saml/xml-crypto/pull/299) ### 🚀 Minor Changes - [**enhancement**] Exports C14nCanonicalization, ExclusiveCanonicalization [#336](https://github.com/node-saml/xml-crypto/pull/336) ### 🔗 Dependencies - [**dependencies**] [**javascript**] Bump @xmldom/xmldom from 0.8.8 to 0.8.10 [#358](https://github.com/node-saml/xml-crypto/pull/358) - [**dependencies**] [**javascript**] Bump @typescript-eslint/eslint-plugin from 5.60.1 to 5.62.0 [#357](https://github.com/node-saml/xml-crypto/pull/357) - [**dependencies**] [**javascript**] Bump @prettier/plugin-xml from 2.2.0 to 3.1.1 [#356](https://github.com/node-saml/xml-crypto/pull/356) - [**dependencies**] [**javascript**] Bump prettier from 2.8.8 to 3.0.0 [#350](https://github.com/node-saml/xml-crypto/pull/350) - [**dependencies**] [**javascript**] Bump release-it from 15.11.0 to 16.1.3 [#352](https://github.com/node-saml/xml-crypto/pull/352) - [**dependencies**] [**javascript**] Bump prettier-plugin-packagejson from 2.4.3 to 2.4.5 [#353](https://github.com/node-saml/xml-crypto/pull/353) - [**dependencies**] [**javascript**] Bump @typescript-eslint/parser from 5.60.1 to 5.62.0 [#354](https://github.com/node-saml/xml-crypto/pull/354) - [**dependencies**] [**javascript**] Bump typescript from 5.1.5 to 5.1.6 [#351](https://github.com/node-saml/xml-crypto/pull/351) - [**dependencies**] [**javascript**] Bump word-wrap from 1.2.3 to 1.2.4 [#348](https://github.com/node-saml/xml-crypto/pull/348) - [**dependencies**] [**javascript**] Bump eslint from 8.42.0 to 8.45.0 [#344](https://github.com/node-saml/xml-crypto/pull/344) - [**dependencies**] Update gren for better support for branches [#340](https://github.com/node-saml/xml-crypto/pull/340) - [**dependencies**] [**github_actions**] Bump codecov/codecov-action from 3.1.1 to 3.1.4 [#290](https://github.com/node-saml/xml-crypto/pull/290) ### 🐛 Bug Fixes - [**bug**] Fix issue in case when namespace has no prefix [#330](https://github.com/node-saml/xml-crypto/pull/330) - [**bug**] Use correct path for code coverage reports [#302](https://github.com/node-saml/xml-crypto/pull/302) ### ⚙️ Technical Tasks - [**chore**] Enforce eslint `no-unused-vars` [#349](https://github.com/node-saml/xml-crypto/pull/349) - [**chore**] Enforce eslint `deprecation` [#347](https://github.com/node-saml/xml-crypto/pull/347) - [**chore**] Enforce eslint `prefer-template` [#346](https://github.com/node-saml/xml-crypto/pull/346) - [**chore**] Enforce eslint `no-this-alias` [#345](https://github.com/node-saml/xml-crypto/pull/345) - [**chore**] Convert this project to TypeScript [#325](https://github.com/node-saml/xml-crypto/pull/325) - [**chore**] Rename files in preparation for TS migration [#343](https://github.com/node-saml/xml-crypto/pull/343) - [**chore**] Don't force `master` branch when generating changelog [#342](https://github.com/node-saml/xml-crypto/pull/342) - [**chore**] Enforce eslint `no-unused-vars` [#322](https://github.com/node-saml/xml-crypto/pull/322) - [**chore**] Enforce eslint `no-prototype-builtins` [#321](https://github.com/node-saml/xml-crypto/pull/321) - [**chore**] Enforce eslint `eqeqeq` rule [#320](https://github.com/node-saml/xml-crypto/pull/320) - [**chore**] Update types [#319](https://github.com/node-saml/xml-crypto/pull/319) - [**chore**] Adjust code to pass eslint `prefer-const` [#312](https://github.com/node-saml/xml-crypto/pull/312) - [**chore**] Adjust code to pass eslint `no-var` [#311](https://github.com/node-saml/xml-crypto/pull/311) - [**chore**] Adjust code to pass eslint `curly` [#310](https://github.com/node-saml/xml-crypto/pull/310) - [**chore**] Adjust code to pass eslint `one-var` [#309](https://github.com/node-saml/xml-crypto/pull/309) - [**chore**] Typings [#295](https://github.com/node-saml/xml-crypto/pull/295) - [**chore**] Lint code for new linting rules [#300](https://github.com/node-saml/xml-crypto/pull/300) - [**chore**] Make linting rules more strict [#293](https://github.com/node-saml/xml-crypto/pull/293) - [**chore**] Replace Nodeunit with Mocha [#294](https://github.com/node-saml/xml-crypto/pull/294) --- ## v3.1.0 (2023-06-05) ### 🚀 Minor Changes - [**enhancement**] Add support for appending attributes to KeyInfo element [#285](https://github.com/node-saml/xml-crypto/pull/285) - [**enhancement**] Use inclusiveNamespacesPrefixList to generate InclusiveNamespaces [#284](https://github.com/node-saml/xml-crypto/pull/284) - [**enhancement**] build: add release-it to facilitate builds [#275](https://github.com/node-saml/xml-crypto/pull/275) - [**enhancement**] [**documentation**] feat: add type declaration [#277](https://github.com/node-saml/xml-crypto/pull/277) - [**enhancement**] make FileKeyInfo extensible for compatibility with TypeScript [#273](https://github.com/node-saml/xml-crypto/pull/273) - [**enhancement**] Updated getKeyInfo function with actual implementation [#249](https://github.com/node-saml/xml-crypto/pull/249) ### 🔗 Dependencies - [**dependencies**] Update dependencies [#296](https://github.com/node-saml/xml-crypto/pull/296) - [**dependencies**] Bump minimatch from 3.0.4 to 3.1.2 [#276](https://github.com/node-saml/xml-crypto/pull/276) - [**dependencies**] [**javascript**] Bump qs from 6.5.2 to 6.5.3 [#271](https://github.com/node-saml/xml-crypto/pull/271) ### 📚 Documentation - [**documentation**] [**chore**] Adjust references for `node-saml` organization [#298](https://github.com/node-saml/xml-crypto/pull/298) ### ⚙️ Technical Tasks - [**chore**] Force CI to run on every PR [#286](https://github.com/node-saml/xml-crypto/pull/286) - [**chore**] Format code [#289](https://github.com/node-saml/xml-crypto/pull/289) - [**chore**] Lint code [#288](https://github.com/node-saml/xml-crypto/pull/288) - [**chore**] Add support for linting [#287](https://github.com/node-saml/xml-crypto/pull/287) --- ## v3.0.1 (2022-10-31) ### 🔗 Dependencies - [**dependencies**] [**javascript**] Bump ajv and har-validator [#266](https://github.com/node-saml/xml-crypto/pull/266) - [**dependencies**] [**javascript**] Bump yargs-parser and tap [#257](https://github.com/node-saml/xml-crypto/pull/257) - [**dependencies**] [**javascript**] Bump minimist and tap [#264](https://github.com/node-saml/xml-crypto/pull/264) --- ## v3.0.0 (2022-10-13) ### 🔗 Dependencies - [**dependencies**] [**javascript**] Bump @xmldom/xmldom from 0.7.0 to 0.8.3 [#261](https://github.com/node-saml/xml-crypto/pull/261) - [**dependencies**] [**javascript**] Bump handlebars from 4.0.11 to 4.7.7 [#247](https://github.com/node-saml/xml-crypto/pull/247) - [**dependencies**] [**javascript**] Bump lodash from 4.17.10 to 4.17.21 [#248](https://github.com/node-saml/xml-crypto/pull/248) - [**dependencies**] [**javascript**] Bump hosted-git-info from 2.6.0 to 2.8.9 [#246](https://github.com/node-saml/xml-crypto/pull/246) - [**dependencies**] [**javascript**] Bump ejs from 2.6.1 to 3.1.7 [#244](https://github.com/node-saml/xml-crypto/pull/244) - [**dependencies**] [**javascript**] Bump path-parse from 1.0.5 to 1.0.7 [#245](https://github.com/node-saml/xml-crypto/pull/245) ### ⚙️ Technical Tasks - [**chore**] build(ci): test on later node versions [#251](https://github.com/node-saml/xml-crypto/pull/251) --- ## v2.1.4 (2022-07-08) ### 🐛 Bug Fixes - [**bug**] Correct behavior for XML canonicalization with namespaces and nested elements [#242](https://github.com/node-saml/xml-crypto/pull/242) --- ## v2.1.3 (2021-08-20) ### 🔗 Dependencies - [**dependencies**] [**javascript**] [**security**] Update xmldom to 0.7.0 [#236](https://github.com/node-saml/xml-crypto/pull/236) - [**dependencies**] [**java**] Bump commons-io from 2.4 to 2.7 in /test/validators/XmlCryptoJava [#229](https://github.com/node-saml/xml-crypto/pull/229) --- ## v2.1.2 (2021-04-19) _No changelog for this release._ --- ## v2.1.1 (2021-03-16) _No changelog for this release._ --- ## v2.1.0 (2021-03-15) ### 🔗 Dependencies - [**dependencies**] [**javascript**] Bump xmldom from 0.1.27 to 0.5.0 [#225](https://github.com/node-saml/xml-crypto/pull/225) - [**dependencies**] [**java**] Bump junit from 4.12 to 4.13.1 in /test/validators/XmlCryptoJava [#217](https://github.com/node-saml/xml-crypto/pull/217) ### 🐛 Bug Fixes - [**bug**] fix for #201 [#218](https://github.com/node-saml/xml-crypto/pull/218) ### ⚙️ Technical Tasks - [**chore**] Don't pull the example folder into the module build [#220](https://github.com/node-saml/xml-crypto/pull/220) --- ## v2.0.0 (2020-10-05) _No changelog for this release._ --- ## v1.5.3 (2020-04-14) ### 🚀 Minor Changes - [**enhancement**] Async response for built in algo sign/verify [#209](https://github.com/node-saml/xml-crypto/pull/209) --- ## v1.5.2 (2020-04-13) _No changelog for this release._ --- ## v1.5.1 (2020-04-13) ### 🐛 Bug Fixes - [**bug**] Test suites of other projects (mocha) that include v1.5.0 fail [#207](https://github.com/node-saml/xml-crypto/pull/207) --- ## v1.5.0 (2020-04-12) ### 🚀 Minor Changes - [**enhancement**] Add callback options to sign/verify asynchronously [#206](https://github.com/node-saml/xml-crypto/pull/206) --- ## v1.4.1 (2020-04-03) ### 🔗 Dependencies - [**dependencies**] Bump js-yaml from 3.12.0 to 3.13.1 [#205](https://github.com/node-saml/xml-crypto/pull/205) ### 🐛 Bug Fixes - [**bug**] validation instruction typo [#192](https://github.com/node-saml/xml-crypto/pull/192) - [**bug**] Fixes line end and white space normalization. [#196](https://github.com/node-saml/xml-crypto/pull/196) --- ## v1.4.0 (2019-04-26) ### 🐛 Bug Fixes - [**bug**] Fix canon xml being computed differently when signing, than when verifying [#183](https://github.com/node-saml/xml-crypto/pull/183) --- ## v1.3.0 (2019-03-23) ### 🐛 Bug Fixes - [**bug**] Xml enc c14# inclusivenamespace fixes [#179](https://github.com/node-saml/xml-crypto/pull/179) --- ## v1.2.0 (2019-02-26) ### 🐛 Bug Fixes - [**bug**] Accept existing xml prefixes to avoid adding to signature [#171](https://github.com/node-saml/xml-crypto/pull/171) --- ## v1.1.4 (2019-02-11) ### 🐛 Bug Fixes - [**bug**] fix for enveloped signatures [#174](https://github.com/node-saml/xml-crypto/pull/174) --- ## v1.1.3 (2019-02-10) ### 🐛 Bug Fixes - [**bug**] Update signed-xml.js [#172](https://github.com/node-saml/xml-crypto/pull/172) --- ## v1.1.2 (2019-01-28) _No changelog for this release._ --- ## v1.1.1 (2019-01-01) _No changelog for this release._ --- ## v1.1.0 (2019-01-01) _No changelog for this release._ --- ## v1.0.2 (2018-11-08) ### 🐛 Bug Fixes - [**bug**] Bugfix: a namespace in the inclusive namespace list should be treated… [#163](https://github.com/node-saml/xml-crypto/pull/163) --- ## v1.0.1 (2018-09-10) _No changelog for this release._ --- ## v1.0.0 (2018-09-10) ### 🔗 Dependencies - [**dependencies**] Addresses issue #235 by upgrading xmldom version to 0.1.27 [#155](https://github.com/node-saml/xml-crypto/pull/155) ### 🐛 Bug Fixes - [**bug**] Decode DigestValue for validation [#160](https://github.com/node-saml/xml-crypto/pull/160) - [**bug**] Patch for non exclusive c14n [#157](https://github.com/node-saml/xml-crypto/pull/157) - [**bug**] Merge changes from datagovsg fork [#161](https://github.com/node-saml/xml-crypto/pull/161) --- ## v0.9.0 (2017-02-26) _No changelog for this release._ --- ## 0.9.0 (2017-02-26) ### 🚀 Minor Changes - [**enhancement**] Separate namespaces with same prefix but different URI [#117](https://github.com/node-saml/xml-crypto/pull/117) ### 🐛 Bug Fixes - [**bug**] Implement transform: 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315' [#116](https://github.com/node-saml/xml-crypto/pull/116) --- ## v0.8.5 (2016-12-08) ### 🚀 Minor Changes - [**enhancement**] Add possible id attribute 'id' [#121](https://github.com/node-saml/xml-crypto/pull/121) ### 📚 Documentation - [**documentation**] Update license field to npm recommendation [#119](https://github.com/node-saml/xml-crypto/pull/119) - [**documentation**] Fix author field format [#120](https://github.com/node-saml/xml-crypto/pull/120) - [**documentation**] Remove namespace-breaking reserialization of signature from example in README [#105](https://github.com/node-saml/xml-crypto/pull/105) --- ## v0.8.4 (2016-03-12) _No changelog for this release._ --- ## v0.8.3 (2016-03-06) _No changelog for this release._ --- ## v0.8.2 (2015-12-13) _No changelog for this release._ --- ## v0.8.1 (2015-10-15) _No changelog for this release._ --- ## v0.8.0 (2015-10-03) _No changelog for this release._ --- ## V1 (2013-07-20) _No changelog for this release._ ================================================ FILE: LICENSE ================================================ (The MIT License) Copyright (c) Yaron Naveh Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # xml-crypto ![Build Status](https://github.com/node-saml/xml-crypto/workflows/Test%20Status/badge.svg) [![code style: prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![codecov](https://codecov.io/gh/node-saml/xml-crypto/branch/master/graph/badge.svg?token=PQWCMBWBFB)](https://codecov.io/gh/node-saml/xml-crypto) [![DeepScan grade](https://deepscan.io/api/teams/17569/projects/30525/branches/981134/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=17569&pid=30525&bid=981134) [![NPM](https://nodei.co/npm/xml-crypto.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/xml-crypto) ## Sponsors ![workos](https://github.com/workos.png?size=30) [workos](https://github.com/workos) ![stytchauth](https://github.com/stytchauth.png?size=30) [stytchauth](https://github.com/stytchauth) ## Upgrading The `.getReferences()` AND the `.references` APIs are deprecated. Please do not attempt to access them. The content in them should be treated as unsigned. Instead, we strongly encourage users to migrate to the `.getSignedReferences()` API. See the [Verifying XML document](#verifying-xml-documents) section We understand that this may take a lot of efforts to migrate, feel free to ask for help. This will help prevent future XML signature wrapping attacks. ## Supported Algorithms ### Canonicalization and Transformation Algorithms - Canonicalization - Canonicalization with comments - Exclusive Canonicalization - Exclusive Canonicalization with comments - Enveloped Signature transform ### Hashing Algorithms - SHA1 digests - SHA256 digests - SHA512 digests ### Signature Algorithms - RSA-SHA1 - RSA-SHA256 - RSA-SHA256 with MGF1 - RSA-SHA512 HMAC-SHA1 is also available but it is disabled by default - HMAC-SHA1 to enable HMAC-SHA1, call `enableHMAC()` on your instance of `SignedXml`. This will enable HMAC and disable digital signature algorithms. Due to key confusion issues, it is risky to have both HMAC-based and public key digital signature algorithms enabled at same time. [You are able to extend xml-crypto with custom algorithms.](#customizing-algorithms) ## Signing Xml documents When signing a xml document you can pass the following options to the `SignedXml` constructor to customize the signature process: - `privateKey` - **[required]** a `Buffer` or pem encoded `String` containing your private key - `publicCert` - **[optional]** a `Buffer` or pem encoded `String` containing your public key - `signatureAlgorithm` - **[required]** one of the supported [signature algorithms](#signature-algorithms). Ex: `sign.signatureAlgorithm = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"` - `canonicalizationAlgorithm` - **[required]** one of the supported [canonicalization algorithms](#canonicalization-and-transformation-algorithms). Ex: `sign.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"` Use this code: ```javascript var SignedXml = require("xml-crypto").SignedXml, fs = require("fs"); var xml = "" + "" + "Harry Potter" + "" + ""; var sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") }); sig.addReference({ xpath: "//*[local-name(.)='book']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); fs.writeFileSync("signed.xml", sig.getSignedXml()); ``` The result will be: ```xml Harry Potter cdiS43aFDQMnb3X8yaIUej3+z9Q= vhWzpQyIYuncHUZV9W...[long base64 removed]... ``` Note: If you set the `publicCert` and the `getKeyInfoContent` properties, a `` element with the public certificate will be generated in the signature: ```xml ...[signature info removed]... vhWzpQyIYuncHUZV9W...[long base64 removed]... MIIGYjCCBJagACCBN...[long base64 removed]... ``` For `getKeyInfoContent`, a default implementation `SignedXml.getKeyInfoContent` is available. To customize this see [customizing algorithms](#customizing-algorithms) for an example. ## Verifying Xml documents When verifying a xml document you can pass the following options to the `SignedXml` constructor to customize the verify process: - `publicCert` - **[optional]** your certificate as a string, a string of multiple certs in PEM format, or a Buffer - `privateKey` - **[optional]** your private key as a string or a Buffer - used for verifying symmetrical signatures (HMAC) The certificate that will be used to check the signature will first be determined by calling `this.getCertFromKeyInfo()`, which function you can customize as you see fit. If that returns `null`, then `publicCert` is used. If that is `null`, then `privateKey` is used (for symmetrical signing applications). Example: ```javascript new SignedXml({ publicCert: client_public_pem, getCertFromKeyInfo: () => null, }); ``` You can use any dom parser you want in your code (or none, depending on your usage). This sample uses [xmldom](https://github.com/xmldom/xmldom), so you should install it first: ```shell npm install @xmldom/xmldom ``` Example: ```javascript var select = require("xml-crypto").xpath, dom = require("@xmldom/xmldom").DOMParser, SignedXml = require("xml-crypto").SignedXml, fs = require("fs"); var xml = fs.readFileSync("signed.xml").toString(); var doc = new dom().parseFromString(xml); // DO NOT attempt to parse whatever data object you have here in `doc` // and then use it to verify the signature. This can lead to security issues. // i.e. BAD: parseAssertion(doc), // good: see below var signature = select( doc, "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", )[0]; var sig = new SignedXml({ publicCert: fs.readFileSync("client_public.pem") }); sig.loadSignature(signature); try { var res = sig.checkSignature(xml); } catch (ex) { console.log(ex); } ``` In order to protect from some attacks we must check the content we want to use is the one that has been signed: ```javascript if (!res) { throw "Invalid Signature"; } // good: The XML Signature has been verified, meaning some subset of XML is verified. var signedBytes = sig.getSignedReferences(); var authenticatedDoc = new dom().parseFromString(signedBytes[0]); // Take the first signed reference // It is now safe to load SAML, obtain the assertion XML, or do whatever else is needed. // Be sure to only use authenticated data. let signedAssertionNode = extractAssertion(authenticatedDoc); let parsedAssertion = parseAssertion(signedAssertionNode); return parsedAssertion; // This the correctly verified signed Assertion // BAD example: DO not use the .getReferences() API. ``` Note: The xml-crypto api requires you to supply it separately the xml signature ("<Signature>...</Signature>", in loadSignature) and the signed xml (in checkSignature). The signed xml may or may not contain the signature in it, but you are still required to supply the signature separately. ### Caring for Implicit transform If you fail to verify signed XML, then one possible cause is that there are some hidden implicit transforms(#). (#) Normalizing XML document to be verified. i.e. remove extra space within a tag, sorting attributes, importing namespace declared in ancestor nodes, etc. The reason for these implicit transform might come from [complex xml signature specification](https://www.w3.org/TR/2002/REC-xmldsig-core-20020212), which makes XML developers confused and then leads to incorrect implementation for signing XML document. If you keep failing verification, it is worth trying to guess such a hidden transform and specify it to the option as below: ```javascript var options = { implicitTransforms: ["http://www.w3.org/TR/2001/REC-xml-c14n-20010315"], publicCert: fs.readFileSync("client_public.pem"), }; var sig = new SignedXml(options); sig.loadSignature(signature); var res = sig.checkSignature(xml); ``` You might find it difficult to guess such transforms, but there are typical transforms you can try. - - - - ## API ### xpath See [xpath.js](https://github.com/yaronn/xpath.js) for usage. Note that this is actually using [another library](https://github.com/goto100/xpath) as the underlying implementation. ### SignedXml The `SignedXml` constructor provides an abstraction for sign and verify xml documents. The object is constructed using `new SignedXml(options?: SignedXmlOptions)` where the possible options are: - `idMode` - default `null` - if the value of `wssecurity` is passed it will create/validate id's with the ws-security namespace. - `idAttribute` - string - default `Id` or `ID` or `id` - the name of the attribute that contains the id of the element - `privateKey` - string or Buffer - default `null` - the private key to use for signing - `publicCert` - string or Buffer - default `null` - the public certificate to use for verifying - `signatureAlgorithm` - string - the signature algorithm to use - `canonicalizationAlgorithm` - string - default `undefined` - the canonicalization algorithm to use - `inclusiveNamespacesPrefixList` - string - default `null` - a list of namespace prefixes to include during canonicalization - `implicitTransforms` - string[] - default `[]` - a list of implicit transforms to use during verification - `keyInfoAttributes` - object - default `{}` - a hash of attributes and values `attrName: value` to add to the KeyInfo node - `getKeyInfoContent` - function - default `SignedXml.getKeyInfoContent` - a function that returns the content of the KeyInfo node - `getCertFromKeyInfo` - function - default `noop` - a function that returns the certificate from the `` node #### API A `SignedXml` object provides the following methods: To sign xml documents: - `addReference({ xpath, transforms, digestAlgorithm, id, type })` - adds a reference to a xml element where: - `xpath` - a string containing a XPath expression referencing a xml element - `transforms` - an array of [transform algorithms](#canonicalization-and-transformation-algorithms), the referenced element will be transformed for each value in the array - `digestAlgorithm` - one of the supported [hashing algorithms](#hashing-algorithms) - `id` - an optional `Id` attribute to add to the reference element - `type` - the optional `Type` attribute to add to the reference element (represented as a URI) - `computeSignature(xml, [options])` - compute the signature of the given xml where: - `xml` - a string containing a xml document - `options` - an object with the following properties: - `prefix` - adds this value as a prefix for the generated signature tags - `attrs` - a hash of attributes and values `attrName: value` to add to the signature root node - `location` - customize the location of the signature, pass an object with a `reference` key which should contain a XPath expression to a reference node, an `action` key which should contain one of the following values: `append`, `prepend`, `before`, `after` - `existingPrefixes` - A hash of prefixes and namespaces `prefix: namespace` that shouldn't be in the signature because they already exist in the xml - `getSignedXml()` - returns the original xml document with the signature in it, **must be called only after `computeSignature`** - `getSignatureXml()` - returns just the signature part, **must be called only after `computeSignature`** - `getOriginalXmlWithIds()` - returns the original xml with Id attributes added on relevant elements (required for validation), **must be called only after `computeSignature`** To verify xml documents: - `loadSignature(signatureXml)` - loads the signature where: - `signatureXml` - a string or node object (like an [xmldom](https://github.com/xmldom/xmldom) node) containing the xml representation of the signature - `checkSignature(xml)` - validates the given xml document and returns `true` if the validation was successful ## Customizing Algorithms The following sample shows how to sign a message using custom algorithms. First import some modules: ```javascript var SignedXml = require("xml-crypto").SignedXml, fs = require("fs"); ``` Now define the extension point you want to implement. You can choose one or more. To determine the inclusion and contents of a `` element, the function `this.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 `this.getKeyInfoContent`. If you prefer to use the default implementation, assign `SignedXml.getKeyInfoContent` to `this.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. ```javascript function MyDigest() { this.getHash = function (xml) { return "the base64 hash representation of the given xml string"; }; this.getAlgorithmName = function () { return "http://myDigestAlgorithm"; }; } ``` A custom signing algorithm. ```javascript function MySignatureAlgorithm() { /*sign the given SignedInfo using the key. return base64 signature value*/ this.getSignature = function (signedInfo, privateKey) { return "signature of signedInfo as base64..."; }; this.getAlgorithmName = function () { return "http://mySigningAlgorithm"; }; } ``` Custom transformation algorithm. ```javascript function MyTransformation() { /*given a node (from the xmldom module) return its canonical representation (as string)*/ this.process = function (node) { //you should apply your transformation before returning return node.toString(); }; this.getAlgorithmName = function () { return "http://myTransformation"; }; } ``` Custom canonicalization is actually the same as custom transformation. It is applied on the SignedInfo rather than on references. ```javascript function MyCanonicalization() { /*given a node (from the xmldom module) return its canonical representation (as string)*/ this.process = function (node) { //you should apply your transformation before returning return "< x/>"; }; this.getAlgorithmName = function () { return "http://myCanonicalization"; }; } ``` Now you need to register the new algorithms: ```javascript /*register all the custom algorithms*/ signedXml.CanonicalizationAlgorithms["http://MyTransformation"] = MyTransformation; signedXml.CanonicalizationAlgorithms["http://MyCanonicalization"] = MyCanonicalization; signedXml.HashAlgorithms["http://myDigestAlgorithm"] = MyDigest; signedXml.SignatureAlgorithms["http://mySigningAlgorithm"] = MySignatureAlgorithm; ``` Now do the signing. Note how we configure the signature to use the above algorithms: ```javascript function signXml(xml, xpath, key, dest) { var options = { publicCert: fs.readFileSync("my_public_cert.pem", "latin1"), privateKey: fs.readFileSync(key), /*configure the signature object to use the custom algorithms*/ signatureAlgorithm: "http://mySignatureAlgorithm", canonicalizationAlgorithm: "http://MyCanonicalization", }; var sig = new SignedXml(options); sig.addReference({ xpath: "//*[local-name(.)='x']", transforms: ["http://MyTransformation"], digestAlgorithm: "http://myDigestAlgorithm", }); sig.addReference({ xpath, transforms: ["http://MyTransformation"], digestAlgorithm: "http://myDigestAlgorithm", }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); fs.writeFileSync(dest, sig.getSignedXml()); } var xml = "" + "" + "Harry Potter" + ""; (""); signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml"); ``` You can always look at the actual code as a sample. ## Asynchronous signing and verification If the private key is not stored locally, and you wish to use a signing server or Hardware Security Module (HSM) to sign documents, you can create a custom signing algorithm that uses an asynchronous callback. ```javascript function AsyncSignatureAlgorithm() { this.getSignature = function (signedInfo, privateKey, callback) { var signer = crypto.createSign("RSA-SHA1"); signer.update(signedInfo); var res = signer.sign(privateKey, "base64"); //Do some asynchronous things here callback(null, res); }; this.getAlgorithmName = function () { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }; } var sig = new SignedXml({ signatureAlgorithm: "http://asyncSignatureAlgorithm" }); sig.SignatureAlgorithms["http://asyncSignatureAlgorithm"] = AsyncSignatureAlgorithm; sig.signatureAlgorithm = "http://asyncSignatureAlgorithm"; sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.computeSignature(xml, opts, function (err) { var signedResponse = sig.getSignedXml(); }); ``` The function `sig.checkSignature` may also use a callback if asynchronous verification is needed. ## X.509 / Key formats Xml-Crypto internally relies on node's crypto module. This means pem encoded certificates are supported. So to sign an xml use key.pem that looks like this (only the beginning of the key content is shown): ```text -----BEGIN PRIVATE KEY----- MIICdwIBADANBgkqhkiG9w0... -----END PRIVATE KEY----- ``` And for verification use key_public.pem: ```text -----BEGIN CERTIFICATE----- MIIBxDCCAW6gAwIBAgIQxUSX... -----END CERTIFICATE----- ``` ### Converting .pfx certificates to pem If you have .pfx certificates you can convert them to .pem using [openssl](http://www.openssl.org/): ```shell openssl pkcs12 -in c:\certs\yourcert.pfx -out c:\certs\cag.pem ``` Then you could use the result as is for the purpose of signing. For the purpose of validation open the resulting .pem with a text editor and copy from -----BEGIN CERTIFICATE----- to -----END CERTIFICATE----- (including) to a new text file and save it as .pem. ## Examples ### how to sign a root node (_coming soon_) ### how to add a prefix for the signature Use the `prefix` option when calling `computeSignature` to add a prefix to the signature. ```javascript var SignedXml = require("xml-crypto").SignedXml, fs = require("fs"); var xml = "" + "" + "Harry Potter" + "" + ""; var sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") }); sig.addReference({ xpath: "//*[local-name(.)='book']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { prefix: "ds", }); ``` ### how to specify the location of the signature Use the `location` option when calling `computeSignature` to move the signature around. Set `action` to one of the following: - append(default) - append to the end of the xml document - prepend - prepend to the xml document - before - prepend to a specific node (use the `referenceNode` property) - after - append to specific node (use the `referenceNode` property) ```javascript const SignedXml = require("xml-crypto").SignedXml; const fs = require("fs"); const xml = "" + "" + "Harry Potter" + "" + ""; const sig = new SignedXml({ privateKey: fs.readFileSync("client.pem") }); sig.addReference({ xpath: "//*[local-name(.)='book']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { location: { reference: "//*[local-name(.)='book']", action: "after" }, // This will place the signature after the book element }); ``` ### How to add custom Objects to the signature Use the `objects` option when creating a SignedXml instance to add custom Objects to the signature. ```javascript const SignedXml = require("xml-crypto").SignedXml; const fs = require("fs"); const xml = "" + "" + "Harry Potter" + "" + ""; const sig = new SignedXml({ privateKey: fs.readFileSync("client.pem"), canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", objects: [ { content: "Test data in Object", attributes: { Id: "Object1", MimeType: "text/xml", }, }, ], }); // Add a reference to the Object element sig.addReference({ xpath: "//*[@Id='Object1']", digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.computeSignature(xml); fs.writeFileSync("signed.xml", sig.getSignedXml()); ``` ### more examples (_coming soon_) ## Development The testing framework we use is [Mocha](https://github.com/mochajs/mocha) with [Chai](https://github.com/chaijs/chai) as the assertion framework. To run tests use: ```shell npm test ``` ## Sponsors ![Short-io logo](https://github.com/Short-io.png?size=30) [Short-io](https://github.com/Short-io) ![RideAmigosCorp logo](https://github.com/RideAmigosCorp.png?size=30) [RideAmigosCorp](https://github.com/RideAmigosCorp) ## License This project is licensed under the [MIT License](http://opensource.org/licenses/MIT). See the [LICENSE](LICENSE) file for more info. ================================================ FILE: example/client.pem ================================================ -----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----- ================================================ FILE: example/client_public.pem ================================================ -----BEGIN CERTIFICATE----- MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5 sT/txBnVJGziyO8DPYdu2fPMER8ajJfl -----END CERTIFICATE----- ================================================ FILE: example/example.js ================================================ /* eslint-disable no-console */ const select = require("xml-crypto").xpath; const dom = require("@xmldom/xmldom").DOMParser; const SignedXml = require("xml-crypto").SignedXml; const fs = require("fs"); function signXml(xml, xpath, key, dest) { const sig = new SignedXml(); sig.privateKey = fs.readFileSync(key); sig.addReference(xpath); sig.computeSignature(xml); fs.writeFileSync(dest, sig.getSignedXml()); } function validateXml(xml, key) { const doc = new dom().parseFromString(xml); const signature = select( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, )[0]; const sig = new SignedXml(); sig.publicCert = key; sig.loadSignature(signature.toString()); const res = sig.checkSignature(xml); if (!res) { console.log(sig.validationErrors); } return res; } const xml = "" + "" + "Harry Potter" + "" + ""; //sign an xml document signXml(xml, "//*[local-name(.)='book']", "client.pem", "result.xml"); console.log("xml signed successfully"); const signedXml = fs.readFileSync("result.xml").toString(); console.log("validating signature..."); //validate an xml document if (validateXml(signedXml, "client_public.pem")) { console.log("signature is valid"); } else { console.log("signature not valid"); } ================================================ FILE: package.json ================================================ { "name": "xml-crypto", "version": "6.0.0", "private": false, "description": "Xml digital signature and encryption library for Node.js", "keywords": [ "xml", "digital signature", "xml encryption", "x.509 certificate" ], "repository": { "type": "git", "url": "https://github.com/node-saml/xml-crypto.git" }, "license": "MIT", "author": "Yaron Naveh (http://webservices20.blogspot.com/)", "contributors": [ "LoneRifle ", "Chris Barth " ], "main": "./lib", "files": [ "lib", "LICENSE", "README.md" ], "scripts": { "build": "npx tsc", "changelog": "gren changelog --override --generate", "lint": "eslint \"{src,test}/*.ts\" --cache && npm run prettier-check", "lint:fix": "eslint --fix \"{src,test}/*.ts\" && npm run prettier-format", "prepare": "tsc", "prettier-check": "prettier --config .prettierrc.json --check .", "prettier-format": "prettier --config .prettierrc.json --write .", "prerelease": "git clean -xfd && npm ci && npm test", "release": "release-it", "test": "nyc mocha" }, "dependencies": { "@xmldom/is-dom-node": "^1.0.1", "@xmldom/xmldom": "^0.8.10", "xpath": "^0.0.33" }, "devDependencies": { "@cjbarth/github-release-notes": "^4.2.0", "@istanbuljs/nyc-config-typescript": "^1.0.2", "@prettier/plugin-xml": "^3.2.2", "@types/chai": "^4.3.11", "@types/mocha": "^10.0.6", "@types/node": "^16.18.69", "@typescript-eslint/eslint-plugin": "^6.18.1", "@typescript-eslint/parser": "^6.18.1", "chai": "^4.3.10", "choma": "^1.2.1", "ejs": "^3.1.9", "eslint": "^8.56.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-deprecation": "^2.0.0", "lcov": "^1.16.0", "mocha": "^10.2.0", "nyc": "^15.1.0", "prettier": "^3.1.0", "prettier-plugin-packagejson": "^2.4.6", "release-it": "^16.3.0", "source-map-support": "^0.5.21", "ts-node": "^10.9.1", "typescript": "^5.3.2" }, "engines": { "node": ">=16" } } ================================================ FILE: src/c14n-canonicalization.ts ================================================ import type { CanonicalizationOrTransformationAlgorithm, CanonicalizationOrTransformationAlgorithmProcessOptions, NamespacePrefix, RenderedNamespace, } from "./types"; import * as utils from "./utils"; import * as isDomNode from "@xmldom/is-dom-node"; export class C14nCanonicalization implements CanonicalizationOrTransformationAlgorithm { protected includeComments = false; constructor() { this.includeComments = false; } attrCompare(a, b) { if (!a.namespaceURI && b.namespaceURI) { return -1; } if (!b.namespaceURI && a.namespaceURI) { return 1; } const left = a.namespaceURI + a.localName; const right = b.namespaceURI + b.localName; if (left === right) { return 0; } else if (left < right) { return -1; } else { return 1; } } nsCompare(a, b) { const attr1 = a.prefix; const attr2 = b.prefix; if (attr1 === attr2) { return 0; } return attr1.localeCompare(attr2); } renderAttrs(node) { let i; let attr; const attrListToRender: Attr[] = []; if (isDomNode.isCommentNode(node)) { return this.renderComment(node); } if (node.attributes) { for (i = 0; i < node.attributes.length; ++i) { attr = node.attributes[i]; //ignore namespace definition attributes if (attr.name.indexOf("xmlns") === 0) { continue; } attrListToRender.push(attr); } } attrListToRender.sort(this.attrCompare); const res = attrListToRender.map((attr) => { return ` ${attr.name}="${utils.encodeSpecialCharactersInAttribute(attr.value)}"`; }); return res.join(""); } /** * Create the string of all namespace declarations that should appear on this element * * @param node The node we now render * @param prefixesInScope The prefixes defined on this node parents which are a part of the output set * @param defaultNs The current default namespace * @param defaultNsForPrefix * @param ancestorNamespaces Import ancestor namespaces if it is specified * @api private */ renderNs( node: Element, prefixesInScope: string[], defaultNs: string, defaultNsForPrefix: string, ancestorNamespaces: NamespacePrefix[], ): RenderedNamespace { let i; let attr; const res: string[] = []; let newDefaultNs = defaultNs; const nsListToRender: { prefix: string; namespaceURI: string }[] = []; const currNs = node.namespaceURI || ""; //handle the namespace of the node itself if (node.prefix) { if (prefixesInScope.indexOf(node.prefix) === -1) { nsListToRender.push({ prefix: node.prefix, namespaceURI: node.namespaceURI || defaultNsForPrefix[node.prefix], }); prefixesInScope.push(node.prefix); } } else if (defaultNs !== currNs) { //new default ns newDefaultNs = node.namespaceURI || ""; res.push(' xmlns="', newDefaultNs, '"'); } //handle the attributes namespace if (node.attributes) { for (i = 0; i < node.attributes.length; ++i) { attr = node.attributes[i]; //handle all prefixed attributes that are included in the prefix list and where //the prefix is not defined already. New prefixes can only be defined by `xmlns:`. if (attr.prefix === "xmlns" && prefixesInScope.indexOf(attr.localName) === -1) { nsListToRender.push({ prefix: attr.localName, namespaceURI: attr.value }); prefixesInScope.push(attr.localName); } //handle all prefixed attributes that are not xmlns definitions and where //the prefix is not defined already if ( attr.prefix && prefixesInScope.indexOf(attr.prefix) === -1 && attr.prefix !== "xmlns" && attr.prefix !== "xml" ) { nsListToRender.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI }); prefixesInScope.push(attr.prefix); } } } if (utils.isArrayHasLength(ancestorNamespaces)) { // Remove namespaces which are already present in nsListToRender for (const ancestorNamespace of ancestorNamespaces) { let alreadyListed = false; for (const nsToRender of nsListToRender) { if ( nsToRender.prefix === ancestorNamespace.prefix && nsToRender.namespaceURI === ancestorNamespace.namespaceURI ) { alreadyListed = true; } } if (!alreadyListed) { nsListToRender.push(ancestorNamespace); } } } nsListToRender.sort(this.nsCompare); //render namespaces res.push( ...nsListToRender.map((attr) => { if (attr.prefix) { return ` xmlns:${attr.prefix}="${attr.namespaceURI}"`; } return ` xmlns="${attr.namespaceURI}"`; }), ); return { rendered: res.join(""), newDefaultNs }; } /** * @param node Node */ processInner(node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces) { if (isDomNode.isCommentNode(node)) { return this.renderComment(node); } if (node.data) { return utils.encodeSpecialCharactersInText(node.data); } if (isDomNode.isElementNode(node)) { let i; let pfxCopy; const ns = this.renderNs( node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces, ); const res = ["<", node.tagName, ns.rendered, this.renderAttrs(node), ">"]; for (i = 0; i < node.childNodes.length; ++i) { pfxCopy = prefixesInScope.slice(0); res.push( this.processInner(node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, []), ); } res.push(""); return res.join(""); } throw new Error(`Unable to canonicalize node type: ${node.nodeType}`); } // Thanks to deoxxa/xml-c14n for comment renderer renderComment(node: Comment) { if (!this.includeComments) { return ""; } const isOutsideDocument = node.ownerDocument === node.parentNode; let isBeforeDocument = false; let isAfterDocument = false; if (isOutsideDocument) { let nextNode: ChildNode | null = node; let previousNode: ChildNode | null = node; while (nextNode !== null) { if (nextNode === node.ownerDocument.documentElement) { isBeforeDocument = true; break; } nextNode = nextNode.nextSibling; } while (previousNode !== null) { if (previousNode === node.ownerDocument.documentElement) { isAfterDocument = true; break; } previousNode = previousNode.previousSibling; } } const afterDocument = isAfterDocument ? "\n" : ""; const beforeDocument = isBeforeDocument ? "\n" : ""; const encodedText = utils.encodeSpecialCharactersInText(node.data); return `${afterDocument}${beforeDocument}`; } /** * Perform canonicalization of the given node * * @param node * @api public */ process(node: Node, options: CanonicalizationOrTransformationAlgorithmProcessOptions): string { options = options || {}; const defaultNs = options.defaultNs || ""; const defaultNsForPrefix = options.defaultNsForPrefix || {}; const ancestorNamespaces = options.ancestorNamespaces || []; const prefixesInScope: string[] = []; for (let i = 0; i < ancestorNamespaces.length; i++) { prefixesInScope.push(ancestorNamespaces[i].prefix); } const res = this.processInner( node, prefixesInScope, defaultNs, defaultNsForPrefix, ancestorNamespaces, ); return res; } getAlgorithmName() { return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315"; } } /** * Add c14n#WithComments here (very simple subclass) */ export class C14nCanonicalizationWithComments extends C14nCanonicalization { constructor() { super(); this.includeComments = true; } getAlgorithmName() { return "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments"; } } ================================================ FILE: src/enveloped-signature.ts ================================================ import * as xpath from "xpath"; import * as isDomNode from "@xmldom/is-dom-node"; import type { CanonicalizationOrTransformationAlgorithm, CanonicalizationOrTransformationAlgorithmProcessOptions, CanonicalizationOrTransformAlgorithmType, } from "./types"; export class EnvelopedSignature implements CanonicalizationOrTransformationAlgorithm { protected includeComments = false; constructor() { this.includeComments = false; } process(node: Node, options: CanonicalizationOrTransformationAlgorithmProcessOptions): Node { if (null == options.signatureNode) { const signature = xpath.select1( "./*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", node, ); if (isDomNode.isNodeLike(signature) && signature.parentNode) { signature.parentNode.removeChild(signature); } return node; } const signatureNode = options.signatureNode; const expectedSignatureValue = xpath.select1( ".//*[local-name(.)='SignatureValue']/text()", signatureNode, ); if (isDomNode.isTextNode(expectedSignatureValue)) { const expectedSignatureValueData = expectedSignatureValue.data; const signatures = xpath.select( ".//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", node, ); for (const nodeSignature of Array.isArray(signatures) ? signatures : []) { const signatureValue = xpath.select1( ".//*[local-name(.)='SignatureValue']/text()", nodeSignature, ); if (isDomNode.isTextNode(signatureValue)) { const signatureValueData = signatureValue.data; if (expectedSignatureValueData === signatureValueData) { if (nodeSignature.parentNode) { nodeSignature.parentNode.removeChild(nodeSignature); } } } } } return node; } getAlgorithmName(): CanonicalizationOrTransformAlgorithmType { return "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; } } ================================================ FILE: src/exclusive-canonicalization.ts ================================================ import type { CanonicalizationOrTransformationAlgorithm, CanonicalizationOrTransformationAlgorithmProcessOptions, NamespacePrefix, } from "./types"; import * as utils from "./utils"; import * as isDomNode from "@xmldom/is-dom-node"; function isPrefixInScope(prefixesInScope, prefix, namespaceURI) { let ret = false; prefixesInScope.forEach(function (pf) { if (pf.prefix === prefix && pf.namespaceURI === namespaceURI) { ret = true; } }); return ret; } export class ExclusiveCanonicalization implements CanonicalizationOrTransformationAlgorithm { protected includeComments = false; constructor() { this.includeComments = false; } attrCompare(a, b) { if (!a.namespaceURI && b.namespaceURI) { return -1; } if (!b.namespaceURI && a.namespaceURI) { return 1; } const left = a.namespaceURI + a.localName; const right = b.namespaceURI + b.localName; if (left === right) { return 0; } else if (left < right) { return -1; } else { return 1; } } nsCompare(a, b) { const attr1 = a.prefix; const attr2 = b.prefix; if (attr1 === attr2) { return 0; } return attr1.localeCompare(attr2); } renderAttrs(node) { let i; let attr; const res: string[] = []; const attrListToRender: Attr[] = []; if (isDomNode.isCommentNode(node)) { return this.renderComment(node); } if (node.attributes) { for (i = 0; i < node.attributes.length; ++i) { attr = node.attributes[i]; //ignore namespace definition attributes if (attr.name.indexOf("xmlns") === 0) { continue; } attrListToRender.push(attr); } } attrListToRender.sort(this.attrCompare); for (attr of attrListToRender) { res.push(" ", attr.name, '="', utils.encodeSpecialCharactersInAttribute(attr.value), '"'); } return res.join(""); } /** * Create the string of all namespace declarations that should appear on this element * * @param {Node} node. The node we now render * @param {Array} prefixesInScope. The prefixes defined on this node * parents which are a part of the output set * @param {String} defaultNs. The current default namespace * @return {String} * @api private */ renderNs( node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList: string[], ) { let i; let attr; const res: string[] = []; let newDefaultNs = defaultNs; const nsListToRender: NamespacePrefix[] = []; const currNs = node.namespaceURI || ""; //handle the namespaceof the node itself if (node.prefix) { if ( !isPrefixInScope( prefixesInScope, node.prefix, node.namespaceURI || defaultNsForPrefix[node.prefix], ) ) { nsListToRender.push({ prefix: node.prefix, namespaceURI: node.namespaceURI || defaultNsForPrefix[node.prefix], }); prefixesInScope.push({ prefix: node.prefix, namespaceURI: node.namespaceURI || defaultNsForPrefix[node.prefix], }); } } else if (defaultNs !== currNs) { //new default ns newDefaultNs = node.namespaceURI; res.push(' xmlns="', newDefaultNs, '"'); } //handle the attributes namespace if (node.attributes) { for (i = 0; i < node.attributes.length; ++i) { attr = node.attributes[i]; //handle all prefixed attributes that are included in the prefix list and where //the prefix is not defined already if ( attr.prefix && !isPrefixInScope(prefixesInScope, attr.localName, attr.value) && inclusiveNamespacesPrefixList.indexOf(attr.localName) >= 0 ) { nsListToRender.push({ prefix: attr.localName, namespaceURI: attr.value }); prefixesInScope.push({ prefix: attr.localName, namespaceURI: attr.value }); } //handle all prefixed attributes that are not xmlns definitions and where //the prefix is not defined already if ( attr.prefix && !isPrefixInScope(prefixesInScope, attr.prefix, attr.namespaceURI) && attr.prefix !== "xmlns" && attr.prefix !== "xml" ) { nsListToRender.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI }); prefixesInScope.push({ prefix: attr.prefix, namespaceURI: attr.namespaceURI }); } } } nsListToRender.sort(this.nsCompare); //render namespaces for (const p of nsListToRender) { res.push(" xmlns:", p.prefix, '="', p.namespaceURI, '"'); } return { rendered: res.join(""), newDefaultNs: newDefaultNs }; } /** * @param node Node */ processInner( node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList: string[], ) { if (isDomNode.isCommentNode(node)) { return this.renderComment(node); } if (node.data) { return utils.encodeSpecialCharactersInText(node.data); } if (isDomNode.isElementNode(node)) { let i; let pfxCopy; const ns = this.renderNs( node, prefixesInScope, defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList, ); const res = ["<", node.tagName, ns.rendered, this.renderAttrs(node), ">"]; for (i = 0; i < node.childNodes.length; ++i) { pfxCopy = prefixesInScope.slice(0); res.push( this.processInner( node.childNodes[i], pfxCopy, ns.newDefaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList, ), ); } res.push(""); return res.join(""); } throw new Error(`Unable to exclusive canonicalize node type: ${node.nodeType}`); } // Thanks to deoxxa/xml-c14n for comment renderer renderComment(node: Comment) { if (!this.includeComments) { return ""; } const isOutsideDocument = node.ownerDocument === node.parentNode; let isBeforeDocument = false; let isAfterDocument = false; if (isOutsideDocument) { let nextNode: ChildNode | null = node; let previousNode: ChildNode | null = node; while (nextNode != null) { if (nextNode === node.ownerDocument.documentElement) { isBeforeDocument = true; break; } nextNode = nextNode.nextSibling; } while (previousNode != null) { if (previousNode === node.ownerDocument.documentElement) { isAfterDocument = true; break; } previousNode = previousNode.previousSibling; } } const afterDocument = isAfterDocument ? "\n" : ""; const beforeDocument = isBeforeDocument ? "\n" : ""; const encodedText = utils.encodeSpecialCharactersInText(node.data); return `${afterDocument}${beforeDocument}`; } /** * Perform canonicalization of the given element node * * @api public */ process(elem: Element, options: CanonicalizationOrTransformationAlgorithmProcessOptions): string { options = options || {}; let inclusiveNamespacesPrefixList = options.inclusiveNamespacesPrefixList || []; const defaultNs = options.defaultNs || ""; const defaultNsForPrefix = options.defaultNsForPrefix || {}; const ancestorNamespaces = options.ancestorNamespaces || []; /** * If the inclusiveNamespacesPrefixList has not been explicitly provided then look it up in CanonicalizationMethod/InclusiveNamespaces */ if (!utils.isArrayHasLength(inclusiveNamespacesPrefixList)) { const CanonicalizationMethod = utils.findChildren(elem, "CanonicalizationMethod"); if (CanonicalizationMethod.length !== 0) { const inclusiveNamespaces = utils.findChildren( CanonicalizationMethod[0], "InclusiveNamespaces", ); if (inclusiveNamespaces.length !== 0) { inclusiveNamespacesPrefixList = ( inclusiveNamespaces[0].getAttribute("PrefixList") || "" ).split(" "); } } } /** * If you have a PrefixList then use it and the ancestors to add the necessary namespaces */ if (utils.isArrayHasLength(inclusiveNamespacesPrefixList)) { inclusiveNamespacesPrefixList.forEach(function (prefix) { if (ancestorNamespaces) { ancestorNamespaces.forEach(function (ancestorNamespace) { if (prefix === ancestorNamespace.prefix) { elem.setAttributeNS( "http://www.w3.org/2000/xmlns/", `xmlns:${prefix}`, ancestorNamespace.namespaceURI, ); } }); } }); } const res = this.processInner( elem, [], defaultNs, defaultNsForPrefix, inclusiveNamespacesPrefixList, ); return res; } getAlgorithmName() { return "http://www.w3.org/2001/10/xml-exc-c14n#"; } } export class ExclusiveCanonicalizationWithComments extends ExclusiveCanonicalization { constructor() { super(); this.includeComments = true; } getAlgorithmName() { return "http://www.w3.org/2001/10/xml-exc-c14n#WithComments"; } } ================================================ FILE: src/hash-algorithms.ts ================================================ import * as crypto from "crypto"; import type { HashAlgorithm } from "./types"; export class Sha1 implements HashAlgorithm { getHash = function (xml) { const shasum = crypto.createHash("sha1"); shasum.update(xml, "utf8"); const res = shasum.digest("base64"); return res; }; getAlgorithmName = function () { return "http://www.w3.org/2000/09/xmldsig#sha1"; }; } export class Sha256 implements HashAlgorithm { getHash = function (xml) { const shasum = crypto.createHash("sha256"); shasum.update(xml, "utf8"); const res = shasum.digest("base64"); return res; }; getAlgorithmName = function () { return "http://www.w3.org/2001/04/xmlenc#sha256"; }; } export class Sha512 implements HashAlgorithm { getHash = function (xml) { const shasum = crypto.createHash("sha512"); shasum.update(xml, "utf8"); const res = shasum.digest("base64"); return res; }; getAlgorithmName = function () { return "http://www.w3.org/2001/04/xmlenc#sha512"; }; } ================================================ FILE: src/index.ts ================================================ export { C14nCanonicalization, C14nCanonicalizationWithComments } from "./c14n-canonicalization"; export { ExclusiveCanonicalization, ExclusiveCanonicalizationWithComments, } from "./exclusive-canonicalization"; export { SignedXml } from "./signed-xml"; export * from "./types"; export * from "./utils"; ================================================ FILE: src/signature-algorithms.ts ================================================ import * as crypto from "crypto"; import { type SignatureAlgorithm, createOptionalCallbackFunction } from "./types"; export class RsaSha1 implements SignatureAlgorithm { getSignature = createOptionalCallbackFunction( (signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike): string => { const signer = crypto.createSign("RSA-SHA1"); signer.update(signedInfo); const res = signer.sign(privateKey, "base64"); return res; }, ); verifySignature = createOptionalCallbackFunction( (material: string, key: crypto.KeyLike, signatureValue: string): boolean => { const verifier = crypto.createVerify("RSA-SHA1"); verifier.update(material); const res = verifier.verify(key, signatureValue, "base64"); return res; }, ); getAlgorithmName = () => { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }; } export class RsaSha256 implements SignatureAlgorithm { getSignature = createOptionalCallbackFunction( (signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike): string => { const signer = crypto.createSign("RSA-SHA256"); signer.update(signedInfo); const res = signer.sign(privateKey, "base64"); return res; }, ); verifySignature = createOptionalCallbackFunction( (material: string, key: crypto.KeyLike, signatureValue: string): boolean => { const verifier = crypto.createVerify("RSA-SHA256"); verifier.update(material); const res = verifier.verify(key, signatureValue, "base64"); return res; }, ); getAlgorithmName = () => { return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; }; } export class RsaSha256Mgf1 implements SignatureAlgorithm { getSignature = createOptionalCallbackFunction( (signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike): string => { if (!(typeof privateKey === "string" || Buffer.isBuffer(privateKey))) { throw new Error("keys must be strings or buffers"); } const signer = crypto.createSign("RSA-SHA256"); signer.update(signedInfo); const res = signer.sign( { key: privateKey, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST, }, "base64", ); return res; }, ); verifySignature = createOptionalCallbackFunction( (material: string, key: crypto.KeyLike, signatureValue: string): boolean => { if (!(typeof key === "string" || Buffer.isBuffer(key))) { throw new Error("keys must be strings or buffers"); } const verifier = crypto.createVerify("RSA-SHA256"); verifier.update(material); const res = verifier.verify( { key: key, padding: crypto.constants.RSA_PKCS1_PSS_PADDING, saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST, }, signatureValue, "base64", ); return res; }, ); getAlgorithmName = () => { return "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1"; }; } export class RsaSha512 implements SignatureAlgorithm { getSignature = createOptionalCallbackFunction( (signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike): string => { const signer = crypto.createSign("RSA-SHA512"); signer.update(signedInfo); const res = signer.sign(privateKey, "base64"); return res; }, ); verifySignature = createOptionalCallbackFunction( (material: string, key: crypto.KeyLike, signatureValue: string): boolean => { const verifier = crypto.createVerify("RSA-SHA512"); verifier.update(material); const res = verifier.verify(key, signatureValue, "base64"); return res; }, ); getAlgorithmName = () => { return "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512"; }; } export class HmacSha1 implements SignatureAlgorithm { getSignature = createOptionalCallbackFunction( (signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike): string => { const signer = crypto.createHmac("SHA1", privateKey); signer.update(signedInfo); const res = signer.digest("base64"); return res; }, ); verifySignature = createOptionalCallbackFunction( (material: string, key: crypto.KeyLike, signatureValue: string): boolean => { const verifier = crypto.createHmac("SHA1", key); verifier.update(material); const res = verifier.digest("base64"); // Use constant-time comparison to prevent timing attacks (CWE-208) // See: https://github.com/node-saml/xml-crypto/issues/522 try { return crypto.timingSafeEqual( Buffer.from(res, "base64"), Buffer.from(signatureValue, "base64"), ); } catch (e) { // timingSafeEqual throws if buffer lengths don't match return false; } }, ); getAlgorithmName = () => { return "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; }; } ================================================ FILE: src/signed-xml.ts ================================================ import type { CanonicalizationAlgorithmType, CanonicalizationOrTransformAlgorithmType, CanonicalizationOrTransformationAlgorithm, CanonicalizationOrTransformationAlgorithmProcessOptions, ComputeSignatureOptions, ErrorFirstCallback, GetKeyInfoContentArgs, HashAlgorithm, HashAlgorithmType, ObjectAttributes, Reference, SignatureAlgorithm, SignatureAlgorithmType, SignedXmlOptions, } from "./types"; import * as isDomNode from "@xmldom/is-dom-node"; import * as xmldom from "@xmldom/xmldom"; import * as crypto from "crypto"; import { deprecate } from "util"; import * as xpath from "xpath"; import * as c14n from "./c14n-canonicalization"; import * as envelopedSignatures from "./enveloped-signature"; import * as execC14n from "./exclusive-canonicalization"; import * as hashAlgorithms from "./hash-algorithms"; import * as signatureAlgorithms from "./signature-algorithms"; import * as utils from "./utils"; export class SignedXml { idMode?: "wssecurity"; idAttributes: string[]; /** * A {@link Buffer} or pem encoded {@link String} containing your private key */ privateKey?: crypto.KeyLike; publicCert?: crypto.KeyLike; /** * One of the supported signature algorithms. * @see {@link SignatureAlgorithmType} */ signatureAlgorithm?: SignatureAlgorithmType = undefined; /** * Rules used to convert an XML document into its canonical form. */ canonicalizationAlgorithm?: CanonicalizationAlgorithmType = undefined; /** * It specifies a list of namespace prefixes that should be considered "inclusive" during the canonicalization process. */ inclusiveNamespacesPrefixList: string[] = []; namespaceResolver: XPathNSResolver = { lookupNamespaceURI: function (/* prefix */) { throw new Error("Not implemented"); }, }; implicitTransforms: ReadonlyArray = []; keyInfoAttributes: { [attrName: string]: string } = {}; getKeyInfoContent = SignedXml.getKeyInfoContent; getCertFromKeyInfo = SignedXml.getCertFromKeyInfo; objects?: Array<{ content: string; attributes?: ObjectAttributes }>; // Internal state private id = 0; private signedXml = ""; private signatureXml = ""; private signatureNode: Node | null = null; private signatureValue = ""; private originalXmlWithIds = ""; private keyInfo: Node | null = null; /** * Contains the references that were signed. * @see {@link Reference} */ private references: Reference[] = []; /** * Contains the canonicalized XML of the references that were validly signed. * * This populates with the canonical XML of the reference only after * verifying the signature is cryptographically authentic. */ private signedReferences: string[] = []; /** * 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} */ CanonicalizationAlgorithms: Record< CanonicalizationOrTransformAlgorithmType, new () => CanonicalizationOrTransformationAlgorithm > = { "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, "http://www.w3.org/2001/10/xml-exc-c14n#": execC14n.ExclusiveCanonicalization, "http://www.w3.org/2001/10/xml-exc-c14n#WithComments": execC14n.ExclusiveCanonicalizationWithComments, "http://www.w3.org/2000/09/xmldsig#enveloped-signature": envelopedSignatures.EnvelopedSignature, }; // TODO: In v7.x we may consider deprecating sha1 /** * 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} */ HashAlgorithms: Record HashAlgorithm> = { "http://www.w3.org/2000/09/xmldsig#sha1": hashAlgorithms.Sha1, "http://www.w3.org/2001/04/xmlenc#sha256": hashAlgorithms.Sha256, "http://www.w3.org/2001/04/xmlenc#sha512": hashAlgorithms.Sha512, }; // TODO: In v7.x we may consider deprecating sha1 /** * 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} */ SignatureAlgorithms: Record SignatureAlgorithm> = { "http://www.w3.org/2000/09/xmldsig#rsa-sha1": signatureAlgorithms.RsaSha1, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256": signatureAlgorithms.RsaSha256, "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1": signatureAlgorithms.RsaSha256Mgf1, "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512": signatureAlgorithms.RsaSha512, // Disabled by default due to key confusion concerns. // 'http://www.w3.org/2000/09/xmldsig#hmac-sha1': SignatureAlgorithms.HmacSha1 }; static defaultNsForPrefix = { ds: "http://www.w3.org/2000/09/xmldsig#", }; static noop = () => null; /** * The SignedXml constructor provides an abstraction for sign and verify xml documents. The object is constructed using * @param options {@link SignedXmlOptions} */ constructor(options: SignedXmlOptions = {}) { const { idMode, idAttribute, privateKey, publicCert, signatureAlgorithm, canonicalizationAlgorithm, inclusiveNamespacesPrefixList, implicitTransforms, keyInfoAttributes, getKeyInfoContent, getCertFromKeyInfo, objects, } = options; // Options this.idMode = idMode; this.idAttributes = ["Id", "ID", "id"]; if (idAttribute) { this.idAttributes.unshift(idAttribute); } this.privateKey = privateKey; this.publicCert = publicCert; this.signatureAlgorithm = signatureAlgorithm ?? this.signatureAlgorithm; this.canonicalizationAlgorithm = canonicalizationAlgorithm; if (typeof inclusiveNamespacesPrefixList === "string") { this.inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList.split(" "); } else if (utils.isArrayHasLength(inclusiveNamespacesPrefixList)) { this.inclusiveNamespacesPrefixList = inclusiveNamespacesPrefixList; } this.implicitTransforms = implicitTransforms ?? this.implicitTransforms; this.keyInfoAttributes = keyInfoAttributes ?? this.keyInfoAttributes; this.getKeyInfoContent = getKeyInfoContent ?? this.getKeyInfoContent; this.getCertFromKeyInfo = getCertFromKeyInfo ?? SignedXml.noop; this.objects = objects; this.CanonicalizationAlgorithms; this.HashAlgorithms; this.SignatureAlgorithms; } /** * Due to key-confusion issues, it's risky to have both hmac * and digital signature algorithms enabled at the same time. * This enables HMAC and disables other signing algorithms. */ enableHMAC(): void { this.SignatureAlgorithms = { "http://www.w3.org/2000/09/xmldsig#hmac-sha1": signatureAlgorithms.HmacSha1, }; this.getKeyInfoContent = SignedXml.noop; } /** * 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 "" * * @return an XML string representation of the contents of a KeyInfo element, or `null` if no `KeyInfo` element should be included */ static getKeyInfoContent({ publicCert, prefix }: GetKeyInfoContentArgs): string | null { if (publicCert == null) { return null; } prefix = prefix ? `${prefix}:` : ""; let x509Certs = ""; if (Buffer.isBuffer(publicCert)) { publicCert = publicCert.toString("latin1"); } let publicCertMatches: string[] = []; if (typeof publicCert === "string") { publicCertMatches = publicCert.match(utils.EXTRACT_X509_CERTS) || []; } if (publicCertMatches.length > 0) { x509Certs = publicCertMatches .map( (c) => `<${prefix}X509Certificate>${utils .pemToDer(c) .toString("base64")}`, ) .join(""); } return `<${prefix}X509Data>${x509Certs}`; } /** * Returns the value of the signing certificate based on the contents of the * specified KeyInfo. * * @param keyInfo 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 */ static getCertFromKeyInfo(keyInfo?: Node | null): string | null { if (keyInfo != null) { const cert = xpath.select1(".//*[local-name(.)='X509Certificate']", keyInfo); if (isDomNode.isNodeLike(cert)) { return utils.derToPem(cert.textContent ?? "", "CERTIFICATE"); } } return null; } /** * Validates the signature of the provided XML document synchronously using the configured key info provider. * * @param xml The XML document containing the signature to be validated. * @returns `true` if the signature is valid * @throws Error if no key info resolver is provided. */ checkSignature(xml: string): boolean; /** * Validates the signature of the provided XML document synchronously using the configured key info provider. * * @param xml The XML document containing the signature to be validated. * @param callback Callback function to handle the validation result asynchronously. * @throws Error if the last parameter is provided and is not a function, or if no key info resolver is provided. */ checkSignature(xml: string, callback: (error: Error | null, isValid?: boolean) => void): void; checkSignature( xml: string, callback?: (error: Error | null, isValid?: boolean) => void, ): unknown { if (callback != null && typeof callback !== "function") { throw new Error("Last parameter must be a callback function"); } this.signedXml = xml; const doc = new xmldom.DOMParser().parseFromString(xml); // Reset the references as only references from our re-parsed signedInfo node can be trusted this.references = []; const unverifiedSignedInfoCanon = this.getCanonSignedInfoXml(doc); if (!unverifiedSignedInfoCanon) { if (callback) { callback(new Error("Canonical signed info cannot be empty"), false); return; } throw new Error("Canonical signed info cannot be empty"); } // unsigned, verify later to keep with consistent callback behavior const parsedUnverifiedSignedInfo = new xmldom.DOMParser().parseFromString( unverifiedSignedInfoCanon, "text/xml", ); const unverifiedSignedInfoDoc = parsedUnverifiedSignedInfo.documentElement; if (!unverifiedSignedInfoDoc) { if (callback) { callback(new Error("Could not parse unverifiedSignedInfoCanon into a document"), false); return; } throw new Error("Could not parse unverifiedSignedInfoCanon into a document"); } const references = utils.findChildren(unverifiedSignedInfoDoc, "Reference"); if (!utils.isArrayHasLength(references)) { if (callback) { callback(new Error("could not find any Reference elements"), false); return; } throw new Error("could not find any Reference elements"); } // TODO: In a future release we'd like to load the Signature and its References at the same time, // however, in the `.loadSignature()` method we don't have the entire document, // which we need to to keep the inclusive namespaces for (const reference of references) { this.loadReference(reference); } /* eslint-disable-next-line deprecation/deprecation */ if (!this.getReferences().every((ref) => this.validateReference(ref, doc))) { /* Trustworthiness can only be determined if SignedInfo's (which holds References' DigestValue(s) which were validated at this stage) signature is valid. Execution does not proceed to validate signature phase thus each References' DigestValue must be considered to be untrusted (attacker might have injected any data with new new references and/or recalculated new DigestValue for altered Reference(s)). Returning any content via `signedReferences` would give false sense of trustworthiness if/when SignedInfo's (which holds references' DigestValues) signature is not valid(ated). Put simply: if one fails, they are all not trustworthy. */ this.signedReferences = []; this.references.forEach((ref) => { ref.signedReference = undefined; }); // TODO: add this breaking change here later on for even more security: `this.references = [];` if (callback) { callback(new Error("Could not validate all references"), false); return; } // We return false because some references validated, but not all // We should actually be throwing an error here, but that would be a breaking change // See https://www.w3.org/TR/xmldsig-core/#sec-CoreValidation return false; } // (Stage B authentication step, show that the `signedInfoCanon` is signed) // First find the key & signature algorithm, these should match // Stage B: Take the signature algorithm and key and verify the `SignatureValue` against the canonicalized `SignedInfo` const signer = this.findSignatureAlgorithm(this.signatureAlgorithm); const key = this.getCertFromKeyInfo(this.keyInfo) || this.publicCert || this.privateKey; if (key == null) { throw new Error("KeyInfo or publicCert or privateKey is required to validate signature"); } // Check the signature verification to know whether to reset signature value or not. const sigRes = signer.verifySignature(unverifiedSignedInfoCanon, key, this.signatureValue); if (sigRes === true) { if (callback) { callback(null, true); } else { return true; } } else { // Ideally, we would start by verifying the `signedInfoCanon` first, // but that may cause some breaking changes, so we'll handle that in v7.x. // If we were validating `signedInfoCanon` first, we wouldn't have to reset this array. this.signedReferences = []; this.references.forEach((ref) => { ref.signedReference = undefined; }); // TODO: add this breaking change here later on for even more security: `this.references = [];` if (callback) { callback( new Error(`invalid signature: the signature value ${this.signatureValue} is incorrect`), ); return; // return early } else { throw new Error( `invalid signature: the signature value ${this.signatureValue} is incorrect`, ); } } } private getCanonSignedInfoXml(doc: Document) { if (this.signatureNode == null) { throw new Error("No signature found."); } if (typeof this.canonicalizationAlgorithm !== "string") { throw new Error("Missing canonicalizationAlgorithm when trying to get signed info for XML"); } const signedInfo = utils.findChildren(this.signatureNode, "SignedInfo"); if (signedInfo.length === 0) { throw new Error("could not find SignedInfo element in the message"); } if (signedInfo.length > 1) { throw new Error( "could not get canonicalized signed info for a signature that contains multiple SignedInfo nodes", ); } if ( this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" || this.canonicalizationAlgorithm === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" ) { if (!doc || typeof doc !== "object") { throw new Error( "When canonicalization method is non-exclusive, whole xml dom must be provided as an argument", ); } } /** * Search for ancestor namespaces before canonicalization. */ const ancestorNamespaces = utils.findAncestorNs(doc, "//*[local-name()='SignedInfo']"); const c14nOptions = { ancestorNamespaces: ancestorNamespaces, }; return this.getCanonXml([this.canonicalizationAlgorithm], signedInfo[0], c14nOptions); } private getCanonReferenceXml(doc: Document, ref: Reference, node: Node) { /** * Search for ancestor namespaces before canonicalization. */ if (Array.isArray(ref.transforms)) { ref.ancestorNamespaces = utils.findAncestorNs(doc, ref.xpath, this.namespaceResolver); } const c14nOptions = { inclusiveNamespacesPrefixList: ref.inclusiveNamespacesPrefixList, ancestorNamespaces: ref.ancestorNamespaces, }; return this.getCanonXml(ref.transforms, node, c14nOptions); } private calculateSignatureValue(doc: Document, callback?: ErrorFirstCallback) { const signedInfoCanon = this.getCanonSignedInfoXml(doc); const signer = this.findSignatureAlgorithm(this.signatureAlgorithm); if (this.privateKey == null) { throw new Error("Private key is required to compute signature"); } if (typeof callback === "function") { signer.getSignature(signedInfoCanon, this.privateKey, callback); } else { this.signatureValue = signer.getSignature(signedInfoCanon, this.privateKey); } } private findSignatureAlgorithm(name?: SignatureAlgorithmType) { if (name == null) { throw new Error("signatureAlgorithm is required"); } const algo = this.SignatureAlgorithms[name]; if (algo) { return new algo(); } else { throw new Error(`signature algorithm '${name}' is not supported`); } } private findCanonicalizationAlgorithm(name: CanonicalizationOrTransformAlgorithmType) { if (name != null) { const algo = this.CanonicalizationAlgorithms[name]; if (algo) { return new algo(); } } throw new Error(`canonicalization algorithm '${name}' is not supported`); } private findHashAlgorithm(name: HashAlgorithmType) { const algo = this.HashAlgorithms[name]; if (algo) { return new algo(); } else { throw new Error(`hash algorithm '${name}' is not supported`); } } validateElementAgainstReferences(elemOrXpath: Element | string, doc: Document): Reference { let elem: Element; if (typeof elemOrXpath === "string") { const firstElem = xpath.select1(elemOrXpath, doc); isDomNode.assertIsElementNode(firstElem); elem = firstElem; } else { elem = elemOrXpath; } /* eslint-disable-next-line deprecation/deprecation */ for (const ref of this.getReferences()) { const uri = ref.uri?.[0] === "#" ? ref.uri.substring(1) : ref.uri; for (const attr of this.idAttributes) { const elemId = elem.getAttribute(attr); if (uri === elemId) { ref.xpath = `//*[@*[local-name(.)='${attr}']='${uri}']`; break; // found the correct element, no need to check further } } const canonXml = this.getCanonReferenceXml(doc, ref, elem); const hash = this.findHashAlgorithm(ref.digestAlgorithm); const digest = hash.getHash(canonXml); if (utils.validateDigestValue(digest, ref.digestValue)) { return ref; } } throw new Error("No references passed validation"); } private validateReference(ref: Reference, doc: Document) { const uri = ref.uri?.[0] === "#" ? ref.uri.substring(1) : ref.uri; let elem: xpath.SelectSingleReturnType = null; if (uri === "") { elem = xpath.select1("//*", doc); } else if (uri?.indexOf("'") !== -1) { // xpath injection throw new Error("Cannot validate a uri with quotes inside it"); } else { let num_elements_for_id = 0; for (const attr of this.idAttributes) { const tmp_elemXpath = `//*[@*[local-name(.)='${attr}']='${uri}']`; const tmp_elem = xpath.select(tmp_elemXpath, doc); if (utils.isArrayHasLength(tmp_elem)) { num_elements_for_id += tmp_elem.length; if (num_elements_for_id > 1) { throw new Error( "Cannot validate a document which contains multiple elements with the " + "same value for the ID / Id / Id attributes, in order to prevent " + "signature wrapping attack.", ); } elem = tmp_elem[0]; ref.xpath = tmp_elemXpath; } } } ref.getValidatedNode = deprecate((xpathSelector?: string) => { xpathSelector = xpathSelector || ref.xpath; if (typeof xpathSelector !== "string" || ref.validationError != null) { return null; } const selectedValue = xpath.select1(xpathSelector, doc); return isDomNode.isNodeLike(selectedValue) ? selectedValue : null; }, "`ref.getValidatedNode()` is deprecated and insecure. Use `ref.signedReference` or `this.getSignedReferences()` instead."); if (!isDomNode.isNodeLike(elem)) { const validationError = new Error( `invalid signature: the signature references an element with uri ${ref.uri} but could not find such element in the xml`, ); ref.validationError = validationError; return false; } const canonXml = this.getCanonReferenceXml(doc, ref, elem); const hash = this.findHashAlgorithm(ref.digestAlgorithm); const digest = hash.getHash(canonXml); if (!utils.validateDigestValue(digest, ref.digestValue)) { const validationError = new Error( `invalid signature: for uri ${ref.uri} calculated digest is ${digest} but the xml to validate supplies digest ${ref.digestValue}`, ); ref.validationError = validationError; return false; } // This step can only be done after we have verified the `signedInfo`. // We verified that they have same hash, // thus the `canonXml` and _only_ the `canonXml` can be trusted. // Append this to `signedReferences`. this.signedReferences.push(canonXml); ref.signedReference = canonXml; return true; } findSignatures(doc: Node): Node[] { const nodes = xpath.select( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); return isDomNode.isArrayOfNodes(nodes) ? nodes : []; } /** * Loads the signature information from the provided XML node or string. * * @param signatureNode The XML node or string representing the signature. */ loadSignature(signatureNode: Node | string): void { if (typeof signatureNode === "string") { this.signatureNode = signatureNode = new xmldom.DOMParser().parseFromString(signatureNode); } else { this.signatureNode = signatureNode; } this.signatureXml = signatureNode.toString(); const node = xpath.select1( ".//*[local-name(.)='CanonicalizationMethod']/@Algorithm", signatureNode, ); if (!isDomNode.isNodeLike(node)) { throw new Error("could not find CanonicalizationMethod/@Algorithm element"); } if (isDomNode.isAttributeNode(node)) { this.canonicalizationAlgorithm = node.value as CanonicalizationAlgorithmType; } const signatureAlgorithm = xpath.select1( ".//*[local-name(.)='SignatureMethod']/@Algorithm", signatureNode, ); if (isDomNode.isAttributeNode(signatureAlgorithm)) { this.signatureAlgorithm = signatureAlgorithm.value as SignatureAlgorithmType; } const signedInfoNodes = utils.findChildren(this.signatureNode, "SignedInfo"); if (!utils.isArrayHasLength(signedInfoNodes)) { throw new Error("no signed info node found"); } if (signedInfoNodes.length > 1) { throw new Error("could not load signature that contains multiple SignedInfo nodes"); } // Try to operate on the c14n version of `signedInfo`. This forces the initial `getReferences()` // API call to always return references that are loaded under the canonical `SignedInfo` // in the case that the client access the `.references` **before** signature verification. // Ensure canonicalization algorithm is exclusive, otherwise we'd need the entire document let canonicalizationAlgorithmForSignedInfo = this.canonicalizationAlgorithm; if ( !canonicalizationAlgorithmForSignedInfo || canonicalizationAlgorithmForSignedInfo === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" || canonicalizationAlgorithmForSignedInfo === "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" ) { canonicalizationAlgorithmForSignedInfo = "http://www.w3.org/2001/10/xml-exc-c14n#"; } const temporaryCanonSignedInfo = this.getCanonXml( [canonicalizationAlgorithmForSignedInfo], signedInfoNodes[0], ); const temporaryCanonSignedInfoXml = new xmldom.DOMParser().parseFromString( temporaryCanonSignedInfo, "text/xml", ); const signedInfoDoc = temporaryCanonSignedInfoXml.documentElement; this.references = []; const references = utils.findChildren(signedInfoDoc, "Reference"); if (!utils.isArrayHasLength(references)) { throw new Error("could not find any Reference elements"); } for (const reference of references) { this.loadReference(reference); } const signatureValue = xpath.select1( ".//*[local-name(.)='SignatureValue']/text()", signatureNode, ); if (isDomNode.isTextNode(signatureValue)) { this.signatureValue = signatureValue.data.replace(/\r?\n/g, ""); } const keyInfo = xpath.select1(".//*[local-name(.)='KeyInfo']", signatureNode); if (isDomNode.isNodeLike(keyInfo)) { this.keyInfo = keyInfo; } } /** * Load the reference xml node to a model * */ private loadReference(refNode: Node) { let nodes = utils.findChildren(refNode, "DigestMethod"); if (nodes.length === 0) { throw new Error(`could not find DigestMethod in reference ${refNode.toString()}`); } const digestAlgoNode = nodes[0]; const attr = utils.findAttr(digestAlgoNode, "Algorithm"); if (!attr) { throw new Error(`could not find Algorithm attribute in node ${digestAlgoNode.toString()}`); } const digestAlgo = attr.value; nodes = utils.findChildren(refNode, "DigestValue"); if (nodes.length === 0) { throw new Error(`could not find DigestValue node in reference ${refNode.toString()}`); } if (nodes.length > 1) { throw new Error( `could not load reference for a node that contains multiple DigestValue nodes: ${refNode.toString()}`, ); } const digestValue = nodes[0].textContent; if (!digestValue) { throw new Error(`could not find the value of DigestValue in ${refNode.toString()}`); } const transforms: string[] = []; let inclusiveNamespacesPrefixList: string[] = []; nodes = utils.findChildren(refNode, "Transforms"); if (nodes.length !== 0) { const transformsNode = nodes[0]; const transformsAll = utils.findChildren(transformsNode, "Transform"); for (const transform of transformsAll) { const transformAttr = utils.findAttr(transform, "Algorithm"); if (transformAttr) { transforms.push(transformAttr.value); } } // This is a little strange, we are looking for children of the last child of `transformsNode` const inclusiveNamespaces = utils.findChildren( transformsAll[transformsAll.length - 1], "InclusiveNamespaces", ); if (utils.isArrayHasLength(inclusiveNamespaces)) { // Should really only be one prefix list, but maybe there's some circumstances where more than one to let's handle it inclusiveNamespacesPrefixList = inclusiveNamespaces .flatMap((namespace) => (namespace.getAttribute("PrefixList") ?? "").split(" ")) .filter((value) => value.length > 0); } } if (utils.isArrayHasLength(this.implicitTransforms)) { this.implicitTransforms.forEach(function (t) { transforms.push(t); }); } /** * DigestMethods take an octet stream rather than a node set. If the output of the last transform is a node set, we * need to canonicalize the node set to an octet stream using non-exclusive canonicalization. If there are no * transforms, we need to canonicalize because URI dereferencing for a same-document reference will return a node-set. * @see: * https://www.w3.org/TR/xmldsig-core1/#sec-DigestMethod * https://www.w3.org/TR/xmldsig-core1/#sec-ReferenceProcessingModel * https://www.w3.org/TR/xmldsig-core1/#sec-Same-Document */ if ( transforms.length === 0 || transforms[transforms.length - 1] === "http://www.w3.org/2000/09/xmldsig#enveloped-signature" ) { transforms.push("http://www.w3.org/TR/2001/REC-xml-c14n-20010315"); } const refUri = isDomNode.isElementNode(refNode) ? refNode.getAttribute("URI") || undefined : undefined; this.addReference({ transforms, digestAlgorithm: digestAlgo, uri: refUri, digestValue, inclusiveNamespacesPrefixList, isEmptyUri: false, }); } /** * Adds a reference to the signature. * * @param xpath The XPath expression to select the XML nodes to be referenced. * @param transforms An array of transform algorithms to be applied to the selected nodes. * @param digestAlgorithm The digest algorithm to use for computing the digest value. * @param uri The URI identifier for the reference. If empty, an empty URI will be used. * @param digestValue The expected digest value for the reference. * @param inclusiveNamespacesPrefixList The prefix list for inclusive namespace canonicalization. * @param isEmptyUri Indicates whether the URI is empty. Defaults to `false`. * @param id An optional `Id` attribute for the reference. * @param type An optional `Type` attribute for the reference. */ addReference({ xpath, transforms, digestAlgorithm, uri = "", digestValue, inclusiveNamespacesPrefixList = [], isEmptyUri = false, id = undefined, type = undefined, }: Partial & Pick): void { if (digestAlgorithm == null) { throw new Error("digestAlgorithm is required"); } if (!utils.isArrayHasLength(transforms)) { throw new Error("transforms must contain at least one transform algorithm"); } this.references.push({ xpath, transforms, digestAlgorithm, uri, digestValue, inclusiveNamespacesPrefixList, isEmptyUri, id, type, getValidatedNode: () => { throw new Error( "Reference has not been validated yet; Did you call `sig.checkSignature()`?", ); }, }); } /** * Returns the list of references. */ getReferences() { // TODO: Refactor once `getValidatedNode` is removed /* Once we completely remove the deprecated `getValidatedNode()` method, we can change this to return a clone to prevent accidental mutations, e.g.: return [...this.references]; */ return this.references; } getSignedReferences() { return [...this.signedReferences]; } /** * Compute the signature of the given XML (using the already defined settings). * * @param xml The XML to compute the signature for. * @param callback A callback function to handle the signature computation asynchronously. * @returns void * @throws TypeError If the xml can not be parsed. */ computeSignature(xml: string): void; /** * Compute the signature of the given XML (using the already defined settings). * * @param xml The XML to compute the signature for. * @param callback A callback function to handle the signature computation asynchronously. * @returns void * @throws TypeError If the xml can not be parsed. */ computeSignature(xml: string, callback: ErrorFirstCallback): void; /** * Compute the signature of the given XML (using the already defined settings). * * @param xml The XML to compute the signature for. * @param opts An object containing options for the signature computation. * @returns If no callback is provided, returns `this` (the instance of SignedXml). * @throws TypeError If the xml can not be parsed, or Error if there were invalid options passed. */ computeSignature(xml: string, options: ComputeSignatureOptions): void; /** * Compute the signature of the given XML (using the already defined settings). * * @param xml The XML to compute the signature for. * @param opts An object containing options for the signature computation. * @param callback A callback function to handle the signature computation asynchronously. * @returns void * @throws TypeError If the xml can not be parsed, or Error if there were invalid options passed. */ computeSignature( xml: string, options: ComputeSignatureOptions, callback: ErrorFirstCallback, ): void; computeSignature( xml: string, options?: ComputeSignatureOptions | ErrorFirstCallback, callbackParam?: ErrorFirstCallback, ): void { let callback: ErrorFirstCallback; if (typeof options === "function" && callbackParam == null) { callback = options as ErrorFirstCallback; options = {} as ComputeSignatureOptions; } else { callback = callbackParam as ErrorFirstCallback; options = (options ?? {}) as ComputeSignatureOptions; } const doc = new xmldom.DOMParser().parseFromString(xml); let xmlNsAttr = "xmlns"; const signatureAttrs: string[] = []; let currentPrefix: string; const validActions = ["append", "prepend", "before", "after"]; const prefix = options.prefix; const attrs = options.attrs || {}; const location = options.location || {}; const existingPrefixes = options.existingPrefixes || {}; this.namespaceResolver = { lookupNamespaceURI: function (prefix) { return prefix ? existingPrefixes[prefix] : null; }, }; // defaults to the root node location.reference = location.reference || "/*"; // defaults to append action location.action = location.action || "append"; if (validActions.indexOf(location.action) === -1) { const err = new Error( `location.action option has an invalid action: ${ location.action }, must be any of the following values: ${validActions.join(", ")}`, ); if (!callback) { throw err; } else { callback(err); return; } } // Add IDs for all non-self references upfront for (const ref of this.getReferences()) { if (ref.isEmptyUri) { continue; } // No specific nodes to ID for empty URI const nodes = xpath.selectWithResolver( ref.xpath ?? "", doc, this.namespaceResolver, ) as Element[]; for (const node of nodes) { isDomNode.assertIsElementNode(node); this.ensureHasId(node); } } // Capture original with IDs (no sig yet) this.originalXmlWithIds = doc.toString(); // automatic insertion of `:` if (prefix) { xmlNsAttr += `:${prefix}`; currentPrefix = `${prefix}:`; } else { currentPrefix = ""; } Object.keys(attrs).forEach(function (name) { if (name !== "xmlns" && name !== xmlNsAttr) { signatureAttrs.push(`${name}="${attrs[name]}"`); } }); // add the xml namespace attribute signatureAttrs.push(`${xmlNsAttr}="http://www.w3.org/2000/09/xmldsig#"`); let signatureXml = `<${currentPrefix}Signature ${signatureAttrs.join(" ")}>`; signatureXml += this.createSignedInfo(doc, prefix); signatureXml += this.getKeyInfo(prefix); signatureXml += this.getObjects(prefix); signatureXml += ``; let existingPrefixesString = ""; Object.keys(existingPrefixes).forEach(function (key) { existingPrefixesString += `xmlns:${key}="${existingPrefixes[key]}" `; }); // A trick to remove the namespaces that already exist in the xml // This only works if the prefix and namespace match with those in the xml const dummySignatureWrapper = `${signatureXml}`; const nodeXml = new xmldom.DOMParser().parseFromString(dummySignatureWrapper); // Because we are using a dummy wrapper hack described above, we know there will be a `firstChild` // and that it will be an `Element` node. // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const signatureElem = nodeXml.documentElement.firstChild! as Element; const referenceNode = xpath.select1(location.reference, doc); if (!isDomNode.isNodeLike(referenceNode)) { const err2 = new Error( `the following xpath cannot be used because it was not found: ${location.reference}`, ); if (!callback) { throw err2; } else { callback(err2); return; } } if (location.action === "append") { referenceNode.appendChild(signatureElem); } else if (location.action === "prepend") { referenceNode.insertBefore(signatureElem, referenceNode.firstChild); } else if (location.action === "before") { if (referenceNode.parentNode == null) { throw new Error( "`location.reference` refers to the root node (by default), so we can't insert `before`", ); } referenceNode.parentNode.insertBefore(signatureElem, referenceNode); } else if (location.action === "after") { if (referenceNode.parentNode == null) { throw new Error( "`location.reference` refers to the root node (by default), so we can't insert `after`", ); } referenceNode.parentNode.insertBefore(signatureElem, referenceNode.nextSibling); } // Now add all references (including any to the signature itself) this.addAllReferences(doc, signatureElem, prefix); this.signatureNode = signatureElem; const signedInfoNodes = utils.findChildren(this.signatureNode, "SignedInfo"); if (signedInfoNodes.length === 0) { const err3 = new Error("could not find SignedInfo element in the message"); if (!callback) { throw err3; } else { callback(err3); return; } } const signedInfoNode = signedInfoNodes[0]; if (typeof callback === "function") { // Asynchronous flow this.calculateSignatureValue(doc, (err, signature) => { if (err) { callback(err); } else { this.signatureValue = signature || ""; signatureElem.insertBefore(this.createSignature(prefix), signedInfoNode.nextSibling); this.signatureXml = signatureElem.toString(); this.signedXml = doc.toString(); callback(null, this); } }); } else { // Synchronous flow this.calculateSignatureValue(doc); signatureElem.insertBefore(this.createSignature(prefix), signedInfoNode.nextSibling); this.signatureXml = signatureElem.toString(); this.signedXml = doc.toString(); } } /** * Adds all references to the SignedInfo after the signature placeholder is inserted. */ private addAllReferences(doc: Document, signatureElem: Element, prefix?: string): void { if (!utils.isArrayHasLength(this.references)) { return; } const currentPrefix = prefix ? `${prefix}:` : ""; const signatureNamespace = "http://www.w3.org/2000/09/xmldsig#"; // Find the SignedInfo element to append to const signedInfoNode = xpath.select1(`./*[local-name(.)='SignedInfo']`, signatureElem); isDomNode.assertIsElementNode(signedInfoNode); // Type-safe assertion // Signature document is technically the same document as the one we are signing, // but we will extract it here for clarity (and also make it support detached signatures in the future) const signatureDoc = signatureElem.ownerDocument; // Process each reference for (const ref of this.getReferences()) { const nodes = xpath.selectWithResolver(ref.xpath ?? "", doc, this.namespaceResolver); if (!utils.isArrayHasLength(nodes)) { throw new Error( `the following xpath cannot be signed because it was not found: ${ref.xpath}`, ); } // Process the reference for (const node of nodes) { isDomNode.assertIsElementNode(node); // Must not be a reference to Signature, SignedInfo, or a child of SignedInfo if ( node === signatureElem || node === signedInfoNode || utils.isDescendantOf(node, signedInfoNode) ) { throw new Error( `Cannot sign a reference to the Signature or SignedInfo element itself: ${ref.xpath}`, ); } // Compute the target URI (ID already ensured earlier, extract it) let targetUri: string; if (ref.isEmptyUri) { targetUri = ""; } else { const id = this.ensureHasId(node); ref.uri = id; targetUri = `#${id}`; } // Create the reference element directly using DOM methods to avoid namespace issues const referenceElem = signatureDoc.createElementNS( signatureNamespace, `${currentPrefix}Reference`, ); referenceElem.setAttribute("URI", targetUri); if (ref.id) { referenceElem.setAttribute("Id", ref.id); } if (ref.type) { referenceElem.setAttribute("Type", ref.type); } const transformsElem = signatureDoc.createElementNS( signatureNamespace, `${currentPrefix}Transforms`, ); for (const trans of ref.transforms || []) { const transform = this.findCanonicalizationAlgorithm(trans); const transformElem = signatureDoc.createElementNS( signatureNamespace, `${currentPrefix}Transform`, ); transformElem.setAttribute("Algorithm", transform.getAlgorithmName()); if (utils.isArrayHasLength(ref.inclusiveNamespacesPrefixList)) { const inclusiveNamespacesElem = signatureDoc.createElementNS( transform.getAlgorithmName(), "InclusiveNamespaces", ); inclusiveNamespacesElem.setAttribute( "PrefixList", ref.inclusiveNamespacesPrefixList.join(" "), ); transformElem.appendChild(inclusiveNamespacesElem); } transformsElem.appendChild(transformElem); } // Get the canonicalized XML const canonXml = this.getCanonReferenceXml(doc, ref, node); // Get the digest algorithm and compute the digest value const digestAlgorithm = this.findHashAlgorithm(ref.digestAlgorithm); const digestMethodElem = signatureDoc.createElementNS( signatureNamespace, `${currentPrefix}DigestMethod`, ); digestMethodElem.setAttribute("Algorithm", digestAlgorithm.getAlgorithmName()); const digestValueElem = signatureDoc.createElementNS( signatureNamespace, `${currentPrefix}DigestValue`, ); digestValueElem.textContent = digestAlgorithm.getHash(canonXml); referenceElem.appendChild(transformsElem); referenceElem.appendChild(digestMethodElem); referenceElem.appendChild(digestValueElem); // Append the reference element to SignedInfo signedInfoNode.appendChild(referenceElem); } } } private getKeyInfo(prefix) { const currentPrefix = prefix ? `${prefix}:` : ""; let keyInfoAttrs = ""; if (this.keyInfoAttributes) { Object.keys(this.keyInfoAttributes).forEach((name) => { keyInfoAttrs += ` ${name}="${this.keyInfoAttributes[name]}"`; }); } const keyInfoContent = this.getKeyInfoContent({ publicCert: this.publicCert, prefix }); if (keyInfoAttrs || keyInfoContent) { return `<${currentPrefix}KeyInfo${keyInfoAttrs}>${keyInfoContent}`; } return ""; } /** * Creates XML for Object elements to be included in the signature * * @param prefix Optional namespace prefix * @returns XML string with Object elements or empty string if none */ private getObjects(prefix?: string) { const currentPrefix = prefix ? `${prefix}:` : ""; if (!this.objects || this.objects.length === 0) { return ""; } let result = ""; for (const obj of this.objects) { let objectAttrs = ""; if (obj.attributes) { Object.keys(obj.attributes).forEach((name) => { const value = obj.attributes?.[name]; if (value !== undefined) { objectAttrs += ` ${name}="${value}"`; } }); } result += `<${currentPrefix}Object${objectAttrs}>${obj.content}`; } return result; } getCanonXml( transforms: Reference["transforms"], node: Node, options: CanonicalizationOrTransformationAlgorithmProcessOptions = {}, ) { options.defaultNsForPrefix = options.defaultNsForPrefix ?? SignedXml.defaultNsForPrefix; options.signatureNode = this.signatureNode; const canonXml = node.cloneNode(true); // Deep clone let transformedXml: Node | string = canonXml; transforms.forEach((transformName) => { if (isDomNode.isNodeLike(transformedXml)) { // If, after processing, `transformedNode` is a string, we can't do anymore transforms on it const transform = this.findCanonicalizationAlgorithm(transformName); transformedXml = transform.process(transformedXml, options); } //TODO: currently transform.process may return either Node or String value (enveloped transformation returns Node, exclusive-canonicalization returns String). //This either needs to be more explicit in the API, or all should return the same. //exclusive-canonicalization returns String since it builds the Xml by hand. If it had used xmldom it would incorrectly minimize empty tags //to instead of and also incorrectly handle some delicate line break issues. //enveloped transformation returns Node since if it would return String consider this case: // //if only y is the node to sign then a string would be without the definition of the p namespace. probably xmldom toString() should have added it. }); return transformedXml.toString(); } /** * Ensure an element has Id attribute. If not create it with unique value. * Work with both normal and wssecurity Id flavour */ private ensureHasId(node) { let attr; if (this.idMode === "wssecurity") { attr = utils.findAttr( node, "Id", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", ); } else { this.idAttributes.some((idAttribute) => { attr = utils.findAttr(node, idAttribute); return !!attr; // This will break the loop as soon as a truthy attr is found. }); } if (attr) { return attr.value; } //add the attribute const id = `_${this.id++}`; if (this.idMode === "wssecurity") { node.setAttributeNS( "http://www.w3.org/2000/xmlns/", "xmlns:wsu", "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", ); node.setAttributeNS( "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", "wsu:Id", id, ); } else { node.setAttribute("Id", id); } return id; } /** * Create the SignedInfo element * */ private createSignedInfo(doc, prefix) { if (typeof this.canonicalizationAlgorithm !== "string") { throw new Error( "Missing canonicalizationAlgorithm when trying to create signed info for XML", ); } const transform = this.findCanonicalizationAlgorithm(this.canonicalizationAlgorithm); const algo = this.findSignatureAlgorithm(this.signatureAlgorithm); const currentPrefix = prefix ? `${prefix}:` : ""; let res = `<${currentPrefix}SignedInfo>`; res += `<${currentPrefix}CanonicalizationMethod Algorithm="${transform.getAlgorithmName()}"`; if (utils.isArrayHasLength(this.inclusiveNamespacesPrefixList)) { res += ">"; res += ``; res += ``; } else { res += " />"; } res += `<${currentPrefix}SignatureMethod Algorithm="${algo.getAlgorithmName()}" />`; // No references here - added later res += ``; return res; } /** * Create the Signature element * */ private createSignature(prefix?: string) { let xmlNsAttr = "xmlns"; if (prefix) { xmlNsAttr += `:${prefix}`; prefix += ":"; } else { prefix = ""; } const signatureValueXml = `<${prefix}SignatureValue>${this.signatureValue}`; //the canonicalization requires to get a valid xml node. //we need to wrap the info in a dummy signature since it contains the default namespace. const dummySignatureWrapper = `<${prefix}Signature ${xmlNsAttr}="http://www.w3.org/2000/09/xmldsig#">${signatureValueXml}`; const doc = new xmldom.DOMParser().parseFromString(dummySignatureWrapper); // Because we are using a dummy wrapper hack described above, we know there will be a `firstChild` // eslint-disable-next-line @typescript-eslint/no-non-null-assertion return doc.documentElement.firstChild!; } /** * Returns just the signature part, must be called only after {@link computeSignature} * * @returns The signature XML. */ getSignatureXml(): string { return this.signatureXml; } /** * Returns the original xml with Id attributes added on relevant elements (required for validation), must be called only after {@link computeSignature} * * @returns The original XML with IDs. */ getOriginalXmlWithIds(): string { return this.originalXmlWithIds; } /** * Returns the original xml document with the signature in it, must be called only after {@link computeSignature} * * @returns The signed XML. */ getSignedXml(): string { return this.signedXml; } } ================================================ FILE: src/types.ts ================================================ /* eslint-disable no-unused-vars */ // Type definitions for @node-saml/xml-crypto // Project: https://github.com/node-saml/xml-crypto#readme // Original definitions by: Eric Heikes // Max Chehab /// import * as crypto from "crypto"; export type ErrorFirstCallback = (err: Error | null, result?: T) => void; export type CanonicalizationAlgorithmType = | "http://www.w3.org/TR/2001/REC-xml-c14n-20010315" | "http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments" | "http://www.w3.org/2001/10/xml-exc-c14n#" | "http://www.w3.org/2001/10/xml-exc-c14n#WithComments" | string; export type CanonicalizationOrTransformAlgorithmType = | CanonicalizationAlgorithmType | "http://www.w3.org/2000/09/xmldsig#enveloped-signature"; export type HashAlgorithmType = | "http://www.w3.org/2000/09/xmldsig#sha1" | "http://www.w3.org/2001/04/xmlenc#sha256" | "http://www.w3.org/2001/04/xmlenc#sha512" | string; export type SignatureAlgorithmType = | "http://www.w3.org/2000/09/xmldsig#rsa-sha1" | "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" | "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1" | "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512" | "http://www.w3.org/2000/09/xmldsig#hmac-sha1" | string; /** * @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 { publicCert?: crypto.KeyLike; prefix?: string | null; } /** * Object attributes as defined in XMLDSig spec and are emitted verbatim * @see https://www.w3.org/TR/xmldsig-core/#sec-Object */ export interface ObjectAttributes { /** Optional ID attribute */ Id?: string; /** Optional MIME type attribute */ MimeType?: string; /** Optional encoding attribute */ Encoding?: string; /** Any additional custom attributes */ [key: string]: string | undefined; } /** * Options for the SignedXml constructor. */ export interface SignedXmlOptions { idMode?: "wssecurity"; idAttribute?: string; privateKey?: crypto.KeyLike; publicCert?: crypto.KeyLike; signatureAlgorithm?: SignatureAlgorithmType; canonicalizationAlgorithm?: CanonicalizationAlgorithmType; inclusiveNamespacesPrefixList?: string | string[]; implicitTransforms?: ReadonlyArray; keyInfoAttributes?: Record; getKeyInfoContent?(args?: GetKeyInfoContentArgs): string | null; getCertFromKeyInfo?(keyInfo?: Node | null): string | null; objects?: Array<{ content: string; attributes?: ObjectAttributes }>; } export interface NamespacePrefix { prefix: string; namespaceURI: string; } export interface RenderedNamespace { rendered: string; newDefaultNs: string; } export interface CanonicalizationOrTransformationAlgorithmProcessOptions { defaultNs?: string; defaultNsForPrefix?: Record; ancestorNamespaces?: NamespacePrefix[]; signatureNode?: Node | null; inclusiveNamespacesPrefixList?: string[]; } export interface ComputeSignatureOptionsLocation { reference?: string; action?: "append" | "prepend" | "before" | "after"; } /** * Options for the computeSignature method. * * - `prefix` {String} Adds a prefix for the generated signature tags * - `attrs` {Object} A hash of attributes and values `attrName: value` to add to the signature root node * - `location` {{ reference: String, action: String }} * - `existingPrefixes` {Object} A hash of prefixes and namespaces `prefix: namespace` already in the xml * An object with a `reference` key which should * contain a XPath expression, an `action` key which * should contain one of the following values: * `append`, `prepend`, `before`, `after` */ export interface ComputeSignatureOptions { prefix?: string; attrs?: Record; location?: ComputeSignatureOptionsLocation; existingPrefixes?: Record; } /** * Represents a reference node for XML digital signature. */ export interface Reference { // The XPath expression that selects the data to be signed. xpath?: string; // An array of transforms to be applied to the data before signing. transforms: ReadonlyArray; // The algorithm used to calculate the digest value of the data. digestAlgorithm: HashAlgorithmType; // The URI that identifies the data to be signed. uri: string; // Optional. The digest value of the referenced data. digestValue?: unknown; // A list of namespace prefixes to be treated as "inclusive" during canonicalization. inclusiveNamespacesPrefixList: string[]; // Optional. Indicates whether the URI is empty. isEmptyUri: boolean; // Optional. The `Id` attribute of the reference node. id?: string; // Optional. The `Type` attribute of the reference node. type?: string; // Optional. The type of the reference node. ancestorNamespaces?: NamespacePrefix[]; validationError?: Error; getValidatedNode(xpathSelector?: string): Node | null; signedReference?: string; } /** Implement this to create a new CanonicalizationOrTransformationAlgorithm */ export interface CanonicalizationOrTransformationAlgorithm { process( node: Node, options: CanonicalizationOrTransformationAlgorithmProcessOptions, ): Node | string; getAlgorithmName(): CanonicalizationOrTransformAlgorithmType; } /** Implement this to create a new HashAlgorithm */ export interface HashAlgorithm { getAlgorithmName(): HashAlgorithmType; getHash(xml: string): string; } /** Extend this to create a new SignatureAlgorithm */ export interface SignatureAlgorithm { /** * Sign the given string using the given key */ getSignature(signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike): string; getSignature( signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike, callback?: ErrorFirstCallback, ): void; /** * Verify the given signature of the given string using key * * @param key a public cert, public key, or private key can be passed here */ verifySignature(material: string, key: crypto.KeyLike, signatureValue: string): boolean; verifySignature( material: string, key: crypto.KeyLike, signatureValue: string, callback?: ErrorFirstCallback, ): void; getAlgorithmName(): SignatureAlgorithmType; } /** Implement this to create a new TransformAlgorithm */ export interface TransformAlgorithm { getAlgorithmName(): CanonicalizationOrTransformAlgorithmType; process(node: Node): string; } /** * ### Sign * #### Properties * - {@link SignedXml#privateKey} [required] * - {@link SignedXml#publicCert} [optional] * - {@link SignedXml#signatureAlgorithm} [optional] * - {@link SignedXml#canonicalizationAlgorithm} [optional] * #### Api * - {@link SignedXml#addReference} * - {@link SignedXml#computeSignature} * - {@link SignedXml#getSignedXml} * - {@link SignedXml#getSignatureXml} * - {@link SignedXml#getOriginalXmlWithIds} * * ### Verify * #### Properties * - {@link SignedXml#publicCert} [optional] * #### Api * - {@link SignedXml#loadSignature} * - {@link SignedXml#checkSignature} */ function isErrorFirstCallback( possibleCallback: unknown, ): possibleCallback is ErrorFirstCallback { return typeof possibleCallback === "function"; } /** * This function will add a callback version of a sync function. * * This follows the factory pattern. * Just call this function, passing the function that you'd like to add a callback version of. */ export function createOptionalCallbackFunction( syncVersion: (...args: A) => T, ): { (...args: A): T; (...args: [...A, ErrorFirstCallback]): void; } { return ((...args: A | [...A, ErrorFirstCallback]) => { const possibleCallback = args[args.length - 1]; if (isErrorFirstCallback(possibleCallback)) { try { const result = syncVersion(...(args.slice(0, -1) as A)); possibleCallback(null, result); } catch (err) { possibleCallback(err instanceof Error ? err : new Error("Unknown error")); } } else { return syncVersion(...(args as A)); } }) as { (...args: A): T; (...args: [...A, ErrorFirstCallback]): void; }; } ================================================ FILE: src/utils.ts ================================================ import * as xpath from "xpath"; import type { NamespacePrefix } from "./types"; import * as isDomNode from "@xmldom/is-dom-node"; export function isArrayHasLength(array: unknown): array is unknown[] { return Array.isArray(array) && array.length > 0; } function attrEqualsExplicitly(attr: Attr, localName: string, namespace?: string) { return attr.localName === localName && (attr.namespaceURI === namespace || namespace == null); } function attrEqualsImplicitly(attr: Attr, localName: string, namespace?: string, node?: Element) { return ( attr.localName === localName && ((!attr.namespaceURI && node?.namespaceURI === namespace) || namespace == null) ); } export function findAttr(element: Element, localName: string, namespace?: string) { for (let i = 0; i < element.attributes.length; i++) { const attr = element.attributes[i]; if ( attrEqualsExplicitly(attr, localName, namespace) || attrEqualsImplicitly(attr, localName, namespace, element) ) { return attr; } } return null; } export function findChildren(node: Node | Document, localName: string, namespace?: string) { const element = (node as Document).documentElement ?? node; const res: Element[] = []; for (let i = 0; i < element.childNodes.length; i++) { const child = element.childNodes[i]; if ( isDomNode.isElementNode(child) && child.localName === localName && (child.namespaceURI === namespace || namespace == null) ) { res.push(child); } } return res; } /** @deprecated */ export function findChilds(node: Node | Document, localName: string, namespace?: string) { return findChildren(node, localName, namespace); } const xml_special_to_encoded_attribute = { "&": "&", "<": "<", '"': """, "\r": " ", "\n": " ", "\t": " ", }; const xml_special_to_encoded_text = { "&": "&", "<": "<", ">": ">", "\r": " ", }; export function encodeSpecialCharactersInAttribute(attributeValue) { return attributeValue.replace(/([&<"\r\n\t])/g, function (str, item) { /** Special character normalization. * @see: * - https://www.w3.org/TR/xml-c14n#ProcessingModel (Attribute Nodes) * - https://www.w3.org/TR/xml-c14n#Example-Chars */ return xml_special_to_encoded_attribute[item]; }); } export function encodeSpecialCharactersInText(text: string): string { return text.replace(/([&<>\r])/g, function (str, item) { /** Special character normalization. * @see: * - https://www.w3.org/TR/xml-c14n#ProcessingModel (Text Nodes) * - https://www.w3.org/TR/xml-c14n#Example-Chars */ return xml_special_to_encoded_text[item]; }); } /** * PEM format has wide range of usages, but this library * is enforcing RFC7468 which focuses on PKIX, PKCS and CMS. * * https://www.rfc-editor.org/rfc/rfc7468 * * PEM_FORMAT_REGEX is validating given PEM file against RFC7468 'stricttextualmsg' definition. * * 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. */ export const PEM_FORMAT_REGEX = new RegExp( "^-----BEGIN [A-Z\x20]{1,48}-----([^-]*)-----END [A-Z\x20]{1,48}-----$", "s", ); export const EXTRACT_X509_CERTS = new RegExp( "-----BEGIN CERTIFICATE-----[^-]*-----END CERTIFICATE-----", "g", ); export const BASE64_REGEX = new RegExp( "^(?:[A-Za-z0-9\\+\\/]{4}\\n{0,1})*(?:[A-Za-z0-9\\+\\/]{2}==|[A-Za-z0-9\\+\\/]{3}=)?$", "s", ); /** * -----BEGIN [LABEL]----- * base64([DATA]) * -----END [LABEL]----- * * Above is shown what PEM file looks like. As can be seen, base64 data * can be in single line or multiple lines. * * 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' * * With a couple of notes: * - 'eol' is normalized to '\n' * * @param pem The PEM string to normalize to RFC7468 'stricttextualmsg' definition */ export function normalizePem(pem: string): string { return `${( pem .trim() .replace(/(\r\n|\r)/g, "\n") .match(/.{1,64}/g) ?? [] ).join("\n")}\n`; } /** * @param pem The PEM-encoded base64 certificate to strip headers from */ export function pemToDer(pem: string): Buffer { if (!PEM_FORMAT_REGEX.test(pem.trim())) { throw new Error("Invalid PEM format."); } return Buffer.from( pem .replace(/(\r\n|\r)/g, "") .replace(/-----BEGIN [A-Z\x20]{1,48}-----\n?/, "") .replace(/-----END [A-Z\x20]{1,48}-----\n?/, ""), "base64", ); } /** * @param der The DER-encoded base64 certificate to add PEM headers too * @param pemLabel The label of the header and footer to add */ export function derToPem( der: string | Buffer, pemLabel?: "CERTIFICATE" | "PRIVATE KEY" | "RSA PUBLIC KEY", ): string { const base64Der = Buffer.isBuffer(der) ? der.toString("base64").trim() : der.replace(/(\r\n|\r)/g, "").trim(); if (PEM_FORMAT_REGEX.test(base64Der)) { return normalizePem(base64Der); } if (BASE64_REGEX.test(base64Der.replace(/ /g, ""))) { if (pemLabel == null) { throw new Error("PEM label is required when DER is given."); } const pem = `-----BEGIN ${pemLabel}-----\n${base64Der.replace( / /g, "", )}\n-----END ${pemLabel}-----`; return normalizePem(pem); } throw new Error("Unknown DER format."); } function collectAncestorNamespaces( node: Element, nsArray: NamespacePrefix[] = [], ): NamespacePrefix[] { if (!isDomNode.isElementNode(node.parentNode)) { return nsArray; } const parent: Element = node.parentNode; if (!parent) { return nsArray; } if (parent.attributes && parent.attributes.length > 0) { for (let i = 0; i < parent.attributes.length; i++) { const attr = parent.attributes[i]; if (attr && attr.nodeName && attr.nodeName.search(/^xmlns:?/) !== -1) { nsArray.push({ prefix: attr.nodeName.replace(/^xmlns:?/, ""), namespaceURI: attr.nodeValue || "", }); } } } return collectAncestorNamespaces(parent, nsArray); } function findNSPrefix(subset) { const subsetAttributes = subset.attributes; for (let k = 0; k < subsetAttributes.length; k++) { const nodeName = subsetAttributes[k].nodeName; if (nodeName.search(/^xmlns:?/) !== -1) { return nodeName.replace(/^xmlns:?/, ""); } } return subset.prefix || ""; } function isElementSubset(docSubset: Node[]): docSubset is Element[] { return docSubset.every((node) => isDomNode.isElementNode(node)); } /** * Extract ancestor namespaces in order to import it to root of document subset * which is being canonicalized for non-exclusive c14n. * * @param doc - Usually a product from `new xmldom.DOMParser().parseFromString()` * @param docSubsetXpath - xpath query to get document subset being canonicalized * @param namespaceResolver - xpath namespace resolver * @returns i.e. [{prefix: "saml", namespaceURI: "urn:oasis:names:tc:SAML:2.0:assertion"}] */ export function findAncestorNs( doc: Document, docSubsetXpath?: string, namespaceResolver?: XPathNSResolver, ): NamespacePrefix[] { if (docSubsetXpath == null) { return []; } const docSubset = xpath.selectWithResolver(docSubsetXpath, doc, namespaceResolver); if (!isArrayHasLength(docSubset)) { return []; } if (!isElementSubset(docSubset)) { throw new Error("Document subset must be list of elements"); } // Remove duplicate on ancestor namespace const ancestorNs = collectAncestorNamespaces(docSubset[0]); const ancestorNsWithoutDuplicate: NamespacePrefix[] = []; for (let i = 0; i < ancestorNs.length; i++) { let notOnTheList = true; for (const v in ancestorNsWithoutDuplicate) { if (ancestorNsWithoutDuplicate[v].prefix === ancestorNs[i].prefix) { notOnTheList = false; break; } } if (notOnTheList) { ancestorNsWithoutDuplicate.push(ancestorNs[i]); } } // Remove namespaces which are already declared in the subset with the same prefix const returningNs: NamespacePrefix[] = []; const subsetNsPrefix = findNSPrefix(docSubset[0]); for (const ancestorNs of ancestorNsWithoutDuplicate) { if (ancestorNs.prefix !== subsetNsPrefix) { returningNs.push(ancestorNs); } } return returningNs; } export function validateDigestValue(digest, expectedDigest) { const buffer = Buffer.from(digest, "base64"); const expectedBuffer = Buffer.from(expectedDigest, "base64"); if (typeof buffer.equals === "function") { return buffer.equals(expectedBuffer); } if (buffer.length !== expectedBuffer.length) { return false; } for (let i = 0; i < buffer.length; i++) { if (buffer[i] !== expectedBuffer[i]) { return false; } } return true; } // Check if the given node is descendant of the given parent node export function isDescendantOf(node: Node, parent: Node): boolean { if (!node || !parent) { return false; } let currentNode: Node | null = node.parentNode; while (currentNode) { if (currentNode === parent) { return true; } currentNode = currentNode.parentNode; } return false; } ================================================ FILE: test/c14n-non-exclusive-unit-tests.spec.ts ================================================ import { expect } from "chai"; import { C14nCanonicalization } from "../src/c14n-canonicalization"; import * as xmldom from "@xmldom/xmldom"; import * as xpath from "xpath"; import * as utils from "../src/utils"; import * as isDomNode from "@xmldom/is-dom-node"; const test_C14nCanonicalization = function (xml, xpathArg, expected) { const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1(xpathArg, doc); const can = new C14nCanonicalization(); isDomNode.assertIsNodeLike(node); const result = can .process(node, { ancestorNamespaces: utils.findAncestorNs(doc, xpathArg), }) .toString(); expect(result).to.equal(expected); }; const test_findAncestorNs = function (xml, xpath, expected) { const doc = new xmldom.DOMParser().parseFromString(xml); const result = utils.findAncestorNs(doc, xpath); expect(result).to.deep.equal(expected); }; describe("C14N non-exclusive canonicalization tests", function () { it("findAncestorNs: Correctly picks up root ancestor namespace", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = [{ prefix: "aaa", namespaceURI: "bbb" }]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Correctly picks up intermediate ancestor namespace", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = [{ prefix: "aaa", namespaceURI: "bbb" }]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Correctly picks up multiple ancestor namespaces declared in the one same element", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = [ { prefix: "aaa", namespaceURI: "bbb" }, { prefix: "ccc", namespaceURI: "ddd" }, ]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Correctly picks up multiple ancestor namespaces scattered among depth", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = [ { prefix: "ccc", namespaceURI: "ddd" }, { prefix: "aaa", namespaceURI: "bbb" }, ]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Correctly picks up multiple ancestor namespaces without duplicate", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = [{ prefix: "ccc", namespaceURI: "bbb" }]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Correctly eliminates duplicate prefix", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = [{ prefix: "ccc", namespaceURI: "AAA" }]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Exclude namespace which is already declared with same prefix on target node", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = []; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Ignores namespace declared in the target xpath node", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = [{ prefix: "aaa", namespaceURI: "bbb" }]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Should find namespace without prefix", function () { const xml = ""; const xpath = "//*[local-name()='child2']"; const expected = [{ prefix: "", namespaceURI: "bbb" }]; test_findAncestorNs(xml, xpath, expected); }); it("findAncestorNs: Should not find namespace when both has no prefix", function () { const xml = ""; const xpath = "//*[local-name()='child2']"; const expected = []; test_findAncestorNs(xml, xpath, expected); }); // Tests for c14nCanonicalization it("C14n: Correctly picks up root ancestor namespace", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Correctly picks up intermediate ancestor namespace", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Correctly picks up multiple ancestor namespaces declared in the one same element", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Correctly picks up multiple ancestor namespaces scattered among depth", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Correctly picks up multiple ancestor namespaces without duplicate", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Correctly eliminates duplicate prefix", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Exclude namespace which is already declared with same prefix on target node", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Preserve namespace declared in the target xpath node", function () { const xml = ''; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Don't redeclare an attribute's namespace prefix if already in scope", function () { const xml = ""; const xpath = "/root/child1/child2"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: Don't declare an attribute's namespace prefix if in scope from parent", function () { const xml = ""; const xpath = "/root/child1"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); it("C14n: should not has colon when parent namespace has no prefix", function () { const xml = ""; const xpath = "//*[local-name()='child3']"; const expected = ''; test_C14nCanonicalization(xml, xpath, expected); }); }); ================================================ FILE: test/c14nWithComments-unit-tests.spec.ts ================================================ import { expect } from "chai"; import { ExclusiveCanonicalizationWithComments as c14nWithComments } from "../src/exclusive-canonicalization"; import * as xmldom from "@xmldom/xmldom"; import * as xpath from "xpath"; import { SignedXml } from "../src/index"; import * as isDomNode from "@xmldom/is-dom-node"; const compare = function (xml, xpathArg, expected, inclusiveNamespacesPrefixList?: string[]) { const doc = new xmldom.DOMParser().parseFromString(xml); const elem = xpath.select1(xpathArg, doc); const can = new c14nWithComments(); isDomNode.assertIsElementNode(elem); const result = can.process(elem, { inclusiveNamespacesPrefixList }).toString(); expect(result).to.equal(expected); }; describe("Exclusive canonicalization with comments", function () { it("Exclusive canonicalization works on xml with no namespaces", function () { compare("123", "//*", "123"); }); it("Exclusive canonicalization works on inner xpath", function () { compare("123", "//*[local-name(.)='child']", "123"); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("element used prefixed ns which is also the default", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes. ns definition is not duplicated on each usage", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes but before used", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list", function () { compare( '123', "//*[local-name(.)='child']", '123', ["inclusive"], ); }); it("Exclusive canonicalization works on xml with multiple prefixed namespaces defined in inclusive list", function () { compare( '123456', "//*[local-name(.)='child']", '123456', ["inclusive", "inclusive2"], ); }); it("Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list defined outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ["inclusive"], ); }); it("Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list used on attribute", function () { compare( '123', "//*[local-name(.)='child']", '123', ["inclusive"], ); }); it("Exclusive canonicalization works on xml with default namespace inside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with multiple different default namespaces", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with multiple similar default namespaces", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with default namespace outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works when prefixed namespace is defined in output nodes not in the parent chain of who needs it", function () { compare( '', "//*[local-name(.)='child']", '', ); }); it("Exclusive canonicalization works on xml with unordered attributes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization sorts upper case attributes before lower case", function () { compare('', "//*[local-name(.)='x']", ''); }); it("C14N#WithComments retains Comments", function () { compare( '', "//*[local-name(.)='x']", '', ); }); it("Exclusive canonicalization works on xml with attributes with different namespace than element", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with attribute and element values with special characters", function () { compare( '&11', "//*[local-name(.)='child']", '&11', ); }); it("Exclusive canonicalization preserves white space in values", function () { compare( "12\n3\t", "//*[local-name(.)='child']", "12\n3\t", ); }); it("Exclusive canonicalization preserves white space between elements", function () { compare( "123\n", "//*[local-name(.)='child']", "123\n", ); }); it("Exclusive canonicalization turns empty element to start-end tag pairs", function () { compare( "", "//*[local-name(.)='child']", "", ); }); it("Exclusive canonicalization preserves empty start-end tag pairs", function () { compare( "", "//*[local-name(.)='child']", "", ); }); it("Exclusive canonicalization with empty default namespace outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", "123", ); }); /* TODO: Uncomment this when this issue is fixed it("Exclusive canonicalization removal of whitespace between PITarget and its data", function () { compare( "123", "//*[local-name(.)='child']", "123"); }); */ it("Exclusive canonicalization with empty default namespace inside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("The XML declaration and document type declaration (DTD) are removed", function () { compare( '123', "//*[local-name(.)='child']", "123", ); }); /* TODO: Uncomment this when this issue is fixed it("The XML declaration and document type declaration (DTD) are removed, stylesheet retained", function () { compare( "123", "//*[local-name(.)='child']", "123"); }); */ it("Attribute value delimiters are set to quotation marks (double quotes)", function () { compare( "123 ", "//*[local-name(.)='child']", '123 ', ); }); it("CDATA sections are replaced with their character content", function () { compare( "123]]>", "//*[local-name(.)='child']", "foo & bar in the <x>123</x>", ); }); it("SignedInfo canonization", function () { compare( 'http://stockservice.contoso.com/wse/samples/2003/06/StockQuoteRequestuuid:6250c037-bcde-40ab-82b3-3a08efc86cdchttp://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymoushttp://localhost:8889/2008-09-01T17:44:21Z2008-09-01T17:49:21ZMIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=Fkm7AbwiJCiOzY8ldfuA9pTW1G+EtE+UX4Cv7SoMIqeUdfWRDVHZpJAQyf7aoQnlpJNV/3k9L1PT6rJbfV478CkULJENPLm1m0fmDeLzhIHDEANuzp/AirC60tMD5jCARb4B4Nr/6bTmoyDQsTY8VLRiiINng7Mpweg1FZvd8a0=FABRIKAM', "//*[local-name(.)='SignedInfo']", '+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=', ); }); it("Exclusive canonicalization works on complex xml", function () { compare( '\n' + '\n' + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + ' \n' + " ererer\n" + " dfdf\n" + " \n" + " \n" + " \n" + " \n" + ' \n' + ' \n' + ' \n' + " \n" + " \n" + " erer\n" + ' \n' + " \n" + ' \n' + " \n" + " \n" + ' \n' + " \n" + ' \n' + " \n" + ' \n' + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + " \r" + " \n" + " \n" + " \n" + ' \n' + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + "", "//*[local-name(.)='Body']", '\n \n \n \n \n \n \n \n \n \n \n \n \n ererer\n dfdf\n \n \n \n \n \n \n \n \n \n erer\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ', ); }); it("Multiple Canonicalization with namespace definition outside of signed element", function () { const doc = new xmldom.DOMParser().parseFromString( '', ); const node = xpath.select1("//*[local-name(.)='y']", doc); isDomNode.assertIsNodeLike(node); const sig = new SignedXml(); const res = sig.getCanonXml( [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], node, ); expect(res).to.equal(''); }); it("Enveloped-signature canonicalization respects current node", function () { // older versions of enveloped-signature removed the first signature in the whole doc, but should // be the signature inside the current node if we want to be able to verify multiple signatures // in a document. const xml = ''; const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1("//*[local-name(.)='y']", doc); const sig = new SignedXml(); const transforms = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"]; isDomNode.assertIsNodeLike(node); const res = sig.getCanonXml(transforms, node); expect(res).to.equal(""); }); it("The XML canonicalization method processes a node-set by imposing the following additional document order rules on the namespace and attribute nodes of each element: \ - An element's namespace and attribute nodes have a document order position greater than the element but less than any child node of the element. \ Namespace nodes have a lesser document order position than attribute nodes. \ - An element's namespace nodes are sorted lexicographically by local name (the default namespace node, if one exists, has no local name and is therefore lexicographically least). \ - An element's attribute nodes are sorted lexicographically with namespace URI as the primary key and local name as the secondary key (an empty namespace URI is lexicographically least). \ Lexicographic comparison, which orders strings from least to greatest alphabetically, is based on the UCS codepoint values, which is equivalent to lexicographic ordering based on UTF-8.", function () { compare( '', "//*[local-name(.)='root']", '', ); }); it("saml attributed order (bug #25)", function () { compare( '', "//*[local-name(.)='root']", '', ); }); }); ================================================ FILE: test/canonicalization-unit-tests.spec.ts ================================================ import { expect } from "chai"; import { ExclusiveCanonicalization } from "../src/exclusive-canonicalization"; import * as xmldom from "@xmldom/xmldom"; import * as xpath from "xpath"; import { SignedXml } from "../src/index"; import * as isDomNode from "@xmldom/is-dom-node"; const compare = function ( xml: string, xpathArg: string, expected: string, inclusiveNamespacesPrefixList?: string[], defaultNsForPrefix?: Record, ) { const doc = new xmldom.DOMParser().parseFromString(xml); const elem = xpath.select1(xpathArg, doc); const can = new ExclusiveCanonicalization(); isDomNode.assertIsElementNode(elem); const result = can .process(elem, { inclusiveNamespacesPrefixList, defaultNsForPrefix, }) .toString(); expect(expected).to.equal(result); }; describe("Canonicalization unit tests", function () { it("Exclusive canonicalization works on xml with no namespaces", function () { compare("123", "//*", "123"); }); it("Exclusive canonicalization works on inner xpath", function () { compare("123", "//*[local-name(.)='child']", "123"); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("element used prefixed ns which is also the default", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works with default namespace for prefix", function () { compare( '', "//*[local-name(.)='SignedInfo']", '', undefined, { ds: "http://www.w3.org/2000/09/xmldsig#" }, ); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes. ns definition is not duplicated on each usage", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined in output nodes but before used", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with prefixed namespaces defined outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list", function () { compare( '123', "//*[local-name(.)='child']", '123', ["inclusive"], ); }); it("Exclusive canonicalization works on xml with multiple prefixed namespaces defined in inclusive list", function () { compare( '123456', "//*[local-name(.)='child']", '123456', ["inclusive", "inclusive2"], ); }); it("Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list defined outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ["inclusive"], ); }); it("Exclusive canonicalization works on xml with prefixed namespace defined in inclusive list used on attribute", function () { compare( '123', "//*[local-name(.)='child']", '123', ["inclusive"], ); }); it("Exclusive canonicalization works on xml with default namespace inside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with multiple different default namespaces", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with multiple similar default namespaces", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with default namespace outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works when prefixed namespace is defined in output nodes not in the parent chain of who needs it", function () { compare( '', "//*[local-name(.)='child']", '', ); }); it("Exclusive canonicalization works on xml with unordered attributes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization sorts upper case attributes before lower case", function () { compare('', "//*[local-name(.)='x']", ''); }); it("Exclusive canonicalization removes Comments", function () { compare('', "//*[local-name(.)='x']", ''); }); it("Exclusive canonicalization works on xml with attributes with different namespace than element", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("Exclusive canonicalization works on xml with attribute values with special characters", function () { compare( '"11\r\n\'>11', "//*[local-name(.)='child']", '11', ); }); it("Exclusive canonicalization does not normalize whitespace characters into single spaces", function () { compare( '11', "//*[local-name(.)='child']", '11', ); }); it("Exclusive canonicalization works on xml with element values with special characters", function () { compare( // eslint-disable-next-line no-useless-escape '&<>"11 &>"11\r', "//*[local-name(.)='child']", '&<>"11 &>"11\n', ); }); it("Exclusive canonicalization preserves white space in values", function () { compare( "12\n3\t", "//*[local-name(.)='child']", "12\n3\t", ); }); it("Exclusive canonicalization does not alter CR-NL (windows line separator) sequences", function () { compare( "123\r\n", "//*[local-name(.)='child']", "123\n", ); }); it("Exclusive canonicalization preserves and encodes CR white space", function () { compare( "\r12\r3\r", "//*[local-name(.)='child']", "\n12\n3\n", ); }); it("Exclusive canonicalization preserves white space between elements", function () { compare( "123\n", "//*[local-name(.)='child']", "123\n", ); }); it("Exclusive canonicalization turns empty element to start-end tag pairs", function () { compare( "", "//*[local-name(.)='child']", "", ); }); it("Exclusive canonicalization preserves empty start-end tag pairs", function () { compare( "", "//*[local-name(.)='child']", "", ); }); it("Exclusive canonicalization with empty default namespace outside output nodes", function () { compare( '123', "//*[local-name(.)='child']", "123", ); }); /* TODO: Uncomment this when this issue is fixed it("Exclusive canonicalization removal of whitespace between PITarget and its data", function() { compare( "123", "//*[local-name(.)='child']", "123" ); }); */ it("Exclusive canonicalization with empty default namespace inside output nodes", function () { compare( '123', "//*[local-name(.)='child']", '123', ); }); it("The XML declaration and document type declaration (DTD) are removed", function () { compare( '123', "//*[local-name(.)='child']", "123", ); }); /* TODO: Uncomment this when this issue is fixed it("The XML declaration and document type declaration (DTD) are removed, stylesheet retained", function() { compare( "123", "//*[local-name(.)='child']", "123" ); }); */ it("Attribute value delimiters are set to quotation marks (double quotes)", function () { compare( "123 ", "//*[local-name(.)='child']", '123 ', ); }); it("CDATA sections are replaced with their character content", function () { compare( "123]]>", "//*[local-name(.)='child']", "foo & bar in the <x>123</x>", ); }); it("SignedInfo canonization", function () { compare( 'http://stockservice.contoso.com/wse/samples/2003/06/StockQuoteRequestuuid:6250c037-bcde-40ab-82b3-3a08efc86cdchttp://schemas.xmlsoap.org/ws/2004/03/addressing/role/anonymoushttp://localhost:8889/2008-09-01T17:44:21Z2008-09-01T17:49:21ZMIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=Fkm7AbwiJCiOzY8ldfuA9pTW1G+EtE+UX4Cv7SoMIqeUdfWRDVHZpJAQyf7aoQnlpJNV/3k9L1PT6rJbfV478CkULJENPLm1m0fmDeLzhIHDEANuzp/AirC60tMD5jCARb4B4Nr/6bTmoyDQsTY8VLRiiINng7Mpweg1FZvd8a0=FABRIKAM', "//*[local-name(.)='SignedInfo']", '+465BlJx5xOfHsIFezQt0MS1vZQ=jEe8rnaaqBWZQe+xHBQXriVG99o=W45ginYdBVqOqEaqPI2piZMPReA=m2VlWz/ZDTWL7FREHK+wpKhvjJM=Qws229qmAzSTZ4OKmAUWgl0PWWo=iEazGnkPY5caCWVZOHyR87CZ1h0=', ); }), it("Exclusive canonicalization works on complex xml", function () { compare( '\n' + '\n' + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + ' \n' + " ererer\n" + " dfdf\n" + " \n" + " \n" + " \n" + " \n" + ' \n' + ' \n' + ' \n' + " \n" + " \n" + " erer\n" + ' \n' + " \n" + ' \n' + " \n" + " \n" + ' \n' + " \n" + ' \n' + " \n" + ' \n' + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + " \r" + " \n" + " \n" + " \n" + ' \n' + " \n" + ' \n' + " \n" + " \n" + " \n" + " \n" + " \n" + "", "//*[local-name(.)='Body']", '\n \n \n \n \n \n \n \n \n \n \n \n \n ererer\n dfdf\n \n \n \n \n \n \n \n \n \n erer\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n ', ); }); it("Multiple Canonicalization with namespace definition outside of signed element", function () { const doc = new xmldom.DOMParser().parseFromString( '', ); const node = xpath.select1("//*[local-name(.)='y']", doc); isDomNode.assertIsNodeLike(node); const sig = new SignedXml(); const res = sig.getCanonXml( [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], node, ); expect(res).to.equal(''); }); it("Shouldn't continue processing transforms if we end up with a string as a result of a transform", function () { const doc = new xmldom.DOMParser().parseFromString( '', ); const node1 = xpath.select1("//*[local-name(.)='y']", doc); const node2 = xpath.select1("//*[local-name(.)='y']", doc); isDomNode.assertIsNodeLike(node1); isDomNode.assertIsNodeLike(node2); const sig = new SignedXml(); const res1 = sig.getCanonXml( [ "http://www.w3.org/2001/10/xml-exc-c14n#", "http://www.w3.org/2000/09/xmldsig#enveloped-signature", ], node1, ); const res2 = sig.getCanonXml(["http://www.w3.org/2001/10/xml-exc-c14n#"], node2); expect(res1) .to.equal(res2) .to.equal( '', ); }); it("Enveloped-signature canonicalization respects current node", function () { // older versions of enveloped-signature removed the first signature in the whole doc, but should // be the signature inside the current node if we want to be able to verify multiple signatures // in a document. const xml = ''; const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1("//*[local-name(.)='y']", doc); isDomNode.assertIsNodeLike(node); const sig = new SignedXml(); const transforms = ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"]; const res = sig.getCanonXml(transforms, node); expect(res).to.equal(""); }); it("The XML canonicalization method processes a node-set by imposing the following additional document order rules on the namespace and attribute nodes of each element: \ - An element's namespace and attribute nodes have a document order position greater than the element but less than any child node of the element. \ Namespace nodes have a lesser document order position than attribute nodes. \ - An element's namespace nodes are sorted lexicographically by local name (the default namespace node, if one exists, has no local name and is therefore lexicographically least). \ - An element's attribute nodes are sorted lexicographically with namespace URI as the primary key and local name as the secondary key (an empty namespace URI is lexicographically least). \ Lexicographic comparison, which orders strings from least to greatest alphabetically, is based on the UCS codepoint values, which is equivalent to lexicographic ordering based on UTF-8.", function () { compare( '', "//*[local-name(.)='root']", '', ); }); it("saml attributed order (bug #25)", function () { compare( '', "//*[local-name(.)='root']", '', ); }); it("Body Xml Element Canonicalization", function () { compare( '' + '' + '3810016849-201501-KB-0000' + "" + "", "//*", '' + '' + '3810016849-201501-KB-0000' + "" + "", ); }); it("Overriding namespace in canonicalization", function () { compare( '', "//*", '', ["ds"], ); }); }); ================================================ FILE: test/document-tests.spec.ts ================================================ import { SignedXml } from "../src/index"; import * as xpath from "xpath"; import * as xmldom from "@xmldom/xmldom"; import * as fs from "fs"; import { expect } from "chai"; import * as isDomNode from "@xmldom/is-dom-node"; describe("Document tests", function () { it("test with a document (using FileKeyInfo)", function () { const xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(node); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(node); const result = sig.checkSignature(xml); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("test with a document (using StringKeyInfo)", function () { const xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(node); const sig = new SignedXml(); const feidePublicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.publicCert = feidePublicCert; sig.loadSignature(node); const result = sig.checkSignature(xml); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); }); describe("Validated node references tests", function () { it("should return references if the document is validly signed", 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; expect(sig.getSignedReferences().length).to.equal(1); /* eslint-disable-next-line deprecation/deprecation */ const ref = sig.getReferences()[0]; const result = ref.getValidatedNode(); expect(result?.toString()).to.equal(doc.toString()); expect(sig.getSignedReferences().length).to.equal(1); }); it("should not return references if the document is not validly signed", function () { const xml = fs.readFileSync("./test/static/invalid_signature - changed content.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const sig = new SignedXml(); sig.loadSignature(sig.findSignatures(doc)[0]); const validSignature = sig.checkSignature(xml); expect(validSignature).to.be.false; expect(sig.getSignedReferences().length).to.equal(0); /* eslint-disable-next-line deprecation/deprecation */ const ref = sig.getReferences()[1]; const result = ref.getValidatedNode(); expect(result).to.be.null; expect(sig.getSignedReferences().length).to.equal(0); }); it("should return `null` if the selected node isn't found", 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; expect(sig.getSignedReferences().length).to.equal(1); /* eslint-disable-next-line deprecation/deprecation */ const ref = sig.getReferences()[0]; const result = ref.getValidatedNode("/non-existent-node"); expect(result).to.be.null; }); it("should return the selected node if it is validly signed", 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; expect(sig.getSignedReferences().length).to.equal(1); /* eslint-disable-next-line deprecation/deprecation */ const ref = sig.getReferences()[0]; const result = ref.getValidatedNode( "//*[local-name()='Attribute' and @Name='mail']/*[local-name()='AttributeValue']/text()", ); expect(result?.nodeValue).to.equal("henri.bergius@nemein.com"); expect(sig.getSignedReferences().length).to.equal(1); }); it("should return `null` if the selected node isn't validly signed", function () { const xml = fs.readFileSync("./test/static/invalid_signature - changed content.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const sig = new SignedXml(); sig.loadSignature(sig.findSignatures(doc)[0]); const validSignature = sig.checkSignature(xml); expect(validSignature).to.be.false; expect(sig.getSignedReferences().length).to.equal(0); /* eslint-disable-next-line deprecation/deprecation */ const ref = sig.getReferences()[0]; const result = ref.getValidatedNode( "//*[local-name()='Attribute' and @Name='mail']/*[local-name()='AttributeValue']/text()", ); expect(result).to.be.null; // Not all references verified, so no references should be in `.getSignedReferences()`. expect(sig.getSignedReferences().length).to.equal(0); }); }); ================================================ FILE: test/hmac-tests.spec.ts ================================================ import { SignedXml } from "../src/index"; import * as xpath from "xpath"; import * as xmldom from "@xmldom/xmldom"; import * as fs from "fs"; import { expect } from "chai"; import * as isDomNode from "@xmldom/is-dom-node"; describe("HMAC tests", function () { it("test validating HMAC signature", function () { const xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.enableHMAC(); sig.publicCert = fs.readFileSync("./test/static/hmac.key"); sig.loadSignature(signature); const result = sig.checkSignature(xml); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("test HMAC signature with incorrect key", function () { const xml = fs.readFileSync("./test/static/hmac_signature.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.enableHMAC(); sig.publicCert = fs.readFileSync("./test/static/hmac-foobar.key"); sig.loadSignature(signature); expect(() => sig.checkSignature(xml)).to.throw(/^invalid signature/); expect(sig.getSignedReferences().length).to.equal(0); }); it("test create and validate HMAC signature", function () { const xml = "" + "" + "Harry Potter" + "" + ""; const sig = new SignedXml(); sig.enableHMAC(); sig.privateKey = fs.readFileSync("./test/static/hmac.key"); sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; sig.addReference({ xpath: "//*[local-name(.)='book']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.computeSignature(xml); const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const verify = new SignedXml(); verify.enableHMAC(); verify.publicCert = fs.readFileSync("./test/static/hmac.key"); verify.loadSignature(signature); const result = verify.checkSignature(sig.getSignedXml()); expect(result).to.be.true; expect(verify.getSignedReferences().length).to.equal(1); }); }); ================================================ FILE: test/key-info-tests.spec.ts ================================================ import * as xmldom from "@xmldom/xmldom"; import * as fs from "fs"; import * as xpath from "xpath"; import { SignedXml } from "../src/index"; import { expect } from "chai"; import * as isDomNode from "@xmldom/is-dom-node"; describe("KeyInfo tests", function () { it("adds X509Certificate element during signature", function () { const xml = ""; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); 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.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const x509 = xpath.select("//*[local-name(.)='X509Certificate']", doc.documentElement); isDomNode.assertIsArrayOfNodes(x509); 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 SignedXml(); sig.privateKey = fs.readFileSync("./test/static/hmac.key"); sig.publicCert = fs.readFileSync("./test/static/hmac.key"); sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#hmac-sha1"; sig.enableHMAC(); sig.addReference({ xpath: "//*[local-name(.)='book']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.computeSignature(xml); const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); const keyInfo = xpath.select1("//*[local-name(.)='KeyInfo']", doc); expect(keyInfo).to.be.undefined; }); }); ================================================ FILE: test/saml-response-tests.spec.ts ================================================ import { SignedXml } from "../src/index"; import * as xpath from "xpath"; import * as xmldom from "@xmldom/xmldom"; import * as fs from "fs"; import { expect } from "chai"; import * as isDomNode from "@xmldom/is-dom-node"; describe("SAML response tests", function () { it("test validating SAML response", function () { const xml = fs.readFileSync("./test/static/valid_saml.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); const result = sig.checkSignature(xml); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("test validating SAML response with sha256-rsa-MGF1", function () { const xml = fs.readFileSync("./test/static/valid_saml_sha256_rsa_mgf1.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/idp_certificate.pem"); sig.loadSignature(signature); const result = sig.checkSignature(xml); expect(result).to.be.true; }); it("test validating SAML response with sha256-rsa-MGF1 fails for modified file", function () { const xml = fs.readFileSync("./test/static/invalid_saml_sha256_rsa_mgf1.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/idp_certificate.pem"); sig.loadSignature(signature); const result = sig.checkSignature(xml); expect(result).to.be.false; }); it("test validating wrapped assertion signature", function () { const xml = fs.readFileSync("./test/static/valid_saml_signature_wrapping.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const assertion = xpath.select1("//*[local-name(.)='Assertion']", doc); isDomNode.assertIsNodeLike(assertion); const signature = xpath.select1( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", assertion, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); expect( function () { sig.checkSignature(xml); }, "Should not validate a document which contains multiple elements with the " + "same value for the ID / Id / Id attributes, in order to prevent " + "signature wrapping attack.", ).to.throw(); expect(sig.getSignedReferences().length).to.equal(0); }); it("test validating SAML response where a namespace is defined outside the signed element", function () { const xml = fs.readFileSync("./test/static/saml_external_ns.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/saml_external_ns.pem"); sig.loadSignature(signature); const result = sig.checkSignature(xml); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("test reference id does not contain quotes", function () { const xml = fs.readFileSync("./test/static/id_with_quotes.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const assertion = xpath.select1("//*[local-name(.)='Assertion']", doc); isDomNode.assertIsNodeLike(assertion); const signature = xpath.select1( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", assertion, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); expect(function () { sig.checkSignature(xml); }, "id should not contain quotes").to.throw(); }); it("test validating SAML response WithComments", function () { const xml = fs.readFileSync("./test/static/valid_saml_withcomments.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); // This doesn't matter, just want to make sure that we don't fail due to unknown algorithm expect(() => sig.checkSignature(xml)).to.throw(/^invalid signature/); expect(sig.getSignedReferences().length).to.equal(0); }); it("throws an error for a document with no `SignedInfo` node", function () { const xml = fs.readFileSync("./test/static/invalid_saml_no_signed_info.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(node); const sig = new SignedXml(); const feidePublicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.publicCert = feidePublicCert; expect(() => sig.loadSignature(node)).to.throw("no signed info node found"); }); it("test validation ignores an additional wrapped `SignedInfo` node", function () { const xml = fs.readFileSync("./test/static/saml_wrapped_signed_info_node.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const assertion = xpath.select1("//*[local-name(.)='Assertion']", doc); isDomNode.assertIsNodeLike(assertion); const signature = xpath.select1( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", assertion, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/saml_external_ns.pem"); sig.loadSignature(signature); /* eslint-disable-next-line deprecation/deprecation */ expect(sig.getReferences().length).to.equal(1); const checkSignatureResult = sig.checkSignature(xml); expect(checkSignatureResult).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("test signature throws if multiple `SignedInfo` nodes are found", function () { const xml = fs.readFileSync("./test/static/saml_multiple_signed_info_nodes.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const assertion = xpath.select1("//*[local-name(.)='Assertion'][1]", doc); isDomNode.assertIsNodeLike(assertion); const signature = xpath.select1( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", assertion, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/saml_external_ns.pem"); expect(() => sig.loadSignature(signature)).to.throw( "could not load signature that contains multiple SignedInfo nodes", ); }); describe("for a SAML response with a digest value comment", () => { it("loads digest value from text content instead of comment", function () { const xml = fs.readFileSync("./test/static/valid_saml_with_digest_comment.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const assertion = xpath.select1("//*[local-name(.)='Assertion']", doc); isDomNode.assertIsNodeLike(assertion); const signature = xpath.select1( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", assertion, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/feide_public.pem"); sig.loadSignature(signature); /* eslint-disable-next-line deprecation/deprecation */ expect(sig.getReferences()[0].digestValue).to.equal("RnNjoyUguwze5w2R+cboyTHlkQk="); expect(sig.checkSignature(xml)).to.be.false; expect(sig.getSignedReferences().length).to.equal(0); }); }); }); ================================================ FILE: test/signature-integration-tests.spec.ts ================================================ import * as xpath from "xpath"; import * as xmldom from "@xmldom/xmldom"; import { SignedXml } from "../src/index"; import * as fs from "fs"; import { expect } from "chai"; import * as isDomNode from "@xmldom/is-dom-node"; describe("Signature integration tests", function () { function verifySignature(xml, expected, xpath, canonicalizationAlgorithm) { const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); xpath.map(function (n) { sig.addReference({ xpath: n, digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); }); sig.canonicalizationAlgorithm = canonicalizationAlgorithm; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signed = sig.getSignedXml(); const expectedContent = fs.readFileSync(expected).toString(); expect(signed, "signature xml different than expected").to.equal(expectedContent); } it("verify signature", function () { const xml = ''; verifySignature( xml, "./test/static/integration/expectedVerify.xml", ["//*[local-name(.)='x']", "//*[local-name(.)='y']", "//*[local-name(.)='w']"], "http://www.w3.org/2001/10/xml-exc-c14n#", ); }); it("verify signature of complex element", function () { const xml = "" + "" + "Harry Potter" + '' + "Joanne K" + "Rowling" + "" + "" + ""; verifySignature( xml, "./test/static/integration/expectedVerifyComplex.xml", ["//*[local-name(.)='book']"], "http://www.w3.org/2001/10/xml-exc-c14n#", ); }); it("empty URI reference should consider the whole document", function () { const xml = "" + "" + "Harry Potter" + "" + ""; const signature = '' + "" + '' + '' + '' + "" + '' + "" + '' + "1tjZsV007JgvE1YFe1C8sMQ+iEg=" + "" + "" + "FONRc5/nnQE2GMuEV0wK5/ofUJMHH7dzZ6VVd+oHDLfjfWax/lCMzUahJxW1i/dtm9Pl0t2FbJONVd3wwDSZzy6u5uCnj++iWYkRpIEN19RAzEMD1ejfZET8j3db9NeBq2JjrPbw81Fm7qKvte6jGa9ThTTB+1MHFRkC8qjukRM=" + ""; const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/client_public.pem"); sig.loadSignature(signature); const result = sig.checkSignature(xml); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("add canonicalization if output of transforms will be a node-set rather than an octet stream", function () { let xml = fs.readFileSync("./test/static/windows_store_signature.xml", "utf-8"); /** Make sure that whitespace in the source document is removed -- * @see xml-crypto issue #23 and post at * http://webservices20.blogspot.co.il/2013/06/validating-windows-mobile-app-store.html * This regex is naive but works for this test case; for a more general solution consider * the xmldom-fork-fixed library which can pass {ignoreWhiteSpace: true} into the Dom constructor. */ xml = xml.replace(/>\s*<"); const doc = new xmldom.DOMParser().parseFromString(xml); const childXml = doc.firstChild?.toString(); const signature = xpath.select1( "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/windows_store_certificate.pem"); sig.loadSignature(signature); const result = sig.checkSignature(childXml ?? ""); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("signature with inclusive namespaces", function () { const xml = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const childXml = doc.firstChild?.toString(); const signature = xpath.select1( "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.pem"); sig.loadSignature(signature); const result = sig.checkSignature(childXml ?? ""); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("signature with inclusive namespaces with unix line separators", function () { const xml = fs.readFileSync( "./test/static/signature_with_inclusivenamespaces_lines.xml", "utf-8", ); const doc = new xmldom.DOMParser().parseFromString(xml); const childXml = doc.firstChild?.toString(); const signature = xpath.select1( "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.pem"); sig.loadSignature(signature); const result = sig.checkSignature(childXml ?? ""); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("signature with inclusive namespaces with windows line separators", function () { const xml = fs.readFileSync( "./test/static/signature_with_inclusivenamespaces_lines_windows.xml", "utf-8", ); const doc = new xmldom.DOMParser().parseFromString(xml); const childXml = doc.firstChild?.toString(); const signature = xpath.select1( "//*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/signature_with_inclusivenamespaces.pem"); sig.loadSignature(signature); const result = sig.checkSignature(childXml ?? ""); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); it("should create single root xml document when signing inner node", function () { const xml = "" + "" + "Harry Potter" + "" + ""; const sig = new SignedXml(); sig.addReference({ xpath: "//*[local-name(.)='book']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signed = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signed); /* Expecting this structure: Harry Potter cdiS43aFDQMnb3X8yaIUej3+z9Q= J79hiSUrKdLOuX....Mthy1M= */ expect(doc.documentElement.nodeName, "root node = .").to.equal("library"); expect(doc.childNodes.length, "only one root node is expected.").to.equal(1); expect( doc.documentElement.childNodes.length, " should have two child nodes : and ", ).to.equal(2); }); }); ================================================ FILE: test/signature-object-tests.spec.ts ================================================ import * as fs from "fs"; import { expect, assert } from "chai"; import * as xpath from "xpath"; import * as xmldom from "@xmldom/xmldom"; import * as isDomNode from "@xmldom/is-dom-node"; import { SignedXml } from "../src"; import { Sha256 } from "../src/hash-algorithms"; const privateKey = fs.readFileSync("./test/static/client.pem", "utf-8"); const publicCert = fs.readFileSync("./test/static/client_public.pem", "utf-8"); const publicCertDer = fs.readFileSync("./test/static/client_public.der"); const selectNs = (expression: string, node: Node, ns?: Record) => xpath.useNamespaces({ ds: "http://www.w3.org/2000/09/xmldsig#", xades: "http://uri.etsi.org/01903/v1.3.2#", ...ns, })(expression, node, false); const select1Ns = (expression: string, node: Node, ns?: Record) => xpath.useNamespaces({ ds: "http://www.w3.org/2000/09/xmldsig#", xades: "http://uri.etsi.org/01903/v1.3.2#", ...ns, })(expression, node, true); const checkSignature = (signedXml: string, signedDoc: Document) => { const verifier = new SignedXml({ publicCert }); const signatureNode = select1Ns("//ds:Signature", signedDoc); isDomNode.assertIsNodeLike(signatureNode); verifier.loadSignature(signatureNode); const valid = verifier.checkSignature(signedXml); return { valid, errorMessage: verifier .getReferences() .flatMap((ref) => ref.validationError?.message || []) .join(", "), }; }; describe("ds:Object support in XML signatures", function () { it("should add custom ds:Object elements with attributes to the signature", function () { const xml = ''; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", objects: [ { content: "Test data in Object element", attributes: { Id: "object1", MimeType: "text/xml", Encoding: "", }, }, { content: "Plain text content", attributes: { Id: "object2", MimeType: "text/plain", }, }, { content: Buffer.from("This is base64 encoded data").toString("base64"), attributes: { Id: "object3", MimeType: "text/plain", Encoding: "http://www.w3.org/2000/09/xmldsig#base64", }, }, ], }); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); // Should have three Object elements const objectNodes = selectNs("/root/ds:Signature/ds:Object", doc); isDomNode.assertIsArrayOfNodes(objectNodes); expect(objectNodes.length).to.equal(3); // Verify the first Object element const object1 = objectNodes[0]; isDomNode.assertIsElementNode(object1); expect(object1.getAttribute("Id")).to.equal("object1"); expect(object1.getAttribute("MimeType")).to.equal("text/xml"); expect(object1.hasAttribute("Encoding")).to.be.true; expect(object1.getAttribute("Encoding")).to.equal(""); const object1Data = select1Ns("ds:Data", object1); isDomNode.assertIsElementNode(object1Data); expect(object1Data.textContent).to.equal("Test data in Object element"); // Verify the second Object element const object2 = objectNodes[1]; isDomNode.assertIsElementNode(object2); expect(object2.getAttribute("Id")).to.equal("object2"); expect(object2.getAttribute("MimeType")).to.equal("text/plain"); expect(object2.hasAttribute("Encoding")).to.be.false; expect(object2.textContent).to.equal("Plain text content"); // Verify the third Object element const object3 = objectNodes[2]; isDomNode.assertIsElementNode(object3); expect(object3.getAttribute("Id")).to.equal("object3"); expect(object3.getAttribute("MimeType")).to.equal("text/plain"); expect(object3.getAttribute("Encoding")).to.equal("http://www.w3.org/2000/09/xmldsig#base64"); assert(object3.textContent); expect(Buffer.from(object3.textContent, "base64").toString("utf-8")).to.equal( "This is base64 encoded data", ); }); it("should have correct ds:Object namespace when there is no default namespace", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", objects: [ { content: "Test data", attributes: { Id: "object1", MimeType: "text/plain", }, }, ], }); sig.addReference({ xpath: "/*", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); // When we add a prefix to the signature, there is no default namespace sig.computeSignature(xml, { prefix: "ds" }); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); // Verify the namespace of the ds:Object element const objectNode = select1Ns("/root/ds:Signature/ds:Object[@Id='object1']", doc); isDomNode.assertIsElementNode(objectNode); }); it("should handle empty or undefined objects", function () { const xml = ""; // Test with undefined objects const sigWithNull = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", objects: undefined, }); sigWithNull.addReference({ xpath: "/*", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sigWithNull.computeSignature(xml); const signedXmlWithNull = sigWithNull.getSignedXml(); const docWithNull = new xmldom.DOMParser().parseFromString(signedXmlWithNull); // Verify that no Object elements exist const objectNodesWithNull = selectNs("//ds:Object", docWithNull); isDomNode.assertIsArrayOfNodes(objectNodesWithNull); expect(objectNodesWithNull.length).to.equal(0); // Test with empty array objects const sigWithEmpty = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", objects: [], }); sigWithEmpty.addReference({ xpath: "/*", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sigWithEmpty.computeSignature(xml); const signedXmlWithEmpty = sigWithEmpty.getSignedXml(); const docWithEmpty = new xmldom.DOMParser().parseFromString(signedXmlWithEmpty); // Verify that no Object elements exist const objectNodesWithEmpty = selectNs("//ds:Object", docWithEmpty); isDomNode.assertIsArrayOfNodes(objectNodesWithEmpty); expect(objectNodesWithEmpty.length).to.equal(0); }); it("should handle Reference to Object", function () { const xml = ""; const sig = new SignedXml({ privateKey: privateKey, signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", objects: [ { content: "Content", attributes: { Id: "object1", }, }, ], }); sig.addReference({ xpath: "//*[local-name(.)='Object' and @Id='object1']", inclusiveNamespacesPrefixList: ["ns1", "ns2"], digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const signedDoc = new xmldom.DOMParser().parseFromString(signedXml); // Verify that there is exactly one ds:Reference const referenceNodes = selectNs("/root/ds:Signature/ds:SignedInfo/ds:Reference", signedDoc); isDomNode.assertIsArrayOfNodes(referenceNodes); expect(referenceNodes.length).to.equal(1); const referenceEl = referenceNodes[0]; isDomNode.assertIsElementNode(referenceEl); // Verify that the Reference URI points to the Object expect(referenceEl.getAttribute("URI")).to.equal("#object1"); // Verify that the Reference contains the correct Transform const transformEl = select1Ns("ds:Transforms/ds:Transform", referenceEl); isDomNode.assertIsElementNode(transformEl); expect(transformEl.getAttribute("Algorithm")).to.equal( "http://www.w3.org/2001/10/xml-exc-c14n#", ); // Verify that the InclusiveNamespacesPrefixList is set correctly const inclusiveNamespacesEl = select1Ns("ec:InclusiveNamespaces", transformEl, { ec: "http://www.w3.org/2001/10/xml-exc-c14n#", }); isDomNode.assertIsElementNode(inclusiveNamespacesEl); expect(inclusiveNamespacesEl.getAttribute("PrefixList")).to.equal("ns1 ns2"); // Verify that the Reference contains the correct DigestMethod const digestMethodEl = select1Ns("ds:DigestMethod", referenceEl); isDomNode.assertIsElementNode(digestMethodEl); expect(digestMethodEl.getAttribute("Algorithm")).to.equal( "http://www.w3.org/2000/09/xmldsig#sha1", ); // Verify that the Reference contains a non-empty DigestValue const digestValueEl = select1Ns("ds:DigestValue", referenceEl); isDomNode.assertIsElementNode(digestValueEl); expect(digestValueEl.textContent).to.not.be.empty; }); }); describe("Valid signatures with ds:Object elements", function () { it("should create valid signatures with NO references to ds:Object", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", objects: [ { content: "Test data in Object element", attributes: { Id: "object1", MimeType: "text/xml", }, }, ], }); sig.addReference({ xpath: "/*", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); // Verify that the signature is valid const { valid, errorMessage } = checkSignature(signedXml, doc); expect(valid, errorMessage).to.be.true; }); it("should create valid signatures with references to ds:Object", () => { const xml = ''; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", inclusiveNamespacesPrefixList: ["ns1", "ns2"], objects: [ { content: 'Test data in Object element', attributes: { Id: "object1", MimeType: "text/xml", }, }, ], }); sig.addReference({ xpath: "/*", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.addReference({ xpath: "//*[local-name(.)='Object' and @Id='object1']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], inclusiveNamespacesPrefixList: ["ns1", "ns3"], }); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); // Verify that there are two Reference elements const referenceNodes = selectNs("/ns1:root/ds:Signature/ds:SignedInfo/ds:Reference", doc, { ns1: "uri:ns1", }); isDomNode.assertIsArrayOfNodes(referenceNodes); expect(referenceNodes.length).to.equal(2); // Verify that the second Reference points to the ds:Object const objectReference = referenceNodes[1]; isDomNode.assertIsElementNode(objectReference); expect(objectReference.getAttribute("URI")).to.equal("#object1"); // Verify that the signature is valid const { valid, errorMessage } = checkSignature(signedXml, doc); expect(valid, errorMessage).to.be.true; }); it("should create valid signature and generate Id attribute for ds:Object when not provided", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", objects: [ { content: "Test data in Object element", }, ], }); sig.addReference({ xpath: "//*[local-name(.)='Data']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.computeSignature(xml, { prefix: "ds" }); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); // Find the ds:Object/Data element and get the value of its Id attribute (ensuring it was generated) const dataEl = select1Ns("/root/ds:Signature/ds:Object/Data[@Id]", doc); isDomNode.assertIsElementNode(dataEl); const idValue = dataEl.getAttribute("Id"); expect(idValue).to.be.a("string").that.is.not.empty; // Verify that there is a Reference pointing to the generated Id const uri = `#${idValue}`; const refEl = select1Ns(`/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='${uri}']`, doc); isDomNode.assertIsElementNode(refEl); // Verify that the signature is valid const { valid, errorMessage } = checkSignature(signedXml, doc); expect(valid, errorMessage).to.be.true; }); }); describe("Should successfuly sign references to ds:KeyInfo elements", function () { it("should create valid signatures with references to ds:KeyInfo when the Id attribute is provided", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", keyInfoAttributes: { Id: "key-info-1", }, getKeyInfoContent: () => "", }); sig.addReference({ xpath: "//*[local-name(.)='KeyInfo']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); // Verify that there is a Reference to KeyInfo const referenceEl = select1Ns( "/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='#key-info-1']", doc, ); isDomNode.assertIsElementNode(referenceEl); // Verify that the signature is valid const { valid, errorMessage } = checkSignature(signedXml, doc); expect(valid, errorMessage).to.be.true; }); it("should create valid signatures with references to ds:KeyInfo when the Id attribute is autogenerated", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", getKeyInfoContent: () => "", }); sig.addReference({ xpath: "//*[local-name(.)='KeyInfo']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); // Find the KeyInfo element and get the value of its Id attribute (ensuring it was generated) const keyInfoEl = select1Ns("/root/ds:Signature/ds:KeyInfo[@Id]", doc); isDomNode.assertIsElementNode(keyInfoEl); const idValue = keyInfoEl.getAttribute("Id"); expect(idValue).to.be.a("string").that.is.not.empty; // Find a Reference with URI=`#${idValue}` const uri = `#${idValue}`; const referenceEl = select1Ns( `/root/ds:Signature/ds:SignedInfo/ds:Reference[@URI='${uri}']`, doc, ); isDomNode.assertIsElementNode(referenceEl); // Verify that the signature is valid const { valid, errorMessage } = checkSignature(signedXml, doc); expect(valid, errorMessage).to.be.true; }); }); describe("XAdES Object support in XML signatures", function () { it("should be able to add and sign XAdES objects", function () { const signatureId = "signature_0"; const signedPropertiesId = "signedProperties_0"; const publicCertDigest = new Sha256().getHash(publicCertDer); const xml = `text`; const sig = new SignedXml({ publicCert, privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", objects: [ { content: `` + `` + `` + `2025-06-21T12:00:00Z` + `` + `` + `${publicCertDigest}` + `` + `` + `` + ``, }, ], }); sig.addReference({ xpath: `/*`, isEmptyUri: true, digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256", transforms: [ "http://www.w3.org/2000/09/xmldsig#enveloped-signature", "http://www.w3.org/2001/10/xml-exc-c14n#", ], }); sig.addReference({ xpath: `//*[@Id='${signedPropertiesId}']`, type: "http://uri.etsi.org/01903#SignedProperties", digestAlgorithm: "http://www.w3.org/2001/04/xmlenc#sha256", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.computeSignature(xml, { prefix: "ds", location: { action: "append", reference: "/root", }, attrs: { Id: signatureId, }, }); const signedXml = sig.getSignedXml(); const signedDoc = new xmldom.DOMParser().parseFromString(signedXml); // ds:Signature exists and has the expected Id const elSig = select1Ns(`/root/ds:Signature[@Id='${signatureId}']`, signedDoc); isDomNode.assertIsElementNode(elSig); // ds:Object/xades:QualifyingProperties exists within the signature const elQP = select1Ns("ds:Object/xades:QualifyingProperties", elSig); isDomNode.assertIsElementNode(elQP); // The Reference to SignedProperties exists and has the correct URI and Type const elSPRef = select1Ns( `ds:SignedInfo/ds:Reference[@URI='#${signedPropertiesId}' and @Type='http://uri.etsi.org/01903#SignedProperties']`, elSig, ); isDomNode.assertIsElementNode(elSPRef); // Verify that the signature is valid const { valid, errorMessage } = checkSignature(signedXml, signedDoc); expect(valid, errorMessage).to.be.true; }); }); describe("Signature self-reference prevention", function () { it("should not allow self-referencing the Signature element", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", }); sig.addReference({ xpath: ".//*[local-name(.)='Signature']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); expect(() => { sig.computeSignature(xml); }).to.throw(/Cannot sign a reference to the Signature or SignedInfo element itself/); }); it("should not allow self-referencing the SignedInfo element", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", }); sig.addReference({ xpath: ".//*[local-name(.)='SignedInfo']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); expect(() => { sig.computeSignature(xml); }).to.throw(/Cannot sign a reference to the Signature or SignedInfo element itself/); }); it("should not allow signing children of the SignedInfo element", function () { const xml = ""; const sig = new SignedXml({ privateKey, canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", }); sig.addReference({ xpath: "/*", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.addReference({ xpath: ".//*[local-name(.)='Reference']/*", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); expect(() => { sig.computeSignature(xml); }).to.throw(/Cannot sign a reference to the Signature or SignedInfo element itself/); }); }); ================================================ FILE: test/signature-unit-tests.spec.ts ================================================ import * as xpath from "xpath"; import * as xmldom from "@xmldom/xmldom"; import { SignedXml, createOptionalCallbackFunction } from "../src/index"; import * as fs from "fs"; import * as crypto from "crypto"; import { expect } from "chai"; import * as isDomNode from "@xmldom/is-dom-node"; const signatureAlgorithms = [ "http://www.w3.org/2000/09/xmldsig#rsa-sha1", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", "http://www.w3.org/2007/05/xmldsig-more#sha256-rsa-MGF1", "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512", ]; describe("Signature unit tests", function () { describe("sign and verify", function () { signatureAlgorithms.forEach((signatureAlgorithm) => { function signWith(signatureAlgorithm: string): string { const xml = ''; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = signatureAlgorithm; sig.computeSignature(xml); return sig.getSignedXml(); } function loadSignature(xml: string): SignedXml { const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(node); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/client_public.pem"); sig.loadSignature(node); return sig; } it(`should verify signed xml with ${signatureAlgorithm}`, function () { const xml = signWith(signatureAlgorithm); const sig = loadSignature(xml); const res = sig.checkSignature(xml); expect( res, `expected all signatures with ${signatureAlgorithm} to be valid, but some reported invalid`, ).to.be.true; }); it(`should fail verification of signed xml with ${signatureAlgorithm} after manipulation`, function () { const xml = signWith(signatureAlgorithm); const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1("//*[local-name(.)='x']", doc); isDomNode.assertIsElementNode(node); const targetElement = node as Element; targetElement.setAttribute("attr", "manipulatedValue"); const manipulatedXml = new xmldom.XMLSerializer().serializeToString(doc); const sig = loadSignature(manipulatedXml); const res = sig.checkSignature(manipulatedXml); expect( res, `expected all signatures with ${signatureAlgorithm} to be invalid, but some reported valid`, ).to.be.false; }); }); }); describe("verify adds ID", function () { function nodeExists(doc, xpathArg) { if (!doc && !xpathArg) { return; } const node = xpath.select(xpathArg, doc); isDomNode.assertIsArrayOfNodes(node); expect(node.length, `xpath ${xpathArg} not found`).to.equal(1); } function verifyAddsId(mode, nsMode) { const xml = ''; const sig = new SignedXml({ idMode: mode }); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.addReference({ xpath: "//*[local-name(.)='y']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.addReference({ xpath: "//*[local-name(.)='w']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getOriginalXmlWithIds(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const op = nsMode === "equal" ? "=" : "!="; const xpathArg = `//*[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']]`; //verify each of the signed nodes now has an "Id" attribute with the right value nodeExists(doc, xpathArg.replace("{id}", "0").replace("{elem}", "x")); nodeExists(doc, xpathArg.replace("{id}", "1").replace("{elem}", "y")); nodeExists(doc, xpathArg.replace("{id}", "2").replace("{elem}", "w")); } it("signer adds increasing different id attributes to elements", function () { verifyAddsId(null, "different"); }); it("signer adds increasing equal id attributes to elements", function () { verifyAddsId("wssecurity", "equal"); }); }); it("signer adds references with namespaces", function () { const xml = 'xml-cryptogithub'; const sig = new SignedXml({ idMode: "wssecurity" }); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[@wsu:Id]", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { existingPrefixes: { wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", }, }); const signedXml = sig.getSignatureXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const references = xpath.select("//*[local-name(.)='Reference']", doc); isDomNode.assertIsArrayOfNodes(references); expect(references.length).to.equal(2); }); describe("signer does not duplicate id attributes", function () { function verifyDoesNotDuplicateIdAttributes(prefix: string, idMode?: "wssecurity") { const xml = ``; const sig = new SignedXml({ idMode }); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getOriginalXmlWithIds(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const attrs = xpath.select("//@*", doc); isDomNode.assertIsArrayOfNodes(attrs); expect(attrs.length, "wrong number of attributes").to.equal(2); } it("signer does not implicitly duplicate existing id attributes", function () { verifyDoesNotDuplicateIdAttributes(""); }); it("signer does not explicitly duplicate existing id attributes", function () { verifyDoesNotDuplicateIdAttributes("wsu:", "wssecurity"); }); }); it("signer adds custom attributes to the signature root node", function () { const xml = 'xml-cryptogithub'; const sig = new SignedXml(); const attrs = { Id: "signatureTest", data: "dataValue", xmlns: "http://custom-xmlns#", }; sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='name']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { attrs: attrs, }); const signedXml = sig.getSignatureXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const signatureNode = doc.documentElement; expect(attrs.Id, `Id attribute is not equal to the expected value: "${attrs.Id}"`).to.equal( signatureNode.getAttribute("Id"), ); expect( attrs.data, `data attribute is not equal to the expected value: "${attrs.data}"`, ).to.equal(signatureNode.getAttribute("data")); expect(attrs.xmlns, "xmlns attribute can not be overridden").not.to.equal( signatureNode.getAttribute("xmlns"), ); expect( signatureNode.getAttribute("xmlns"), 'xmlns attribute is not equal to the expected value: "http://www.w3.org/2000/09/xmldsig#"', ).to.equal("http://www.w3.org/2000/09/xmldsig#"); }); it("signer appends signature to the root node by default", function () { const xml = "xml-cryptogithub"; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='name']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); const lastChild = doc.documentElement.lastChild; isDomNode.assertIsElementNode(lastChild); expect( lastChild.localName, "the signature must be appended to the root node by default", ).to.equal("Signature"); }); it("signer appends signature to a reference node", function () { const xml = "xml-cryptogithub"; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='repository']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { location: { reference: "/root/name", action: "append", }, }); const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); const referenceNode = xpath.select1("/root/name", doc); isDomNode.assertIsNodeLike(referenceNode); const lastChild = referenceNode.lastChild; isDomNode.assertIsElementNode(lastChild); expect(lastChild.localName, "the signature should be appended to root/name").to.equal( "Signature", ); }); it("signer prepends signature to a reference node", function () { const xml = "xml-cryptogithub"; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='repository']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { location: { reference: "/root/name", action: "prepend", }, }); const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); const referenceNode = xpath.select1("/root/name", doc); isDomNode.assertIsNodeLike(referenceNode); const firstChild = referenceNode.firstChild; isDomNode.assertIsElementNode(firstChild); expect(firstChild.localName, "the signature should be prepended to root/name").to.equal( "Signature", ); }); it("signer inserts signature before a reference node", function () { const xml = "xml-cryptogithub"; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='repository']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { location: { reference: "/root/name", action: "before", }, }); const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); const referenceNode = xpath.select1("/root/name", doc); isDomNode.assertIsNodeLike(referenceNode); const previousSibling = referenceNode.previousSibling; isDomNode.assertIsElementNode(previousSibling); expect( previousSibling.localName, "the signature should be inserted before to root/name", ).to.equal("Signature"); }); it("signer inserts signature after a reference node", function () { const xml = "xml-cryptogithub"; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='repository']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { location: { reference: "/root/name", action: "after", }, }); const doc = new xmldom.DOMParser().parseFromString(sig.getSignedXml()); const referenceNode = xpath.select1("/root/name", doc); isDomNode.assertIsNodeLike(referenceNode); const nextSibling = referenceNode.nextSibling; isDomNode.assertIsElementNode(nextSibling); expect(nextSibling.localName, "the signature should be inserted after to root/name").to.equal( "Signature", ); }); it("signer creates signature with correct structure", function () { class DummyDigest { getHash = function () { return "dummy digest"; }; getAlgorithmName = function () { return "dummy digest algorithm"; }; } class DummySignatureAlgorithm { verifySignature = function () { return true; }; getSignature = function () { return "dummy signature"; }; getAlgorithmName = function () { return "dummy algorithm"; }; } class DummyTransformation { includeComments = false; process = function () { return "< x/>"; }; getAlgorithmName = function () { return "dummy transformation"; }; } class DummyCanonicalization { includeComments = false; process = function () { return "< x/>"; }; getAlgorithmName = function () { return "dummy canonicalization"; }; } const xml = ''; const sig = new SignedXml(); 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 () { return "dummy key info"; }; sig.canonicalizationAlgorithm = "http://DummyCanonicalization"; sig.privateKey = ""; sig.addReference({ xpath: "//*[local-name(.)='x']", transforms: ["http://DummyTransformation"], digestAlgorithm: "http://dummyDigest", }); sig.addReference({ xpath: "//*[local-name(.)='y']", transforms: ["http://DummyTransformation"], digestAlgorithm: "http://dummyDigest", }); sig.addReference({ xpath: "//*[local-name(.)='w']", transforms: ["http://DummyTransformation"], digestAlgorithm: "http://dummyDigest", }); sig.computeSignature(xml); const signature = sig.getSignatureXml(); const expected = '' + "" + '' + '' + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + "" + "dummy signature" + "" + "dummy key info" + "" + ""; expect(expected, "wrong signature format").to.equal(signature); const signedXml = sig.getSignedXml(); const expectedSignedXml = '' + '' + "" + '' + '' + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + "" + "dummy signature" + "" + "dummy key info" + "" + "" + ""; expect(expectedSignedXml, "wrong signedXml format").to.equal(signedXml); const originalXmlWithIds = sig.getOriginalXmlWithIds(); const expectedOriginalXmlWithIds = ''; expect(expectedOriginalXmlWithIds, "wrong OriginalXmlWithIds").to.equal(originalXmlWithIds); }); it("signer creates signature with correct structure (with prefix)", function () { const prefix = "ds"; class DummyDigest { getHash = function () { return "dummy digest"; }; getAlgorithmName = function () { return "dummy digest algorithm"; }; } class DummySignatureAlgorithm { verifySignature = function () { return true; }; getSignature = function () { return "dummy signature"; }; getAlgorithmName = function () { return "dummy algorithm"; }; } class DummyTransformation { includeComments = false; process = function () { return "< x/>"; }; getAlgorithmName = function () { return "dummy transformation"; }; } class DummyCanonicalization { includeComments = false; process = function () { return "< x/>"; }; getAlgorithmName = function () { return "dummy canonicalization"; }; } const xml = ''; const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/client_public.pem"); 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.canonicalizationAlgorithm = "http://DummyCanonicalization"; sig.privateKey = ""; sig.addReference({ xpath: "//*[local-name(.)='x']", transforms: ["http://DummyTransformation"], digestAlgorithm: "http://dummyDigest", }); sig.addReference({ xpath: "//*[local-name(.)='y']", transforms: ["http://DummyTransformation"], digestAlgorithm: "http://dummyDigest", }); sig.addReference({ xpath: "//*[local-name(.)='w']", transforms: ["http://DummyTransformation"], digestAlgorithm: "http://dummyDigest", }); sig.computeSignature(xml, { prefix: prefix }); const signature = sig.getSignatureXml(); const expected = '' + "" + '' + '' + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + "" + "dummy signature" + "" + "" + "MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl" + "" + "" + ""; expect(signature, "wrong signature format").to.equal(expected); const signedXml = sig.getSignedXml(); const expectedSignedXml = '' + '' + "" + '' + '' + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + '' + "" + '' + "" + '' + "dummy digest" + "" + "" + "dummy signature" + "" + "" + "MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl" + "" + "" + "" + ""; expect(signedXml, "wrong signedXml format").to.equal(expectedSignedXml); const originalXmlWithIds = sig.getOriginalXmlWithIds(); const expectedOriginalXmlWithIds = ''; expect(originalXmlWithIds, "wrong OriginalXmlWithIds").to.equal(expectedOriginalXmlWithIds); }); it("signer creates correct signature values", function () { const xml = ''; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.addReference({ xpath: "//*[local-name(.)='y']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.addReference({ xpath: "//*[local-name(.)='w']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const expected = '' + '' + "" + '' + '' + '' + "" + '' + '' + "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=" + "" + '' + "" + '' + "" + '' + "4Pq/sBri+AyOtxtSFsPSOyylyzk=" + "" + '' + "" + '' + "" + '' + "6I7SDu1iV2YOajTlf+iMLIBfLnE=" + "" + "" + "NejzGB9MDUddKCt3GL2vJhEd5q6NBuhLdQc3W4bJI5q34hk7Hk6zBRoW3OliX+/f7Hpi9y0INYoqMSUfrsAVm3IuPzUETKlI6xiNZo07ULRj1DwxRo6cU66ar1EKUQLRuCZas795FjB8jvUI2lyhcax/00uMJ+Cjf4bwAQ+9gOQ=" + "" + ""; expect(expected, "wrong signature format").to.equal(signedXml); }); it("signer creates correct signature values using async callback", function () { class DummySignatureAlgorithm { verifySignature = function () { return true; }; getSignature = createOptionalCallbackFunction( (signedInfo: crypto.BinaryLike, privateKey: crypto.KeyLike) => { const signer = crypto.createSign("RSA-SHA1"); signer.update(signedInfo); const res = signer.sign(privateKey, "base64"); return res; }, ); getAlgorithmName = function () { return "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; }; } const xml = ''; const sig = new SignedXml(); sig.SignatureAlgorithms["http://dummySignatureAlgorithmAsync"] = DummySignatureAlgorithm; sig.signatureAlgorithm = "http://dummySignatureAlgorithmAsync"; sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.addReference({ xpath: "//*[local-name(.)='y']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.addReference({ xpath: "//*[local-name(.)='w']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.computeSignature(xml, function () { const signedXml = sig.getSignedXml(); const expected = '' + '' + "" + '' + '' + '' + "" + '' + '' + "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=" + "" + '' + "" + '' + "" + '' + "4Pq/sBri+AyOtxtSFsPSOyylyzk=" + "" + '' + "" + '' + "" + '' + "6I7SDu1iV2YOajTlf+iMLIBfLnE=" + "" + "" + "NejzGB9MDUddKCt3GL2vJhEd5q6NBuhLdQc3W4bJI5q34hk7Hk6zBRoW3OliX+/f7Hpi9y0INYoqMSUfrsAVm3IuPzUETKlI6xiNZo07ULRj1DwxRo6cU66ar1EKUQLRuCZas795FjB8jvUI2lyhcax/00uMJ+Cjf4bwAQ+9gOQ=" + "" + ""; expect(expected, "wrong signature format").to.equal(signedXml); }); }); describe("verify existing signature", function () { describe("pass loading signatures", function () { function passLoadSignature(file: string, toString?: boolean) { const xml = fs.readFileSync(file, "utf8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsElementNode(signature); const sig = new SignedXml(); sig.loadSignature(toString ? signature.toString() : signature); expect(sig.canonicalizationAlgorithm, "wrong canonicalization method").to.equal( "http://www.w3.org/2001/10/xml-exc-c14n#", ); expect(sig.signatureAlgorithm, "wrong signature method").to.equal( "http://www.w3.org/2000/09/xmldsig#rsa-sha1", ); sig.getCertFromKeyInfo = (keyInfo) => { isDomNode.assertIsNodeLike(keyInfo); const keyInfoContents = xpath.select1( "//*[local-name(.)='KeyInfo']/*[local-name(.)='dummyKey']", keyInfo, ); isDomNode.assertIsNodeLike(keyInfoContents); const firstChild = keyInfoContents.firstChild; isDomNode.assertIsTextNode(firstChild); expect(firstChild.data, "keyInfo clause not correctly loaded").to.equal("1234"); return fs.readFileSync("./test/static/client.pem", "latin1"); }; const checkedSignature = sig.checkSignature(xml); expect(checkedSignature).to.be.true; /* eslint-disable-next-line deprecation/deprecation */ expect(sig.getReferences().length).to.equal(3); expect(sig.getSignedReferences().length).to.equal(3); const digests = [ "b5GCZ2xpP5T7tbLWBTkOl4CYupQ=", "K4dI497ZCxzweDIrbndUSmtoezY=", "sH1gxKve8wlU8LlFVa2l6w3HMJ0=", ]; const firstGrandchild = doc.firstChild?.firstChild; isDomNode.assertIsElementNode(firstGrandchild); const matchedReference = sig.validateElementAgainstReferences(firstGrandchild, doc); expect(matchedReference).to.not.be.false; /* eslint-disable-next-line deprecation/deprecation */ for (let i = 0; i < sig.getReferences().length; i++) { /* eslint-disable-next-line deprecation/deprecation */ const ref = sig.getReferences()[i]; const expectedUri = `#_${i}`; expect( ref.uri, `wrong uri for index ${i}. expected: ${expectedUri} actual: ${ref.uri}`, ).to.equal(expectedUri); expect(ref.transforms.length).to.equal(1); expect(ref.transforms[0]).to.equal("http://www.w3.org/2001/10/xml-exc-c14n#"); expect(ref.digestValue).to.equal(digests[i]); expect(ref.digestAlgorithm).to.equal("http://www.w3.org/2000/09/xmldsig#sha1"); } } it("correctly loads signature", function () { passLoadSignature("./test/static/valid_signature.xml"); }); it("correctly loads signature with validation", function () { passLoadSignature("./test/static/valid_signature.xml", true); }); it("correctly loads signature with root level sig namespace", function () { passLoadSignature("./test/static/valid_signature_with_root_level_sig_namespace.xml"); }); }); describe("pass verify signature", function () { function loadSignature(xml: string, idMode?: "wssecurity") { const doc = new xmldom.DOMParser().parseFromString(xml); const node = xpath.select1( "//*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(node); const sig = new SignedXml({ idMode }); sig.publicCert = fs.readFileSync("./test/static/client_public.pem"); sig.loadSignature(node); return sig; } function passValidSignature(file: string, mode?: "wssecurity") { const xml = fs.readFileSync(file, "utf8"); const sig = loadSignature(xml, mode); const res = sig.checkSignature(xml); expect(res, "expected all signatures to be valid, but some reported invalid").to.be.true; /* eslint-disable-next-line deprecation/deprecation */ expect(sig.getSignedReferences().length).to.equal(sig.getReferences().length); } function failInvalidSignature(file: string, idMode?: "wssecurity") { const xml = fs.readFileSync(file).toString(); const sig = loadSignature(xml, idMode); const res = sig.checkSignature(xml); expect(res, "expected a signature to be invalid, but all were reported valid").to.be.false; expect(sig.getSignedReferences().length).to.equal(0); } function throwsValidatingSignature(file: string, idMode?: "wssecurity") { const xml = fs.readFileSync(file).toString(); const sig = loadSignature(xml, idMode); expect( () => sig.checkSignature(xml), "expected an error to be thrown because signatures couldn't be checked for validity", ).to.throw(); expect(sig.getSignedReferences().length).to.equal(0); } it("verifies valid signature", function () { passValidSignature("./test/static/valid_signature.xml"); }); it("verifies valid signature with lowercase id attribute", function () { passValidSignature("./test/static/valid_signature_with_lowercase_id_attribute.xml"); }); it("verifies valid signature with wsu", function () { passValidSignature("./test/static/valid_signature wsu.xml", "wssecurity"); }); it("verifies valid signature with reference keyInfo", function () { passValidSignature("./test/static/valid_signature_with_reference_keyInfo.xml"); }); it("verifies valid signature with whitespace in digestvalue", function () { passValidSignature("./test/static/valid_signature_with_whitespace_in_digestvalue.xml"); }); it("verifies valid utf8 signature", function () { passValidSignature("./test/static/valid_signature_utf8.xml"); }); it("verifies valid signature with unused prefixes", function () { passValidSignature("./test/static/valid_signature_with_unused_prefixes.xml"); }); it("verifies valid signature without transforms element", function () { passValidSignature("./test/static/valid_signature_without_transforms_element.xml"); }); it("throws validating signature - signature value", function () { throwsValidatingSignature("./test/static/invalid_signature - signature value.xml"); }); it("fails invalid signature - hash", function () { failInvalidSignature("./test/static/invalid_signature - hash.xml"); }); it("fails invalid signature - non existing reference", function () { failInvalidSignature("./test/static/invalid_signature - non existing reference.xml"); }); it("fails invalid signature - changed content", function () { failInvalidSignature("./test/static/invalid_signature - changed content.xml"); }); it("fails invalid signature - wsu - invalid signature value", function () { failInvalidSignature( "./test/static/invalid_signature - wsu - invalid signature value.xml", "wssecurity", ); }); it("fails invalid signature - wsu - hash", function () { failInvalidSignature("./test/static/invalid_signature - wsu - hash.xml", "wssecurity"); }); it("fails invalid signature - wsu - non existing reference", function () { failInvalidSignature( "./test/static/invalid_signature - wsu - non existing reference.xml", "wssecurity", ); }); it("fails invalid signature - wsu - changed content", function () { failInvalidSignature( "./test/static/invalid_signature - wsu - changed content.xml", "wssecurity", ); }); it("fails invalid signature without transforms element", function () { failInvalidSignature("./test/static/invalid_signature_without_transforms_element.xml"); }); }); }); it("allow empty reference uri when signing", function () { const xml = ""; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='root']", transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", uri: "", digestValue: "", inclusiveNamespacesPrefixList: [], isEmptyUri: true, }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const URI = xpath.select1("//*[local-name(.)='Reference']/@URI", doc); isDomNode.assertIsAttributeNode(URI); 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(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='repository']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); try { sig.computeSignature(xml, { location: { reference: "/root/foobar", action: "append", }, }); expect.fail("Expected an error to be thrown"); } catch (err) { expect(err).not.to.be.an.instanceof(TypeError); } }); it("signer adds existing prefixes", function () { function getKeyInfoContentWithAssertionId({ assertionId }) { return ( ` ` + `${assertionId}` + `` ); } const xml = ' ' + " " + " ' + " " + " " + " " + ""; const sig = new SignedXml(); const assertionId = "_81d5fba5c807be9e9cf60c58566349b1"; sig.getKeyInfoContent = getKeyInfoContentWithAssertionId.bind(this, { assertionId }); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml, { prefix: "ds", location: { reference: "//Assertion", action: "after", }, existingPrefixes: { wsse: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd", wsu: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd", }, }); 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 = ""; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='root']", transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", uri: "", digestValue: "", inclusiveNamespacesPrefixList: ["prefix1", "prefix2"], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const inclusiveNamespaces = xpath.select( "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement, ); isDomNode.assertIsArrayOfNodes(inclusiveNamespaces); expect(inclusiveNamespaces.length, "InclusiveNamespaces element should exist").to.equal(1); const firstNamespace = inclusiveNamespaces[0]; isDomNode.assertIsElementNode(firstNamespace); const prefixListAttribute = firstNamespace.getAttribute("PrefixList"); expect( prefixListAttribute, "InclusiveNamespaces element should have the correct PrefixList attribute value", ).to.equal("prefix1 prefix2"); }); it("does not create InclusiveNamespaces element when inclusiveNamespacesPrefixList is not set on Reference", function () { const xml = ""; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='root']", transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", uri: "", digestValue: "", inclusiveNamespacesPrefixList: [], }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const inclusiveNamespaces = xpath.select1( "//*[local-name(.)='Reference']/*[local-name(.)='Transforms']/*[local-name(.)='Transform']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement, ); expect(inclusiveNamespaces, "InclusiveNamespaces element should not exist").to.be.undefined; }); it("creates InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is set on SignedXml options", function () { const xml = ""; const sig = new SignedXml({ inclusiveNamespacesPrefixList: "prefix1 prefix2" }); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='root']", transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const inclusiveNamespaces = xpath.select( "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement, ); isDomNode.assertIsArrayOfNodes(inclusiveNamespaces); expect( inclusiveNamespaces.length, "InclusiveNamespaces element should exist inside CanonicalizationMethod", ).to.equal(1); const firstNamespace = inclusiveNamespaces[0]; isDomNode.assertIsElementNode(firstNamespace); const prefixListAttribute = firstNamespace.getAttribute("PrefixList"); expect( prefixListAttribute, "InclusiveNamespaces element inside CanonicalizationMethod should have the correct PrefixList attribute value", ).to.equal("prefix1 prefix2"); }); it("does not create InclusiveNamespaces element inside CanonicalizationMethod when inclusiveNamespacesPrefixList is not set on SignedXml options", function () { const xml = ""; const sig = new SignedXml(); // Omit inclusiveNamespacesPrefixList property sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='root']", transforms: ["http://www.w3.org/2000/09/xmldsig#enveloped-signature"], digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const inclusiveNamespaces = xpath.select1( "//*[local-name(.)='CanonicalizationMethod']/*[local-name(.)='InclusiveNamespaces']", doc.documentElement, ); expect( inclusiveNamespaces, "InclusiveNamespaces element should not exist inside CanonicalizationMethod", ).to.be.undefined; }); it("adds attributes to KeyInfo element when attrs are present in keyInfoProvider", function () { const xml = ""; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.keyInfoAttributes = { CustomUri: "http://www.example.com/keyinfo", CustomAttribute: "custom-value", }; sig.getKeyInfoContent = () => ""; sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const keyInfoElements = xpath.select("//*[local-name(.)='KeyInfo']", doc.documentElement); isDomNode.assertIsArrayOfNodes(keyInfoElements); expect(keyInfoElements.length, "KeyInfo element should exist").to.equal(1); const keyInfoElement = keyInfoElements[0]; isDomNode.assertIsElementNode(keyInfoElement); const algorithmAttribute = keyInfoElement.getAttribute("CustomUri"); expect( algorithmAttribute, "KeyInfo element should have the correct CustomUri attribute value", ).to.equal("http://www.example.com/keyinfo"); const customAttribute = keyInfoElement.getAttribute("CustomAttribute"); expect( customAttribute, "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 = ""; const sig = new SignedXml(); const pemBuffer = fs.readFileSync("./test/static/client_bundle.pem"); sig.privateKey = pemBuffer; 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.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const x509certificates = xpath.select( "//*[local-name(.)='X509Certificate']", doc.documentElement, ); isDomNode.assertIsArrayOfNodes(x509certificates); 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", ); }); it("adds id and type attributes to Reference elements when provided", function () { const xml = ""; const sig = new SignedXml(); sig.privateKey = fs.readFileSync("./test/static/client.pem"); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], id: "ref-1", type: "http://www.w3.org/2000/09/xmldsig#Object", }); sig.canonicalizationAlgorithm = "http://www.w3.org/2001/10/xml-exc-c14n#"; sig.signatureAlgorithm = "http://www.w3.org/2000/09/xmldsig#rsa-sha1"; sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const referenceElements = xpath.select("//*[local-name(.)='Reference']", doc); isDomNode.assertIsArrayOfNodes(referenceElements); expect(referenceElements.length, "Reference element should exist").to.equal(1); const referenceElement = referenceElements[0]; isDomNode.assertIsElementNode(referenceElement); const idAttribute = referenceElement.getAttribute("Id"); expect(idAttribute, "Reference element should have the correct Id attribute value").to.equal( "ref-1", ); const typeAttribute = referenceElement.getAttribute("Type"); expect( typeAttribute, "Reference element should have the correct Type attribute value", ).to.equal("http://www.w3.org/2000/09/xmldsig#Object"); }); it("should throw if xpath matches no nodes", () => { const sig = new SignedXml({ privateKey: fs.readFileSync("./test/static/client.pem"), canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256", }); sig.addReference({ xpath: "//definitelyNotThere", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); expect(() => sig.computeSignature("")).to.throw( /the following xpath cannot be signed because it was not found/, ); }); it("should sign references when the Id attribute is prefixed", () => { const xml = ''; const sig = new SignedXml({ privateKey: fs.readFileSync("./test/static/client.pem"), canonicalizationAlgorithm: "http://www.w3.org/2001/10/xml-exc-c14n#", signatureAlgorithm: "http://www.w3.org/2000/09/xmldsig#rsa-sha1", }); sig.addReference({ xpath: "//*[local-name(.)='x']", digestAlgorithm: "http://www.w3.org/2000/09/xmldsig#sha1", transforms: ["http://www.w3.org/2001/10/xml-exc-c14n#"], }); sig.computeSignature(xml); const signedXml = sig.getSignedXml(); const doc = new xmldom.DOMParser().parseFromString(signedXml); const referenceElements = xpath.select("//*[local-name(.)='Reference']", doc); isDomNode.assertIsArrayOfNodes(referenceElements); expect(referenceElements.length, "Reference element should exist").to.equal(1); const referenceElement = referenceElements[0]; isDomNode.assertIsElementNode(referenceElement); const uriAttribute = referenceElement.getAttribute("URI"); expect(uriAttribute, "Reference element should have the correct URI attribute value").to.equal( "#unique-id", ); }); }); ================================================ FILE: test/static/client.pem ================================================ -----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----- ================================================ FILE: test/static/client_bundle.pem ================================================ -----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----- ================================================ FILE: test/static/client_public.pem ================================================ -----BEGIN CERTIFICATE----- MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAW MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEy MzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqG SIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPd Vu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9x O3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8juf z2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEU MBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcN AQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5 sT/txBnVJGziyO8DPYdu2fPMER8ajJfl -----END CERTIFICATE----- ================================================ FILE: test/static/empty_uri.pem ================================================ -----BEGIN CERTIFICATE----- MIICcTCCAVmgAwIBAgIBATANBgkqhkiG9w0BAQQFADA+MQswCQYDVQQGEwJFUzEbMBkGA1UEChMS U2N5dGwgVU5JVCBURVNUIENBMRIwEAYDVQQLEwlVTklUIFRFU1QwHhcNMDYwMzAyMTEwOTEyWhcN MzAwNDA2MTEwOTEyWjA+MQswCQYDVQQGEwJFUzEbMBkGA1UEChMSU2N5dGwgVU5JVCBURVNUIENB MRIwEAYDVQQLEwlVTklUIFRFU1QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBALfqpJyXfyna NPX70K0U4aof9xMg0I/M09WU+7l5or54uFWsrH+sx7/qDkSLwLfZ7WRNmQon/b+gglvoZkRZHYEa FaZg9kxK48pM6MMehX8n+QZFZ1QfPfoP2ajcsr14gXvEpqQa0SzjIn522nMqyXLKx38JQp7shil4 9S38w3EfAgMBAAEwDQYJKoZIhvcNAQEEBQADggEBAJrtSsuqdB3RvGZMriETpO0pqQsBhZ+JRYqs 5dFIiC+bdJE+dUB6v8fpv1exSQ0+i+e1oTs2I9Wd4eIQ4rlZCG9K6EYBKP9Z59OBDTf6luaisoul 6gnKW7+kUfQQkBfJJw/4RA7FbaAmYNGEEfXTwDMU+ZoqIO/PV+JsfOtFUdPcwv1hPQHTE+KR1DtR AC+A4Ak+fOoaRnyTprvbT4inaPdn6D9fBrIpO6hQSAtM5K9+/VaskMSFqw03DkADRyxHacdz9JLp 5ChDbRrXCDdT/a832acPDViS3nG/GhuSTK8mlrXeHjpG6vaSbKiXNXwrwz80k/XoNh3k5jlfrzUM CQ4= -----END CERTIFICATE----- ================================================ FILE: test/static/feide_public.pem ================================================ -----BEGIN CERTIFICATE----- MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMC Tk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UE CxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0B CQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoX DTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhl aW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBv cGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdA dW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlx AZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4Hln O4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZ jcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQ Yj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1j wKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3K jjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w== -----END CERTIFICATE----- ================================================ FILE: test/static/hmac-foobar.key ================================================ FG}P\+=(9(z )e ,z68ã ================================================ FILE: test/static/hmac.key ================================================ R -B%P) Akl`978ZvjCBK4twpNullktn5R4thOSezHWxuouNf6siHYJ1E=EEX7i+HCAfEELjwwKP1vKyPrW10=some-key-name ================================================ FILE: test/static/id_with_quotes.xml ================================================ https://openidp.feide.no https://openidp.feide.no RnNjoyUguwze5w2R+cboyTHlkQk=aw5711jKP7xragunjRRCAD4mT4xKHc37iohBpQDbdSomD3ksOSB96UZQp0MtaC3xlVSkMtYw85Om96T2q2xrxLLYVA50eFJEMMF7SCVPStWTVjBlaCuOPEQxIaHyJs9Sy3MCEfbBh4Pqn9IJBd1kzwdlCrWWjAmksbFFg5wHQJA= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w== some text ================================================ FILE: test/static/idp_certificate.pem ================================================ -----BEGIN CERTIFICATE----- MIIFFzCCAv+gAwIBAgIUaAU88KUbZLe7NwTw+jdCHIDU6wIwDQYJKoZIhvcNAQEL BQAwGjEYMBYGA1UEAwwPaWRwLmV4YW1wbGUuY29tMCAXDTI1MDkwODExMTUzMFoY DzIxMjUwODE1MTExNTMwWjAaMRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wggIi MA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDXpgSoph176VQPw+4e91UAL6j0 TbL/aI3Amj62TU91KOk4xigy+8xFSeU2IC64W7abfFq+25K+1ybJmMBq94UKyKab 7yeXmen2xJ9PT5br4TwnsbeBZrziXSR8uTE60DdWLZYJEBREgu96JeEWLzP/0Hfn FlnuG5kXlb6rpy7l723YeDvU9lvsm6Rj46m5R8j4CSenKmLsHSIhmNP59SsEpJ16 D+RfKcrKqxPxp6t/oCEKXjpmOeGoX9WT5V9UMxrGFgY2YLab9tLCIK48i6rgXpHU nbMq2XyAT58bSG/Tif99hoiQ2ovzsVUrVeCa4/uUg/pr+w1bZnIBl6R8WVPTVwgm YN+8Ww27aLiNksCn70t74XZLr9xnYnbnj324AiZp9Z48vDECm41Tc+V9eqGNO/5Y LgZqoGTy4El3AMcNF3lkecZ2UZKMI341pI1vHyRG0jCr2ZYpy30pYQKd+Z7AvZKM UTJFQfBIOn3zXN6SA8lWZLPOW4VTdOfcGjQij3AUDeTeilUigpRgkTl97WUrGfas vLXMrdRoXYxjGPe+s4+tf+gnKhTVQ6h6we+ISaFVOeXpCrOUwZM0OdikQtKYgH0G 9OrJKxDZDl4YkkYFqqhyw5Eum9HZwMU8631lkBSqMRgz8AX7KdBn1PrGS6wOp+GC 28L1ISeRxRriWtaeXQIDAQABo1MwUTAdBgNVHQ4EFgQULRbUCREu3zQABqNW8LMO fraF5BMwHwYDVR0jBBgwFoAULRbUCREu3zQABqNW8LMOfraF5BMwDwYDVR0TAQH/ BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAA6Jm3pll7XHBY/oXrtRAu60RpH4p cZBSf68zhCQKUnXALy2FWrUGU8uVCJJuxXIcxszTOCZmwVF12YceIFpBrWHxKCDZ WYDO6hrwZvsa2vh5mXDZo3c/HX6GmJR4f4oIIZnbxFhXR30419PlrwMW3Rk7rnVS Dz/HpQfdS4y8jYgsm2dIRo+PXQytFRCDj4afsT3eZa27QjFxLTuK+SwkhupSH1WW YmqL9+iIJiLL/ntfM4MwtOUwcfqI0ttbvFZZIPneBCuEDLn/zJ/QBV3ZvjQDt53W 21HaPGPHBBfVZVroZPvuGvulWRLeECI1Hmbl3al/2aOC0LWzPIk8dlTcN+EWcCEu fpTOEgkob8waEyxlX0Z5OBjkCHpyDTPGkxBBOCJv88Frx7qdbu8eSV0OviuaM/B7 ky2NbDMKIybX4tf/Q8FNfjPLvTv+8nbrMz6kTno3RR2YC7ttI7Glb/eOg3F/ouF4 wcoPAj+OyU5Q5WJMMaZ9cXF9pwszsglLbFms+WD5PFxlloh6I1hO7TccWKTeUJ5f YZSYYm31JqWit3DBltXTyRyL7KFSdT/FyRBk62YLCJqJmcukIUJUTya73/RmjW1M 4zMBNaIj/pH77opKWnVbm9F4XnBCG4r9+FRXdV2zKW6DgvDzQ6DaZ+0cwC36vSqk DyjZYv/tviuWfM8= -----END CERTIFICATE----- ================================================ FILE: test/static/idp_private_key.pem ================================================ -----BEGIN PRIVATE KEY----- MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDXpgSoph176VQP w+4e91UAL6j0TbL/aI3Amj62TU91KOk4xigy+8xFSeU2IC64W7abfFq+25K+1ybJ mMBq94UKyKab7yeXmen2xJ9PT5br4TwnsbeBZrziXSR8uTE60DdWLZYJEBREgu96 JeEWLzP/0HfnFlnuG5kXlb6rpy7l723YeDvU9lvsm6Rj46m5R8j4CSenKmLsHSIh mNP59SsEpJ16D+RfKcrKqxPxp6t/oCEKXjpmOeGoX9WT5V9UMxrGFgY2YLab9tLC IK48i6rgXpHUnbMq2XyAT58bSG/Tif99hoiQ2ovzsVUrVeCa4/uUg/pr+w1bZnIB l6R8WVPTVwgmYN+8Ww27aLiNksCn70t74XZLr9xnYnbnj324AiZp9Z48vDECm41T c+V9eqGNO/5YLgZqoGTy4El3AMcNF3lkecZ2UZKMI341pI1vHyRG0jCr2ZYpy30p YQKd+Z7AvZKMUTJFQfBIOn3zXN6SA8lWZLPOW4VTdOfcGjQij3AUDeTeilUigpRg kTl97WUrGfasvLXMrdRoXYxjGPe+s4+tf+gnKhTVQ6h6we+ISaFVOeXpCrOUwZM0 OdikQtKYgH0G9OrJKxDZDl4YkkYFqqhyw5Eum9HZwMU8631lkBSqMRgz8AX7KdBn 1PrGS6wOp+GC28L1ISeRxRriWtaeXQIDAQABAoICAGhLJwKngCfu4xRS1mWMicPy yNwKffDPUIsfLgg94JlRhWXLVCLAK30xLVUdgGryFCEjpcGbcN+yL1SddyXkeqgJ /aX5pmTH7+LEGiYh4GRJBK5P4WeIV/6EPILDj/8ZN0IK/v54E81Eo+wnyLHRd20X lf2hjjG9kC9bYSEkVGapAq+ICqvG0BNg/MLAltOAV745sz9CHSCDQIAOKSrAuyLe ODkR2Yl6rVSSI62iQSuStpgMlWLeSHgFjUYfTxjqNF8rxKpk4LwSRcDUTGAEzkoA ArhY4o9tKqzllRX9VPPyUCmVuJOR1tCvaXjxahSPARvFLoYtnzqek7GYdNkc3JA8 yMOnXAVfuJ6xML9gFQvCJ9qFL5ayVBi+2OSXpKX0O2AwIOqFilT8/QSoR6bJXMwJ R4VRtsUbvQ84wG7cRSmGpMWs+PrGOgO2JmjCeLPrSZXNUIYchxGun+nG1OlBMjTc GdEzUdC0BLwOuGpvTKtCMDvwXStogq1PtjCHvEfjX84jtlV3mvJFHnqIJBGkLQPX O9P2SVL3g8vNwuAKvfMtSgW2sUXf+nFBtiAFXqXdYy0vL3O4AAoWU8Jfl5EUbO1S bAGJcF5N9vsSFGDfcZzuAbbSdj6qUlIkncTBlVO/tWsdFIg9BXbH/qQZEaprX//P 5Z5wv+qBL353z7nCkb8zAoIBAQDrBQ8R49u/Pxupvr5pcFfZ14ifmIZnUar1bkig o/ylb6Si3WgQ5PP788snYp2R3kbo+JBrdyl/OQBWgtCKOXxFllrBjwlihDXnK+SB igqHCkKbjTMjI+Gkgot7sR+tC9UnM0SNHlX6fkWfbmj6dQIIv0Cd3yOzx/v+ml2w TUwxCfmroiX67y3Sl0yVAGmXfjQ1uRCLBiulJEfONAu6+it0H8Whtx5d9FglF2Hj ll+cOamAB+GZiJzAF6MucizS+Cfw+kWkrlOpFj2EqE7Z847Bhp1b6TSPCVZ+IUpY 1FTYIBw1JFe7xeRKsfHs0KgrpSf3AxuSalFfpLeV997+sd/3AoIBAQDq5kPnZZTT V/i+DCa3Q94gNNQC+6j1YE/PDaQeOS7ybYz6pFizrIn6ioIfKD1as8vViTnvw9kq CehnO+rOzPA4oloVFDNKqliLViJMz7CY8hRoiOUuLzGbsmTyqvjcSi39IOmWs7Pq YFE0vDftEimMCxLNuTP/JpSa1ZY9std0Ljkk9zBTAykkXrrmjUxvd938DMSOqAv/ Pnib/Oq5cumROMXpPaRMjvhc56iyfEyluTntpJREN9KHvtll8TrGmDk/WW8sF87t jmr0hNSa4dFOTkUS+EODL6BPKg+MwGN6UL7L811qztErKtFCK77JGr+dq66fU1bV y9JHSPYvJ8dLAoIBAQCnSUWNzWwoeEo/jCdLNA1EYXe9ajsZJfeTlXma5r86HvrI duLRS7cju0f68+YG43oD8JIT/JEMHs3PxnOcQAjmG3zkU+UxO7yGnSac0l5l+vao dFxXAf5mNAoG9HAAo/CIY6TC8jnvAJycPGH7DPhys3fSJ/foy0vi0Ywopwy5x0jx U4zHTiKGyO1ZDh8bF1kgeGd/HdhJR0bZTxCeed4eXVM2pfq2k+t+E2O5NNs/f4fY O0PpHmW9EdY0hE7FqJ/9lpel/fRM4ijN2WOvHf+aXzB5MMkZm2L2ism56wGtiUWq ygCtBtJWHM2AbJGX54pH9+1TTdw4QS3wUKxpDMHHAoIBAAcs9ZISBlPWciDMGjqq 9hQhyQA2U9j7EjUPA55wvMBnHFFjx9nlQWnH5WWyQv2MVIO3Z2+tmeqw1sqgh/G9 TPFN9FaEgXScc4v+G5ohFhH2Ay2WUPnyMx/AeVj4ZBXGplT/NmOGJc7ZFmH4BfVW ArLme7KRH8eBlDSOpcJIvlAsQU6hxnYiuJUF18vHMTiOftd+RFrfQ06Ox/xr43e6 zOvEwjb3zRcRnwCniv240laVq/FYf7b9xY6kA9wbXGJIsCcBQmYkbAvRt+60SBJb J5uuxGlp8BYH1GvWqxbvoZpQ8SMl0gq7OqSI8E+HKpLWIFhesDzpvNNXIJtQ3URf gLUCggEAa0SzoEaLut65B1mQuYaYXCvyXohRJn49BoHULUub/Zg2D+mPMJcyefBK o/FUZD8ewXUiEoj6eoEXHTT8eOSvhXQHp2dc6AW6rADzL4Ni0oSQ8LwT5fgGq7B7 yxWXkSkK3T43RLvrb7FSV21WTcsXYH560LHa5me1S4Pb1Zrz8+KCRNdNcoffZMfi b09pdLkS7Sbiskg69NrOFmwfD8SdAb9opPLY5lLWEVtkaPwQS4MrboJJlDutV9VC /Ye8QbuMwKQHduhAGGsCpkEwZhO5T4owdebVeY28t80HsInv7mFuiqZbIN/BMnU7 zvi/SKHNm+elLUD1r7tC3+KVTtqSSw== -----END PRIVATE KEY----- ================================================ FILE: test/static/integration/expectedVerify.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI= ================================================ FILE: test/static/integration/expectedVerifyComplex.xml ================================================ Harry PotterJoanne KRowlingVXkWV6BNrUKqdmwcxhIgRa86HUI=Y71mpTZ0Ba5qED2TxHLp34tq8Pa9X41M5PuABwPq7r2GaL5Ib28ELkhMtnZuZboO50bxh/9fwK6DPApP92zUuOTZi4htlPcGJRP5kvHznbwzvq7aO5FWAcpUTX+yMrlERIutiJi7mjChX+mIKF5/4DK66JfIlY5vlq61TX2gaJw= ================================================ FILE: test/static/invalid_saml_no_signed_info.xml ================================================ https://openidp.feide.no dkONrkxW+LSuDvnNMG/mWYFa47d2WGyapLhXSTYqrlT9Td+tT7ciojNJ55WTaPaCMt7IrGtIxxskPAZIjdIn5pRyDxHr0joWxzZ7oZHCOI1CnQV5HjOq+rzzmEN2LctCZ6S4hbL7SQ1qJ3vp2BCXAygy4tmJOURQdnk0KLwwRS8= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==https://openidp.feide.no RnNjoyUguwze5w2R+cboyTHlkQk=aw5711jKP7xragunjRRCAD4mT4xKHc37iohBpQDbdSomD3ksOSB96UZQp0MtaC3xlVSkMtYw85Om96T2q2xrxLLYVA50eFJEMMF7SCVPStWTVjBlaCuOPEQxIaHyJs9Sy3MCEfbBh4Pqn9IJBd1kzwdlCrWWjAmksbFFg5wHQJA= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==_6c5dcaa3053321ff4d63785fbc3f67c59a129cde82passport-samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordbergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3ebergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3e ================================================ FILE: test/static/invalid_saml_sha256_rsa_mgf1.xml ================================================ bA/90zzLS/36DstvPJRrMNwGax5WQv8NneSuNdLnMYs= 0fK5rJEtj0+JIL3hAuDsEAuKYoiwHzZTgQgspLyEe+XQan1FzT+qu3GBJSpSDfArBHjHXtizVfi8 irId6a1kOj6ShEw2ZSGYD8Dh2d0HmrHqlOqpZ5eLiWeFA6VTtW1Cqmvr+x4Ndxcg0wWmmGr4hpSD Yg8fkA8e32Fd2QxqLsQqVlCcuvJVCJ/12XSGcMW+Tse254fN6JENLLUdilu+14NNQKAHpKpjeajg jG3fn0VNvyVQXKi2deYTWYaLRujBgv3Ncz8t9Hjthk+XxrRVHJiGc6HyVvqdpi5ChM41fjB1+eBo NkZ0Q73ZSCbTAqVduUWqL5pQzINq16kUm2ovkg7h3JoqSQr5yhoBJXZEf6FEyYdCCLd3rlIrkcvD +wfF2CwNRc6utgO/05JAA7Z1x2e0K6o2a6EQy93dkUIBhpxPYU/IdmGb15AfKJ3OrB1K/jTrxZ2q og/u0fJe0vU1sL4EDOXqVMj/unTZqDP/K1mOHK/eDWafs/IwMv65ebZUwTk74AMk/oOYV9mL8beY JAVYMN+xPA1cXnHlRgwATWLXjfiQcMXo44nhaw0YlOUGIoRLYURqHmXz0W3d8pXYdybLmdClkqLR vo1ryK1OC2paYG9qwk51QJ5wzMv6HRB5tDIL9/7mP7khlKgm4p+EZXFGYmvYDbk2x219SvxPmfY= 16YEqKYde+lUD8PuHvdVAC+o9E2y/2iNwJo+tk1PdSjpOMYoMvvMRUnlNiAuuFu2m3xavtuSvtcm yZjAaveFCsimm+8nl5np9sSfT0+W6+E8J7G3gWa84l0kfLkxOtA3Vi2WCRAURILveiXhFi8z/9B3 5xZZ7huZF5W+q6cu5e9t2Hg71PZb7JukY+OpuUfI+Aknpypi7B0iIZjT+fUrBKSdeg/kXynKyqsT 8aerf6AhCl46ZjnhqF/Vk+VfVDMaxhYGNmC2m/bSwiCuPIuq4F6R1J2zKtl8gE+fG0hv04n/fYaI kNqL87FVK1XgmuP7lIP6a/sNW2ZyAZekfFlT01cIJmDfvFsNu2i4jZLAp+9Le+F2S6/cZ2J25499 uAImafWePLwxApuNU3PlfXqhjTv+WC4GaqBk8uBJdwDHDRd5ZHnGdlGSjCN+NaSNbx8kRtIwq9mW Kct9KWECnfmewL2SjFEyRUHwSDp981zekgPJVmSzzluFU3Tn3Bo0Io9wFA3k3opVIoKUYJE5fe1l Kxn2rLy1zK3UaF2MYxj3vrOPrX/oJyoU1UOoesHviEmhVTnl6QqzlMGTNDnYpELSmIB9BvTqySsQ 2Q5eGJJGBaqocsORLpvR2cDFPOt9ZZAUqjEYM/AF+ynQZ9T6xkusDqfhgtvC9SEnkcUa4lrWnl0= AQAB MIIFFzCCAv+gAwIBAgIUaAU88KUbZLe7NwTw+jdCHIDU6wIwDQYJKoZIhvcNAQELBQAwGjEYMBYG A1UEAwwPaWRwLmV4YW1wbGUuY29tMCAXDTI1MDkwODExMTUzMFoYDzIxMjUwODE1MTExNTMwWjAa MRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC AQDXpgSoph176VQPw+4e91UAL6j0TbL/aI3Amj62TU91KOk4xigy+8xFSeU2IC64W7abfFq+25K+ 1ybJmMBq94UKyKab7yeXmen2xJ9PT5br4TwnsbeBZrziXSR8uTE60DdWLZYJEBREgu96JeEWLzP/ 0HfnFlnuG5kXlb6rpy7l723YeDvU9lvsm6Rj46m5R8j4CSenKmLsHSIhmNP59SsEpJ16D+RfKcrK qxPxp6t/oCEKXjpmOeGoX9WT5V9UMxrGFgY2YLab9tLCIK48i6rgXpHUnbMq2XyAT58bSG/Tif99 hoiQ2ovzsVUrVeCa4/uUg/pr+w1bZnIBl6R8WVPTVwgmYN+8Ww27aLiNksCn70t74XZLr9xnYnbn j324AiZp9Z48vDECm41Tc+V9eqGNO/5YLgZqoGTy4El3AMcNF3lkecZ2UZKMI341pI1vHyRG0jCr 2ZYpy30pYQKd+Z7AvZKMUTJFQfBIOn3zXN6SA8lWZLPOW4VTdOfcGjQij3AUDeTeilUigpRgkTl9 7WUrGfasvLXMrdRoXYxjGPe+s4+tf+gnKhTVQ6h6we+ISaFVOeXpCrOUwZM0OdikQtKYgH0G9OrJ KxDZDl4YkkYFqqhyw5Eum9HZwMU8631lkBSqMRgz8AX7KdBn1PrGS6wOp+GC28L1ISeRxRriWtae XQIDAQABo1MwUTAdBgNVHQ4EFgQULRbUCREu3zQABqNW8LMOfraF5BMwHwYDVR0jBBgwFoAULRbU CREu3zQABqNW8LMOfraF5BMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAA6Jm 3pll7XHBY/oXrtRAu60RpH4pcZBSf68zhCQKUnXALy2FWrUGU8uVCJJuxXIcxszTOCZmwVF12Yce IFpBrWHxKCDZWYDO6hrwZvsa2vh5mXDZo3c/HX6GmJR4f4oIIZnbxFhXR30419PlrwMW3Rk7rnVS Dz/HpQfdS4y8jYgsm2dIRo+PXQytFRCDj4afsT3eZa27QjFxLTuK+SwkhupSH1WWYmqL9+iIJiLL /ntfM4MwtOUwcfqI0ttbvFZZIPneBCuEDLn/zJ/QBV3ZvjQDt53W21HaPGPHBBfVZVroZPvuGvul WRLeECI1Hmbl3al/2aOC0LWzPIk8dlTcN+EWcCEufpTOEgkob8waEyxlX0Z5OBjkCHpyDTPGkxBB OCJv88Frx7qdbu8eSV0OviuaM/B7ky2NbDMKIybX4tf/Q8FNfjPLvTv+8nbrMz6kTno3RR2YC7tt I7Glb/eOg3F/ouF4wcoPAj+OyU5Q5WJMMaZ9cXF9pwszsglLbFms+WD5PFxlloh6I1hO7TccWKTe UJ5fYZSYYm31JqWit3DBltXTyRyL7KFSdT/FyRBk62YLCJqJmcukIUJUTya73/RmjW1M4zMBNaIj /pH77opKWnVbm9F4XnBCG4r9+FRXdV2zKW6DgvDzQ6DaZ+0cwC36vSqkDyjZYv/tviuWfM8= https://idp.example.com/ https://idp.example.com/ modifiedFakeNameId audience urn:oasis:names:tc:SAML:2.0:ac:classes:Password ================================================ FILE: test/static/invalid_signature - changed content.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI=1234 ================================================ FILE: test/static/invalid_signature - hash.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CPupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI= ================================================ FILE: test/static/invalid_signature - non existing reference.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI= ================================================ FILE: test/static/invalid_signature - signature value.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0Q1UI= ================================================ FILE: test/static/invalid_signature - wsu - changed content.xml ================================================ 5jkdJLKVpfEiAQ0k5bnvfCQJKks=97gNxHth/Gc2wH4FiTiWJJrLaHI=mXRUtj7KJU3kMPLD9R30CGEiWio=CRK8LTGiMM5p++3usB6Nxjg5WrPfvq97bFzHwtt9Sijqf1BqqCHLnQLxzhtPR7j6g0TJaQt4bmF8neO7IwgloSWX/ZXUl9dz5Ax9IMfESBI0L8Ew2BE1osZp3Nlc0xSSIVFKMfo9gvqi317jBjr/VklqjHXjMfhtGv8GATpdwPQ= ================================================ FILE: test/static/invalid_signature - wsu - hash.xml ================================================ kapR1Bn68VZxlpAEcUqcpOiL1LQ=Ih44YjG8IC7apTJSIkMLSjiU0H0=3jbv3y5YtUUG6EIQSVL5faQorFY=tk7uiJCzpfI9TUGAuZuLELPqN7LdVIhljaJRKHdaBpbgu3XH5YiGQXbOOmGbX6ghr2mabbMgw2kXQJbUATzqdQn+wRbeMRqFaj4vBpEoTohiwdoIw67zGbSCXKoGSZ7FxJADe53gS955wZgdcGt+Ni7ec5EbXb0XtzM6gDF+eUo= ================================================ FILE: test/static/invalid_signature - wsu - invalid signature value.xml ================================================ 5jkdJLKVpfEiAQ0k5bnvfCQJKks=97gNxHth/Gc2wH4FiTiWJJrLaHI=mXRUtj7KJU3kMPLD9R30CGEiWio=CRK8LTGiMM5p++3usB6Nxjg5WrPfvq97bFzHwtt9Sijqf1BqqCHLnQLxzhtPR7j6g0TJaQt4bmF8neO7IwgloSWX/ZXUl9dz5Ax9IMfESBI0L8Ew2BE1osZp3Nlc0xSSIVFKMfo9gvqi317jBkr/VklqjHXjMfhtGv8GATpdwPQ= ================================================ FILE: test/static/invalid_signature - wsu - non existing reference.xml ================================================ kapR1Bn6OVZxlpAEcUqcpOiL1LQ=Ih44YjG8IC7apTJSIkMLSjiU0H0=3jbv3y5YtUUG6EIQSVL5faQorFY=tk7uiJCzpfI9TUGAuZuLELPqN7LdVIhljaJRKHdaBpbgu3XH5YiGQXbOOmGbX6ghr2mabbMgw2kXQJbUATzqdQn+wRbeMRqFaj4vBpEoTohiwdoIw67zGbSCXKoGSZ7FxJADe53gS955wZgdcGt+Ni7ec5EbXb0XtzM6gDF+eUo= ================================================ FILE: test/static/invalid_signature_without_transforms_element.xml ================================================ some tampered textLsMoqo1d6Sqh8DKLp00MK0fSBDA=OR1SYcyU18qELj+3DX/bW/r5DqueuyPAnNFEh3hNKFaj8ZKLB/mdsR9w8GDBCmZ2 lsCTEvJqWC37oF8rm2eBSonNbdBnA+TM6Y22C8rffVzaoM3zpNoeWMH2LwFmpdKB UXOMWVExEaz/s4fOcyv1ajVuk42I3nl0xcD95+i7PjY= ================================================ FILE: test/static/keyinfo - pretty-printed.xml ================================================ https://evil-corp.com uAP/VBhhaK2+Bn1lYjHB5tSHTcE=Q2F/63pvENmI+eURRIRmVI1fNpW+CxDan6YmGo2G5il+XOx72sBHS+FspoWDyezYUfO/Wfi+tBupK /9eHOeg60uPUkQFwkdPmUsH2hJAPVCatQReSUJDhHCO6a2rrQixecPDBhzJjstCpibgvgNzevVVu 9h3eMH/BNzdwO5EbnVTcb2JQj2F18MAh1LlVMBWWDaZmIQTk7npMY/NVEajM1fbwXyrPdNRU8poK bjVoM7IL9s0TesQIeyQ01QOQQQ3kHZWnoqlWE6VbbTqHwuidfkqaQLLvF9sDneqXKBa7y2YEJXVm4 GMzNoL/JOdSuNnU3rF0ET8UDSleV953/lbUA== MIIDtTCCAp2gAwIBAgIJAKg4VeVcIDz1MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNVBAYTAlVTMRMwE QYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRzIFB0eSBMdGQwHhcNMT UwODEzMDE1NDIwWhcNMTUwOTEyMDE1NDIwWjBFMQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1 TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEF AAOCAQ8AMIIBCgKCAQEAxG3ouM7U+fXbJt69X1H6d4UNg/uRr06pFuU9RkfIwNC+yaXyptqB3ynXK sL7BFt4DCd0fflRvJAx3feJIDp16wN9GDVHcufWMYPhh2j5HcTW/j9JoIJzGhJyvO00YKBt+hHy83 iN1SdChKv5y0iSyiPP5GnqFw+ayyHoM6hSO0PqBou1Xb0ZSIE+DHosBnvVna5w2AiPY4xrJl9yZHZ 4Q7DfMiYTgstjETio4bX+6oLiBnYktn7DjdEslqhffVme4PuBxNojI+uCeg/sn4QVLd/iogMJfDWN uLD8326Mi/FE9cCRvFlvAiMSaebMI3zPaySsxTK7Zgj5TpEbmbHI9wIDAQABo4GnMIGkMB0GA1UdD gQWBBSVGgvoW4MhMuzBGce29PY8vSzHFzB1BgNVHSMEbjBsgBSVGgvoW4MhMuzBGce29PY8vSzHF6 FJpEcwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV 0IFdpZGdpdHMgUHR5IEx0ZIIJAKg4VeVcIDz1MAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQAD ggEBAJu1rqs+anD74dbdwgd3CnqnQsQDJiEXmBhG2leaGt3ve9b/9gKaJg2pyb2NyppDe1uLqh6nN XDuzg1oNZrPz5pJL/eCXPl7FhxhMUi04TtLf8LeNTCIWYZiFuO4pmhohHcv8kRvYR1+6SkLTC8j/T Zerm7qvesSiTQFNapa1eNdVQ8nFwVkEtWl+JzKEM1BlRcn42sjJkijeFp7DpI7pU+PnYeiaXpRv5p Jo8ogM1iFxN+SnfEs0EuQ7fhKIG9aHKi7bKZ7L6SyX7MDIGLeulEU6lf5D9BfXNmcMambiS0pXhL2 QXajt96UBq8FT2KNXY8XNtR4y6MyyCzhaiZZcc8= https://evil-corp.com vincent.vega@evil-corp.com urn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransport vincent.vega@evil-corp.com Vincent VEGA 123 Main St. Suite 11 ================================================ FILE: test/static/keyinfo.pem ================================================ -----BEGIN CERTIFICATE----- MIIDtTCCAp2gAwIBAgIJAKg4VeVcIDz1MA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV BAYTAlVTMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX aWRnaXRzIFB0eSBMdGQwHhcNMTUwODEzMDE1NDIwWhcNMTUwOTEyMDE1NDIwWjBF MQswCQYDVQQGEwJVUzETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50 ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB CgKCAQEAxG3ouM7U+fXbJt69X1H6d4UNg/uRr06pFuU9RkfIwNC+yaXyptqB3ynX KsL7BFt4DCd0fflRvJAx3feJIDp16wN9GDVHcufWMYPhh2j5HcTW/j9JoIJzGhJy vO00YKBt+hHy83iN1SdChKv5y0iSyiPP5GnqFw+ayyHoM6hSO0PqBou1Xb0ZSIE+ DHosBnvVna5w2AiPY4xrJl9yZHZ4Q7DfMiYTgstjETio4bX+6oLiBnYktn7DjdEs lqhffVme4PuBxNojI+uCeg/sn4QVLd/iogMJfDWNuLD8326Mi/FE9cCRvFlvAiMS aebMI3zPaySsxTK7Zgj5TpEbmbHI9wIDAQABo4GnMIGkMB0GA1UdDgQWBBSVGgvo W4MhMuzBGce29PY8vSzHFzB1BgNVHSMEbjBsgBSVGgvoW4MhMuzBGce29PY8vSzH F6FJpEcwRTELMAkGA1UEBhMCVVMxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAKg4VeVcIDz1MAwGA1UdEwQF MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAJu1rqs+anD74dbdwgd3CnqnQsQDJiEX mBhG2leaGt3ve9b/9gKaJg2pyb2NyppDe1uLqh6nNXDuzg1oNZrPz5pJL/eCXPl7 FhxhMUi04TtLf8LeNTCIWYZiFuO4pmhohHcv8kRvYR1+6SkLTC8j/TZerm7qvesS iTQFNapa1eNdVQ8nFwVkEtWl+JzKEM1BlRcn42sjJkijeFp7DpI7pU+PnYeiaXpR v5pJo8ogM1iFxN+SnfEs0EuQ7fhKIG9aHKi7bKZ7L6SyX7MDIGLeulEU6lf5D9Bf XNmcMambiS0pXhL2QXajt96UBq8FT2KNXY8XNtR4y6MyyCzhaiZZcc8= -----END CERTIFICATE----- ================================================ FILE: test/static/saml_external_ns.pem ================================================ -----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAoXoc7IFZQRv+SwJ15zjIl9touwY5e6b7H4vn3OtOUByjOKHUX8VX 0TpbAV2ctZE2GSALx1AGuQAv6O4MVUH+qn/2IAiBY3a7zKN07UBsya7xFMQVHuGE 6EiBAs9jpA9wjvYMPRkS5wYZcwjpTQSZK7zFPPtobG8K/1vDbm/tWZjNLmZmQePm XpwrQAuC0+NlzlmnjoQYB2xp2NaTUK9JnnmuB5qev3dpUwlYGSJpf+HUIoxuo8Ip xAXOymq1d6tEEJgU1kR2sa7o1sSRFo31YeW/qYCP/gcLJZo3MRUDFe0g5MHeliFu e9DsKYUsC6qwAD3gc+MI47buiD6Msu11cwIDAQAB -----END RSA PUBLIC KEY----- ================================================ FILE: test/static/saml_external_ns.xml ================================================ https://app.onelogin.com/saml/metadata/164679https://app.onelogin.com/saml/metadata/164679Gx0mTydMn1k6804jZBrdUrZmbV4=oHEPKtwoCbfq1QRm2pjx35zVMqAsti4nQU+3ws8EUJUXHmPG2EoX3HBkb7D2wN4m+ZFrdwARUpNJlhhOIz/eG4jES6ar0tvlNN3qE5cqcQhwZHyRARLnTlERqyZU9Qm729DnAGBeXCdMb736zi16onOIVPNA63LRTzUIxhyZqypDCf1wd6me/ur6UUgH11nYOu4JDYx0iWNkXc1Nad7vkF9oMPeO1QsMxuZSIVH4tvdJkue+qAnu2l+dFJb0LPfm+xmIC0FBo+VX1ECCWRoUZIxjotQfAM6yZpHIi5fNqPXkVyN9fYoUEa9CafqHlc4tAAdgAgGeOqA3jWeC8ZnOVA==MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA1MjcwODU1MTNaFw0xODA1MjcwODU1MTNaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXoc7IFZQRv+SwJ15zjIl9touwY5e6b7H4vn3OtOUByjOKHUX8VX0TpbAV2ctZE2GSALx1AGuQAv6O4MVUH+qn/2IAiBY3a7zKN07UBsya7xFMQVHuGE6EiBAs9jpA9wjvYMPRkS5wYZcwjpTQSZK7zFPPtobG8K/1vDbm/tWZjNLmZmQePmXpwrQAuC0+NlzlmnjoQYB2xp2NaTUK9JnnmuB5qev3dpUwlYGSJpf+HUIoxuo8IpxAXOymq1d6tEEJgU1kR2sa7o1sSRFo31YeW/qYCP/gcLJZo3MRUDFe0g5MHeliFue9DsKYUsC6qwAD3gc+MI47buiD6Msu11cwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFAAJFJRIlpQQSFsuNdeq7FkTJIH4MIGRBgNVHSMEgYkwgYaAFAAJFJRIlpQQSFsuNdeq7FkTJIH4oWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB9zN+g6N4sUBE61RaMUH2LSHWwOtfhL64i7pjHjvZa47/qcV/S0Yyd4IE44ho9i2N+AM79d34mThc30oK5aVxOKphKf+xM/cOyVaWIeqr+dCbkY/0OpLEwWOh9VSgOizRO3evLMurbtR892LbSK/Td3hG5jfwoHD23nHH87Dv/3KyZox9MkJdY2DXOHGGIcsqoIifaTyNZyhW6RgwEujQ6LjsaolP1YoeV85TZFKTLa1Ta7ZLUVUC2UJWqz+kRlsyGxf+E/ZmJ7hSq0ZBVHrVOyXjCcFn6X0/W5SrpOmN3fZYcj8Bp6vhB0cJk9qpjgWOP2RCuBdHZVawjCjIaE+bc=kartik.cds@gmail.comForNodeJSurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportkartik.cds@gmail.comKartikCDS ================================================ FILE: test/static/saml_multiple_signed_info_nodes.xml ================================================ https://app.onelogin.com/saml/metadata/164679https://app.onelogin.com/saml/metadata/164679Gx0mTydMn1k6804jZBrdUrZmbV4=jGst6BnAC9xOeqa6hKNPsoMm2TY=oHEPKtwoCbfq1QRm2pjx35zVMqAsti4nQU+3ws8EUJUXHmPG2EoX3HBkb7D2wN4m+ZFrdwARUpNJlhhOIz/eG4jES6ar0tvlNN3qE5cqcQhwZHyRARLnTlERqyZU9Qm729DnAGBeXCdMb736zi16onOIVPNA63LRTzUIxhyZqypDCf1wd6me/ur6UUgH11nYOu4JDYx0iWNkXc1Nad7vkF9oMPeO1QsMxuZSIVH4tvdJkue+qAnu2l+dFJb0LPfm+xmIC0FBo+VX1ECCWRoUZIxjotQfAM6yZpHIi5fNqPXkVyN9fYoUEa9CafqHlc4tAAdgAgGeOqA3jWeC8ZnOVA==MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA1MjcwODU1MTNaFw0xODA1MjcwODU1MTNaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXoc7IFZQRv+SwJ15zjIl9touwY5e6b7H4vn3OtOUByjOKHUX8VX0TpbAV2ctZE2GSALx1AGuQAv6O4MVUH+qn/2IAiBY3a7zKN07UBsya7xFMQVHuGE6EiBAs9jpA9wjvYMPRkS5wYZcwjpTQSZK7zFPPtobG8K/1vDbm/tWZjNLmZmQePmXpwrQAuC0+NlzlmnjoQYB2xp2NaTUK9JnnmuB5qev3dpUwlYGSJpf+HUIoxuo8IpxAXOymq1d6tEEJgU1kR2sa7o1sSRFo31YeW/qYCP/gcLJZo3MRUDFe0g5MHeliFue9DsKYUsC6qwAD3gc+MI47buiD6Msu11cwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFAAJFJRIlpQQSFsuNdeq7FkTJIH4MIGRBgNVHSMEgYkwgYaAFAAJFJRIlpQQSFsuNdeq7FkTJIH4oWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB9zN+g6N4sUBE61RaMUH2LSHWwOtfhL64i7pjHjvZa47/qcV/S0Yyd4IE44ho9i2N+AM79d34mThc30oK5aVxOKphKf+xM/cOyVaWIeqr+dCbkY/0OpLEwWOh9VSgOizRO3evLMurbtR892LbSK/Td3hG5jfwoHD23nHH87Dv/3KyZox9MkJdY2DXOHGGIcsqoIifaTyNZyhW6RgwEujQ6LjsaolP1YoeV85TZFKTLa1Ta7ZLUVUC2UJWqz+kRlsyGxf+E/ZmJ7hSq0ZBVHrVOyXjCcFn6X0/W5SrpOmN3fZYcj8Bp6vhB0cJk9qpjgWOP2RCuBdHZVawjCjIaE+bc=anyone@gmail.comForNodeJSurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportkartik.cds@gmail.comKartikCDShttps://app.onelogin.com/saml/metadata/164679kartik.cds@gmail.comForNodeJSurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportkartik.cds@gmail.comKartikCDS ================================================ FILE: test/static/saml_wrapped_signed_info_node.xml ================================================ https://app.onelogin.com/saml/metadata/164679https://app.onelogin.com/saml/metadata/164679jGst6BnAC9xOeqa6hKNPsoMm2TY=Gx0mTydMn1k6804jZBrdUrZmbV4=oHEPKtwoCbfq1QRm2pjx35zVMqAsti4nQU+3ws8EUJUXHmPG2EoX3HBkb7D2wN4m+ZFrdwARUpNJlhhOIz/eG4jES6ar0tvlNN3qE5cqcQhwZHyRARLnTlERqyZU9Qm729DnAGBeXCdMb736zi16onOIVPNA63LRTzUIxhyZqypDCf1wd6me/ur6UUgH11nYOu4JDYx0iWNkXc1Nad7vkF9oMPeO1QsMxuZSIVH4tvdJkue+qAnu2l+dFJb0LPfm+xmIC0FBo+VX1ECCWRoUZIxjotQfAM6yZpHIi5fNqPXkVyN9fYoUEa9CafqHlc4tAAdgAgGeOqA3jWeC8ZnOVA==MIIEHjCCAwagAwIBAgIBATANBgkqhkiG9w0BAQUFADBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbTAeFw0xMzA1MjcwODU1MTNaFw0xODA1MjcwODU1MTNaMGcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRUwEwYDVQQHDAxTYW50YSBNb25pY2ExETAPBgNVBAoMCE9uZUxvZ2luMRkwFwYDVQQDDBBhcHAub25lbG9naW4uY29tMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoXoc7IFZQRv+SwJ15zjIl9touwY5e6b7H4vn3OtOUByjOKHUX8VX0TpbAV2ctZE2GSALx1AGuQAv6O4MVUH+qn/2IAiBY3a7zKN07UBsya7xFMQVHuGE6EiBAs9jpA9wjvYMPRkS5wYZcwjpTQSZK7zFPPtobG8K/1vDbm/tWZjNLmZmQePmXpwrQAuC0+NlzlmnjoQYB2xp2NaTUK9JnnmuB5qev3dpUwlYGSJpf+HUIoxuo8IpxAXOymq1d6tEEJgU1kR2sa7o1sSRFo31YeW/qYCP/gcLJZo3MRUDFe0g5MHeliFue9DsKYUsC6qwAD3gc+MI47buiD6Msu11cwIDAQABo4HUMIHRMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFAAJFJRIlpQQSFsuNdeq7FkTJIH4MIGRBgNVHSMEgYkwgYaAFAAJFJRIlpQQSFsuNdeq7FkTJIH4oWukaTBnMQswCQYDVQQGEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEVMBMGA1UEBwwMU2FudGEgTW9uaWNhMREwDwYDVQQKDAhPbmVMb2dpbjEZMBcGA1UEAwwQYXBwLm9uZWxvZ2luLmNvbYIBATAOBgNVHQ8BAf8EBAMCBPAwDQYJKoZIhvcNAQEFBQADggEBAB9zN+g6N4sUBE61RaMUH2LSHWwOtfhL64i7pjHjvZa47/qcV/S0Yyd4IE44ho9i2N+AM79d34mThc30oK5aVxOKphKf+xM/cOyVaWIeqr+dCbkY/0OpLEwWOh9VSgOizRO3evLMurbtR892LbSK/Td3hG5jfwoHD23nHH87Dv/3KyZox9MkJdY2DXOHGGIcsqoIifaTyNZyhW6RgwEujQ6LjsaolP1YoeV85TZFKTLa1Ta7ZLUVUC2UJWqz+kRlsyGxf+E/ZmJ7hSq0ZBVHrVOyXjCcFn6X0/W5SrpOmN3fZYcj8Bp6vhB0cJk9qpjgWOP2RCuBdHZVawjCjIaE+bc=anyone@gmail.comForNodeJSurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportkartik.cds@gmail.comKartikCDShttps://app.onelogin.com/saml/metadata/164679kartik.cds@gmail.comForNodeJSurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportkartik.cds@gmail.comKartikCDS ================================================ FILE: test/static/signature_with_inclusivenamespaces.pem ================================================ -----BEGIN CERTIFICATE----- MIICnTCCAgagAwIBAgIGAUBGHxqUMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYDVQQGEwJVUzETMBEG A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU MBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqGSIb3DQEJARYN aW5mb0Bva3RhLmNvbTAeFw0xMzA4MDMyMTM4MzhaFw00MzA4MDMyMTM5MzhaMIGRMQswCQYDVQQG EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UE CgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqG SIb3DQEJARYNaW5mb0Bva3RhLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsCB9lJTH qB7vdM5jeOH84cW8u7IHYv4/OAPYF0fBYe9wJy19CgyM2OgiASuAcItnH4WhB+io2ZPwb/Xwl7Uu 4XmUE0l+mkCNuDYp5fXTZxwv5G6HvkAxXZio0Rk9T0VETCroxgpS5LxQ/o/owjR39S7xzRnj6ddX 3Mq2yGjKyBcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAB1qGNqSNLLWq+RPcP+wOaWtYpJOJ8/MbZ EWWm9/KKHKXM6J/zgUUIXZi3czMeO+Y+X14PR8lGXoAHf5b/JavG9FmFvRn4fGa45VTVo2GfMN6K aIKF0obeCbYi/QUf8B+Xi1tSIJm1VCKRE7nnliQ/TzGaNulgWeyTbVkG0/X8LQ== -----END CERTIFICATE----- ================================================ FILE: test/static/signature_with_inclusivenamespaces.xml ================================================ http://www.okta.com/k7xkhq0jUHUPQAXVMUAN4G+uveKmtiB1EkY5BAt+8lmQwjI=Q80N6FUr5/YPtEzRlRdMoPu+bL0MssDxNUY+yxykzbmxsI0joEo/SmmSgZrDYQKTllZk/KfzBMPFV9yBH4+mEzCU5E3xuCs99jZzafcw3K8mIMTJy1YHxjc359d27R5s50i9w5PHsusRov0MjQIoJ2w48Gy4EnYaViqBR3UVEqE=MIICnTCCAgagAwIBAgIGAUBGHxqUMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYDVQQGEwJVUzETMBEG A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU MBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqGSIb3DQEJARYN aW5mb0Bva3RhLmNvbTAeFw0xMzA4MDMyMTM4MzhaFw00MzA4MDMyMTM5MzhaMIGRMQswCQYDVQQG EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UE CgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqG SIb3DQEJARYNaW5mb0Bva3RhLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsCB9lJTH qB7vdM5jeOH84cW8u7IHYv4/OAPYF0fBYe9wJy19CgyM2OgiASuAcItnH4WhB+io2ZPwb/Xwl7Uu 4XmUE0l+mkCNuDYp5fXTZxwv5G6HvkAxXZio0Rk9T0VETCroxgpS5LxQ/o/owjR39S7xzRnj6ddX 3Mq2yGjKyBcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAB1qGNqSNLLWq+RPcP+wOaWtYpJOJ8/MbZ EWWm9/KKHKXM6J/zgUUIXZi3czMeO+Y+X14PR8lGXoAHf5b/JavG9FmFvRn4fGa45VTVo2GfMN6K aIKF0obeCbYi/QUf8B+Xi1tSIJm1VCKRE7nnliQ/TzGaNulgWeyTbVkG0/X8LQ==admin@kluglabs.comhttps://auth0145.auth0.comurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportAdmin ================================================ FILE: test/static/signature_with_inclusivenamespaces_lines.xml ================================================ http://www.okta.com/k7xkhq0jUHUPQAXVMUAN4G+uveKmtiB1EkY5BAt+8lmQwjI= Q80N6FUr5/YPtEzRlRdMoPu+bL0MssDxNUY+yxykzbmxsI0joEo/SmmSgZrDYQKTllZk/KfzBMPF V9yBH4+mEzCU5E3xuCs99jZzafcw3K8mIMTJy1YHxjc359d27R5s50i9w5PHsusRov0MjQIoJ2w4 8Gy4EnYaViqBR3UVEqE= MIICnTCCAgagAwIBAgIGAUBGHxqUMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYDVQQGEwJVUzETMBEG A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU MBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqGSIb3DQEJARYN aW5mb0Bva3RhLmNvbTAeFw0xMzA4MDMyMTM4MzhaFw00MzA4MDMyMTM5MzhaMIGRMQswCQYDVQQG EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UE CgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqG SIb3DQEJARYNaW5mb0Bva3RhLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsCB9lJTH qB7vdM5jeOH84cW8u7IHYv4/OAPYF0fBYe9wJy19CgyM2OgiASuAcItnH4WhB+io2ZPwb/Xwl7Uu 4XmUE0l+mkCNuDYp5fXTZxwv5G6HvkAxXZio0Rk9T0VETCroxgpS5LxQ/o/owjR39S7xzRnj6ddX 3Mq2yGjKyBcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAB1qGNqSNLLWq+RPcP+wOaWtYpJOJ8/MbZ EWWm9/KKHKXM6J/zgUUIXZi3czMeO+Y+X14PR8lGXoAHf5b/JavG9FmFvRn4fGa45VTVo2GfMN6K aIKF0obeCbYi/QUf8B+Xi1tSIJm1VCKRE7nnliQ/TzGaNulgWeyTbVkG0/X8LQ== admin@kluglabs.comhttps://auth0145.auth0.comurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportAdmin ================================================ FILE: test/static/signature_with_inclusivenamespaces_lines_windows.xml ================================================ http://www.okta.com/k7xkhq0jUHUPQAXVMUAN4G+uveKmtiB1EkY5BAt+8lmQwjI= Q80N6FUr5/YPtEzRlRdMoPu+bL0MssDxNUY+yxykzbmxsI0joEo/SmmSgZrDYQKTllZk/KfzBMPF V9yBH4+mEzCU5E3xuCs99jZzafcw3K8mIMTJy1YHxjc359d27R5s50i9w5PHsusRov0MjQIoJ2w4 8Gy4EnYaViqBR3UVEqE= MIICnTCCAgagAwIBAgIGAUBGHxqUMA0GCSqGSIb3DQEBBQUAMIGRMQswCQYDVQQGEwJVUzETMBEG A1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UECgwET2t0YTEU MBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqGSIb3DQEJARYN aW5mb0Bva3RhLmNvbTAeFw0xMzA4MDMyMTM4MzhaFw00MzA4MDMyMTM5MzhaMIGRMQswCQYDVQQG EwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzENMAsGA1UE CgwET2t0YTEUMBIGA1UECwwLU1NPUHJvdmlkZXIxEjAQBgNVBAMMCWtsdWdsYWJzMjEcMBoGCSqG SIb3DQEJARYNaW5mb0Bva3RhLmNvbTCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAsCB9lJTH qB7vdM5jeOH84cW8u7IHYv4/OAPYF0fBYe9wJy19CgyM2OgiASuAcItnH4WhB+io2ZPwb/Xwl7Uu 4XmUE0l+mkCNuDYp5fXTZxwv5G6HvkAxXZio0Rk9T0VETCroxgpS5LxQ/o/owjR39S7xzRnj6ddX 3Mq2yGjKyBcCAwEAATANBgkqhkiG9w0BAQUFAAOBgQAB1qGNqSNLLWq+RPcP+wOaWtYpJOJ8/MbZ EWWm9/KKHKXM6J/zgUUIXZi3czMeO+Y+X14PR8lGXoAHf5b/JavG9FmFvRn4fGa45VTVo2GfMN6K aIKF0obeCbYi/QUf8B+Xi1tSIJm1VCKRE7nnliQ/TzGaNulgWeyTbVkG0/X8LQ== admin@kluglabs.comhttps://auth0145.auth0.comurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordProtectedTransportAdmin ================================================ FILE: test/static/unsigned_saml_response.xml ================================================ https://idp.example.com/ https://idp.example.com/ nameId audience urn:oasis:names:tc:SAML:2.0:ac:classes:Password ================================================ FILE: test/static/valid_saml.xml ================================================ https://openidp.feide.no fc21hh1bKZpaMNjx9HfOfVelfWw=dkONrkxW+LSuDvnNMG/mWYFa47d2WGyapLhXSTYqrlT9Td+tT7ciojNJ55WTaPaCMt7IrGtIxxskPAZIjdIn5pRyDxHr0joWxzZ7oZHCOI1CnQV5HjOq+rzzmEN2LctCZ6S4hbL7SQ1qJ3vp2BCXAygy4tmJOURQdnk0KLwwRS8= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==https://openidp.feide.no RnNjoyUguwze5w2R+cboyTHlkQk=aw5711jKP7xragunjRRCAD4mT4xKHc37iohBpQDbdSomD3ksOSB96UZQp0MtaC3xlVSkMtYw85Om96T2q2xrxLLYVA50eFJEMMF7SCVPStWTVjBlaCuOPEQxIaHyJs9Sy3MCEfbBh4Pqn9IJBd1kzwdlCrWWjAmksbFFg5wHQJA= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==_6c5dcaa3053321ff4d63785fbc3f67c59a129cde82passport-samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordbergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3ebergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3e ================================================ FILE: test/static/valid_saml_sha256_rsa_mgf1.xml ================================================ bA/90zzLS/36DstvPJRrMNwGax5WQv8NneSuNdLnMYs= 0fK5rJEtj0+JIL3hAuDsEAuKYoiwHzZTgQgspLyEe+XQan1FzT+qu3GBJSpSDfArBHjHXtizVfi8 irId6a1kOj6ShEw2ZSGYD8Dh2d0HmrHqlOqpZ5eLiWeFA6VTtW1Cqmvr+x4Ndxcg0wWmmGr4hpSD Yg8fkA8e32Fd2QxqLsQqVlCcuvJVCJ/12XSGcMW+Tse254fN6JENLLUdilu+14NNQKAHpKpjeajg jG3fn0VNvyVQXKi2deYTWYaLRujBgv3Ncz8t9Hjthk+XxrRVHJiGc6HyVvqdpi5ChM41fjB1+eBo NkZ0Q73ZSCbTAqVduUWqL5pQzINq16kUm2ovkg7h3JoqSQr5yhoBJXZEf6FEyYdCCLd3rlIrkcvD +wfF2CwNRc6utgO/05JAA7Z1x2e0K6o2a6EQy93dkUIBhpxPYU/IdmGb15AfKJ3OrB1K/jTrxZ2q og/u0fJe0vU1sL4EDOXqVMj/unTZqDP/K1mOHK/eDWafs/IwMv65ebZUwTk74AMk/oOYV9mL8beY JAVYMN+xPA1cXnHlRgwATWLXjfiQcMXo44nhaw0YlOUGIoRLYURqHmXz0W3d8pXYdybLmdClkqLR vo1ryK1OC2paYG9qwk51QJ5wzMv6HRB5tDIL9/7mP7khlKgm4p+EZXFGYmvYDbk2x219SvxPmfY= 16YEqKYde+lUD8PuHvdVAC+o9E2y/2iNwJo+tk1PdSjpOMYoMvvMRUnlNiAuuFu2m3xavtuSvtcm yZjAaveFCsimm+8nl5np9sSfT0+W6+E8J7G3gWa84l0kfLkxOtA3Vi2WCRAURILveiXhFi8z/9B3 5xZZ7huZF5W+q6cu5e9t2Hg71PZb7JukY+OpuUfI+Aknpypi7B0iIZjT+fUrBKSdeg/kXynKyqsT 8aerf6AhCl46ZjnhqF/Vk+VfVDMaxhYGNmC2m/bSwiCuPIuq4F6R1J2zKtl8gE+fG0hv04n/fYaI kNqL87FVK1XgmuP7lIP6a/sNW2ZyAZekfFlT01cIJmDfvFsNu2i4jZLAp+9Le+F2S6/cZ2J25499 uAImafWePLwxApuNU3PlfXqhjTv+WC4GaqBk8uBJdwDHDRd5ZHnGdlGSjCN+NaSNbx8kRtIwq9mW Kct9KWECnfmewL2SjFEyRUHwSDp981zekgPJVmSzzluFU3Tn3Bo0Io9wFA3k3opVIoKUYJE5fe1l Kxn2rLy1zK3UaF2MYxj3vrOPrX/oJyoU1UOoesHviEmhVTnl6QqzlMGTNDnYpELSmIB9BvTqySsQ 2Q5eGJJGBaqocsORLpvR2cDFPOt9ZZAUqjEYM/AF+ynQZ9T6xkusDqfhgtvC9SEnkcUa4lrWnl0= AQAB MIIFFzCCAv+gAwIBAgIUaAU88KUbZLe7NwTw+jdCHIDU6wIwDQYJKoZIhvcNAQELBQAwGjEYMBYG A1UEAwwPaWRwLmV4YW1wbGUuY29tMCAXDTI1MDkwODExMTUzMFoYDzIxMjUwODE1MTExNTMwWjAa MRgwFgYDVQQDDA9pZHAuZXhhbXBsZS5jb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC AQDXpgSoph176VQPw+4e91UAL6j0TbL/aI3Amj62TU91KOk4xigy+8xFSeU2IC64W7abfFq+25K+ 1ybJmMBq94UKyKab7yeXmen2xJ9PT5br4TwnsbeBZrziXSR8uTE60DdWLZYJEBREgu96JeEWLzP/ 0HfnFlnuG5kXlb6rpy7l723YeDvU9lvsm6Rj46m5R8j4CSenKmLsHSIhmNP59SsEpJ16D+RfKcrK qxPxp6t/oCEKXjpmOeGoX9WT5V9UMxrGFgY2YLab9tLCIK48i6rgXpHUnbMq2XyAT58bSG/Tif99 hoiQ2ovzsVUrVeCa4/uUg/pr+w1bZnIBl6R8WVPTVwgmYN+8Ww27aLiNksCn70t74XZLr9xnYnbn j324AiZp9Z48vDECm41Tc+V9eqGNO/5YLgZqoGTy4El3AMcNF3lkecZ2UZKMI341pI1vHyRG0jCr 2ZYpy30pYQKd+Z7AvZKMUTJFQfBIOn3zXN6SA8lWZLPOW4VTdOfcGjQij3AUDeTeilUigpRgkTl9 7WUrGfasvLXMrdRoXYxjGPe+s4+tf+gnKhTVQ6h6we+ISaFVOeXpCrOUwZM0OdikQtKYgH0G9OrJ KxDZDl4YkkYFqqhyw5Eum9HZwMU8631lkBSqMRgz8AX7KdBn1PrGS6wOp+GC28L1ISeRxRriWtae XQIDAQABo1MwUTAdBgNVHQ4EFgQULRbUCREu3zQABqNW8LMOfraF5BMwHwYDVR0jBBgwFoAULRbU CREu3zQABqNW8LMOfraF5BMwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAgEAA6Jm 3pll7XHBY/oXrtRAu60RpH4pcZBSf68zhCQKUnXALy2FWrUGU8uVCJJuxXIcxszTOCZmwVF12Yce IFpBrWHxKCDZWYDO6hrwZvsa2vh5mXDZo3c/HX6GmJR4f4oIIZnbxFhXR30419PlrwMW3Rk7rnVS Dz/HpQfdS4y8jYgsm2dIRo+PXQytFRCDj4afsT3eZa27QjFxLTuK+SwkhupSH1WWYmqL9+iIJiLL /ntfM4MwtOUwcfqI0ttbvFZZIPneBCuEDLn/zJ/QBV3ZvjQDt53W21HaPGPHBBfVZVroZPvuGvul WRLeECI1Hmbl3al/2aOC0LWzPIk8dlTcN+EWcCEufpTOEgkob8waEyxlX0Z5OBjkCHpyDTPGkxBB OCJv88Frx7qdbu8eSV0OviuaM/B7ky2NbDMKIybX4tf/Q8FNfjPLvTv+8nbrMz6kTno3RR2YC7tt I7Glb/eOg3F/ouF4wcoPAj+OyU5Q5WJMMaZ9cXF9pwszsglLbFms+WD5PFxlloh6I1hO7TccWKTe UJ5fYZSYYm31JqWit3DBltXTyRyL7KFSdT/FyRBk62YLCJqJmcukIUJUTya73/RmjW1M4zMBNaIj /pH77opKWnVbm9F4XnBCG4r9+FRXdV2zKW6DgvDzQ6DaZ+0cwC36vSqkDyjZYv/tviuWfM8= https://idp.example.com/ https://idp.example.com/ nameId audience urn:oasis:names:tc:SAML:2.0:ac:classes:Password ================================================ FILE: test/static/valid_saml_signature_wrapping.xml ================================================ https://openidp.feide.no https://openidp.feide.no RnNjoyUguwze5w2R+cboyTHlkQk=aw5711jKP7xragunjRRCAD4mT4xKHc37iohBpQDbdSomD3ksOSB96UZQp0MtaC3xlVSkMtYw85Om96T2q2xrxLLYVA50eFJEMMF7SCVPStWTVjBlaCuOPEQxIaHyJs9Sy3MCEfbBh4Pqn9IJBd1kzwdlCrWWjAmksbFFg5wHQJA= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==_6c5dcaa3053321ff4d63785fbc3f67c59a129cde82passport-samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordbergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3ebergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3e https://openidp.feide.no RnNjoyUguwze5w2R+cboyTHlkQk=aw5711jKP7xragunjRRCAD4mT4xKHc37iohBpQDbdSomD3ksOSB96UZQp0MtaC3xlVSkMtYw85Om96T2q2xrxLLYVA50eFJEMMF7SCVPStWTVjBlaCuOPEQxIaHyJs9Sy3MCEfbBh4Pqn9IJBd1kzwdlCrWWjAmksbFFg5wHQJA= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w== ================ This Assertion contains malicious data. ================ ================================================ FILE: test/static/valid_saml_with_digest_comment.xml ================================================ https://openidp.feide.no https://openidp.feide.no RnNjoyUguwze5w2R+cboyTHlkQk=aw5711jKP7xragunjRRCAD4mT4xKHc37iohBpQDbdSomD3ksOSB96UZQp0MtaC3xlVSkMtYw85Om96T2q2xrxLLYVA50eFJEMMF7SCVPStWTVjBlaCuOPEQxIaHyJs9Sy3MCEfbBh4Pqn9IJBd1kzwdlCrWWjAmksbFFg5wHQJA= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==test@example.compassport-samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordbergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3ebergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3e ================================================ FILE: test/static/valid_saml_withcomments.xml ================================================ https://openidp.feide.no fc21hh1bKZpaMNjx9HfOfVelfWw=dkONrkxW+LSuDvnNMG/mWYFa47d2WGyapLhXSTYqrlT9Td+tT7ciojNJ55WTaPaCMt7IrGtIxxskPAZIjdIn5pRyDxHr0joWxzZ7oZHCOI1CnQV5HjOq+rzzmEN2LctCZ6S4hbL7SQ1qJ3vp2BCXAygy4tmJOURQdnk0KLwwRS8= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==https://openidp.feide.no RnNjoyUguwze5w2R+cboyTHlkQk=aw5711jKP7xragunjRRCAD4mT4xKHc37iohBpQDbdSomD3ksOSB96UZQp0MtaC3xlVSkMtYw85Om96T2q2xrxLLYVA50eFJEMMF7SCVPStWTVjBlaCuOPEQxIaHyJs9Sy3MCEfbBh4Pqn9IJBd1kzwdlCrWWjAmksbFFg5wHQJA= MIICizCCAfQCCQCY8tKaMc0BMjANBgkqhkiG9w0BAQUFADCBiTELMAkGA1UEBhMCTk8xEjAQBgNVBAgTCVRyb25kaGVpbTEQMA4GA1UEChMHVU5JTkVUVDEOMAwGA1UECxMFRmVpZGUxGTAXBgNVBAMTEG9wZW5pZHAuZmVpZGUubm8xKTAnBgkqhkiG9w0BCQEWGmFuZHJlYXMuc29sYmVyZ0B1bmluZXR0Lm5vMB4XDTA4MDUwODA5MjI0OFoXDTM1MDkyMzA5MjI0OFowgYkxCzAJBgNVBAYTAk5PMRIwEAYDVQQIEwlUcm9uZGhlaW0xEDAOBgNVBAoTB1VOSU5FVFQxDjAMBgNVBAsTBUZlaWRlMRkwFwYDVQQDExBvcGVuaWRwLmZlaWRlLm5vMSkwJwYJKoZIhvcNAQkBFhphbmRyZWFzLnNvbGJlcmdAdW5pbmV0dC5ubzCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAt8jLoqI1VTlxAZ2axiDIThWcAOXdu8KkVUWaN/SooO9O0QQ7KRUjSGKN9JK65AFRDXQkWPAu4HlnO4noYlFSLnYyDxI66LCr71x4lgFJjqLeAvB/GqBqFfIZ3YK/NrhnUqFwZu63nLrZjcUZxNaPjOOSRSDaXpv1kb5k3jOiSGECAwEAATANBgkqhkiG9w0BAQUFAAOBgQBQYj4cAafWaYfjBU2zi1ElwStIaJ5nyp/s/8B8SAPK2T79McMyccP3wSW13LHkmM1jwKe3ACFXBvqGQN0IbcH49hu0FKhYFM/GPDJcIHFBsiyMBXChpye9vBaTNEBCtU3KjjyG0hRT2mAQ9h+bkPmOvlEo/aH0xR68Z9hw4PF13w==_6c5dcaa3053321ff4d63785fbc3f67c59a129cde82passport-samlurn:oasis:names:tc:SAML:2.0:ac:classes:PasswordbergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3ebergieHenriBergiusHenri Bergiushenri.bergius@nemein.combergie@rnd.feide.no8216c78fe244502efa13f62e6615c94acb7bdf3e ================================================ FILE: test/static/valid_signature wsu.xml ================================================ 5jkdJLKVpfEiAQ0k5bnvfCQJKks=97gNxHth/Gc2wH4FiTiWJJrLaHI=TUkwwDGRfgW3CORC9RvimMKtFpU=V5wZiNWAzyQmAhm+F3ji0IIq9ariZ/ORCdHXjhwDU36yPUuRiyLoy7WJvl7tkyV/C/D7R2WiQ/TwbM3973ZWToKUwAM/FSzOTu+4P/ea8zZ6SDxYTFxDwvxSdtfiYqZ/C61EApnNU5haoyiZ6H9ZBeJaERWIDI+fpJcCA5c6sb0= ================================================ FILE: test/static/valid_signature.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI=1234 ================================================ FILE: test/static/valid_signature_utf8.xml ================================================ urn:issueris/s3v+lPE4xdF9ImlbRMoXJjOPWKH8C/ixcfRaDPgU=DgIb3UlS5p7j6iEUD1SsaPs2HqHnI3nC2ixw7CRebgcz8lG6o7AbgRsy57ePr1hX90lxOGvO8Txz8kJDq3vWH6g9fU0Nu+1QxCq4/mb3a2Jr84lFDivSUQa7YEugECH789EvRzEXRTseKx8XYKjlH6ffvai3Oqn9kSLWPQbY7xI=MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJflвКонтактеurn:myappсообщить@bar.comсообщить вКонтактеurn:oasis:names:tc:SAML:2.0:ac:classes:unspecified ================================================ FILE: test/static/valid_signature_with_lowercase_id_attribute.xml ================================================ AAGWFnPFdPl0aU4854Deo+j9BmU=79MH3RaPsyFcSyrzl4Jrpwp/lgo=MEdYYk8XXRBbW5Hlo8GlIO9J0fc=aFMgQTWuz+T0AzqERRTq/+XAxZmIs+ff031I579zfDTD/PjrCOhb4TVAkgydZajUkZwbOc5/mnBs9S/nqfABh1JXvqO/a+U+AzgSDoZesJ3OMHcPPSFt+5iYASjsV0B3MLUds4iNl76ETcwp1HC6u8rRspQ3uWzFt2zBzfEvmtY=1234 ================================================ FILE: test/static/valid_signature_with_reference_keyInfo.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=PI2xGt3XrVcxYZ34Kw7nFdq75c7Mmo7J0q7yeDhBprHuJal/KV9KyKG+Zy3bmQIxNwkPh0KMP5r1YMTKlyifwbWK0JitRCSa0Fa6z6+TgJi193yiR5S1MQ+esoQT0RzyIOBl9/GuJmXx/1rXnqrTxmL7UxtqKuM29/eHwF0QDUI= ================================================ FILE: test/static/valid_signature_with_root_level_sig_namespace.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ=K4dI497ZCxzweDIrbndUSmtoezY=sH1gxKve8wlU8LlFVa2l6w3HMJ0=rR8+4xHiI8GQJ9Wty2TUbNI7Dd4uc89/BsAygYfeobEjmt4awzg6bQNA0nuQ+VggiPCYdKuKL8cPI7FUhk8osbVKdLPdy+rdJnibsyNpV87R7W5GZlFBEu/NqG6EYOMTHjpD4hq+H8ZeHC5YZDHPknPzJV8+A1UKN/BL2oWMQcg=1234 ================================================ FILE: test/static/valid_signature_with_unused_prefixes.xml ================================================ GAEFzMhRbXD8lZeTd5GwSqkB73Q=c2bqLRa0Q+/P7+vuXZHO8/iiac8=95WoCzf9J63UgdLCj/05PYaJIAw=pa04vRHbAdvfr6Re5wgx2qFQepE=YYNZUGNHN8uRGzMqPVC2n5XODP8=GvVowT6jH/4fExGYfFzDbzhSwTs=F8Lm5aUouA5FtxQ1krQ3unE9NFh0QSl+QscEyTcK3FrIpWCB195Z7cbmYa9RsiYMdr2wTHHmou/+wrjpk9pZFJq+b0tpHKCpfj6B302Rexb5f+cDpUjBB/NGb11qaUiM65keVIWzmYnHC0iCxsCaG3lwHMELNr7GxNun1U7LzzI=MIIBxDCCAW6gAwIBAgIQxUSXFzWJYYtOZnmmuOMKkjANBgkqhkiG9w0BAQQFADAWMRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMzA3MDgxODQ3NTlaFw0zOTEyMzEyMzU5NTlaMB8xHTAbBgNVBAMTFFdTRTJRdWlja1N0YXJ0Q2xpZW50MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC+L6aB9x928noY4+0QBsXnxkQE4quJl7c3PUPdVu7k9A02hRG481XIfWhrDY5i7OEB7KGW7qFJotLLeMec/UkKUwCgv3VvJrs2nE9xO3SSWIdNzADukYh+Cxt+FUU6tUkDeqg7dqwivOXhuOTRyOI3HqbWTbumaLdc8jufz2LhaQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCNYSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mCEAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQADQQAfIbnMPVYkNNfX1tG1F+qfLhHwJdfDUZuPyRPucWF5qkh6sSdWVBY5sT/txBnVJGziyO8DPYdu2fPMER8ajJfl2019-03-18T16:27:47.984Z2019-03-19T04:27:47.984Zhttp://www.example.com/testActionclientuuid:c519e597-0570-4d35-92e5-0df733a17cc1testtestMessage ================================================ FILE: test/static/valid_signature_with_whitespace_in_digestvalue.xml ================================================ b5GCZ2xpP5T7tbLWBTkOl4CYupQ= K4dI497ZCxzweDIrbndUSmtoezY= sH1gxKve8wlU8LlFVa2l6w3HMJ0= gGp+jskU2HohMdhaeGTdJBbN/rngzWrQs0+N4bqJDzHQEqUm6rVk0mDoFybJaW0AEXf/dSVhM0faYTwQK0p9aipsAORTaPq677GcdKwyEHHRnly064D8GlikyQ49451SEViy89kYZO1yf+x5MNHNyCi7VVRIixbsSLOUgu0A9Yo= ================================================ FILE: test/static/valid_signature_without_transforms_element.xml ================================================ some textLsMoqo1d6Sqh8DKLp00MK0fSBDA=OR1SYcyU18qELj+3DX/bW/r5DqueuyPAnNFEh3hNKFaj8ZKLB/mdsR9w8GDBCmZ2 lsCTEvJqWC37oF8rm2eBSonNbdBnA+TM6Y22C8rffVzaoM3zpNoeWMH2LwFmpdKB UXOMWVExEaz/s4fOcyv1ajVuk42I3nl0xcD95+i7PjY= ================================================ FILE: test/static/windows_store_certificate.pem ================================================ -----BEGIN CERTIFICATE----- MIIDyTCCArGgAwIBAgIQNP+YKvSo8IVArhlhpgc/xjANBgkqhkiG9w0BAQsFADCB jjELMAkGA1UEBhMCVVMxEzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1Jl ZG1vbmQxHjAcBgNVBAoMFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEWMBQGA1UECwwN V2luZG93cyBTdG9yZTEgMB4GA1UEAwwXV2luZG93cyBTdG9yZSBMaWNlbnNpbmcw HhcNMTExMTE3MjMwNTAyWhcNMzYxMTEwMjMxMzQ0WjCBjjELMAkGA1UEBhMCVVMx EzARBgNVBAgMCldhc2hpbmd0b24xEDAOBgNVBAcMB1JlZG1vbmQxHjAcBgNVBAoM FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEWMBQGA1UECwwNV2luZG93cyBTdG9yZTEg MB4GA1UEAwwXV2luZG93cyBTdG9yZSBMaWNlbnNpbmcwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQCcr4/vgqZFtzMqy3jO0XHjBUNx6j7ZTXEnNpLl2VSe zVQA9KK2RlvroXKhYMUUdJpw+txm1mqi/W7D9QOYTq1e83GLhWC9IRh/OSmSYt0e kgVLB+icyRH3dtpYcJ5sspU2huPf4I/Nc06OuXlMsD9MU4Ug9IBD2HSDBEquhGRo xV64YuEH4645oB14LlEay0+JZlkKZ/mVhx/sdzSBfrda1X/Ckc7SOgnTSM3d/DnO 5DKwV2WYn+7i/rBqe4/op6IqQMrPpHyem9Sny+i0xiUMA+1IwkX0hs0gvHM6zDww TMDiTapbCy9LnmMx65oMq56hhsQydLEmquq8lVYUDEzLAgMBAAGjITAfMB0GA1Ud DgQWBBREzrOBz7zw+HWskxonOXAPMa6+NzANBgkqhkiG9w0BAQsFAAOCAQEAeVtN 4c6muxO6yfht9SaxEfleUBIjGfe0ewLBp00Ix7b7ldJ/lUQcA6y+Drrl7vjmkHQK OU3uZiFbCxTvgTcoz9o+1rzR/WPXmqH5bqu6ua/UrobGKavAScqqI/G6o56Xmx/y oErWN0VapN370crKJvNWxh3yw8DCl+W0EcVRiWX5lFsMBNBbVpK4Whp+VhkSJilu iRpe1B35Q8EqOz/4RQkOpVI0dREnuSYkBy/h2ggCtiQ5yfvH5zCdcfhFednYDevS axmt3W5WuHz8zglkg+OQ3qpXaXySRlrmLdxEmWu2MOiZbQkU2ZjBSQmvFAOy0dd6 P1YLS4+Eyh5drQJc0Q== -----END CERTIFICATE----- ================================================ FILE: test/static/windows_store_signature.xml ================================================ cdiU06eD8X/w1aGCHeaGCG9w/kWZ8I099rw4mmPpvdU= SjRIxS/2r2P6ZdgaR9bwUSa6ZItYYFpKLJZrnAa3zkMylbiWjh9oZGGng2p6/gtBHC2dSTZlLbqnysJjl7mQp/A3wKaIkzjyRXv3kxoVaSV0pkqiPt04cIfFTP0JZkE5QD/vYxiWjeyGp1dThEM2RV811sRWvmEs/hHhVxb32e8xCLtpALYx3a9lW51zRJJN0eNdPAvNoiCJlnogAoTToUQLHs72I1dECnSbeNPXiG7klpy5boKKMCZfnVXXkneWvVFtAA1h2sB7ll40LEHO4oYN6VzD+uKd76QOgGmsu9iGVyRvvmMtahvtL1/pxoxsTRedhKq6zrzCfT8qfh3C1w== ================================================ FILE: test/static/wsfederation_metadata.pem ================================================ -----BEGIN CERTIFICATE----- MIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAt MSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4X DTE0MTAyODAwMDAwMFoXDTE2MTAyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3Vu dHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQAD ggEPADCCAQoCggEBALyKs/uPhEf7zVizjfcr/ISGFe9+yUOqwpel38zgutvLHmFD 39E2hpPdQhcXn4c4dt1fU5KvkbcDdVbP8+e4TvNpJMy/nEB2V92zCQ/hhBjilwhF 1ETe1TMmVjALs0KFvbxW9ZN3EdUVvxFvz/gvG29nQhl4QWKj3x8opr89lmq14Z7T 0mzOV8kub+cgsOU/1bsKqrIqN1fMKKFhjKaetctdjYTfGzVQ0AJAzzbtg0/Q1wdY NAnhSDafygEv6kNiquk0r0RyasUUevEXs2LY3vSgKsKseI8ZZlQEMtE9/k/iAG7J NcEbVg53YTurNTrPnXJOU88mf3TToX14HpYsS1ECAwEAATANBgkqhkiG9w0BAQsF AAOCAQEAfolx45w0i8CdAUjjeAaYdhG9+NDHxop0UvNOqlGqYJexqPLuvX8iyUaY xNGzZxFgGI3GpKfmQP2JQWQ1E5JtY/n8iNLOKRMwqkuxSCKJxZJq4Sl/m/Yv7TS1 P5LNgAj8QLCypxsWrTAmq2HSpkeSk4JBtsYxX6uhbGM/K1sEktKybVTHu22/7TmR qWTmOUy9wQvMjJb2IXdMGLG3hVntN/WWcs5w8vbt1i8Kk6o19W2MjZ95JaECKjBD YRlhG1KmSBtrsKsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0 H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ== -----END CERTIFICATE----- ================================================ FILE: test/static/wsfederation_metadata.xml ================================================ qIVhfzD3HVMA4BUQZ+zUF6AlFgcL7FyQ8tN35NZWFJs=HxgwvF+xtlUb4Qa9AzEiti4X3rHu6xWOmew2sVH+BBWpuwhDWWPxK9hHVhYYcuCHZDBnN7LLTY1L80/D2+KruNug9B1kOb6c3S/VWV09wbmIyocG1nH4/FGQf8+AU7ajFizG+ODhfJY0xEOag1E5cwXqrM4ULu6HBSAkLDNBA85m8qi/UAd6INyel0DzwfANvjz34VZOLMX+rydyXoKhSpKoBlip1eHdUdzOM4HtlnemIZhMkgofNjxbFjRNwPclizwWJuF0I0Xj1jwT8wR4X7wWvPO9JgxgR6CixveZRt/is5IVgKl/UeqHCzS/a5tYatoF0o35byC0E2ehOJGCBw==MIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE0MTAyODAwMDAwMFoXDTE2MTAyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALyKs/uPhEf7zVizjfcr/ISGFe9+yUOqwpel38zgutvLHmFD39E2hpPdQhcXn4c4dt1fU5KvkbcDdVbP8+e4TvNpJMy/nEB2V92zCQ/hhBjilwhF1ETe1TMmVjALs0KFvbxW9ZN3EdUVvxFvz/gvG29nQhl4QWKj3x8opr89lmq14Z7T0mzOV8kub+cgsOU/1bsKqrIqN1fMKKFhjKaetctdjYTfGzVQ0AJAzzbtg0/Q1wdYNAnhSDafygEv6kNiquk0r0RyasUUevEXs2LY3vSgKsKseI8ZZlQEMtE9/k/iAG7JNcEbVg53YTurNTrPnXJOU88mf3TToX14HpYsS1ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfolx45w0i8CdAUjjeAaYdhG9+NDHxop0UvNOqlGqYJexqPLuvX8iyUaYxNGzZxFgGI3GpKfmQP2JQWQ1E5JtY/n8iNLOKRMwqkuxSCKJxZJq4Sl/m/Yv7TS1P5LNgAj8QLCypxsWrTAmq2HSpkeSk4JBtsYxX6uhbGM/K1sEktKybVTHu22/7TmRqWTmOUy9wQvMjJb2IXdMGLG3hVntN/WWcs5w8vbt1i8Kk6o19W2MjZ95JaECKjBDYRlhG1KmSBtrsKsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ==MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZMIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE0MTAyODAwMDAwMFoXDTE2MTAyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALyKs/uPhEf7zVizjfcr/ISGFe9+yUOqwpel38zgutvLHmFD39E2hpPdQhcXn4c4dt1fU5KvkbcDdVbP8+e4TvNpJMy/nEB2V92zCQ/hhBjilwhF1ETe1TMmVjALs0KFvbxW9ZN3EdUVvxFvz/gvG29nQhl4QWKj3x8opr89lmq14Z7T0mzOV8kub+cgsOU/1bsKqrIqN1fMKKFhjKaetctdjYTfGzVQ0AJAzzbtg0/Q1wdYNAnhSDafygEv6kNiquk0r0RyasUUevEXs2LY3vSgKsKseI8ZZlQEMtE9/k/iAG7JNcEbVg53YTurNTrPnXJOU88mf3TToX14HpYsS1ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfolx45w0i8CdAUjjeAaYdhG9+NDHxop0UvNOqlGqYJexqPLuvX8iyUaYxNGzZxFgGI3GpKfmQP2JQWQ1E5JtY/n8iNLOKRMwqkuxSCKJxZJq4Sl/m/Yv7TS1P5LNgAj8QLCypxsWrTAmq2HSpkeSk4JBtsYxX6uhbGM/K1sEktKybVTHu22/7TmRqWTmOUy9wQvMjJb2IXdMGLG3hVntN/WWcs5w8vbt1i8Kk6o19W2MjZ95JaECKjBDYRlhG1KmSBtrsKsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ==NameThe mutable display name of the user.SubjectAn immutable, globally unique, non-reusable identifier of the user that is unique to the application for which a token is issued.Given NameFirst name of the user.SurnameLast name of the user.Display NameDisplay name of the user.Nick NameNick name of the user.Authentication InstantThe time (UTC) when the user is authenticated to Windows Azure Active Directory.Authentication MethodThe method that Windows Azure Active Directory uses to authenticate users.ObjectIdentifierPrimary identifier for the user in the directory. Immutable, globally unique, non-reusable.TenantIdIdentifier for the user's tenant.IdentityProviderIdentity provider for the user.EmailEmail address of the user.GroupsGroups of the user.External Access TokenAccess token issued by external identity provider.External Access Token ExpirationUTC expiration time of access token issued by external identity provider.External OpenID 2.0 IdentifierOpenID 2.0 identifier issued by external identity provider.GroupsOverageClaimIssued when number of user's group claims exceeds return limit.Role ClaimRoles that the user or Service Principal is attached toRoleTemplate Id ClaimRole template id of the Built-in Directory Roles that the user is a member of
https://login.windows.net/8bd6e98d-e212-4022-b13f-a244fab4c253/wsfed
https://login.windows.net/8bd6e98d-e212-4022-b13f-a244fab4c253/wsfed
MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZMIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE0MTAyODAwMDAwMFoXDTE2MTAyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALyKs/uPhEf7zVizjfcr/ISGFe9+yUOqwpel38zgutvLHmFD39E2hpPdQhcXn4c4dt1fU5KvkbcDdVbP8+e4TvNpJMy/nEB2V92zCQ/hhBjilwhF1ETe1TMmVjALs0KFvbxW9ZN3EdUVvxFvz/gvG29nQhl4QWKj3x8opr89lmq14Z7T0mzOV8kub+cgsOU/1bsKqrIqN1fMKKFhjKaetctdjYTfGzVQ0AJAzzbtg0/Q1wdYNAnhSDafygEv6kNiquk0r0RyasUUevEXs2LY3vSgKsKseI8ZZlQEMtE9/k/iAG7JNcEbVg53YTurNTrPnXJOU88mf3TToX14HpYsS1ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfolx45w0i8CdAUjjeAaYdhG9+NDHxop0UvNOqlGqYJexqPLuvX8iyUaYxNGzZxFgGI3GpKfmQP2JQWQ1E5JtY/n8iNLOKRMwqkuxSCKJxZJq4Sl/m/Yv7TS1P5LNgAj8QLCypxsWrTAmq2HSpkeSk4JBtsYxX6uhbGM/K1sEktKybVTHu22/7TmRqWTmOUy9wQvMjJb2IXdMGLG3hVntN/WWcs5w8vbt1i8Kk6o19W2MjZ95JaECKjBDYRlhG1KmSBtrsKsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ==
https://sts.windows.net/8bd6e98d-e212-4022-b13f-a244fab4c253/
https://login.windows.net/8bd6e98d-e212-4022-b13f-a244fab4c253/wsfed
https://login.windows.net/8bd6e98d-e212-4022-b13f-a244fab4c253/wsfed
MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZMIIC4jCCAcqgAwIBAgIQQNXrmzhLN4VGlUXDYCRT3zANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE0MTAyODAwMDAwMFoXDTE2MTAyNzAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALyKs/uPhEf7zVizjfcr/ISGFe9+yUOqwpel38zgutvLHmFD39E2hpPdQhcXn4c4dt1fU5KvkbcDdVbP8+e4TvNpJMy/nEB2V92zCQ/hhBjilwhF1ETe1TMmVjALs0KFvbxW9ZN3EdUVvxFvz/gvG29nQhl4QWKj3x8opr89lmq14Z7T0mzOV8kub+cgsOU/1bsKqrIqN1fMKKFhjKaetctdjYTfGzVQ0AJAzzbtg0/Q1wdYNAnhSDafygEv6kNiquk0r0RyasUUevEXs2LY3vSgKsKseI8ZZlQEMtE9/k/iAG7JNcEbVg53YTurNTrPnXJOU88mf3TToX14HpYsS1ECAwEAATANBgkqhkiG9w0BAQsFAAOCAQEAfolx45w0i8CdAUjjeAaYdhG9+NDHxop0UvNOqlGqYJexqPLuvX8iyUaYxNGzZxFgGI3GpKfmQP2JQWQ1E5JtY/n8iNLOKRMwqkuxSCKJxZJq4Sl/m/Yv7TS1P5LNgAj8QLCypxsWrTAmq2HSpkeSk4JBtsYxX6uhbGM/K1sEktKybVTHu22/7TmRqWTmOUy9wQvMjJb2IXdMGLG3hVntN/WWcs5w8vbt1i8Kk6o19W2MjZ95JaECKjBDYRlhG1KmSBtrsKsCBQoBzwH/rXfksTO9JoUYLXiW0IppB7DhNH4PJ5hZI91R8rR0H3/bKkLSuDaKLWSqMhozdhXsIIKvJQ==
================================================ FILE: test/utils-tests.spec.ts ================================================ import * as fs from "fs"; import * as utils from "../src/utils"; import { expect } from "chai"; import * as xmldom from "@xmldom/xmldom"; import * as xpath from "xpath"; import * as isDomNode from "@xmldom/is-dom-node"; 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(); }); it("will return a normalized PEM format when given a DER Buffer", function () { const normalizedPem = fs.readFileSync("./test/static/client_public.pem", "latin1"); const derBuffer = fs.readFileSync("./test/static/client_public.der"); expect(utils.derToPem(derBuffer, "CERTIFICATE")).to.equal(normalizedPem); }); it("will return a normalized PEM format when given a base64 string with line breaks", function () { const normalizedPem = fs.readFileSync("./test/static/client_public.pem", "latin1"); const base64String = fs.readFileSync("./test/static/client_public.der", "base64"); expect(utils.derToPem(base64String, "CERTIFICATE")).to.equal(normalizedPem); }); it("will return a normalized PEM format when given a base64 string with line breaks and spaces at the line breaks", function () { const xml = new xmldom.DOMParser().parseFromString( fs.readFileSync("./test/static/keyinfo - pretty-printed.xml", "latin1"), ); const cert = xpath.select1(".//*[local-name(.)='X509Certificate']", xml); isDomNode.assertIsNodeLike(cert); const normalizedPem = fs.readFileSync("./test/static/keyinfo.pem", "latin1"); expect(utils.derToPem(cert.textContent ?? "", "CERTIFICATE")).to.equal(normalizedPem); }); it("will throw if the DER string is not base64 encoded", function () { expect(() => utils.derToPem("not base64", "CERTIFICATE")).to.throw(); }); it("will throw if the PEM label is not provided", function () { const derBuffer = fs.readFileSync("./test/static/client_public.der"); expect(() => utils.derToPem(derBuffer)).to.throw(); }); }); describe("pemToDer", function () { it("will return a Buffer of binary DER when given a normalized PEM format", function () { const pem = fs.readFileSync("./test/static/client_public.pem", "latin1"); const derBuffer = fs.readFileSync("./test/static/client_public.der"); const result = utils.pemToDer(pem); expect(result).to.be.instanceOf(Buffer); expect(result).to.deep.equal(derBuffer); }); it("will throw if the format is not PEM", function () { expect(() => utils.pemToDer("not a pem")).to.throw(); }); }); }); ================================================ FILE: test/validators/XmlCryptoJava/pom.xml ================================================ 4.0.0 XML Crypto Java Tests org.nodejs xml-crypto-java 1.0.0-SNAPSHOT jar 2015 MIT License http://www.opensource.org/licenses/mit-license.php fcorneli Frank Cornelis info@e-contract.be junit junit 4.13.1 test org.slf4j slf4j-api 1.7.12 test org.slf4j slf4j-log4j12 1.7.12 test log4j log4j 1.2.17 test commons-io commons-io 2.7 test org.apache.maven.plugins maven-compiler-plugin 3.3 1.7 1.7 UTF-8 UTF-8 ================================================ FILE: test/validators/XmlCryptoJava/src/test/java/org/nodejs/xmlcrypto/HMACTest.java ================================================ package org.nodejs.xmlcrypto; import java.io.File; import java.util.Collections; import java.util.LinkedList; import java.util.List; import javax.crypto.KeyGenerator; import javax.crypto.SecretKey; import javax.xml.crypto.dsig.CanonicalizationMethod; import javax.xml.crypto.dsig.DigestMethod; import javax.xml.crypto.dsig.Reference; import javax.xml.crypto.dsig.SignatureMethod; import javax.xml.crypto.dsig.SignedInfo; import javax.xml.crypto.dsig.Transform; import javax.xml.crypto.dsig.XMLSignature; import javax.xml.crypto.dsig.XMLSignatureFactory; import javax.xml.crypto.dsig.dom.DOMSignContext; import javax.xml.crypto.dsig.keyinfo.KeyInfo; import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.commons.io.FileUtils; import org.junit.Test; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; public class HMACTest { public static final String NamespaceSpecNS = "http://www.w3.org/2000/xmlns/"; private static final Logger LOGGER = LoggerFactory .getLogger(HMACTest.class); @Test public void testCreateHMACSignature() throws Exception { // generate key KeyGenerator keyGenerator = KeyGenerator.getInstance("HmacSHA1"); SecretKey secretKey = keyGenerator.generateKey(); // generate DOM document DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setNamespaceAware(true); DocumentBuilder documentBuilder = documentBuilderFactory .newDocumentBuilder(); Document document = documentBuilder.newDocument(); Element rootElement = document.createElementNS("urn:test", "test:Root"); rootElement.setAttributeNS(NamespaceSpecNS, "xmlns:test", "urn:test"); document.appendChild(rootElement); XMLSignatureFactory xmlSignatureFactory = XMLSignatureFactory .getInstance("DOM"); // XML Signature references List references = new LinkedList<>(); List transforms = new LinkedList<>(); Transform envTransform = xmlSignatureFactory.newTransform( CanonicalizationMethod.ENVELOPED, (C14NMethodParameterSpec) null); transforms.add(envTransform); Transform exclTransform = xmlSignatureFactory.newTransform( CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null); transforms.add(exclTransform); Reference reference = xmlSignatureFactory.newReference("", xmlSignatureFactory.newDigestMethod(DigestMethod.SHA256, null), transforms, null, null); references.add(reference); // XML Signature SignedInfo SignedInfo signedInfo = xmlSignatureFactory.newSignedInfo( xmlSignatureFactory.newCanonicalizationMethod( CanonicalizationMethod.EXCLUSIVE, (C14NMethodParameterSpec) null), xmlSignatureFactory.newSignatureMethod( SignatureMethod.HMAC_SHA1, null), references); // XML Signature KeyInfo KeyInfoFactory keyInfoFactory = xmlSignatureFactory.getKeyInfoFactory(); KeyInfo keyInfo = keyInfoFactory.newKeyInfo(Collections .singletonList(keyInfoFactory.newKeyName("some-key-name"))); Element parentElement = document.getDocumentElement(); DOMSignContext domSignContext = new DOMSignContext( secretKey, parentElement); domSignContext.setDefaultNamespacePrefix("ds"); XMLSignature xmlSignature = xmlSignatureFactory.newXMLSignature( signedInfo, keyInfo); xmlSignature.sign(domSignContext); File tmpFile = File.createTempFile("xml-signature-hmac-", ".xml"); LOGGER.debug("XML signature file: {}", tmpFile.getAbsolutePath()); toFile(document, tmpFile); File tmpKeyFile = File.createTempFile("hmac-", ".key"); FileUtils.writeByteArrayToFile(tmpKeyFile, secretKey.getEncoded()); LOGGER.debug("key file: {}", tmpKeyFile.getAbsolutePath()); } private void toFile(Node node, File file) throws Exception { TransformerFactory transformerFactory = TransformerFactory .newInstance(); Transformer transformer = transformerFactory.newTransformer(); transformer.transform(new DOMSource(node), new StreamResult(file)); } } ================================================ FILE: test/validators/XmlCryptoJava/src/test/resources/log4j.xml ================================================ ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/Program.cs ================================================ // // This example signs an XML file using an // envelope signature. It then verifies the // signed XML. // using System; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.Text; using System.Xml; using System.IO; using System.Reflection; public class SignVerifyEnvelope { public static void Main(String[] args) { var exe = Assembly.GetExecutingAssembly().Location; var folder = Path.GetDirectoryName(exe); var pfx = Path.Combine(folder, "ClientPrivate.pfx"); var c = new X509Certificate2(File.ReadAllBytes(pfx), "wse2qs"); if (args[0] == "verify") { Console.WriteLine("verifying signature..."); var file = Path.Combine(folder, "SignedExample.xml"); bool b = VerifyXmlFile(file, (RSA)c.PublicKey.Key); Console.WriteLine("signature is " + (b ? "valid" : "not valid!")); if (!b) Environment.Exit(-1); } else if (args[0] == "sign") { Console.WriteLine("generating signature..."); var xmlFile = Path.Combine(folder, "Example.xml"); var sigFile = Path.Combine(folder, "signedExample.xml"); CreateSomeXml(xmlFile); SignXmlFile(xmlFile, sigFile, (RSA)c.PrivateKey); Console.WriteLine("done"); } } // Sign an XML file and save the signature in a new file. This method does not // save the public key within the XML file. This file cannot be verified unless // the verifying code has the key with which it was signed. public static void SignXmlFile(string FileName, string SignedFileName, RSA Key) { // Create a new XML document. XmlDocument doc = new XmlDocument(); // Load the passed XML file using its name. doc.Load(new XmlTextReader(FileName)); // Create a SignedXml object. SignedXml signedXml = new SignedXml(doc); // Add the key to the SignedXml document. signedXml.SigningKey = Key; // Create a reference to be signed. Reference reference = new Reference(); reference.Uri = ""; // Add an enveloped transformation to the reference. XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); reference.AddTransform(env); // Add the reference to the SignedXml object. signedXml.AddReference(reference); // Compute the signature. signedXml.ComputeSignature(); // Get the XML representation of the signature and save // it to an XmlElement object. XmlElement xmlDigitalSignature = signedXml.GetXml(); // Append the element to the XML document. doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true)); if (doc.FirstChild is XmlDeclaration) { doc.RemoveChild(doc.FirstChild); } // Save the signed XML document to a file specified // using the passed string. XmlTextWriter xmltw = new XmlTextWriter(SignedFileName, new UTF8Encoding(false)); doc.WriteTo(xmltw); xmltw.Close(); } // Verify the signature of an XML file against an asymetric // algorithm and return the result. public static Boolean VerifyXmlFile(String Name, RSA Key) { // Create a new XML document. XmlDocument xmlDocument = new XmlDocument(); // Load the passed XML file into the document. xmlDocument.Load(Name); // Create a new SignedXml object and pass it // the XML document class. SignedXml signedXml = new SignedXml(xmlDocument); // Find the "Signature" node and create a new // XmlNodeList object. XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature"); // Load the signature node. signedXml.LoadXml((XmlElement)nodeList[0]); // Check the signature and return the result. return signedXml.CheckSignature(Key); } // Create example data to sign. public static void CreateSomeXml(string FileName) { // Create a new XmlDocument object. XmlDocument document = new XmlDocument(); // Create a new XmlNode object. XmlNode node = document.CreateNode(XmlNodeType.Element, "", "MyElement", "samples"); // Add some text to the node. node.InnerText = "Example text to be signed."; // Append the node to the document. document.AppendChild(node); // Save the XML document to the file name specified. XmlTextWriter xmltw = new XmlTextWriter(FileName, new UTF8Encoding(false)); document.WriteTo(xmltw); xmltw.Close(); } } ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/Properties/AssemblyInfo.cs ================================================ using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. [assembly: AssemblyTitle("XmlCryptoUtilities")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("Microsoft")] [assembly: AssemblyProduct("XmlCryptoUtilities")] [assembly: AssemblyCopyright("Copyright © Microsoft 2012")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. [assembly: ComVisible(false)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("8305ddd7-a079-45b3-8db1-83728172475e")] // Version information for an assembly consists of the following four values: // // Major Version // Minor Version // Build Number // Revision // // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] [assembly: AssemblyVersion("1.0.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/ValidateSignature.csproj ================================================  Debug x86 8.0.30703 2.0 {CAA449E3-720D-4CD6-B03B-94991E11FF25} Exe Properties XmlCryptoUtilities XmlCryptoUtilities v4.0 Client 512 x86 true full false bin\Debug\ DEBUG;TRACE prompt 4 x86 pdbonly true bin\Release\ TRACE prompt 4 ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/bin/Debug/Example.xml ================================================ Example text to be signed. ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/bin/Debug/XmlCryptoUtilities.vshost.exe.manifest ================================================  ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/bin/Debug/signedExample.xml ================================================ Harry PotterJoanne KRowlingVXkWV6BNrUKqdmwcxhIgRa86HUI=Y71mpTZ0Ba5qED2TxHLp34tq8Pa9X41M5PuABwPq7r2GaL5Ib28ELkhMtnZuZboO50bxh/9fwK6DPApP92zUuOTZi4htlPcGJRP5kvHznbwzvq7aO5FWAcpUTX+yMrlERIutiJi7mjChX+mIKF5/4DK66JfIlY5vlq61TX2gaJw= ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/obj/x86/Debug/ValidateSignature.csproj.FileListAbsolute.txt ================================================ C:\Users\naveh\Documents\features\projects\xml-crypto\test\validators\XmlCryptoUtilities\XmlCryptoUtilities\bin\Debug\XmlCryptoUtilities.exe C:\Users\naveh\Documents\features\projects\xml-crypto\test\validators\XmlCryptoUtilities\XmlCryptoUtilities\bin\Debug\XmlCryptoUtilities.pdb C:\Users\naveh\Documents\features\projects\xml-crypto\test\validators\XmlCryptoUtilities\XmlCryptoUtilities\obj\x86\Debug\ResolveAssemblyReference.cache C:\Users\naveh\Documents\features\projects\xml-crypto\test\validators\XmlCryptoUtilities\XmlCryptoUtilities\obj\x86\Debug\XmlCryptoUtilities.exe C:\Users\naveh\Documents\features\projects\xml-crypto\test\validators\XmlCryptoUtilities\XmlCryptoUtilities\obj\x86\Debug\XmlCryptoUtilities.pdb ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/program-repro-misc-validation-and-canon.cs ================================================ // // This example signs an XML file using an // envelope signature. It then verifies the // signed XML. // using System; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.Xml; using System.Text; using System.Xml; using System.IO; using System.Reflection; public class SignVerifyEnvelope { public sealed class RSAPKCS1SHA256SignatureDescription : SignatureDescription { public RSAPKCS1SHA256SignatureDescription() { base.KeyAlgorithm = typeof(RSACryptoServiceProvider).FullName; base.DigestAlgorithm = typeof(SHA256Managed).FullName; base.FormatterAlgorithm = typeof(RSAPKCS1SignatureFormatter).FullName; base.DeformatterAlgorithm = typeof(RSAPKCS1SignatureDeformatter).FullName; } public override AsymmetricSignatureDeformatter CreateDeformatter(AsymmetricAlgorithm key) { if (key == null) { throw new ArgumentNullException("key"); } RSAPKCS1SignatureDeformatter deformatter = new RSAPKCS1SignatureDeformatter(key); deformatter.SetHashAlgorithm("SHA256"); return deformatter; } public override AsymmetricSignatureFormatter CreateFormatter(AsymmetricAlgorithm key) { if (key == null) { throw new ArgumentNullException("key"); } RSAPKCS1SignatureFormatter formatter = new RSAPKCS1SignatureFormatter(key); formatter.SetHashAlgorithm("SHA256"); return formatter; } } static bool ValidateXml(XmlDocument receipt, X509Certificate2 certificate) { // Create the signed XML object. SignedXml sxml = new SignedXml(receipt); // Get the XML Signature node and load it into the signed XML object. XmlNode dsig = receipt.GetElementsByTagName("Signature", SignedXml.XmlDsigNamespaceUrl)[0]; if (dsig == null) { // If signature is not found return false System.Console.WriteLine("Signature not found."); return false; } sxml.LoadXml((XmlElement)dsig); // Check the signature bool isValid = sxml.CheckSignature(certificate, true); FieldInfo field = sxml.GetType().GetField("m_signature", BindingFlags.NonPublic | BindingFlags.Instance); var sig = (Signature)field.GetValue(sxml); var _ref = (Reference)sig.SignedInfo.References[0]; //var pre = Type.GetType("System.Security.Cryptography.Xml.Utils").GetMethod("PreProcessDocumentInput"); //pre.Invoke(null, new[] { }); var enveloped = (XmlDsigEnvelopedSignatureTransform)_ref.TransformChain[0]; enveloped.LoadInput(receipt); var outputstream = enveloped.GetOutput(); var securityUrl = receipt.BaseURI; var resolver = new XmlSecureResolver(new XmlUrlResolver(), securityUrl); //TransformToOctetStream(Stream input, XmlResolver resolver, string baseUri) MethodInfo trans = _ref.TransformChain.GetType().GetMethods(BindingFlags.NonPublic | BindingFlags.Instance)[2]; var stream = trans.Invoke(_ref.TransformChain, new object[] {receipt, resolver, securityUrl}); var canontype = sig.GetType().Assembly.GetType("System.Security.Cryptography.Xml.CanonicalXml"); var foo = Activator.CreateInstance(canontype, BindingFlags.NonPublic | BindingFlags.Instance, null, new object[] {receipt, resolver}, null); MethodInfo method = _ref.GetType().GetMethod("CalculateHashValue", BindingFlags.NonPublic | BindingFlags.Instance); FieldInfo refs = sig.GetType().GetField("m_referencedItems", BindingFlags.NonPublic | BindingFlags.Instance); var refs1 = refs.GetValue(sig); var res = method.Invoke(_ref, new [] {receipt, refs1}); var str = Convert.ToBase64String((byte[])res); return isValid; } public static void Main(String[] args) { //calculate caninicalized xml var t = new XmlDsigEnvelopedSignatureTransform(false); XmlDocument doc = new XmlDocument(); //doc.PreserveWhitespace = true; doc.Load(@"c:\temp\x.xml"); t.LoadInput(doc); FieldInfo field = t.GetType().GetField("_signaturePosition", BindingFlags.NonPublic | BindingFlags.Instance); field.SetValue(t, 1); var res = (XmlDocument)t.GetOutput(); var s = res.OuterXml; var c14 = new XmlDsigC14NTransform(); c14.LoadInput(res); var mem = (MemoryStream)c14.GetOutput(); var sha = new SHA256Managed(); var byte1 = c14.GetDigestedOutput(new SHA256Managed()); var digest1 = Convert.ToBase64String(byte1); var byte2 = sha.ComputeHash(mem.ToArray()); var digest2 = Convert.ToBase64String(byte2); var s1 = System.Text.Encoding.UTF8.GetString(mem.ToArray()); var byte3 = sha.ComputeHash(System.Text.Encoding.UTF8.GetBytes(s1)); var digest3 = Convert.ToBase64String(byte3); //return; //validate signature CryptoConfig.AddAlgorithm(typeof(RSAPKCS1SHA256SignatureDescription), "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"); XmlDocument xmlDoc = new XmlDocument(); xmlDoc.Load(@"c:\temp\x.xml"); XmlNode node = xmlDoc.DocumentElement; X509Certificate2 cert = new X509Certificate2(File.ReadAllBytes(@"c:\temp\x.cer")); bool isValid = ValidateXml(xmlDoc, cert); //return; //calc hash var sha1 = new SHA256Managed(); var b1 = sha1.ComputeHash(System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(@"c:\temp\x_no_sig.xml"))); var b64 = Convert.ToBase64String(b1); } // Sign an XML file and save the signature in a new file. This method does not // save the public key within the XML file. This file cannot be verified unless // the verifying code has the key with which it was signed. public static void SignXmlFile(string FileName, string SignedFileName, RSA Key) { // Create a new XML document. XmlDocument doc = new XmlDocument(); // Load the passed XML file using its name. doc.Load(new XmlTextReader(FileName)); // Create a SignedXml object. SignedXml signedXml = new SignedXml(doc); // Add the key to the SignedXml document. signedXml.SigningKey = Key; // Create a reference to be signed. Reference reference = new Reference(); reference.Uri = ""; // Add an enveloped transformation to the reference. XmlDsigEnvelopedSignatureTransform env = new XmlDsigEnvelopedSignatureTransform(); reference.AddTransform(env); // Add the reference to the SignedXml object. signedXml.AddReference(reference); // Compute the signature. signedXml.ComputeSignature(); // Get the XML representation of the signature and save // it to an XmlElement object. XmlElement xmlDigitalSignature = signedXml.GetXml(); // Append the element to the XML document. doc.DocumentElement.AppendChild(doc.ImportNode(xmlDigitalSignature, true)); if (doc.FirstChild is XmlDeclaration) { doc.RemoveChild(doc.FirstChild); } // Save the signed XML document to a file specified // using the passed string. XmlTextWriter xmltw = new XmlTextWriter(SignedFileName, new UTF8Encoding(false)); doc.WriteTo(xmltw); xmltw.Close(); } // Verify the signature of an XML file against an asymetric // algorithm and return the result. public static Boolean VerifyXmlFile(String Name, RSA Key) { // Create a new XML document. XmlDocument xmlDocument = new XmlDocument(); // Load the passed XML file into the document. xmlDocument.Load(Name); // Create a new SignedXml object and pass it // the XML document class. SignedXml signedXml = new SignedXml(xmlDocument); // Find the "Signature" node and create a new // XmlNodeList object. XmlNodeList nodeList = xmlDocument.GetElementsByTagName("Signature"); // Load the signature node. signedXml.LoadXml((XmlElement)nodeList[0]); // Check the signature and return the result. return signedXml.CheckSignature(Key); } // Create example data to sign. public static void CreateSomeXml(string FileName) { // Create a new XmlDocument object. XmlDocument document = new XmlDocument(); // Create a new XmlNode object. XmlNode node = document.CreateNode(XmlNodeType.Element, "", "MyElement", "samples"); // Add some text to the node. node.InnerText = "Example text to be signed."; // Append the node to the document. document.AppendChild(node); // Save the XML document to the file name specified. XmlTextWriter xmltw = new XmlTextWriter(FileName, new UTF8Encoding(false)); document.WriteTo(xmltw); xmltw.Close(); } } ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities/utilities.cs ================================================ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Security.Cryptography.Xml; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Xml; using System.IO; namespace ConsoleApplication31 { /* class Program { static void Main(string[] args) { GetCanonization(); //GetSignature(); } static void GetSignature() { XmlDocument doc = new XmlDocument(); //doc.LoadXml(""); doc.LoadXml(""); SignedXml signedXml = new SignedXml(doc); var c = new X509Certificate2( File.ReadAllBytes(@"C:\Program Files\Microsoft WSE\v2.0\Samples\Sample Test Certificates\Client Private.pfx"), "wse2qs"); signedXml.SigningKey = c.PrivateKey; signedXml.Signature.SignedInfo.CanonicalizationMethod = "http://www.w3.org/2001/10/xml-exc-c14n#"; Reference ref0 = new Reference(); ref0.Uri = "#_0"; Reference ref1 = new Reference(); ref1.Uri = "#_1"; Reference ref2 = new Reference(); ref2.Uri = "#_2"; var t = new XmlDsigExcC14NTransform(); ref0.AddTransform(t); ref1.AddTransform(t); ref2.AddTransform(t); signedXml.AddReference(ref0); signedXml.AddReference(ref1); signedXml.AddReference(ref2); signedXml.ComputeSignature(); var xmlDigitalSignature = signedXml.GetXml(); var s = xmlDigitalSignature.OuterXml; } static void GetCanonization() { var c = new XmlDsigExcC14NTransform(true, ""); var doc = new XmlDocument(); doc.PreserveWhitespace = true; doc.LoadXml(""); var node = doc.SelectSingleNode("//*[local-name(.)='x']"); var nodes = node.SelectNodes(".|.//*|.//text()|.//@*"); c.LoadInput(nodes); var h = new SHA1CryptoServiceProvider(); var b = c.GetDigestedOutput(h); var b64 = Convert.ToBase64String(b); var res = c.GetOutput() as MemoryStream; string s2 = System.Text.Encoding.UTF8.GetString(res.ToArray()); } }*/ } ================================================ FILE: test/validators/XmlCryptoUtilities/XmlCryptoUtilities.sln ================================================  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual Studio 2010 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ValidateSignature", "XmlCryptoUtilities\ValidateSignature.csproj", "{CAA449E3-720D-4CD6-B03B-94991E11FF25}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x86 = Debug|x86 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {CAA449E3-720D-4CD6-B03B-94991E11FF25}.Debug|x86.ActiveCfg = Debug|x86 {CAA449E3-720D-4CD6-B03B-94991E11FF25}.Debug|x86.Build.0 = Debug|x86 {CAA449E3-720D-4CD6-B03B-94991E11FF25}.Release|x86.ActiveCfg = Release|x86 {CAA449E3-720D-4CD6-B03B-94991E11FF25}.Release|x86.Build.0 = Release|x86 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: test/wsfed-metadata-tests.spec.ts ================================================ import { SignedXml } from "../src/index"; import * as xpath from "xpath"; import * as xmldom from "@xmldom/xmldom"; import * as fs from "fs"; import { expect } from "chai"; import * as isDomNode from "@xmldom/is-dom-node"; describe("WS-Fed Metadata tests", function () { it("test validating WS-Fed Metadata", function () { const xml = fs.readFileSync("./test/static/wsfederation_metadata.xml", "utf-8"); const doc = new xmldom.DOMParser().parseFromString(xml); const signature = xpath.select1( "/*/*[local-name(.)='Signature' and namespace-uri(.)='http://www.w3.org/2000/09/xmldsig#']", doc, ); isDomNode.assertIsNodeLike(signature); const sig = new SignedXml(); sig.publicCert = fs.readFileSync("./test/static/wsfederation_metadata.pem"); sig.loadSignature(signature); const result = sig.checkSignature(xml); expect(result).to.be.true; expect(sig.getSignedReferences().length).to.equal(1); }); }); ================================================ FILE: tsconfig.eslint.json ================================================ { "extends": "./tsconfig.json", "exclude": [], "include": ["test", "src"] } ================================================ FILE: tsconfig.json ================================================ { "compilerOptions": { /* Visit https://aka.ms/tsconfig.json to read more about this file */ /* Basic Options */ // "incremental": true, /* Enable incremental compilation */ "target": "es2020" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', or 'ESNEXT'. */, "module": "commonjs" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */, "lib": ["es2020"] /* Specify library files to be included in the compilation. */, "allowJs": true /* Allow javascript files to be compiled. */, // "checkJs": true, /* Report errors in .js files. */ // "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', or 'react'. */ "declaration": true /* Generates corresponding '.d.ts' file. */, // "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */ "sourceMap": true /* Generates corresponding '.map' file. */, // "outFile": "./", /* Concatenate and emit output to single file. */ "outDir": "./lib" /* Redirect output structure to the directory. */, "rootDir": "./src" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */, // "composite": true, /* Enable project compilation */ // "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */ // "removeComments": true, /* Do not emit comments to output. */ // "noEmit": true, /* Do not emit outputs. */ // "importHelpers": true, /* Import emit helpers from 'tslib'. */ // "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */ // "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */ /* Strict Type-Checking Options */ "strict": true /* Enable all strict type-checking options. */, "noImplicitAny": false /* Raise error on expressions and declarations with an implied 'any' type. */, // "strictNullChecks": true, /* Enable strict null checks. */ // "strictFunctionTypes": true, /* Enable strict checking of function types. */ // "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */ // "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */ // "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */ // "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ /* Additional Checks */ // "noUnusedLocals": true, /* Report errors on unused locals. */ // "noUnusedParameters": true, /* Report errors on unused parameters. */ // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ // "baseUrl": "./", /* Base directory to resolve non-absolute module names. */ // "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */ // "typeRoots": [], /* List of folders to include type definitions from. */ // "types": [], /* Type declaration files to be included in compilation. */ // "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */ // "esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */ // "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */ // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */ /* Source Map Options */ // "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */ // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */ // "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */ "inlineSources": true /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */, /* Experimental Options */ // "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */ // "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */ /* Advanced Options */ "skipLibCheck": true /* Skip type checking of declaration files. */, "forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */ }, "exclude": ["node_modules", "docs", "lib", "test", "coverage", "example"], "include": ["src"] }