[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report\nabout: Create a report to help us improve\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!--\nPLEASE DO NOT USE GITHUB ISSUES TO ASK QUESTIONS! \n\nThe JJWT team uses GitHub Issues only to track actionable work that requires changes to the JJWT codebase.\n\nIf you have a usability or general question about JJWT, please instead post your question to StackOverflow using the following JJWT-specific link:\n\nhttps://stackoverflow.com/questions/ask?tags=jjwt&guided=false\n\nTo help ensure you receive a timely response, please follow the StackOverflow guidelines on how to ask a good question: https://stackoverflow.com/help/how-to-ask\n\nOk, now that that's out of the way - if you're still sure you need to report a bug, please fill in the sections below...\n-->\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Go to '...'\n2. Click on '....'\n3. Scroll down to '....'\n4. See error\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n<!--\nPLEASE DO NOT USE GITHUB ISSUES TO ASK QUESTIONS! \n\nThe JJWT team uses GitHub Issues only to track actionable work that requires changes to the JJWT codebase.\n\nIf you have a usability or general question about JJWT, please instead post your question to StackOverflow using the following JJWT-specific link:\n\nhttps://stackoverflow.com/questions/ask?tags=jjwt&guided=false\n\nTo help ensure you receive a timely response, please follow the StackOverflow guidelines on how to ask a good question: https://stackoverflow.com/help/how-to-ask\n\nOk, now that that's out of the way - if you're still sure you want to create a feature request or enhancement, please fill in the sections below...\n-->\n\n**Is your feature request related to a problem? Please describe.**\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like**\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered**\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context**\nAdd any other context or screenshots about the feature request here.\n"
  },
  {
    "path": ".github/stale.yml",
    "content": "#\n# Copyright (C) 2014 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n# Configuration for probot-stale - https://github.com/probot/stale\n\n# Number of days of inactivity before an Issue or Pull Request becomes stale\ndaysUntilStale: 60\n\n# Number of days of inactivity before an Issue or Pull Request with the stale label is closed.\n# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale.\ndaysUntilClose: 7\n\n# Only issues or pull requests with all of these labels are check if stale. Defaults to `[]` (disabled)\nonlyLabels: []\n\n# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable\nexemptLabels:\n  - pinned\n  - security\n  - help-wanted\n  - bug\n  - rfc-compliance\n  - \"[Status] Maybe Later\"\n\n# Set to true to ignore issues in a project (defaults to false)\nexemptProjects: false\n\n# Set to true to ignore issues in a milestone (defaults to false)\nexemptMilestones: true\n\n# Set to true to ignore issues with an assignee (defaults to false)\nexemptAssignees: false\n\n# Label to use when marking as stale\nstaleLabel: stale\n\n# Comment to post when marking as stale. Set to `false` to disable\nmarkComment: >\n  This issue has been automatically marked as stale due to inactivity for 60 or more days.\n  It will be closed in 7 days if no further activity occurs.\n\n# Comment to post when removing the stale label.\n# unmarkComment: >\n#   Your comment here.\n\n# Comment to post when closing a stale Issue or Pull Request.\ncloseComment: >\n  Closed due to inactivity.\n\n# Limit the number of actions per hour, from 1-30. Default is 30\nlimitPerRun: 30\n\n# Limit to only `issues` or `pulls`\nonly: issues\n\n# Optionally, specify configuration settings that are specific to just 'issues' or 'pulls':\n# pulls:\n#   daysUntilStale: 30\n#   markComment: >\n#     This pull request has been automatically marked as stale because it has not had\n#     recent activity. It will be closed if no further activity occurs. Thank you\n#     for your contributions.\n\n# issues:\n#   exemptLabels:\n#     - confirmed\n\n"
  },
  {
    "path": ".github/workflows/ci.yml",
    "content": "name: CI\n\non:\n  workflow_dispatch: \n  pull_request: # all pull requests\n  push:\n    branches:\n      - master\n\nenv:\n  MVN_CMD: ./mvnw --no-transfer-progress -B\n\njobs:\n  oracle:\n    strategy:\n      matrix:\n        java: [ '17' ]\n    runs-on: 'ubuntu-latest'\n    name: jdk-${{ matrix.java }}-oracle\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up JDK\n        uses: actions/setup-java@v4.7.0\n        with:\n          distribution: oracle\n          java-version: ${{ matrix.java }}\n      - name: Install softhsm2\n        run: sudo apt-get install -y softhsm2\n      - name: Install opensc\n        run: sudo apt-get install -y opensc\n      - name: Ensure SoftHSM user configuration\n        run: impl/src/test/scripts/softhsm configure\n      - name: Populate SoftHSM with JJWT test keys\n        run: impl/src/test/scripts/softhsm import\n      - name: Build\n        # run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg\n        # to sign artifacts, since we don't want to mess with storing signing credentials in CI:\n        run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true\n\n  temurin:\n    strategy:\n      matrix:\n        java: [ '8', '11', '17', '18' ]\n    runs-on: 'ubuntu-latest'\n    name: jdk-${{ matrix.java }}-temurin\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up JDK\n        uses: actions/setup-java@v4\n        with:\n          java-version: ${{ matrix.java }}\n          distribution: 'temurin'\n          cache: 'maven'\n          check-latest: true\n      - name: Install softhsm2\n        run: sudo apt-get install -y softhsm2\n      - name: Install opensc\n        run: sudo apt-get install -y opensc\n      - name: Ensure SoftHSM user configuration\n        run: impl/src/test/scripts/softhsm configure\n      - name: Populate SoftHSM with JJWT test keys\n        run: impl/src/test/scripts/softhsm import\n      - name: Build\n        # run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg\n        # to sign artifacts, since we don't want to mess with storing signing credentials in CI:\n        run: ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true\n\n  zulu:\n    strategy:\n      matrix:\n        java: [ '7', '8', '9', '11', '12', '13', '14', '15', '16', '17', '18', '21' ]\n    runs-on: 'ubuntu-latest'\n    env:\n      JDK_MAJOR_VERSION: ${{ matrix.java }}\n    name: jdk-${{ matrix.java }}-zulu\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up JDK\n        uses: actions/setup-java@v4\n        with:\n          java-version: ${{ matrix.java }}\n          distribution: 'zulu'\n          cache: 'maven'\n          check-latest: true\n      - name: Install softhsm2\n        run: sudo apt-get install -y softhsm2\n      - name: Install opensc\n        run: sudo apt-get install -y opensc\n      - name: Ensure SoftHSM user configuration\n        run: impl/src/test/scripts/softhsm configure\n      - name: Populate SoftHSM with JJWT test keys\n        run: impl/src/test/scripts/softhsm import\n      - name: Build\n        # run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg\n        # to sign artifacts, since we don't want to mess with storing signing credentials in CI:\n        run: |\n          if [ \"$JDK_MAJOR_VERSION\" == \"7\" ]; then export MAVEN_OPTS=\"-Xmx512m -XX:MaxPermSize=128m\"; fi\n          ${{env.MVN_CMD}} verify -Possrh -Dgpg.skip=true\n\n  # ensure all of our files have the correct/updated license header\n  license-check:\n    runs-on: 'ubuntu-latest'\n    steps:\n      - uses: actions/checkout@v4\n        with:\n          fetch-depth: 0 # avoid license plugin history warnings (plus it needs full history)\n      - name: Set up JDK\n        uses: actions/setup-java@v4\n        with:\n          distribution: 'zulu'\n          java-version: '8'\n          cache: 'maven'\n          check-latest: true\n      - name: License Check\n        # This adds about 1 minute to any build, which is why we don't want to do it on every other build:\n        run: |\n          ${{env.MVN_CMD}} license:check\n\n  code-coverage:\n    # (commented out for now - see the comments in 'Wait to start' below for why.  Keeping this here as a placeholder\n    # as it may be better to use instead of an artificial delay once we no longer need to build on JDK 7):\n    #needs: zulu # wait until others finish so a coverage failure doesn't cancel others accidentally\n    runs-on: 'ubuntu-latest'\n    steps:\n      - uses: actions/checkout@v4\n      - name: Set up JDK\n        uses: actions/setup-java@v4\n        with:\n          distribution: 'zulu'\n          java-version: '8'\n          cache: 'maven'\n          check-latest: true\n      - name: Install softhsm2\n        run: sudo apt-get install -y softhsm2\n      - name: Install opensc\n        run: sudo apt-get install -y opensc\n      - name: Ensure SoftHSM user configuration\n        run: impl/src/test/scripts/softhsm configure\n      - name: Populate SoftHSM with JJWT test keys\n        run: impl/src/test/scripts/softhsm import\n      - name: Wait to start\n        # wait a little to start: code coverage usually only takes about 1 1/2 minutes.  If coverage fails, it will\n        # cancel the other running builds, and we don't want that (because we want to see if jobs fail due to\n        # build issues, not due to the code-coverage job causing the others to cancel).  We could have used the\n        # 'need' property (commented out above), and that would wait until all the other jobs have finished before\n        # starting this one, but that introduces unnecessary (sometimes 2 1/2 minute) delay, whereas delaying the\n        # start of the code coverage checks a bit should allow everything to finish around the same time without having\n        # much of an adverse effect on the other jobs above.\n        run: sleep 90s\n        shell: bash\n      - name: Code Coverage\n        # run a full build, just as we would for a release (i.e. the `ossrh` profile), but don't use gpg\n        # to sign artifacts, since we don't want to mess with storing signing credentials in CI:\n        run: |\n          ${{env.MVN_CMD}} clover:setup test && \\\n            ${{env.MVN_CMD}} -pl . clover:clover clover:check coveralls:report \\\n              -DrepoToken=\"${{ secrets.GITHUB_TOKEN }}\" \\\n              -DserviceName=github \\\n              -DserviceBuildNumber=\"${{ env.GITHUB_RUN_ID }}\"\n"
  },
  {
    "path": ".gitignore",
    "content": ".DS_Store\n\n# Mobile Tools for Java (J2ME)\n.mtj.tmp/\n\n# Package Files #\n*.jar\n*.war\n*.ear\n\n# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml\nhs_err_pid*\n\ntarget/\n.idea\n*.iml\n*.iws\n\n.classpath\n.project\n.settings\n"
  },
  {
    "path": ".mvn/wrapper/maven-wrapper.properties",
    "content": "# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#   http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\ndistributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.8/apache-maven-3.8.8-bin.zip\nwrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.6.jar\n\n#distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.1/apache-maven-3.6.1-bin.zip\n#wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.5/maven-wrapper-0.5.5.jar\n"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## Release Notes\n\n### 0.13.0\n\nThis is the last minor JJWT release branch that will support Java 7. Any necessary emergency bug fixes will be fixed in subsequent `0.13.x` patch releases, but all new development, including Java 8 compatible changes, will be in the next minor (`0.14.0`) release.\n\n**All future JJWT major and minor versions (  `0.14.0` and later) will require Java 8 or later.**\n\nThis `0.13.0` minor release has only one change:\n\n  - The previously private `JacksonDeserializer(ObjectMapper objectMapper, Map<String, Class<?>> claimTypeMap)` constructor is now `public` for those that want register a claims \n   type converter on their own specified `ObjectMapper` instance.  See [Issue 914](https://github.com/jwtk/jjwt/issues/914).\n\n### 0.12.7\n\nThis patch release:\n\n* Adds a new Maven BOM, useful for multi-module projects. See [Issue 967](https://github.com/jwtk/jjwt/issues/967).\n* Allows the `JwtParserBuilder` to have empty nested algorithm collections, effectively disabling the parser's associated feature:\n    - Emptying the `zip()` nested collection disables JWT decompression.\n    - Emptying the `sig()` nested collection disables JWS mac/signature verification (i.e. all JWSs will be unsupported/rejected).\n    - Emptying either the `enc()` or `key()` nested collections disables JWE decryption (i.e. all JWEs will be unsupported/rejected)\n  \n  See [Issue 996](https://github.com/jwtk/jjwt/issues/996).\n* Fixes [bug 961](https://github.com/jwtk/jjwt/issues/961) where `JwtParserBuilder` nested collection builders were not correctly replacing algorithms with the same id.\n* Ensures a `JwkSet`'s `keys` collection is no longer entirely secret/redacted by default.  This was an overzealous default that was unnecessarily restrictive; the `keys` collection itself should always be public, and each individual key within should determine which fields should be redacted when printed. See [Issue 976](https://github.com/jwtk/jjwt/issues/976).\n* Improves performance slightly by ensuring all `jjwt-api` utility methods that create `*Builder` instances (`Jwts.builder()`, `Jwts.parserBuilder()`, `Jwks.builder()`, etc) no longer use reflection.\n \n  Instead,`static` factories are created via reflection only once during initial `jjwt-api` classloading, and then `*Builder`s are created via standard instantiation using the `new` operator thereafter.  This also benefits certain environments that may not have ideal `ClassLoader` implementations (e.g. Tomcat in some cases).\n \n  **NOTE: because this changes which classes are loaded via reflection, any environments that must explicitly reference reflective class names (e.g. GraalVM applications) will need to be updated to reflect the new factory class names**.\n  \n  See [Issue 988](https://github.com/jwtk/jjwt/issues/988).\n* Upgrades the Gson dependency to `2.11.0` \n* Upgrades the BouncyCastle dependency to `1.78.1`\n\n### 0.12.6\n\nThis patch release:\n\n* Ensures that after successful JWS signature verification, an application-configured Base64Url `Decoder` output is\n  used to construct a `Jws` instance (instead of JJWT's default decoder). See\n  [Issue 947](https://github.com/jwtk/jjwt/issues/947).\n* Fixes a decompression memory leak in concurrent/multi-threaded environments introduced in 0.12.0 when decompressing JWTs with a `zip` header of `GZIP`. See [Issue 949](https://github.com/jwtk/jjwt/issues/949).\n* Upgrades BouncyCastle to 1.78 via [PR 941](https://github.com/jwtk/jjwt/pull/941).\n* Ensures that a `JwkSet`'s `keys` list member is no longer considered secret and is not redacted by default. However, each individual JWK element within the `keys` list may still have [redacted private or secret members](https://github.com/jwtk/jjwt?tab=readme-ov-file#jwk-tostring-safety) as expected. See [Issue 976](https://github.com/jwtk/jjwt/issues/976).\n\n### 0.12.5\n\nThis patch release:\n\n* Ensures that builders' `NestedCollection` changes are applied to the collection immediately as mutation methods are called, no longer\n  requiring application developers to call `.and()` to 'commit' or apply a change.  For example, prior to this release,\n  the following code did not apply changes:\n  ```java\n  JwtBuilder builder = Jwts.builder();\n  builder.audience().add(\"an-audience\"); // no .and() call\n  builder.compact(); // would not keep 'an-audience'\n  ```\n  Now this code works as expected and all other `NestedCollection` instances like it apply changes immediately (e.g. when calling\n  `.add(value)`).\n  \n  However, standard fluent builder chains are still recommended for readability when feasible, e.g.\n  \n  ```java\n  Jwts.builder()\n      .audience().add(\"an-audience\").and() // allows fluent chaining\n      .subject(\"Joe\")\n      // etc...\n      .compact()\n  ```\n  See [Issue 916](https://github.com/jwtk/jjwt/issues/916).\n\n### 0.12.4\n\nThis patch release includes various changes listed below.\n\n#### Jackson Default Parsing Behavior\n\nThis release makes two behavioral changes to JJWT's default Jackson `ObjectMapper` parsing settings:\n\n1. In the interest of having stronger standards to reject potentially malformed/malicious/accidental JSON that could\n   have undesirable effects on an application, JJWT's default `ObjectMapper `is now configured to explicitly reject/fail \n   parsing JSON (JWT headers and/or Claims) if/when that JSON contains duplicate JSON member names. \n   \n   For example, now the following JSON, if parsed, would fail (be rejected) by default:\n   ```json\n   {\n     \"hello\": \"world\",\n     \"thisWillFail\": 42,\n     \"thisWillFail\": \"test\"\n   }\n    ```\n   \n   Technically, the JWT RFCs _do allow_ duplicate named fields as long as the last parsed member is the one used\n   (see [JWS RFC 7515, Section 4](https://datatracker.ietf.org/doc/html/rfc7515#section-4)), so this is allowed.\n   However, because JWTs often reflect security concepts, it's usually better to be defensive and reject these \n   unexpected scenarios by default. The RFC later supports this position/preference in \n   [Section 10.12](https://datatracker.ietf.org/doc/html/rfc7515#section-10.12):\n       \n       Ambiguous and potentially exploitable situations\n       could arise if the JSON parser used does not enforce the uniqueness\n       of member names or returns an unpredictable value for duplicate\n       member names.\n       \n   Finally, this is just a default, and the RFC does indeed allow duplicate member names if the last value is used,\n   so applications that require duplicates to be allowed can simply configure their own `ObjectMapper` and use\n   that with JJWT instead of assuming this (new) JJWT default. See \n   [Issue #877](https://github.com/jwtk/jjwt/issues/877) for more.\n2. If using JJWT's support to use Jackson to parse \n   [Custom Claim Types](https://github.com/jwtk/jjwt#json-jackson-custom-types) (for example, a Claim that should be\n   unmarshalled into a POJO), and the JSON for that POJO contained a member that is not represented in the specified\n   class, Jackson would fail parsing by default.  Because POJOs and JSON data models can sometimes be out of sync\n   due to different class versions, the default behavior has been changed to ignore these unknown JSON members instead \n   of failing (i.e. the `ObjectMapper`'s  `DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES` is now set to `false`)\n   by default.\n   \n   Again, if you prefer the stricter behavior of rejecting JSON with extra or unknown properties, you can configure\n   `true` on your own `ObjectMapper` instance and use that instance with the `Jwts.parser()` builder.\n\n#### Additional Changes\n\nThis release also:\n\n* Fixes a thread-safety issue when using `java.util.ServiceLoader` to dynamically lookup/instantiate pluggable \n  implementations of JJWT interfaces (e.g. JSON parsers, etc).  See \n  [Issue #873](https://github.com/jwtk/jjwt/issues/873) and its documented fix in \n  [PR #893](https://github.com/jwtk/jjwt/pull/892).\n* Ensures Android environments and older `org.json` library usages can parse JSON from a `JwtBuilder`-provided\n  `java.io.Reader` instance. [Issue 882](https://github.com/jwtk/jjwt/issues/882).\n* Ensures a single string `aud` (Audience) claim is retained (without converting it to a `Set`) when copying/applying a \n  source Claims instance to a destination Claims builder. [Issue 890](https://github.com/jwtk/jjwt/issues/890).\n* Ensures P-256, P-384 and P-521 Elliptic Curve JWKs zero-pad their field element (`x`, `y`, and `d`) byte array values\n  if necessary before Base64Url-encoding per [RFC 7518](https://datatracker.ietf.org/doc/html/rfc7518), Sections \n  [6.2.1.2](https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.2), \n  [6.2.1.3](https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.3), and\n  [6.2.2.1](https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.2.1), respectively. \n  [Issue 901](https://github.com/jwtk/jjwt/issues/901).\n* Ensures that Secret JWKs for HMAC-SHA algorithms with `k` sizes larger than the algorithm minimum can\n  be parsed/used as expected. See [Issue #905](https://github.com/jwtk/jjwt/issues/905) \n* Ensures there is an upper bound (maximum) iterations enforced for PBES2 decryption to help mitigate potential DoS \n  attacks. Many thanks to Jingcheng Yang and Jianjun Chen from Sichuan University and Zhongguancun Lab for their \n  work on this. See [PR 911](https://github.com/jwtk/jjwt/pull/911).\n* Fixes various typos in documentation and JavaDoc. Thanks to those contributing pull requests for these!\n\n### 0.12.3\n\nThis patch release:\n\n* Upgrades the `org.json` dependency to `20231013` to address that library's\n  [CVE-2023-5072](https://nvd.nist.gov/vuln/detail/CVE-2023-5072) vulnerability.\n* (Re-)enables empty values for custom claims, which was the behavior in <= 0.11.5. \n  [Issue 858](https://github.com/jwtk/jjwt/issues/858).\n\n### 0.12.2\n\nThis is a follow-up release to finalize the work in 0.12.1 that tried to fix a reflection scope problem\non >= JDK 17.  The 0.12.1 fix worked, but only if the importing project or application did _not_ have its own\n`module-info.java` file.\n\nThis release removes that reflection code entirely in favor of a JJWT-native implementation, eliminating JPMS \nmodule (scope) problems on >= JDK 17. As such, `--add-opens` flags are no longer required to use JJWT.\n\nThe fix has been tested up through JDK 21 in a separate application environment (out of JJWT's codebase) to assert\nexpected functionality in a 'clean room' environment in a project both with and without `module-info.java` usage.\n\n### 0.12.1\n\nEnabled reflective access on JDK 17+ to `java.io.ByteArrayInputStream` and `sun.security.util.KeyUtil` for\n`jjwt-impl.jar`\n\n### 0.12.0\n\nThis is a big release! JJWT now fully supports Encrypted JSON Web Tokens (JWE), JSON Web Keys (JWK) and more!  See the \nsections below enumerating all new features as well as important notes on breaking changes or backwards-incompatible \nchanges made in preparation for the upcoming 1.0 release.\n\n**Because breaking changes are being introduced, it is strongly recommended to wait until the upcoming 1.0 release\nwhere you can address breaking changes one time only**.\n\nThose that need immediate JWE encryption and JWK key support\nhowever will likely want to upgrade now and deal with the smaller subset of breaking changes in the 1.0 release.\n\n#### Simplified Starter Jar\n\nThose upgrading to new modular JJWT versions from old single-jar versions will transparently obtain everything \nthey need in their Maven, Gradle or Android projects.\n\nJJWT's early releases had one and only one .jar: `jjwt.jar`.  Later releases moved to a modular design with 'api' and\n'impl' jars including 'plugin' jars for Jackson, GSON, org.json, etc.  Some users upgrading from the earlier single \njar to JJWT's later versions have been frustrated by being forced to learn how to configure the more modular .jars.\n\nThis release re-introduces the `jjwt.jar` artifact again, but this time it is simply an empty .jar with Maven \nmetadata that will automatically transitively download the following into a project, retaining the old single-jar \nbehavior:\n* `jjwt-api.jar`\n* `jjwt-impl.jar`\n* `jjwt-jackson.jar`\n\nNaturally, developers are still encouraged to configure the modular .jars as described in JJWT's documentation for \ngreater control and to enable their preferred JSON parser, but this stop-gap should help those unaware when upgrading.\n\n#### JSON Web Encryption (JWE) Support!\n\nThis has been a long-awaited feature for JJWT, years in the making, and it is quite extensive - so many encryption \nalgorithms and key management algorithms are defined by the JWA specification, and new API concepts had to be \nintroduced for all of them, as well as extensive testing with RFC-defined test vectors.  The wait is over!  \nAll JWA-defined encryption algorithms and key management algorithms are fully implemented and supported and \navailable immediately.  For example:\n\n```java\nAeadAlgorithm enc = Jwts.ENC.A256GCM;\nSecretKey key = enc.key().build();\nString compact = Jwts.builder().setSubject(\"Joe\").encryptWith(key, enc).compact();\n\nJwe<Claims> jwe = Jwts.parser().decryptWith(key).build().parseEncryptedClaims(compact);\n```\n\nMany other RSA and Elliptic Curve examples are in the full README documentation. \n\n#### JSON Web Key (JWK) Support!\n\nRepresenting cryptographic keys - SecretKeys, RSA Public and Private Keys, Elliptic Curve Public and \nPrivate keys - as fully encoded JSON objects according to the JWK specification - is now fully implemented and\nsupported.  The new `Jwks` utility class exists to create JWK builders and parsers as desired.  For example:\n\n```java\nSecretKey key = Jwts.SIG.HS256.key().build();\nSecretJwk jwk = Jwks.builder().forKey(key).build();\nassert key.equals(jwk.toKey());\n\n// or if receiving a JWK string:\nJwk<?> parsedJwk = Jwks.parser().build().parse(jwkString);\nassert jwk.equals(parsedJwk);\nassert key.equals(parsedJwk.toKey());\n```\n\nMany JJWT users won't need to use JWKs explicitly, but some JWA Key Management Algorithms (and lots of RFC test \nvectors) utilize JWKs when transmitting JWEs.  As this was required by JWE, it is now implemented in full for \nJWE use as well as general-purpose JWK support.\n\n#### JWK Thumbprint and JWK Thumbprint URI support\n\nThe [JWK Thumbprint](https://www.rfc-editor.org/rfc/rfc7638.html) and \n[JWK Thumbprint URI](https://www.rfc-editor.org/rfc/rfc9278.html) RFC specifications are now fully supported.  Please\nsee the README.md file's corresponding named sections for both for full documentation and usage examples.\n\n#### JWS Unencoded Payload Option (`b64`) support\n\nThe [JSON Web Signature (JWS) Unencoded Payload Option](https://www.rfc-editor.org/rfc/rfc7797.html) RFC specification\nis now fully supported.  Please see the README.md corresponding named section for documentation and usage examples.\n\n#### Better PKCS11 and Hardware Security Module (HSM) support\n\nPrevious versions of JJWT enforced that Private Keys implemented the `RSAKey` and `ECKey` interfaces to enforce key \nlength requirements.  With this release, JJWT will still perform those checks when those data types are available, \nbut if not, as is common with keys from PKCS11 and HSM KeyStores, JJWT will still allow those Keys to be used, \nexpecting the underlying Security Provider to enforce any key requirements. This should reduce or eliminate any \ncustom code previously written to extend JJWT to use keys from those KeyStores or Providers.\n\nAdditionally, PKCS11/HSM tests using [SoftHSMv2](https://www.opendnssec.org/softhsm/) are run on every build with\nevery JWS MAC and Signature algorithm and every JWE Key algorithm to ensure continued stable support with\nAndroid and Sun PKCS11 implementations and spec-compliant Hardware Security Modules that use the PKCS11 interface\n(such as YubiKey, etc.)\n\n#### Custom Signature Algorithms\n\nThe `io.jsonwebtoken.SignatureAlgorithm` enum has been deprecated in favor of new \n`io.jsonwebtoken.security.SecureDigestAlgorithm`, `io.jsonwebtoken.security.MacAlgorithm`, and \n`io.jsonwebtoken.security.SignatureAlgorithm` interfaces to allow custom algorithm implementations.  The new nested\n`Jwts.SIG` static inner class is a registry of all standard JWS algorithms as expected, exactly like the \nold enum.  This change was made because enums are a static concept by design and cannot \nsupport custom values: those who wanted to use custom signature algorithms could not do so until now.  The new \ninterfaces now allow anyone to plug in and support custom algorithms with JJWT as desired.\n\n#### KeyBuilder and KeyPairBuilder\n\nBecause the `io.jsonwebtoken.security.Keys#secretKeyFor` and `io.jsonwebtoken.security.Keys#keyPairFor` methods \naccepted the now-deprecated `io.jsonwebtoken.SignatureAlgorithm` enum, they have also been deprecated in favor of \ncalling new `key()` or `keyPair()` builder methods on `MacAlgorithm` and `SignatureAlgorithm` instances directly.  \nFor example:\n\n```java\nSecretKey key = Jwts.SIG.HS256.key().build();\nKeyPair pair = Jwts.SIG.RS256.keyPair().build();\n```\n\nThe builders allow for customization of the JCA `Provider` and `SecureRandom` during Key or KeyPair generation if desired, whereas\nthe old enum-based static utility methods did not.\n\n#### Preparation for 1.0\n\nNow that the JWE and JWK specifications are implemented, only a few things remain for JJWT to be considered at \nversion 1.0.  We have been waiting to apply the 1.0 release version number until the entire set of JWT specifications \nare fully supported **and** we drop JDK 7 support (to allow users to use JDK 8 APIs).  To that end, we have had to \ndeprecate some concepts, or in some cases, completely break backwards compatibility to ensure the transition to \n1.0 (and JDK 8 APIs) are possible.  Most backwards-incompatible changes are listed in the next section below.\n\n#### Backwards Compatibility Breaking Changes, Warnings and Deprecations\n\n* `io.jsonwebtoken.Jwt`'s `getBody()` method has been deprecated in favor of a new `getPayload()` method to\n  reflect correct JWT specification nomenclature/taxonomy.\n\n\n* `io.jsonwebtoken.Jws`'s `getSignature()` method has been deprecated in favor of a new `getDigest()` method to\n  support expected congruent behavior with `Jwe` instances (both have digests).\n\n\n* `io.jsonwebtoken.JwtParser`'s `parseContentJwt`, `parseClaimsJwt`, `parseContentJws`, and `parseClaimsJws` methods\n  have been deprecated in favor of more intuitive respective `parseUnsecuredContent`, `parseUnsecuredClaims`,\n  `parseSignedContent` and `parseSignedClaims` methods.\n\n\n* `io.jsonwebtoken.CompressionCodec` is now deprecated in favor of the new `io.jsonwebtoken.io.CompressionAlgorithm`\n  interface. This is to guarantee API congruence with all other JWT-identifiable algorithm IDs that can be set as a \n  header value.\n\n\n* `io.jsonwebtoken.CompressionCodecResolver` has been deprecated in favor of the new\n  `JwtParserBuilder#addCompressionAlgorithms` method.\n\n\n#### Breaking Changes\n\n* **`io.jsonwebtoken.Claims` and `io.jsonwebtoken.Header` instances are now immutable** to enhance security and thread\n  safety.  Creation and mutation are supported with newly introduced `ClaimsBuilder` and `HeaderBuilder` concepts.\n  Even though mutation methods have migrated, there are a couple that have been removed entirely:\n  * `io.jsonwebtoken.JwsHeader#setAlgorithm` has been removed - the `JwtBuilder` will always set the appropriate\n    `alg` header automatically based on builder state.\n  * `io.jsonwebtoken.Header#setCompressionAlgorithm` has been removed - the `JwtBuilder` will always set the appropriate\n  `zip` header automatically based on builder state.\n\n\n* `io.jsonwebtoken.Jwts`'s `header(Map)`, `jwsHeader()` and `jwsHeader(Map)` methods have been removed in favor\n  of the new `header()` method that returns a `HeaderBuilder` to support method chaining and dynamic `Header` type \n  creation. The `HeaderBuilder` will dynamically create a `Header`, `JwsHeader` or `JweHeader` automatically based on \n  builder state.\n\n\n* Similarly, `io.jsonwebtoken.Jwts`'s `claims()` static method has been changed to return a `ClaimsBuilder` instead\n  of a `Claims` instance.\n\n\n* **JWTs that do not contain JSON Claims now have a payload type of `byte[]` instead of `String`** (that is, \n  `Jwt<byte[]>` instead of `Jwt<String>`).  This is because JWTs, especially when used with the \n  `cty` (Content Type) header, are capable of handling _any_ type of payload, not just Strings. The previous JJWT \n  releases didn't account for this, and now the API accurately reflects the JWT RFC specification payload \n  capabilities. Additionally, the name of `plaintext` has been changed to `content` in method names and JavaDoc to \n  reflect this taxonomy. This change has impacted the following JJWT APIs:\n\n  * The `JwtBuilder`'s `setPayload(String)` method has been deprecated in favor of two new methods:\n  \n    * `setContent(byte[])`, and \n    * `setContent(byte[], String contentType)`\n    \n    These new methods allow any kind of content\n    within a JWT, not just Strings. The existing `setPayload(String)` method implementation has been changed to \n    delegate to this new `setContent(byte[])` method with the argument's UTF-8 bytes, for example \n    `setContent(payloadString.getBytes(StandardCharsets.UTF_8))`.\n\n  * The `JwtParser`'s `Jwt<Header, String> parsePlaintextJwt(String plaintextJwt)` and\n    `Jws<String> parsePlaintextJws(String plaintextJws)` methods have been changed to\n    `Jwt<Header, byte[]> parseContentJwt(String plaintextJwt)` and\n    `Jws<byte[]> parseContentJws(String plaintextJws)` respectively.\n\n  * `JwtHandler`'s `onPlaintextJwt(String)` and `onPlaintextJws(String)` methods have been changed to\n    `onContentJwt(byte[])` and `onContentJws(byte[])` respectively.\n\n  * `io.jsonwebtoken.JwtHandlerAdapter` has been changed to reflect the above-mentioned name and `String`-to-`byte[]` \n    argument changes, as well adding the `abstract` modifier.  This class was never intended\n    to be instantiated directly, and is provided for subclassing only.  The missing modifier has been added to ensure\n    the class is used as it had always been intended.\n\n  * `io.jsonwebtoken.SigningKeyResolver`'s `resolveSigningKey(JwsHeader, String)` method has been changed to\n    `resolveSigningKey(JwsHeader, byte[])`.\n\n\n* `io.jsonwebtoken.JwtParser` is now immutable.  All mutation/modification methods (setters, etc) deprecated 4 years \n  ago have been removed.  All parser configuration requires using the `JwtParserBuilder`.\n\n\n* Similarly, `io.jsonwebtoken.Jwts`'s `parser()` method deprecated 4 years ago has been changed to now return a \n  `JwtParserBuilder` instead of a direct `JwtParser` instance.  The previous `Jwts.parserBuilder()` method has been \n  removed as it is now redundant.\n\n\n* The `JwtParserBuilder` no longer supports `PrivateKey`s for signature verification.  This was an old\n  legacy behavior scheduled for removal years ago, and that change is now complete.  For various cryptographic/security\n  reasons, asymmetric public/private key signatures should always be created with `PrivateKey`s and verified with\n  `PublicKey`s.\n\n\n* `io.jsonwebtoken.CompressionCodec` implementations are no longer discoverable via `java.util.ServiceLoader` due to\n  runtime performance problems with the JDK's `ServiceLoader` implementation per\n  https://github.com/jwtk/jjwt/issues/648.  Custom implementations should be made available to the `JwtParser` via\n  the new `JwtParserBuilder#addCompressionAlgorithms` method.\n\n\n* Prior to this release, if there was a serialization problem when serializing the JWT Header, an `IllegalStateException`\n  was thrown. If there was a problem when serializing the JWT claims, an `IllegalArgumentException` was\n  thrown.  This has been changed up to ensure consistency: any serialization error with either headers or claims\n  will now throw a `io.jsonwebtoken.io.SerializationException`.\n\n\n* Parsing of unsecured JWTs (`alg` header of `none`) are now disabled by default as mandated by \n  [RFC 7518, Section 3.6](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6). If you require parsing of\n  unsecured JWTs, you must call the `JwtParserBuilder#enableUnsecured()` method, but note the security\n  implications mentioned in that method's JavaDoc before doing so.\n\n\n* `io.jsonwebtoken.gson.io.GsonSerializer` now requires `Gson` instances that have a registered\n  `GsonSupplierSerializer` type adapter, for example:\n  ```java\n  new GsonBuilder()\n    .registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)    \n    .disableHtmlEscaping().create();\n  ```\n  This is to ensure JWKs have `toString()` and application log safety (do not print secure material), but still \n  serialize to JSON correctly.\n\n\n* `io.jsonwebtoken.InvalidClaimException` and its two subclasses (`IncorrectClaimException` and `MissingClaimException`)\n  were previously mutable, allowing the corresponding claim name and claim value to be set on the exception after\n  creation.  These should have always been immutable without those setters (just getters), and this was a previous\n  implementation oversight.  This release has ensured they are immutable without the setters.\n\n### 0.11.5\n\nThis patch release adds additional security guards against an ECDSA bug in Java SE versions 15-15.0.6, 17-17.0.2, and 18\n([CVE-2022-21449](https://nvd.nist.gov/vuln/detail/CVE-2022-21449)) in addition to the guards added in the JJWT 0.11.3 \nrelease. This patch allows JJWT users using those JVM versions to upgrade to JJWT 0.11.5, even if they are unable to \nupgrade their JVM to patched/fixed JVM version in a timely manner.  Note: if your application does not use these JVM \nversions, you are not exposed to the JVM vulnerability.\n\nNote that the CVE is not a bug within JJWT itself - it is a bug within the above listed JVM versions, and the\nJJWT 0.11.5 release adds additional precautions within JJWT in case an application team is not able to upgrade\ntheir JVM in a timely manner.\n\nHowever, even with these additional JJWT security guards, the root cause of the issue is the JVM, so it **strongly\nrecommended** to upgrade your JVM to version\n15.0.7, 17.0.3, or 18.0.1 or later to ensure the bug does not surface elsewhere in your application code or any other\nthird party library in your application that may not contain similar security guards. \n\nIssues included in this patch are listed in the [JJWT 0.11.5 milestone](https://github.com/jwtk/jjwt/milestone/26?closed=1).\n\n#### Credits\n\nThank you to [Neil Madden](https://neilmadden.blog), the security researcher that first discovered the JVM\nvulnerability as covered in his [Psychic Signatures in Java](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/) \nblog post.  Neil worked directly with the JJWT team to provide these additional guards, beyond what was in the JJWT 0.11.3\nrelease, and we're grateful for his help and collaboration in reviewing our fixes and for the additional tests he\nprovided the JJWT team.\n\n### 0.11.4\n\nThis patch release:\n\n* Adds additional handling for rare JSON parsing exceptions and wraps them in a `JwtException` to allow the application to handle these conditions as JWT concerns.\n* Upgrades the `jjwt-jackson` module's Jackson dependency to `2.12.6.1`.\n* Upgrades the `jjwt-orgjson` module's org.json:json dependency to `20220320`.\n* Upgrades the `jjwt-gson` module's gson dependency to `2.9.0`.\n* Upgrades the internal testing BouncyCastle version and any references in README documentation examples to `1.70`.\n* Contains various documentation and typo fixes.\n\nThe patch also makes various internal project POM and build enhancements to reduce repetition and the chance for \nstale references, and overall create a cleaner build with less warnings.  It also ensures that CI testing builds\nand executes on all latest OpenJDK versions from Java 7 to Java 18 (inclusive).\n\nIssues included in this patch are listed in the [JJWT 0.11.4 milestone](https://github.com/jwtk/jjwt/milestone/25?closed=1).\n\n### 0.11.3\n\nThis patch release adds security guards against an ECDSA bug in Java SE versions 15-15.0.6, 17-17.0.2, and 18\n([CVE-2022-21449](https://nvd.nist.gov/vuln/detail/CVE-2022-21449)). Note: if your application does not use these \nJVM versions, you are not exposed to the JVM vulnerability.\n\nNote that the CVE is not a bug within JJWT itself - it is a bug within the above listed JVM versions.  However, even \nwith these additional JJWT security guards, the root cause of the issue is the JVM, so it **strongly \nrecommended** to upgrade your JVM to version 15.0.7, 17.0.3, or 18.0.1 or later to ensure the bug does not surface \nelsewhere in your application code or any other third party library in your application that may not contain similar \nsecurity guards.\n\nIssues included in this patch are listed in the [JJWT 0.11.3 milestone](https://github.com/jwtk/jjwt/milestone/24).\n\n#### Backwards Compatibility Warning\n\nIn addition to additional protections against \n[r or s values of zero in ECDSA signatures](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/), this \nrelease also disables by default legacy DER-encoded signatures that might be included in an ECDSA-signed JWT. \n(DER-encoded signatures are not supported by the JWT RFC specifications, so they are not frequently encountered.)\n\nHowever, if you are using an application that needs to consume such legacy JWTs (either produced by a very \nearly version of JJWT, or a different JWT library), you may re-enable DER-encoded ECDSA signatures by setting the \n`io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported` System property to the _exact_ \n`String` value `true`.  For example:\n\n```java\nSystem.setProperty(\"io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported\", \"true\");\n```\n\n*BUT BE CAREFUL*:  **DO NOT** set this System property if your application may run on one of the vulnerable JVMs\nnoted above (Java SE versions 15-15.0.6, 17-17.0.2, and 18).\n\nYou may safely set this property to a `String` value of `true` on all other versions of the JVM if you need to \nsupport these legacy JWTs, *otherwise it is best to ignore (not set) the property entirely*.\n\n#### Credits\n\nThank you to [Neil Madden](https://neilmadden.blog), the security researcher that first discovered the JVM\nvulnerability as covered in his [Psychic Signatures in Java](https://neilmadden.blog/2022/04/19/psychic-signatures-in-java/) blog post.\n\nWe'd also like to thank Toshiki Sasazaki, a member of [LINE Corporation](https://linecorp.com)'s Application Security \nTeam as the first person to report the concern directly to the JJWT team, as well as for working with us during testing \nleading to our conclusions and subsequent 0.11.3 patch release.\n\n### 0.11.2\n\nThis patch release:\n\n* Allows empty JWS bodies to support [RFC 8555](https://tools.ietf.org/html/rfc8555) and similar initiatives. [Pull Request 540](https://github.com/jwtk/jjwt/pull/540)\n* Ensures OSGi environments can access JJWT implementation bundles (`jjwt-jackson`, `jjwt-gson`, etc) as fragments to `jjwt-api` bundle. [Pull Request 580](https://github.com/jwtk/jjwt/pull/580)\n* Rejects `allowedClockSkewSeconds` values that would cause numeric overflow. [Issue 583](https://github.com/jwtk/jjwt/issues/583) \n* Upgrades Jackson dependency to version `2.9.10.4` to address all known Jackson CVE vulnerabilities. [Issue 585](https://github.com/jwtk/jjwt/issues/585)\n* Updates `SecretKey` algorithm name validation to allow PKCS12 KeyStore OIDs in addition to JCA Names. [Issue 588](https://github.com/jwtk/jjwt/issues/588)\n* Enabled CI builds on JDK 14. [Pull Request 590](https://github.com/jwtk/jjwt/pull/590)\n* Adds missing parameters type to `Maps.add()`, which removes an unchecked type warning. [Issue 591](https://github.com/jwtk/jjwt/issues/591)\n* Ensures `GsonDeserializer` always uses `UTF-8` for encoding bytes to Strings. [Pull Request 592](https://github.com/jwtk/jjwt/pull/592)\n\nAll issues and PRs are listed in the Github [JJWT 0.11.2 milestone](https://github.com/jwtk/jjwt/milestone/23?closed=1).\n\n\n### 0.11.1\n\nThis patch release:\n\n* Upgrades the `jjwt-jackson` module's Jackson dependency to `2.9.10.3`.\n* Fixes an issue when using Java 9+ `Map.of` with `JacksonDeserializer` that resulted in an `NullPointerException`.\n* Fixes an issue that prevented the `jjwt-gson` .jar's seralizer/deserializer implementation from being detected automatically.\n* Ensures service implementations are now loaded from the context class loader, Services.class.classLoader, and the system classloader, the first classloader with a service wins, and the others are ignored. This mimics how `Classes.forName()` works, and how JJWT attempted to auto-discover various implementations in previous versions.\n* Fixes a minor error in the `Claims#getIssuedAt` JavaDoc.\n\n### 0.11.0\n\nThis minor release:\n\n* Adds [Google's Gson](https://github.com/google/gson) as a natively supported JSON parser. Installation instructions \n  have been updated and new [JJWT Gson usage guidelines](https://github.com/jwtk/jjwt#json-gson) have been added.\n* Updates the Jackson dependency version to [2.9.10](https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.9#patches)\nto address three security vulnerabilities in Jackson.\n* A new `JwtParserBuilder` interface has been added and is the recommended way of creating an immutable and thread-safe JwtParser instance.  Mutable methods in `JwtParser` will be removed before v1.0.\n    Migration to the new signatures is straightforward, for example:\n    \n    Previous Version:\n    ```java \n     Jwts.parser()\n         .requireAudience(\"string\")\n         .parse(jwtString)\n    ```\n    Current Version:\n    ```java\n    Jwts.parserBuilder()\n        .requireAudience(\"string\")\n        .build()\n        .parse(jwtString)\n    ```\n* Adds `io.jsonwebtoken.lang.Maps` utility class to make creation of maps fluent, as demonstrated next.\n* Adds support for custom types when deserializing with Jackson. To use configure your parser:\n    ```java\n    Jwts.parserBuilder().deserializeJsonWith(\n        new JacksonDeserializer(\n            Maps.of(\"claimName\", YourType.class).build() // <--\n        )\n    ).build()\n  ```\n* Moves JSON Serializer/Deserializer implementations to a different package name.\n  - `io.jsonwebtoken.io.JacksonSerializer` -> `io.jsonwebtoken.jackson.io.JacksonSerializer`\n  - `io.jsonwebtoken.io.JacksonDeserializer` -> `io.jsonwebtoken.jackson.io.JacksonDeserializer`\n  - `io.jsonwebtoken.io.OrgJsonSerializer` -> `io.jsonwebtoken.orgjson.io.OrgJsonSerializer`\n  - `io.jsonwebtoken.io.OrgJsonDeserializer` -> `io.jsonwebtoken.orgjson.io.OrgJsonDeserializer`\n\n  A backward compatibility modules has been created using the `deprecated` classifier (`io.jsonwebtoken:jjwt-jackson:0.11.0:deprecated` and `io.jsonwebtoken:jjwt-orjson:0.11.0:deprecated`), if you are compiling against these classes directly, otherwise you will be unaffected.\n\n#### Backwards Compatibility Warning\n\nDue to this package move, if you are currently using one of the above four existing (pre 0.11.0) classes with `compile` scope, you must either:\n  1. change your code to use the newer package classes (recommended), or \n  1. change your build/dependency configuration to use the `deprecated` dependency classifier to use the existing classes, as follows:\n      \n**Maven**\n\n```xml\n<dependency>\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-jackson</artifactId>\n    <version>0.11.0</version>\n    <classifier>deprecated</classifier>\n    <scope>compile</scope>\n</dependency>\n```\n\n**Gradle**\n\n```groovy\ncompile 'io.jsonwebtoken:jjwt-jackson:0.11.0:deprecated'\n```\n\n**Note:** that the first option is recommended since the second option will not be available starting with the 1.0 release.\n\n### 0.10.8\n\nThis patch release:\n\n* Ensures that SignatureAlgorithms `PS256`, `PS384`, and `PS512` work properly on JDK 11 and later without the need\n  for BouncyCastle.  Previous releases referenced a BouncyCastle-specific \n  algorithm name instead of the Java Security Standard Algorithm Name of \n  [`RSASSA-PSS`](https://docs.oracle.com/en/java/javase/11/docs/specs/security/standard-names.html#signature-algorithms).\n  This release ensures the standard name is used moving forward.\n  \n* Fixes a backwards-compatibility [bug](https://github.com/jwtk/jjwt/issues/536) when parsing compressed JWTs \n  created from 0.10.6 or earlier using the `DEFLATE` compression algorithm.  \n\n### 0.10.7\n\nThis patch release:\n \n* Adds a new [Community section](https://github.com/jwtk/jjwt#community) in the documentation discussing asking \n  questions, using Slack and Gittr, and opening new issues and pull requests. \n* Fixes a [memory leak](https://github.com/jwtk/jjwt/issues/392) found in the DEFLATE compression \ncodec implementation.\n* Updates the Jackson dependency version to [2.9.9.1](https://github.com/FasterXML/jackson/wiki/Jackson-Release-2.9#patches)\nto address three security vulnerabilities in Jackson:\n[CVE-2019-12086](https://nvd.nist.gov/vuln/detail/CVE-2019-12086),\n[CVE-2019-12384](https://nvd.nist.gov/vuln/detail/CVE-2019-12384), and\n[CVE-2019-12814](https://nvd.nist.gov/vuln/detail/CVE-2019-12814).\n* Fixes a [bug](https://github.com/jwtk/jjwt/issues/397) when Jackson is in the classpath but the `jjwt-jackson` .jar is not.\n* Fixes various documentation and typo fixes.\n\n### 0.10.6\n\nThis patch release updates the jackson-databind version to 2.9.8 to address a critical security vulnerability in that\nlibrary.\n\n### 0.10.5\n\nThis patch release fixed an Android `org.json` library compatibility [issue](https://github.com/jwtk/jjwt/issues/388).\n\n### 0.10.4\n\nThis patch release fixed an [outstanding issue](https://github.com/jwtk/jjwt/issues/381) with JCA name \ncase-sensitivity that impacted Android that was not caught in the 0.10.3 release.\n\n### 0.10.3\n\nThis is a minor patch release that fixed a key length assertion for `SignatureAlgorithm.forSigningKey` that was \nfailing in Android environments.  The Android dependencies and ProGuard exclusions documentation was updated as \nwell to reflect Android Studio 3.0 conventions.\n\n### 0.10.2\n\nThis is a minor patch release that ensures the `OrgJsonSerializer` and `OrgJsonDeserializer` implementations are \ncompatible with Android's older `org.json` API.  Previously JJWT used newer `org.json` APIs that are not \navailable on Android.\n\n### 0.10.1\n\nThis is a minor point release that ensures the BouncyCastle dependency is optional and not pulled in as a transitive\ndependency into projects.\n \nInternal implementation code (not impacting the JJWT API) and documentation was also updated to reflect that all \nElliptic Curve algorithms are standard on the JDK and do not require Bouncy Castle.\n\nBouncy Castle is only needed when using PS256, PS384, and PS512 signature algorithms on < JDK 11. \n[JDK 11 and later](https://bugs.openjdk.java.net/browse/JDK-8146293) supports these algorithms natively.\n\n### 0.10.0\n\nThis is a fairly large feature enhancement release that enables the following:\n\n* Modular project structure resulting in pluggable JJWT dependencies ([Issue 348](https://github.com/jwtk/jjwt/issues/348))\n* Auto-configuration for Jackson or JSON-Java [JSON processors](https://github.com/jwtk/jjwt#json).\n* [Automatic SignatureAlgorithm selection](https://github.com/jwtk/jjwt#jws-create-key) based on specified signing Key.\n* Algorithm and Key [Strength Assertions](https://github.com/jwtk/jjwt#jws-key)\n* [Simplified Key generation](https://github.com/jwtk/jjwt#jws-key-create)\n* Deterministic [Base64(URL) support](https://github.com/jwtk/jjwt#base64) on all JDK and Android platforms\n* [Custom JSON processing](https://github.com/jwtk/jjwt#json-custom)\n* Complete [documentation](https://github.com/jwtk/jjwt)\n* and a bunch of other [minor fixes and enhancements](https://github.com/jwtk/jjwt/milestone/11).\n\n**BACKWARDS-COMPATIBILITY NOTICE:**\n\nJJWT's new modular design utilizes distinctions between compile and runtime dependencies to ensure you only depend\non the public APIs that are safe to use in your application.  All internal/private implementation classes have\nbeen moved to a new `jjwt-impl` runtime dependency.\n\nIf you depended on any internal implementation classes in the past, you have two choices:\n\n1. Refactor your code to use the public-only API classes and interfaces in the `jjwt-api` .jar.  Any functionality\n   you might have used in the internal implementation should be available via newer cleaner interfaces and helper \n   classes in that .jar.\n   \n2. Specify the new `jjwt-impl` .jar not as a runtime dependency but as a compile dependency.  This would make your\n   upgrade to JJWT 0.10.0 fully backwards compatible, but you do so _at your own risk_.  JJWT will make **NO** \n   semantic version compatibility guarantees in the `jjwt-impl` .jar moving forward.  Semantic versioning will be \n   very carefully adhered to in all other JJWT dependencies however.\n\n### 0.9.1\n\nThis is a minor patch release that updates the Jackson dependency to 2.9.6 to address Jackson CVE-2017-17485.\n\n### 0.9.0\n\nThis is a minor release that includes changes to dependencies and plugins to allow for building jjwt with Java 9.\nJavadocs in a few classes were updated as well to support proper linting in both Java 8 and Java 9.\n\n### 0.8.0\n\nThis is a minor feature enhancement, dependency version update and build update release. We switched from Jacoco to \nOpenClover as OpenClover delivers a higher quality of test metrics. As an interim measure, we introduced a new \nrepository that has an updated version of the coveralls-maven-plugin which includes support for Clover reporting to\nCoveralls. Once this change has been merged and released to the official coveralls-maven-plugin on maven central, \nthis repository will be removed. The following dependencies were updated to the latest release version: maven \ncompiler, maven enforcer, maven failsafe, maven release, maven scm provider, maven bundle, maven gpg, maven source, \nmaven javadoc, jackson, bouncy castle, groovy, logback and powermock. Of significance, is the upgrade for jackson as \na security issue was addressed in its latest release.\n\nAn `addClaims` method is added to the `JwtBuilder` interface in this release. It adds all given name/value pairs to\nthe JSON Claims in the payload.\n\nAdditional tests were added to improve overall test coverage.\n\n### 0.7.0\n\nThis is a minor feature enhancement and bugfix release.  One of the bug fixes is particularly important if using\nelliptic curve signatures, please see below.\n\n#### Elliptic Curve Signature Length Bug Fix\n\nPrevious versions of JJWT safely calculated and verified Elliptic Curve signatures (no security risks), however, the\n signatures were encoded using the JVM's default ASN.1/DER format.  The JWS specification however \nrequires EC signatures to be in a R + S format.  JJWT >= 0.7.0 now correctly represents newly computed EC signatures in \nthis spec-compliant format.\n\nWhat does this mean for you?\n\nSignatures created from previous JJWT versions can still be verified, so your existing tokens will still be parsed \ncorrectly. HOWEVER, new JWTs with EC signatures created by JJWT >= 0.7.0 are now spec compliant and therefore can only \nbe verified by JJWT >= 0.7.0 (or any other spec compliant library).\n\n**This means that if you generate JWTs using Elliptic Curve Signatures after upgrading to JJWT >= 0.7.0, you _must_ \nalso upgrade any applications that parse these JWTs to upgrade to JJWT >= 0.7.0 as well.**\n\n#### Clock Skew Support\n\nWhen parsing a JWT, you might find that `exp` or `nbf` claims fail because the clock on the parsing machine is not \nperfectly in sync with the clock on the machine that created the JWT.  You can now account for these differences \n(usually no more than a few minutes) when parsing using the new `setAllowedClockSkewSeconds` method on the parser.\nFor example:\n\n```java\nlong seconds = 3 * 60; //3 minutes\nJwts.parser().setAllowedClockSkewSeconds(seconds).setSigningKey(key).parseClaimsJws(jwt);\n```\n\nThis ensures that clock differences between machines can be ignored.  Two or three minutes should be more than enough; it\nwould be very strange if a machine's clock was more than 5 minutes difference from most atomic clocks around the world.\n\n#### Custom Clock Support\n\nTimestamps created during parsing can now be obtained via a custom time source via an implementation of\n the new `io.jsonwebtoken.Clock` interface.  The default implementation simply returns `new Date()` to reflect the time\n  when parsing occurs, as most would expect.  However, supplying your own clock could be useful, especially during test \n  cases to guarantee deterministic behavior.\n\n#### Android RSA Private Key Support\n\nPrevious versions of JJWT required RSA private keys to implement `java.security.interfaces.RSAPrivateKey`, but Android \n6 RSA private keys do not implement this interface.  JJWT now asserts that RSA keys are instances of both \n`java.security.interfaces.RSAKey` and `java.security.PrivateKey` which should work fine on both Android and all other\n'standard' JVMs as well.\n\n#### Library version updates\n\nThe few dependencies JWWT has (e.g. Jackson) have been updated to their latest stable versions at the time of release.\n\n#### Issue List\n\nFor all completed issues, please see the [0.7.0 Milestone List](https://github.com/jwtk/jjwt/milestone/7?closed=1)\n\n### 0.6.0\n\n#### Enforce JWT Claims when Parsing\n\nYou can now enforce that JWT claims have expected values when parsing a compact JWT string.\n\nFor example, let's say that you require that the JWT you are parsing has a specific `sub` (subject) value,\notherwise you may not trust the token.  You can do that by using one of the `require` methods on the parser builder:\n\n```java\ntry {\n    Jwts.parser().requireSubject(\"jsmith\").setSigningKey(key).parseClaimsJws(s);\n} catch(InvalidClaimException ice) {\n    // the sub claim was missing or did not have a 'jsmith' value\n}\n```\n\nIf it is important to react to a missing vs an incorrect value, instead of catching `InvalidClaimException`, you can catch either `MissingClaimException` or `IncorrectClaimException`:\n\n```java\ntry {\n    Jwts.parser().requireSubject(\"jsmith\").setSigningKey(key).parseClaimsJws(s);\n} catch(MissingClaimException mce) {\n    // the parsed JWT did not have the sub claim\n} catch(IncorrectClaimException ice) {\n    // the parsed JWT had a sub claim, but its value was not equal to 'jsmith'\n}\n```\n\nYou can also require custom claims by using the `require(claimName, requiredValue)` method - for example:\n\n```java\ntry {\n    Jwts.parser().require(\"myClaim\", \"myRequiredValue\").setSigningKey(key).parseClaimsJws(s);\n} catch(InvalidClaimException ice) {\n    // the 'myClaim' claim was missing or did not have a 'myRequiredValue' value\n}\n```\n(or, again, you could catch either MissingClaimException or IncorrectClaimException instead)\n\n#### Body Compression\n\n**This feature is NOT JWT specification compliant**, *but it can be very useful when you parse your own tokens*.\n\nIf your JWT body is large and you have size restrictions (for example, if embedding a JWT in a URL and the URL must be under a certain length for legacy browsers or mail user agents), you may now compress the JWT body using a `CompressionCodec`:\n\n```java\nJwts.builder().claim(\"foo\", \"someReallyLongDataString...\")\n    .compressWith(CompressionCodecs.DEFLATE) // or CompressionCodecs.GZIP\n    .signWith(SignatureAlgorithm.HS256, key)\n    .compact();\n```\n\nThis will set a new `zip` header with the name of the compression algorithm used so that parsers can see that value and decompress accordingly.\n\nThe default parser implementation will automatically decompress DEFLATE or GZIP compressed bodies, so you don't need to set anything on the parser - it looks like normal:\n\n```java\nJwts.parser().setSigningKey(key).parseClaimsJws(compact);\n```\n\n##### Custom Compression Algorithms\n\nIf the DEFLATE or GZIP algorithms are not sufficient for your needs, you can specify your own Compression algorithms by implementing the `CompressionCodec` interface and setting it on the parser:\n\n```java\nJwts.builder().claim(\"foo\", \"someReallyLongDataString...\")\n    .compressWith(new MyCompressionCodec())\n    .signWith(SignatureAlgorithm.HS256, key)\n    .compact();\n```\n\nYou will then need to specify a `CompressionCodecResolver` on the parser, so you can inspect the `zip` header and return your custom codec when discovered:\n\n```java\nJwts.parser().setSigningKey(key)\n    .setCompressionCodecResolver(new MyCustomCompressionCodecResolver())\n    .parseClaimsJws(compact);\n```\n\n*NOTE*: Because body compression is not JWT specification compliant, you should only enable compression if both your JWT builder and parser are JJWT versions >= 0.6.0, or if you're using another library that implements the exact same functionality.  This feature is best reserved for your own use cases - where you both create and later parse the tokens.  It will likely cause problems if you compressed a token and expected a 3rd party (who doesn't use JJWT) to parse the token.\n\n### 0.5.1\n\n- Minor [bug](https://github.com/jwtk/jjwt/issues/31) fix [release](https://github.com/jwtk/jjwt/issues?q=milestone%3A0.5.1+is%3Aclosed) that ensures correct Base64 padding in Android runtimes.\n\n### 0.5\n\n- Android support! Android's built-in Base64 codec will be used if JJWT detects it is running in an Android environment.  Other than Base64, all other parts of JJWT were already Android-compliant.  Now it is fully compliant.\n\n- Elliptic Curve signature algorithms!  `SignatureAlgorithm.ES256`, `ES384` and `ES512` are now supported.\n\n- Super convenient key generation methods, so you don't have to worry how to do this safely:\n  - `MacProvider.generateKey(); //or generateKey(SignatureAlgorithm)`\n  - `RsaProvider.generateKeyPair(); //or generateKeyPair(sizeInBits)`\n  - `EllipticCurveProvider.generateKeyPair(); //or generateKeyPair(SignatureAlgorithm)`\n\n  The `generate`* methods that accept an `SignatureAlgorithm` argument know to generate a key of sufficient strength that reflects the specified algorithm strength.\n\nPlease see the full [0.5 closed issues list](https://github.com/jwtk/jjwt/issues?q=milestone%3A0.5+is%3Aclosed) for more information.\n\n### 0.4\n\n- [Issue 8](https://github.com/jwtk/jjwt/issues/8): Add ability to find signing key by inspecting the JWS values before verifying the signature.\n\nThis is a handy little feature.  If you need to parse a signed JWT (a JWS) and you don't know which signing key was used to sign it, you can now use the new `SigningKeyResolver` concept.\n\nA `SigningKeyresolver` can inspect the JWS header and body (Claims or String) _before_ the JWS signature is verified. By inspecting the data, you can find the key and return it, and the parser will use the returned key to validate the signature.  For example:\n\n```java\nSigningKeyResolver resolver = new MySigningKeyResolver();\n\nJws<Claims> jws = Jwts.parser().setSigningKeyResolver(resolver).parseClaimsJws(compact);\n```\n\nThe signature is still validated, and the JWT instance will still not be returned if the jwt string is invalid, as \nexpected.  You just get to 'see' the JWT data for key discovery before the parser validates.  Nice.\n\nThis of course requires that you put some sort of information in the JWS when you create it so that your \n`SigningKeyResolver` implementation can look at it later and look up the key.  The *standard* way to do this is to \nuse the JWS `kid` ('key id') header parameter, for example:\n\n```java\nJwts.builder().setHeaderParam(\"kid\", your_signing_key_id_NOT_THE_SECRET).build();\n```\n\nYou could of course set any other header parameter or claims instead of setting `kid` if you want - \nthat's just the default parameter reserved for signing key identification.  If you can locate the signing key based \non other information in the header or claims, you don't need to set the `kid` parameter - just make sure your \nresolver implementation knows how to look up the key.\n\nFinally, a nice `SigningKeyResolverAdapter` is provided to allow you to write quick and simple subclasses or \nanonymous classes instead of having to implement the `SigningKeyResolver` interface directly.  For example:\n\n```java\nJws<Claims> jws = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {\n        @Override\n        public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n            //inspect the header or claims, lookup and return the signing key\n            String keyId = header.getKeyId(); //or any other parameter that you need to inspect\n            return getSigningKey(keyId); //implement me\n        }})\n    .parseClaimsJws(compact);\n```\n\n### 0.3\n\n- [Issue 6](https://github.com/jwtk/jjwt/issues/6): Parsing an expired Claims JWT or JWS (as determined by the `exp` \n  claim) will now throw an `ExpiredJwtException`.\n- [Issue 7](https://github.com/jwtk/jjwt/issues/7): Parsing a premature Claims JWT or JWS (as determined by the `nbf`\n  claim) will now throw a `PrematureJwtException`.\n\n### 0.2\n\n#### More convenient Claims building\n\nThis release adds convenience methods to the `JwtBuilder` interface so you can set claims directly on the builder without having to create a separate Claims instance/builder, reducing the amount of code you have to write.  For example, this:\n\n```java\nClaims claims = Jwts.claims().setSubject(\"Joe\");\n\nString compactJwt = Jwts.builder().setClaims(claims).signWith(HS256, key).compact();\n```\n\ncan now be written as:\n\n```java\nString compactJwt = Jwts.builder().setSubject(\"Joe\").signWith(HS256, key).compact();\n```\n\nA Claims instance based on the specified claims will be created and set as the JWT's payload automatically.\n\n#### Type-safe handling for JWT and JWS with generics\n\nThe following < 0.2 code produced a JWT as expected:\n\n```java\nJwt jwt = Jwts.parser().setSigningKey(key).parse(compact);\n```\n\nBut you couldn't easily determine if the `jwt` was a `JWT` or `JWS` instance or if the body was a `Claims` instance or a plaintext `String` without resorting to a bunch of yucky `instanceof` checks.  In 0.2, we introduce the `JwtHandler` when you don't know the exact format of the compact JWT string ahead of time, and parsing convenience methods when you do.\n\n##### JwtHandler\n\nIf you do not know the format of the compact JWT string at the time you try to parse it, you can determine what type it is after parsing by providing a `JwtHandler` instance to the `JwtParser` with the new `parse(String compactJwt, JwtHandler handler)` method.  For example:\n\n```java\nT returnVal = Jwts.parser().setSigningKey(key).parse(compact, new JwtHandler<T>() {\n    @Override\n    public T onPlaintextJwt(Jwt<Header, String> jwt) {\n        //the JWT parsed was an unsigned plaintext JWT\n        //inspect it, then return an instance of T (see returnVal above)\n    }\n\n    @Override\n    public T onClaimsJwt(Jwt<Header, Claims> jwt) {\n        //the JWT parsed was an unsigned Claims JWT\n        //inspect it, then return an instance of T (see returnVal above)\n    }\n\n    @Override\n    public T onPlaintextJws(Jws<String> jws) {\n        //the JWT parsed was a signed plaintext JWS\n        //inspect it, then return an instance of T (see returnVal above)\n    }\n\n    @Override\n    public T onClaimsJws(Jws<Claims> jws) {\n        //the JWT parsed was a signed Claims JWS\n        //inspect it, then return an instance of T (see returnVal above)\n    }\n});\n```\n\nOf course, if you know you'll only have to parse a subset of the above, you can use the `JwtHandlerAdapter` and implement only the methods you need.  For example:\n\n```java\nT returnVal = Jwts.parser().setSigningKey(key).parse(plaintextJwt, new JwtHandlerAdapter<Jwt<Header, T>>() {\n    @Override\n    public T onPlaintextJws(Jws<String> jws) {\n        //the JWT parsed was a signed plaintext JWS\n        //inspect it, then return an instance of T (see returnVal above)\n    }\n\n    @Override\n    public T onClaimsJws(Jws<Claims> jws) {\n        //the JWT parsed was a signed Claims JWS\n        //inspect it, then return an instance of T (see returnVal above)\n    }\n});\n```\n\n##### Known Type convenience parse methods\n\nIf, unlike above, you are confident of the compact string format and know which type of JWT or JWS it will produce, you can just use one of the 4 new convenience parsing methods to get exactly the type of JWT or JWS you know exists.  For example:\n\n```java\n\n//for a known plaintext jwt string:\nJwt<Header,String> jwt = Jwts.parser().parsePlaintextJwt(compact);\n\n//for a known Claims JWT string:\nJwt<Header,Claims> jwt = Jwts.parser().parseClaimsJwt(compact);\n\n//for a known signed plaintext JWT (aka a plaintext JWS):\nJws<String> jws = Jwts.parser().setSigningKey(key).parsePlaintextJws(compact);\n\n//for a known signed Claims JWT (aka a Claims JWS):\nJws<Claims> jws = Jwts.parser().setSigningKey(key).parseClaimsJws(compact);\n\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"{}\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright {yyyy} {name of copyright owner}\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "NOTICE.md",
    "content": "## Base64 implementation\n\nJJWT's `io.jsonwebtoken.io.Base64` implementation is based on [MigBase64](https://github.com/brsanthu/migbase64) with \ncontinued modifications for Base64 URL support and additional test cases. The MigBase64 copyright and license notice \nhave been retained and are repeated here per that code's requirements:\n\n```\nLicence (BSD):\n==============\n\nCopyright (c) 2004, Mikael Grev, MiG InfoCom AB. (base64 @ miginfocom . com)\nAll rights reserved.\n\nRedistribution and use in source and binary forms, with or without modification,\nare permitted provided that the following conditions are met:\nRedistributions of source code must retain the above copyright notice, this list\nof conditions and the following disclaimer.\nRedistributions in binary form must reproduce the above copyright notice, this\nlist of conditions and the following disclaimer in the documentation and/or other\nmaterials provided with the distribution.\nNeither the name of the MiG InfoCom AB nor the names of its contributors may be\nused to endorse or promote products derived from this software without specific\nprior written permission.\n\nTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS \"AS IS\" AND\nANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED\nWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.\nIN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,\nINDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,\nBUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\nOR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,\nWHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\nARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY\nOF SUCH DAMAGE.\n```\n\nAdditionally, the following classes were copied from the Apache Commons-Codec project, with further JJWT-specific\nmodifications:\n* io.jsonwebtoken.impl.io.Base64Codec\n* io.jsonwebtoken.impl.io.Base64InputStream\n* io.jsonwebtoken.impl.io.Base64OutputStream\n* io.jsonwebtoken.impl.io.BaseNCodec\n* io.jsonwebtoken.impl.io.BaseNCodecInputStream\n* io.jsonwebtoken.impl.io.BaseNCodecOutputStream\n* io.jsonwebtoken.impl.io.CodecPolicy\n\nIts attribution:\n\n```\nApache Commons Codec\nCopyright 2002-2023 The Apache Software Foundation\n\nThis product includes software developed at\nThe Apache Software Foundation (https://www.apache.org/).\n```\n\nAlso, the following classes were copied from the Apache Commons-IO project, with further JJWT-specific modifications:\n* io.jsonwebtoken.impl.io.CharSequenceReader\n* io.jsonwebtoken.impl.io.FilteredInputStream\n* io.jsonwebtoken.impl.io.FilteredOutputStream\n* io.jsonwebtoken.impl.io.ClosedInputStream\n* io.jsonwebtoken.impl.io.UncloseableInputStream\n\nIt's attribution:\n\n```\nApache Commons IO\nCopyright 2002-2023 The Apache Software Foundation\n\nThis product includes software developed at\nThe Apache Software Foundation (https://www.apache.org/).\n```"
  },
  {
    "path": "README.adoc",
    "content": ":doctype: book\n= Java JWT: JSON Web Token for Java and Android\n:project-version: 0.13.0\n:toc:\n:toc-title:\n:toc-placement!:\n:toclevels: 4\n\nifdef::env-github[]\n:tip-caption: ✏️TIP\n:note-caption: ℹ️ NOTE\n:important-caption: ‼️IMPORTANT\n:caution-caption: ⛔️CAUTION\n:warning-caption: ⚠️WARNING\nendif::[]\n\n// Macros\n:fn-require-java8-plus: Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.\n:fn-require-java11-plus: Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.\n:fn-require-java15-plus: Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.\n\nimage:https://github.com/jwtk/jjwt/actions/workflows/ci.yml/badge.svg?branch=master[Build Status,link=https://github.com/jwtk/jjwt/actions/workflows/ci.yml?query=branch%3Amaster]\nimage:https://coveralls.io/repos/github/jwtk/jjwt/badge.svg?branch=master[Coverage Status,link=https://coveralls.io/github/jwtk/jjwt?branch=master]\nimage:https://snyk-widget.herokuapp.com/badge/mvn/io.jsonwebtoken/jjwt-root/badge.svg[Vuln score,link=https://snyk-widget.herokuapp.com/badge/mvn/io.jsonwebtoken/jjwt-root/badge.svg]\nimage:https://snyk.io/test/github/jwtk/jjwt/badge.svg[Known Vulns,link=https://snyk.io/test/github/jwtk/jjwt/badge.svg]\n\nJJWT aims to be the easiest to use and understand library for creating and verifying JSON Web Tokens (JWTs) and\nJSON Web Keys (JWKs) on the JVM and Android.\n\nJJWT is a pure Java implementation based exclusively on the\nhttps://datatracker.ietf.org/wg/jose/documents/[JOSE Working Group] RFC specifications:\n\n* https://tools.ietf.org/html/rfc7519[RFC 7519: JSON Web Token (JWT)]\n* https://tools.ietf.org/html/rfc7515[RFC 7515: JSON Web Signature (JWS)]\n* https://tools.ietf.org/html/rfc7516[RFC 7516: JSON Web Encryption (JWE)]\n* https://tools.ietf.org/html/rfc7517[RFC 7517: JSON Web Key (JWK)]\n* https://tools.ietf.org/html/rfc7518[RFC 7518: JSON Web Algorithms (JWA)]\n* https://www.rfc-editor.org/rfc/rfc7638.html[RFC 7638: JSON Web Key Thumbprint]\n* https://www.rfc-editor.org/rfc/rfc9278.html[RFC 9278: JSON Web Key Thumbprint URI]\n* https://www.rfc-editor.org/rfc/rfc7797.html[RFC 7797: JWS Unencoded Payload Option]\n* https://www.rfc-editor.org/rfc/rfc8037[RFC 8037: Edwards Curve algorithms and JWKs]\n\nIt was created by https://github.com/lhazlewood[Les Hazlewood]\nand is supported and maintained by a https://github.com/jwtk/jjwt/graphs/contributors[community] of contributors.\n\nJJWT is open source under the terms of the http://www.apache.org/licenses/LICENSE-2.0[Apache 2.0 License].\n\n====\n[discrete]\n== Table of Contents\n---\ntoc::[]\n====\n\n+++<a name=\"features\">++++++</a>+++\n\n== Features\n\n* Fully functional on all Java 7+ JDKs and Android\n* Automatic security best practices and assertions\n* Easy to learn and read API\n* Convenient and readable http://en.wikipedia.org/wiki/Fluent_interface[fluent] interfaces, great for IDE\nauto-completion to write code quickly\n* Fully RFC specification compliant on all implemented functionality, tested against RFC-specified test vectors\n* Stable implementation with almost 1,700 tests and enforced 100% test code coverage.  Every single method, statement\nand conditional branch variant in the entire codebase is tested and required to pass on every build.\n* Creating, parsing and verifying digitally signed compact JWTs (aka JWSs) with all standard JWS algorithms:\n+\n|===\n| Identifier | Signature Algorithm\n\n| `HS256`\n| HMAC using SHA-256\n\n| `HS384`\n| HMAC using SHA-384\n\n| `HS512`\n| HMAC using SHA-512\n\n| `ES256`\n| ECDSA using P-256 and SHA-256\n\n| `ES384`\n| ECDSA using P-384 and SHA-384\n\n| `ES512`\n| ECDSA using P-521 and SHA-512\n\n| `RS256`\n| RSASSA-PKCS-v1_5 using SHA-256\n\n| `RS384`\n| RSASSA-PKCS-v1_5 using SHA-384\n\n| `RS512`\n| RSASSA-PKCS-v1_5 using SHA-512\n\n| `PS256`\n| RSASSA-PSS using SHA-256 and MGF1 with SHA-256^*1*^\n\n| `PS384`\n| RSASSA-PSS using SHA-384 and MGF1 with SHA-384^*1*^\n\n| `PS512`\n| RSASSA-PSS using SHA-512 and MGF1 with SHA-512^*1*^\n\n| `EdDSA`\n| Edwards-curve Digital Signature Algorithm^*2*^\n|===\n+\n^*1.*{sp}{fn-require-java11-plus}^\n+\n^*2*.{sp}{fn-require-java15-plus}^\n\n* Creating, parsing and decrypting encrypted compact JWTs (aka JWEs) with all standard JWE encryption algorithms:\n+\n|===\n| Identifier | Encryption Algorithm\n\n| `A128CBC‑HS256`\n| https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.3[AES_128_CBC_HMAC_SHA_256] authenticated encryption algorithm\n\n| `A192CBC-HS384`\n| https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.4[AES_192_CBC_HMAC_SHA_384] authenticated encryption algorithm\n\n| `A256CBC-HS512`\n| https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5[AES_256_CBC_HMAC_SHA_512] authenticated encryption algorithm\n\n| `A128GCM`\n| AES GCM using 128-bit key^*1*^\n\n| `A192GCM`\n| AES GCM using 192-bit key^*1*^\n\n| `A256GCM`\n| AES GCM using 256-bit key^*1*^\n|===\n+\n^*1*.{sp}{fn-require-java8-plus}^\n\n* All Key Management Algorithms for obtaining JWE encryption and decryption keys:\n+\n|===\n| Identifier | Key Management Algorithm\n\n| `RSA1_5`\n| RSAES-PKCS1-v1_5\n\n| `RSA-OAEP`\n| RSAES OAEP using default parameters\n\n| `RSA-OAEP-256`\n| RSAES OAEP using SHA-256 and MGF1 with SHA-256\n\n| `A128KW`\n| AES Key Wrap with default initial value using 128-bit key\n\n| `A192KW`\n| AES Key Wrap with default initial value using 192-bit key\n\n| `A256KW`\n| AES Key Wrap with default initial value using 256-bit key\n\n| `dir`\n| Direct use of a shared symmetric key as the CEK\n\n| `ECDH-ES`\n| Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF\n\n| `ECDH-ES+A128KW`\n| ECDH-ES using Concat KDF and CEK wrapped with \"A128KW\"\n\n| `ECDH-ES+A192KW`\n| ECDH-ES using Concat KDF and CEK wrapped with \"A192KW\"\n\n| `ECDH-ES+A256KW`\n| ECDH-ES using Concat KDF and CEK wrapped with \"A256KW\"\n\n| `A128GCMKW`\n| Key wrapping with AES GCM using 128-bit key^*1*^\n\n| `A192GCMKW`\n| Key wrapping with AES GCM using 192-bit key^*1*^\n\n| `A256GCMKW`\n| Key wrapping with AES GCM using 256-bit key^*1*^\n\n| `PBES2-HS256+A128KW`\n| PBES2 with HMAC SHA-256 and \"A128KW\" wrapping^*1*^\n\n| `PBES2-HS384+A192KW`\n| PBES2 with HMAC SHA-384 and \"A192KW\" wrapping^*1*^\n\n| `PBES2‑HS512+A256KW`\n| PBES2 with HMAC SHA-512 and \"A256KW\" wrapping^*1*^\n|===\n+\n^*1*.{sp}{fn-require-java8-plus}^\n\n* Creating, parsing and verifying JSON Web Keys (JWKs) in all standard JWA key formats using native Java `Key` types:\n+\n|===\n| JWK Key Format | Java `Key` Type | JJWT `Jwk` Type\n\n| Symmetric Key\n| `SecretKey`\n| `SecretJwk`\n\n| Elliptic Curve Public Key\n| `ECPublicKey`\n| `EcPublicJwk`\n\n| Elliptic Curve Private Key\n| `ECPrivateKey`\n| `EcPrivateJwk`\n\n| RSA Public Key\n| `RSAPublicKey`\n| `RsaPublicJwk`\n\n| RSA Private Key\n| `RSAPrivateKey`\n| `RsaPrivateJwk`\n\n| XDH Private Key\n| `XECPublicKey`^*1*^\n| `OctetPublicJwk`\n\n| XDH Private Key\n| `XECPrivateKey`^*1*^\n| `OctetPrivateJwk`\n\n| EdDSA Public Key\n| `EdECPublicKey`^*2*^\n| `OctetPublicJwk`\n\n| EdDSA Private Key\n| `EdECPublicKey`^*2*^\n| `OctetPrivateJwk`\n|===\n+\n^*1*.{sp}{fn-require-java15-plus}^\n+\n^*2*.{sp}{fn-require-java15-plus}^\n\n* Convenience enhancements beyond the specification such as\n ** Payload compression for any large JWT, not just JWEs\n ** Claims assertions (requiring specific values)\n ** Claim POJO marshaling and unmarshalling when using a compatible JSON parser (e.g. Jackson)\n ** Secure Key generation based on desired JWA algorithms\n ** and more...\n\n+++<a name=\"features-unsupported\">++++++</a>+++\n\n=== Currently Unsupported Features\n\n* https://tools.ietf.org/html/rfc7515#section-7.2[Non-compact] serialization and parsing.\n\nThis feature may be implemented in a future release.  Community contributions are welcome!\n\n+++<a name=\"community\">++++++</a>+++\n\n== Community\n\n+++<a name=\"help\">++++++</a>+++\n\n=== Getting Help\n\nIf you have trouble using JJWT, please first read the documentation on this page before asking questions.  We try\nvery hard to ensure JJWT's documentation is robust, categorized with a table of contents, and up to date for each\nrelease.\n\n+++<a name=\"help-questions\">++++++</a>+++\n\n==== Questions\n\nIf the documentation or the API JavaDoc isn't sufficient, and you either have usability questions or are confused\nabout something, please https://github.com/jwtk/jjwt/discussions/new?category=q-a[ask your question here]. However:\n\n*Please do not create a GitHub issue to ask a question.*\n\nWe use GitHub Issues to track actionable work that requires changes to JJWT's design and/or codebase.  If you have a\nusability question, instead please\nhttps://github.com/jwtk/jjwt/discussions/new?category=q-a[ask your question here], and we can convert that to an\nissue if necessary.\n\n*If a GitHub Issue is created that does not represent actionable work for JJWT's codebase, it will be promptly\nclosed.*\n\n+++<a name=\"help-issues\">++++++</a>+++\n\n==== Bugs, Feature Requests, Ideas and General Discussions\n\nIf you do not have a usability question and believe you have a legitimate bug or feature request,\nplease https://github.com/jwtk/jjwt/discussions[discuss it here] *_FIRST_*. Please do a quick search first to\nsee if an existing discussion related to yours exist already and join that existing discussion if necesary.\n\nIf you feel like you'd like to help fix a bug or implement the new feature yourself, please read the Contributing\nsection next before starting any work.\n\n+++<a name=\"contributing\">++++++</a>+++\n\n=== Contributing\n\n+++<a name=\"contributing-pull-requests\">++++++</a>+++\n\n==== Pull Requests\n\nSimple Pull Requests that fix anything other than JJWT core code (documentation, JavaDoc, typos, test cases, etc) are\nalways appreciated and have a high likelihood of being merged quickly. Please send them!\n\nHowever, if you want or feel the need to change JJWT's functionality or core code, please do not issue a pull request\nwithout https://github.com/jwtk/jjwt/discussions[starting a new JJWT discussion] and discussing your desired\nchanges *first*, _before you start working on it_.\n\nIt would be a shame to reject your earnest and genuinely-appreciated pull request if it might not align with the\nproject's goals, design expectations or planned functionality.  We've sadly had to reject large PRs in the past because\nthey were out of sync with project or design expectations - all because the PR author didn't first check in with\nthe team first before working on a solution.\n\nSo, please https://github.com/jwtk/jjwt/discussions[create a new JJWT discussion] first to discuss, and then we\ncan see easily convert the discussion to an issue and then see if (or how) a PR is warranted.  Thank you!\n\n+++<a name=\"contributing-help-wanted\">++++++</a>+++\n\n==== Help Wanted\n\nIf you would like to help, but don't know where to start, please visit the\nhttps://github.com/jwtk/jjwt/labels/help%20wanted[Help Wanted Issues] page and pick any of the\nones there, and we'll be happy to discuss and answer questions in the issue comments.\n\nIf any of those don't appeal to you, no worries! Any help you would like to offer would be\nappreciated based on the above caveats concerning <<contributing-pull-requests,contributing pull requests>>. Feel free\nto https://github.com/jwtk/jjwt/discussions[discuss or ask questions first] if you're not sure. :)\n\n+++<a name=\"overview\">++++++</a>+++\n\n== What is a JSON Web Token?\n\nJSON Web Token (JWT) is a _general-purpose_ text-based messaging format for transmitting information in a\ncompact and secure way.  Contrary to popular belief, JWT is not just useful for sending and receiving identity tokens\non the web - even if that is the most common use case.  JWTs can be used as messages for _any_ type of data.\n\nA JWT in its simplest form contains two parts:\n\n. The primary data within the JWT, called the `payload`, and\n. A JSON `Object` with name/value pairs that represent metadata about the `payload` and the\nmessage itself, called the `header`.\n\nA JWT `payload` can be absolutely anything at all - anything that can be represented as a byte array, such as Strings,\nimages, documents, etc.\n\nBut because a JWT `header` is a JSON `Object`, it would make sense that a JWT `payload` could also be a JSON\n`Object` as well. In many cases, developers like the `payload` to be JSON that\nrepresents data about a user or computer or similar identity concept. When used this way, the `payload` is called a\nJSON `Claims` object, and each name/value pair within that object is called a `claim` - each piece of information\nwithin 'claims' something about an identity.\n\nAnd while it is useful to 'claim' something about an identity, really anyone can do that. What's important is that you\n_trust_ the claims by verifying they come from a person or computer you trust.\n\nA nice feature of JWTs is that they can be secured in various ways. A JWT can be cryptographically signed (making it\nwhat we call a https://tools.ietf.org/html/rfc7515[JWS]) or encrypted (making it a\nhttps://tools.ietf.org/html/rfc7516[JWE]).  This adds a powerful layer of verifiability to the JWT - a\nJWS or JWE recipient can have a high degree of confidence it comes from someone they trust\nby verifying a signature or decrypting it. It is this feature of verifiability that makes JWT a good choice\nfor sending and receiving secure information, like identity claims.\n\nFinally, JSON with whitespace for human readability is nice, but it doesn't make for a very efficient message\nformat.  Therefore, JWTs can be _compacted_ (and even compressed) to a minimal representation - basically\nBase64URL-encoded strings - so they can be transmitted around the web more efficiently, such as in HTTP headers or URLs.\n\n+++<a name=\"overview-example-jwt\">++++++</a>+++\n\n=== JWT Example\n\nOnce you have a `payload` and `header`, how are they compacted for web transmission, and what does the final JWT\nactually look like? Let's walk through a simplified version of the process with some pseudocode:\n\n. Assume we have a JWT with a JSON `header` and a simple text message payload:\n+\n*header*\n+\n----\n{\n  \"alg\": \"none\"\n}\n----\n+\n*payload*\n+\n----\nThe true sign of intelligence is not knowledge but imagination.\n----\n\n. Remove all unnecessary whitespace in the JSON:\n+\n[,groovy]\n----\nString header = '{\"alg\":\"none\"}'\nString payload = 'The true sign of intelligence is not knowledge but imagination.'\n----\n\n. Get the UTF-8 bytes and Base64URL-encode each:\n+\n[,groovy]\n----\nString encodedHeader = base64URLEncode( header.getBytes(\"UTF-8\") )\nString encodedPayload = base64URLEncode( payload.getBytes(\"UTF-8\") )\n----\n\n. Join the encoded header and claims with period ('.') characters:\n+\n[,groovy]\n----\nString compact = encodedHeader + '.' + encodedPayload + '.'\n----\n\nThe final concatenated `compact` JWT String looks like this:\n\n----\neyJhbGciOiJub25lIn0.VGhlIHRydWUgc2lnbiBvZiBpbnRlbGxpZ2VuY2UgaXMgbm90IGtub3dsZWRnZSBidXQgaW1hZ2luYXRpb24u.\n----\n\nThis is called an 'unprotected' JWT because no security was involved - no digital signatures or encryption to\n'protect' the JWT to ensure it cannot be changed by 3rd parties.\n\nIf we wanted to digitally sign the compact form so that we could at least guarantee that no-one changes the data\nwithout us detecting it, we'd have to perform a few more steps, shown next.\n\n+++<a name=\"overview-example-jws\">++++++</a>+++\n\n=== JWS Example\n\nInstead of a plain text payload, the next example will use probably the most common type of payload - a JSON claims\n`Object` containing information about a particular identity.  We'll also digitally sign the JWT to ensure it\ncannot be changed by a 3rd party without us knowing.\n\n. Assume we have a JSON `header` and a claims `payload`:\n+\n*header*\n+\n[,json]\n----\n{\n  \"alg\": \"HS256\"\n}\n----\n+\n*payload*\n+\n[,json]\n----\n{\n  \"sub\": \"Joe\"\n}\n----\n+\nIn this case, the `header` indicates that the `HS256` (HMAC using SHA-256) algorithm will be used to cryptographically sign\nthe JWT. Also, the `payload` JSON object has a single claim, `sub` with value `Joe`.\n+\nThere are a number of standard claims, called https://tools.ietf.org/html/rfc7519#section-4.1[Registered Claims],\nin the specification and `sub` (for 'Subject') is one of them.\n\n. Remove all unnecessary whitespace in both JSON objects:\n+\n[,groovy]\n----\nString header = '{\"alg\":\"HS256\"}'\nString claims = '{\"sub\":\"Joe\"}'\n----\n\n. Get their UTF-8 bytes and Base64URL-encode each:\n+\n[,groovy]\n----\nString encodedHeader = base64URLEncode( header.getBytes(\"UTF-8\") )\nString encodedClaims = base64URLEncode( claims.getBytes(\"UTF-8\") )\n----\n\n. Concatenate the encoded header and claims with a period character '.' delimiter:\n+\n[,groovy]\n----\nString concatenated = encodedHeader + '.' + encodedClaims\n----\n\n. Use a sufficiently-strong cryptographic secret or private key, along with a signing algorithm of your choice\n (we'll use HMAC-SHA-256 here), and sign the concatenated string:\n+\n[,groovy]\n----\n SecretKey key = getMySecretKey()\n byte[] signature = hmacSha256( concatenated, key )\n----\n\n. Because signatures are always byte arrays, Base64URL-encode the signature and join it to the `concatenated` string\nwith a period character '.' delimiter:\n+\n[,groovy]\n----\nString compact = concatenated + '.' + base64URLEncode( signature )\n----\n\nAnd there you have it, the final `compact` String looks like this:\n\n----\neyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60AlD4\n----\n\nThis is called a 'JWS' - short for _signed_ JWT.\n\nOf course, no one would want to do this manually in code, and worse, if you get anything wrong, you could introduce\nserious security problems and weaknesses.  As a result, JJWT was created to handle all of this for you: JJWT completely\nautomates both the creation of JWSs and the parsing and verification of JWSs for you.\n\n+++<a name=\"overview-example-jwe\">++++++</a>+++\n\n=== JWE Example\n\nSo far we have seen an unprotected JWT and a cryptographically signed JWT (called a 'JWS').  One of the things\nthat is inherent to both of these two is that all the information within them can be seen by anyone - all the data in\nboth the header and the payload is publicly visible.  JWS just ensures the data hasn't been changed by anyone -\nit doesn't prevent anyone from seeing it.  Many times, this is just fine because the data within them is not\nsensitive information.\n\nBut what if you needed to represent information in a JWT that _is_ considered sensitive information - maybe someone's\npostal address or social security number or bank account number?\n\nIn these cases, we'd want a fully-encrypted JWT, called a 'JWE' for short.  A JWE uses cryptography to ensure that the\npayload remains fully encrypted _and_ authenticated so unauthorized parties cannot see data within, nor change the data\nwithout being detected.  Specifically, the JWE specification requires that\nhttps://en.wikipedia.org/wiki/Authenticated_encryption#Authenticated_encryption_with_associated_data_(AEAD)[Authenticated Encryption with Associated Data]\nalgorithms are used to fully encrypt and protect data.\n\nA full overview of AEAD algorithms are out of scope for this documentation, but here's an example of a final compact\nJWE that utilizes these algorithms (line breaks are for readability only):\n\n----\neyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.\n6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ.\nAxY8DCtDaGlsbGljb3RoZQ.\nKDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.\nU0m_YmjN04DJvceFICbCVQ\n----\n\nNext we'll cover how to install JJWT in your project, and then we'll see how to use JJWT's nice fluent API instead\nof risky string manipulation to quickly and safely build JWTs, JWSs, and JWEs.\n\n+++<a name=\"install\">++++++</a>+++\n\n== Installation\n\nUse your favorite Maven-compatible build tool to pull the dependencies from Maven Central.\n\nThe dependencies could differ slightly if you are working with a <<install-jdk,JDK project>> or an\n<<install-android,Android project>>.\n\n+++<a name=\"install-jdk\">++++++</a>+++\n\n=== JDK Projects\n\nIf you're building a (non-Android) JDK project, you will want to define the following dependencies:\n\n+++<a name=\"install-jdk-maven\">++++++</a>+++\n\n==== Maven\n\n[,xml,subs=\"+attributes\"]\n----\n<dependency>\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-api</artifactId>\n    <version>{project-version}</version>\n</dependency>\n<dependency>\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-impl</artifactId>\n    <version>{project-version}</version>\n    <scope>runtime</scope>\n</dependency>\n<dependency>\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-jackson</artifactId> <!-- or jjwt-gson if Gson is preferred -->\n    <version>{project-version}</version>\n    <scope>runtime</scope>\n</dependency>\n<!-- Uncomment this next dependency if you are using:\n     - JDK 10 or earlier, and you want to use RSASSA-PSS (PS256, PS384, PS512) signature algorithms.\n     - JDK 10 or earlier, and you want to use EdECDH (X25519 or X448) Elliptic Curve Diffie-Hellman encryption.\n     - JDK 14 or earlier, and you want to use EdDSA (Ed25519 or Ed448) Elliptic Curve signature algorithms.\n     It is unnecessary for these algorithms on JDK 15 or later.\n<dependency>\n    <groupId>org.bouncycastle</groupId>\n    <artifactId>bcprov-jdk18on</artifactId> or bcprov-jdk15to18 on JDK 7\n    <version>1.76</version>\n    <scope>runtime</scope>\n</dependency>\n-->\n----\n\n+++<a name=\"install-jdk-gradle\">++++++</a>+++\n\n==== Gradle\n\n[,groovy,subs=\"+attributes\"]\n----\ndependencies {\n    implementation 'io.jsonwebtoken:jjwt-api:{project-version}'\n    runtimeOnly 'io.jsonwebtoken:jjwt-impl:{project-version}'\n    runtimeOnly 'io.jsonwebtoken:jjwt-jackson:{project-version}' // or 'io.jsonwebtoken:jjwt-gson:{project-version}' for gson\n    /*\n      Uncomment this next dependency if you are using:\n       - JDK 10 or earlier, and you want to use RSASSA-PSS (PS256, PS384, PS512) signature algorithms.\n       - JDK 10 or earlier, and you want to use EdECDH (X25519 or X448) Elliptic Curve Diffie-Hellman encryption.\n       - JDK 14 or earlier, and you want to use EdDSA (Ed25519 or Ed448) Elliptic Curve signature algorithms.\n      It is unnecessary for these algorithms on JDK 15 or later.\n    */\n    // runtimeOnly 'org.bouncycastle:bcprov-jdk18on:1.76' // or bcprov-jdk15to18 on JDK 7\n}\n----\n\n+++<a name=\"install-android\">++++++</a>+++\n\n=== Android Projects\n\nAndroid projects will want to define the following dependencies and Proguard exclusions, and optional\nBouncyCastle `Provider`:\n\n+++<a name=\"install-android-dependencies\">++++++</a>+++\n\n==== Dependencies\n\nAdd the dependencies to your project:\n\n[,groovy,subs=\"+attributes\"]\n----\ndependencies {\n    api('io.jsonwebtoken:jjwt-api:{project-version}')\n    runtimeOnly('io.jsonwebtoken:jjwt-impl:{project-version}')\n    runtimeOnly('io.jsonwebtoken:jjwt-orgjson:{project-version}') {\n        exclude(group: 'org.json', module: 'json') //provided by Android natively\n    }\n    /*\n      Uncomment this next dependency if you want to use:\n       - RSASSA-PSS (PS256, PS384, PS512) signature algorithms.\n       - EdECDH (X25519 or X448) Elliptic Curve Diffie-Hellman encryption.\n       - EdDSA (Ed25519 or Ed448) Elliptic Curve signature algorithms.\n      ** AND ALSO ensure you enable the BouncyCastle provider as shown below **\n    */\n    //implementation('org.bouncycastle:bcprov-jdk18on:1.76') // or bcprov-jdk15to18 for JDK 7\n}\n----\n\n+++<a name=\"install-android-proguard\">++++++</a>+++\n\n==== Proguard\n\nYou can use the following https://developer.android.com/studio/build/shrink-code[Android Proguard] exclusion rules:\n\n----\n-keepattributes InnerClasses\n\n-keep class io.jsonwebtoken.** { *; }\n-keepnames class io.jsonwebtoken.* { *; }\n-keepnames interface io.jsonwebtoken.* { *; }\n\n-keep class org.bouncycastle.** { *; }\n-keepnames class org.bouncycastle.** { *; }\n-dontwarn org.bouncycastle.**\n----\n\n+++<a name=\"install-android-bc\">++++++</a>+++\n\n==== Bouncy Castle\n\nIf you want to use JWT RSASSA-PSS algorithms (i.e. `PS256`, `PS384`, and `PS512`), EdECDH (`X25512` or `X448`)\nElliptic Curve Diffie-Hellman encryption, EdDSA (`Ed25519` or `Ed448`) signature algorithms, or you just want to\nensure your Android application is running an updated version of BouncyCastle, you will need to:\n\n. Uncomment the BouncyCastle dependency as commented above in the <<install-android-dependencies,dependencies>> section.\n. Replace the legacy Android custom `BC` provider with the updated one.\n\nProvider registration needs to be done _early_ in the application's lifecycle, preferably in your application's\nmain `Activity` class as a static initialization block.  For example:\n\n[,kotlin]\n----\nclass MainActivity : AppCompatActivity() {\n\n    companion object {\n        init {\n            Security.removeProvider(\"BC\") //remove old/legacy Android-provided BC provider\n            Security.addProvider(BouncyCastleProvider()) // add 'real'/correct BC provider\n        }\n    }\n\n    // ... etc ...\n}\n----\n\n+++<a name=\"install-understandingdependencies\">++++++</a>+++\n\n=== Understanding JJWT Dependencies\n\nNotice the above JJWT dependency declarations all have only one compile-time dependency and the rest are declared as\n_runtime_ dependencies.\n\nThis is because JJWT is designed so you only depend on the APIs that are explicitly designed for you to use in\nyour applications and all other internal implementation details - that can change without warning - are relegated to\nruntime-only dependencies.  This is an extremely important point if you want to ensure stable JJWT usage and\nupgrades over time:\n\n[WARNING]\n====\nJJWT guarantees semantic versioning compatibility for all of its artifacts _except_ the `jjwt-impl` .jar.  No such\nguarantee is made for the `jjwt-impl` .jar and internal changes in that .jar can happen at any time.  Never add the\n`jjwt-impl` .jar to your project with `compile` scope - always declare it with `runtime` scope.\n====\n\nThis is done to benefit you: great care goes into curating the `jjwt-api` .jar and ensuring it contains what you need\nand remains backwards compatible as much as is possible so you can depend on that safely with compile scope.  The\nruntime `jjwt-impl` .jar strategy affords the JJWT developers the flexibility to change the internal packages and\nimplementations whenever and however necessary.  This helps us implement features, fix bugs, and ship new releases to\nyou more quickly and efficiently.\n\n+++<a name=\"quickstart\">++++++</a>+++\n\n== Quickstart\n\nMost complexity is hidden behind a convenient and readable builder-based\nhttp://en.wikipedia.org/wiki/Fluent_interface[fluent interface], great for relying on IDE auto-completion to write\ncode quickly.  Here's an example:\n\n[,java]\n----\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.security.Keys;\nimport javax.crypto.SecretKey;\n\n// We need a signing key, so we'll create one just for this example. Usually\n// the key would be read from your application configuration instead.\nSecretKey key = Jwts.SIG.HS256.key().build();\n\nString jws = Jwts.builder().subject(\"Joe\").signWith(key).compact();\n----\n\nHow easy was that!?\n\nIn this case, we are:\n\n. _building_ a JWT that will have the\nhttps://tools.ietf.org/html/rfc7519#section-4.1[registered claim] `sub` (Subject) set to `Joe`. We are then\n. _signing_ the JWT using a key suitable for the HMAC-SHA-256 algorithm.  Finally, we are\n. _compacting_ it into its final `String` form.  A signed JWT is called a 'JWS'.\n\nThe resultant `jws` String looks like this:\n\n----\neyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJKb2UifQ.1KP0SsvENi7Uz1oQc07aXTL7kpQG5jBNIybqr60AlD4\n----\n\nNow let's verify the JWT (you should always discard JWTs that don't match an expected signature):\n\n[,java]\n----\nassert Jwts.parser().verifyWith(key).build().parseSignedClaims(jws).getPayload().getSubject().equals(\"Joe\");\n----\n\nThere are two things going on here. The `key` from before is being used to verify the signature of the JWT. If it\nfails to verify the JWT, a `SignatureException` (which extends `JwtException`) is thrown. Assuming the JWT is\nverified, we parse the claims and assert that that subject is set to `Joe`.  You have to love code one-liners\nthat pack a punch!\n\n[NOTE]\n====\n*Type-safe JWTs:* To get a type-safe `Claims` JWT result, call the `parseSignedClaims` method (since there are many\nsimilar methods available). You will get an `UnsupportedJwtException` if you parse your JWT with wrong method.\n====\n\nBut what if parsing or signature validation failed?  You can catch `JwtException` and react accordingly:\n\n[,java]\n----\ntry {\n\n    Jwts.parser().verifyWith(key).build().parseSignedClaims(compactJws);\n\n    //OK, we can trust this JWT\n\n} catch (JwtException e) {\n\n    //don't trust the JWT!\n}\n----\n\nNow that we've had a quickstart 'taste' of how to create and parse JWTs, let's cover JJWT's API in-depth.\n\n+++<a name=\"jwt-create\">++++++</a>+++\n\n== Creating a JWT\n\nYou create a JWT as follows:\n\n. Use the `Jwts.builder()` method to create a `JwtBuilder` instance.\n. Optionally set any <<jwt-header,`header` parameters>> as desired.\n. Call builder methods to set the payload <<jwt-content,content>> or <<jwt-claims,claims>>.\n. Optionally call `signWith` or `encryptWith` methods if you want to digitally sign or encrypt the JWT.\n. Call the `compact()` method to produce the resulting compact JWT string.\n\nFor example:\n\n[,java]\n----\nString jwt = Jwts.builder()                     // (1)\n\n    .header()                                   // (2) optional\n        .keyId(\"aKeyId\")\n        .and()\n\n    .subject(\"Bob\")                             // (3) JSON Claims, or\n    //.content(aByteArray, \"text/plain\")        //     any byte[] content, with media type\n\n    .signWith(signingKey)                       // (4) if signing, or\n    //.encryptWith(key, keyAlg, encryptionAlg)  //     if encrypting\n\n    .compact();                                 // (5)\n----\n\n* The JWT `payload` may be either `byte[]` content (via `content`) _or_ JSON Claims\n(such as `subject`, `claims`, etc), but not both.\n* Either digital signatures (`signWith`) or encryption (`encryptWith`) may be used, but not both.\n\n[WARNING]\n====\n*Unprotected JWTs*: If you do not use the `signWith` or `encryptWith` builder methods, *an Unprotected JWT will be\ncreated, which offers no security protection at all*.  If you need security protection, consider either\n<<jws,digitally signing>> or <<jwe,encrypting>> the JWT before calling the `compact()` builder method.\n====\n\n+++<a name=\"jwt-header\">++++++</a>++++++<a name=\"jws-create-header\">++++++</a>+++\n// legacy anchors for old links\n\n=== JWT Header\n\nA JWT header is a JSON `Object` that provides metadata about the contents, format, and any cryptographic operations\nrelevant to the JWT `payload`.  JJWT provides a number of ways of setting the entire header and/or multiple individual\nheader parameters (name/value pairs).\n\n+++<a name=\"jwt-header-builder\">++++++</a>++++++<a name=\"jws-create-header-instance\">++++++</a>+++\n// legacy anchors for old links\n\n==== JwtBuilder Header\n\nThe easiest and recommended way to set one or more JWT header parameters (name/value pairs) is to use the\n``JwtBuilder``'s `header()` builder as desired, and then call its `and()` method to return back\nto the `JwtBuilder` for further configuration. For example:\n\n[,java]\n----\nString jwt = Jwts.builder()\n\n    .header()                        // <----\n        .keyId(\"aKeyId\")\n        .x509Url(aUri)\n        .add(\"someName\", anyValue)\n        .add(mapValues)\n        // ... etc ...\n        .and()                      // go back to the JwtBuilder\n\n    .subject(\"Joe\")                 // resume JwtBuilder calls...\n    // ... etc ...\n    .compact();\n----\n\nThe `JwtBuilder` `header()` builder also supports automatically calculating X.509 thumbprints and other builder-style benefits that\na simple property getter/setter object would not do.\n\n[NOTE]\n====\n*Automatic Headers*: You do not need to set the `alg`, `enc` or `zip` headers - JJWT will always set them\nautomatically as needed.\n====\n\n+++<a name=\"jwt-header-params\">++++++</a>+++\n\n===== Custom Header Parameters\n\nIn addition to type-safe builder methods for standard header parameters, `JwtBuilder.header()` can also support\narbitrary name/value pairs via the `add` method:\n\n[,java]\n----\nJwts.builder()\n\n    .header()\n        .add(\"aHeaderName\", aValue)\n        // ... etc ...\n        .and() // return to the JwtBuilder\n\n// ... etc ...\n----\n\n+++<a name=\"jwt-header-map\">++++++</a>++++++<a name=\"jws-create-header-map\">++++++</a>+++\n// legacy anchors for old links\n\n===== Header Parameter Map\n\nThe `add` method is also overloaded to support multiple parameters in a `Map`:\n\n[,java]\n----\nJwts.builder()\n\n    .header()\n        .add(multipleHeaderParamsMap)\n        // ... etc ...\n        .and() // return to the JwtBuilder\n\n// ... etc ...\n----\n\n==== Jwts HeaderBuilder\n\nUsing `Jwts.builder().header()` shown above is the preferred way to modify a header when using the `JwtBuilder`.\n\nHowever, if you would like to create a 'standalone' `Header` outside of the context of using the `JwtBuilder`, you\ncan use `Jwts.header()` instead to return an independent `Header` builder.  For example:\n\n[,java]\n----\nHeader header = Jwts.header()\n\n        .keyId(\"aKeyId\")\n        .x509Url(aUri)\n        .add(\"someName\", anyValue)\n        .add(mapValues)\n        // ... etc ...\n\n        .build()  // <---- not 'and()'\n----\n\nThere are only two differences between `Jwts.header()` and `Jwts.builder().header()`:\n\n. `Jwts.header()` builds a 'detached' `Header` that is not associated with any particular JWT, whereas\n`Jwts.builder().header()` always modifies the header of the immediate JWT being constructed by its parent\n`JwtBuilder`.\n. `Jwts.header()` has a `build()` method to produce an explicit `Header` instance and\n`Jwts.builder().header()` does not (it has an `and()` method instead) because its parent `JwtBuilder` will implicitly\ncreate the header instance when necessary.\n\nA standalone header might be useful if you want to aggregate common header parameters in a single 'template'\ninstance so you don't have to repeat them for each `JwtBuilder` usage.  Then this 'template' `Header` can be used to\npopulate `JwtBuilder` usages by just appending it to the `JwtBuilder` header, for example:\n\n[,java]\n----\n// perhaps somewhere in application configuration:\nHeader commonHeaders = Jwts.header()\n    .issuer(\"My Company\")\n    // ... etc ...\n    .build();\n\n// --------------------------------\n\n// somewhere else during actual Jwt construction:\nString jwt = Jwts.builder()\n\n    .header()\n        .add(commonHeaders)                   // <----\n        .add(\"specificHeader\", specificValue) // jwt-specific headers...\n        .and()\n\n    .subject(\"whatever\")\n    // ... etc ...\n    .compact();\n----\n\n+++<a name=\"jwt-payload\">++++++</a>+++\n\n=== JWT Payload\n\nA JWT `payload` can be anything at all - anything that can be represented as a byte array, such as text, images,\ndocuments, and more.  But since a JWT `header` is always JSON, it makes sense that the `payload` could also be JSON,\nespecially for representing identity claims.\n\nAs a result, the `JwtBuilder` supports two distinct payload options:\n\n* `content` if you would like the payload to be arbitrary byte array content, or\n* `claims` (and supporting helper methods) if you would like the payload to be a JSON Claims `Object`.\n\nEither option may be used, but not both. Using both will cause `compact()` to throw an exception.\n\n+++<a name=\"jwt-content\">++++++</a>+++\n\n==== Arbitrary Content\n\nYou can set the JWT payload to be any arbitrary byte array content by using the `JwtBuilder` `content` method.\nFor example:\n\n[,java]\n----\nbyte[] content = \"Hello World\".getBytes(StandardCharsets.UTF_8);\n\nString jwt = Jwts.builder()\n\n    .content(content, \"text/plain\") // <---\n\n    // ... etc ...\n\n    .build();\n----\n\nNotice this particular example of `content` uses the two-argument convenience variant:\n\n. The first argument is the actual byte content to set as the JWT payload\n. The second argument is a String identifier of an IANA Media Type.\n\nThe second argument will cause the `JwtBuilder` to automatically set the `cty` (Content Type) header according to the\nJWT specification's https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10[recommended compact format].\n\nThis two-argument variant is typically recommended over the single-argument `content(byte[])` method because it\nguarantees the JWT recipient can inspect the `cty` header to determine how to convert the `payload` byte array into\na final form that the application can use.\n\nWithout setting the `cty` header, the JWT recipient _must_ know via out-of-band (external) information how to process\nthe byte array, which is usually less convenient and always requires code changes if the content format ever changes.\nFor these reasons, it is strongly recommended to use the two-argument `content` method variant.\n\n+++<a name=\"jwt-claims\">++++++</a>++++++<a name=\"jws-create-claims\">++++++</a>+++\n// legacy anchors for old links\n\n==== JWT Claims\n\nInstead of a content byte array, a JWT payload may contain assertions or claims for a JWT recipient. In\nthis case, the payload is a `Claims` JSON `Object`, and JJWT supports claims creation with type-safe\nbuilder methods.\n\n+++<a name=\"jwt-claims-standard\">++++++</a>++++++<a name=\"jws-create-claims-standard\">++++++</a>+++\n// legacy anchors for old links\n\n===== Standard Claims\n\nThe `JwtBuilder` provides convenient builder methods for standard registered Claim names defined in the JWT\nspecification.  They are:\n\n* `issuer`: sets the https://tools.ietf.org/html/rfc7519#section-4.1.1[`iss` (Issuer) Claim]\n* `subject`: sets the https://tools.ietf.org/html/rfc7519#section-4.1.2[`sub` (Subject) Claim]\n* `audience`: sets the https://tools.ietf.org/html/rfc7519#section-4.1.3[`aud` (Audience) Claim]\n* `expiration`: sets the https://tools.ietf.org/html/rfc7519#section-4.1.4[`exp` (Expiration Time) Claim]\n* `notBefore`: sets the https://tools.ietf.org/html/rfc7519#section-4.1.5[`nbf` (Not Before) Claim]\n* `issuedAt`: sets the https://tools.ietf.org/html/rfc7519#section-4.1.6[`iat` (Issued At) Claim]\n* `id`: sets the https://tools.ietf.org/html/rfc7519#section-4.1.7[`jti` (JWT ID) Claim]\n\nFor example:\n\n[,java]\n----\n\nString jws = Jwts.builder()\n\n    .issuer(\"me\")\n    .subject(\"Bob\")\n    .audience().add(\"you\").and()\n    .expiration(expiration) //a java.util.Date\n    .notBefore(notBefore) //a java.util.Date\n    .issuedAt(new Date()) // for example, now\n    .id(UUID.randomUUID().toString()) //just an example id\n\n    /// ... etc ...\n----\n\n+++<a name=\"jwt-claims-custom\">++++++</a>++++++<a name=\"jws-create-claims-custom\">++++++</a>+++\n// legacy anchors for old links\n\n===== Custom Claims\n\nIf you need to set one or more custom claims that don't match the standard setter method claims shown above, you\ncan simply call the `JwtBuilder` `claim` method one or more times as needed:\n\n[,java]\n----\nString jws = Jwts.builder()\n\n    .claim(\"hello\", \"world\")\n\n    // ... etc ...\n----\n\nEach time `claim` is called, it simply appends the key-value pair to an internal `Claims` builder, potentially\noverwriting any existing identically-named key/value pair.\n\nObviously, you do not need to call `claim` for any <<jws-create-claims-standard,standard claim name>>, and it is\nrecommended instead to call the standard respective type-safe named builder method as this enhances readability.\n\n+++<a name=\"jws-create-claims-instance\">++++++</a>+++\n// legacy anchors for old links\n+++<a name=\"jwt-claims-instance\">++++++</a>+++\n+++<a name=\"jwt-claims-map\">++++++</a>++++++<a name=\"jws-create-claims-map\">++++++</a>+++\n// legacy anchors for old links\n\n===== Claims Map\n\nIf you want to add multiple claims at once, you can use `JwtBuilder` `claims(Map)` method:\n\n[,java]\n----\n\nMap<String,?> claims = getMyClaimsMap(); //implement me\n\nString jws = Jwts.builder()\n\n    .claims(claims)\n\n    // ... etc ...\n----\n\n+++<a name=\"jwt-compression\">++++++</a>++++++<a name=\"jws-create-compression\">++++++</a>+++\n// legacy anchors for old links\n\n=== JWT Compression\n\nIf your JWT payload is large (contains a lot of data), you might want to compress the JWT to reduce its size.  Note\nthat this is _not_ a standard feature for all JWTs - only JWEs - and is not likely to be supported by other JWT\nlibraries for non-JWE tokens.  JJWT supports compression for both JWSs and JWEs, however.\n\nPlease see the main <<compression,Compression>> section to see how to compress and decompress JWTs.\n\n+++<a name=\"jwt-read\">++++++</a>+++\n\n== Reading a JWT\n\nYou read (parse) a JWT as follows:\n\n. Use the `Jwts.parser()` method to create a `JwtParserBuilder` instance.\n. Optionally call `keyLocator`, `verifyWith` or `decryptWith` methods if you expect to parse <<jws,signed>> or <<jwe,encrypted>> JWTs.\n. Call the `build()` method on the `JwtParserBuilder` to create and return a thread-safe `JwtParser`.\n. Call one of the various `parse*` methods with your compact JWT string, depending on the type of JWT you expect.\n. Wrap the `parse*` call in a try/catch block in case parsing, signature verification, or decryption fails.\n\nFor example:\n\n[,java]\n----\nJwt<?,?> jwt;\n\ntry {\n    jwt = Jwts.parser()     // (1)\n\n    .keyLocator(keyLocator) // (2) dynamically locate signing or encryption keys\n    //.verifyWith(key)      //     or a constant key used to verify all signed JWTs\n    //.decryptWith(key)     //     or a constant key used to decrypt all encrypted JWTs\n\n    .build()                // (3)\n\n    .parse(compact);        // (4) or parseSignedClaims, parseEncryptedClaims, parseSignedContent, etc\n\n    // we can safely trust the JWT\n\ncatch (JwtException ex) {   // (5)\n\n    // we *cannot* use the JWT as intended by its creator\n}\n----\n\n[NOTE]\n====\n*Type-safe JWTs:* If you are certain your parser will only ever encounter a specific kind of JWT (for example, you only\never use signed JWTs with `Claims` payloads, or encrypted JWTs with `byte[]` content payloads, etc), you can call the\nassociated type-safe `parseSignedClaims`, `parseEncryptedClaims`, (etc) method variant instead of the generic `parse` method.\n\nThese `parse*` methods will return the type-safe JWT you are expecting, for example, a `Jws<Claims>` or `Jwe<byte[]>`\ninstead of a generic `Jwt<?,?>` instance.\n====\n\n+++<a name=\"jwt-read-key\">++++++</a>+++\n\n=== Constant Parsing Key\n\nIf the JWT parsed is a JWS or JWE, a key will be necessary to verify the signature or decrypt it.  If a JWS and\nsignature verification fails, or if a JWE and decryption fails, the JWT cannot be safely trusted and should be\ndiscarded.\n\nSo which key do we use?\n\n* If parsing a JWS and the JWS was signed with a `SecretKey`, the same `SecretKey` should be specified on the\n`JwtParserBuilder`.  For example:\n+\n[,java]\n----\nJwts.parser()\n\n  .verifyWith(secretKey) // <----\n\n  .build()\n  .parseSignedClaims(jwsString);\n----\n\n* If parsing a JWS and the JWS was signed with a `PrivateKey`, that key's corresponding `PublicKey` (not the\n`PrivateKey`) should be specified on the `JwtParserBuilder`.  For example:\n+\n[,java]\n----\nJwts.parser()\n\n  .verifyWith(publicKey) // <---- publicKey, not privateKey\n\n  .build()\n  .parseSignedClaims(jwsString);\n----\n\n* If parsing a JWE and the JWE was encrypted with direct encryption using a `SecretKey`, the same `SecretKey` should be\nspecified on the `JwtParserBuilder`. For example:\n+\n[,java]\n----\nJwts.parser()\n\n  .decryptWith(secretKey) // <---- or a Password from Keys.password(charArray)\n\n  .build()\n  .parseEncryptedClaims(jweString);\n----\n\n* If parsing a JWE and the JWE was encrypted with a key algorithm using with a `PublicKey`, that key's corresponding\n`PrivateKey` (not the `PublicKey`) should be specified on the `JwtParserBuilder`.  For example:\n+\n[,java]\n----\nJwts.parser()\n\n  .decryptWith(privateKey) // <---- privateKey, not publicKey\n\n  .build()\n  .parseEncryptedClaims(jweString);\n----\n\n==== Multiple Keys?\n\nBut you might have noticed something - what if your application doesn't use just a single `SecretKey` or `KeyPair`? What\nif JWSs and JWEs can be created with different ``SecretKey``s or public/private keys, or a combination of both?  How do\nyou know which key to specify if you don't inspect the JWT first?\n\nIn these cases, you can't call the ``JwtParserBuilder``'s `verifyWith` or `decryptWith` methods with a single key -\ninstead, you'll need to configure a parsing Key Locator, discussed next.\n\n+++<a name=\"key-locator\">++++++</a>+++\n\n=== Dynamic Key Lookup\n\nIt is common in many applications to receive JWTs that can be encrypted or signed by different cryptographic keys.  For\nexample, maybe a JWT created to assert a specific user identity uses a Key specific to that exact user. Or perhaps JWTs\nspecific to a particular customer all use that customer's Key.  Or maybe your application creates JWTs that are\nencrypted with a key specific to your application for your own use (e.g. a user session token).\n\nIn all of these and similar scenarios, you won't know which key was used to sign or encrypt a JWT until the JWT is\nreceived, at parse time, so you can't 'hard code' any verification or decryption key using the ``JwtParserBuilder``'s\n`verifyWith` or `decryptWith` methods.  Those are only to be used when the same key is used to verify or decrypt\n_all_ JWSs or JWEs, which won't work for dynamically signed or encrypted JWTs.\n\n+++<a name=\"key-locator-custom\">++++++</a>+++\n\n==== Key Locator\n\nIf you need to support dynamic key lookup when encountering JWTs, you'll need to implement\nthe `Locator<Key>` interface and specify an instance on the `JwtParserBuilder` via the `keyLocator` method. For\nexample:\n\n[,java]\n----\nLocator<Key> keyLocator = getMyKeyLocator();\n\nJwts.parser()\n\n    .keyLocator(keyLocator) // <----\n\n    .build()\n    // ... etc ...\n----\n\nA `Locator<Key>` is used to lookup _both_ JWS signature verification keys _and_ JWE decryption keys.  You need to\ndetermine which key to return based on information in the JWT `header`, for example:\n\n[,java]\n----\npublic class MyKeyLocator extends LocatorAdapter<Key> {\n\n    @Override\n    public Key locate(ProtectedHeader<?> header) { // a JwsHeader or JweHeader\n        // implement me\n    }\n}\n----\n\nThe `JwtParser` will invoke the `locate` method after parsing the JWT `header`, but _before parsing the `payload`,\nor verifying any JWS signature or decrypting any JWE ciphertext_. This allows you to inspect the `header` argument\nfor any information that can help you look up the `Key` to use for verifying _that specific jwt_.  This is very\npowerful for applications with more complex security models that might use different keys at different times or for\ndifferent users or customers.\n\n+++<a name=\"key-locator-strategy\">++++++</a>+++\n\n==== Key Locator Strategy\n\nWhat data might you inspect to determine how to lookup a signature verification or decryption key?\n\nThe JWT specifications' preferred approach is to set a `kid` (Key ID) header value when the JWT is being created,\nfor example:\n\n[,java]\n----\nKey key = getSigningKey(); // or getEncryptionKey() for JWE\n\nString keyId = getKeyId(key); //any mechanism you have to associate a key with an ID is fine\n\nString jws = Jwts.builder()\n\n    .header().keyId(keyId).and()               // <--- add `kid` header\n\n    .signWith(key)                             // for JWS\n    //.encryptWith(key, keyAlg, encryptionAlg) // for JWE\n    .compact();\n----\n\nThen during parsing, your `Locator<Key>` implementation can inspect the `header` to get the `kid` value and then use it\nto look up the verification or decryption key from somewhere, like a database, keystore or Hardware Security Module\n(HSM).  For example:\n\n[,java]\n----\npublic class MyKeyLocator extends LocatorAdapter<Key> {\n\n    @Override\n    public Key locate(ProtectedHeader<?> header) { // both JwsHeader and JweHeader extend ProtectedHeader\n\n        //inspect the header, lookup and return the verification key\n        String keyId = header.getKeyId(); //or any other parameter that you need to inspect\n\n        Key key = lookupKey(keyId); //implement me\n\n        return key;\n    }\n}\n----\n\nNote that inspecting the `header.getKeyId()` is just the most common approach to look up a key - you could inspect any\nnumber of header parameters to determine how to lookup the verification or decryption key.  It is all based on how\nthe JWT was created.\n\nIf you extend `LocatorAdapter<Key>` as shown above, but for some reason have different lookup strategies for\nsignature verification keys versus decryption keys, you can forego overriding the `locate(ProtectedHeader<?>)` method\nin favor of two respective `locate(JwsHeader)` and `locate(JweHeader)` methods:\n\n[,java]\n----\npublic class MyKeyLocator extends LocatorAdapter<Key> {\n\n    @Override\n    public Key locate(JwsHeader header) {\n        String keyId = header.getKeyId(); //or any other parameter that you need to inspect\n        return lookupSignatureVerificationKey(keyId); //implement me\n    }\n\n    @Override\n    public Key locate(JweHeader header) {\n        String keyId = header.getKeyId(); //or any other parameter// that you need to inspect\n        return lookupDecryptionKey(keyId); //implement me\n    }\n}\n----\n\n[NOTE]\n====\n*Simpler Lookup*: If possible, try to keep the key lookup strategy the same between JWSs and JWEs (i.e. using\nonly `locate(ProtectedHeader<?>)`), preferably using only\nthe `kid` (Key ID) header value or perhaps a public key thumbprint.  You will find the implementation is much\nsimpler and easier to maintain over time, and also creates smaller headers for compact transmission.\n====\n\n+++<a name=\"key-locator-retvals\">++++++</a>+++\n\n==== Key Locator Return Values\n\nRegardless of which implementation strategy you choose, remember to return the appropriate type of key depending\non the type of JWS or JWE algorithm used.  That is:\n\n* For JWS:\n ** For HMAC-based signature algorithms, the returned verification key should be a `SecretKey`, and,\n ** For asymmetric signature algorithms, the returned verification key should be a `PublicKey` (not a `PrivateKey`).\n* For JWE:\n ** For JWE direct encryption, the returned decryption key should be a `SecretKey`.\n ** For password-based key derivation algorithms, the returned decryption key should be a\n`io.jsonwebtoken.security.Password`.  You can create a `Password` instance by calling\n`Keys.password(char[] passwordCharacters)`.\n ** For asymmetric key management algorithms, the returned decryption key should be a `PrivateKey` (not a `PublicKey`).\n\n+++<a name=\"key-locator-provider\">++++++</a>+++\n\n==== Provider-constrained Keys\n\nIf any verification or decryption key returned from a Key `Locator` must be used with a specific security `Provider`\n(such as for PKCS11 or Hardware Security Module (HSM) keys), you must make that `Provider` available for JWT parsing\nin one of 3 ways, listed in order of recommendation and simplicity:\n\n. https://docs.oracle.com/en/java/javase/17/security/howtoimplaprovider.html#GUID-831AA25F-F702-442D-A2E4-8DA6DEA16F33[Configure the Provider in the JVM],\neither by modifying the `java.security` file or by registering the `Provider` dynamically via\nhttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/Security.html#addProvider(java.security.Provider)[Security.addProvider(Provider)].\nThis is the recommended approach so you do not need to modify code anywhere that may need to parse JWTs.\n. Set the `Provider` as the parser default by calling `JwtParserBuilder#provider(Provider)`.  This will\nensure the provider is used by default with _all_ located keys unless overridden by a key-specific Provider. This\nis only recommended when you are confident that all JWTs encountered by the parser instance will use keys\nattributed to the same `Provider`, unless overridden by a specific key.\n. Associate the `Provider` with a specific key using `Keys.builder` so it is used for that key only.  This option is\nuseful if some located keys require a specific provider, while other located keys can assume a default provider. For\nexample:\n+\n[,java]\n----\npublic Key locate(Header<?> header) {\n\n    PrivateKey /* or SecretKey */ key = findKey(header); // implement me\n\n    Provider keySpecificProvider = findKeyProvider(key); // implement me\n    if (keySpecificProvider != null) {\n        // Ensure the key-specific provider (e.g. for PKCS11 or HSM) will be used\n        // during decryption with the KeyAlgorithm in the JWE 'alg' header\n        return Keys.builder(key).provider(keySpecificProvider).build();\n    }\n\n    // otherwise default provider is fine:\n    return key;\n}\n----\n\n+++<a name=\"jwt-read-claims\">++++++</a>++++++<a name=\"jws-read-claims\">++++++</a>+++\n// legacy anchor for old links\n\n=== Claim Assertions\n\nYou can enforce that the JWT you are parsing conforms to expectations that you require and are important for your\napplication.\n\nFor example, let's say that you require that the JWT you are parsing has a specific `sub` (subject) value,\notherwise you may not trust the token.  You can do that by using one of the various `require`* methods on the\n`JwtParserBuilder`:\n\n[,java]\n----\ntry {\n    Jwts.parser().requireSubject(\"jsmith\")/* etc... */.build().parse(s);\n} catch (InvalidClaimException ice) {\n    // the sub claim was missing or did not have a 'jsmith' value\n}\n----\n\nIf it is important to react to a missing vs an incorrect value, instead of catching `InvalidClaimException`,\nyou can catch either `MissingClaimException` or `IncorrectClaimException`:\n\n[,java]\n----\ntry {\n    Jwts.parser().requireSubject(\"jsmith\")/* etc... */.build().parse(s);\n} catch(MissingClaimException mce) {\n    // the parsed JWT did not have the sub claim\n} catch(IncorrectClaimException ice) {\n    // the parsed JWT had a sub claim, but its value was not equal to 'jsmith'\n}\n----\n\nYou can also require custom claims by using the `require(claimName, requiredValue)` method - for example:\n\n[,java]\n----\ntry {\n    Jwts.parser().require(\"myClaim\", \"myRequiredValue\")/* etc... */.build().parse(s);\n} catch(InvalidClaimException ice) {\n    // the 'myClaim' claim was missing or did not have a 'myRequiredValue' value\n}\n----\n\n(or, again, you could catch either `MissingClaimException` or `IncorrectClaimException` instead).\n\nPlease see the `JwtParserBuilder` class and/or JavaDoc for a full list of the various `require`* methods you may use\nfor claims assertions.\n\n+++<a name=\"jwt-read-clock\">++++++</a>++++++<a name=\"jws-read-clock\">++++++</a>+++\n// legacy anchor for old links\n\n=== Accounting for Clock Skew\n\nWhen parsing a JWT, you might find that `exp` or `nbf` claim assertions fail (throw exceptions) because the clock on\nthe parsing machine is not perfectly in sync with the clock on the machine that created the JWT.  This can cause\nobvious problems since `exp` and `nbf` are time-based assertions, and clock times need to be reliably in sync for shared\nassertions.\n\nYou can account for these differences (usually no more than a few minutes) when parsing using the ``JwtParserBuilder``'s\n`clockSkewSeconds`. For example:\n\n[,java]\n----\nlong seconds = 3 * 60; //3 minutes\n\nJwts.parser()\n\n    .clockSkewSeconds(seconds) // <----\n\n    // ... etc ...\n    .build()\n    .parse(jwt);\n----\n\nThis ensures that minor clock differences between the machines can be ignored. Two or three minutes should be more than\nenough; it would be fairly strange if a production machine's clock was more than 5 minutes difference from most\natomic clocks around the world.\n\n+++<a name=\"jwt-read-clock-custom\">++++++</a>++++++<a name=\"jws-read-clock-custom\">++++++</a>+++\n// legacy anchor for old links\n\n==== Custom Clock Support\n\nIf the above `clockSkewSeconds` isn't sufficient for your needs, the timestamps created\nduring parsing for timestamp comparisons can be obtained via a custom time source.  Call the ``JwtParserBuilder``'s\n`clock` method with an implementation of the `io.jsonwebtoken.Clock` interface.  For example:\n\n[,java]\n----\nClock clock = new MyClock();\n\nJwts.parser().clock(myClock) //... etc ...\n----\n\nThe ``JwtParser``'s default `Clock` implementation simply returns `new Date()` to reflect the time when parsing occurs,\nas most would expect.  However, supplying your own clock could be useful, especially when writing test cases to\nguarantee deterministic behavior.\n\n+++<a name=\"jwt-read-decompression\">++++++</a>+++\n\n=== JWT Decompression\n\nIf you used JJWT to compress a JWT and you used a custom compression algorithm, you will need to tell the\n`JwtParserBuilder` how to resolve your `CompressionAlgorithm` to decompress the JWT.\n\nPlease see the <<compression,Compression>> section below to see how to decompress JWTs during parsing.\n\n+++<a name=\"jws\">++++++</a>+++\n\n== Signed JWTs\n\nThe JWT specification provides for the ability to\nhttps://en.wikipedia.org/wiki/Digital_signature[cryptographically _sign_] a JWT.  Signing a JWT:\n\n. guarantees the JWT was created by someone we know (it is authentic) as well as\n. guarantees that no-one has manipulated or changed the JWT after it was created (its integrity is maintained).\n\nThese two properties - authenticity and integrity - assure us that a JWT contains information we can trust.  If a\nJWT fails authenticity or integrity checks, we should always reject that JWT because we can't trust it.\n\nBut before we dig in to showing you how to create a JWS using JJWT, let's briefly discuss Signature Algorithms and\nKeys, specifically as they relate to the JWT specifications.  Understanding them is critical to being able to create a\nJWS properly.\n\n+++<a name=\"jws-alg\">++++++</a>+++\n\n=== Standard Signature Algorithms\n\nThe JWT specifications identify 13 standard signature algorithms - 3 secret key algorithms and 10 asymmetric\nkey algorithms:\n\n|===\n| Identifier | Signature Algorithm\n\n| `HS256`\n| HMAC using SHA-256\n\n| `HS384`\n| HMAC using SHA-384\n\n| `HS512`\n| HMAC using SHA-512\n\n| `ES256`\n| ECDSA using P-256 and SHA-256\n\n| `ES384`\n| ECDSA using P-384 and SHA-384\n\n| `ES512`\n| ECDSA using P-521 and SHA-512\n\n| `RS256`\n| RSASSA-PKCS-v1_5 using SHA-256\n\n| `RS384`\n| RSASSA-PKCS-v1_5 using SHA-384\n\n| `RS512`\n| RSASSA-PKCS-v1_5 using SHA-512\n\n| `PS256`\n| RSASSA-PSS using SHA-256 and MGF1 with SHA-256^*1*^\n\n| `PS384`\n| RSASSA-PSS using SHA-384 and MGF1 with SHA-384^*1*^\n\n| `PS512`\n| RSASSA-PSS using SHA-512 and MGF1 with SHA-512^*1*^\n\n| `EdDSA`\n| Edwards-Curve Digital Signature Algorithm (EdDSA)^*2*^\n|===\n\n^*1*.{sp}{fn-require-java15-plus}^\n\n^*2*.{sp}{fn-require-java15-plus}^\n\nThese are all represented as constants in the `io.jsonwebtoken.Jwts.SIG` registry class.\n\n+++<a name=\"jws-key\">++++++</a>+++\n\n=== Signature Algorithms Keys\n\nWhat's really important about the above standard signature algorithms - other than their security properties - is that\nthe JWT specification https://tools.ietf.org/html/rfc7518#section-3[RFC 7518, Sections 3.2 through 3.5]\n_requires_ (mandates) that you MUST use keys that are sufficiently strong for a chosen algorithm.\n\nThis means that JJWT - a specification-compliant library - will also enforce that you use sufficiently strong keys\nfor the algorithms you choose.  If you provide a weak key for a given algorithm, JJWT will reject it and throw an\nexception.\n\nThis is not because we want to make your life difficult, we promise! The reason why the JWT specification, and\nconsequently JJWT, mandates key lengths is that the security model of a particular algorithm can completely break\ndown if you don't adhere to the mandatory key properties of the algorithm, effectively having no security at all.  No\none wants completely insecure JWTs, right?  Right!\n\nSo what are the key strength requirements?\n\n+++<a name=\"jws-key-hmacsha\">++++++</a>+++\n\n==== HMAC-SHA\n\nJWT HMAC-SHA signature algorithms `HS256`, `HS384`, and `HS512` require a secret key that is _at least_ as many bits as\nthe algorithm's signature (digest) length per https://tools.ietf.org/html/rfc7518#section-3.2[RFC 7512 Section 3.2].\nThis means:\n\n* `HS256` is HMAC-SHA-256, and that produces digests that are 256 bits (32 bytes) long, so `HS256` _requires_ that you\nuse a secret key that is at least 32 bytes long.\n* `HS384` is HMAC-SHA-384, and that produces digests that are 384 bits (48 bytes) long, so `HS384` _requires_ that you\nuse a secret key that is at least 48 bytes long.\n* `HS512` is HMAC-SHA-512, and that produces digests that are 512 bits (64 bytes) long, so `HS512` _requires_ that you\nuse a secret key that is at least 64 bytes long.\n\n+++<a name=\"jws-key-rsa\">++++++</a>+++\n\n==== RSA\n\nJWT RSA signature algorithms `RS256`, `RS384`, `RS512`, `PS256`, `PS384` and `PS512` all require a minimum key length\n(aka an RSA modulus bit length) of `2048` bits per RFC 7512 Sections\nhttps://tools.ietf.org/html/rfc7518#section-3.3[3.3] and https://tools.ietf.org/html/rfc7518#section-3.5[3.5].\nAnything smaller than this (such as 1024 bits) will be rejected with an `WeakKeyException`.\n\nThat said, in keeping with best practices and increasing key lengths for security longevity, JJWT\nrecommends that you use:\n\n* at least 2048 bit keys with `RS256` and `PS256`\n* at least 3072 bit keys with `RS384` and `PS384`\n* at least 4096 bit keys with `RS512` and `PS512`\n\nThese are only JJWT suggestions and not requirements. JJWT only enforces JWT specification requirements and\nfor any RSA key, the requirement is the RSA key (modulus) length in bits MUST be >= 2048 bits.\n\n+++<a name=\"jws-key-ecdsa\">++++++</a>+++\n\n==== Elliptic Curve\n\nJWT Elliptic Curve signature algorithms `ES256`, `ES384`, and `ES512` all require a key length\n(aka an Elliptic Curve order bit length) equal to the algorithm signature's individual\n`R` and `S` components per https://tools.ietf.org/html/rfc7518#section-3.4[RFC 7512 Section 3.4].  This means:\n\n* `ES256` requires that you use a private key that is exactly 256 bits (32 bytes) long.\n* `ES384` requires that you use a private key that is exactly 384 bits (48 bytes) long.\n* `ES512` requires that you use a private key that is exactly 521 bits (65 or 66 bytes) long (depending on format).\n\n+++<a name=\"jws-key-eddsa\">++++++</a>+++\n\n==== Edwards Curve\n\nThe JWT Edwards Curve signature algorithm `EdDSA` supports two sizes of private and public ``EdECKey``s (these types\nwere introduced in Java 15):\n\n* `Ed25519` algorithm keys must be 256 bits (32 bytes) long and produce signatures 512 bits (64 bytes) long.\n* `Ed448` algorithm keys must be 456 bits (57 bytes) long and produce signatures 912 bits (114 bytes) long.\n\n+++<a name=\"jws-key-create\">++++++</a>+++\n\n==== Creating Safe Keys\n\nIf you don't want to think about bit length requirements or just want to make your life easier, JJWT has\nprovided convenient builder classes that can generate sufficiently secure keys for any given\nJWT signature algorithm you might want to use.\n\n+++<a name=\"jws-key-create-secret\">++++++</a>+++\n\n===== Secret Keys\n\nIf you want to generate a sufficiently strong `SecretKey` for use with the JWT HMAC-SHA algorithms, use the respective\nalgorithm's `key()` builder method:\n\n[,java]\n----\nSecretKey key = Jwts.SIG.HS256.key().build(); //or HS384.key() or HS512.key()\n----\n\nUnder the hood, JJWT uses the JCA default provider's `KeyGenerator` to create a secure-random key with the correct\nminimum length for the given algorithm.\n\nIf you want to specify a specific JCA `Provider` or `SecureRandom` to use during key generation, you may specify those\nas builder arguments. For example:\n\n[,java]\n----\nSecretKey key = Jwts.SIG.HS256.key().provider(aProvider).random(aSecureRandom).build();\n----\n\nIf you need to save this new `SecretKey`, you can Base64 (or Base64URL) encode it:\n\n[,java]\n----\nString secretString = Encoders.BASE64.encode(key.getEncoded());\n----\n\nEnsure you save the resulting `secretString` somewhere safe -\n<<base64-not-encryption,Base64-encoding is not encryption>>, so it's still considered sensitive information. You can\nfurther encrypt it, etc, before saving to disk (for example).\n\n+++<a name=\"jws-key-create-asym\">++++++</a>+++\n\n===== Asymmetric Keys\n\nIf you want to generate sufficiently strong Elliptic Curve or RSA asymmetric key pairs for use with JWT ECDSA or RSA\nalgorithms, use an algorithm's respective `keyPair()` builder method:\n\n[,java]\n----\nKeyPair keyPair = Jwts.SIG.RS256.keyPair().build(); //or RS384, RS512, PS256, etc...\n----\n\nOnce you've generated a `KeyPair`, you can use the private key (`keyPair.getPrivate()`) to create a JWS and the\npublic key (`keyPair.getPublic()`) to parse/verify a JWS.\n\n[NOTE]\n====\n* *The `PS256`, `PS384`, and `PS512` algorithms require JDK 11 or a compatible JCA Provider\n(like BouncyCastle) in the runtime classpath.*\n* *The `EdDSA` algorithms requires JDK 15 or a compatible JCA Provider (like BouncyCastle) in the runtime classpath.*\n\nIf you want to use either set of algorithms, and you are on an earlier JDK that does not support them,\nsee the <<Installation,Installation>> section to see how to enable BouncyCastle.  All other algorithms are\nnatively supported by the JDK.\n====\n\n+++<a name=\"jws-create\">++++++</a>+++\n\n=== Creating a JWS\n\nYou create a JWS as follows:\n\n. Use the `Jwts.builder()` method to create a `JwtBuilder` instance.\n. Call `JwtBuilder` methods to set the `payload` content or claims and any header parameters as desired.\n. Specify the `SecretKey` or asymmetric `PrivateKey` you want to use to sign the JWT.\n. Finally, call the `compact()` method to compact and sign, producing the final jws.\n\nFor example:\n\n[,java]\n----\nString jws = Jwts.builder() // (1)\n\n    .subject(\"Bob\")         // (2)\n\n    .signWith(key)          // (3) <---\n\n    .compact();             // (4)\n----\n\n+++<a name=\"jws-create-key\">++++++</a>+++\n\n==== Signing Key\n\nIt is usually recommended to specify the signing key by calling the ``JwtBuilder``'s `signWith` method and let JJWT\ndetermine the most secure algorithm allowed for the specified key.:\n\n[,java]\n----\nString jws = Jwts.builder()\n\n   // ... etc ...\n\n   .signWith(key) // <---\n\n   .compact();\n----\n\nFor example, if you call `signWith` with a `SecretKey` that is 256 bits (32 bytes) long, it is not strong enough for\n`HS384` or `HS512`, so JJWT will automatically sign the JWT using `HS256`.\n\nWhen using `signWith` JJWT will also automatically set the required `alg` header with the associated algorithm\nidentifier.\n\nSimilarly, if you called `signWith` with an RSA `PrivateKey` that was 4096 bits long, JJWT will use the `RS512`\nalgorithm and automatically set the `alg` header to `RS512`.\n\nThe same selection logic applies for Elliptic Curve ``PrivateKey``s.\n\n[NOTE]\n====\n*You cannot sign JWTs with ``PublicKey``s as this is always insecure.* JJWT will reject any specified\n`PublicKey` for signing with an `InvalidKeyException`.\n====\n\n+++<a name=\"jws-create-key-secret\">++++++</a>+++\n\n===== SecretKey Formats\n\nIf you want to sign a JWS using HMAC-SHA algorithms, and you have a secret key `String` or\nhttps://docs.oracle.com/javase/8/docs/api/java/security/Key.html#getEncoded--[encoded byte array], you will need\nto convert it into a `SecretKey` instance to use as the `signWith` method argument.\n\nIf your secret key is:\n\n* An https://docs.oracle.com/javase/8/docs/api/java/security/Key.html#getEncoded--[encoded byte array]:\n+\n[,java]\n----\nSecretKey key = Keys.hmacShaKeyFor(encodedKeyBytes);\n----\n\n* A Base64-encoded string:\n+\n[,java]\n----\nSecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64.decode(secretString));\n----\n\n* A Base64URL-encoded string:\n+\n[,java]\n----\nSecretKey key = Keys.hmacShaKeyFor(Decoders.BASE64URL.decode(secretString));\n----\n\n* A raw (non-encoded) string (e.g. a password String):\n+\n[,java]\n----\nPassword key = Keys.password(secretString.toCharArray());\n----\n\n[WARNING]\n====\nIt is almost always incorrect to call any variant of `secretString.getBytes` in any cryptographic context. +\nSafe cryptographic keys are never represented as direct (unencoded) strings.  If you have a password that should\nbe represented as a `Key` for `HMAC-SHA` algorithms, it is _strongly_ recommended to use a key derivation\nalgorithm to derive a cryptographically-strong `Key` from the password, and never use the password directly.\n====\n\n+++<a name=\"jws-create-key-algoverride\">++++++</a>+++\n\n===== SignatureAlgorithm Override\n\nIn some specific cases, you might want to override JJWT's default selected signature algorithm for a given key.\n\nFor example, if you have an RSA `PrivateKey` that is 2048 bits, JJWT would automatically choose the `RS256` algorithm.\nIf you wanted to use `RS384` or `RS512` instead, you could manually specify it with the overloaded `signWith` method\nthat accepts the `SignatureAlgorithm` as an additional argument:\n\n[,java]\n----\n\n   .signWith(privateKey, Jwts.SIG.RS512) // <---\n\n   .compact();\n----\n\nThis is allowed because the JWT specification allows any RSA algorithm strength for any RSA key >= 2048 bits.  JJWT just\nprefers `RS512` for keys >= 4096 bits, followed by `RS384` for keys >= 3072 bits and finally `RS256` for keys >= 2048\nbits.\n\n*In all cases however, regardless of your chosen algorithms, JJWT will assert that the specified key is allowed to be\nused for that algorithm when possible according to the JWT specification requirements.*\n\n+++<a name=\"jws-create-compression\">++++++</a>+++\n\n==== JWS Compression\n\nIf your JWT payload is large (contains a lot of data), and you are certain that JJWT will also be the same library\nthat reads/parses your JWS, you might want to compress the JWS to reduce its size.\n\n[WARNING]\n====\n*Not Standard for JWS*: JJWT supports compression for JWS, but it is not a standard feature for JWS.  The\nJWT RFC specifications standardize this _only_ for JWEs, and it is not likely to be supported by other JWT libraries\nfor JWS.  Use JWS compression only if you are certain that JJWT (or another library that supports JWS compression)\nwill be parsing the JWS.\n====\n\nPlease see the main <<compression,Compression>> section to see how to compress and decompress JWTs.\n\n+++<a name=\"jws-read\">++++++</a>+++\n\n=== Reading a JWS\n\nYou read (parse) a JWS as follows:\n\n. Use the `Jwts.parser()` method to create a `JwtParserBuilder` instance.\n. Call either <<key-locator,keyLocator>> or `verifyWith` methods to determine the key used to verify the JWS signature.\n. Call the `build()` method on the `JwtParserBuilder` to return a thread-safe `JwtParser`.\n. Finally, call the `parseSignedClaims(String)` method with your jws `String`, producing the original JWS.\n. The entire call is wrapped in a try/catch block in case parsing or signature validation fails.  We'll cover\nexceptions and causes for failure later.\n\nFor example:\n\n[,java]\n----\nJws<Claims> jws;\n\ntry {\n    jws = Jwts.parser()            // (1)\n\n    .keyLocator(keyLocator)        // (2) dynamically lookup verification keys based on each JWS\n    //.verifyWith(key)             //     or a static key used to verify all encountered JWSs\n\n    .build()                       // (3)\n    .parseSignedClaims(jwsString); // (4) or parseSignedContent(jwsString)\n\n    // we can safely trust the JWT\n\ncatch (JwtException ex) {          // (5)\n\n    // we *cannot* use the JWT as intended by its creator\n}\n----\n\n[NOTE]\n====\n.Type-safe JWSs\n\n* If you are expecting a JWS with a Claims `payload`, call the ``JwtParser``'s `parseSignedClaims` method.\n* If you are expecting a JWS with a content `payload`, call the ``JwtParser``'s `parseSignedContent` method.\n====\n\n+++<a name=\"jws-read-key\">++++++</a>+++\n\n==== Verification Key\n\nThe most important thing to do when reading a JWS is to specify the key used to verify the JWS's\ncryptographic signature.  If signature verification fails, the JWT cannot be safely trusted and should be\ndiscarded.\n\nSo which key do we use for verification?\n\n* If the jws was signed with a `SecretKey`, the same `SecretKey` should be specified on the `JwtParserBuilder`. +\nFor example:\n+\n[,java]\n----\nJwts.parser()\n\n  .verifyWith(secretKey) // <----\n\n  .build()\n  .parseSignedClaims(jwsString);\n----\n\n* If the jws was signed with a `PrivateKey`, that key's corresponding `PublicKey` (not the `PrivateKey`) should be\nspecified on the `JwtParserBuilder`.  For example:\n+\n[,java]\n----\nJwts.parser()\n\n  .verifyWith(publicKey) // <---- publicKey, not privateKey\n\n  .build()\n  .parseSignedClaims(jwsString);\n----\n\n+++<a name=\"jws-read-key-locator\">++++++</a>++++++<a name=\"jws-read-key-resolver\">++++++</a>+++\n// legacy anchors for old links\n\n==== Verification Key Locator\n\nBut you might have noticed something - what if your application doesn't use just a single `SecretKey` or `KeyPair`? What\nif JWSs can be created with different ``SecretKey``s or public/private keys, or a combination of both?  How do you\nknow which key to specify if you can't inspect the JWT first?\n\nIn these cases, you can't call the ``JwtParserBuilder``'s `verifyWith` method with a single key - instead, you'll need a\nKey Locator.  Please see the <<key-locator,Key Lookup>> section to see how to dynamically obtain different keys when\nparsing JWSs or JWEs.\n\n+++<a name=\"jws-read-decompression\">++++++</a>+++\n\n==== JWS Decompression\n\nIf you used JJWT to compress a JWS and you used a custom compression algorithm, you will need to tell the\n`JwtParserBuilder` how to resolve your `CompressionAlgorithm` to decompress the JWT.\n\nPlease see the <<compression,Compression>> section below to see how to decompress JWTs during parsing.\n\n+++<a name=\"jws-unencoded\">++++++</a>+++\n\n=== Unencoded Payload Option\n\nIn some cases, especially if a JWS payload is large, it could be desirable to _not_ Base64URL-encode the JWS payload,\nor even exclude the payload from the compact JWS string entirely.  The JWT RFC specifications provide support\nfor these use cases via the\nhttps://www.rfc-editor.org/rfc/rfc7797.html[JSON Web Signature (JWS) Unencoded Payload Option] specification,\nwhich JJWT supports.\n\nThis option comes with both benefits and disadvantages:\n\n==== Benefits\n\nA JWS producer can still create a JWS string to use for payload integrity verification without having to either:\n\n. Base64URL-encode the (potentially very large) payload, saving the time that could take.\n. Include the payload in the compact JWS string at all. Omitting the payload from the JWS compact string\nentirely produces smaller JWSs that can be more efficient to transfer.\n\n==== Disadvantages\n\n. Your application, and not JJWT, incurs the responsibility to ensure the payload is not modified during transmission\nso the recipient can verify the JWS signature. For example, by using a sufficiently strong TLS (https) cipher\nsuite as well as any additional care before and after transmission, since\nhttps://tozny.com/blog/end-to-end-encryption-vs-https/[TLS does not guarantee end-to-end security].\n. If you choose to include the unencoded payload in the JWS compact string, your application\nhttps://www.rfc-editor.org/rfc/rfc7797.html#section-5.2[MUST] ensure that the payload does not contain a\nperiod (`.`) character anywhere in the payload.  The JWS recipient will experience parsing errors otherwise.\n\nBefore attempting to use this option, one should be aware of the RFC's\nhttps://www.rfc-editor.org/rfc/rfc7797.html#section-8[security considerations] first.\n\n[NOTE]\n====\n.Protected JWS Only\n\nThe RFC specification defines the Unencoded Payload option for use only with JWSs. It may not be used with\nwith unprotected JWTs or encrypted JWEs.\n====\n\n+++<a name=\"jws-unencoded-detached\">++++++</a>+++\n\n==== Detached Payload Example\n\nThis example shows creating and parsing a compact JWS using an unencoded payload that is detached, i.e. where the\npayload is not embedded in the compact JWS string at all.\n\nWe need to do three things during creation:\n\n. Specify the JWS signing key; it's a JWS and still needs to be signed.\n. Specify the raw payload bytes via the ``JwtBuilder``'s `content` method.\n. Indicate that the payload should _not_ be Base64Url-encoded using the ``JwtBuilder``'s `encodePayload(false)` method.\n\n[,java]\n----\n// create a test key for this example:\nSecretKey testKey = Jwts.SIG.HS512.key().build();\n\nString message = \"Hello World. It's a Beautiful Day!\";\nbyte[] content = message.getBytes(StandardCharsets.UTF_8);\n\nString jws = Jwts.builder().signWith(testKey) // #1\n        .content(content)                     // #2\n        .encodePayload(false)                 // #3\n        .compact();\n----\n\nTo parse the resulting `jws` string, we need to do two things when creating the `JwtParser`:\n\n. Specify the signature verification key.\n. Specify the externally-transmitted unencoded payload bytes, required for signature verification.\n\n[,java]\n----\nJws<byte[]> parsed = Jwts.parser().verifyWith(testKey) // 1\n        .build()\n        .parseSignedContent(jws, content);             // 2\n\nassertArrayEquals(content, parsed.getPayload());\n----\n\n+++<a name=\"jws-unencoded-nondetached\">++++++</a>+++\n\n==== Non-Detached Payload Example\n\nThis example shows creating and parsing a compact JWS with what the RFC calls a 'non-detached' unencoded payload, i.e.\na raw string directly embedded as the payload in the compact JWS string.\n\nWe need to do three things during creation:\n\n. Specify the JWS signing key; it's a JWS and still needs to be signed.\n. Specify the raw payload string via the ``JwtBuilder``'s `content` method.  Per\nhttps://www.rfc-editor.org/rfc/rfc7797.html#section-5.2[the RFC], the payload string *_MUST NOT contain any\nperiod (`.`) characters_*.\n. Indicate that the payload should _not_ be Base64Url-encoded using the ``JwtBuilder``'s `encodePayload(false)` method.\n\n[,java]\n----\n// create a test key for this example:\nSecretKey testKey = Jwts.SIG.HS512.key().build();\n\nString claimsString = \"{\\\"sub\\\":\\\"joe\\\",\\\"iss\\\":\\\"me\\\"}\";\n\nString jws = Jwts.builder().signWith(testKey) // #1\n        .content(claimsString)                // #2\n        .encodePayload(false)                 // #3\n        .compact();\n----\n\nIf you were to print the `jws` string, you'd see something like this:\n\n----\neyJhbGciOiJIUzUxMiIsImI2NCI6ZmFsc2UsImNyaXQiOlsiYjY0Il19.{\"sub\":\"joe\",\"iss\":\"me\"}.wkoxYEd//...etc...\n----\n\nSee how the `claimsString` is embedded directly as the center `payload` token instead of a standard Base64URL value?\nThis is why no period (`.`) characters can exist in the payload.  If they did, any standard JWT parser would see more\nthan two periods total, which is required for parsing standard JWSs.\n\nTo parse the resulting `jws` string, we need to do two things when creating the `JwtParser`:\n\n. Specify the signature verification key.\n. Indicate that we want to support Unencoded Payload Option JWSs by enabling the `b64` `crit` header parameter.\n\n[,java]\n----\nJws<Claims> parsed = Jwts.parser().verifyWith(testKey) // 1\n        .critical().add(\"b64\").and()                   // 2\n        .build()\n        .parseSignedClaims(jws);\n\nassert \"joe\".equals(parsed.getPayload().getSubject());\nassert \"me\".equals(parsed.getPayload().getIssuer());\n----\n\nDid you notice we used the `.parseSignedClaims(String)` method instead of `.parseSignedClaims(String, byte[])`? This is\nbecause the non-detached payload is already present and JJWT has what it needs for signature verification.\n\nAdditionally, we needed to specify the `b64` critical value:  because we're not using the two-argument\n`parseSignedClaims(jws, content)` method, the parser has no way of knowing if you wish to allow or support unencoded\npayloads. Unencoded payloads have additional security considerations as described above, so they are disabled by\nthe parser by default unless you indicate you want to support them by using `critical().add(\"b64\")`.\n\nFinally, even if the payload contains a non-detached String, you could still use the two-argument method using the\npayload String's UTF-8 bytes instead:\n\n[,java]\n----\nparsed = Jwts.parser().verifyWith(testKey)\n        .build()\n        .parseSignedClaims(jws, claimsString.getBytes(StandardCharsets.UTF_8)); // <---\n----\n\n+++<a name=\"jwe\">++++++</a>+++\n\n== Encrypted JWTs\n\nThe JWT specification also provides for the ability to encrypt and decrypt a JWT.  Encrypting a JWT:\n\n. guarantees that no-one other than the intended JWT recipient can see the JWT `payload` (it is confidential), and\n. guarantees that no-one has manipulated or changed the JWT after it was created (its integrity is maintained).\n\nThese two properties - confidentiality and integrity - assure us that an encrypted JWT contains a `payload` that\nno-one else can see, _nor_ has anyone changed or altered the data in transit.\n\nEncryption and confidentiality seem somewhat obvious: if you encrypt a message, it is confidential by the notion that\nrandom 3rd parties cannot make sense of the encrypted message. But some might be surprised to know that *_general\nencryption does _not_ guarantee that someone hasn't tampered/altered an encrypted message in transit_*.  Most of us\nassume that if a message can be decrypted, then the message would be authentic and unchanged - after all, if you can\ndecrypt it, it must not have been tampered with, right? Because if it was changed, decryption would surely fail, right?\n\nUnfortunately, this is not actually guaranteed in all cryptographic ciphers. There are certain attack vectors where\nit is possible to change an encrypted payload (called 'ciphertext'), and the message recipient is still able to\nsuccessfully decrypt the (modified) payload.  In these cases, the ciphertext integrity was not maintained - a\nmalicious 3rd party could intercept a message and change the payload content, even if they don't understand what is\ninside the payload, and the message recipient could never know.\n\nTo combat this, there is a category of encryption algorithms that ensures                                                                                 both confidentiality _and_ integrity of the\nciphertext data.  These types of algorithms are called\nhttps://en.wikipedia.org/wiki/Authenticated_encryption[Authenticated Encryption] algorithms.\n\nAs a result, to ensure JWTs do not suffer from this problem, the JWE RFC specifications require that any encryption\nalgorithm used to encrypt a JWT _MUST_ be an Authenticated Encryption algorithm.  JWT users can be sufficiently\nconfident their encrypted JWTs maintain the properties of both confidentiality and integrity.\n\n+++<a name=\"jwe-enc\">++++++</a>+++\n\n=== JWE Encryption Algorithms\n\nThe JWT specification defines 6 standard Authenticated Encryption algorithms used to encrypt a JWT `payload`:\n\n|===\n| Identifier | Required Key Bit Length | Encryption Algorithm\n\n| `A128CBC‑HS256`\n| 256\n| https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.3[AES_128_CBC_HMAC_SHA_256] authenticated encryption algorithm\n\n| `A192CBC-HS384`\n| 384\n| https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.4[AES_192_CBC_HMAC_SHA_384] authenticated encryption algorithm\n\n| `A256CBC-HS512`\n| 512\n| https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5[AES_256_CBC_HMAC_SHA_512] authenticated encryption algorithm\n\n| `A128GCM`\n| 128\n| AES GCM using 128-bit key^*1*^\n\n| `A192GCM`\n| 192\n| AES GCM using 192-bit key^*1*^\n\n| `A256GCM`\n| 256\n| AES GCM using 256-bit key^*1*^\n|===\n\n^*1*.{sp}{fn-require-java8-plus}^\n\nThese are all represented as constants in the `io.jsonwebtoken.Jwts.ENC` registry singleton as\nimplementations of the `io.jsonwebtoken.security.AeadAlgorithm` interface.\n\nAs shown in the table above, each algorithm requires a key of sufficient length.  The JWT specification\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.3[RFC 7518, Sections 5.2.3 through 5.3]\n_requires_ (mandates) that you MUST use keys that are sufficiently strong for a chosen algorithm.  This means that\nJJWT - a specification-compliant library - will also enforce that you use sufficiently strong keys\nfor the algorithms you choose.  If you provide a weak key for a given algorithm, JJWT will reject it and throw an\nexception.\n\nThe reason why the JWT specification, and consequently JJWT, mandates key lengths is that the security model of a\nparticular algorithm can completely break down if you don't adhere to the mandatory key properties of the algorithm,\neffectively having no security at all.\n\n+++<a name=\"jwe-enc-symmetric\">++++++</a>+++\n\n==== Symmetric Ciphers\n\nYou might have noticed something about the above Authenticated Encryption algorithms: they're all variants of the\nAES algorithm, and AES always uses a symmetric (secret) key to perform encryption and decryption.  That's kind of\nstrange, isn't it?\n\nWhat about RSA and Elliptic Curve asymmetric key cryptography? And Diffie-Hellman key exchange?  What about\npassword-based key derivation algorithms? Surely any of those could be desirable depending on the use case, no?\n\nYes, they definitely can, and the JWT specifications do support them, albeit indirectly:  those other\nalgorithms _are_ indeed supported and used, but they aren't used to encrypt the JWT `payload` directly.  They are\nused to _produce_ the actual key used to encrypt the `JWT` payload.\n\nThis is all done via the JWT specification's concept of a Key Management Algorithm, covered next.  After we cover that,\nwe'll show you how to encrypt and parse your own JWTs with the `JwtBuilder` and `JwtParserBuilder`.\n\n+++<a name=\"jwe-alg\">++++++</a>+++\n\n=== JWE Key Management Algorithms\n\nAs stated above, all standard JWA Encryption Algorithms are AES-based authenticated encryption algorithms.  So what\nabout RSA and Elliptic Curve cryptography? And password-based key derivation, or Diffie-Hellman exchange?\n\nAll of those are supported as well, but they are not used directly for encryption. They are used to _produce_ the\nkey that will be used to directly encrypt the JWT `payload`.\n\nThat is, JWT encryption can be thought of as a two-step process, shown in the following pseudocode:\n\n[,groovy]\n----\nKey algorithmKey = getKeyManagementAlgorithmKey(); // PublicKey, SecretKey, or Password\n\nSecretKey contentEncryptionKey = keyManagementAlgorithm.produceEncryptionKey(algorithmKey); // 1\n\nbyte[] ciphertext = encryptionAlgorithm.encrypt(payload, contentEncryptionKey);             // 2\n----\n\nSteps:\n\n. Use the `algorithmKey` to produce the actual key that will be used to encrypt the payload.  The JWT specifications\ncall this result the 'Content Encryption Key'.\n. Take the resulting Content Encryption Key and use it directly with the Authenticated Encryption algorithm to\nactually encrypt the JWT `payload`.\n\nSo why the indirection?  Why not just use any `PublicKey`, `SecretKey` or `Password` to encrypt the `payload`\n_directly_ ?\n\nThere are quite a few reasons for this.\n\n. Asymmetric key encryption (like RSA and Elliptic Curve) tends to be slow.  Like _really_ slow.  Symmetric key\ncipher algorithms in contrast are _really fast_.  This matters a lot in production applications that could be\nhandling a JWT on every HTTP request, which could be thousands per second.\n. RSA encryption (for example) can only encrypt a relatively small amount of data. A 2048-bit RSA key can only\nencrypt up to a maximum of 245 bytes.  A 4096-bit RSA key can only encrypt up to a maximum of 501 bytes.  There are\nplenty of JWTs that can exceed 245 bytes, and that would make RSA unusable.\n. Passwords usually make for very poor encryption keys - they often have poor entropy, or they themselves are\noften too short to be used directly with algorithms that mandate minimum key lengths to help ensure safety.\n\nFor these reasons and more, using one secure algorithm to generate or encrypt a key used for another (very fast) secure\nalgorithm has been proven to be a great way to increase security through many more secure algorithms while\nalso still resulting in very fast and secure output.  This is after all how TLS (for https encryption) works -\ntwo parties can use more complex cryptography (like RSA or Elliptic Curve) to negotiate a small, fast encryption key.\nThis fast encryption key is produced during the 'TLS handshake' and is called the TLS 'session key'.\n\nSo the JWT specifications work much in the same way: one key from any number of various algorithm types can be used\nto produce a final symmetric key, and that symmetric key is used to encrypt the JWT `payload`.\n\n+++<a name=\"jwe-alg-standard\">++++++</a>+++\n\n==== JWE Standard Key Management Algorithms\n\nThe JWT specification defines 17 standard Key Management Algorithms used to produce the JWE\nContent Encryption Key (CEK):\n\n|===\n| Identifier | Key Management Algorithm\n\n| `RSA1_5`\n| RSAES-PKCS1-v1_5\n\n| `RSA-OAEP`\n| RSAES OAEP using default parameters\n\n| `RSA-OAEP-256`\n| RSAES OAEP using SHA-256 and MGF1 with SHA-256\n\n| `A128KW`\n| AES Key Wrap with default initial value using 128-bit key\n\n| `A192KW`\n| AES Key Wrap with default initial value using 192-bit key\n\n| `A256KW`\n| AES Key Wrap with default initial value using 256-bit key\n\n| `dir`\n| Direct use of a shared symmetric key as the Content Encryption Key\n\n| `ECDH-ES`\n| Elliptic Curve Diffie-Hellman Ephemeral Static key agreement using Concat KDF\n\n| `ECDH-ES+A128KW`\n| ECDH-ES using Concat KDF and CEK wrapped with \"A128KW\"\n\n| `ECDH-ES+A192KW`\n| ECDH-ES using Concat KDF and CEK wrapped with \"A192KW\"\n\n| `ECDH-ES+A256KW`\n| ECDH-ES using Concat KDF and CEK wrapped with \"A256KW\"\n\n| `A128GCMKW`\n| Key wrapping with AES GCM using 128-bit key^*1*^\n\n| `A192GCMKW`\n| Key wrapping with AES GCM using 192-bit key^*1*^\n\n| `A256GCMKW`\n| Key wrapping with AES GCM using 256-bit key^*1*^\n\n| `PBES2-HS256+A128KW`\n| PBES2 with HMAC SHA-256 and \"A128KW\" wrapping^*1*^\n\n| `PBES2-HS384+A192KW`\n| PBES2 with HMAC SHA-384 and \"A192KW\" wrapping^*1*^\n\n| `PBES2‑HS512+A256KW`\n| PBES2 with HMAC SHA-512 and \"A256KW\" wrapping^*1*^\n|===\n\n^*1*.{sp}{fn-require-java8-plus}^\n\nThese are all represented as constants in the `io.jsonwebtoken.Jwts.KEY` registry singleton as\nimplementations of the `io.jsonwebtoken.security.KeyAlgorithm` interface.\n\nBut 17 algorithms are a lot to choose from.  When would you use them?  The sections below describe when you might\nchoose each category of algorithms and how they behave.\n\n+++<a name=\"jwe-alg-rsa\">++++++</a>+++\n\n===== RSA Key Encryption\n\nThe JWT RSA key management algorithms `RSA1_5`, `RSA-OAEP`, and `RSA-OAEP-256` are used when you want to use the\nJWE recipient's RSA _public_ key during encryption.  This ensures that only the JWE recipient can decrypt\nand read the JWE (using their RSA `private` key).\n\nDuring JWE creation, these algorithms:\n\n* Generate a new secure-random Content Encryption Key (CEK) suitable for the desired <<jwe-enc,encryption algorithm>>.\n* Encrypt the JWE payload with the desired encryption algorithm using the new CEK, producing the JWE payload ciphertext.\n* Encrypt the CEK itself with the specified RSA key wrap algorithm using the JWE recipient's RSA public key.\n* Embed the payload ciphertext and encrypted CEK in the resulting JWE.\n\nDuring JWE decryption, these algorithms:\n\n* Retrieve the encrypted Content Encryption Key (CEK) embedded in the JWE.\n* Decrypt the encrypted CEK with the discovered RSA key unwrap algorithm using the JWE recipient's RSA private key,\nproducing the decrypted Content Encryption Key (CEK).\n* Decrypt the JWE ciphertext payload with the JWE's identified <<jwe-enc,encryption algorithm>> using the decrypted CEK.\n\n[WARNING]\n====\nRFC 7518 Sections https://www.rfc-editor.org/rfc/rfc7518.html#section-4.2[4.2] and\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-4.3[4.3] _require_ (mandate) that RSA keys >= 2048 bits\nMUST be used with these algorithms. JJWT will throw an exception if it detects weaker keys being used.\n====\n\n+++<a name=\"jwe-alg-aes\">++++++</a>+++\n\n===== AES Key Encryption\n\nThe JWT AES key management algorithms `A128KW`, `A192KW`, `A256KW`, `A128GCMKW`, `A192GCMKW`, and `A256GCMKW` are\nused when you have a symmetric secret key, but you don't want to use that secret key to directly\nencrypt/decrypt the JWT.\n\nInstead, a new secure-random key is generated each time a JWE is created, and that new/random key is used to directly\nencrypt/decrypt the JWT payload.  The secure-random key is itself encrypted with your symmetric secret key\nusing the AES Wrap algorithm, and the encrypted key is embedded in the resulting JWE.\n\nThis allows the JWE to be encrypted with a random short-lived key, reducing material exposure of the potentially\nlonger-lived symmetric secret key.\n\nBecause these particular algorithms use a symmetric secret key, they are best suited when the JWE creator and\nreceiver are the same, ensuring the secret key does not need to be shared with multiple parties.\n\nDuring JWE creation, these algorithms:\n\n* Generate a new secure-random Content Encryption Key (CEK) suitable for the desired <<jwe-enc,encryption algorithm>>.\n* Encrypt the JWE payload with the desired encryption algorithm using the new CEK, producing the JWE payload ciphertext.\n* Encrypt the CEK itself with the specified AES key algorithm (either AES Key Wrap or AES with GCM encryption),\nproducing the encrypted CEK.\n* Embed the payload ciphertext and encrypted CEK in the resulting JWE.\n\nDuring JWE decryption, these algorithms:\n\n* Retrieve the encrypted Content Encryption Key (CEK) embedded in the JWE.\n* Decrypt the encrypted CEK with the discovered AES key algorithm using the symmetric secret key.\n* Decrypt the JWE ciphertext payload with the JWE's identified <<jwe-enc,encryption algorithm>> using the decrypted CEK.\n\n[WARNING]\n====\nThe symmetric key used for the AES key algorithms MUST be 128, 192 or 256 bits as required by the specific AES\nkey algorithm.  JJWT will throw an exception if it detects weaker keys than what is required.\n====\n\n+++<a name=\"jwe-alg-dir\">++++++</a>+++\n\n===== Direct Key Encryption\n\nThe JWT `dir` (direct) key management algorithm is used when you have a symmetric secret key, and you want to use it\nto directly encrypt the JWT payload.\n\nBecause this algorithm uses a symmetric secret key, it is best suited when the JWE creator and receiver are the\nsame, ensuring the secret key does not need to be shared with multiple parties.\n\nThis is the simplest key algorithm for direct encryption that does not perform any key encryption.  It is essentially\na 'no op' key algorithm, allowing the shared key to be used to directly encrypt the JWT payload.\n\nDuring JWE creation, this algorithm:\n\n* Encrypts the JWE payload with the desired encryption algorithm directly using the symmetric secret key,\nproducing the JWE payload ciphertext.\n* Embeds the payload ciphertext in the resulting JWE.\n\nNote that because this algorithm does not produce an encrypted key value, an encrypted CEK is _not_ embedded in the\nresulting JWE.\n\nDuring JWE decryption, this algorithm decrypts the JWE ciphertext payload with the JWE's\nidentified <<jwe-enc,encryption algorithm>> directly using the symmetric secret key.  No encrypted CEK is used.\n\n[WARNING]\n====\nThe symmetric secret key MUST be 128, 192 or 256 bits as required by the associated\n<<jwe-enc,AEAD encryption algorithm>> used to encrypt the payload. JJWT will throw an exception if it detects\nweaker keys than what is required.\n====\n\n+++<a name=\"jwe-alg-pbes2\">++++++</a>+++\n\n===== Password-Based Key Encryption\n\nThe JWT password-based key encryption algorithms `PBES2-HS256+A128KW`, `PBES2-HS384+A192KW`, and `PBES2-HS512+A256KW`\nare used when you want to use a password (character array) to encrypt and decrypt a JWT.\n\nHowever, because passwords are usually too weak or problematic to use directly in cryptographic contexts, these\nalgorithms utilize key derivation techniques with work factors (e.g. computation iterations) and secure-random salts\nto produce stronger cryptographic keys suitable for cryptographic operations.\n\nThis allows the payload to be encrypted with a random short-lived cryptographically-stronger key, reducing the need to\nexpose the longer-lived (and potentially weaker) password.\n\nBecause these algorithms use a secret password, they are best suited when the JWE creator and receiver are the\nsame, ensuring the secret password does not need to be shared with multiple parties.\n\nDuring JWE creation, these algorithms:\n\n* Generate a new secure-random Content Encryption Key (CEK) suitable for the desired <<jwe-enc,encryption algorithm>>.\n* Encrypt the JWE payload with the desired encryption algorithm using the new CEK, producing the JWE payload ciphertext.\n* Derive a 'key encryption key' (KEK) with the desired \"PBES2 with HMAC SHA\" algorithm using the password, a suitable\nnumber of computational iterations, and a secure-random salt value.\n* Encrypt the generated CEK with the corresponding AES Key Wrap algorithm using the password-derived KEK.\n* Embed the payload ciphertext and encrypted CEK in the resulting JWE.\n\n[NOTE]\n====\n.Secure defaults\n\nWhen using these algorithms, if you do not specify a work factor (i.e. number of computational\niterations), JJWT will automatically use an\nhttps://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2[OWASP PBKDF2 recommended]\ndefault appropriate for the specified `PBES2` algorithm.\n====\n\nDuring JWE decryption, these algorithms:\n\n* Retrieve the encrypted Content Encryption Key (CEK) embedded in the JWE.\n* Derive the 'key encryption key' (KEK) with the discovered \"PBES2 with HMAC SHA\" algorithm using the password and the\nnumber of computational iterations and secure-random salt value discovered in the JWE header.\n* Decrypt the encrypted CEK with the corresponding AES Key Unwrap algorithm using the password-derived KEK.\n* Decrypt the JWE ciphertext payload with the JWE's identified <<jwe-enc,encryption algorithm>> using the decrypted CEK.\n\n+++<a name=\"jwe-alg-ecdhes\">++++++</a>+++\n\n===== Elliptic Curve Diffie-Hellman Ephemeral Static Key Agreement (ECDH-ES)\n\nThe JWT Elliptic Curve Diffie-Hellman Ephemeral Static key agreement algorithms `ECDH-ES`, `ECDH-ES+A128KW`,\n`ECDH-ES+A192KW`, and `ECDH-ES+A256KW` are used when you want to use the JWE recipient's Elliptic Curve _public_ key\nduring encryption.  This ensures that only the JWE recipient can decrypt and read the JWE (using their Elliptic Curve\n_private_ key).\n\nDuring JWE creation, these algorithms:\n\n* Obtain the Content Encryption Key (CEK) used to encrypt the JWE payload as follows:\n ** Inspect the JWE recipient's Elliptic Curve public key and determine its Curve.\n ** Generate a new secure-random ephemeral Elliptic Curve public/private key pair on this same Curve.\n ** Add the ephemeral EC public key to the JWE\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1[epk header] for inclusion in the final JWE.\n ** Produce an ECDH shared secret with the ECDH Key Agreement algorithm using the JWE recipient's EC public key\nand the ephemeral EC private key.\n ** Derive a symmetric secret key with the Concat Key Derivation Function\n(https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf[NIST.800-56A], Section 5.8.1) using\nthis ECDH shared secret and any provided\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2[PartyUInfo] and/or\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3[PartyVInfo].\n ** If the key algorithm is `ECDH-ES`:\n  *** Use the Concat KDF-derived symmetric secret key directly as the Content Encryption Key (CEK). No encrypted key\nis created, nor embedded in the resulting JWE.\n ** Otherwise, if the key algorithm is `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, or `ECDH-ES+A256KW`:\n  *** Generate a new secure-random Content Encryption Key (CEK) suitable for the desired <<jwe-enc,encryption algorithm>>.\n  *** Encrypt this new CEK with the corresponding AES Key Wrap algorithm using the Concat KDF-derived secret key,\nproducing the encrypted CEK.\n  *** Embed the encrypted CEK in the resulting JWE.\n* Encrypt the JWE payload with the desired encryption algorithm using the obtained CEK, producing the JWE payload\nciphertext.\n* Embed the payload ciphertext in the resulting JWE.\n\nDuring JWE decryption, these algorithms:\n\n* Obtain the Content Encryption Key (CEK) used to decrypt the JWE payload as follows:\n ** Retrieve the required ephemeral Elliptic Curve public key from the JWE's\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1[epk header].\n ** Ensure the ephemeral EC public key exists on the same curve as the JWE recipient's EC private key.\n ** Produce the ECDH shared secret with the ECDH Key Agreement algorithm using the JWE recipient's EC private key\nand the ephemeral EC public key.\n ** Derive a symmetric secret key with the Concat Key Derivation Function\n(https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf[NIST.800-56A], Section 5.8.1) using\nthis ECDH shared secret and any\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2[PartyUInfo] and/or\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3[PartyVInfo] found in the JWE header.\n ** If the key algorithm is `ECDH-ES`:\n  *** Use the Concat KDF-derived secret key directly as the Content Encryption Key (CEK). No encrypted key is used.\n ** Otherwise, if the key algorithm is `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, or `ECDH-ES+A256KW`:\n  *** Obtain the encrypted key ciphertext embedded in the JWE.\n  *** Decrypt the encrypted key ciphertext with the associated AES Key Unwrap algorithm using the Concat KDF-derived\nsecret key, producing the unencrypted Content Encryption Key (CEK).\n* Decrypt the JWE payload ciphertext with the JWE's discovered encryption algorithm using the obtained CEK.\n\n+++<a name=\"jwe-create\">++++++</a>+++\n\n=== Creating a JWE\n\nNow that we know the difference between a JWE Encryption Algorithm and a JWE Key Management Algorithm, how do we use\nthem to encrypt a JWT?\n\nYou create an encrypted JWT (called a 'JWE') as follows:\n\n. Use the `Jwts.builder()` method to create a `JwtBuilder` instance.\n. Call `JwtBuilder` methods to set the `payload` content or claims and any <<jws-create-header,header>> parameters as desired.\n. Call the `encryptWith` method, specifying the Key, Key Algorithm, and Encryption Algorithm you want to use.\n. Finally, call the `compact()` method to compact and encrypt, producing the final jwe.\n\nFor example:\n\n[,java]\n----\nString jwe = Jwts.builder()                              // (1)\n\n    .subject(\"Bob\")                                      // (2)\n\n    .encryptWith(key, keyAlgorithm, encryptionAlgorithm) // (3)\n\n    .compact();                                          // (4)\n----\n\nBefore calling `compact()`,  you may set any <<jws-create-header,header>> parameters and <<jws-create-claims,claims>>\nexactly the same way as described for JWS.\n\n+++<a name=\"jwe-compression\">++++++</a>+++\n\n==== JWE Compression\n\nIf your JWT payload or Claims set is large (contains a lot of data), you might want to compress the JWE to reduce\nits size.  Please see the main <<compression,Compression>> section to see how to compress and decompress JWTs.\n\n+++<a name=\"jwe-read\">++++++</a>+++\n\n=== Reading a JWE\n\nYou read (parse) a JWE as follows:\n\n. Use the `Jwts.parser()` method to create a `JwtParserBuilder` instance.\n. Call either <<key-locator,keyLocator>> or `decryptWith` methods to determine the key used to decrypt the JWE.\n. Call the ``JwtParserBuilder``'s `build()` method to create a thread-safe `JwtParser`.\n. Parse the jwe string with the ``JwtParser``'s `parseEncryptedClaims` or `parseEncryptedContent` method.\n. Wrap the entire call is in a try/catch block in case decryption or integrity verification fails.\n\nFor example:\n\n[,java]\n----\nJwe<Claims> jwe;\n\ntry {\n    jwe = Jwts.parser()               // (1)\n\n    .keyLocator(keyLocator)           // (2) dynamically lookup decryption keys based on each JWE\n    //.decryptWith(key)               //     or a static key used to decrypt all encountered JWEs\n\n    .build()                          // (3)\n    .parseEncryptedClaims(jweString); // (4) or parseEncryptedContent(jweString);\n\n    // we can safely trust the JWT\n\ncatch (JwtException ex) {             // (5)\n\n    // we *cannot* use the JWT as intended by its creator\n}\n----\n\n[NOTE]\n====\n.Type-safe JWEs\n\n* If you are expecting a JWE with a Claims `payload`, call the ``JwtParser``'s `parseEncryptedClaims` method.\n* If you are expecting a JWE with a content `payload`, call the ``JwtParser``'s `parseEncryptedContent` method.\n====\n\n+++<a name=\"jwe-read-key\">++++++</a>+++\n\n==== Decryption Key\n\nThe most important thing to do when reading a JWE is to specify the key used during decryption.  If decryption or\nintegrity protection checks fail, the JWT cannot be safely trusted and should be discarded.\n\nSo which key do we use for decryption?\n\n* If the jwe was encrypted _directly_ with a `SecretKey`, the same `SecretKey` must be specified on the\n`JwtParserBuilder`. For example:\n+\n[,java]\n----\nJwts.parser()\n\n  .decryptWith(secretKey) // <----\n\n  .build()\n  .parseEncryptedClaims(jweString);\n----\n\n* If the jwe was encrypted using a key produced by a Password-based key derivation `KeyAlgorithm`, the same\n`Password` must be specified on the `JwtParserBuilder`. For example:\n+\n[,java]\n----\nPassword password = Keys.password(passwordChars);\n\nJwts.parser()\n\n  .decryptWith(password) // <---- an `io.jsonwebtoken.security.Password` instance\n\n  .build()\n  .parseEncryptedClaims(jweString);\n----\n\n* If the jwe was encrypted with a key produced by an asymmetric `KeyAlgorithm`, the corresponding `PrivateKey` (not\nthe `PublicKey`) must be specified on the `JwtParserBuilder`.  For example:\n+\n[,java]\n----\nJwts.parser()\n\n  .decryptWith(privateKey) // <---- a `PrivateKey`, not a `PublicKey`\n\n  .build()\n  .parseSignedClaims(jweString);\n----\n\n+++<a name=\"jwe-key-locator\">++++++</a>+++\n\n==== Decryption Key Locator\n\nWhat if your application doesn't use just a single `SecretKey` or `KeyPair`? What\nif JWEs can be created with different ``SecretKey``s, ``Password``s or public/private keys, or a combination of all of\nthem?  How do you know which key to specify if you can't inspect the JWT first?\n\nIn these cases, you can't call the ``JwtParserBuilder``'s `decryptWith` method with a single key - instead, you'll need\nto use a Key `Locator`.  Please see the <<key-locator,Key Lookup>> section to see how to dynamically obtain different\nkeys when parsing JWSs or JWEs.\n\n+++<a name=\"jwe-key-pkcs11\">++++++</a>+++\n\n==== ECDH-ES Decryption with PKCS11 PrivateKeys\n\nThe JWT `ECDH-ES`, `ECDH-ES+A128KW`, `ECDH-ES+A192KW`, and `ECDH-ES+A256KW` key algorithms validate JWE input using\npublic key information, even when using ``PrivateKey``s to decrypt.  Ordinarily this is automatically performed\nby JJWT when your `PrivateKey` instances implement the\nhttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/interfaces/ECKey.html[ECKey] or\nhttps://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/security/interfaces/EdECKey.html[EdECKey]\n(or BouncyCastle equivalent) interfaces, which is the case for most JCA `Provider` implementations.\n\nHowever, if your decryption ``PrivateKey``s are stored in a Hardware Security Module (HSM) and/or you use the\nhttps://docs.oracle.com/en/java/javase/17/security/pkcs11-reference-guide1.html#GUID-6DA72F34-6C6A-4F7D-ADBA-5811576A9331[SunPKCS11 Provider],\nit is likely that your `PrivateKey` instances _do not_ implement `ECKey`.\n\nIn these cases, you need to provide both the PKCS11 `PrivateKey` and it's companion `PublicKey` during decryption\nby using the `Keys.builder` method. For example:\n\n[,java]\n----\nKeyPair pair = getMyPkcs11KeyPair();\nPrivateKey jwtParserDecryptionKey = Keys.builder(pair.getPrivate())\n    .publicKey(pair.getPublic()) // PublicKey must implement ECKey or EdECKey or BouncyCastle equivalent\n    .build();\n----\n\nYou then use the resulting `jwtParserDecryptionKey` (not `pair.getPrivate()`) with the `JwtParserBuilder` or as\nthe return value from a custom <<key-locator,Key Locator>> implementation.  For example:\n\n[,java]\n----\nPrivateKey decryptionKey = Keys.builder(pkcs11PrivateKey).publicKey(pkcs11PublicKey).build();\n\nJwts.parser()\n    .decryptWith(decryptionKey) // <----\n    .build()\n    .parseEncryptedClaims(jweString);\n----\n\nOr as the return value from your key locator:\n\n[,java]\n----\nJwts.parser()\n    .keyLocator(keyLocator) // your keyLocator.locate(header) would return Keys.builder...\n    .build()\n    .parseEncryptedClaims(jweString);\n----\n\nPlease see the <<key-locator-provider,Provider-constrained Keys>> section for more information, as well as\ncode examples of how to implement a Key `Locator` using the `Keys.builder` technique.\n\n+++<a name=\"jwe-read-decompression\">++++++</a>+++\n\n==== JWE Decompression\n\nIf a JWE is compressed using the `DEF` (https://www.rfc-editor.org/rfc/rfc1951[DEFLATE]) or `GZIP`\n(https://www.rfc-editor.org/rfc/rfc1952.html[GZIP]) compression algorithms, it will automatically be decompressed\nafter decryption, and there is nothing you need to configure.\n\nIf, however, a custom compression algorithm was used to compress the JWE, you will need to tell the\n`JwtParserBuilder` how to resolve your `CompressionAlgorithm` to decompress the JWT.\n\nPlease see the <<compression,Compression>> section below to see how to decompress JWTs during parsing.\n\n+++<a name=\"jwk\">++++++</a>+++\n\n== JSON Web Keys (JWKs)\n\nhttps://www.rfc-editor.org/rfc/rfc7517.html[JSON Web Keys] (JWKs) are JSON serializations of cryptographic keys,\nallowing key material to be embedded in JWTs or transmitted between parties in a standard JSON-based text format. They\nare essentially a JSON-based alternative to other text-based key formats, such as the\nhttps://serverfault.com/a/9717[DER, PEM and PKCS12] text strings or files commonly used when configuring TLS on web\nservers, for example.\n\nFor example, an identity web service may expose its RSA or Elliptic Curve Public Keys to 3rd parties in the JWK format.\nA client may then parse the public key JWKs to verify the service's <<jws,JWS>> tokens, as well as send encrypted\ninformation to the service using <<jwe,JWE>>s.\n\nJWKs can be converted to and from standard Java `Key` types as expected using the same builder/parser patterns we've\nseen for JWTs.\n\n+++<a name=\"jwk-create\">++++++</a>+++\n\n=== Create a JWK\n\nYou create a JWK as follows:\n\n. Use the `Jwks.builder()` method to create a `JwkBuilder` instance.\n. Call the `key` method with the Java key you wish to represent as a JWK.\n. Call builder methods to set any additional key parameters or metadata, such as a `kid` (Key ID), X509 Certificates,\netc as desired.\n. Call the `build()` method to produce the resulting JWK.\n\nFor example:\n\n[,java]\n----\nSecretKey key = getSecretKey();     // or RSA or EC PublicKey or PrivateKey\nSecretJwk = Jwks.builder().key(key) // (1) and (2)\n\n    .id(\"mySecretKeyId\")            // (3)\n    // ... etc ...\n\n    .build();                       // (4)\n----\n\n==== JWK from a Map\n\nIf you have a `Map<String,?>` of name/value pairs that reflect an existing JWK, you add them and build a type-safe\n`Jwk` instance:\n\n[,java]\n----\nMap<String,?> jwkValues = getMyJwkMap();\n\nJwk<?> jwk = Jwks.builder().add(jwkValues).build();\n----\n\n+++<a name=\"jwk-read\">++++++</a>+++\n\n=== Read a JWK\n\nYou can read/parse a JWK by building a `JwkParser` and parsing the JWK JSON string with its `parse` method:\n\n[,java]\n----\nString json = getJwkJsonString();\nJwk<?> jwk = Jwks.parser()\n    //.provider(aJcaProvider)     // optional\n    //.deserializer(deserializer) // optional\n    .build()                      // create the parser\n    .parse(json);                 // actually parse the JSON\n\nKey key = jwk.toKey();            // convert to a Java Key instance\n----\n\nAs shown above you can specify a custom JCA Provider or <<json,JSON deserializer>> in the same way as the `JwtBuilder`.\n\n+++<a name=\"jwk-private\">++++++</a>+++\n\n=== PrivateKey JWKs\n\nUnlike Java, the JWA specification requires a private JWKs to contain _both_ public key _and_ private key material\n(see https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.2[RFC 7518, Section 6.1.1] and\nhttps://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.2[RFC 7518, Section 6.3.2]).\n\nIn this sense, a private JWK (represented as a `PrivateJwk` or a subtype, such as `RsaPrivateJwk`, `EcPrivateJwk`, etc)\ncan be thought of more like a Java `KeyPair` instance.  Consequently, when creating a `PrivateJwk` instance,\nthe ``PrivateKey``'s corresponding `PublicKey` is required.\n\n+++<a name=\"jwk-private-public\">++++++</a>+++\n\n==== Private JWK `PublicKey`\n\nIf you do not provide a `PublicKey` when creating a `PrivateJwk`, JJWT will automatically derive the `PublicKey` from\nthe `PrivateKey` instance if possible. However, because this can add\nsome computing time, it is typically recommended to provide the `PublicKey` when possible to avoid this extra work.\n\nFor example:\n\n[,java]\n----\nRSAPrivateKey rsaPrivateKey = getRSAPrivateKey(); // or ECPrivateKey\n\nRsaPrivateJwk jwk = Jwks.builder().key(rsaPrivateKey)\n\n        //.publicKey(rsaPublicKey)  // optional, but recommended to avoid extra computation work\n\n        .build();\n----\n\n+++<a name=\"jwk-private-keypair\">++++++</a>+++\n\n==== Private JWK from KeyPair\n\nIf you have a Java `KeyPair` instance, then you have both the public and private key material necessary to create a\n`PrivateJwk`. For example:\n\n[,java]\n----\nKeyPair rsaKeyPair = getRSAKeyPair();\nRsaPrivateJwk rsaPrivJwk = Jwks.builder().rsaKeyPair(rsaKeyPair).build();\n\nKeyPair ecKeyPair = getECKeyPair();\nEcPrivateJwk ecPrivJwk = Jwks.builder().ecKeyPair(ecKeyPair).build();\n\nKeyPair edEcKeyPair = getEdECKeyPair();\nOctetPrivateJwk edEcPrivJwk = Jwks.builder().octetKeyPair(edEcKeyPair).build();\n----\n\nNote that:\n\n* An exception will be thrown when calling `rsaKeyPair` if the specified `KeyPair` instance does not contain\n`RSAPublicKey` and `RSAPrivateKey` instances.\n* Similarly, an exception will be thrown when calling `ecKeyPair` if\nthe `KeyPair` instance does not contain `ECPublicKey` and `ECPrivateKey` instances.\n* Finally, an exception will be\nthrown when calling `octetKeyPair` if the `KeyPair` instance does not contain X25519, X448, Ed25519, or Ed448 keys\n(introduced in JDK 11 and 15 or when using BouncyCastle).\n\n+++<a name=\"jwk-private-topub\">++++++</a>+++\n\n==== Private JWK Public Conversion\n\nBecause private JWKs contain public key material, you can always obtain the private JWK's corresponding public JWK and\nJava `PublicKey` or `KeyPair`.  For example:\n\n[,java]\n----\nRsaPrivateJwk privateJwk = Jwks.builder().key(rsaPrivateKey).build(); // or ecPrivateKey or edEcPrivateKey\n\n// Get the matching public JWK and/or PublicKey:\nRsaPublicJwk pubJwk = privateJwk.toPublicJwk();       // JWK instance\nRSAPublicKey pubKey = pubJwk.toKey();                 // Java PublicKey instance\nKeyPair pair = privateJwk.toKeyPair();                // io.jsonwebtoken.security.KeyPair retains key types\njava.security.KeyPair jdkPair = pair.toJavaKeyPair(); // does not retain pub/private key types\n----\n\n+++<a name=\"jwk-thumbprint\">++++++</a>+++\n\n=== JWK Thumbprints\n\nA https://www.rfc-editor.org/rfc/rfc7638.html[JWK Thumbprint] is a digest (aka hash) of a canonical JSON\nrepresentation of a JWK's public properties. 'Canonical' in this case means that only RFC-specified values in any JWK\nare used in an exact order thumbprint calculation.  This ensures that anyone can calculate a JWK's same exact\nthumbprint, regardless of custom parameters or JSON key/value ordering differences in a JWK.\n\nAll `Jwk` instances support https://www.rfc-editor.org/rfc/rfc7638.html[JWK Thumbprint]s via the\n`thumbprint()` and `thumbprint(HashAlgorithm)` methods:\n\n[,java]\n----\nHashAlgorithm hashAlg = Jwks.HASH.SHA256; // or SHA384, SHA512, etc.\n\nJwk<?> jwk = Jwks.builder(). /* ... */ .build();\n\nJwkThumbprint sha256Thumbprint = jwk.thumbprint(); // SHA-256 thumbprint by default\n\nJwkThumbprint anotherThumbprint = jwk.thumbprint(Jwks.HASH.SHA512); // or a specified hash algorithm\n----\n\nThe resulting `JwkThumbprint` instance provides some useful methods:\n\n* `jwkThumbprint.toByteArray()`: the thumbprint's actual digest bytes - i.e. the raw output from the hash algorithm\n* `jwkThumbprint.toString()`: the digest bytes as a Base64URL-encoded string\n* `jwkThumbprint.getHashAlgorithm()`: the specific `HashAlgorithm` used to compute the thumbprint. Many standard IANA\n                                    hash algorithms are available as constants in the `Jwks.HASH` utility class.\n* `jwkThumbprint.toURI()`: the thumbprint's canonical URI as defined by the https://www.rfc-editor.org/rfc/rfc9278.html[JWK Thumbprint URI] specification\n\n+++<a name=\"jwk-thumbprint-kid\">++++++</a>+++\n\n==== JWK Thumbprint as a Key ID\n\nBecause a thumbprint is an order-guaranteed unique digest of a JWK, JWK thumbprints are often used as convenient\nunique identifiers for a JWK (e.g. the JWK's `kid` (Key ID) value). These identifiers can be useful when\n<<key-locator,locating keys>> for JWS signature verification or JWE decryption, for example.\n\nFor example:\n\n[,java]\n----\nString kid = jwk.thumbprint().toString(); // Thumbprint bytes as a Base64URL-encoded string\nKey key = findKey(kid);\nassert jwk.toKey().equals(key);\n----\n\nHowever, because `Jwk` instances are immutable, you can't set the key id after the JWK is created. For example, the\nfollowing is not possible:\n\n[,java]\n----\nString kid = jwk.thumbprint().toString();\njwk.setId(kid) // Jwks are immutable - there is no `setId` method\n----\n\nInstead, you may use the `idFromThumbprint` methods on the `JwkBuilder` when creating a `Jwk`:\n\n[,java]\n----\nJwk<?> jwk = Jwks.builder().key(aKey)\n\n    .idFromThumbprint() // or idFromThumbprint(HashAlgorithm)\n\n    .build();\n----\n\nCalling either `idFromThumbprint` method will ensure that calling `jwk.getId()` equals `thumbprint.toString()`\n(which is `Encoders.BASE64URL.encode(thumbprint.toByteArray())`).\n\n+++<a name=\"jwk-thumbprint-uri\">++++++</a>+++\n\n==== JWK Thumbprint URI\n\nA JWK's thumbprint's canonical URI as defined by the https://www.rfc-editor.org/rfc/rfc9278.html[JWK Thumbprint URI]\nspecification may be obtained by calling the thumbprint's `toURI()` method:\n\n[,java]\n----\nURI canonicalThumbprintURI = jwk.thumbprint().toURI();\n----\n\nPer the RFC specification, if you call `canonicalThumbprintURI.toString()`, you would see a string that looks like this:\n\n[,text]\n----\nurn:ietf:params:oauth:jwk-thumbprint:HASH_ALG_ID:BASE64URL_DIGEST\n----\n\nwhere:\n\n* `urn:ietf:params:oauth:jwk-thumbprint:` is the URI scheme+prefix\n* `HASH_ALG_ID` is the standard identifier used to compute the thumbprint as defined in the\nhttps://www.iana.org/assignments/named-information/named-information.xhtml[IANA Named Information Hash Algorithm Registry].\nThis is the same as `thumbprint.getHashAlgorithm().getId()`.\n* `BASE64URL_DIGEST` is the Base64URL-encoded thumbprint bytes, equal to `jwkThumbprint.toString()`.\n\n+++<a name=\"jwk-security\">++++++</a>+++\n\n=== JWK Security Considerations\n\nBecause they contain secret or private key material, `SecretJwk` and `PrivateJwk` (e.g. `RsaPrivateJwk`, +\n`EcPrivateJwk`, etc) instances should be used with great care and never accidentally transmitted to 3rd parties.\n\nEven so, JJWT's `Jwk` implementations will suppress certain values in `toString()` output for safety as described\nnext.\n\n+++<a name=\"jwk-tostring\">++++++</a>+++\n\n==== JWK `toString()` Safety\n\nBecause it would be incredibly easy to accidentally print key material to `System.out.println()` or application\nlogs, all `Jwk` implementations will print redacted values instead of actual secret or private key material.\n\nFor example, consider the following Secret JWK JSON example from\nhttps://www.rfc-editor.org/rfc/rfc7515#appendix-A.1.1[RFC 7515, Appendix A.1.1]:\n\n[,json]\n----\n{\n  \"kty\": \"oct\",\n  \"k\": \"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\",\n  \"kid\": \"HMAC key used in https://www.rfc-editor.org/rfc/rfc7515#appendix-A.1.1 example.\"\n}\n----\n\nThe `k` value (`+AyAyM1SysPpby...+`) reflects secure key material and should never be accidentally\nexposed.\n\nIf you were to parse this JSON as a `Jwk`, calling `toString()` will _NOT_ print this value.  It will\ninstead print the string literal `<redacted>` for any secret or private key data value.  For example:\n\n[,java]\n----\nString json = getExampleSecretKeyJson();\nJwk<?> jwk = Jwks.parser().build().parse(json);\n\nSystem.out.printn(jwk);\n----\n\nThis code would print the following string literal to the System console:\n\n[,text]\n----\n{kty=oct, k=<redacted>, kid=HMAC key used in https://www.rfc-editor.org/rfc/rfc7515#appendix-A.1.1 example.}\n----\n\nThis is true for all secret or private key members in `SecretJwk` and `PrivateJwk` (e.g. `RsaPrivateJwk`,\n`EcPrivateJwk`, etc) instances.\n\n+++<a name=\"jwkset\">++++++</a>+++\n\n== JWK Sets\n\nThe JWK specification specification also defines the concept of a\nhttps://datatracker.ietf.org/doc/html/rfc7517#section-5[JWK Set]:\n\n A JWK Set is a JSON object that represents a set of JWKs.  The JSON\n object MUST have a \"keys\" member, with its value being an array of\n JWKs.\n\nFor example:\n\n[,txt]\n----\n{\n  \"keys\": [jwk1, jwk2, ...]\n}\n----\n\nWhere `jwk1`, `jwk2`, etc., are each a single <<jwk,JWK>> JSON Object.\n\nA JWK Set _may_ have other members that are peers to the `keys` member, but the JWK specification does not define any\nothers - any such additional members would be custom or unique based on an application's needs or preferences.\n\nA JWK Set can be useful for conveying multiple keys simultaneously.  For example, an identity web service could expose\nall of its RSA or Elliptic Curve public keys that might be used for various purposes or different algorithms to\n3rd parties or API clients as a single JWK Set JSON Object or document.  An API client can then parse the JWK Set\nto obtain the keys that might be used to verify or decrypt JWTs sent by the web service.\n\nJWK Sets are (mostly) simple collections of JWKs, and they are easily supported by JJWT with parallel builder/parser\nconcepts we've seen above.\n\n+++<a name=\"jwkset-create\">++++++</a>+++\n\n=== Create a JWK Set\n\nYou create a JWK Set as follows:\n\n. Use the `Jwks.set()` method to create a `JwkSetBuilder` instance.\n. Call the `add(Jwk)` method any number of times to add one or more JWKs to the set.\n. Call builder methods to set any additional JSON members if desired, or the `operationPolicy(KeyOperationPolicy)`\nbuilder method to control what key operations may be assigned to any given JWK added to the set.\n. Call the `build()` method to produce the resulting JWK Set.\n\nFor example:\n\n[,java]\n----\nJwk<?> jwk = Jwks.builder()/* ... */.build();\nSecretJwk = Jwks.set()              // 1\n    .add(jwk)                       // 2, appends a key\n    //.add(aCollection)             //    append multiple keys\n    //.keys(allJwks)                //    sets/replaces all keys\n    //.add(\"aName\", \"aValue\")       // 3, optional\n    //.operationPolicy(Jwks.OP      // 3, optional\n    //     .policy()\n    //     /* etc... */\n    //     .build())\n    //.provider(aJcaProvider)       //    optional\n    .build();                       // (4)\n----\n\nAs shown, you can optionally configure the `.operationPolicy(KeyOperationPolicy)` method using a\n`Jwts.OP.policy()` builder.  A `KeyOperationPolicy` allows you control what operations are allowed for any JWK\nbefore being added to the JWK Set; any JWK that does not match the policy will be rejected and not added to the set.\nJJWT internally defaults to a standard RFC-compliant policy, but you can create a\npolicy to override the default if desired using the `Jwks.OP.policy()` builder method.\n\n+++<a name=\"jwkset-read\">++++++</a>+++\n\n=== Read a JWK Set\n\nYou can read/parse a JWK Set by building a JWK Set `Parser` and parsing the JWK Set JSON with one of its various\n`parse` methods:\n\n[,java]\n----\nJwkSet jwkSet = Jwks.setParser()\n    //.provider(aJcaProvider)      // optional\n    //.deserializer(deserializer)  // optional\n    //.policy(aKeyOperationPolicy) // optional\n    .build()                       // create the parser\n    .parse(json);                  // actually parse JSON String, InputStream, Reader, etc.\n\njwkSet.forEach(jwk -> System.out.println(jwk));\n----\n\nAs shown above, you can specify a custom JCA Provider, <<json,JSON deserializer>> or `KeyOperationPolicy` in the\nsame way as the `JwkSetBuilder`. Any JWK that does not match the default (or configured) policy will be\nrejected. You can create a policy to override the default if desired using the `Jwks.OP.policy()` builder method.\n\n+++<a name=\"compression\">++++++</a>+++\n\n== Compression\n\n[WARNING]\n====\nThe JWT specification standardizes compression for JWEs (Encrypted JWTs) ONLY, however JJWT supports it for JWS\n(Signed JWTs) as well.\n\nIf you are positive that a JWS you create with JJWT will _also_ be parsed with JJWT,\nyou can use this feature with both JWEs and JWSs, otherwise it is best to only use it for JWEs.\n====\n\nIf a JWT's `payload` is sufficiently large - that is, it is a large content byte array or JSON with a lot of\nname/value pairs (or individual values are very large or verbose) - you can reduce the size of the compact JWT by\ncompressing the payload.\n\nThis might be important to you if the resulting JWT is used in a URL for example, since URLs are best kept under\n4096 characters due to browser, user mail agent, or HTTP gateway compatibility issues.  Smaller JWTs also help reduce\nbandwidth utilization, which may or may not be important depending on your application's volume or needs.\n\nIf you want to compress your JWT, you can use the ``JwtBuilder``'s  `compressWith(CompressionAlgorithm)` method.  For\nexample:\n\n[,java]\n----\nJwts.builder()\n\n   .compressWith(Jwts.ZIP.DEF) // DEFLATE compression algorithm\n\n   // .. etc ...\n----\n\nIf you use any of the algorithm constants in the `Jwts.ZIP` class, that's it, you're done.  You don't have to\ndo anything during parsing or configure the `JwtParserBuilder` for compression - JJWT will automatically decompress\nthe payload as expected.\n\n+++<a name=\"compression-custom\">++++++</a>+++\n\n=== Custom Compression Algorithm\n\nIf the default `Jwts.ZIP` compression algorithms are not suitable for your needs, you can create your own\n`CompressionAlgorithm` implementation(s).\n\nJust as you would with the default algorithms, you may specify that you want a JWT compressed by calling the\n``JwtBuilder``'s `compressWith` method, supplying your custom implementation instance.  For example:\n\n[,java]\n----\nCompressionAlgorithm myAlg = new MyCompressionAlgorithm();\n\nJwts.builder()\n\n   .compressWith(myAlg) // <----\n\n   // .. etc ...\n----\n\nWhen you call `compressWith`, the JWT `payload` will be compressed with your algorithm, and the\nhttps://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.3[`zip` (Compression Algorithm)]\nheader will automatically be set to the value returned by your algorithm's `algorithm.getId()` method as\nrequired by the JWT specification.\n\n+++<a name=\"compression-custom-locator\">++++++</a>+++\n// legacy link\nHowever, the `JwtParser` needs to be aware of this custom algorithm as well, so it can use it while parsing. You do this\nby modifying the ``JwtParserBuilder``'s `zip()` collection.  For example:\n\n[,java]\n----\nCompressionAlgorithm myAlg = new MyCompressionAlgorithm();\n\nJwts.parser()\n\n    .zip().add(myAlg).and() // <----\n\n    // .. etc ...\n----\n\nThis adds additional `CompressionAlgorithm` implementations to the parser's overall total set of supported compression\nalgorithms (which already includes all of the `Jwts.ZIP` algorithms by default).\n\nThe parser will then automatically check to see if the JWT `zip` header has been set to see if a compression\nalgorithm has been used to compress the JWT.  If set, the parser will automatically look up your\n`CompressionAlgorithm` by its `getId()` value, and use it to decompress the JWT.\n\n+++<a name=\"json\">++++++</a>+++\n\n== JSON Support\n\nA `JwtBuilder` will serialize the `Header` and `Claims` maps (and potentially any Java objects they\ncontain) to JSON with a `Serializer<Map<String, ?>>` instance.  Similarly, a `JwtParser` will\ndeserialize JSON into the `Header` and `Claims` using a `Deserializer<Map<String, ?>>` instance.\n\nIf you don't explicitly configure a ``JwtBuilder``'s `Serializer` or a ``JwtParserBuilder``'s `Deserializer`, JJWT will\nautomatically attempt to discover and use the following JSON implementations if found in the runtime classpath. +\nThey are checked in order, and the first one found is used:\n\n. Jackson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-jackson` as a project runtime\ndependency.  Jackson supports POJOs as claims with full marshaling/unmarshaling as necessary.\n. Gson: This will automatically be used if you specify `io.jsonwebtoken:jjwt-gson` as a project runtime dependency.\nGson also supports POJOs as claims with full marshaling/unmarshaling as necessary.\n. JSON-Java (`org.json`): This will be used automatically if you specify `io.jsonwebtoken:jjwt-orgjson` as a\nproject runtime dependency.\n+\n[NOTE]\n====\n`org.json` APIs are natively enabled in Android environments so this is the recommended JSON processor for\nAndroid applications _unless_ you want to use POJOs as claims.  The `org.json` library supports simple\nObject-to-JSON marshaling, but it _does not_ support JSON-to-Object unmarshalling.\n====\n\n*If you want to use POJOs as claim values, use either the `io.jsonwebtoken:jjwt-jackson` or\n`io.jsonwebtoken:jjwt-gson` dependency* (or implement your own Serializer and Deserializer if desired). *But beware*,\nJackson will force a sizable (> 1 MB) dependency to an Android application thus increasing the app download size for\nmobile users.\n\n+++<a name=\"json-custom\">++++++</a>+++\n\n=== Custom JSON Processor\n\nIf you don't want to use JJWT's runtime dependency approach, or just want to customize how JSON serialization and\ndeserialization works, you can implement the `Serializer` and `Deserializer` interfaces and specify instances of\nthem on the `JwtBuilder` and `JwtParserBuilder` respectively.  For example:\n\nWhen creating a JWT:\n\n[,java]\n----\nSerializer<Map<String,?>> serializer = getMySerializer(); //implement me\n\nJwts.builder()\n\n    .json(serializer)\n\n    // ... etc ...\n----\n\nWhen reading a JWT:\n\n[,java]\n----\nDeserializer<Map<String,?>> deserializer = getMyDeserializer(); //implement me\n\nJwts.parser()\n\n    .json(deserializer)\n\n    // ... etc ...\n----\n\n+++<a name=\"json-jackson\">++++++</a>+++\n\n=== Jackson JSON Processor\n\nIf you want to use Jackson for JSON processing, just including the `io.jsonwebtoken:jjwt-jackson` dependency as a\nruntime dependency is all that is necessary in most projects, since Gradle and Maven will automatically pull in\nthe necessary Jackson dependencies as well.\n\nAfter including this dependency, JJWT will automatically find the Jackson implementation on the runtime classpath and\nuse it internally for JSON parsing.  There is nothing else you need to do - JJWT will automatically create a new\nJackson ObjectMapper for its needs as required.\n\nHowever, if you have an application-wide Jackson `ObjectMapper` (as is typically recommended for most applications),\nyou can configure JJWT to use your own `ObjectMapper` instead.\n\nYou do this by declaring the `io.jsonwebtoken:jjwt-jackson` dependency with *compile* scope (not runtime\nscope which is the typical JJWT default).  That is:\n\n*Maven*\n\n[,xml,subs=\"+attributes\"]\n----\n<dependency>\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-jackson</artifactId>\n    <version>{project-version}</version>\n    <scope>compile</scope> <!-- Not runtime -->\n</dependency>\n----\n\n*Gradle or Android*\n\n[,groovy,subs=\"+attributes\"]\n----\ndependencies {\n    implementation 'io.jsonwebtoken:jjwt-jackson:{project-version}'\n}\n----\n\nAnd then you can specify the `JacksonSerializer` using your own `ObjectMapper` on the `JwtBuilder`:\n\n[,java]\n----\nObjectMapper objectMapper = getMyObjectMapper(); //implement me\n\nString jws = Jwts.builder()\n\n    .json(new JacksonSerializer(objectMapper))\n\n    // ... etc ...\n----\n\nand the `JacksonDeserializer` using your `ObjectMapper` on the `JwtParserBuilder`:\n\n[,java]\n----\nObjectMapper objectMapper = getMyObjectMapper(); //implement me\n\nJwts.parser()\n\n    .json(new JacksonDeserializer(objectMapper))\n\n    // ... etc ...\n----\n\n+++<a name=\"json-jackson-custom-types\">++++++</a>+++\n\n==== Parsing of Custom Claim Types\n\nBy default, JJWT will only convert simple claim types: String, Date, Long, Integer, Short and Byte.  If you need to\ndeserialize other types you can configure the `JacksonDeserializer` by passing a `Map` of claim names to types in\nthrough a constructor. For example:\n\n[,java]\n----\nnew JacksonDeserializer(Maps.of(\"user\", User.class).build())\n----\n\nThis would trigger the value in the `user` claim to be deserialized into the custom type of `User`.  Given the claims\npayload of:\n\n[,json]\n----\n{\n    \"issuer\": \"https://example.com/issuer\",\n    \"user\": {\n      \"firstName\": \"Jill\",\n      \"lastName\": \"Coder\"\n    }\n}\n----\n\nThe `User` object could be retrieved from the `user` claim with the following code:\n\n[,java]\n----\nJwts.parser()\n\n    .json(new JacksonDeserializer(Maps.of(\"user\", User.class).build())) // <-----\n\n    .build()\n\n    .parseUnprotectedClaims(aJwtString)\n\n    .getPayload()\n\n    .get(\"user\", User.class); // <-----\n----\n\n[NOTE]\n====\nUsing this constructor is mutually exclusive with the `JacksonDeserializer(ObjectMapper)` constructor\n<<json-jackson,described above>>. This is because JJWT configures an `ObjectMapper` directly and could have negative\nconsequences for a shared `ObjectMapper` instance. This should work for most applications, if you need a more advanced\nparsing options, <<json-jackson,configure the mapper directly>>.\n====\n\n+++<a name=\"json-gson\">++++++</a>+++\n\n=== Gson JSON Processor\n\nIf you want to use Gson for JSON processing, just including the `io.jsonwebtoken:jjwt-gson` dependency as a\nruntime dependency is all that is necessary in most projects, since Gradle and Maven will automatically pull in\nthe necessary Gson dependencies as well.\n\nAfter including this dependency, JJWT will automatically find the Gson implementation on the runtime classpath and\nuse it internally for JSON parsing.  There is nothing else you need to do - just declaring the dependency is\nall that is required, no code or config is necessary.\n\nIf you're curious, JJWT will automatically create an internal default Gson instance for its own needs as follows:\n\n[,java]\n----\nnew GsonBuilder()\n    .registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)\n    .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)\n    .setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)\n    .disableHtmlEscaping()\n    .create();\n----\n\nThe `registerTypeHierarchyAdapter` builder call is required to serialize JWKs with secret or private values.\n\nHowever, if you prefer to use a different Gson instance instead of JJWT's default, you can configure JJWT to use your\nown - just *don't forget to register the necessary JJWT type hierarchy adapter*.\n\nYou do this by declaring the `io.jsonwebtoken:jjwt-gson` dependency with *compile* scope (not runtime\nscope which is the typical JJWT default).  That is:\n\n*Maven*\n\n[,xml,subs=\"+attributes\"]\n----\n<dependency>\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-gson</artifactId>\n    <version>{project-version}</version>\n    <scope>compile</scope> <!-- Not runtime -->\n</dependency>\n----\n\n*Gradle or Android*\n\n[,groovy,subs=\"+attributes\"]\n----\ndependencies {\n    implementation 'io.jsonwebtoken:jjwt-gson:{project-version}'\n}\n----\n\nAnd then you can specify the `GsonSerializer` using your own `Gson` instance on the `JwtBuilder`:\n\n[,java]\n----\n\nGson gson = new GsonBuilder()\n    // don't forget this line!:\n    .registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)\n    .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)\n    .setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)\n    .disableHtmlEscaping().create();\n\nString jws = Jwts.builder()\n\n    .json(new GsonSerializer(gson))\n\n    // ... etc ...\n----\n\nand the `GsonDeserializer` using your `Gson` instance on the `JwtParser`:\n\n[,java]\n----\nGson gson = getGson(); //implement me\n\nJwts.parser()\n\n    .json(new GsonDeserializer(gson))\n\n    // ... etc ...\n----\n\nAgain, as shown above, it is critical to create your `Gson` instance using the `GsonBuilder` and include the line:\n\n[,java]\n----\n.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, GsonSupplierSerializer.INSTANCE)\n----\n\nto ensure JWK serialization works as expected.\n\n+++<a name=\"base64\">++++++</a>+++\n\n== Base64 Support\n\nJJWT uses a very fast pure-Java https://tools.ietf.org/html/rfc4648[Base64] codec for Base64 and\nBase64Url encoding and decoding that is guaranteed to work deterministically in all JDK and Android environments.\n\nYou can access JJWT's encoders and decoders using the `io.jsonwebtoken.io.Encoders` and `io.jsonwebtoken.io.Decoders`\nutility classes.\n\n`io.jsonwebtoken.io.Encoders`:\n\n* `BASE64` is an RFC 4648 https://tools.ietf.org/html/rfc4648#section-4[Base64] encoder\n* `BASE64URL` is an RFC 4648 https://tools.ietf.org/html/rfc4648#section-5[Base64URL] encoder\n\n`io.jsonwebtoken.io.Decoders`:\n\n* `BASE64` is an RFC 4648 https://tools.ietf.org/html/rfc4648#section-4[Base64] decoder\n* `BASE64URL` is an RFC 4648 https://tools.ietf.org/html/rfc4648#section-5[Base64URL] decoder\n\n+++<a name=\"base64-security\">++++++</a>+++\n\n=== Understanding Base64 in Security Contexts\n\nAll cryptographic operations, like encryption and message digest calculations, result in binary data - raw byte arrays.\n\nBecause raw byte arrays cannot be represented natively in JSON, the JWT\nspecifications employ the Base64URL encoding scheme to represent these raw byte values in JSON documents or compound\nstructures like a JWT.\n\nThis means that the Base64 and Base64URL algorithms take a raw byte array and converts the bytes into a string suitable\nto use in text documents and protocols like HTTP.  These algorithms can also convert these strings back\ninto the original raw byte arrays for decryption or signature verification as necessary.\n\nThat's nice and convenient, but there are two very important properties of Base64 (and Base64URL) text strings that\nare critical to remember when they are used in security scenarios like with JWTs:\n\n* <<base64-not-encryption,Base64 is not encryption>>\n* <<base64-changing-characters,Changing Base64 characters>> *does not automatically invalidate data*.\n\n+++<a name=\"base64-not-encryption\">++++++</a>+++\n\n==== Base64 is not encryption\n\nBase64-encoded text is _not_ encrypted.\n\nWhile a byte array representation can be converted to text with the Base64 algorithms,\nanyone in the world can take Base64-encoded text, decode it with any standard Base64 decoder, and obtain the\nunderlying raw byte array data.  No key or secret is required to decode Base64 text - anyone can do it.\n\nBased on this, when encoding sensitive byte data with Base64 - like a shared or private key - *the resulting\nstring is NOT safe to expose publicly*.\n\nA base64-encoded key is still sensitive information and must be kept as secret and as safe as the original source\nof the bytes (e.g. a Java `PrivateKey` or `SecretKey` instance).\n\nAfter Base64-encoding data into a string, it is possible to then encrypt the string to keep it safe from prying\neyes if desired, but this is different.  Encryption is not encoding.  They are separate concepts.\n\n+++<a name=\"base64-changing-characters\">++++++</a>+++\n\n==== Changing Base64 Characters\n\nIn an effort to see if signatures or encryption is truly validated correctly, some try to edit a JWT\nstring - particularly the Base64-encoded signature part - to see if the edited string fails security validations.\n\nThis conceptually makes sense: change the signature string, you would assume that signature validation would fail.\n\n_But this doesn't always work. Changing base64 characters is an invalid test_.\n\nWhy?\n\nBecause of the way the Base64 algorithm works, there are multiple Base64 strings that can represent the same raw byte\narray.\n\nGoing into the details of the Base64 algorithm is out of scope for this documentation, but there are many good\nStackoverflow https://stackoverflow.com/questions/33663113/multiple-strings-base64-decoded-to-same-byte-array?noredirect=1&lq=1[answers]\nand https://github.com/jwtk/jjwt/issues/211#issuecomment-283076269[JJWT issue comments] that explain this in detail.\nHere's one https://stackoverflow.com/questions/29941270/why-do-base64-decode-produce-same-byte-array-for-different-strings[good answer]:\n\n[IMPORTANT]\n====\nRemember that Base64 encodes each 8 bit entity into 6 bit chars. The resulting string then needs exactly\n11 * 8 / 6 bytes, or 14 2/3 chars. But you can't write partial characters. Only the first 4 bits (or 2/3 of the\nlast char) are significant. The last two bits are not decoded. Thus all of:\n\n[,text]\n----\ndGVzdCBzdHJpbmo\ndGVzdCBzdHJpbmp\ndGVzdCBzdHJpbmq\ndGVzdCBzdHJpbmr \n----\nAll decode to the same 11 bytes (116, 101, 115, 116, 32, 115, 116, 114, 105, 110, 106).\n====\n\nAs you can see by the above 4 examples, they all decode to the same exact 11 bytes.  So just changing one or two\ncharacters at the end of a Base64 string may not work and can often result in an invalid test.\n\n+++<a name=\"base64-invalid-characters\">++++++</a>+++\n\n===== Adding Invalid Characters\n\nJJWT's default Base64/Base64URL decoders automatically ignore illegal Base64 characters located in the beginning and\nend of an encoded string. Therefore, prepending or appending invalid characters like `{` or `]` or similar will also\nnot fail JJWT's signature checks either.  Why?\n\nBecause such edits - whether changing a trailing character or two, or appending invalid characters - do not actually\nchange the _real_ signature, which in cryptographic contexts, is always a byte array. Instead, tests like these\nchange a text encoding of the byte array, and as we covered above, they are different things.\n\nSo JJWT 'cares' more about the real byte array and less about its text encoding because that is what actually matters\nin cryptographic operations.  In this sense, JJWT follows the https://en.wikipedia.org/wiki/Robustness_principle[Robustness Principle]\nin being _slightly_ lenient on what is accepted per the rules of Base64, but if anything in the real underlying\nbyte array is changed, then yes, JJWT's cryptographic assertions will definitely fail.\n\nTo help understand JJWT's approach, we have to remember why signatures exist. From our documentation above on\n<<jws,signing JWTs>>:\n\n____\n* guarantees it was created by someone we know (it is authentic), as well as\n* guarantees that no-one has manipulated or changed it after it was created (its integrity is maintained).\n____\n\nJust prepending or appending invalid text to try to 'trick' the algorithm doesn't change the integrity of the\nunderlying claims or signature byte arrays, nor the authenticity of the claims byte array, because those byte\narrays are still obtained intact.\n\nPlease see https://github.com/jwtk/jjwt/issues/518[JJWT Issue #518] and its referenced issues and links for more\ninformation.\n\n+++<a name=\"base64-custom\">++++++</a>+++\n\n=== Custom Base64\n\nIf for some reason you want to specify your own Base64Url encoder and decoder, you can use the `JwtBuilder`\n`encoder` method to set the encoder:\n\n[,java]\n----\nEncoder<byte[], String> encoder = getMyBase64UrlEncoder(); //implement me\n\nString jws = Jwts.builder()\n\n    .b64Url(encoder)\n\n    // ... etc ...\n----\n\nand the ``JwtParserBuilder``'s `decoder` method to set the decoder:\n\n[,java]\n----\nDecoder<String, byte[]> decoder = getMyBase64UrlDecoder(); //implement me\n\nJwts.parser()\n\n    .b64Url(decoder)\n\n    // ... etc ...\n----\n\n+++<a name=\"examples\">++++++</a>+++\n\n== Examples\n\n* <<example-jws-hs,JWS Signed with HMAC>>\n* <<example-jws-rsa,JWS Signed with RSA>>\n* <<example-jws-ecdsa,JWS Signed with ECDSA>>\n* <<example-jws-eddsa,JWS Signed with EdDSA>>\n* <<example-jwe-dir,JWE Encrypted Directly with a SecretKey>>\n* <<example-jwe-rsa,JWE Encrypted with RSA>>\n* <<example-jwe-aeskw,JWE Encrypted with AES Key Wrap>>\n* <<example-jwe-ecdhes,JWE Encrypted with ECDH-ES>>\n* <<example-jwe-password,JWE Encrypted with a Password>>\n* <<example-jwk-secret,SecretKey JWK>>\n* <<example-jwk-rsapub,RSA Public JWK>>\n* <<example-jwk-rsapriv,RSA Private JWK>>\n* <<example-jwk-ecpub,Elliptic Curve Public JWK>>\n* <<example-jwk-ecpriv,Elliptic Curve Private JWK>>\n* <<example-jwk-edpub,Edwards Elliptic Curve Public JWK>>\n* <<example-jwk-edpriv,Edwards Elliptic Curve Private JWK>>\n\n+++<a name=\"example-jws-hs\">++++++</a>+++\n\n=== JWT Signed with HMAC\n\nThis is an example showing how to digitally sign a JWT using an https://en.wikipedia.org/wiki/HMAC[HMAC]\n(hash-based message authentication code).  The JWT specifications define 3 standard HMAC signing algorithms:\n\n* `HS256`: HMAC with SHA-256. This requires a 256-bit (32 byte) `SecretKey` or larger.\n* `HS384`: HMAC with SHA-384. This requires a 384-bit (48 byte) `SecretKey` or larger.\n* `HS512`: HMAC with SHA-512. This requires a 512-bit (64 byte) `SecretKey` or larger.\n\nExample:\n\n[,java]\n----\n// Create a test key suitable for the desired HMAC-SHA algorithm:\nMacAlgorithm alg = Jwts.SIG.HS512; //or HS384 or HS256\nSecretKey key = alg.key().build();\n\nString message = \"Hello World!\";\nbyte[] content = message.getBytes(StandardCharsets.UTF_8);\n\n// Create the compact JWS:\nString jws = Jwts.builder().content(content, \"text/plain\").signWith(key, alg).compact();\n\n// Parse the compact JWS:\ncontent = Jwts.parser().verifyWith(key).build().parseSignedContent(jws).getPayload();\n\nassert message.equals(new String(content, StandardCharsets.UTF_8));\n----\n\n+++<a name=\"example-jws-rsa\">++++++</a>+++\n\n=== JWT Signed with RSA\n\nThis is an example showing how to digitally sign and verify a JWT using RSA cryptography. The JWT specifications\ndefine <<jws-alg,6 standard RSA signing algorithms>>.  All 6 require that <<jws-key-rsa,RSA keys 2048-bits or larger>>\nmust be used.\n\nIn this example, Bob will sign a JWT using his RSA private key, and Alice can verify it came from Bob using Bob's RSA\npublic key:\n\n[,java]\n----\n// Create a test key suitable for the desired RSA signature algorithm:\nSignatureAlgorithm alg = Jwts.SIG.RS512; //or PS512, RS256, etc...\nKeyPair pair = alg.keyPair().build();\n\n// Bob creates the compact JWS with his RSA private key:\nString jws = Jwts.builder().subject(\"Alice\")\n    .signWith(pair.getPrivate(), alg) // <-- Bob's RSA private key\n    .compact();\n\n// Alice receives and verifies the compact JWS came from Bob:\nString subject = Jwts.parser()\n    .verifyWith(pair.getPublic()) // <-- Bob's RSA public key\n    .build().parseSignedClaims(jws).getPayload().getSubject();\n\nassert \"Alice\".equals(subject);\n----\n\n+++<a name=\"example-jws-ecdsa\">++++++</a>+++\n\n=== JWT Signed with ECDSA\n\nThis is an example showing how to digitally sign and verify a JWT using the Elliptic Curve Digital Signature Algorithm.\nThe JWT specifications define <<jws-alg,3 standard ECDSA signing algorithms>>:\n\n* `ES256`: ECDSA using P-256 and SHA-256. This requires an EC Key exactly 256 bits (32 bytes) long.\n* `ES384`: ECDSA using P-384 and SHA-384. This requires an EC Key exactly 384 bits (48 bytes) long.\n* `ES512`: ECDSA using P-521 and SHA-512. This requires an EC Key exactly 521 bits (65 or 66 bytes depending on format) long.\n\nIn this example, Bob will sign a JWT using his EC private key, and Alice can verify it came from Bob using Bob's EC\npublic key:\n\n[,java]\n----\n// Create a test key suitable for the desired ECDSA signature algorithm:\nSignatureAlgorithm alg = Jwts.SIG.ES512; //or ES256 or ES384\nKeyPair pair = alg.keyPair().build();\n\n// Bob creates the compact JWS with his EC private key:\nString jws = Jwts.builder().subject(\"Alice\")\n    .signWith(pair.getPrivate(), alg) // <-- Bob's EC private key\n    .compact();\n\n// Alice receives and verifies the compact JWS came from Bob:\nString subject = Jwts.parser()\n    .verifyWith(pair.getPublic()) // <-- Bob's EC public key\n    .build().parseSignedClaims(jws).getPayload().getSubject();\n\nassert \"Alice\".equals(subject);\n----\n\n+++<a name=\"example-jws-eddsa\">++++++</a>+++\n\n=== JWT Signed with EdDSA\n\nThis is an example showing how to digitally sign and verify a JWT using the\nhttps://www.rfc-editor.org/rfc/rfc8032[Edwards Curve Digital Signature Algorithm] using\n`Ed25519` or `Ed448` keys.\n\n[NOTE]\n====\nThe `Ed25519` and `Ed448` algorithms require JDK 15 or a compatible JCA Provider\n(like BouncyCastle) in the runtime classpath.\n\nIf you are using JDK 14 or earlier and you want to use them, see\nthe <<Installation,Installation>> section to see how to enable BouncyCastle.\n====\n\nThe `EdDSA` signature algorithm is defined for JWS in https://www.rfc-editor.org/rfc/rfc8037#section-3.1[RFC 8037, Section 3.1]\nusing keys for two Edwards curves:\n\n* `Ed25519`: `EdDSA` using curve `Ed25519`. `Ed25519` algorithm keys must be 255 bits long and produce\n           signatures 512 bits (64 bytes) long.\n* `Ed448`: `EdDSA` using curve `Ed448`. `Ed448` algorithm keys must be 448 bits long and produce signatures\n         912 bits (114 bytes) long.\n\nIn this example, Bob will sign a JWT using his Edwards Curve private key, and Alice can verify it came from Bob\nusing Bob's Edwards Curve public key:\n\n[,java]\n----\n// Create a test key suitable for the EdDSA signature algorithm using Ed25519 or Ed448 keys:\nCurve curve = Jwks.CRV.Ed25519; //or Ed448\nKeyPair pair = curve.keyPair().build();\n\n// Bob creates the compact JWS with his Edwards Curve private key:\nString jws = Jwts.builder().subject(\"Alice\")\n    .signWith(pair.getPrivate(), Jwts.SIG.EdDSA) // <-- Bob's Edwards Curve private key w/ EdDSA\n    .compact();\n\n// Alice receives and verifies the compact JWS came from Bob:\nString subject = Jwts.parser()\n    .verifyWith(pair.getPublic()) // <-- Bob's Edwards Curve public key\n    .build().parseSignedClaims(jws).getPayload().getSubject();\n\nassert \"Alice\".equals(subject);\n----\n\n+++<a name=\"example-jwe-dir\">++++++</a>+++\n\n=== JWT Encrypted Directly with a SecretKey\n\nThis is an example showing how to encrypt a JWT <<jwe-alg-dir,directly using a symmetric secret key>>.  The\nJWT specifications define <<jwe-enc,6 standard AEAD Encryption algorithms>>:\n\n* `A128GCM`: AES GCM using a 128-bit (16 byte) `SecretKey` or larger.\n* `A192GCM`: AES GCM using a 192-bit (24 byte) `SecretKey` or larger.\n* `A256GCM`: AES GCM using a 256-bit (32 byte) `SecretKey` or larger.\n* `A128CBC-HS256`: https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.3[AES_128_CBC_HMAC_SHA_256] using a\n256-bit (32 byte) `SecretKey`.\n* `A192CBC-HS384`: https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.4[AES_192_CBC_HMAC_SHA_384] using a\n384-bit (48 byte) `SecretKey`.\n* `A256CBC-HS512`: https://www.rfc-editor.org/rfc/rfc7518.html#section-5.2.5[AES_256_CBC_HMAC_SHA_512] using a\n512-bit (64 byte) `SecretKey`.\n\nThe AES GCM (`A128GCM`, `A192GCM` and `A256GCM`) algorithms are strongly recommended - they are faster and more\nefficient than the `A*CBC-HS*` variants, but they do require JDK 8 or later (or JDK 7 + BouncyCastle).\n\nExample:\n\n[,java]\n----\n// Create a test key suitable for the desired payload encryption algorithm:\n// (A*GCM algorithms are recommended, but require JDK >= 8 or BouncyCastle)\nAeadAlgorithm enc = Jwts.ENC.A256GCM; //or A128GCM, A192GCM, A256CBC-HS512, etc...\nSecretKey key = enc.key().build();\n\nString message = \"Live long and prosper.\";\nbyte[] content = message.getBytes(StandardCharsets.UTF_8);\n\n// Create the compact JWE:\nString jwe = Jwts.builder().content(content, \"text/plain\").encryptWith(key, enc).compact();\n\n// Parse the compact JWE:\ncontent = Jwts.parser().decryptWith(key).build().parseEncryptedContent(jwe).getPayload();\n\nassert message.equals(new String(content, StandardCharsets.UTF_8));\n----\n\n+++<a name=\"example-jwe-rsa\">++++++</a>+++\n\n=== JWT Encrypted with RSA\n\nThis is an example showing how to encrypt and decrypt a JWT using RSA cryptography.\n\nBecause RSA cannot encrypt much data, RSA is used to encrypt and decrypt a secure-random key, and that generated key\nin turn is used to actually encrypt the payload as described in the link:jwe-alg-rsa[RSA Key Encryption] section\nabove. As such, RSA Key Algorithms must be paired with an AEAD Encryption Algorithm, as shown below.\n\nIn this example, Bob will encrypt a JWT using Alice's RSA public key to ensure only she may read it.  Alice can then\ndecrypt the JWT using her RSA private key:\n\n[,java]\n----\n// Create a test KeyPair suitable for the desired RSA key algorithm:\nKeyPair pair = Jwts.SIG.RS512.keyPair().build();\n\n// Choose the key algorithm used encrypt the payload key:\nKeyAlgorithm<PublicKey, PrivateKey> alg = Jwts.KEY.RSA_OAEP_256; //or RSA_OAEP or RSA1_5\n// Choose the Encryption Algorithm to encrypt the payload:\nAeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n// Bob creates the compact JWE with Alice's RSA public key so only she may read it:\nString jwe = Jwts.builder().audience().add(\"Alice\").and()\n    .encryptWith(pair.getPublic(), alg, enc) // <-- Alice's RSA public key\n    .compact();\n\n// Alice receives and decrypts the compact JWE:\nSet<String> audience = Jwts.parser()\n    .decryptWith(pair.getPrivate()) // <-- Alice's RSA private key\n    .build().parseEncryptedClaims(jwe).getPayload().getAudience();\n\nassert audience.contains(\"Alice\");\n----\n\n+++<a name=\"example-jwe-aeskw\">++++++</a>+++\n\n=== JWT Encrypted with AES Key Wrap\n\nThis is an example showing how to encrypt and decrypt a JWT using AES Key Wrap algorithms.\n\nThese algorithms use AES to encrypt and decrypt a secure-random key, and that generated key in turn is used to actually encrypt\nthe payload as described in the link:jwe-alg-aes[AES Key Encryption] section above. This allows the payload to be\nencrypted with a random short-lived key, reducing material exposure of the potentially longer-lived symmetric secret\nkey.  This approach requires the AES Key Wrap algorithms to be paired with an AEAD content encryption algorithm,\nas shown below.\n\nThe AES GCM Key Wrap algorithms (`A128GCMKW`, `A192GCMKW` and `A256GCMKW`) are preferred - they are faster and more\nefficient than the `A*KW` variants, but they do require JDK 8 or later (or JDK 7 + BouncyCastle).\n\n[,java]\n----\n// Create a test SecretKey suitable for the desired AES Key Wrap algorithm:\nSecretKeyAlgorithm alg = Jwts.KEY.A256GCMKW; //or A192GCMKW, A128GCMKW, A256KW, etc...\nSecretKey key = alg.key().build();\n\n// Chooose the Encryption Algorithm used to encrypt the payload:\nAeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n// Create the compact JWE:\nString jwe = Jwts.builder().issuer(\"me\").encryptWith(key, alg, enc).compact();\n\n// Parse the compact JWE:\nString issuer = Jwts.parser().decryptWith(key).build()\n    .parseEncryptedClaims(jwe).getPayload().getIssuer();\n\nassert \"me\".equals(issuer);\n----\n\n+++<a name=\"example-jwe-ecdhes\">++++++</a>+++\n\n=== JWT Encrypted with ECDH-ES\n\nThis is an example showing how to encrypt and decrypt a JWT using Elliptic Curve Diffie-Hellman Ephemeral Static\nKey Agreement (ECDH-ES) algorithms.\n\nThese algorithms use ECDH-ES to encrypt and decrypt a secure-random key, and that\ngenerated key in turn is used to actually encrypt the payload as described in the\nlink:jwe-alg-ecdhes[Elliptic Curve Diffie-Hellman Ephemeral Static Key Agreement] section above. Because of this, ECDH-ES\nKey Algorithms must be paired with an AEAD Encryption Algorithm, as shown below.\n\nIn this example, Bob will encrypt a JWT using Alice's Elliptic Curve public key to ensure only she may read it. +\nAlice can then decrypt the JWT using her Elliptic Curve private key:\n\n[,java]\n----\n// Create a test KeyPair suitable for the desired EC key algorithm:\nKeyPair pair = Jwts.SIG.ES512.keyPair().build();\n\n// Choose the key algorithm used encrypt the payload key:\nKeyAlgorithm<PublicKey, PrivateKey> alg = Jwts.KEY.ECDH_ES_A256KW; //ECDH_ES_A192KW, etc...\n// Choose the Encryption Algorithm to encrypt the payload:\nAeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n// Bob creates the compact JWE with Alice's EC public key so only she may read it:\nString jwe = Jwts.builder().audience().add(\"Alice\").and()\n    .encryptWith(pair.getPublic(), alg, enc) // <-- Alice's EC public key\n    .compact();\n\n// Alice receives and decrypts the compact JWE:\nSet<String> audience = Jwts.parser()\n    .decryptWith(pair.getPrivate()) // <-- Alice's EC private key\n    .build().parseEncryptedClaims(jwe).getPayload().getAudience();\n\nassert audience.contains(\"Alice\");\n----\n\n+++<a name=\"example-jwe-password\">++++++</a>+++\n\n=== JWT Encrypted with a Password\n\nThis is an example showing how to encrypt and decrypt a JWT using Password-based key-derivation algorithms.\n\nThese algorithms use a password to securely derive a random key, and that derived random key in turn is used to actually\nencrypt the payload as described in the link:jwe-alg-pbes2[Password-based Key Encryption] section above. This allows\nthe payload to be encrypted with a random short-lived cryptographically-stronger key, reducing the need to\nexpose the longer-lived (and potentially weaker) password.\n\nThis approach requires the Password-based Key Wrap algorithms to be paired with an AEAD content encryption algorithm,\nas shown below.\n\n[,java]\n----\n//DO NOT use this example password in a real app, it is well-known to password crackers:\nString pw = \"correct horse battery staple\";\nPassword password = Keys.password(pw.toCharArray());\n\n// Choose the desired PBES2 key derivation algorithm:\nKeyAlgorithm<Password, Password> alg = Jwts.KEY.PBES2_HS512_A256KW; //or PBES2_HS384_A192KW or PBES2_HS256_A128KW\n\n// Optionally choose the number of PBES2 computational iterations to use to derive the key.\n// This is optional - if you do not specify a value, JJWT will automatically choose a value\n// based on your chosen PBES2 algorithm and OWASP PBKDF2 recommendations here:\n// https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\n//\n// If you do specify a value, ensure the iterations are large enough for your desired alg\n//int pbkdf2Iterations = 120000; //for HS512. Needs to be much higher for smaller hash algs.\n\n// Choose the Encryption Algorithm used to encrypt the payload:\nAeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n// Create the compact JWE:\nString jwe = Jwts.builder().issuer(\"me\")\n    // Optional work factor is specified in the header:\n    //.header().pbes2Count(pbkdf2Iterations)).and()\n    .encryptWith(password, alg, enc)\n    .compact();\n\n// Parse the compact JWE:\nString issuer = Jwts.parser().decryptWith(password)\n    .build().parseEncryptedClaims(jwe).getPayload().getIssuer();\n\nassert \"me\".equals(issuer);\n----\n\n+++<a name=\"example-jwk-secret\">++++++</a>+++\n\n=== SecretKey JWK\n\nExample creating and parsing a secret JWK:\n\n[,java]\n----\nSecretKey key = Jwts.SIG.HS512.key().build(); // or HS384 or HS256\nSecretJwk jwk = Jwks.builder().key(key).idFromThumbprint().build();\n\nassert jwk.getId().equals(jwk.thumbprint().toString());\nassert key.equals(jwk.toKey());\n\nbyte[] utf8Bytes = new JacksonSerializer().serialize(jwk); // or GsonSerializer(), etc\nString jwkJson = new String(utf8Bytes, StandardCharsets.UTF_8);\nJwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\nassert parsed instanceof SecretJwk;\nassert jwk.equals(parsed);\n----\n\n+++<a name=\"example-jwk-rsapub\">++++++</a>+++\n\n=== RSA Public JWK\n\nExample creating and parsing an RSA Public JWK:\n\n[,java]\n----\nRSAPublicKey key = (RSAPublicKey)Jwts.SIG.RS512.keyPair().build().getPublic();\nRsaPublicJwk jwk = Jwks.builder().key(key).idFromThumbprint().build();\n\nassert jwk.getId().equals(jwk.thumbprint().toString());\nassert key.equals(jwk.toKey());\n\nbyte[] utf8Bytes = new JacksonSerializer().serialize(jwk); // or GsonSerializer(), etc\nString jwkJson = new String(utf8Bytes, StandardCharsets.UTF_8);\nJwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\nassert parsed instanceof RsaPublicJwk;\nassert jwk.equals(parsed);\n----\n\n+++<a name=\"example-jwk-rsapriv\">++++++</a>+++\n\n=== RSA Private JWK\n\nExample creating and parsing an RSA Private JWK:\n\n[,java]\n----\nKeyPair pair = Jwts.SIG.RS512.keyPair().build();\nRSAPublicKey pubKey = (RSAPublicKey) pair.getPublic();\nRSAPrivateKey privKey = (RSAPrivateKey) pair.getPrivate();\n\nRsaPrivateJwk privJwk = Jwks.builder().key(privKey).idFromThumbprint().build();\nRsaPublicJwk pubJwk = privJwk.toPublicJwk();\n\nassert privJwk.getId().equals(privJwk.thumbprint().toString());\nassert pubJwk.getId().equals(pubJwk.thumbprint().toString());\nassert privKey.equals(privJwk.toKey());\nassert pubKey.equals(pubJwk.toKey());\n\nbyte[] utf8Bytes = new JacksonSerializer().serialize(privJwk); // or GsonSerializer(), etc\nString jwkJson = new String(utf8Bytes, StandardCharsets.UTF_8);\nJwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\nassert parsed instanceof RsaPrivateJwk;\nassert privJwk.equals(parsed);\n----\n\n+++<a name=\"example-jwk-ecpub\">++++++</a>+++\n\n=== Elliptic Curve Public JWK\n\nExample creating and parsing an Elliptic Curve Public JWK:\n\n[,java]\n----\nECPublicKey key = (ECPublicKey) Jwts.SIG.ES512.keyPair().build().getPublic();\nEcPublicJwk jwk = Jwks.builder().key(key).idFromThumbprint().build();\n\nassert jwk.getId().equals(jwk.thumbprint().toString());\nassert key.equals(jwk.toKey());\n\nbyte[] utf8Bytes = new JacksonSerializer().serialize(jwk); // or GsonSerializer(), etc\nString jwkJson = new String(utf8Bytes, StandardCharsets.UTF_8);\nJwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\nassert parsed instanceof EcPublicJwk;\nassert jwk.equals(parsed);\n----\n\n+++<a name=\"example-jwk-ecpriv\">++++++</a>+++\n\n=== Elliptic Curve Private JWK\n\nExample creating and parsing an Elliptic Curve Private JWK:\n\n[,java]\n----\nKeyPair pair = Jwts.SIG.ES512.keyPair().build();\nECPublicKey pubKey = (ECPublicKey) pair.getPublic();\nECPrivateKey privKey = (ECPrivateKey) pair.getPrivate();\n\nEcPrivateJwk privJwk = Jwks.builder().key(privKey).idFromThumbprint().build();\nEcPublicJwk pubJwk = privJwk.toPublicJwk();\n\nassert privJwk.getId().equals(privJwk.thumbprint().toString());\nassert pubJwk.getId().equals(pubJwk.thumbprint().toString());\nassert privKey.equals(privJwk.toKey());\nassert pubKey.equals(pubJwk.toKey());\n\nbyte[] utf8Bytes = new JacksonSerializer().serialize(privJwk); // or GsonSerializer(), etc\nString jwkJson = new String(utf8Bytes, StandardCharsets.UTF_8);\nJwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\nassert parsed instanceof EcPrivateJwk;\nassert privJwk.equals(parsed);\n----\n\n+++<a name=\"example-jwk-edpub\">++++++</a>+++\n\n=== Edwards Elliptic Curve Public JWK\n\nExample creating and parsing an Edwards Elliptic Curve (Ed25519, Ed448, X25519, X448) Public JWK\n(the JWT https://www.rfc-editor.org/rfc/rfc8037[RFC 8037] specification calls these `Octet` keys, hence the\n`OctetPublicJwk` interface names):\n\n[,java]\n----\nPublicKey key = Jwks.CRV.Ed25519.keyPair().build().getPublic(); // or Ed448, X25519, X448\nOctetPublicJwk<PublicKey> jwk = builder().octetKey(key).idFromThumbprint().build();\n\nassert jwk.getId().equals(jwk.thumbprint().toString());\nassert key.equals(jwk.toKey());\n\nbyte[] utf8Bytes = new JacksonSerializer().serialize(jwk); // or GsonSerializer(), etc\nString jwkJson = new String(utf8Bytes, StandardCharsets.UTF_8);\nJwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\nassert parsed instanceof OctetPublicJwk;\nassert jwk.equals(parsed);\n----\n\n+++<a name=\"example-jwk-edpriv\">++++++</a>+++\n\n=== Edwards Elliptic Curve Private JWK\n\nExample creating and parsing an Edwards Elliptic Curve (Ed25519, Ed448, X25519, X448) Private JWK\n(the JWT https://www.rfc-editor.org/rfc/rfc8037[RFC 8037] specification calls these `Octet` keys, hence the\n`OctetPrivateJwk` and `OctetPublicJwk` interface names):\n\n[,java]\n----\nKeyPair pair = Jwks.CRV.Ed448.keyPair().build(); // or Ed25519, X25519, X448\nPublicKey pubKey = pair.getPublic();\nPrivateKey privKey = pair.getPrivate();\n\nOctetPrivateJwk<PrivateKey, PublicKey> privJwk = builder().octetKey(privKey).idFromThumbprint().build();\nOctetPublicJwk<PublicKey> pubJwk = privJwk.toPublicJwk();\n\nassert privJwk.getId().equals(privJwk.thumbprint().toString());\nassert pubJwk.getId().equals(pubJwk.thumbprint().toString());\nassert privKey.equals(privJwk.toKey());\nassert pubKey.equals(pubJwk.toKey());\n\nbyte[] utf8Bytes = new JacksonSerializer().serialize(privJwk); // or GsonSerializer(), etc\nString jwkJson = new String(utf8Bytes, StandardCharsets.UTF_8);\nJwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\nassert parsed instanceof OctetPrivateJwk;\nassert privJwk.equals(parsed);\n----\n\n== Learn More\n\n* https://web.archive.org/web/20230427122653/https://stormpath.com/blog/jjwt-how-it-works-why[JSON Web Token for Java and Android]\n* https://web.archive.org/web/20230426235608/https://stormpath.com/blog/jwt-java-create-verify[How to Create and Verify JWTs in Java]\n* https://web.archive.org/web/20230428094039/https://stormpath.com/blog/where-to-store-your-jwts-cookies-vs-html5-web-storage[Where to Store Your JWTs - Cookies vs HTML5 Web Storage]\n* https://web.archive.org/web/20230428184004/https://stormpath.com/blog/jwt-the-right-way[Use JWT the Right Way!]\n* https://web.archive.org/web/20230427151310/https://stormpath.com/blog/token-auth-for-java[Token Authentication for Java Applications]\n* xref:CHANGELOG.adoc[JJWT Changelog]\n\n== Author\n\nMaintained by Les Hazlewood & the extended Java community :heart:\n\n+++<a name=\"license\">++++++</a>+++\n\n== License\n\nThis project is open-source via the http://www.apache.org/licenses/LICENSE-2.0[Apache 2.0 License].\n"
  },
  {
    "path": "SECURITY.md",
    "content": "Thanks for helping make JJWT safe for everyone.\n\n# Security Policy\n\nThe JJWT development team are security professionals who take security seriously.  However, as we are an unpaid team of volunteers, we are unable to offer a bug bounty program.  Even so, we welcome any potential good faith security reports.\n\n## Supported Versions\n\nAs JJWT isn't yet at version 1.0, only the latest minor and point revisions are supported for security fixes.  \nWe ask that all users or security researchers upgrade to the latest stable release version and use that for testing before issuing a security report.\n\n| Version  | Supported          |\n| -------- | ------------------ |\n| 0.12.x   | :white_check_mark: |\n| < 0.12.0 | :x:                |\n\n## Reporting Security Issues\n\nIf you believe you have found a security vulnerability in the JJWT codebase, please report it to us through coordinated disclosure.\n\n**Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.**\n\nInstead, please send an email to security[@]jjwt.org.\n\nPlease include as much of the information listed below as you can to help us better understand and resolve the issue:\n\n  * The type of issue (e.g., buffer overflow, invalid header behavior, etc)\n  * Full paths of source file(s) related to the manifestation of the issue\n  * The location of the affected source code (tag/branch/commit or direct URL)\n  * Any special configuration required to reproduce the issue\n  * Step-by-step instructions to reproduce the issue\n  * Proof-of-concept or exploit code (if possible)\n  * Impact of the issue, including how an attacker might exploit the issue\n\nThis information will help us triage your report more quickly.\n\n### Valid Issues\n\nIf we find the report to be valid - that is, we recognize it as actual security issue that needs to be fixed in the codebase - \nwe will work with you to identify a timeline for a public fix to be released.\n\nPlease do not publish any details related to the issue in any communication medium (blog posts, social media posts, etc) \nexcept via the above JJWT security email address.  This allows us to create and publish a pointfix release that \ncontains the necessary fix(es) to the public before public discussion might occur, allowing JJWT users to fix their applications.  \n\nOnce the fix is publicly released, we ask for one week of time to pass to allow application developers to upgrade to this \npointfix security release before publishing public communication or analysis (blog posts, etc) about the security vulnerability.\n\n### Invalid Issues\n\nIf we find that a report is not a problem with the JJWT codebase - such as a problem with how JJWT is being used, or counter to or in conflict with JJWT's documentation - we \nwill explain why we do not consider it a security issue and explain the expected solution.\n"
  },
  {
    "path": "api/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2018 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt-api</artifactId>\n    <name>JJWT :: API</name>\n    <packaging>jar</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/..</jjwt.root>\n    </properties>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.github.siom79.japicmp</groupId>\n                <artifactId>japicmp-maven-plugin</artifactId>\n                <executions>\n                    <execution>\n                        <id>japicmp</id>\n                        <goals>\n                            <goal>cmp</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/ClaimJwtException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * ClaimJwtException is a subclass of the {@link JwtException} that is thrown after a validation of an JWT claim failed.\n *\n * @since 0.5\n */\npublic abstract class ClaimJwtException extends JwtException {\n\n    /**\n     * Deprecated as this is an implementation detail accidentally exposed in the JJWT 0.5 public API. It is no\n     * longer referenced anywhere in JJWT's implementation and will be removed in a future release.\n     *\n     * @deprecated will be removed in a future release.\n     */\n    @Deprecated\n    public static final String INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE = \"Expected %s claim to be: %s, but was: %s.\";\n\n    /**\n     * Deprecated as this is an implementation detail accidentally exposed in the JJWT 0.5 public API. It is no\n     * longer referenced anywhere in JJWT's implementation and will be removed in a future release.\n     *\n     * @deprecated will be removed in a future release.\n     */\n    @Deprecated\n    public static final String MISSING_EXPECTED_CLAIM_MESSAGE_TEMPLATE = \"Expected %s claim to be: %s, but was not present in the JWT claims.\";\n\n    /**\n     * The header associated with the Claims that failed validation.\n     */\n    private final Header header;\n\n    /**\n     * The Claims that failed validation.\n     */\n    private final Claims claims;\n\n    /**\n     * Creates a new instance with the specified header, claims and exception message.\n     *\n     * @param header  the header inspected\n     * @param claims  the claims obtained\n     * @param message the exception message\n     */\n    protected ClaimJwtException(Header header, Claims claims, String message) {\n        super(message);\n        this.header = header;\n        this.claims = claims;\n    }\n\n    /**\n     * Creates a new instance with the specified header, claims and exception message as a result of encountering\n     * the specified {@code cause}.\n     *\n     * @param header  the header inspected\n     * @param claims  the claims obtained\n     * @param message the exception message\n     * @param cause   the exception that caused this ClaimJwtException to be thrown.\n     */\n    protected ClaimJwtException(Header header, Claims claims, String message, Throwable cause) {\n        super(message, cause);\n        this.header = header;\n        this.claims = claims;\n    }\n\n    /**\n     * Returns the {@link Claims} that failed validation.\n     *\n     * @return the {@link Claims} that failed validation.\n     */\n    public Claims getClaims() {\n        return claims;\n    }\n\n    /**\n     * Returns the header associated with the {@link #getClaims() claims} that failed validation.\n     *\n     * @return the header associated with the {@link #getClaims() claims} that failed validation.\n     */\n    public Header getHeader() {\n        return header;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Claims.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * A JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4\">Claims set</a>.\n *\n * <p>This is an immutable JSON map with convenient type-safe getters for JWT standard claim names.</p>\n *\n * <p>Additionally, this interface also extends <code>Map&lt;String, Object&gt;</code>, so you can use standard\n * {@code Map} accessor/iterator methods as desired, for example:</p>\n *\n * <blockquote><pre>\n * claims.get(\"someKey\");</pre></blockquote>\n *\n * <p>However, because {@code Claims} instances are immutable, calling any of the map mutation methods\n * (such as {@code Map.}{@link Map#put(Object, Object) put}, etc) will result in a runtime exception.  The\n * {@code Map} interface is implemented specifically for the convenience of working with existing Map-based utilities\n * and APIs.</p>\n *\n * @since 0.1\n */\npublic interface Claims extends Map<String, Object>, Identifiable {\n\n    /**\n     * JWT {@code Issuer} claims parameter name: <code>\"iss\"</code>\n     */\n    String ISSUER = \"iss\";\n\n    /**\n     * JWT {@code Subject} claims parameter name: <code>\"sub\"</code>\n     */\n    String SUBJECT = \"sub\";\n\n    /**\n     * JWT {@code Audience} claims parameter name: <code>\"aud\"</code>\n     */\n    String AUDIENCE = \"aud\";\n\n    /**\n     * JWT {@code Expiration} claims parameter name: <code>\"exp\"</code>\n     */\n    String EXPIRATION = \"exp\";\n\n    /**\n     * JWT {@code Not Before} claims parameter name: <code>\"nbf\"</code>\n     */\n    String NOT_BEFORE = \"nbf\";\n\n    /**\n     * JWT {@code Issued At} claims parameter name: <code>\"iat\"</code>\n     */\n    String ISSUED_AT = \"iat\";\n\n    /**\n     * JWT {@code JWT ID} claims parameter name: <code>\"jti\"</code>\n     */\n    String ID = \"jti\";\n\n    /**\n     * Returns the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1\">\n     * <code>iss</code></a> (issuer) value or {@code null} if not present.\n     *\n     * @return the JWT {@code iss} value or {@code null} if not present.\n     */\n    String getIssuer();\n\n    /**\n     * Returns the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2\">\n     * <code>sub</code></a> (subject) value or {@code null} if not present.\n     *\n     * @return the JWT {@code sub} value or {@code null} if not present.\n     */\n    String getSubject();\n\n    /**\n     * Returns the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3\">\n     * <code>aud</code></a> (audience) value or {@code null} if not present.\n     *\n     * @return the JWT {@code aud} value or {@code null} if not present.\n     */\n    Set<String> getAudience();\n\n    /**\n     * Returns the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4\">\n     * <code>exp</code></a> (expiration) timestamp or {@code null} if not present.\n     *\n     * <p>A JWT obtained after this timestamp should not be used.</p>\n     *\n     * @return the JWT {@code exp} value or {@code null} if not present.\n     */\n    Date getExpiration();\n\n    /**\n     * Returns the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5\">\n     * <code>nbf</code></a> (not before) timestamp or {@code null} if not present.\n     *\n     * <p>A JWT obtained before this timestamp should not be used.</p>\n     *\n     * @return the JWT {@code nbf} value or {@code null} if not present.\n     */\n    Date getNotBefore();\n\n    /**\n     * Returns the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6\">\n     * <code>iat</code></a> (issued at) timestamp or {@code null} if not present.\n     *\n     * <p>If present, this value is the timestamp when the JWT was created.</p>\n     *\n     * @return the JWT {@code iat} value or {@code null} if not present.\n     */\n    Date getIssuedAt();\n\n    /**\n     * Returns the JWTs <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7\">\n     * <code>jti</code></a> (JWT ID) value or {@code null} if not present.\n     *\n     * <p>This value is a CaSe-SenSiTiVe unique identifier for the JWT. If available, this value is expected to be\n     * assigned in a manner that ensures that there is a negligible probability that the same value will be\n     * accidentally\n     * assigned to a different data object.  The ID can be used to prevent the JWT from being replayed.</p>\n     *\n     * @return the JWT {@code jti} value or {@code null} if not present.\n     */\n    @Override\n    // just for JavaDoc specific to the JWT spec\n    String getId();\n\n    /**\n     * Returns the JWTs claim ({@code claimName}) value as a {@code requiredType} instance, or {@code null} if not\n     * present.\n     *\n     * <p>JJWT only converts simple String, Date, Long, Integer, Short and Byte types automatically. Anything more\n     * complex is expected to be already converted to your desired type by the JSON parser. You may specify a custom\n     * JSON processor using the {@code JwtParserBuilder}'s\n     * {@link JwtParserBuilder#json(io.jsonwebtoken.io.Deserializer) json(Deserializer)} method. See the JJWT\n     * documentation on <a href=\"https://github.com/jwtk/jjwt#custom-json-processor\">custom JSON processor</a>s for more\n     * information. If using Jackson, you can specify custom claim POJO types as described in\n     * <a href=\"https://github.com/jwtk/jjwt#json-jackson-custom-types\">custom claim types</a>.\n     *\n     * @param claimName    name of claim\n     * @param requiredType the type of the value expected to be returned\n     * @param <T>          the type of the value expected to be returned\n     * @return the JWT {@code claimName} value or {@code null} if not present.\n     * @throws RequiredTypeException throw if the claim value is not null and not of type {@code requiredType}\n     * @see <a href=\"https://github.com/jwtk/jjwt#json-support\">JJWT JSON Support</a>\n     */\n    <T> T get(String claimName, Class<T> requiredType);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/ClaimsBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.lang.Builder;\nimport io.jsonwebtoken.lang.MapMutator;\n\n/**\n * {@link Builder} used to create an immutable {@link Claims} instance.\n *\n * @see JwtBuilder\n * @see Claims\n * @since 0.12.0\n */\npublic interface ClaimsBuilder extends MapMutator<String, Object, ClaimsBuilder>, ClaimsMutator<ClaimsBuilder>, Builder<Claims> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/ClaimsMutator.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.lang.NestedCollection;\n\nimport java.util.Collection;\nimport java.util.Date;\n\n/**\n * Mutation (modifications) to a {@link io.jsonwebtoken.Claims Claims} instance.\n *\n * @param <T> the type of mutator\n * @see io.jsonwebtoken.JwtBuilder\n * @see io.jsonwebtoken.Claims\n * @since 0.2\n */\npublic interface ClaimsMutator<T extends ClaimsMutator<T>> {\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1\">\n     * <code>iss</code></a> (issuer) claim.  A {@code null} value will remove the property from the JSON Claims map.\n     *\n     * @param iss the JWT {@code iss} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @deprecated since 0.12.0 in favor of the shorter and more modern builder-style named\n     * {@link #issuer(String)}. This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    T setIssuer(String iss);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1\">\n     * <code>iss</code></a> (issuer) claim.  A {@code null} value will remove the property from the JSON Claims map.\n     *\n     * @param iss the JWT {@code iss} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @since 0.12.0\n     */\n    T issuer(String iss);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2\">\n     * <code>sub</code></a> (subject) claim.  A {@code null} value will remove the property from the JSON Claims map.\n     *\n     * @param sub the JWT {@code sub} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @deprecated since 0.12.0 in favor of the shorter and more modern builder-style named\n     * {@link #subject(String)}. This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    T setSubject(String sub);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2\">\n     * <code>sub</code></a> (subject) claim.  A {@code null} value will remove the property from the JSON Claims map.\n     *\n     * @param sub the JWT {@code sub} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @since 0.12.0\n     */\n    T subject(String sub);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3\"><code>aud</code> (audience)\n     * claim</a> as <em>a single String, <b>NOT</b> a String array</em>.  This method exists only for producing\n     * JWTs sent to legacy recipients that are unable to interpret the {@code aud} value as a JSON String Array; it is\n     * strongly recommended to avoid calling this method whenever possible and favor the\n     * {@link #audience()}.{@link AudienceCollection#add(Object) add(String)} and\n     * {@link AudienceCollection#add(Collection) add(Collection)} methods instead, as they ensure a single\n     * deterministic data type for recipients.\n     *\n     * @param aud the JWT {@code aud} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @deprecated since 0.12.0 in favor of {@link #audience()}. This method will be removed before\n     * the JJWT 1.0 release.\n     */\n    @Deprecated\n    T setAudience(String aud);\n\n    /**\n     * Configures the JWT\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3\"><code>aud</code></a> (audience) Claim\n     * set, quietly ignoring any null, empty, whitespace-only, or existing value already in the set.\n     *\n     * <p>When finished, the {@code audience} collection's {@link AudienceCollection#and() and()} method may be used\n     * to continue configuration. For example:</p>\n     * <blockquote><pre>\n     *  Jwts.builder() // or Jwts.claims()\n     *\n     *     .audience().add(\"anAudience\")<b>.and() // return parent</b>\n     *\n     *  .subject(\"Joe\") // resume configuration...\n     *  // etc...\n     * </pre></blockquote>\n     *\n     * @return the {@link AudienceCollection AudienceCollection} to use for {@code aud} configuration.\n     * @see AudienceCollection AudienceCollection\n     * @see AudienceCollection#single(String) AudienceCollection.single(String)\n     * @since 0.12.0\n     */\n    AudienceCollection<T> audience();\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4\">\n     * <code>exp</code></a> (expiration) timestamp claim.  A {@code null} value will remove the property from the\n     * JSON Claims map.\n     *\n     * <p>A JWT obtained after this timestamp should not be used.</p>\n     *\n     * @param exp the JWT {@code exp} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @deprecated since 0.12.0 in favor of the shorter and more modern builder-style named\n     * {@link #expiration(Date)}. This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    T setExpiration(Date exp);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4\">\n     * <code>exp</code></a> (expiration) timestamp claim.  A {@code null} value will remove the property from the\n     * JSON Claims map.\n     *\n     * <p>A JWT obtained after this timestamp should not be used.</p>\n     *\n     * @param exp the JWT {@code exp} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @since 0.12.0\n     */\n    T expiration(Date exp);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5\">\n     * <code>nbf</code></a> (not before) timestamp claim.  A {@code null} value will remove the property from the\n     * JSON Claims map.\n     *\n     * <p>A JWT obtained before this timestamp should not be used.</p>\n     *\n     * @param nbf the JWT {@code nbf} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @deprecated since 0.12.0 in favor of the shorter and more modern builder-style named\n     * {@link #notBefore(Date)}. This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    T setNotBefore(Date nbf);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5\">\n     * <code>nbf</code></a> (not before) timestamp claim.  A {@code null} value will remove the property from the\n     * JSON Claims map.\n     *\n     * <p>A JWT obtained before this timestamp should not be used.</p>\n     *\n     * @param nbf the JWT {@code nbf} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @since 0.12.0\n     */\n    T notBefore(Date nbf);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6\">\n     * <code>iat</code></a> (issued at) timestamp claim.  A {@code null} value will remove the property from the\n     * JSON Claims map.\n     *\n     * <p>The value is the timestamp when the JWT was created.</p>\n     *\n     * @param iat the JWT {@code iat} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @deprecated since 0.12.0 in favor of the shorter and more modern builder-style named\n     * {@link #issuedAt(Date)}. This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    T setIssuedAt(Date iat);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6\">\n     * <code>iat</code></a> (issued at) timestamp claim.  A {@code null} value will remove the property from the\n     * JSON Claims map.\n     *\n     * <p>The value is the timestamp when the JWT was created.</p>\n     *\n     * @param iat the JWT {@code iat} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @since 0.12.0\n     */\n    T issuedAt(Date iat);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7\">\n     * <code>jti</code></a> (JWT ID) claim.  A {@code null} value will remove the property from the JSON Claims map.\n     *\n     * <p>This value is a CaSe-SenSiTiVe unique identifier for the JWT. If specified, this value MUST be assigned in a\n     * manner that ensures that there is a negligible probability that the same value will be accidentally\n     * assigned to a different data object.  The ID can be used to prevent the JWT from being replayed.</p>\n     *\n     * @param jti the JWT {@code jti} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @deprecated since 0.12.0 in favor of the shorter and more modern builder-style named\n     * {@link #id(String)}. This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    T setId(String jti);\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7\">\n     * <code>jti</code></a> (JWT ID) claim.  A {@code null} value will remove the property from the JSON Claims map.\n     *\n     * <p>This value is a CaSe-SenSiTiVe unique identifier for the JWT. If specified, this value MUST be assigned in a\n     * manner that ensures that there is a negligible probability that the same value will be accidentally\n     * assigned to a different data object.  The ID can be used to prevent the JWT from being replayed.</p>\n     *\n     * @param jti the JWT {@code jti} value or {@code null} to remove the property from the JSON map.\n     * @return the {@code Claims} instance for method chaining.\n     * @since 0.12.0\n     */\n    T id(String jti);\n\n    /**\n     * A {@code NestedCollection} for setting {@link #audience()} values that also allows overriding the collection\n     * to be a {@link #single(String) single string value} for legacy JWT recipients if necessary.\n     *\n     * <p>Because this interface extends {@link NestedCollection}, the {@link #and()} method may be used to continue\n     * parent configuration. For example:</p>\n     * <blockquote><pre>\n     *  Jwts.builder() // or Jwts.claims()\n     *\n     *     .audience().add(\"anAudience\")<b>.and() // return parent</b>\n     *\n     *  .subject(\"Joe\") // resume parent configuration...\n     *  // etc...</pre></blockquote>\n     *\n     * @param <P> the type of ClaimsMutator to return for method chaining.\n     * @see #single(String)\n     * @since 0.12.0\n     */\n    interface AudienceCollection<P> extends NestedCollection<String, P> {\n\n        /**\n         * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.3\"><code>aud</code> (audience)\n         * Claim</a> as <em>a single String, <b>NOT</b> a String array</em>.  This method exists only for producing\n         * JWTs sent to legacy recipients that are unable to interpret the {@code aud} value as a JSON String Array;\n         * it is strongly recommended to avoid calling this method whenever possible and favor the\n         * {@link #add(Object) add(String)} or {@link #add(Collection)} methods instead, as they ensure a single\n         * deterministic data type for recipients.\n         *\n         * @param aud the value to use as the {@code aud} Claim single-String value (and not an array of Strings), or\n         *            {@code null}, empty or whitespace to remove the property from the JSON map.\n         * @return the instance for method chaining\n         * @since 0.12.0\n         * @deprecated This is technically not deprecated because the JWT RFC mandates support for single string values,\n         * but it is marked as deprecated to discourage its use when possible.\n         */\n        // DO NOT REMOVE EVER. This is a required RFC feature, but marked as deprecated to discourage its use\n        @Deprecated\n        P single(String aud);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Clock.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport java.util.Date;\n\n/**\n * A clock represents a time source that can be used when creating and verifying JWTs.\n *\n * @since 0.7.0\n */\npublic interface Clock {\n\n    /**\n     * Returns the clock's current timestamp at the instant the method is invoked.\n     *\n     * @return the clock's current timestamp at the instant the method is invoked.\n     */\n    Date now();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/CompressionCodec.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.io.CompressionAlgorithm;\n\n/**\n * Compresses and decompresses byte arrays according to a compression algorithm.\n *\n * <p><b>&quot;zip&quot; identifier</b></p>\n *\n * <p>{@code CompressionCodec} extends {@code Identifiable}; the value returned from\n * {@link Identifiable#getId() getId()} will be used as the JWT\n * <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.3\"><code>zip</code></a> header value.</p>\n *\n * @see Jwts.ZIP#DEF\n * @see Jwts.ZIP#GZIP\n * @since 0.6.0\n * @deprecated since 0.12.0 in favor of {@link io.jsonwebtoken.io.CompressionAlgorithm} to equal the RFC name for this concept.\n */\n@Deprecated\npublic interface CompressionCodec extends CompressionAlgorithm {\n\n    /**\n     * The algorithm name to use as the JWT\n     * <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.3\"><code>zip</code></a> header value.\n     *\n     * @return the algorithm name to use as the JWT\n     * <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.3\"><code>zip</code></a> header value.\n     * @deprecated since 0.12.0 in favor of {@link #getId()} to ensure congruence with\n     * all other identifiable algorithms.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    String getAlgorithmName();\n\n    /**\n     * Compresses the specified byte array, returning the compressed byte array result.\n     *\n     * @param content bytes to compress\n     * @return compressed bytes\n     * @throws CompressionException if the specified byte array cannot be compressed.\n     */\n    @Deprecated\n    byte[] compress(byte[] content) throws CompressionException;\n\n    /**\n     * Decompresses the specified compressed byte array, returning the decompressed byte array result.  The\n     * specified byte array must already be in compressed form.\n     *\n     * @param compressed compressed bytes\n     * @return decompressed bytes\n     * @throws CompressionException if the specified byte array cannot be decompressed.\n     */\n    @Deprecated\n    byte[] decompress(byte[] compressed) throws CompressionException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/CompressionCodecResolver.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Looks for a JWT {@code zip} header, and if found, returns the corresponding {@link CompressionCodec} the parser\n * can use to decompress the JWT body.\n *\n * <p>JJWT's default {@link JwtParser} implementation supports both the\n * {@link Jwts.ZIP#DEF DEFLATE} and {@link Jwts.ZIP#GZIP GZIP} algorithms by default - you do not need to\n * specify a {@code CompressionCodecResolver} in these cases.</p>\n *\n * <p>However, if you want to use a compression algorithm other than {@code DEF} or {@code GZIP}, you can implement\n * your own {@link CompressionCodecResolver} and specify that when\n * {@link io.jsonwebtoken.JwtBuilder#compressWith(io.jsonwebtoken.io.CompressionAlgorithm) building} and\n * {@link io.jsonwebtoken.JwtParserBuilder#setCompressionCodecResolver(CompressionCodecResolver) parsing} JWTs.</p>\n *\n * @see JwtParserBuilder#setCompressionCodecResolver(CompressionCodecResolver)\n * @see JwtParserBuilder#zip()\n * @since 0.6.0\n * @deprecated in favor of {@link JwtParserBuilder#zip()}\n */\n@SuppressWarnings(\"DeprecatedIsStillUsed\")\n@Deprecated\npublic interface CompressionCodecResolver {\n\n    /**\n     * Looks for a JWT {@code zip} header, and if found, returns the corresponding {@link CompressionCodec} the parser\n     * can use to decompress the JWT body.\n     *\n     * @param header of the JWT\n     * @return CompressionCodec matching the {@code zip} header, or null if there is no {@code zip} header.\n     * @throws CompressionException if a {@code zip} header value is found and not supported.\n     */\n    CompressionCodec resolveCompressionCodec(Header header) throws CompressionException;\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/CompressionCodecs.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Provides default implementations of the {@link CompressionCodec} interface.\n *\n * @see Jwts.ZIP#DEF\n * @see Jwts.ZIP#GZIP\n * @since 0.7.0\n * @deprecated in favor of {@link Jwts.ZIP}.\n */\n@Deprecated //TODO: delete for 1.0\npublic final class CompressionCodecs {\n\n    private CompressionCodecs() {\n    } //prevent external instantiation\n\n    /**\n     * Codec implementing the <a href=\"https://tools.ietf.org/html/rfc7518\">JWA</a> standard\n     * <a href=\"https://en.wikipedia.org/wiki/DEFLATE\">deflate</a> compression algorithm\n     *\n     * @deprecated in favor of {@link Jwts.ZIP#DEF}.\n     */\n    @Deprecated\n    public static final CompressionCodec DEFLATE = (CompressionCodec) Jwts.ZIP.DEF;\n\n    /**\n     * Codec implementing the <a href=\"https://en.wikipedia.org/wiki/Gzip\">gzip</a> compression algorithm.\n     *\n     * <p><b>Compatibility Warning</b></p>\n     *\n     * <p><b>This is not a standard JWA compression algorithm</b>.  Be sure to use this only when you are confident\n     * that all parties accessing the token support the gzip algorithm.</p>\n     *\n     * <p>If you're concerned about compatibility, the {@link Jwts.ZIP#DEF DEF} code is JWA standards-compliant.</p>\n     *\n     * @deprecated in favor of {@link Jwts.ZIP#GZIP}\n     */\n    @Deprecated\n    public static final CompressionCodec GZIP = (CompressionCodec) Jwts.ZIP.GZIP;\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/CompressionException.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.io.IOException;\n\n/**\n * Exception indicating that either compressing or decompressing a JWT body failed.\n *\n * @since 0.6.0\n */\npublic class CompressionException extends IOException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public CompressionException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public CompressionException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n}"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/ExpiredJwtException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception indicating that a JWT was accepted after it expired and must be rejected.\n *\n * @since 0.3\n */\npublic class ExpiredJwtException extends ClaimJwtException {\n\n    /**\n     * Creates a new instance with the specified header, claims, and explanation message.\n     *\n     * @param header  jwt header\n     * @param claims  jwt claims (body)\n     * @param message the message explaining why the exception is thrown.\n     */\n    public ExpiredJwtException(Header header, Claims claims, String message) {\n        super(header, claims, message);\n    }\n\n    /**\n     * Creates a new instance with the specified header, claims, explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     * @param header  jwt header\n     * @param claims  jwt claims (body)\n     * @since 0.5\n     */\n    public ExpiredJwtException(Header header, Claims claims, String message, Throwable cause) {\n        super(header, claims, message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Header.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport java.util.Map;\n\n/**\n * A JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-5\">JOSE header</a>.\n *\n * <p>This is an immutable JSON map with convenient type-safe getters for JWT standard header parameter names.</p>\n *\n * <p>Because this interface extends <code>Map&lt;String, Object&gt;</code>, you can use standard {@code Map}\n * accessor/iterator methods as desired, for example:</p>\n *\n * <blockquote><pre>\n * header.get(\"someKey\");</pre></blockquote>\n *\n * <p>However, because {@code Header} instances are immutable, calling any of the map mutation methods\n * (such as {@code Map.}{@link Map#put(Object, Object) put}, etc) will result in a runtime exception.</p>\n *\n * <p><b>Security</b></p>\n *\n * <p>The {@code Header} interface itself makes no implications of integrity protection via either digital signatures or\n * encryption. Instead, {@link JwsHeader} and {@link JweHeader} represent this information for respective\n * {@link Jws} and {@link Jwe} instances.</p>\n *\n * @see ProtectedHeader\n * @see JwsHeader\n * @see JweHeader\n * @since 0.1\n */\npublic interface Header extends Map<String, Object> {\n\n    /**\n     * JWT {@code Type} (typ) value: <code>\"JWT\"</code>\n     *\n     * @deprecated since 0.12.0 - this constant is never used within the JJWT codebase.\n     */\n    @Deprecated\n    String JWT_TYPE = \"JWT\";\n\n    /**\n     * JWT {@code Type} header parameter name: <code>\"typ\"</code>\n     * @deprecated since 0.12.0 in favor of {@link #getType()}.\n     */\n    @Deprecated\n    String TYPE = \"typ\";\n\n    /**\n     * JWT {@code Content Type} header parameter name: <code>\"cty\"</code>\n     * @deprecated since 0.12.0 in favor of {@link #getContentType()}.\n     */\n    @Deprecated\n    String CONTENT_TYPE = \"cty\";\n\n    /**\n     * JWT {@code Algorithm} header parameter name: <code>\"alg\"</code>.\n     *\n     * @see <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\">JWS Algorithm Header</a>\n     * @see <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.1\">JWE Algorithm Header</a>\n     * @deprecated since 0.12.0 in favor of {@link #getAlgorithm()}.\n     */\n    @Deprecated\n    String ALGORITHM = \"alg\";\n\n    /**\n     * JWT {@code Compression Algorithm} header parameter name: <code>\"zip\"</code>\n     * @deprecated since 0.12.0 in favor of {@link #getCompressionAlgorithm()}\n     */\n    @Deprecated\n    String COMPRESSION_ALGORITHM = \"zip\";\n\n    /**\n     * JJWT legacy/deprecated compression algorithm header parameter name: <code>\"calg\"</code>\n     *\n     * @deprecated use {@link #COMPRESSION_ALGORITHM} instead.\n     */\n    @Deprecated\n    String DEPRECATED_COMPRESSION_ALGORITHM = \"calg\";\n\n    /**\n     * Returns the <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-5.1\">\n     * <code>typ</code> (Type)</a> header value or {@code null} if not present.\n     *\n     * @return the {@code typ} header value or {@code null} if not present.\n     */\n    String getType();\n\n    /**\n     * Returns the <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10\">\n     * <code>cty</code> (Content Type)</a> header value or {@code null} if not present.\n     *\n     * <p>The <code>cty</code> (Content Type) Header Parameter is used by applications to declare the\n     * <a href=\"https://www.iana.org/assignments/media-types/media-types.xhtml\">IANA MediaType</a> of the content\n     * (the payload).  This is intended for use by the application when more than\n     * one kind of object could be present in the Payload; the application can use this value to disambiguate among\n     * the different kinds of objects that might be present.  It will typically not be used by applications when\n     * the kind of object is already known.  This parameter is ignored by JWT implementations (like JJWT); any\n     * processing of this parameter is performed by the JWS application.  Use of this Header Parameter is OPTIONAL.</p>\n     *\n     * <p>To keep messages compact in common situations, it is RECOMMENDED that producers omit an\n     * <b><code>application/</code></b> prefix of a media type value in a {@code cty} Header Parameter when\n     * no other '<b>/</b>' appears in the media type value.  A recipient using the media type value <em>MUST</em>\n     * treat it as if <b><code>application/</code></b> were prepended to any {@code cty} value not containing a\n     * '<b>/</b>'. For instance, a {@code cty} value of <b><code>example</code></b> <em>SHOULD</em> be used to\n     * represent the <b><code>application/example</code></b> media type, whereas the media type\n     * <b><code>application/example;part=&quot;1/2&quot;</code></b> cannot be shortened to\n     * <b><code>example;part=&quot;1/2&quot;</code></b>.</p>\n     *\n     * @return the {@code typ} header parameter value or {@code null} if not present.\n     */\n    String getContentType();\n\n    /**\n     * Returns the JWT {@code alg} (Algorithm) header value or {@code null} if not present.\n     *\n     * <ul>\n     *     <li>If the JWT is a Signed JWT (a JWS), the <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\">\n     *      <code>alg</code></a> (Algorithm) header parameter identifies the cryptographic algorithm used to secure the\n     *      JWS.  Consider using {@link Jwts.SIG}.{@link io.jsonwebtoken.lang.Registry#get(Object) get(id)}\n     *      to convert this string value to a type-safe {@code SecureDigestAlgorithm} instance.</li>\n     *      <li>If the JWT is an Encrypted JWT (a JWE), the\n     * <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.1\"><code>alg</code></a> (Algorithm) header parameter\n     * identifies the cryptographic key management algorithm used to encrypt or determine the value of the Content\n     * Encryption Key (CEK).  The encrypted content is not usable if the <code>alg</code> value does not represent a\n     * supported algorithm, or if the recipient does not have a key that can be used with that algorithm.  Consider\n     * using {@link Jwts.KEY}.{@link io.jsonwebtoken.lang.Registry#get(Object) get(id)} to convert this string value\n     * to a type-safe {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm} instance.</li>\n     * </ul>\n     *\n     * @return the {@code alg} header value or {@code null} if not present.  This will always be\n     * {@code non-null} on validly constructed JWT instances, but could be {@code null} during construction.\n     * @since 0.12.0\n     */\n    String getAlgorithm();\n\n    /**\n     * Returns the JWT  <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.3\"><code>zip</code></a>\n     * (Compression Algorithm) header parameter value or {@code null} if not present.\n     *\n     * <p><b>Compatibility Note</b></p>\n     *\n     * <p>While the JWT family of specifications only defines the <code>zip</code> header in the JWE\n     * (JSON Web Encryption) specification, JJWT will also support compression for JWS as well if you choose to use it.\n     * However, be aware that <b>if you use compression when creating a JWS token, other libraries may not be able to\n     * parse the JWS</b>. However, compression when creating JWE tokens should be universally accepted for any library\n     * that supports JWE.</p>\n     *\n     * @return the {@code zip} header parameter value or {@code null} if not present.\n     * @since 0.6.0\n     */\n    String getCompressionAlgorithm();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/HeaderMutator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.lang.MapMutator;\n\n/**\n * Mutation (modifications) to a {@link Header Header} instance.\n *\n * @param <T> the mutator subtype, for method chaining\n * @since 0.12.0\n */\npublic interface HeaderMutator<T extends HeaderMutator<T>> extends MapMutator<String, Object, T> {\n\n    //IMPLEMENTOR NOTE: if this `algorithm` method ever needs to be exposed in the public API, it might be better to\n    //                  have it in the Jwts.HeaderBuilder interface and NOT this one: in the context of\n    //                  JwtBuilder.Header, there is never a reason for an application developer to call algorithm(id)\n    //                  directly because the KeyAlgorithm or SecureDigestAlgorithm instance must always be provided\n    //                  via the signWith or encryptWith methods.  The JwtBuilder will always set the algorithm\n    //                  header based on these two instances, so there is no need for an app dev to do so.\n    /*\n     * Sets the JWT {@code alg} (Algorithm) header value.  A {@code null} value will remove the property\n     * from the JSON map.\n     * <ul>\n     *     <li>If the JWT is a Signed JWT (a JWS), the\n     *     <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\">{@code alg}</a> (Algorithm) header\n     *     parameter identifies the cryptographic algorithm used to secure the JWS.</li>\n     *      <li>If the JWT is an Encrypted JWT (a JWE), the\n     * <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.1\"><code>alg</code></a> (Algorithm) header parameter\n     * identifies the cryptographic key management algorithm used to encrypt or determine the value of the Content\n     * Encryption Key (CEK).  The encrypted content is not usable if the <code>alg</code> value does not represent a\n     * supported algorithm, or if the recipient does not have a key that can be used with that algorithm.</li>\n     * </ul>\n     *\n     * @param alg the {@code alg} header value\n     * @return this header for method chaining\n     * @since 0.12.0\n     *\n    T algorithm(String alg);\n    */\n\n    /**\n     * Sets the JWT <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-5.1\">\n     * <code>typ</code> (Type)</a> header value.  A {@code null} value will remove the property from the JSON map.\n     *\n     * @param typ the JWT JOSE {@code typ} header value or {@code null} to remove the property from the JSON map.\n     * @return the instance for method chaining.\n     */\n    T type(String typ);\n\n    /**\n     * Sets the compact <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10\">\n     * <code>cty</code> (Content Type)</a> header parameter value, used by applications to declare the\n     * <a href=\"https://www.iana.org/assignments/media-types/media-types.xhtml\">IANA MediaType</a> of the JWT\n     * payload.  A {@code null} value will remove the property from the JSON map.\n     *\n     * <p><b>Compact Media Type Identifier</b></p>\n     *\n     * <p>This method will automatically remove any <code><b>application/</b></code> prefix from the\n     * {@code cty} string if possible according to the rules defined in the last paragraph of\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10\">RFC 7517, Section 4.1.10</a>:</p>\n     * <blockquote><pre>\n     *     To keep messages compact in common situations, it is RECOMMENDED that\n     *     producers omit an \"application/\" prefix of a media type value in a\n     *     \"cty\" Header Parameter when no other '/' appears in the media type\n     *     value.  A recipient using the media type value MUST treat it as if\n     *     \"application/\" were prepended to any \"cty\" value not containing a\n     *     '/'.  For instance, a \"cty\" value of \"example\" SHOULD be used to\n     *     represent the \"application/example\" media type, whereas the media\n     *     type \"application/example;part=\"1/2\"\" cannot be shortened to\n     *     \"example;part=\"1/2\"\".</pre></blockquote>\n     *\n     * <p>JJWT performs the reverse during JWT parsing: {@link Header#getContentType()} will automatically prepend the\n     * {@code application/} prefix if the parsed {@code cty} value does not contain a '<code>/</code>' character (as\n     * mandated by the RFC language above). This ensures application developers can use and read standard IANA Media\n     * Type identifiers without needing JWT-specific prefix conditional logic in application code.\n     * </p>\n     *\n     * @param cty the JWT {@code cty} header value or {@code null} to remove the property from the JSON map.\n     * @return the instance for method chaining.\n     */\n    T contentType(String cty);\n\n    /**\n     * Deprecated since of 0.12.0, delegates to {@link #type(String)}.\n     *\n     * @param typ the JWT JOSE {@code typ} header value or {@code null} to remove the property from the JSON map.\n     * @return the instance for method chaining.\n     * @see #type(String)\n     * @deprecated since 0.12.0 in favor of the more modern builder-style {@link #type(String)} method.\n     * This method will be removed before the 1.0 release.\n     */\n    @Deprecated\n    T setType(String typ);\n\n    /**\n     * Deprecated as of 0.12.0, delegates to {@link #contentType(String)}.\n     *\n     * @param cty the JWT JOSE {@code cty} header value or {@code null} to remove the property from the JSON map.\n     * @return the instance for method chaining.\n     * @see #contentType(String)\n     * @deprecated since 0.12.0 in favor of the more modern builder-style {@link #contentType(String)}.\n     */\n    @Deprecated\n    T setContentType(String cty);\n\n    /**\n     * Deprecated as of 0.12.0, there is no need to set this any longer as the {@code JwtBuilder} will\n     * always set the {@code zip} header as necessary.\n     *\n     * @param zip the JWT compression algorithm {@code zip} value or {@code null} to remove the property from the JSON map.\n     * @return the instance for method chaining.\n     * @since 0.6.0\n     * @deprecated since 0.12.0 and will be removed before the 1.0 release.\n     */\n    @Deprecated\n    T setCompressionAlgorithm(String zip);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Identifiable.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * An object that may be uniquely identified by an {@link #getId() id} relative to other instances of the same type.\n *\n * <p>The following table indicates how various JWT or JWK {@link #getId() getId()} values are used.</p>\n *\n * <table>\n * <caption>JWA Identifiable Concepts</caption>\n * <thead>\n * <tr>\n * <th>JJWT Type</th>\n * <th>How {@link #getId()} is Used</th>\n * </tr>\n * </thead>\n * <tbody>\n * <tr>\n * <td>{@link io.jsonwebtoken.Claims Claims}</td>\n * <td>JWT's <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7\">{@code jti} (JWT ID)</a>\n * claim.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.security.Jwk Jwk}</td>\n * <td>JWK's <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.5\">{@code kid} (Key ID)</a>\n * parameter value.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.security.Curve Curve}</td>\n * <td>JWK's <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.1.1\">{@code crv} (Curve)</a>\n * parameter value.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.io.CompressionAlgorithm CompressionAlgorithm}</td>\n * <td>JWE protected header's\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.3\">{@code zip} (Compression Algorithm)</a>\n * parameter value.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.security.HashAlgorithm HashAlgorithm}</td>\n * <td>Within a {@link io.jsonwebtoken.security.JwkThumbprint JwkThumbprint}'s URI value.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.security.MacAlgorithm MacAlgorithm}</td>\n * <td>JWS protected header's\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1\">{@code alg} (Algorithm)</a> parameter value.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.security.SignatureAlgorithm SignatureAlgorithm}</td>\n * <td>JWS protected header's\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.1\">{@code alg} (Algorithm)</a> parameter value.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm}</td>\n * <td>JWE protected header's\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1\">{@code alg} (Key Management Algorithm)</a>\n * parameter value.</td>\n * </tr>\n * <tr>\n * <td>{@link io.jsonwebtoken.security.AeadAlgorithm AeadAlgorithm}</td>\n * <td>JWE protected header's\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1\">{@code enc} (Encryption Algorithm)</a>\n * parameter value.</td>\n * </tr>\n * </tbody>\n * </table>\n *\n * @since 0.12.0\n */\npublic interface Identifiable {\n\n    /**\n     * Returns the unique string identifier of the associated object.\n     *\n     * @return the unique string identifier of the associated object.\n     */\n    String getId();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/IncorrectClaimException.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception thrown when discovering that a required claim does not equal the required value, indicating the JWT is\n * invalid and may not be used.\n *\n * @since 0.6\n */\npublic class IncorrectClaimException extends InvalidClaimException {\n\n    /**\n     * Creates a new instance with the specified header, claims and explanation message.\n     *\n     * @param header     the header inspected\n     * @param claims     the claims with the incorrect claim value\n     * @param claimName  the name of the claim that could not be validated\n     * @param claimValue the value of the claim that could not be validated\n     * @param message    the exception message\n     */\n    public IncorrectClaimException(Header header, Claims claims, String claimName, Object claimValue, String message) {\n        super(header, claims, claimName, claimValue, message);\n    }\n\n    /**\n     * Creates a new instance with the specified header, claims, explanation message and underlying cause.\n     *\n     * @param header     the header inspected\n     * @param claims     the claims with the incorrect claim value\n     * @param claimName  the name of the claim that could not be validated\n     * @param claimValue the value of the claim that could not be validated\n     * @param message    the exception message\n     * @param cause      the underlying cause that resulted in this exception being thrown\n     */\n    public IncorrectClaimException(Header header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {\n        super(header, claims, claimName, claimValue, message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/InvalidClaimException.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception indicating a parsed claim is invalid in some way.  Subclasses reflect the specific\n * reason the claim is invalid.\n *\n * @see IncorrectClaimException\n * @see MissingClaimException\n * @since 0.6\n */\npublic class InvalidClaimException extends ClaimJwtException {\n\n    /**\n     * The name of the invalid claim.\n     */\n    private final String claimName;\n\n    /**\n     * The claim value that could not be validated.\n     */\n    private final Object claimValue;\n\n    /**\n     * Creates a new instance with the specified header, claims and explanation message.\n     *\n     * @param header     the header inspected\n     * @param claims     the claims obtained\n     * @param claimName  the name of the claim that could not be validated\n     * @param claimValue the value of the claim that could not be validated\n     * @param message    the exception message\n     */\n    protected InvalidClaimException(Header header, Claims claims, String claimName, Object claimValue, String message) {\n        super(header, claims, message);\n        this.claimName = claimName;\n        this.claimValue = claimValue;\n    }\n\n    /**\n     * Creates a new instance with the specified header, claims, explanation message and underlying cause.\n     *\n     * @param header     the header inspected\n     * @param claims     the claims obtained\n     * @param claimName  the name of the claim that could not be validated\n     * @param claimValue the value of the claim that could not be validated\n     * @param message    the exception message\n     * @param cause      the underlying cause that resulted in this exception being thrown\n     */\n    protected InvalidClaimException(Header header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {\n        super(header, claims, message, cause);\n        this.claimName = claimName;\n        this.claimValue = claimValue;\n    }\n\n    /**\n     * Returns the name of the invalid claim.\n     *\n     * @return the name of the invalid claim.\n     */\n    public String getClaimName() {\n        return claimName;\n    }\n\n    /**\n     * Returns the claim value that could not be validated.\n     *\n     * @return the claim value that could not be validated.\n     */\n    public Object getClaimValue() {\n        return claimValue;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Jwe.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * An encrypted JWT, called a &quot;JWE&quot;, per the\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html\">JWE (RFC 7516) Specification</a>.\n *\n * @param <B> payload type, either {@link Claims} or {@code byte[]} content.\n * @since 0.12.0\n */\npublic interface Jwe<B> extends ProtectedJwt<JweHeader, B> {\n\n    /**\n     * Visitor implementation that ensures the visited JWT is a JSON Web Encryption ('JWE') message with an\n     * authenticated and decrypted {@code byte[]} array payload, and rejects all others with an\n     * {@link UnsupportedJwtException}.\n     *\n     * @see SupportedJwtVisitor#onDecryptedContent(Jwe)\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"UnnecessaryModifier\")\n    public static final JwtVisitor<Jwe<byte[]>> CONTENT = new SupportedJwtVisitor<Jwe<byte[]>>() {\n        @Override\n        public Jwe<byte[]> onDecryptedContent(Jwe<byte[]> jwe) {\n            return jwe;\n        }\n    };\n\n    /**\n     * Visitor implementation that ensures the visited JWT is a JSON Web Encryption ('JWE') message with an\n     * authenticated and decrypted {@link Claims} payload, and rejects all others with an\n     * {@link UnsupportedJwtException}.\n     *\n     * @see SupportedJwtVisitor#onDecryptedClaims(Jwe)\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"UnnecessaryModifier\")\n    public static final JwtVisitor<Jwe<Claims>> CLAIMS = new SupportedJwtVisitor<Jwe<Claims>>() {\n        @Override\n        public Jwe<Claims> onDecryptedClaims(Jwe<Claims> jwe) {\n            return jwe;\n        }\n    };\n\n    /**\n     * Returns the Initialization Vector used during JWE encryption and decryption.\n     *\n     * @return the Initialization Vector used during JWE encryption and decryption.\n     */\n    byte[] getInitializationVector();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JweHeader.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\n\n/**\n * A <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html\">JWE</a> header.\n *\n * @since 0.12.0\n */\npublic interface JweHeader extends ProtectedHeader {\n\n    /**\n     * Returns the JWE <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.2\">{@code enc} (Encryption\n     * Algorithm)</a> header value or {@code null} if not present.\n     *\n     * <p>The JWE {@code enc} (encryption algorithm) Header Parameter identifies the content encryption algorithm\n     * used to perform authenticated encryption on the plaintext to produce the ciphertext and the JWE\n     * {@code Authentication Tag}.</p>\n     *\n     * <p>Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by\n     * supplying an {@link AeadAlgorithm} to a {@link JwtBuilder} via one of its\n     * {@link JwtBuilder#encryptWith(SecretKey, AeadAlgorithm) encryptWith(SecretKey, AeadAlgorithm)} or\n     * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)}\n     * methods. JJWT will then set this {@code enc} header value automatically to the {@code AeadAlgorithm}'s\n     * {@link AeadAlgorithm#getId() getId()} value during encryption.</p>\n     *\n     * @return the JWE {@code enc} (Encryption Algorithm) header value or {@code null} if not present.  This will\n     * always be {@code non-null} on validly-constructed JWE instances, but could be {@code null} during construction.\n     * @see JwtBuilder#encryptWith(SecretKey, AeadAlgorithm)\n     * @see JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm)\n     */\n    String getEncryptionAlgorithm();\n\n    /**\n     * Returns the <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">{@code epk} (Ephemeral\n     * Public Key)</a> header value created by the JWE originator for use with key agreement algorithms, or\n     * {@code null} if not present.\n     *\n     * <p>Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by\n     * supplying an ECDH-ES {@link KeyAlgorithm} to a {@link JwtBuilder} via its\n     * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)}\n     * method. The ECDH-ES {@code KeyAlgorithm} implementation will then set this {@code epk} header value\n     * automatically when producing the encryption key.</p>\n     *\n     * @return the <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">{@code epk} (Ephemeral\n     * Public Key)</a> header value created by the JWE originator for use with key agreement algorithms, or\n     * {@code null} if not present.\n     * @see Jwts.KEY\n     * @see Jwts.KEY#ECDH_ES\n     * @see Jwts.KEY#ECDH_ES_A128KW\n     * @see Jwts.KEY#ECDH_ES_A192KW\n     * @see Jwts.KEY#ECDH_ES_A256KW\n     */\n    PublicJwk<?> getEphemeralPublicKey();\n\n    /**\n     * Returns any information about the JWE producer for use with key agreement algorithms, or {@code null} if not\n     * present.\n     *\n     * @return any information about the JWE producer for use with key agreement algorithms, or {@code null} if not\n     * present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2\">JWE <code>apu</code> (Agreement PartyUInfo) Header Parameter</a>\n     * @see Jwts.KEY#ECDH_ES\n     * @see Jwts.KEY#ECDH_ES_A128KW\n     * @see Jwts.KEY#ECDH_ES_A192KW\n     * @see Jwts.KEY#ECDH_ES_A256KW\n     */\n    byte[] getAgreementPartyUInfo();\n\n    /**\n     * Returns any information about the JWE recipient for use with key agreement algorithms, or {@code null} if not\n     * present.\n     *\n     * @return any information about the JWE recipient for use with key agreement algorithms, or {@code null} if not\n     * present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3\">JWE <code>apv</code> (Agreement PartyVInfo) Header Parameter</a>\n     * @see Jwts.KEY#ECDH_ES\n     * @see Jwts.KEY#ECDH_ES_A128KW\n     * @see Jwts.KEY#ECDH_ES_A192KW\n     * @see Jwts.KEY#ECDH_ES_A256KW\n     */\n    byte[] getAgreementPartyVInfo();\n\n    /**\n     * Returns the 96-bit <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1\">&quot;iv&quot;\n     * (Initialization Vector)</a> generated during key encryption, or {@code null} if not present.\n     * Set by AES GCM {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm} implementations.\n     *\n     * <p>Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by\n     * supplying an AES GCM Wrap {@link KeyAlgorithm} to a {@link JwtBuilder} via its\n     * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)}\n     * method. The AES GCM Wrap {@code KeyAlgorithm} implementation will then set this {@code iv} header value\n     * automatically when producing the encryption key.</p>\n     *\n     * @return the 96-bit initialization vector generated during key encryption, or {@code null} if not present.\n     * @see Jwts.KEY#A128GCMKW\n     * @see Jwts.KEY#A192GCMKW\n     * @see Jwts.KEY#A256GCMKW\n     */\n    byte[] getInitializationVector();\n\n    /**\n     * Returns the 128-bit <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2\">&quot;tag&quot;\n     * (Authentication Tag)</a> resulting from key encryption, or {@code null} if not present.\n     *\n     * <p>Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by\n     * supplying an AES GCM Wrap {@link KeyAlgorithm} to a {@link JwtBuilder} via its\n     * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)}\n     * method. The AES GCM Wrap {@code KeyAlgorithm} implementation will then set this {@code tag} header value\n     * automatically when producing the encryption key.</p>\n     *\n     * @return the 128-bit authentication tag resulting from key encryption, or {@code null} if not present.\n     * @see Jwts.KEY#A128GCMKW\n     * @see Jwts.KEY#A192GCMKW\n     * @see Jwts.KEY#A256GCMKW\n     */\n    byte[] getAuthenticationTag();\n\n    /**\n     * Returns the number of PBKDF2 iterations necessary to derive the key used during JWE encryption, or {@code null}\n     * if not present. Used with password-based {@link io.jsonwebtoken.security.KeyAlgorithm KeyAlgorithm}s.\n     *\n     * @return the number of PBKDF2 iterations necessary to derive the key used during JWE encryption, or {@code null}\n     * if not present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2\">JWE <code>p2c</code> (PBES2 Count) Header Parameter</a>\n     * @see Jwts.KEY#PBES2_HS256_A128KW\n     * @see Jwts.KEY#PBES2_HS384_A192KW\n     * @see Jwts.KEY#PBES2_HS512_A256KW\n     */\n    Integer getPbes2Count();\n\n    /**\n     * Returns the PBKDF2 {@code Salt Input} value necessary to derive the key used during JWE encryption, or\n     * {@code null} if not present.\n     *\n     * <p>Note that there is no corresponding 'setter' method for this 'getter' because JJWT users set this value by\n     * supplying a password-based {@link KeyAlgorithm} to a {@link JwtBuilder} via its\n     * {@link JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)}\n     * method. The password-based {@code KeyAlgorithm} implementation will then set this {@code p2s} header value\n     * automatically when producing the encryption key.</p>\n     *\n     * @return the PBKDF2 {@code Salt Input} value necessary to derive the key used during JWE encryption, or\n     * {@code null} if not present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1\">JWE <code>p2s</code> (PBES2 Salt Input) Header Parameter</a>\n     * @see Jwts.KEY#PBES2_HS256_A128KW\n     * @see Jwts.KEY#PBES2_HS384_A192KW\n     * @see Jwts.KEY#PBES2_HS512_A256KW\n     */\n    byte[] getPbes2Salt();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JweHeaderMutator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.security.KeyAlgorithm;\n\n/**\n * Mutation (modifications) to a {@link JweHeader} instance.\n *\n * @param <T> the mutator subtype, for method chaining\n * @since 0.12.0\n */\npublic interface JweHeaderMutator<T extends JweHeaderMutator<T>> extends ProtectedHeaderMutator<T> {\n\n    /**\n     * Sets any information about the JWE producer for use with key agreement algorithms. A {@code null} or empty value\n     * removes the property from the JSON map.\n     *\n     * @param info information about the JWE producer to use with key agreement algorithms.\n     * @return the header for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2\">JWE <code>apu</code> (Agreement PartyUInfo) Header Parameter</a>\n     * @see Jwts.KEY#ECDH_ES\n     * @see Jwts.KEY#ECDH_ES_A128KW\n     * @see Jwts.KEY#ECDH_ES_A192KW\n     * @see Jwts.KEY#ECDH_ES_A256KW\n     */\n    T agreementPartyUInfo(byte[] info);\n\n    /**\n     * Sets any information about the JWE producer for use with key agreement algorithms. A {@code null} value removes\n     * the property from the JSON map.\n     *\n     * <p>If not {@code null}, this is a convenience method that calls the equivalent of the following:</p>\n     * <blockquote><pre>\n     * {@link #agreementPartyUInfo(byte[]) agreementPartyUInfo}(info.getBytes(StandardCharsets.UTF_8))</pre></blockquote>\n     *\n     * @param info information about the JWE producer to use with key agreement algorithms.\n     * @return the header for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.2\">JWE <code>apu</code> (Agreement PartyUInfo) Header Parameter</a>\n     * @see Jwts.KEY#ECDH_ES\n     * @see Jwts.KEY#ECDH_ES_A128KW\n     * @see Jwts.KEY#ECDH_ES_A192KW\n     * @see Jwts.KEY#ECDH_ES_A256KW\n     */\n    T agreementPartyUInfo(String info);\n\n    /**\n     * Sets any information about the JWE recipient for use with key agreement algorithms. A {@code null} value removes\n     * the property from the JSON map.\n     *\n     * @param info information about the JWE recipient to use with key agreement algorithms.\n     * @return the header for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3\">JWE <code>apv</code> (Agreement PartyVInfo) Header Parameter</a>\n     * @see Jwts.KEY#ECDH_ES\n     * @see Jwts.KEY#ECDH_ES_A128KW\n     * @see Jwts.KEY#ECDH_ES_A192KW\n     * @see Jwts.KEY#ECDH_ES_A256KW\n     */\n    T agreementPartyVInfo(byte[] info);\n\n    /**\n     * Sets any information about the JWE recipient for use with key agreement algorithms. A {@code null} value removes\n     * the property from the JSON map.\n     *\n     * <p>If not {@code null}, this is a convenience method that calls the equivalent of the following:</p>\n     * <blockquote><pre>\n     * {@link #agreementPartyVInfo(byte[]) setAgreementPartVUInfo}(info.getBytes(StandardCharsets.UTF_8))</pre></blockquote>\n     *\n     * @param info information about the JWE recipient to use with key agreement algorithms.\n     * @return the header for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.3\">JWE <code>apv</code> (Agreement PartyVInfo) Header Parameter</a>\n     * @see Jwts.KEY#ECDH_ES\n     * @see Jwts.KEY#ECDH_ES_A128KW\n     * @see Jwts.KEY#ECDH_ES_A192KW\n     * @see Jwts.KEY#ECDH_ES_A256KW\n     */\n    T agreementPartyVInfo(String info);\n\n    /**\n     * Sets the number of PBKDF2 iterations necessary to derive the key used during JWE encryption. If this value\n     * is not set when a password-based {@link KeyAlgorithm} is used, JJWT will automatically choose a suitable\n     * number of iterations based on\n     * <a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\">OWASP PBKDF2 Iteration Recommendations</a>.\n     *\n     * <p><b>Minimum Count</b></p>\n     *\n     * <p>{@code IllegalArgumentException} will be thrown during encryption if a specified {@code count} is\n     * less than 1000 (one thousand), which is the\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2\">minimum number recommended</a> by the\n     * JWA specification. Anything less is susceptible to security attacks so the default PBKDF2\n     * {@code KeyAlgorithm} implementations reject such values.</p>\n     *\n     * @param count the number of PBKDF2 iterations necessary to derive the key used during JWE encryption, must be\n     *              greater than or equal to 1000 (one thousand).\n     * @return the header for method chaining\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2\">JWE <code>p2c</code> (PBES2 Count) Header Parameter</a>\n     * @see Jwts.KEY#PBES2_HS256_A128KW\n     * @see Jwts.KEY#PBES2_HS384_A192KW\n     * @see Jwts.KEY#PBES2_HS512_A256KW\n     * @see <a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\">OWASP PBKDF2 Iteration Recommendations</a>\n     */\n    T pbes2Count(int count);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Jws.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * An expanded (not compact/serialized) Signed JSON Web Token.\n *\n * @param <P> the type of the JWS payload, either a byte[] or a {@link Claims} instance.\n * @since 0.1\n */\npublic interface Jws<P> extends ProtectedJwt<JwsHeader, P> {\n\n    /**\n     * Visitor implementation that ensures the visited JWT is a JSON Web Signature ('JWS') message with a\n     * cryptographically authenticated/verified {@code byte[]} array payload, and rejects all others with an\n     * {@link UnsupportedJwtException}.\n     *\n     * @see SupportedJwtVisitor#onVerifiedContent(Jws)\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"UnnecessaryModifier\")\n    public static final JwtVisitor<Jws<byte[]>> CONTENT = new SupportedJwtVisitor<Jws<byte[]>>() {\n        @Override\n        public Jws<byte[]> onVerifiedContent(Jws<byte[]> jws) {\n            return jws;\n        }\n    };\n\n    /**\n     * Visitor implementation that ensures the visited JWT is a JSON Web Signature ('JWS') message with a\n     * cryptographically authenticated/verified {@link Claims} payload, and rejects all others with an\n     * {@link UnsupportedJwtException}.\n     *\n     * @see SupportedJwtVisitor#onVerifiedClaims(Jws)\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"UnnecessaryModifier\")\n    public static final JwtVisitor<Jws<Claims>> CLAIMS = new SupportedJwtVisitor<Jws<Claims>>() {\n        @Override\n        public Jws<Claims> onVerifiedClaims(Jws<Claims> jws) {\n            return jws;\n        }\n    };\n\n    /**\n     * Returns the verified JWS signature as a Base64Url string.\n     *\n     * @return the verified JWS signature as a Base64Url string.\n     * @deprecated since 0.12.0 in favor of {@link #getDigest() getDigest()}.\n     */\n    @Deprecated\n    String getSignature(); //TODO for 1.0: return a byte[]\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwsHeader.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * A <a href=\"https://tools.ietf.org/html/rfc7515\">JWS</a> header.\n *\n * @since 0.1\n */\npublic interface JwsHeader extends ProtectedHeader {\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.1\">Algorithm Header</a> name: the string literal <b><code>alg</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getAlgorithm()}\n     */\n    @Deprecated\n    String ALGORITHM = \"alg\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.2\">JWK Set URL Header</a> name: the string literal <b><code>jku</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getJwkSetUrl()}\n     */\n    @Deprecated\n    String JWK_SET_URL = \"jku\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7515#section-4.1.3\">JSON Web Key Header</a> name: the string literal <b><code>jwk</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getJwk()}\n     */\n    @Deprecated\n    String JSON_WEB_KEY = \"jwk\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.4\">Key ID Header</a> name: the string literal <b><code>kid</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getKeyId()}\n     */\n    @Deprecated\n    String KEY_ID = \"kid\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.5\">X.509 URL Header</a> name: the string literal <b><code>x5u</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getX509Url()}\n     */\n    @Deprecated\n    String X509_URL = \"x5u\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.6\">X.509 Certificate Chain Header</a> name: the string literal <b><code>x5c</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getX509Chain()}\n     */\n    @Deprecated\n    String X509_CERT_CHAIN = \"x5c\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.7\">X.509 Certificate SHA-1 Thumbprint Header</a> name: the string literal <b><code>x5t</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getX509Sha1Thumbprint()}\n     */\n    @Deprecated\n    String X509_CERT_SHA1_THUMBPRINT = \"x5t\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.8\">X.509 Certificate SHA-256 Thumbprint Header</a> name: the string literal <b><code>x5t#S256</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getX509Sha256Thumbprint()}\n     */\n    @Deprecated\n    String X509_CERT_SHA256_THUMBPRINT = \"x5t#S256\";\n\n    /**\n     * JWS <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.11\">Critical Header</a> name: the string literal <b><code>crit</code></b>\n     *\n     * @deprecated since 0.12.0 in favor of {@link #getCritical()}\n     */\n    @Deprecated\n    String CRITICAL = \"crit\";\n\n    /**\n     * Returns {@code true} if the payload is Base64Url-encoded per standard JWS rules, or {@code false} if the\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc7797\">RFC 7797: JSON Web Signature (JWS) Unencoded Payload\n     * Option</a> has been specified.\n     *\n     * @return {@code true} if the payload is Base64Url-encoded per standard JWS rules, or {@code false} if the\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc7797\">RFC 7797: JSON Web Signature (JWS) Unencoded Payload\n     * Option</a> has been specified.\n     */\n    boolean isPayloadEncoded();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Jwt.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * An expanded (not compact/serialized) JSON Web Token.\n *\n * @param <H> the type of the JWT header\n * @param <P> the type of the JWT payload, either a content byte array or a {@link Claims} instance.\n * @since 0.1\n */\npublic interface Jwt<H extends Header, P> {\n\n    /**\n     * Visitor implementation that ensures the visited JWT is an unsecured content JWT (one not cryptographically\n     * signed or encrypted) and rejects all others with an {@link UnsupportedJwtException}.\n     *\n     * @see SupportedJwtVisitor#onUnsecuredContent(Jwt)\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"UnnecessaryModifier\")\n    public static final JwtVisitor<Jwt<Header, byte[]>> UNSECURED_CONTENT = new SupportedJwtVisitor<Jwt<Header, byte[]>>() {\n        @Override\n        public Jwt<Header, byte[]> onUnsecuredContent(Jwt<Header, byte[]> jwt) {\n            return jwt;\n        }\n    };\n\n    /**\n     * Visitor implementation that ensures the visited JWT is an unsecured {@link Claims} JWT (one not\n     * cryptographically signed or encrypted) and rejects all others with an {@link UnsupportedJwtException}.\n     *\n     * @see SupportedJwtVisitor#onUnsecuredClaims(Jwt)\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"UnnecessaryModifier\")\n    public static final JwtVisitor<Jwt<Header, Claims>> UNSECURED_CLAIMS = new SupportedJwtVisitor<Jwt<Header, Claims>>() {\n        @Override\n        public Jwt<Header, Claims> onUnsecuredClaims(Jwt<Header, Claims> jwt) {\n            return jwt;\n        }\n    };\n\n    /**\n     * Returns the JWT {@link Header} or {@code null} if not present.\n     *\n     * @return the JWT {@link Header} or {@code null} if not present.\n     */\n    H getHeader();\n\n    /**\n     * Returns the JWT payload, either a {@code byte[]} or a {@code Claims} instance.  Use\n     * {@link #getPayload()} instead, as this method will be removed prior to the 1.0 release.\n     *\n     * @return the JWT payload, either a {@code byte[]} or a {@code Claims} instance.\n     * @deprecated since 0.12.0 because it has been renamed to {@link #getPayload()}.  'Payload' (not\n     * body) is what the JWT specifications call this property, so it has been renamed to reflect the correct JWT\n     * nomenclature/taxonomy.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    P getBody(); // TODO: remove for 1.0\n\n    /**\n     * Returns the JWT payload, either a {@code byte[]} or a {@code Claims} instance.  If the payload is a byte\n     * array, and <em>if</em> the JWT creator set the (optional) {@link Header#getContentType() contentType} header\n     * value, the application may inspect the {@code contentType} value to determine how to convert the byte array to\n     * the final content type as desired.\n     *\n     * @return the JWT payload, either a {@code byte[]} or a {@code Claims} instance.\n     * @since 0.12.0\n     */\n    P getPayload();\n\n    /**\n     * Invokes the specified {@code visitor}'s appropriate type-specific {@code visit} method based on this JWT's type.\n     *\n     * @param visitor the visitor to invoke.\n     * @param <T>     the value type returned from the {@code visit} method.\n     * @return the value returned from visitor's {@code visit} method implementation.\n     */\n    <T> T accept(JwtVisitor<T> visitor);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwtBuilder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.Encoder;\nimport io.jsonwebtoken.io.Serializer;\nimport io.jsonwebtoken.lang.Conjunctor;\nimport io.jsonwebtoken.lang.MapMutator;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.Keys;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\nimport io.jsonwebtoken.security.WeakKeyException;\nimport io.jsonwebtoken.security.X509Builder;\n\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.SecureRandom;\nimport java.security.interfaces.ECKey;\nimport java.security.interfaces.RSAKey;\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * A builder for constructing Unprotected JWTs, Signed JWTs (aka 'JWS's) and Encrypted JWTs (aka 'JWE's).\n *\n * @since 0.1\n */\npublic interface JwtBuilder extends ClaimsMutator<JwtBuilder> {\n\n    /**\n     * Sets the JCA Provider to use during cryptographic signing or encryption operations, or {@code null} if the\n     * JCA subsystem preferred provider should be used.\n     *\n     * @param provider the JCA Provider to use during cryptographic signing or encryption operations, or {@code null} if the\n     *                 JCA subsystem preferred provider should be used.\n     * @return the builder for method chaining.\n     * @since 0.12.0\n     */\n    JwtBuilder provider(Provider provider);\n\n    /**\n     * Sets the {@link SecureRandom} to use during cryptographic signing or encryption operations, or {@code null} if\n     * a default {@link SecureRandom} should be used.\n     *\n     * @param secureRandom the {@link SecureRandom} to use during cryptographic signing or encryption operations, or\n     *                     {@code null} if a default {@link SecureRandom} should be used.\n     * @return the builder for method chaining.\n     * @since 0.12.0\n     */\n    JwtBuilder random(SecureRandom secureRandom);\n\n    /**\n     * Returns the {@code Header} to use to modify the constructed JWT's header name/value pairs as desired.\n     * When finished, callers may return to JWT construction via the {@link BuilderHeader#and() and()} method.\n     * For example:\n     *\n     * <blockquote><pre>\n     * String jwt = Jwts.builder()\n     *\n     *     <b>.header()\n     *         .keyId(\"keyId\")\n     *         .add(\"aName\", aValue)\n     *         .add(myHeaderMap)\n     *         // ... etc ...\n     *         .{@link BuilderHeader#and() and()}</b> //return back to the JwtBuilder\n     *\n     *     .subject(\"Joe\") // resume JwtBuilder calls\n     *     // ... etc ...\n     *     .compact();</pre></blockquote>\n     *\n     * @return the {@link BuilderHeader} to use for header construction.\n     * @since 0.12.0\n     */\n    BuilderHeader header();\n\n    /**\n     * Per standard Java idiom 'setter' conventions, this method sets (and fully replaces) any existing header with the\n     * specified name/value pairs.  This is a wrapper method for:\n     *\n     * <blockquote><pre>\n     * {@link #header()}.{@link MapMutator#empty() empty()}.{@link MapMutator#add(Map) add(map)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * <p>If you do not want to replace the existing header and only want to append to it,\n     * call <code>{@link #header()}.{@link io.jsonwebtoken.lang.MapMutator#add(Map) add(map)}.{@link BuilderHeader#and() and()}</code> instead.</p>\n     *\n     * @param map the name/value pairs to set as (and potentially replace) the constructed JWT header.\n     * @return the builder for method chaining.\n     * @deprecated since 0.12.0 in favor of\n     * <code>{@link #header()}.{@link MapMutator#empty() empty()}.{@link MapMutator#add(Map) add(map)}.{@link BuilderHeader#and() and()}</code>\n     * (to replace all header parameters) or\n     * <code>{@link #header()}.{@link MapMutator#add(Map) add(map)}.{@link BuilderHeader#and() and()}</code>\n     * to only append the {@code map} entries.  This method will be removed before the 1.0 release.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder setHeader(Map<String, ?> map);\n\n    /**\n     * Adds the specified name/value pairs to the header.  Any parameter with an empty or null value will remove the\n     * entry from the header. This is a wrapper method for:\n     * <blockquote><pre>\n     * {@link #header()}.{@link MapMutator#add(Map) add(map)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * @param params the header name/value pairs to append to the header.\n     * @return the builder for method chaining.\n     * @deprecated since 0.12.0 in favor of\n     * <code>{@link #header()}.{@link MapMutator#add(Map) add(map)}.{@link BuilderHeader#and() and()}</code>.\n     * This method will be removed before the 1.0 release.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder setHeaderParams(Map<String, ?> params);\n\n    /**\n     * Adds the specified name/value pair to the header. If the value is {@code null} or empty, the parameter will\n     * be removed from the header entirely. This is a wrapper method for:\n     * <blockquote><pre>\n     * {@link #header()}.{@link MapMutator#add(Object, Object) add(name, value)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * @param name  the header parameter name\n     * @param value the header parameter value\n     * @return the builder for method chaining.\n     * @deprecated since 0.12.0 in favor of <code>\n     * {@link #header()}.{@link MapMutator#add(Object, Object) add(name, value)}.{@link BuilderHeader#and() and()}</code>.\n     * This method will be removed before the 1.0 release.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder setHeaderParam(String name, Object value);\n\n    /**\n     * Since JJWT 0.12.0, this is an alias for {@link #content(String)}. This method will be removed\n     * before the 1.0 release.\n     *\n     * @param payload the string used to set UTF-8-encoded bytes as the JWT payload.\n     * @return the builder for method chaining.\n     * @see #content(String)\n     * @deprecated since 0.12.0 in favor of {@link #content(String)}\n     * because both Claims and Content are technically 'payloads', so this method name is misleading.  This method will\n     * be removed before the 1.0 release.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder setPayload(String payload);\n\n    /**\n     * Sets the JWT payload to be the specified string's UTF-8 bytes. This is a convenience method semantically\n     * equivalent to calling:\n     *\n     * <blockquote><pre>\n     * {@link #content(byte[]) content}(payload.getBytes(StandardCharsets.UTF_8))</pre></blockquote>\n     *\n     * <p><b>Content Type Recommendation</b></p>\n     *\n     * <p>Unless you are confident that the JWT recipient will <em>always</em> know to convert the payload bytes\n     * to a UTF-8 string without additional metadata, it is strongly recommended to use the\n     * {@link #content(String, String)} method instead of this one.  That method ensures that a JWT recipient can\n     * inspect the {@code cty} header to know how to handle the payload bytes without ambiguity.</p>\n     *\n     * <p><b>Mutually Exclusive Claims and Content</b></p>\n     *\n     * <p>This method is mutually exclusive of the {@link #claim(String, Object)} and {@link #claims()}\n     * methods. Either {@code claims} or {@code content} method variants may be used, but not both. If you want the\n     * JWT payload to be JSON claims, use the {@link #claim(String, Object)} or {@link #claims()} methods instead.</p>\n     *\n     * @param content the content string to use for the JWT payload\n     * @return the builder for method chaining.\n     * @see #content(String, String)\n     * @see #content(byte[], String)\n     * @see #content(InputStream, String)\n     * @since 0.12.0\n     */\n    JwtBuilder content(String content);\n\n    /**\n     * Sets the JWT payload to be the specified content byte array. This is a convenience method semantically\n     * equivalent to calling:\n     * <blockquote><pre>\n     * {@link #content(InputStream) content}(new ByteArrayInputStream(content))</pre></blockquote>\n     *\n     * <p><b>Content Type Recommendation</b></p>\n     *\n     * <p>Unless you are confident that the JWT recipient will <em>always</em> know how to use the payload bytes\n     * without additional metadata, it is strongly recommended to also set the\n     * {@link Header#getContentType() contentType} header. For example:</p>\n     *\n     * <blockquote><pre>\n     * content(bytes).{@link #header() header()}.{@link HeaderMutator#contentType(String) contentType(cty)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * <p>This ensures a JWT recipient can inspect the {@code cty} header to know how to handle the payload bytes\n     * without ambiguity.</p>\n     *\n     * <p><b>Mutually Exclusive Claims and Content</b></p>\n     *\n     * <p>This method is mutually exclusive of the {@link #claim(String, Object)} and {@link #claims()}\n     * methods. Either {@code claims} or {@code content} method variants may be used, but not both. If you want the\n     * JWT payload to be JSON claims, use the {@link #claim(String, Object)} or {@link #claims()} methods instead.</p>\n     *\n     * @param content the content byte array to use as the JWT payload\n     * @return the builder for method chaining.\n     * @see #content(byte[], String)\n     * @since 0.12.0\n     */\n    JwtBuilder content(byte[] content);\n\n    /**\n     * Sets the JWT payload to be the bytes in the specified content stream.\n     *\n     * <p><b>Content Type Recommendation</b></p>\n     *\n     * <p>Unless you are confident that the JWT recipient will <em>always</em> know how to use the payload bytes\n     * without additional metadata, it is strongly recommended to also set the\n     * {@link HeaderMutator#contentType(String) contentType} header. For example:</p>\n     *\n     * <blockquote><pre>\n     * content(in).{@link #header() header()}.{@link HeaderMutator#contentType(String) contentType(cty)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * <p>This ensures a JWT recipient can inspect the {@code cty} header to know how to handle the payload bytes\n     * without ambiguity.</p>\n     *\n     * <p><b>Mutually Exclusive Claims and Content</b></p>\n     *\n     * <p>This method is mutually exclusive of the {@link #claim(String, Object)} and {@link #claims()}\n     * methods. Either {@code claims} or {@code content} method variants may be used, but not both. If you want the\n     * JWT payload to be JSON claims, use the {@link #claim(String, Object)} or {@link #claims()} methods instead.</p>\n     *\n     * @param in the input stream containing the bytes to use as the JWT payload\n     * @return the builder for method chaining.\n     * @see #content(byte[], String)\n     * @since 0.12.0\n     */\n    JwtBuilder content(InputStream in);\n\n    /**\n     * Sets the JWT payload to be the specified String's UTF-8 bytes, and also sets the\n     * {@link HeaderMutator#contentType(String) contentType} header value to a compact {@code cty} IANA Media Type\n     * identifier to indicate the data format of the resulting byte array. The JWT recipient can inspect the\n     * {@code cty} value to determine how to convert the byte array to the final content type as desired.  This is a\n     * convenience method semantically equivalent to:\n     *\n     * <blockquote><pre>\n     * {@link #content(String) content(content)}.{@link #header()}.{@link HeaderMutator#contentType(String) contentType(cty)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * <p><b>Compact Media Type Identifier</b></p>\n     *\n     * <p>This method will automatically remove any <code><b>application/</b></code> prefix from the\n     * {@code cty} string if possible according to the rules defined in the last paragraph of\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10\">RFC 7517, Section 4.1.10</a>:</p>\n     *\n     * <blockquote><pre>\n     *     To keep messages compact in common situations, it is RECOMMENDED that\n     *     producers omit an \"application/\" prefix of a media type value in a\n     *     \"cty\" Header Parameter when no other '/' appears in the media type\n     *     value.  A recipient using the media type value MUST treat it as if\n     *     \"application/\" were prepended to any \"cty\" value not containing a\n     *     '/'.  For instance, a \"cty\" value of \"example\" SHOULD be used to\n     *     represent the \"application/example\" media type, whereas the media\n     *     type \"application/example;part=\"1/2\"\" cannot be shortened to\n     *     \"example;part=\"1/2\"\".</pre></blockquote>\n     *\n     * <p>JJWT performs the reverse during JWT parsing: {@link Header#getContentType()} will automatically prepend the\n     * {@code application/} prefix if the parsed {@code cty} value does not contain a '<code>/</code>' character (as\n     * mandated by the RFC language above). This ensures application developers can use and read standard IANA Media\n     * Type identifiers without needing JWT-specific prefix conditional logic in application code.\n     * </p>\n     *\n     * <p><b>Mutually Exclusive Claims and Content</b></p>\n     *\n     * <p>This method is mutually exclusive of the {@link #claim(String, Object)} and {@link #claims()}\n     * methods. Either {@code claims} or {@code content} method variants may be used, but not both. If you want the\n     * JWT payload to be JSON claims, use the {@link #claim(String, Object)} or {@link #claims()} methods instead.</p>\n     *\n     * @param content the content byte array that will be the JWT payload.  Cannot be null or empty.\n     * @param cty     the content type (media type) identifier attributed to the byte array. Cannot be null or empty.\n     * @return the builder for method chaining.\n     * @throws IllegalArgumentException if either {@code content} or {@code cty} are null or empty.\n     * @since 0.12.0\n     */\n    JwtBuilder content(String content, String cty) throws IllegalArgumentException;\n\n    /**\n     * Sets the JWT payload to be the specified byte array, and also sets the\n     * {@link HeaderMutator#contentType(String) contentType} header value to a compact {@code cty} IANA Media Type\n     * identifier to indicate the data format of the byte array. The JWT recipient can inspect the\n     * {@code cty} value to determine how to convert the byte array to the final content type as desired.  This is a\n     * convenience method semantically equivalent to:\n     *\n     * <blockquote><pre>\n     * {@link #content(byte[]) content(content)}.{@link #header()}.{@link HeaderMutator#contentType(String) contentType(cty)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * <p><b>Compact Media Type Identifier</b></p>\n     *\n     * <p>This method will automatically remove any <code><b>application/</b></code> prefix from the\n     * {@code cty} string if possible according to the rules defined in the last paragraph of\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10\">RFC 7517, Section 4.1.10</a>:</p>\n     * <blockquote><pre>\n     *     To keep messages compact in common situations, it is RECOMMENDED that\n     *     producers omit an \"application/\" prefix of a media type value in a\n     *     \"cty\" Header Parameter when no other '/' appears in the media type\n     *     value.  A recipient using the media type value MUST treat it as if\n     *     \"application/\" were prepended to any \"cty\" value not containing a\n     *     '/'.  For instance, a \"cty\" value of \"example\" SHOULD be used to\n     *     represent the \"application/example\" media type, whereas the media\n     *     type \"application/example;part=\"1/2\"\" cannot be shortened to\n     *     \"example;part=\"1/2\"\".</pre></blockquote>\n     *\n     * <p>JJWT performs the reverse during JWT parsing: {@link Header#getContentType()} will automatically prepend the\n     * {@code application/} prefix if the parsed {@code cty} value does not contain a '<code>/</code>' character (as\n     * mandated by the RFC language above). This ensures application developers can use and read standard IANA Media\n     * Type identifiers without needing JWT-specific prefix conditional logic in application code.\n     * </p>\n     *\n     * <p><b>Mutually Exclusive Claims and Content</b></p>\n     *\n     * <p>This method is mutually exclusive of the {@link #claim(String, Object)} and {@link #claims()}\n     * methods. Either {@code claims} or {@code content} method variants may be used, but not both. If you want the\n     * JWT payload to be JSON claims, use the {@link #claim(String, Object)} or {@link #claims()} methods instead.</p>\n     *\n     * @param content the content byte array that will be the JWT payload.  Cannot be null or empty.\n     * @param cty     the content type (media type) identifier attributed to the byte array. Cannot be null or empty.\n     * @return the builder for method chaining.\n     * @throws IllegalArgumentException if either {@code content} or {@code cty} are null or empty.\n     * @since 0.12.0\n     */\n    JwtBuilder content(byte[] content, String cty) throws IllegalArgumentException;\n\n    /**\n     * Sets the JWT payload to be the specified content byte stream and also sets the\n     * {@link BuilderHeader#contentType(String) contentType} header value to a compact {@code cty} IANA Media Type\n     * identifier to indicate the data format of the byte array. The JWT recipient can inspect the\n     * {@code cty} value to determine how to convert the byte array to the final content type as desired.  This is a\n     * convenience method semantically equivalent to:\n     *\n     * <blockquote><pre>\n     * {@link #content(InputStream) content(content)}.{@link #header()}.{@link HeaderMutator#contentType(String) contentType(cty)}.{@link BuilderHeader#and() and()}</pre></blockquote>\n     *\n     * <p><b>Compact Media Type Identifier</b></p>\n     *\n     * <p>This method will automatically remove any <code><b>application/</b></code> prefix from the\n     * {@code cty} string if possible according to the rules defined in the last paragraph of\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10\">RFC 7517, Section 4.1.10</a>:</p>\n     *\n     * <blockquote><pre>\n     *     To keep messages compact in common situations, it is RECOMMENDED that\n     *     producers omit an \"application/\" prefix of a media type value in a\n     *     \"cty\" Header Parameter when no other '/' appears in the media type\n     *     value.  A recipient using the media type value MUST treat it as if\n     *     \"application/\" were prepended to any \"cty\" value not containing a\n     *     '/'.  For instance, a \"cty\" value of \"example\" SHOULD be used to\n     *     represent the \"application/example\" media type, whereas the media\n     *     type \"application/example;part=\"1/2\"\" cannot be shortened to\n     *     \"example;part=\"1/2\"\".</pre></blockquote>\n     *\n     * <p>JJWT performs the reverse during JWT parsing: {@link Header#getContentType()} will automatically prepend the\n     * {@code application/} prefix if the parsed {@code cty} value does not contain a '<code>/</code>' character (as\n     * mandated by the RFC language above). This ensures application developers can use and read standard IANA Media\n     * Type identifiers without needing JWT-specific prefix conditional logic in application code.\n     * </p>\n     *\n     * <p><b>Mutually Exclusive Claims and Content</b></p>\n     *\n     * <p>This method is mutually exclusive of the {@link #claim(String, Object)} and {@link #claims()}\n     * methods. Either {@code claims} or {@code content} method variants may be used, but not both. If you want the\n     * JWT payload to be JSON claims, use the {@link #claim(String, Object)} or {@link #claims()} methods instead.</p>\n     *\n     * @param content the content byte array that will be the JWT payload.  Cannot be null.\n     * @param cty     the content type (media type) identifier attributed to the byte array. Cannot be null or empty.\n     * @return the builder for method chaining.\n     * @throws IllegalArgumentException if either {@code content} or {@code cty} are null or empty.\n     * @since 0.12.0\n     */\n    JwtBuilder content(InputStream content, String cty) throws IllegalArgumentException;\n\n    /**\n     * Returns the JWT {@code Claims} payload to modify as desired. When finished, callers may\n     * return to {@code JwtBuilder} configuration via the {@link BuilderClaims#and() and()} method.\n     * For example:\n     *\n     * <blockquote><pre>\n     * String jwt = Jwts.builder()\n     *\n     *     <b>.claims()\n     *         .issuer(\"me\")\n     *         .subject(\"Joe\")\n     *         .audience().add(\"you\").and()\n     *         .add(\"customClaim\", customValue)\n     *         .add(myClaimsMap)\n     *         // ... etc ...\n     *         .{@link BuilderClaims#and() and()}</b> //return back to the JwtBuilder\n     *\n     *     .signWith(key) // resume JwtBuilder calls\n     *     // ... etc ...\n     *     .compact();</pre></blockquote>\n     *\n     * @return the {@link BuilderClaims} to use for Claims construction.\n     * @since 0.12.0\n     */\n    BuilderClaims claims();\n\n    /**\n     * Replaces the JWT Claims payload with the specified name/value pairs. This is an alias for:\n     * <blockquote><pre>\n     * {@link #claims()}.{@link MapMutator#empty() empty()}.{@link MapMutator#add(Map) add(claims)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * <p>The {@code content} and {@code claims} properties are mutually exclusive - only one of the two variants\n     * may be used.</p>\n     *\n     * @param claims the JWT Claims to be set as the JWT payload.\n     * @return the builder for method chaining.\n     * @see #claims()\n     * @see #content(String)\n     * @see #content(byte[])\n     * @see #content(InputStream)\n     * @deprecated since 0.12.0 in favor of using the {@link #claims()} builder.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder setClaims(Map<String, ?> claims);\n\n    /**\n     * Adds/appends all given name/value pairs to the JSON Claims in the payload. This is an alias for:\n     *\n     * <blockquote><pre>\n     * {@link #claims()}.{@link MapMutator#add(Map) add(claims)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * <p>The content and claims properties are mutually exclusive - only one of the two may be used.</p>\n     *\n     * @param claims the JWT Claims to be added to the JWT payload.\n     * @return the builder for method chaining.\n     * @since 0.8\n     * @deprecated since 0.12.0 in favor of\n     * <code>{@link #claims()}.{@link BuilderClaims#add(Map) add(Map)}.{@link BuilderClaims#and() and()}</code>.\n     * This method will be removed before the 1.0 release.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder addClaims(Map<String, ?> claims);\n\n    /**\n     * Sets a JWT claim, overwriting any existing claim with the same name. A {@code null} or empty\n     * value will remove the claim entirely. This is a convenience alias for:\n     * <blockquote><pre>\n     * {@link #claims()}.{@link MapMutator#add(Object, Object) add(name, value)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * @param name  the JWT Claims property name\n     * @param value the value to set for the specified Claims property name\n     * @return the builder instance for method chaining.\n     * @since 0.2\n     */\n    JwtBuilder claim(String name, Object value);\n\n    /**\n     * Adds all given name/value pairs to the JSON Claims in the payload, overwriting any existing claims\n     * with the same names.  If any name has a {@code null} or empty value, that claim will be removed from the\n     * Claims.  This is a convenience alias for:\n     * <blockquote><pre>\n     * {@link #claims()}.{@link MapMutator#add(Map) add(claims)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * <p>The content and claims properties are mutually exclusive - only one of the two may be used.</p>\n     *\n     * @param claims the JWT Claims to be added to the JWT payload.\n     * @return the builder instance for method chaining\n     * @since 0.12.0\n     */\n    JwtBuilder claims(Map<String, ?> claims);\n\n    /**\n     * Sets the JWT Claims <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.1\">\n     * <code>iss</code></a> (issuer) claim.  A {@code null} value will remove the property from the Claims.\n     * This is a convenience wrapper for:\n     * <blockquote><pre>\n     * {@link #claims()}.{@link ClaimsMutator#issuer(String) issuer(iss)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * @param iss the JWT {@code iss} value or {@code null} to remove the property from the Claims map.\n     * @return the builder instance for method chaining.\n     */\n    @Override\n    // for better/targeted JavaDoc\n    JwtBuilder issuer(String iss);\n\n    /**\n     * Sets the JWT Claims <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.2\">\n     * <code>sub</code></a> (subject) claim.  A {@code null} value will remove the property from the Claims.\n     * This is a convenience wrapper for:\n     * <blockquote><pre>\n     * {@link #claims()}.{@link ClaimsMutator#subject(String) subject(sub)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * @param sub the JWT {@code sub} value or {@code null} to remove the property from the Claims map.\n     * @return the builder instance for method chaining.\n     */\n    @Override\n    // for better/targeted JavaDoc\n    JwtBuilder subject(String sub);\n\n    /**\n     * Sets the JWT Claims <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4\">\n     * <code>exp</code></a> (expiration) claim. A {@code null} value will remove the property from the Claims.\n     *\n     * <p>A JWT obtained after this timestamp should not be used.</p>\n     *\n     * <p>This is a convenience wrapper for:</p>\n     * <blockquote><pre>\n     * {@link #claims()}.{@link ClaimsMutator#expiration(Date) expiration(exp)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * @param exp the JWT {@code exp} value or {@code null} to remove the property from the Claims map.\n     * @return the builder instance for method chaining.\n     */\n    @Override\n    // for better/targeted JavaDoc\n    JwtBuilder expiration(Date exp);\n\n    /**\n     * Sets the JWT Claims <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5\">\n     * <code>nbf</code></a> (not before) claim. A {@code null} value will remove the property from the Claims.\n     *\n     * <p>A JWT obtained before this timestamp should not be used.</p>\n     *\n     * <p>This is a convenience wrapper for:</p>\n     * <blockquote><pre>\n     * {@link #claims()}.{@link ClaimsMutator#notBefore(Date) notBefore(nbf)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * @param nbf the JWT {@code nbf} value or {@code null} to remove the property from the Claims map.\n     * @return the builder instance for method chaining.\n     */\n    @Override\n    // for better/targeted JavaDoc\n    JwtBuilder notBefore(Date nbf);\n\n    /**\n     * Sets the JWT Claims <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.6\">\n     * <code>iat</code></a> (issued at) claim. A {@code null} value will remove the property from the Claims.\n     *\n     * <p>The value is the timestamp when the JWT was created.</p>\n     *\n     * <p>This is a convenience wrapper for:</p>\n     * <blockquote><pre>\n     * {@link #claims()}.{@link ClaimsMutator#issuedAt(Date) issuedAt(iat)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * @param iat the JWT {@code iat} value or {@code null} to remove the property from the Claims map.\n     * @return the builder instance for method chaining.\n     */\n    @Override\n    // for better/targeted JavaDoc\n    JwtBuilder issuedAt(Date iat);\n\n    /**\n     * Sets the JWT Claims <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.7\">\n     * <code>jti</code></a> (JWT ID) claim. A {@code null} value will remove the property from the Claims.\n     *\n     * <p>The value is a CaSe-SenSiTiVe unique identifier for the JWT. If specified, this value MUST be assigned in a\n     * manner that ensures that there is a negligible probability that the same value will be accidentally\n     * assigned to a different data object.  The ID can be used to prevent the JWT from being replayed.</p>\n     *\n     * <p>This is a convenience wrapper for:</p>\n     * <blockquote><pre>\n     * {@link #claims()}.{@link ClaimsMutator#id(String) id(jti)}.{@link BuilderClaims#and() and()}</pre></blockquote>\n     *\n     * @param jti the JWT {@code jti} (id) value or {@code null} to remove the property from the Claims map.\n     * @return the builder instance for method chaining.\n     */\n    @Override\n    // for better/targeted JavaDoc\n    JwtBuilder id(String jti);\n\n    /**\n     * Signs the constructed JWT with the specified key using the key's <em>recommended signature algorithm</em>\n     * as defined below, producing a JWS.  If the recommended signature algorithm isn't sufficient for your needs,\n     * consider using {@link #signWith(Key, SecureDigestAlgorithm)} instead.\n     *\n     * <p>If you are looking to invoke this method with a byte array that you are confident may be used for HMAC-SHA\n     * algorithms, consider using {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to\n     * convert the byte array into a valid {@code Key}.</p>\n     *\n     * <p><b><a id=\"recsigalg\">Recommended Signature Algorithm</a></b></p>\n     *\n     * <p>The recommended signature algorithm used with a given key is chosen based on the following:</p>\n     * <table>\n     * <caption>Key Recommended Signature Algorithm</caption>\n     * <thead>\n     * <tr>\n     * <th>If the Key is a:</th>\n     * <th>And:</th>\n     * <th>With a key size of:</th>\n     * <th>The SignatureAlgorithm used will be:</th>\n     * </tr>\n     * </thead>\n     * <tbody>\n     * <tr>\n     * <td>{@link SecretKey}</td>\n     * <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals(\"HmacSHA256\")</code><sup>1</sup></td>\n     * <td>256 &lt;= size &lt;= 383 <sup>2</sup></td>\n     * <td>{@link Jwts.SIG#HS256 HS256}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link SecretKey}</td>\n     * <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals(\"HmacSHA384\")</code><sup>1</sup></td>\n     * <td>384 &lt;= size &lt;= 511</td>\n     * <td>{@link Jwts.SIG#HS384 HS384}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link SecretKey}</td>\n     * <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals(\"HmacSHA512\")</code><sup>1</sup></td>\n     * <td>512 &lt;= size</td>\n     * <td>{@link Jwts.SIG#HS512 HS512}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link ECKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>256 &lt;= size &lt;= 383 <sup>3</sup></td>\n     * <td>{@link Jwts.SIG#ES256 ES256}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link ECKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>384 &lt;= size &lt;= 520 <sup>4</sup></td>\n     * <td>{@link Jwts.SIG#ES384 ES384}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link ECKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td><b>521</b> &lt;= size <sup>4</sup></td>\n     * <td>{@link Jwts.SIG#ES512 ES512}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link RSAKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>2048 &lt;= size &lt;= 3071 <sup>5,6</sup></td>\n     * <td>{@link Jwts.SIG#RS256 RS256}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link RSAKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>3072 &lt;= size &lt;= 4095 <sup>6</sup></td>\n     * <td>{@link Jwts.SIG#RS384 RS384}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link RSAKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>4096 &lt;= size <sup>5</sup></td>\n     * <td>{@link Jwts.SIG#RS512 RS512}</td>\n     * </tr>\n     * <tr>\n     *     <td><a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECKey.html\">EdECKey</a><sup>7</sup></td>\n     *     <td><code>instanceof {@link PrivateKey}</code></td>\n     *     <td>256 || 456</td>\n     *     <td>{@link Jwts.SIG#EdDSA EdDSA}</td>\n     * </tr>\n     * </tbody>\n     * </table>\n     * <p>Notes:</p>\n     * <ol>\n     * <li>{@code SecretKey} instances must have an {@link Key#getAlgorithm() algorithm} name equal\n     * to {@code HmacSHA256}, {@code HmacSHA384} or {@code HmacSHA512}.  If not, the key bytes might not be\n     * suitable for HMAC signatures will be rejected with a {@link InvalidKeyException}. </li>\n     * <li>The JWT <a href=\"https://tools.ietf.org/html/rfc7518#section-3.2\">JWA Specification (RFC 7518,\n     * Section 3.2)</a> mandates that HMAC-SHA-* signing keys <em>MUST</em> be 256 bits or greater.\n     * {@code SecretKey}s with key lengths less than 256 bits will be rejected with an\n     * {@link WeakKeyException}.</li>\n     * <li>The JWT <a href=\"https://tools.ietf.org/html/rfc7518#section-3.4\">JWA Specification (RFC 7518,\n     * Section 3.4)</a> mandates that ECDSA signing key lengths <em>MUST</em> be 256 bits or greater.\n     * {@code ECKey}s with key lengths less than 256 bits will be rejected with a\n     * {@link WeakKeyException}.</li>\n     * <li>The ECDSA {@code P-521} curve does indeed use keys of <b>521</b> bits, not 512 as might be expected.  ECDSA\n     * keys of 384 &lt; size &lt;= 520 are suitable for ES384, while ES512 requires keys &gt;= 521 bits.  The '512' part of the\n     * ES512 name reflects the usage of the SHA-512 algorithm, not the ECDSA key length.  ES512 with ECDSA keys less\n     * than 521 bits will be rejected with a {@link WeakKeyException}.</li>\n     * <li>The JWT <a href=\"https://tools.ietf.org/html/rfc7518#section-3.3\">JWA Specification (RFC 7518,\n     * Section 3.3)</a> mandates that RSA signing key lengths <em>MUST</em> be 2048 bits or greater.\n     * {@code RSAKey}s with key lengths less than 2048 bits will be rejected with a\n     * {@link WeakKeyException}.</li>\n     * <li>Technically any RSA key of length &gt;= 2048 bits may be used with the\n     * {@link Jwts.SIG#RS256 RS256}, {@link Jwts.SIG#RS384 RS384}, and\n     * {@link Jwts.SIG#RS512 RS512} algorithms, so we assume an RSA signature algorithm based on the key\n     * length to parallel similar decisions in the JWT specification for HMAC and ECDSA signature algorithms.\n     * This is not required - just a convenience.</li>\n     * <li><a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECKey.html\">EdECKey</a>s\n     * require JDK &gt;= 15 or BouncyCastle in the runtime classpath.</li>\n     * </ol>\n     *\n     * <p>This implementation does not use the {@link Jwts.SIG#PS256 PS256},\n     * {@link Jwts.SIG#PS384 PS384}, or {@link Jwts.SIG#PS512 PS512} RSA variants for any\n     * specified {@link RSAKey} because the the {@link Jwts.SIG#RS256 RS256},\n     * {@link Jwts.SIG#RS384 RS384}, and {@link Jwts.SIG#RS512 RS512} algorithms are\n     * available in the JDK by default while the {@code PS}* variants require either JDK 11 or an additional JCA\n     * Provider (like BouncyCastle).  If you wish to use a {@code PS}* variant with your key, use the\n     * {@link #signWith(Key, SecureDigestAlgorithm)} method instead.</p>\n     *\n     * <p>Finally, this method will throw an {@link InvalidKeyException} for any key that does not match the\n     * heuristics and requirements documented above, since that inevitably means the Key is either insufficient,\n     * unsupported, or explicitly disallowed by the JWT specification.</p>\n     *\n     * @param key the key to use for signing\n     * @return the builder instance for method chaining.\n     * @throws InvalidKeyException if the Key is insufficient, unsupported, or explicitly disallowed by the JWT\n     *                             specification as described above in <em>recommended signature algorithms</em>.\n     * @see Jwts.SIG\n     * @see #signWith(Key, SecureDigestAlgorithm)\n     * @since 0.10.0\n     */\n    JwtBuilder signWith(Key key) throws InvalidKeyException;\n\n    /**\n     * Signs the constructed JWT using the specified algorithm with the specified key, producing a JWS.\n     *\n     * <p><b>Deprecation Notice: Deprecated as of 0.10.0</b></p>\n     *\n     * <p>Use {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to\n     * obtain the {@code Key} and then invoke {@link #signWith(Key)} or\n     * {@link #signWith(Key, SecureDigestAlgorithm)}.</p>\n     *\n     * <p>This method will be removed in the 1.0 release.</p>\n     *\n     * @param alg       the JWS algorithm to use to digitally sign the JWT, thereby producing a JWS.\n     * @param secretKey the algorithm-specific signing key to use to digitally sign the JWT.\n     * @return the builder for method chaining.\n     * @throws InvalidKeyException if the Key is insufficient for the specified algorithm or explicitly disallowed by\n     *                             the JWT specification.\n     * @deprecated as of 0.10.0: use {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(bytes)} to\n     * obtain the {@code Key} and then invoke {@link #signWith(Key)} or\n     * {@link #signWith(Key, SecureDigestAlgorithm)}.\n     * This method will be removed in the 1.0 release.\n     */\n    @Deprecated\n    JwtBuilder signWith(SignatureAlgorithm alg, byte[] secretKey) throws InvalidKeyException;\n\n    /**\n     * Signs the constructed JWT using the specified algorithm with the specified key, producing a JWS.\n     *\n     * <p>This is a convenience method: the string argument is first BASE64-decoded to a byte array and this resulting\n     * byte array is used to invoke {@link #signWith(SignatureAlgorithm, byte[])}.</p>\n     *\n     * <p><b>Deprecation Notice: Deprecated as of 0.10.0, will be removed in the 1.0 release.</b></p>\n     *\n     * <p>This method has been deprecated because the {@code key} argument for this method can be confusing: keys for\n     * cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were\n     * obtained from the String argument.</p>\n     *\n     * <p>This method always expected a String argument that was effectively the same as the result of the following\n     * (pseudocode):</p>\n     *\n     * <p>{@code String base64EncodedSecretKey = base64Encode(secretKeyBytes);}</p>\n     *\n     * <p>However, a non-trivial number of JJWT users were confused by the method signature and attempted to\n     * use raw password strings as the key argument - for example {@code with(HS256, myPassword)} - which is\n     * almost always incorrect for cryptographic hashes and can produce erroneous or insecure results.</p>\n     *\n     * <p>See this\n     * <a href=\"https://stackoverflow.com/questions/40252903/static-secret-as-byte-key-or-string/40274325#40274325\">\n     * StackOverflow answer</a> explaining why raw (non-base64-encoded) strings are almost always incorrect for\n     * signature operations.</p>\n     *\n     * <p>To perform the correct logic with base64EncodedSecretKey strings with JJWT &gt;= 0.10.0, you may do this:</p>\n     * <pre><code>\n     * byte[] keyBytes = {@link Decoders Decoders}.{@link Decoders#BASE64 BASE64}.{@link Decoder#decode(Object) decode(base64EncodedSecretKey)};\n     * Key key = {@link Keys Keys}.{@link Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor(keyBytes)};\n     * jwtBuilder.with(key); //or {@link #signWith(Key, SignatureAlgorithm)}\n     * </code></pre>\n     *\n     * <p>This method will be removed in the 1.0 release.</p>\n     *\n     * @param alg                    the JWS algorithm to use to digitally sign the JWT, thereby producing a JWS.\n     * @param base64EncodedSecretKey the BASE64-encoded algorithm-specific signing key to use to digitally sign the\n     *                               JWT.\n     * @return the builder for method chaining.\n     * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification as\n     *                             described by {@link SignatureAlgorithm#forSigningKey(Key)}.\n     * @deprecated as of 0.10.0: use {@link #signWith(Key)} or {@link #signWith(Key, SignatureAlgorithm)} instead.  This\n     * method will be removed in the 1.0 release.\n     */\n    @Deprecated\n    JwtBuilder signWith(SignatureAlgorithm alg, String base64EncodedSecretKey) throws InvalidKeyException;\n\n    /**\n     * Signs the constructed JWT using the specified algorithm with the specified key, producing a JWS.\n     *\n     * <p>It is typically recommended to call the {@link #signWith(Key)} instead for simplicity.\n     * However, this method can be useful if the recommended algorithm heuristics do not meet your needs or if\n     * you want explicit control over the signature algorithm used with the specified key.</p>\n     *\n     * @param alg the JWS algorithm to use to digitally sign the JWT, thereby producing a JWS.\n     * @param key the algorithm-specific signing key to use to digitally sign the JWT.\n     * @return the builder for method chaining.\n     * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification for\n     *                             the specified algorithm.\n     * @see #signWith(Key)\n     * @deprecated since 0.10.0. Use {@link #signWith(Key, SecureDigestAlgorithm)} instead.\n     * This method will be removed before the 1.0 release.\n     */\n    @Deprecated\n    JwtBuilder signWith(SignatureAlgorithm alg, Key key) throws InvalidKeyException;\n\n    /**\n     * <p><b>Deprecation Notice</b></p>\n     *\n     * <p><b>This has been deprecated since 0.12.0.  Use\n     * {@link #signWith(Key, SecureDigestAlgorithm)} instead</b>.  Standard JWA algorithms\n     * are represented as instances of this new interface in the {@link Jwts.SIG}\n     * algorithm registry.</p>\n     *\n     * <p>Signs the constructed JWT with the specified key using the specified algorithm, producing a JWS.</p>\n     *\n     * <p>It is typically recommended to call the {@link #signWith(Key)} instead for simplicity.\n     * However, this method can be useful if the recommended algorithm heuristics do not meet your needs or if\n     * you want explicit control over the signature algorithm used with the specified key.</p>\n     *\n     * @param key the signing key to use to digitally sign the JWT.\n     * @param alg the JWS algorithm to use with the key to digitally sign the JWT, thereby producing a JWS.\n     * @return the builder for method chaining.\n     * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification for\n     *                             the specified algorithm.\n     * @see #signWith(Key)\n     * @since 0.10.0\n     * @deprecated since 0.12.0 to use the more flexible {@link #signWith(Key, SecureDigestAlgorithm)}.\n     */\n    @Deprecated\n    JwtBuilder signWith(Key key, SignatureAlgorithm alg) throws InvalidKeyException;\n\n    /**\n     * Signs the constructed JWT with the specified key using the specified algorithm, producing a JWS.\n     *\n     * <p>The {@link Jwts.SIG} registry makes available all standard signature\n     * algorithms defined in the JWA specification.</p>\n     *\n     * <p>It is typically recommended to call the {@link #signWith(Key)} instead for simplicity.\n     * However, this method can be useful if the recommended algorithm heuristics do not meet your needs or if\n     * you want explicit control over the signature algorithm used with the specified key.</p>\n     *\n     * @param key the signing key to use to digitally sign the JWT.\n     * @param <K> The type of key accepted by the {@code SignatureAlgorithm}.\n     * @param alg the JWS algorithm to use with the key to digitally sign the JWT, thereby producing a JWS.\n     * @return the builder for method chaining.\n     * @throws InvalidKeyException if the Key is insufficient or explicitly disallowed by the JWT specification for\n     *                             the specified algorithm.\n     * @see #signWith(Key)\n     * @see Jwts.SIG\n     * @since 0.12.0\n     */\n    <K extends Key> JwtBuilder signWith(K key, SecureDigestAlgorithm<? super K, ?> alg) throws InvalidKeyException;\n\n    /**\n     * Encrypts the constructed JWT with the specified symmetric {@code key} using the provided {@code enc}ryption\n     * algorithm, producing a JWE.  Because it is a symmetric key, the JWE recipient\n     * must also have access to the same key to decrypt.\n     *\n     * <p>This method is a convenience method that delegates to\n     * {@link #encryptWith(Key, KeyAlgorithm, AeadAlgorithm) encryptWith(Key, KeyAlgorithm, AeadAlgorithm)}\n     * based on the {@code key} argument:</p>\n     * <ul>\n     *     <li>If the provided {@code key} is a {@link Password Password} instance,\n     *     the {@code KeyAlgorithm} used will be one of the three JWA-standard password-based key algorithms\n     *      ({@link Jwts.KEY#PBES2_HS256_A128KW PBES2_HS256_A128KW},\n     *      {@link Jwts.KEY#PBES2_HS384_A192KW PBES2_HS384_A192KW}, or\n     *      {@link Jwts.KEY#PBES2_HS512_A256KW PBES2_HS512_A256KW}) as determined by the {@code enc} algorithm's\n     *      {@link AeadAlgorithm#getKeyBitLength() key length} requirement.</li>\n     *     <li>If the {@code key} is otherwise a standard {@code SecretKey}, the {@code KeyAlgorithm} will be\n     *     {@link Jwts.KEY#DIRECT DIRECT}, indicating that {@code key} should be used directly with the\n     *     {@code enc} algorithm.  In this case, the {@code key} argument <em>MUST</em> be of sufficient strength to\n     *     use with the specified {@code enc} algorithm, otherwise an exception will be thrown during encryption. If\n     *     desired, secure-random keys suitable for an {@link AeadAlgorithm} may be generated using the algorithm's\n     *     {@link AeadAlgorithm#key() key()} builder.</li>\n     * </ul>\n     *\n     * @param key the symmetric encryption key to use with the {@code enc} algorithm.\n     * @param enc the {@link AeadAlgorithm} algorithm used to encrypt the JWE, usually one of the JWA-standard\n     *            algorithms accessible via {@link Jwts.ENC}.\n     * @return the JWE builder for method chaining.\n     * @see Jwts.ENC\n     */\n    JwtBuilder encryptWith(SecretKey key, AeadAlgorithm enc);\n\n    /**\n     * Encrypts the constructed JWT using the specified {@code enc} algorithm with the symmetric key produced by the\n     * {@code keyAlg} when invoked with the given {@code key}, producing a JWE.\n     *\n     * <p>This behavior can be illustrated by the following pseudocode, a rough example of what happens during\n     * {@link #compact() compact}ion:</p>\n     * <blockquote><pre>\n     *     SecretKey encryptionKey = keyAlg.getEncryptionKey(key);           // (1)\n     *     byte[] jweCiphertext = enc.encrypt(payloadBytes, encryptionKey);  // (2)</pre></blockquote>\n     * <ol>\n     *     <li>The {@code keyAlg} argument is first invoked with the provided {@code key} argument, resulting in a\n     *         {@link SecretKey}.</li>\n     *     <li>This {@code SecretKey} result is used to call the provided {@code enc} encryption algorithm argument,\n     *         resulting in the final JWE ciphertext.</li>\n     * </ol>\n     *\n     * <p>Most application developers will reference one of the JWA\n     * {@link Jwts.KEY standard key algorithms} and {@link Jwts.ENC standard encryption algorithms}\n     * when invoking this method, but custom implementations are also supported.</p>\n     *\n     * @param <K>    the type of key that must be used with the specified {@code keyAlg} instance.\n     * @param key    the key used to invoke the provided {@code keyAlg} instance.\n     * @param keyAlg the key management algorithm that will produce the symmetric {@code SecretKey} to use with the\n     *               {@code enc} algorithm\n     * @param enc    the {@link AeadAlgorithm} algorithm used to encrypt the JWE\n     * @return the JWE builder for method chaining.\n     * @see Jwts.ENC\n     * @see Jwts.KEY\n     */\n    <K extends Key> JwtBuilder encryptWith(K key, KeyAlgorithm<? super K, ?> keyAlg, AeadAlgorithm enc);\n\n    /**\n     * Compresses the JWT payload using the specified {@link CompressionAlgorithm}.\n     *\n     * <p>If your compact JWTs are large, and you want to reduce their total size during network transmission, this\n     * can be useful.  For example, when embedding JWTs  in URLs, some browsers may not support URLs longer than a\n     * certain length.  Using compression can help ensure the compact JWT fits within that length.  However, NOTE:</p>\n     *\n     * <p><b>Compatibility Warning</b></p>\n     *\n     * <p>The JWT family of specifications defines compression only for JWE (JSON Web Encryption)\n     * tokens.  Even so, JJWT will also support compression for JWS tokens as well if you choose to use it.\n     * However, be aware that <b>if you use compression when creating a JWS token, other libraries may not be able to\n     * parse that JWS token</b>.  When using compression for JWS tokens, be sure that all parties accessing the\n     * JWS token support compression for JWS.</p>\n     *\n     * <p>Compression when creating JWE tokens however should be universally accepted for any\n     * library that supports JWE.</p>\n     *\n     * @param alg implementation of the {@link CompressionAlgorithm} to be used.\n     * @return the builder for method chaining.\n     * @see Jwts.ZIP\n     * @since 0.12.0\n     */\n    JwtBuilder compressWith(CompressionAlgorithm alg);\n\n    /**\n     * Perform Base64Url encoding during {@link #compact() compaction} with the specified Encoder.\n     *\n     * <p>JJWT uses a spec-compliant encoder that works on all supported JDK versions, but you may call this method\n     * to specify a different encoder if you desire.</p>\n     *\n     * @param base64UrlEncoder the encoder to use when Base64Url-encoding\n     * @return the builder for method chaining.\n     * @see #b64Url(Encoder)\n     * @since 0.10.0\n     * @deprecated since 0.12.0 in favor of {@link #b64Url(Encoder)}.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder base64UrlEncodeWith(Encoder<byte[], String> base64UrlEncoder);\n\n    /**\n     * Perform Base64Url encoding during {@link #compact() compaction} with the specified {@code OutputStream} Encoder.\n     * The Encoder's {@link Encoder#encode(Object) encode} method will be given a target {@code OutputStream} to\n     * wrap, and the resulting (wrapping) {@code OutputStream} will be used for writing, ensuring automatic\n     * Base64URL-encoding during write operations.\n     *\n     * <p>JJWT uses a spec-compliant encoder that works on all supported JDK versions, but you may call this method\n     * to specify a different stream encoder if desired.</p>\n     *\n     * @param encoder the encoder to use when Base64Url-encoding\n     * @return the builder for method chaining.\n     * @since 0.12.0\n     */\n    JwtBuilder b64Url(Encoder<OutputStream, OutputStream> encoder);\n\n    /**\n     * Enables <a href=\"https://datatracker.ietf.org/doc/html/rfc7797\">RFC 7797: JSON Web Signature (JWS)\n     * Unencoded Payload Option</a> if {@code false}, or standard JWT/JWS/JWE payload encoding otherwise.  The default\n     * value is {@code true} per standard RFC behavior rules.\n     *\n     * <p>This value may only be {@code false} for JWSs (signed JWTs).  It may not be used for standard\n     * (unprotected) JWTs or encrypted JWTs (JWEs).  The builder will throw an exception during {@link #compact()} if\n     * {@code false} and a JWS is not being created.</p>\n     *\n     * @param b64 whether to Base64URL-encode the JWS payload\n     * @return the builder for method chaining.\n     */\n    JwtBuilder encodePayload(boolean b64);\n\n    /**\n     * Performs Map-to-JSON serialization with the specified Serializer.  This is used by the builder to convert\n     * JWT/JWS/JWE headers and claims Maps to JSON strings as required by the JWT specification.\n     *\n     * <p>If this method is not called, JJWT will use whatever serializer it can find at runtime, checking for the\n     * presence of well-known implementations such Jackson, Gson, and org.json.  If one of these is not found\n     * in the runtime classpath, an exception will be thrown when the {@link #compact()} method is invoked.</p>\n     *\n     * @param serializer the serializer to use when converting Map objects to JSON strings.\n     * @return the builder for method chaining.\n     * @since 0.10.0\n     * @deprecated since 0.12.0 in favor of {@link #json(Serializer)}\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtBuilder serializeToJsonWith(Serializer<Map<String, ?>> serializer);\n\n    /**\n     * Perform Map-to-JSON serialization with the specified Serializer.  This is used by the builder to convert\n     * JWT/JWS/JWE headers and Claims Maps to JSON strings as required by the JWT specification.\n     *\n     * <p>If this method is not called, JJWT will use whatever Serializer it can find at runtime, checking for the\n     * presence of well-known implementations such Jackson, Gson, and org.json.  If one of these is not found\n     * in the runtime classpath, an exception will be thrown when the {@link #compact()} method is invoked.</p>\n     *\n     * @param serializer the Serializer to use when converting Map objects to JSON strings.\n     * @return the builder for method chaining.\n     * @since 0.12.0\n     */\n    JwtBuilder json(Serializer<Map<String, ?>> serializer);\n\n    /**\n     * Actually builds the JWT and serializes it to a compact, URL-safe string according to the\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7519.html#section-7.1\">JWT Compact Serialization</a>\n     * rules.\n     *\n     * @return A compact URL-safe JWT string.\n     */\n    String compact();\n\n    /**\n     * Claims for use with a {@link JwtBuilder} that supports method chaining for standard JWT Claims parameters.\n     * Once claims are configured, the associated {@link JwtBuilder} may be obtained with the {@link #and() and()}\n     * method for continued configuration.\n     *\n     * @since 0.12.0\n     */\n    interface BuilderClaims extends MapMutator<String, Object, BuilderClaims>, ClaimsMutator<BuilderClaims>,\n            Conjunctor<JwtBuilder> {\n    }\n\n    /**\n     * Header for use with a {@link JwtBuilder} that supports method chaining for\n     * standard JWT, JWS and JWE header parameters.  Once header parameters are configured, the associated\n     * {@link JwtBuilder} may be obtained with the {@link #and() and()} method for continued configuration.\n     *\n     * @since 0.12.0\n     */\n    interface BuilderHeader extends JweHeaderMutator<BuilderHeader>, X509Builder<BuilderHeader>,\n            Conjunctor<JwtBuilder> {\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwtException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Base class for JWT-related runtime exceptions.\n *\n * @since 0.1\n */\npublic class JwtException extends RuntimeException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public JwtException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public JwtException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwtHandler.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * A JwtHandler is invoked by a {@link io.jsonwebtoken.JwtParser JwtParser} after parsing a JWT to indicate the exact\n * type of JWT, JWS or JWE parsed.\n *\n * @param <T> the type of object to return to the parser caller after handling the parsed JWT.\n * @since 0.2\n * @deprecated since 0.12.0 in favor of calling {@link Jwt#accept(JwtVisitor)}.\n */\n@SuppressWarnings(\"DeprecatedIsStillUsed\")\n@Deprecated\npublic interface JwtHandler<T> extends JwtVisitor<T> {\n\n    /**\n     * This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is\n     * an unsecured content JWT.  An unsecured content JWT has a byte array payload that is not\n     * cryptographically signed or encrypted.  If the JWT creator set the (optional)\n     * {@link Header#getContentType() contentType} header value, the application may inspect that value to determine\n     * how to convert the byte array to the final content type as desired.\n     *\n     * @param jwt the parsed unsecured content JWT\n     * @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.\n     */\n    T onContentJwt(Jwt<Header, byte[]> jwt);\n\n    /**\n     * This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is\n     * a Claims JWT.  A Claims JWT has a {@link Claims} payload that is not cryptographically signed or encrypted.\n     *\n     * @param jwt the parsed claims JWT\n     * @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.\n     */\n    T onClaimsJwt(Jwt<Header, Claims> jwt);\n\n    /**\n     * This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is\n     * a content JWS.  A content JWS is a JWT with a byte array payload that has been cryptographically signed.\n     * If the JWT creator set the (optional) {@link Header#getContentType() contentType} header value, the\n     * application may inspect that value to determine how to convert the byte array to the final content type\n     * as desired.\n     *\n     * <p>This method will only be invoked if the cryptographic signature can be successfully verified.</p>\n     *\n     * @param jws the parsed content JWS\n     * @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.\n     */\n    T onContentJws(Jws<byte[]> jws);\n\n    /**\n     * This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is\n     * a valid Claims JWS.  A Claims JWS is a JWT with a {@link Claims} payload that has been cryptographically signed.\n     *\n     * <p>This method will only be invoked if the cryptographic signature can be successfully verified.</p>\n     *\n     * @param jws the parsed claims JWS\n     * @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.\n     */\n    T onClaimsJws(Jws<Claims> jws);\n\n    /**\n     * This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is\n     * a content JWE.  A content JWE is a JWE with a byte array payload that has been encrypted. If the JWT creator set\n     * the (optional) {@link Header#getContentType() contentType} header value, the application may inspect that\n     * value to determine how to convert the byte array to the final content type as desired.\n     *\n     * <p>This method will only be invoked if the content JWE can be successfully decrypted.</p>\n     *\n     * @param jwe the parsed content jwe\n     * @return any object to be used after inspecting the JWE, or {@code null} if no return value is necessary.\n     * @since 0.12.0\n     */\n    T onContentJwe(Jwe<byte[]> jwe);\n\n    /**\n     * This method is invoked when a {@link io.jsonwebtoken.JwtParser JwtParser} determines that the parsed JWT is\n     * a valid Claims JWE.  A Claims JWE is a JWT with a {@link Claims} payload that has been encrypted.\n     *\n     * <p>This method will only be invoked if the Claims JWE can be successfully decrypted.</p>\n     *\n     * @param jwe the parsed claims jwe\n     * @return any object to be used after inspecting the JWE, or {@code null} if no return value is necessary.\n     * @since 0.12.0\n     */\n    T onClaimsJwe(Jwe<Claims> jwe);\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwtHandlerAdapter.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * An <a href=\"http://en.wikipedia.org/wiki/Adapter_pattern\">Adapter</a> implementation of the\n * {@link JwtHandler} interface that allows for anonymous subclasses to process only the JWT results that are\n * known/expected for a particular use case.\n *\n * <p>All of the methods in this implementation throw exceptions: overridden methods represent\n * scenarios expected by calling code in known situations.  It would be unexpected to receive a JWT that did\n * not match parsing expectations, so all non-overridden methods throw exceptions to indicate that the JWT\n * input was unexpected.</p>\n *\n * @param <T> the type of object to return to the parser caller after handling the parsed JWT.\n * @since 0.2\n */\npublic abstract class JwtHandlerAdapter<T> extends SupportedJwtVisitor<T> implements JwtHandler<T> {\n\n    /**\n     * Default constructor, does not initialize any internal state.\n     */\n    public JwtHandlerAdapter() {\n    }\n\n    @Override\n    public T onUnsecuredContent(Jwt<Header, byte[]> jwt) {\n        return onContentJwt(jwt); // bridge for existing implementations\n    }\n\n    @Override\n    public T onUnsecuredClaims(Jwt<Header, Claims> jwt) {\n        return onClaimsJwt(jwt);\n    }\n\n    @Override\n    public T onVerifiedContent(Jws<byte[]> jws) {\n        return onContentJws(jws);\n    }\n\n    @Override\n    public T onVerifiedClaims(Jws<Claims> jws) {\n        return onClaimsJws(jws);\n    }\n\n    @Override\n    public T onDecryptedContent(Jwe<byte[]> jwe) {\n        return onContentJwe(jwe);\n    }\n\n    @Override\n    public T onDecryptedClaims(Jwe<Claims> jwe) {\n        return onClaimsJwe(jwe);\n    }\n\n    @Override\n    public T onContentJwt(Jwt<Header, byte[]> jwt) {\n        return super.onUnsecuredContent(jwt);\n    }\n\n    @Override\n    public T onClaimsJwt(Jwt<Header, Claims> jwt) {\n        return super.onUnsecuredClaims(jwt);\n    }\n\n    @Override\n    public T onContentJws(Jws<byte[]> jws) {\n        return super.onVerifiedContent(jws);\n    }\n\n    @Override\n    public T onClaimsJws(Jws<Claims> jws) {\n        return super.onVerifiedClaims(jws);\n    }\n\n    @Override\n    public T onContentJwe(Jwe<byte[]> jwe) {\n        return super.onDecryptedContent(jwe);\n    }\n\n    @Override\n    public T onClaimsJwe(Jwe<Claims> jwe) {\n        return super.onDecryptedClaims(jwe);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwtParser.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.security.SecurityException;\nimport io.jsonwebtoken.security.SignatureException;\n\nimport java.io.InputStream;\n\n/**\n * A parser for reading JWT strings, used to convert them into a {@link Jwt} object representing the expanded JWT.\n * A parser for reading JWT strings, used to convert them into a {@link Jwt} object representing the expanded JWT.\n *\n * @since 0.1\n */\npublic interface JwtParser extends Parser<Jwt<?, ?>> {\n\n    /**\n     * Returns {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}\n     * otherwise.\n     *\n     * <p>Note that if you are reasonably sure that the token is signed, it is more efficient to attempt to\n     * parse the token (and catching exceptions if necessary) instead of calling this method first before parsing.</p>\n     *\n     * @param compact the compact serialized JWT to check\n     * @return {@code true} if the specified JWT compact string represents a signed JWT (aka a 'JWS'), {@code false}\n     * otherwise.\n     */\n    boolean isSigned(CharSequence compact);\n\n    /**\n     * Parses the specified compact serialized JWT string based on the builder's current configuration state and\n     * returns the resulting JWT, JWS, or JWE instance.\n     *\n     * <p>Because it is often cumbersome to determine if the result is a JWT, JWS or JWE, or if the payload is a Claims\n     * or {@code byte[]} array with {@code instanceof} checks, it may be useful to call the result's\n     * {@link Jwt#accept(JwtVisitor) accept(JwtVisitor)} method for a type-safe callback approach instead of using if-then-else\n     * {@code instanceof} conditionals. For example, instead of:</p>\n     *\n     * <blockquote><pre>\n     * // NOT RECOMMENDED:\n     * Jwt&lt;?,?&gt; jwt = parser.parse(input);\n     * if (jwt instanceof Jwe&lt;?&gt;) {\n     *     Jwe&lt;?&gt; jwe = (Jwe&lt;?&gt;)jwt;\n     *     if (jwe.getPayload() instanceof Claims) {\n     *         Jwe&lt;Claims&gt; claimsJwe = (Jwe&lt;Claims&gt;)jwe;\n     *         // do something with claimsJwe\n     *     }\n     * }</pre></blockquote>\n     *\n     * <p>the following alternative is usually preferred:</p>\n     *\n     * <blockquote><pre>\n     * Jwe&lt;Claims&gt; jwe = parser.parse(input).accept({@link Jwe#CLAIMS});</pre></blockquote>\n     *\n     * @param jwt the compact serialized JWT to parse\n     * @return the parsed JWT instance\n     * @throws MalformedJwtException    if the specified JWT was incorrectly constructed (and therefore invalid).\n     *                                  Invalid JWTs should not be trusted and should be discarded.\n     * @throws SignatureException       if a JWS signature was discovered, but could not be verified.  JWTs that fail\n     *                                  signature validation should not be trusted and should be discarded.\n     * @throws SecurityException        if the specified JWT string is a JWE and decryption fails\n     * @throws ExpiredJwtException      if the specified JWT is a Claims JWT and the Claims has an expiration time\n     *                                  before the time this method is invoked.\n     * @throws IllegalArgumentException if the specified string is {@code null} or empty or only whitespace.\n     * @see Jwt#accept(JwtVisitor)\n     */\n    Jwt<?, ?> parse(CharSequence jwt) throws ExpiredJwtException, MalformedJwtException, SignatureException,\n            SecurityException, IllegalArgumentException;\n\n    /**\n     * Deprecated since 0.12.0 in favor of calling any {@code parse*} method immediately\n     * followed by invoking the parsed JWT's {@link Jwt#accept(JwtVisitor) accept} method with your preferred visitor. For\n     * example:\n     *\n     * <blockquote><pre>\n     * {@link #parse(CharSequence) parse}(jwt).{@link Jwt#accept(JwtVisitor) accept}({@link JwtVisitor visitor});</pre></blockquote>\n     *\n     * <p>This method will be removed before the 1.0 release.</p>\n     *\n     * @param jwt     the compact serialized JWT to parse\n     * @param handler the handler to invoke when encountering a specific type of JWT\n     * @param <T>     the type of object returned from the {@code handler}\n     * @return the result returned by the {@code JwtHandler}\n     * @throws MalformedJwtException    if the specified JWT was incorrectly constructed (and therefore invalid).\n     *                                  Invalid JWTs should not be trusted and should be discarded.\n     * @throws SignatureException       if a JWS signature was discovered, but could not be verified.  JWTs that fail\n     *                                  signature validation should not be trusted and should be discarded.\n     * @throws SecurityException        if the specified JWT string is a JWE and decryption fails\n     * @throws ExpiredJwtException      if the specified JWT is a Claims JWT and the Claims has an expiration time\n     *                                  before the time this method is invoked.\n     * @throws IllegalArgumentException if the specified string is {@code null} or empty or only whitespace, or if the\n     *                                  {@code handler} is {@code null}.\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.2\n     * @deprecated since 0.12.0 in favor of\n     * <code>{@link #parse(CharSequence)}.{@link Jwt#accept(JwtVisitor) accept}({@link JwtVisitor visitor});</code>\n     */\n    @Deprecated\n    <T> T parse(CharSequence jwt, JwtHandler<T> handler) throws ExpiredJwtException, UnsupportedJwtException,\n            MalformedJwtException, SignatureException, SecurityException, IllegalArgumentException;\n\n    /**\n     * Deprecated since 0.12.0 in favor of {@link #parseUnsecuredContent(CharSequence)}.\n     *\n     * <p>This method will be removed before the 1.0 release.</p>\n     *\n     * @param jwt a compact serialized unsecured content JWT string.\n     * @return the {@link Jwt Jwt} instance that reflects the specified compact JWT string.\n     * @throws UnsupportedJwtException  if the {@code jwt} argument does not represent an unsecured content JWT\n     * @throws MalformedJwtException    if the {@code jwt} string is not a valid JWT\n     * @throws SignatureException       if the {@code jwt} string is actually a JWS and signature validation fails\n     * @throws SecurityException        if the {@code jwt} string is actually a JWE and decryption fails\n     * @throws IllegalArgumentException if the {@code jwt} string is {@code null} or empty or only whitespace\n     * @see #parseUnsecuredContent(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.2\n     * @deprecated since 0.12.0 in favor of {@link #parseUnsecuredContent(CharSequence)}.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    Jwt<Header, byte[]> parseContentJwt(CharSequence jwt) throws UnsupportedJwtException, MalformedJwtException,\n            SignatureException, SecurityException, IllegalArgumentException;\n\n    /**\n     * Deprecated since 0.12.0 in favor of {@link #parseUnsecuredClaims(CharSequence)}.\n     *\n     * <p>This method will be removed before the 1.0 release.</p>\n     *\n     * @param jwt a compact serialized unsecured Claims JWT string.\n     * @return the {@link Jwt Jwt} instance that reflects the specified compact JWT string.\n     * @throws UnsupportedJwtException  if the {@code jwt} argument does not represent an unsecured Claims JWT\n     * @throws MalformedJwtException    if the {@code jwt} string is not a valid JWT\n     * @throws SignatureException       if the {@code jwt} string is actually a JWS and signature validation fails\n     * @throws SecurityException        if the {@code jwt} string is actually a JWE and decryption fails\n     * @throws IllegalArgumentException if the {@code jwt} string is {@code null} or empty or only whitespace\n     * @see #parseUnsecuredClaims(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.2\n     * @deprecated since 0.12.0 in favor of {@link #parseUnsecuredClaims(CharSequence)}.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    Jwt<Header, Claims> parseClaimsJwt(CharSequence jwt) throws ExpiredJwtException, UnsupportedJwtException,\n            MalformedJwtException, SignatureException, SecurityException, IllegalArgumentException;\n\n    /**\n     * Deprecated since 0.12.0 in favor of {@link #parseSignedContent(CharSequence)}.\n     *\n     * <p>This method will be removed before the 1.0 release.</p>\n     *\n     * @param jws a compact content JWS string\n     * @return the parsed and validated content JWS\n     * @throws UnsupportedJwtException  if the {@code jws} argument does not represent a content JWS\n     * @throws MalformedJwtException    if the {@code jws} string is not a valid JWS\n     * @throws SignatureException       if the {@code jws} JWS signature validation fails\n     * @throws SecurityException        if the {@code jws} string is actually a JWE and decryption fails\n     * @throws IllegalArgumentException if the {@code jws} string is {@code null} or empty or only whitespace\n     * @see #parseSignedContent(CharSequence)\n     * @see #parseEncryptedContent(CharSequence)\n     * @see #parse(CharSequence)\n     * @since 0.2\n     * @deprecated since 0.12.0 in favor of {@link #parseSignedContent(CharSequence)}.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    Jws<byte[]> parseContentJws(CharSequence jws) throws UnsupportedJwtException, MalformedJwtException, SignatureException,\n            SecurityException, IllegalArgumentException;\n\n    /**\n     * Deprecated since 0.12.0 in favor of {@link #parseSignedClaims(CharSequence)}.\n     *\n     * @param jws a compact Claims JWS string.\n     * @return the parsed and validated Claims JWS\n     * @throws UnsupportedJwtException  if the {@code claimsJws} argument does not represent an Claims JWS\n     * @throws MalformedJwtException    if the {@code claimsJws} string is not a valid JWS\n     * @throws SignatureException       if the {@code claimsJws} JWS signature validation fails\n     * @throws SecurityException        if the {@code jws} string is actually a JWE and decryption fails\n     * @throws ExpiredJwtException      if the specified JWT is a Claims JWT and the Claims has an expiration time\n     *                                  before the time this method is invoked.\n     * @throws IllegalArgumentException if the {@code claimsJws} string is {@code null} or empty or only whitespace\n     * @see #parseSignedClaims(CharSequence)\n     * @see #parseEncryptedClaims(CharSequence)\n     * @see #parse(CharSequence)\n     * @since 0.2\n     * @deprecated since 0.12.0 in favor of {@link #parseSignedClaims(CharSequence)}.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    Jws<Claims> parseClaimsJws(CharSequence jws) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException,\n            SignatureException, SecurityException, IllegalArgumentException;\n\n    /**\n     * Parses the {@code jwt} argument, expected to be an unsecured content JWT. If the JWT creator set\n     * the (optional) {@link Header#getContentType() contentType} header value, the application may inspect that\n     * value to determine how to convert the byte array to the final content type as desired.\n     *\n     * <p>This is a convenience method logically equivalent to the following:</p>\n     *\n     * <blockquote><pre>\n     * {@link #parse(CharSequence) parse}(jwt).{@link Jwt#accept(JwtVisitor) accept}({@link\n     * Jwt#UNSECURED_CONTENT});</pre></blockquote>\n     *\n     * @param jwt a compact unsecured content JWT.\n     * @return the parsed unsecured content JWT.\n     * @throws UnsupportedJwtException  if the {@code jwt} argument does not represent an unsecured content JWT\n     * @throws JwtException             if the {@code jwt} string cannot be parsed or validated as required.\n     * @throws IllegalArgumentException if the {@code jwt} string is {@code null} or empty or only whitespace\n     * @see #parse(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.12.0\n     */\n    Jwt<Header, byte[]> parseUnsecuredContent(CharSequence jwt) throws JwtException, IllegalArgumentException;\n\n    /**\n     * Parses the {@code jwt} argument, expected to be an unsecured {@code Claims} JWT. This is a\n     * convenience method logically equivalent to the following:\n     *\n     * <blockquote><pre>\n     * {@link #parse(CharSequence) parse}(jwt).{@link Jwt#accept(JwtVisitor) accept}({@link\n     * Jwt#UNSECURED_CLAIMS});</pre></blockquote>\n     *\n     * @param jwt a compact unsecured Claims JWT.\n     * @return the parsed unsecured Claims JWT.\n     * @throws UnsupportedJwtException  if the {@code jwt} argument does not represent an unsecured Claims JWT\n     * @throws JwtException             if the {@code jwt} string cannot be parsed or validated as required.\n     * @throws IllegalArgumentException if the {@code jwt} string is {@code null} or empty or only whitespace\n     * @see #parse(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.12.0\n     */\n    Jwt<Header, Claims> parseUnsecuredClaims(CharSequence jwt) throws JwtException, IllegalArgumentException;\n\n    /**\n     * Parses the {@code jws} argument, expected to be a cryptographically-signed content JWS. If the JWS\n     * creator set the (optional) {@link Header#getContentType() contentType} header value, the application may\n     * inspect that value to determine how to convert the byte array to the final content type as desired.\n     *\n     * <p>This is a convenience method logically equivalent to the following:</p>\n     *\n     * <blockquote><pre>\n     * {@link #parse(CharSequence) parse}(jws).{@link Jwt#accept(JwtVisitor) accept}({@link\n     * Jws#CONTENT});</pre></blockquote>\n     *\n     * @param jws a compact cryptographically-signed content JWS.\n     * @return the parsed cryptographically-verified content JWS.\n     * @throws UnsupportedJwtException  if the {@code jws} argument does not represent a signed content JWS\n     * @throws JwtException             if the {@code jws} string cannot be parsed or validated as required.\n     * @throws IllegalArgumentException if the {@code jws} string is {@code null} or empty or only whitespace\n     * @see #parse(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.12.0\n     */\n    Jws<byte[]> parseSignedContent(CharSequence jws) throws JwtException, IllegalArgumentException;\n\n    /**\n     * Parses a JWS known to use the\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc7797\">RFC 7797: JSON Web Signature (JWS) Unencoded Payload\n     * Option</a>, using the specified {@code unencodedPayload} for signature verification.\n     *\n     * <p><b>Unencoded Non-Detached Payload</b></p>\n     *\n     * <p>Note that if the JWS contains a valid unencoded Payload string (what RFC 7797 calls an\n     * &quot;<a href=\"https://datatracker.ietf.org/doc/html/rfc7797#section-5.2\">unencoded non-detached\n     * payload</a>&quot;, the {@code unencodedPayload} method argument will be ignored, as the JWS already includes\n     * the payload content necessary for signature verification.</p>\n     *\n     * @param jws              the Unencoded Payload JWS to parse.\n     * @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.\n     * @return the parsed Unencoded Payload.\n     * @since 0.12.0\n     */\n    Jws<byte[]> parseSignedContent(CharSequence jws, byte[] unencodedPayload);\n\n    /**\n     * Parses a JWS known to use the\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc7797\">RFC 7797: JSON Web Signature (JWS) Unencoded Payload\n     * Option</a>, using the bytes from the specified {@code unencodedPayload} stream for signature verification.\n     *\n     * <p>Because it is not possible to know how large the {@code unencodedPayload} stream will be, the stream bytes\n     * will not be buffered in memory, ensuring the resulting {@link Jws} return value's {@link Jws#getPayload()}\n     * is always empty.  This is generally not a concern since the caller already has access to the stream bytes and\n     * may obtain them independently before or after calling this method if they are needed otherwise.</p>\n     *\n     * <p><b>Unencoded Non-Detached Payload</b></p>\n     *\n     * <p>Note that if the JWS contains a valid unencoded payload String (what RFC 7797 calls an\n     * &quot;<a href=\"https://datatracker.ietf.org/doc/html/rfc7797#section-5.2\">unencoded non-detached\n     * payload</a>&quot;, the {@code unencodedPayload} method argument will be ignored, as the JWS already includes\n     * the payload content necessary for signature verification. In this case the resulting {@link Jws} return\n     * value's {@link Jws#getPayload()} will contain the embedded payload String's UTF-8 bytes.</p>\n     *\n     * @param jws              the Unencoded Payload JWS to parse.\n     * @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.\n     * @return the parsed Unencoded Payload.\n     * @since 0.12.0\n     */\n    Jws<byte[]> parseSignedContent(CharSequence jws, InputStream unencodedPayload);\n\n    /**\n     * Parses the {@code jws} argument, expected to be a cryptographically-signed {@code Claims} JWS. This is a\n     * convenience method logically equivalent to the following:\n     *\n     * <blockquote><pre>\n     * {@link #parse(CharSequence) parse}(jws).{@link Jwt#accept(JwtVisitor) accept}({@link\n     * Jws#CLAIMS});</pre></blockquote>\n     *\n     * @param jws a compact cryptographically-signed Claims JWS.\n     * @return the parsed cryptographically-verified Claims JWS.\n     * @throws UnsupportedJwtException  if the {@code jwt} argument does not represent a signed Claims JWT\n     * @throws JwtException             if the {@code jwt} string cannot be parsed or validated as required.\n     * @throws IllegalArgumentException if the {@code jwt} string is {@code null} or empty or only whitespace\n     * @see #parse(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.12.0\n     */\n    Jws<Claims> parseSignedClaims(CharSequence jws) throws JwtException, IllegalArgumentException;\n\n    /**\n     * Parses a JWS known to use the\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc7797\">RFC 7797: JSON Web Signature (JWS) Unencoded Payload\n     * Option</a>, using the specified {@code unencodedPayload} for signature verification.\n     *\n     * <p><b>Unencoded Non-Detached Payload</b></p>\n     *\n     * <p>Note that if the JWS contains a valid unencoded payload String (what RFC 7797 calls an\n     * &quot;<a href=\"https://datatracker.ietf.org/doc/html/rfc7797#section-5.2\">unencoded non-detached\n     * payload</a>&quot;, the {@code unencodedPayload} method argument will be ignored, as the JWS already includes\n     * the payload content necessary for signature verification and claims creation.</p>\n     *\n     * @param jws              the Unencoded Payload JWS to parse.\n     * @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.\n     * @return the parsed and validated Claims JWS.\n     * @throws JwtException             if parsing, signature verification, or JWT validation fails.\n     * @throws IllegalArgumentException if either the {@code jws} or {@code unencodedPayload} are null or empty.\n     * @since 0.12.0\n     */\n    Jws<Claims> parseSignedClaims(CharSequence jws, byte[] unencodedPayload) throws JwtException, IllegalArgumentException;\n\n    /**\n     * Parses a JWS known to use the\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc7797\">RFC 7797: JSON Web Signature (JWS) Unencoded Payload\n     * Option</a>, using the bytes from the specified {@code unencodedPayload} stream for signature verification and\n     * {@link Claims} creation.\n     *\n     * <p><b>NOTE:</b> however, because calling this method indicates a completed\n     * {@link Claims} instance is desired, the specified {@code unencodedPayload} JSON stream will be fully\n     * read into a Claims instance.  If this will be problematic for your application (perhaps if you expect extremely\n     * large Claims), it is recommended to use the {@link #parseSignedContent(CharSequence, InputStream)} method\n     * instead.</p>\n     *\n     * <p><b>Unencoded Non-Detached Payload</b></p>\n     *\n     * <p>Note that if the JWS contains a valid unencoded Payload string (what RFC 7797 calls an\n     * &quot;<a href=\"https://datatracker.ietf.org/doc/html/rfc7797#section-5.2\">unencoded non-detached\n     * payload</a>&quot;, the {@code unencodedPayload} method argument will be ignored, as the JWS already includes\n     * the payload content necessary for signature verification and Claims creation.</p>\n     *\n     * @param jws              the Unencoded Payload JWS to parse.\n     * @param unencodedPayload the JWS's associated required unencoded payload used for signature verification.\n     * @return the parsed and validated Claims JWS.\n     * @throws JwtException             if parsing, signature verification, or JWT validation fails.\n     * @throws IllegalArgumentException if either the {@code jws} or {@code unencodedPayload} are null or empty.\n     * @since 0.12.0\n     */\n    Jws<Claims> parseSignedClaims(CharSequence jws, InputStream unencodedPayload) throws JwtException, IllegalArgumentException;\n\n    /**\n     * Parses the {@code jwe} argument, expected to be an encrypted content JWE. If the JWE\n     * creator set the (optional) {@link Header#getContentType() contentType} header value, the application may\n     * inspect that value to determine how to convert the byte array to the final content type as desired.\n     *\n     * <p>This is a convenience method logically equivalent to the following:</p>\n     *\n     * <blockquote><pre>\n     * {@link #parse(CharSequence) parse}(jwe).{@link Jwt#accept(JwtVisitor) accept}({@link\n     * Jwe#CONTENT});</pre></blockquote>\n     *\n     * @param jwe a compact encrypted content JWE.\n     * @return the parsed decrypted content JWE.\n     * @throws UnsupportedJwtException  if the {@code jwe} argument does not represent an encrypted content JWE\n     * @throws JwtException             if the {@code jwe} string cannot be parsed or validated as required.\n     * @throws IllegalArgumentException if the {@code jwe} string is {@code null} or empty or only whitespace\n     * @see #parse(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.12.0\n     */\n    Jwe<byte[]> parseEncryptedContent(CharSequence jwe) throws JwtException, IllegalArgumentException;\n\n    /**\n     * Parses the {@code jwe} argument, expected to be an encrypted {@code Claims} JWE. This is a\n     * convenience method logically equivalent to the following:\n     *\n     * <blockquote><pre>\n     * {@link #parse(CharSequence) parse}(jwe).{@link Jwt#accept(JwtVisitor) accept}({@link\n     * Jwe#CLAIMS});</pre></blockquote>\n     *\n     * @param jwe a compact encrypted Claims JWE.\n     * @return the parsed decrypted Claims JWE.\n     * @throws UnsupportedJwtException  if the {@code jwe} argument does not represent an encrypted Claims JWE.\n     * @throws JwtException             if the {@code jwe} string cannot be parsed or validated as required.\n     * @throws IllegalArgumentException if the {@code jwe} string is {@code null} or empty or only whitespace\n     * @see #parse(CharSequence)\n     * @see Jwt#accept(JwtVisitor)\n     * @since 0.12.0\n     */\n    Jwe<Claims> parseEncryptedClaims(CharSequence jwe) throws JwtException, IllegalArgumentException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwtParserBuilder.java",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.Deserializer;\nimport io.jsonwebtoken.lang.Builder;\nimport io.jsonwebtoken.lang.Conjunctor;\nimport io.jsonwebtoken.lang.NestedCollection;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\n\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * A builder to construct a {@link JwtParser}. Example usage:\n * <pre>{@code\n *     Jwts.parser()\n *         .requireIssuer(\"https://issuer.example.com\")\n *         .verifyWith(...)\n *         .build()\n *         .parse(jwtString)\n * }</pre>\n *\n * @since 0.11.0\n */\n@SuppressWarnings(\"JavadocLinkAsPlainText\")\npublic interface JwtParserBuilder extends Builder<JwtParser> {\n\n    /**\n     * Enables parsing of Unsecured JWTs (JWTs with an 'alg' (Algorithm) header value of\n     * 'none' or missing the 'alg' header entirely). <b>Be careful when calling this method - one should fully understand\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-8.5\">Unsecured JWS Security Considerations</a>\n     * before enabling this feature.</b>\n     * <p>If this method is not called, Unsecured JWTs are disabled by default as mandated by\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6\">RFC 7518, Section\n     * 3.6</a>.</p>\n     *\n     * @return the builder for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-8.5\">Unsecured JWS Security Considerations</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6\">Using the Algorithm &quot;none&quot;</a>\n     * @see Jwts.SIG#NONE\n     * @see #unsecuredDecompression()\n     * @since 0.12.0\n     */\n    JwtParserBuilder unsecured();\n\n    /**\n     * If the parser is {@link #unsecured()}, calling this method additionally enables\n     * payload decompression of Unsecured JWTs (JWTs with an 'alg' (Algorithm) header value of 'none') that also have\n     * a 'zip' (Compression) header. This behavior is disabled by default because using compression\n     * algorithms with data from unverified (unauthenticated) parties can be susceptible to Denial of Service attacks\n     * and other data integrity problems as described in\n     * <a href=\"https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf\">In the\n     * Compression Hornet’s Nest: A Security Study of Data Compression in Network Services</a>.\n     *\n     * <p>Because this behavior is only relevant if the parser is unsecured,\n     * calling this method without also calling {@link #unsecured()} will result in a build exception, as the\n     * incongruent state could reflect a misunderstanding of both behaviors which should be remedied by the\n     * application developer.</p>\n     *\n     * <b>As is the case for {@link #unsecured()}, be careful when calling this method - one should fully\n     * understand\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-8.5\">Unsecured JWS Security Considerations</a>\n     * before enabling this feature.</b>\n     *\n     * @return the builder for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-8.5\">Unsecured JWS Security Considerations</a>\n     * @see <a href=\"https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf\">In the\n     * Compression Hornet’s Nest: A Security Study of Data Compression in Network Services</a>\n     * @see Jwts.SIG#NONE\n     * @see #unsecured()\n     * @since 0.12.0\n     */\n    JwtParserBuilder unsecuredDecompression();\n\n    /**\n     * Configures the {@link ProtectedHeader} parameter names used in JWT extensions supported by the application. If\n     * the parser encounters a Protected JWT that {@link ProtectedHeader#getCritical() requires} extensions, and\n     * those extensions' header names are not specified via this method, the parser will reject that JWT.\n     *\n     * <p>The collection's {@link Conjunctor#and() and()} method returns to the builder for continued parser\n     * configuration, for example:</p>\n     * <blockquote><pre>\n     * parserBuilder.critical().add(\"headerName\")<b>.{@link Conjunctor#and() and()} // etc...</b></pre></blockquote>\n     *\n     * <p><b>Extension Behavior</b></p>\n     *\n     * <p>The {@code critical} collection only identifies header parameter names that are used in extensions supported\n     * by the application. <b>Application developers, <em>not JJWT</em>, MUST perform the associated extension behavior\n     * using the parsed JWT</b>.</p>\n     *\n     * <p><b>Continued Parser Configuration</b></p>\n     * <p>When finished, use the collection's\n     * {@link Conjunctor#and() and()} method to continue parser configuration, for example:\n     * <blockquote><pre>\n     * Jwts.parser()\n     *     .critical().add(\"headerName\").<b>{@link Conjunctor#and() and()} // return parent</b>\n     * // resume parser configuration...</pre></blockquote>\n     *\n     * @return the {@link NestedCollection} to use for {@code crit} configuration.\n     * @see ProtectedHeader#getCritical()\n     * @since 0.12.0\n     */\n    NestedCollection<String, JwtParserBuilder> critical();\n\n    /**\n     * Sets the JCA Provider to use during cryptographic signature and key decryption operations, or {@code null} if the\n     * JCA subsystem preferred provider should be used.\n     *\n     * @param provider the JCA Provider to use during cryptographic signature and decryption operations, or {@code null}\n     *                 if the JCA subsystem preferred provider should be used.\n     * @return the builder for method chaining.\n     * @since 0.12.0\n     */\n    JwtParserBuilder provider(Provider provider);\n\n    /**\n     * Ensures that the specified {@code jti} exists in the parsed JWT.  If missing or if the parsed\n     * value does not equal the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param id the required value of the {@code jti} header parameter.\n     * @return the parser builder for method chaining.\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder requireId(String id);\n\n    /**\n     * Ensures that the specified {@code sub} exists in the parsed JWT.  If missing or if the parsed\n     * value does not equal the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param subject the required value of the {@code sub} header parameter.\n     * @return the parser builder for method chaining.\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder requireSubject(String subject);\n\n    /**\n     * Ensures that the specified {@code aud} exists in the parsed JWT.  If missing or if the parsed\n     * value does not contain the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param audience the required value of the {@code aud} header parameter.\n     * @return the parser builder for method chaining.\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder requireAudience(String audience);\n\n    /**\n     * Ensures that the specified {@code iss} exists in the parsed JWT.  If missing or if the parsed\n     * value does not equal the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param issuer the required value of the {@code iss} header parameter.\n     * @return the parser builder for method chaining.\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder requireIssuer(String issuer);\n\n    /**\n     * Ensures that the specified {@code iat} exists in the parsed JWT.  If missing or if the parsed\n     * value does not equal the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param issuedAt the required value of the {@code iat} header parameter.\n     * @return the parser builder for method chaining.\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder requireIssuedAt(Date issuedAt);\n\n    /**\n     * Ensures that the specified {@code exp} exists in the parsed JWT.  If missing or if the parsed\n     * value does not equal the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param expiration the required value of the {@code exp} header parameter.\n     * @return the parser builder for method chaining.\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder requireExpiration(Date expiration);\n\n    /**\n     * Ensures that the specified {@code nbf} exists in the parsed JWT.  If missing or if the parsed\n     * value does not equal the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param notBefore the required value of the {@code npf} header parameter.\n     * @return the parser builder for method chaining\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder requireNotBefore(Date notBefore);\n\n    /**\n     * Ensures that the specified {@code claimName} exists in the parsed JWT.  If missing or if the parsed\n     * value does not equal the specified value, an exception will be thrown indicating that the\n     * JWT is invalid and may not be used.\n     *\n     * @param claimName the name of a claim that must exist\n     * @param value     the required value of the specified {@code claimName}\n     * @return the parser builder for method chaining.\n     * @see MissingClaimException\n     * @see IncorrectClaimException\n     */\n    JwtParserBuilder require(String claimName, Object value);\n\n    /**\n     * Sets the {@link Clock} that determines the timestamp to use when validating the parsed JWT.\n     * The parser uses a default Clock implementation that simply returns {@code new Date()} when called.\n     *\n     * @param clock a {@code Clock} object to return the timestamp to use when validating the parsed JWT.\n     * @return the parser builder for method chaining.\n     * @deprecated since 0.12.0 for the more modern builder-style named {@link #clock(Clock)} method.\n     * This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    JwtParserBuilder setClock(Clock clock);\n\n    /**\n     * Sets the {@link Clock} that determines the timestamp to use when validating the parsed JWT.\n     * The parser uses a default Clock implementation that simply returns {@code new Date()} when called.\n     *\n     * @param clock a {@code Clock} object to return the timestamp to use when validating the parsed JWT.\n     * @return the parser builder for method chaining.\n     */\n    JwtParserBuilder clock(Clock clock);\n\n    /**\n     * Sets the amount of clock skew in seconds to tolerate when verifying the local time against the {@code exp}\n     * and {@code nbf} claims.\n     *\n     * @param seconds the number of seconds to tolerate for clock skew when verifying {@code exp} or {@code nbf} claims.\n     * @return the parser builder for method chaining.\n     * @throws IllegalArgumentException if {@code seconds} is a value greater than {@code Long.MAX_VALUE / 1000} as\n     *                                  any such value would cause numeric overflow when multiplying by 1000 to obtain\n     *                                  a millisecond value.\n     * @deprecated since 0.12.0 in favor of the shorter and more modern builder-style named\n     * {@link #clockSkewSeconds(long)}. This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    JwtParserBuilder setAllowedClockSkewSeconds(long seconds) throws IllegalArgumentException;\n\n    /**\n     * Sets the amount of clock skew in seconds to tolerate when verifying the local time against the {@code exp}\n     * and {@code nbf} claims.\n     *\n     * @param seconds the number of seconds to tolerate for clock skew when verifying {@code exp} or {@code nbf} claims.\n     * @return the parser builder for method chaining.\n     * @throws IllegalArgumentException if {@code seconds} is a value greater than {@code Long.MAX_VALUE / 1000} as\n     *                                  any such value would cause numeric overflow when multiplying by 1000 to obtain\n     *                                  a millisecond value.\n     */\n    JwtParserBuilder clockSkewSeconds(long seconds) throws IllegalArgumentException;\n\n    /**\n     * <p><b>Deprecation Notice</b></p>\n     *\n     * <p>This method has been deprecated since 0.12.0 and will be removed before 1.0.  It was not\n     * readily obvious to many JJWT users that this method was for bytes that pertained <em>only</em> to HMAC\n     * {@code SecretKey}s, and could be confused with keys of other types.  It is better to obtain a type-safe\n     * {@link SecretKey} instance and call {@link #verifyWith(SecretKey)} instead.</p>\n     *\n     * <p>Previous Documentation</p>\n     *\n     * <p>Sets the signing key used to verify any discovered JWS digital signature.  If the specified JWT string is not\n     * a JWS (no signature), this key is not used.</p>\n     *\n     * <p>Note that this key <em>MUST</em> be a valid key for the signature algorithm found in the JWT header\n     * (as the {@code alg} header parameter).</p>\n     *\n     * <p>This method overwrites any previously set key.</p>\n     *\n     * @param key the algorithm-specific signature verification key used to validate any discovered JWS digital\n     *            signature.\n     * @return the parser builder for method chaining.\n     * @deprecated since 0.12.0 in favor of {@link #verifyWith(SecretKey)} for type safety and name\n     * congruence with the {@link #decryptWith(SecretKey)} method.\n     */\n    @Deprecated\n    JwtParserBuilder setSigningKey(byte[] key);\n\n    /**\n     * <p><b>Deprecation Notice: Deprecated as of 0.10.0, will be removed in 1.0.0</b></p>\n     *\n     * <p>This method has been deprecated because the {@code key} argument for this method can be confusing: keys for\n     * cryptographic operations are always binary (byte arrays), and many people were confused as to how bytes were\n     * obtained from the String argument.</p>\n     *\n     * <p>This method always expected a String argument that was effectively the same as the result of the following\n     * (pseudocode):</p>\n     *\n     * <p>{@code String base64EncodedSecretKey = base64Encode(secretKeyBytes);}</p>\n     *\n     * <p>However, a non-trivial number of JJWT users were confused by the method signature and attempted to\n     * use raw password strings as the key argument - for example {@code setSigningKey(myPassword)} - which is\n     * almost always incorrect for cryptographic hashes and can produce erroneous or insecure results.</p>\n     *\n     * <p>See this\n     * <a href=\"https://stackoverflow.com/questions/40252903/static-secret-as-byte-key-or-string/40274325#40274325\">\n     * StackOverflow answer</a> explaining why raw (non-base64-encoded) strings are almost always incorrect for\n     * signature operations.</p>\n     *\n     * <p>Finally, please use the {@link #verifyWith(SecretKey)} method instead, as this method (and likely\n     * {@link #setSigningKey(byte[])}) will be removed before the 1.0.0 release.</p>\n     *\n     * <p><b>Previous JavaDoc</b></p>\n     *\n     * <p>This is a convenience method that equates to the following:</p>\n     *\n     * <blockquote><pre>\n     * byte[] bytes = Decoders.{@link io.jsonwebtoken.io.Decoders#BASE64 BASE64}.decode(base64EncodedSecretKey);\n     * Key key = Keys.{@link io.jsonwebtoken.security.Keys#hmacShaKeyFor(byte[]) hmacShaKeyFor}(bytes);\n     * return {@link #verifyWith(SecretKey) verifyWith}(key);</pre></blockquote>\n     *\n     * @param base64EncodedSecretKey BASE64-encoded HMAC-SHA key bytes used to create a Key which will be used to\n     *                               verify all encountered JWS digital signatures.\n     * @return the parser builder for method chaining.\n     * @deprecated in favor of {@link #verifyWith(SecretKey)} as explained in the above <b>Deprecation Notice</b>,\n     * and will be removed in 1.0.0.\n     */\n    @Deprecated\n    JwtParserBuilder setSigningKey(String base64EncodedSecretKey);\n\n    /**\n     * <p><b>Deprecation Notice</b></p>\n     *\n     * <p>This method is being renamed to accurately reflect its purpose - the key is not technically a signing key,\n     * it is a signature verification key, and the two concepts can be different, especially with asymmetric key\n     * cryptography.  The method has been deprecated since 0.12.0 in favor of\n     * {@link #verifyWith(SecretKey)} for type safety, to reflect accurate naming of the concept, and for name\n     * congruence with the {@link #decryptWith(SecretKey)} method.</p>\n     *\n     * <p>This method merely delegates directly to {@link #verifyWith(SecretKey)} or {@link #verifyWith(PublicKey)}}.</p>\n     *\n     * @param key the algorithm-specific signature verification key to use to verify all encountered JWS digital\n     *            signatures.\n     * @return the parser builder for method chaining.\n     * @deprecated since 0.12.0 in favor of {@link #verifyWith(SecretKey)} for naming congruence with the\n     * {@link #decryptWith(SecretKey)} method.\n     */\n    @Deprecated\n    JwtParserBuilder setSigningKey(Key key);\n\n    /**\n     * Sets the signature verification SecretKey used to verify all encountered JWS signatures. If the encountered JWT\n     * string is not a JWS (e.g. unsigned or a JWE), this key is not used.\n     *\n     * <p>This is a convenience method to use in a specific scenario: when the parser will only ever encounter\n     * JWSs with signatures that can always be verified by a single SecretKey.  This also implies that this key\n     * <em>MUST</em> be a valid key for the signature algorithm ({@code alg} header) used for the JWS.</p>\n     *\n     * <p>If there is any chance that the parser will also encounter JWEs, or JWSs that need different signature\n     * verification keys based on the JWS being parsed, it is strongly recommended to configure your own\n     * {@link #keyLocator(Locator) keyLocator} instead of calling this method.</p>\n     *\n     * <p>Calling this method overrides any previously set signature verification key.</p>\n     *\n     * @param key the signature verification key to use to verify all encountered JWS digital signatures.\n     * @return the parser builder for method chaining.\n     * @see #verifyWith(PublicKey)\n     * @since 0.12.0\n     */\n    JwtParserBuilder verifyWith(SecretKey key);\n\n    /**\n     * Sets the signature verification PublicKey used to verify all encountered JWS signatures. If the encountered JWT\n     * string is not a JWS (e.g. unsigned or a JWE), this key is not used.\n     *\n     * <p>This is a convenience method to use in a specific scenario: when the parser will only ever encounter\n     * JWSs with signatures that can always be verified by a single PublicKey.  This also implies that this key\n     * <em>MUST</em> be a valid key for the signature algorithm ({@code alg} header) used for the JWS.</p>\n     *\n     * <p>If there is any chance that the parser will also encounter JWEs, or JWSs that need different signature\n     * verification keys based on the JWS being parsed, it is strongly recommended to configure your own\n     * {@link #keyLocator(Locator) keyLocator} instead of calling this method.</p>\n     *\n     * <p>Calling this method overrides any previously set signature verification key.</p>\n     *\n     * @param key the signature verification key to use to verify all encountered JWS digital signatures.\n     * @return the parser builder for method chaining.\n     * @see #verifyWith(SecretKey)\n     * @since 0.12.0\n     */\n    JwtParserBuilder verifyWith(PublicKey key);\n\n    /**\n     * Sets the decryption SecretKey used to decrypt all encountered JWEs. If the encountered JWT string is not a\n     * JWE (e.g. a JWS), this key is not used.\n     *\n     * <p>This is a convenience method to use in specific circumstances: when the parser will only ever encounter\n     * JWEs that can always be decrypted by a single SecretKey. This also implies that this key <em>MUST</em> be a valid\n     * key for both the key management algorithm ({@code alg} header) and the content encryption algorithm\n     * ({@code enc} header) used for the JWE.</p>\n     *\n     * <p>If there is any chance that the parser will also encounter JWSs, or JWEs that need different decryption\n     * keys based on the JWE being parsed, it is strongly recommended to configure your own\n     * {@link #keyLocator(Locator) keyLocator} instead of calling this method.</p>\n     *\n     * <p>Calling this method overrides any previously set decryption key.</p>\n     *\n     * @param key the algorithm-specific decryption key to use to decrypt all encountered JWEs.\n     * @return the parser builder for method chaining.\n     * @see #decryptWith(PrivateKey)\n     * @since 0.12.0\n     */\n    JwtParserBuilder decryptWith(SecretKey key);\n\n    /**\n     * Sets the decryption PrivateKey used to decrypt all encountered JWEs. If the encountered JWT string is not a\n     * JWE (e.g. a JWS), this key is not used.\n     *\n     * <p>This is a convenience method to use in specific circumstances: when the parser will only ever encounter JWEs\n     * that can always be decrypted by a single PrivateKey. This also implies that this key <em>MUST</em> be a valid\n     * key for the JWE's key management algorithm ({@code alg} header).</p>\n     *\n     * <p>If there is any chance that the parser will also encounter JWSs, or JWEs that need different decryption\n     * keys based on the JWE being parsed, it is strongly recommended to configure your own\n     * {@link #keyLocator(Locator) keyLocator} instead of calling this method.</p>\n     *\n     * <p>Calling this method overrides any previously set decryption key.</p>\n     *\n     * @param key the algorithm-specific decryption key to use to decrypt all encountered JWEs.\n     * @return the parser builder for method chaining.\n     * @see #decryptWith(SecretKey)\n     * @since 0.12.0\n     */\n    JwtParserBuilder decryptWith(PrivateKey key);\n\n    /**\n     * Sets the {@link Locator} used to acquire any signature verification or decryption key needed during parsing.\n     * <ul>\n     *     <li>If the parsed String is a JWS, the {@code Locator} will be called to find the appropriate key\n     *     necessary to verify the JWS signature.</li>\n     *     <li>If the parsed String is a JWE, it will be called to find the appropriate decryption key.</li>\n     * </ul>\n     *\n     * <p>A key {@code Locator} is necessary when the signature verification or decryption key is not\n     * already known before parsing the JWT and the JWT header must be inspected first to determine how to\n     * look up the verification or decryption key.  Once returned by the locator, the JwtParser will then either\n     * verify the JWS signature or decrypt the JWE payload with the returned key.  For example:</p>\n     *\n     * <pre>\n     * Jws&lt;Claims&gt; jws = Jwts.parser().keyLocator(new Locator&lt;Key&gt;() {\n     *         &#64;Override\n     *         public Key locate(Header&lt;?&gt; header) {\n     *             if (header instanceof JwsHeader) {\n     *                 return getSignatureVerificationKey((JwsHeader)header); // implement me\n     *             } else {\n     *                 return getDecryptionKey((JweHeader)header); // implement me\n     *             }\n     *         }})\n     *     .build()\n     *     .parseSignedClaims(compact);\n     * </pre>\n     *\n     * <p>A Key {@code Locator} is invoked once during parsing before performing decryption or signature verification.</p>\n     *\n     * <p><b>Provider-constrained Keys</b></p>\n     *\n     * <p>If any verification or decryption key returned from a Key {@code Locator} must be used with a specific\n     * security {@link Provider} (such as for PKCS11 or Hardware Security Module (HSM) keys), you must make that\n     * Provider available for JWT parsing in one of 3 ways, listed in order of recommendation and simplicity:</p>\n     *\n     * <ol>\n     *     <li><a href=\"https://docs.oracle.com/en/java/javase/17/security/howtoimplaprovider.html#GUID-831AA25F-F702-442D-A2E4-8DA6DEA16F33\">\n     *         Configure the Provider in the JVM</a>, either by modifying the {@code java.security} file or by\n     *         registering the Provider dynamically via\n     *         {@link java.security.Security#addProvider(Provider) Security.addProvider(Provider)}.  This is the\n     *         recommended approach so you do not need to modify code anywhere that may need to parse JWTs.</li>\n     *      <li>Specify the {@code Provider} as the {@code JwtParser} default via {@link #provider(Provider)}. This will\n     *          ensure the provider is used by default with <em>all</em> located keys unless overridden by a\n     *          key-specific Provider. This is only recommended when you are confident that all JWTs encountered by the\n     *          parser instance will use keys attributed to the same {@code Provider}, unless overridden by a specific\n     *          key.</li>\n     *      <li>Associate the {@code Provider} with a specific key so it is used for that key only.  This option\n     *          is useful if some located keys require a specific provider, while other located keys can assume a\n     *          default provider.</li>\n     * </ol>\n     *\n     * <p>If you need to use option &#35;3, you associate a key for the {@code JwtParser}'s needs by using a\n     * key builder before returning the key as the {@code Locator} return value.  For example:</p>\n     * <blockquote><pre>\n     *     public Key locate(Header&lt;?&gt; header) {\n     *         PrivateKey key = findKey(header); // or SecretKey\n     *         Provider keySpecificProvider = getKeyProvider(key); // implement me\n     *         // associate the key with its required provider:\n     *         return Keys.builder(key).provider(keySpecificProvider).build();\n     *     }</pre></blockquote>\n     *\n     * @param keyLocator the locator used to retrieve decryption or signature verification keys.\n     * @return the parser builder for method chaining.\n     * @since 0.12.0\n     */\n    JwtParserBuilder keyLocator(Locator<Key> keyLocator);\n\n    /**\n     * <p><b>Deprecation Notice</b></p>\n     *\n     * <p>This method has been deprecated as of JJWT version 0.12.0 because it only supports key location\n     * for JWSs (signed JWTs) instead of both signed (JWS) and encrypted (JWE) scenarios.  Use the\n     * {@link #keyLocator(Locator) keyLocator} method instead to ensure a locator that can work for both JWS and\n     * JWE inputs.  This method will be removed for the 1.0 release.</p>\n     *\n     * <p><b>Previous Documentation</b></p>\n     *\n     * <p>Sets the {@link SigningKeyResolver} used to acquire the <code>signing key</code> that should be used to verify\n     * a JWS's signature.  If the parsed String is not a JWS (no signature), this resolver is not used.</p>\n     *\n     * <p>Specifying a {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing\n     * the JWT and the JWT header or payload (content byte array or Claims) must be inspected first to determine how to\n     * look up the signing key.  Once returned by the resolver, the JwtParser will then verify the JWS signature with the\n     * returned key.  For example:</p>\n     *\n     * <pre>\n     * Jws&lt;Claims&gt; jws = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {\n     *         &#64;Override\n     *         public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n     *             //inspect the header or claims, lookup and return the signing key\n     *             return getSigningKey(header, claims); //implement me\n     *         }})\n     *     .build().parseSignedClaims(compact);\n     * </pre>\n     *\n     * <p>A {@code SigningKeyResolver} is invoked once during parsing before the signature is verified.</p>\n     *\n     * @param signingKeyResolver the signing key resolver used to retrieve the signing key.\n     * @return the parser builder for method chaining.\n     * @deprecated since 0.12.0 in favor of {@link #keyLocator(Locator)}\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    JwtParserBuilder setSigningKeyResolver(SigningKeyResolver signingKeyResolver);\n\n    /**\n     * Configures the parser's supported {@link AeadAlgorithm}s used to decrypt JWE payloads. If the parser\n     * encounters a JWE {@link JweHeader#getEncryptionAlgorithm() enc} header value that equals an\n     * AEAD algorithm's {@link Identifiable#getId() id}, that algorithm will be used to decrypt the JWT\n     * payload.\n     *\n     * <p>The collection's {@link Conjunctor#and() and()} method returns to the builder for continued parser\n     * configuration, for example:</p>\n     * <blockquote><pre>\n     * parserBuilder.enc().add(anAeadAlgorithm)<b>.{@link Conjunctor#and() and()} // etc...</b></pre></blockquote>\n     *\n     * <p><b>Standard Algorithms and Overrides</b></p>\n     *\n     * <p>All JWA-standard AEAD encryption algorithms in the {@link Jwts.ENC} registry are supported by default and\n     * do not need to be added. The collection may be useful however for removing some algorithms (for example,\n     * any algorithms not used by the application, or those not compatible with application security requirements),\n     * or for adding custom implementations.</p>\n     *\n     * <p><b>Custom Implementations</b></p>\n     *\n     * <p>There may be only one registered {@code AeadAlgorithm} per algorithm {@code id}, and any algorithm\n     * instances that are {@link io.jsonwebtoken.lang.CollectionMutator#add(Object) add}ed to this collection with a\n     * duplicate ID will evict any existing or previously-added algorithm with the same {@code id}. <b>But beware:</b>\n     *\n     * <blockquote><b>\n     * Any algorithm instance added to this collection with a JWA-standard {@link Identifiable#getId() id} will\n     * replace (override) the JJWT standard algorithm implementation</b>.</blockquote>\n     *\n     * <p>This is to allow application developers to favor their\n     * own implementations over JJWT's default implementations if necessary (for example, to support legacy or\n     * custom behavior).</p>\n     *\n     * @return the {@link NestedCollection} to use to configure the AEAD encryption algorithms available when parsing.\n     * @see JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm)\n     * @see Jwts.ENC\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.2\">&quot;enc&quot; (Encryption Algorithm) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1.1\">Encryption Algorithm Name (id) requirements</a>\n     * @since 0.12.0\n     */\n    NestedCollection<AeadAlgorithm, JwtParserBuilder> enc();\n\n    /**\n     * Configures the parser's supported {@link KeyAlgorithm}s used to obtain a JWE's decryption key. If the\n     * parser encounters a JWE {@link JweHeader#getAlgorithm()} alg} header value that equals a {@code KeyAlgorithm}'s\n     * {@link Identifiable#getId() id}, that key algorithm will be used to obtain the JWE's decryption key.\n     *\n     * <p>The collection's {@link Conjunctor#and() and()} method returns to the builder for continued parser\n     * configuration, for example:</p>\n     * <blockquote><pre>\n     * parserBuilder.key().add(aKeyAlgorithm)<b>.{@link Conjunctor#and() and()} // etc...</b></pre></blockquote>\n     *\n     * <p><b>Standard Algorithms and Overrides</b></p>\n     *\n     * <p>All JWA-standard key encryption algorithms in the {@link Jwts.KEY} registry are supported by default and\n     * do not need to be added. The collection may be useful however for removing some algorithms (for example,\n     * any algorithms not used by the application, or those not compatible with application security requirements),\n     * or for adding custom implementations.</p>\n     *\n     * <p><b>Custom Implementations</b></p>\n     *\n     * <p>There may be only one registered {@code KeyAlgorithm} per algorithm {@code id}, and any algorithm\n     * instances that are {@link io.jsonwebtoken.lang.CollectionMutator#add(Object) add}ed to this collection with a\n     * duplicate ID will evict any existing or previously-added algorithm with the same {@code id}. <b>But beware:</b>\n     *\n     * <blockquote><b>\n     * Any algorithm instance added to this collection with a JWA-standard {@link Identifiable#getId() id} will\n     * replace (override) the JJWT standard algorithm implementation</b>.</blockquote>\n     *\n     * <p>This is to allow application developers to favor their\n     * own implementations over JJWT's default implementations if necessary (for example, to support legacy or\n     * custom behavior).</p>\n     *\n     * @return the {@link NestedCollection} to use to configure the key algorithms available when parsing.\n     * @see JwtBuilder#encryptWith(Key, KeyAlgorithm, AeadAlgorithm)\n     * @see Jwts.KEY\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.1\">JWE &quot;alg&quot; (Algorithm) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1.1\">Key Algorithm Name (id) requirements</a>\n     * @since 0.12.0\n     */\n    NestedCollection<KeyAlgorithm<?, ?>, JwtParserBuilder> key();\n\n    /**\n     * Configures the parser's supported\n     * {@link io.jsonwebtoken.security.SignatureAlgorithm SignatureAlgorithm} and\n     * {@link io.jsonwebtoken.security.MacAlgorithm MacAlgorithm}s used to verify JWS signatures. If the parser\n     * encounters a JWS {@link ProtectedHeader#getAlgorithm() alg} header value that equals a signature or MAC\n     * algorithm's {@link Identifiable#getId() id}, that algorithm will be used to verify the JWS signature.\n     *\n     * <p>The collection's {@link Conjunctor#and() and()} method returns to the builder for continued parser\n     * configuration, for example:</p>\n     * <blockquote><pre>\n     * parserBuilder.sig().add(aSignatureAlgorithm)<b>.{@link Conjunctor#and() and()} // etc...</b></pre></blockquote>\n     *\n     * <p><b>Standard Algorithms and Overrides</b></p>\n     *\n     * <p>All JWA-standard signature and MAC algorithms in the {@link Jwts.SIG} registry are supported by default and\n     * do not need to be added. The collection may be useful however for removing some algorithms (for example,\n     * any algorithms not used by the application, or those not compatible with application security requirements), or\n     * for adding custom implementations.</p>\n     *\n     * <p><b>Custom Implementations</b></p>\n     *\n     * <p>There may be only one registered {@code SecureDigestAlgorithm} per algorithm {@code id}, and any algorithm\n     * instances that are {@link io.jsonwebtoken.lang.CollectionMutator#add(Object) add}ed to this collection with a\n     * duplicate ID will evict any existing or previously-added algorithm with the same {@code id}. <b>But beware:</b>\n     *\n     * <blockquote><b>\n     * Any algorithm instance added to this collection with a JWA-standard {@link Identifiable#getId() id} will\n     * replace (override) the JJWT standard algorithm implementation</b>.</blockquote>\n     *\n     * <p>This is to allow application developers to favor their\n     * own implementations over JJWT's default implementations if necessary (for example, to support legacy or\n     * custom behavior).</p>\n     *\n     * @return the {@link NestedCollection} to use to configure the signature and MAC algorithms available when parsing.\n     * @see JwtBuilder#signWith(Key, SecureDigestAlgorithm)\n     * @see Jwts.SIG\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.1\">JWS &quot;alg&quot; (Algorithm) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1.1\">Algorithm Name (id) requirements</a>\n     * @since 0.12.0\n     */\n    NestedCollection<SecureDigestAlgorithm<?, ?>, JwtParserBuilder> sig();\n\n    /**\n     * Configures the parser's supported {@link CompressionAlgorithm}s used to decompress JWT payloads. If the parser\n     * encounters a JWT {@link ProtectedHeader#getCompressionAlgorithm() zip} header value that equals a\n     * compression algorithm's {@link Identifiable#getId() id}, that algorithm will be used to decompress the JWT\n     * payload.\n     *\n     * <p>The collection's {@link Conjunctor#and() and()} method returns to the builder for continued parser\n     * configuration, for example:</p>\n     * <blockquote><pre>\n     * parserBuilder.zip().add(aCompressionAlgorithm)<b>.{@link Conjunctor#and() and()} // etc...</b></pre></blockquote>\n     *\n     * <p><b>Standard Algorithms and Overrides</b></p>\n     *\n     * <p>All JWA-standard compression algorithms in the {@link Jwts.ZIP} registry are supported by default and\n     * do not need to be added. The collection may be useful however for removing some algorithms (for example,\n     * any algorithms not used by the application), or for adding custom implementations.</p>\n     *\n     * <p><b>Custom Implementations</b></p>\n     *\n     * <p>There may be only one registered {@code CompressionAlgorithm} per algorithm {@code id}, and any algorithm\n     * instances that are {@link io.jsonwebtoken.lang.CollectionMutator#add(Object) add}ed to this collection with a\n     * duplicate ID will evict any existing or previously-added algorithm with the same {@code id}. <b>But beware:</b>\n     *\n     * <blockquote><b>\n     * Any algorithm instance added to this collection with a JWA-standard {@link Identifiable#getId() id} will\n     * replace (override) the JJWT standard algorithm implementation</b>.</blockquote>\n     *\n     * <p>This is to allow application developers to favor their\n     * own implementations over JJWT's default implementations if necessary (for example, to support legacy or\n     * custom behavior).</p>\n     *\n     * @return the {@link NestedCollection} to use to configure the compression algorithms available when parsing.\n     * @see JwtBuilder#compressWith(CompressionAlgorithm)\n     * @see Jwts.ZIP\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516#section-4.1.3\">&quot;zip&quot; (Compression Algorithm) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.3.1\">Compression Algorithm Name (id) requirements</a>\n     * @since 0.12.0\n     */\n    NestedCollection<CompressionAlgorithm, JwtParserBuilder> zip();\n\n    /**\n     * <p><b>Deprecated as of JJWT 0.12.0. This method will be removed before the 1.0 release.</b></p>\n     *\n     * <p>This method has been deprecated as of JJWT version 0.12.0 because it imposed unnecessary\n     * implementation requirements on application developers when simply adding to a compression algorithm collection\n     * would suffice.  Use the {@link #zip()} method instead to add\n     * any custom algorithm implementations without needing to also implement a Locator implementation.</p>\n     *\n     * <p><b>Previous Documentation</b></p>\n     * <p>\n     * Sets the {@link CompressionCodecResolver} used to acquire the {@link CompressionCodec} that should be used to\n     * decompress the JWT body. If the parsed JWT is not compressed, this resolver is not used.\n     *\n     * <p><b>WARNING:</b> Compression is not defined by the JWS Specification - only the JWE Specification - and it is\n     * not expected that other libraries (including JJWT versions &lt; 0.6.0) are able to consume a compressed JWS\n     * body correctly.</p>\n     *\n     * <p><b>Default Support</b></p>\n     *\n     * <p>JJWT's default {@link JwtParser} implementation supports both the {@link Jwts.ZIP#DEF DEF}\n     * and {@link Jwts.ZIP#GZIP GZIP} algorithms by default - you do not need to\n     * specify a {@code CompressionCodecResolver} in these cases.</p>\n     *\n     * @param compressionCodecResolver the compression codec resolver used to decompress the JWT body.\n     * @return the parser builder for method chaining.\n     * @deprecated since 0.12.0 in favor of {@link #zip()}. This method will be removed before the\n     * 1.0 release.\n     */\n    @Deprecated\n    JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver compressionCodecResolver);\n\n    /**\n     * Perform Base64Url decoding with the specified Decoder\n     *\n     * <p>JJWT uses a spec-compliant decoder that works on all supported JDK versions, but you may call this method\n     * to specify a different decoder if you desire.</p>\n     *\n     * @param base64UrlDecoder the decoder to use when Base64Url-decoding\n     * @return the parser builder for method chaining.\n     * @deprecated since 0.12.0 in favor of {@link #b64Url(Decoder)}. This method will be removed\n     * before the JJWT 1.0 release.\n     */\n    @Deprecated\n    JwtParserBuilder base64UrlDecodeWith(Decoder<CharSequence, byte[]> base64UrlDecoder);\n\n    /**\n     * Perform Base64Url decoding during parsing with the specified {@code InputStream} Decoder.\n     * The Decoder's {@link Decoder#decode(Object) decode} method will be given a source {@code InputStream} to\n     * wrap, and the resulting (wrapping) {@code InputStream} will be used for reading , ensuring automatic\n     * Base64URL-decoding during read operations.\n     *\n     * <p>JJWT uses a spec-compliant decoder that works on all supported JDK versions, but you may call this method\n     * to specify a different stream decoder if desired.</p>\n     *\n     * @param base64UrlDecoder the stream decoder to use when Base64Url-decoding\n     * @return the parser builder for method chaining.\n     */\n    JwtParserBuilder b64Url(Decoder<InputStream, InputStream> base64UrlDecoder);\n\n    /**\n     * Uses the specified deserializer to convert JSON Strings (UTF-8 byte arrays) into Java Map objects.  This is\n     * used by the parser after Base64Url-decoding to convert JWT/JWS/JWT JSON headers and claims into Java Map\n     * objects.\n     *\n     * <p>If this method is not called, JJWT will use whatever deserializer it can find at runtime, checking for the\n     * presence of well-known implementations such Jackson, Gson, and org.json.  If one of these is not found\n     * in the runtime classpath, an exception will be thrown when one of the various {@code parse}* methods is\n     * invoked.</p>\n     *\n     * @param deserializer the deserializer to use when converting JSON Strings (UTF-8 byte arrays) into Map objects.\n     * @return the builder for method chaining.\n     * @deprecated since 0.12.0 in favor of {@link #json(Deserializer)}.\n     * This method will be removed before the JJWT 1.0 release.\n     */\n    @Deprecated\n    JwtParserBuilder deserializeJsonWith(Deserializer<Map<String, ?>> deserializer);\n\n    /**\n     * Uses the specified JSON {@link Deserializer} to deserialize JSON (UTF-8 byte streams) into Java Map objects.\n     * This is used by the parser after Base64Url-decoding to convert JWT/JWS/JWT headers and Claims into Java Map\n     * instances.\n     *\n     * <p>If this method is not called, JJWT will use whatever Deserializer it can find at runtime, checking for the\n     * presence of well-known implementations such Jackson, Gson, and org.json.  If one of these is not found\n     * in the runtime classpath, an exception will be thrown when one of the various {@code parse}* methods is\n     * invoked.</p>\n     *\n     * @param deserializer the deserializer to use to deserialize JSON (UTF-8 byte streams) into Map instances.\n     * @return the builder for method chaining.\n     * @since 0.12.0\n     */\n    JwtParserBuilder json(Deserializer<Map<String, ?>> deserializer);\n\n    /**\n     * Returns an immutable/thread-safe {@link JwtParser} created from the configuration from this JwtParserBuilder.\n     *\n     * @return an immutable/thread-safe JwtParser created from the configuration from this JwtParserBuilder.\n     */\n    JwtParser build();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/JwtVisitor.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * A JwtVisitor supports the <a href=\"https://en.wikipedia.org/wiki/Visitor_pattern\">Visitor design pattern</a> for\n * {@link Jwt} instances.  Visitor implementations define logic for a specific JWT subtype or payload subtype\n * avoiding type-checking if-then-else conditionals in favor of type-safe method dispatch when encountering a JWT.\n *\n * @param <T> the type of object to return after invoking the {@link Jwt#accept(JwtVisitor)} method.\n * @since 0.12.0\n */\npublic interface JwtVisitor<T> {\n\n    /**\n     * Handles an encountered Unsecured JWT that has not been cryptographically secured at all. Implementations can\n     * check the {@link Jwt#getPayload()} to determine if it is a {@link Claims} instance or a {@code byte[]} array.\n     *\n     * <p>If the payload is a {@code byte[]} array, and the JWT creator has set the (optional)\n     * {@link Header#getContentType()} value, the application may inspect that value to determine how to convert\n     * the byte array to the final type as desired.</p>\n     *\n     * @param jwt the parsed Unsecured JWT.\n     * @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.\n     */\n    T visit(Jwt<?, ?> jwt);\n\n    /**\n     * Handles an encountered JSON Web Signature (aka 'JWS') message that has been cryptographically\n     * verified/authenticated. Implementations can check the {@link Jwt#getPayload()} determine if it is a\n     * {@link Claims} instance or a {@code byte[]} array.\n     *\n     * <p>If the payload is a {@code byte[]} array, and the JWS creator has set the (optional)\n     * {@link Header#getContentType()} value, the application may inspect that value to determine how to convert\n     * the byte array to the final type as desired.</p>\n     *\n     * @param jws the parsed verified/authenticated JWS.\n     * @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.\n     */\n    T visit(Jws<?> jws);\n\n    /**\n     * Handles an encountered JSON Web Encryption (aka 'JWE') message that has been authenticated and decrypted.\n     * Implementations can check the (decrypted) {@link Jwt#getPayload()} to determine if it is a {@link Claims}\n     * instance or a {@code byte[]} array.\n     *\n     * <p>If the payload is a {@code byte[]} array, and the JWE creator has set the (optional)\n     * {@link Header#getContentType()} value, the application may inspect that value to determine how to convert\n     * the byte array to the final type as desired.</p>\n     *\n     * @param jwe the parsed authenticated and decrypted JWE.\n     * @return any object to be used after inspecting the JWE, or {@code null} if no return value is necessary.\n     */\n    T visit(Jwe<?> jwe);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Jwts.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.lang.Builder;\nimport io.jsonwebtoken.lang.Classes;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Supplier;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.KeyPairBuilderSupplier;\nimport io.jsonwebtoken.security.MacAlgorithm;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.SecretKeyAlgorithm;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\nimport io.jsonwebtoken.security.SignatureAlgorithm;\nimport io.jsonwebtoken.security.X509Builder;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.util.Map;\n\n/**\n * Factory class useful for creating instances of JWT interfaces.  Using this factory class can be a good\n * alternative to tightly coupling your code to implementation classes.\n *\n * <p><b>Standard Algorithm References</b></p>\n * <p>Standard JSON Web Token algorithms used during JWS or JWE building or parsing are available organized by\n * algorithm type. Each organized collection of algorithms is available via a constant to allow\n * for easy code-completion in IDEs, showing available algorithm instances.  For example, when typing:</p>\n * <blockquote><pre>\n * Jwts.// press code-completion hotkeys to suggest available algorithm registry fields\n * Jwts.{@link SIG SIG}.// press hotkeys to suggest individual Digital Signature or MAC algorithms or utility methods\n * Jwts.{@link ENC ENC}.// press hotkeys to suggest individual encryption algorithms or utility methods\n * Jwts.{@link KEY KEY}.// press hotkeys to suggest individual key algorithms or utility methods</pre></blockquote>\n *\n * @since 0.1\n */\npublic final class Jwts {\n\n\n    // do not change this visibility.  Raw type method signature not be publicly exposed:\n    @SuppressWarnings(\"unchecked\")\n    private static <T> T get(Registry<String, ?> registry, String id) {\n        return (T) registry.forKey(id);\n    }\n\n    /**\n     * Constants for all standard JWA\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-5\">Cryptographic Algorithms for Content\n     * Encryption</a> defined in the <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1\">JSON\n     * Web Signature and Encryption Algorithms Registry</a>. Each standard algorithm is available as a\n     * ({@code public static final}) constant for direct type-safe reference in application code. For example:\n     * <blockquote><pre>\n     * Jwts.builder()\n     *    // ... etc ...\n     *    .encryptWith(aKey, <b>Jwts.ENC.A256GCM</b>) // or A128GCM, A192GCM, etc...\n     *    .build();</pre></blockquote>\n     * <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>\n     *\n     * @see #get()\n     * @since 0.12.0\n     */\n    public static final class ENC {\n\n        private static final String IMPL_CLASSNAME = \"io.jsonwebtoken.impl.security.StandardEncryptionAlgorithms\";\n        private static final Registry<String, AeadAlgorithm> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);\n\n        /**\n         * Returns all standard JWA <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-5\">Cryptographic\n         * Algorithms for Content Encryption</a> defined in the\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1\">JSON Web Signature and Encryption\n         * Algorithms Registry</a>.\n         *\n         * @return all standard JWA content encryption algorithms.\n         */\n        public static Registry<String, AeadAlgorithm> get() {\n            return REGISTRY;\n        }\n\n        // prevent instantiation\n        private ENC() {\n        }\n\n        /**\n         * {@code AES_128_CBC_HMAC_SHA_256} authenticated encryption algorithm as defined by\n         * <a href=\"https://tools.ietf.org/html/rfc7518#section-5.2.3\">RFC 7518, Section 5.2.3</a>.  This algorithm\n         * requires a 256-bit (32 byte) key.\n         */\n        public static final AeadAlgorithm A128CBC_HS256 = get().forKey(\"A128CBC-HS256\");\n\n        /**\n         * {@code AES_192_CBC_HMAC_SHA_384} authenticated encryption algorithm, as defined by\n         * <a href=\"https://tools.ietf.org/html/rfc7518#section-5.2.4\">RFC 7518, Section 5.2.4</a>. This algorithm\n         * requires a 384-bit (48 byte) key.\n         */\n        public static final AeadAlgorithm A192CBC_HS384 = get().forKey(\"A192CBC-HS384\");\n\n        /**\n         * {@code AES_256_CBC_HMAC_SHA_512} authenticated encryption algorithm, as defined by\n         * <a href=\"https://tools.ietf.org/html/rfc7518#section-5.2.5\">RFC 7518, Section 5.2.5</a>.  This algorithm\n         * requires a 512-bit (64 byte) key.\n         */\n        public static final AeadAlgorithm A256CBC_HS512 = get().forKey(\"A256CBC-HS512\");\n\n        /**\n         * &quot;AES GCM using 128-bit key&quot; as defined by\n         * <a href=\"https://tools.ietf.org/html/rfc7518#section-5.3\">RFC 7518, Section 5.3</a><b><sup>1</sup></b>.  This\n         * algorithm requires a 128-bit (16 byte) key.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         */\n        public static final AeadAlgorithm A128GCM = get().forKey(\"A128GCM\");\n\n        /**\n         * &quot;AES GCM using 192-bit key&quot; as defined by\n         * <a href=\"https://tools.ietf.org/html/rfc7518#section-5.3\">RFC 7518, Section 5.3</a><b><sup>1</sup></b>.  This\n         * algorithm requires a 192-bit (24 byte) key.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         */\n        public static final AeadAlgorithm A192GCM = get().forKey(\"A192GCM\");\n\n        /**\n         * &quot;AES GCM using 256-bit key&quot; as defined by\n         * <a href=\"https://tools.ietf.org/html/rfc7518#section-5.3\">RFC 7518, Section 5.3</a><b><sup>1</sup></b>.  This\n         * algorithm requires a 256-bit (32 byte) key.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 8 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 7 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         */\n        public static final AeadAlgorithm A256GCM = get().forKey(\"A256GCM\");\n    }\n\n    /**\n     * Constants for all JWA (RFC 7518) standard <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3\">\n     * Cryptographic Algorithms for Digital Signatures and MACs</a> defined in the\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1\">JSON Web Signature and Encryption Algorithms\n     * Registry</a>. Each standard algorithm is available as a ({@code public static final}) constant for\n     * direct type-safe reference in application code. For example:\n     * <blockquote><pre>\n     * Jwts.builder()\n     *    // ... etc ...\n     *    .signWith(aKey, <b>Jwts.SIG.HS512</b>) // or RS512, PS256, EdDSA, etc...\n     *    .build();</pre></blockquote>\n     * <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>\n     *\n     * @see #get()\n     * @since 0.12.0\n     */\n    public static final class SIG {\n\n        private static final String IMPL_CLASSNAME = \"io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithms\";\n        private static final Registry<String, SecureDigestAlgorithm<?, ?>> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);\n\n        //prevent instantiation\n        private SIG() {\n        }\n\n        /**\n         * Returns all standard JWA <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3\">Cryptographic\n         * Algorithms for Digital Signatures and MACs</a> defined in the\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1\">JSON Web Signature and Encryption\n         * Algorithms Registry</a>.\n         *\n         * @return all standard JWA digital signature and MAC algorithms.\n         */\n        public static Registry<String, SecureDigestAlgorithm<?, ?>> get() {\n            return REGISTRY;\n        }\n\n        /**\n         * The &quot;none&quot; signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6\">RFC 7518, Section 3.6</a>.  This algorithm\n         * is used only when creating unsecured (not integrity protected) JWSs and is not usable in any other scenario.\n         * Any attempt to call its methods will result in an exception being thrown.\n         */\n        public static final SecureDigestAlgorithm<Key, Key> NONE = Jwts.get(REGISTRY, \"none\");\n\n        /**\n         * {@code HMAC using SHA-256} message authentication algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2\">RFC 7518, Section 3.2</a>.  This algorithm\n         * requires a 256-bit (32 byte) key.\n         */\n        public static final MacAlgorithm HS256 = Jwts.get(REGISTRY, \"HS256\");\n\n        /**\n         * {@code HMAC using SHA-384} message authentication algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2\">RFC 7518, Section 3.2</a>.  This algorithm\n         * requires a 384-bit (48 byte) key.\n         */\n        public static final MacAlgorithm HS384 = Jwts.get(REGISTRY, \"HS384\");\n\n        /**\n         * {@code HMAC using SHA-512} message authentication algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2\">RFC 7518, Section 3.2</a>.  This algorithm\n         * requires a 512-bit (64 byte) key.\n         */\n        public static final MacAlgorithm HS512 = Jwts.get(REGISTRY, \"HS512\");\n\n        /**\n         * {@code RSASSA-PKCS1-v1_5 using SHA-256} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3\">RFC 7518, Section 3.3</a>.  This algorithm\n         * requires a 2048-bit key.\n         */\n        public static final SignatureAlgorithm RS256 = Jwts.get(REGISTRY, \"RS256\");\n\n        /**\n         * {@code RSASSA-PKCS1-v1_5 using SHA-384} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3\">RFC 7518, Section 3.3</a>.  This algorithm\n         * requires a 2048-bit key, but the JJWT team recommends a 3072-bit key.\n         */\n        public static final SignatureAlgorithm RS384 = Jwts.get(REGISTRY, \"RS384\");\n\n        /**\n         * {@code RSASSA-PKCS1-v1_5 using SHA-512} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.3\">RFC 7518, Section 3.3</a>.  This algorithm\n         * requires a 2048-bit key, but the JJWT team recommends a 4096-bit key.\n         */\n        public static final SignatureAlgorithm RS512 = Jwts.get(REGISTRY, \"RS512\");\n\n        /**\n         * {@code RSASSA-PSS using SHA-256 and MGF1 with SHA-256} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.5\">RFC 7518, Section 3.5</a><b><sup>1</sup></b>.\n         * This algorithm requires a 2048-bit key.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         */\n        public static final SignatureAlgorithm PS256 = Jwts.get(REGISTRY, \"PS256\");\n\n        /**\n         * {@code RSASSA-PSS using SHA-384 and MGF1 with SHA-384} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.5\">RFC 7518, Section 3.5</a><b><sup>1</sup></b>.\n         * This algorithm requires a 2048-bit key, but the JJWT team recommends a 3072-bit key.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         */\n        public static final SignatureAlgorithm PS384 = Jwts.get(REGISTRY, \"PS384\");\n\n        /**\n         * {@code RSASSA-PSS using SHA-512 and MGF1 with SHA-512} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.5\">RFC 7518, Section 3.5</a><b><sup>1</sup></b>.\n         * This algorithm requires a 2048-bit key, but the JJWT team recommends a 4096-bit key.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         */\n        public static final SignatureAlgorithm PS512 = Jwts.get(REGISTRY, \"PS512\");\n\n        /**\n         * {@code ECDSA using P-256 and SHA-256} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4\">RFC 7518, Section 3.4</a>.  This algorithm\n         * requires a 256-bit key.\n         */\n        public static final SignatureAlgorithm ES256 = Jwts.get(REGISTRY, \"ES256\");\n\n        /**\n         * {@code ECDSA using P-384 and SHA-384} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4\">RFC 7518, Section 3.4</a>.  This algorithm\n         * requires a 384-bit key.\n         */\n        public static final SignatureAlgorithm ES384 = Jwts.get(REGISTRY, \"ES384\");\n\n        /**\n         * {@code ECDSA using P-521 and SHA-512} signature algorithm as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4\">RFC 7518, Section 3.4</a>.  This algorithm\n         * requires a 521-bit key.\n         */\n        public static final SignatureAlgorithm ES512 = Jwts.get(REGISTRY, \"ES512\");\n\n        /**\n         * {@code EdDSA} signature algorithm defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-3.1\">RFC 8037, Section 3.1</a> that requires\n         * either {@code Ed25519} or {@code Ed448} Edwards Elliptic Curve<sup><b>1</b></sup> keys.\n         *\n         * <p><b>KeyPair Generation</b></p>\n         *\n         * <p>This instance's {@link KeyPairBuilderSupplier#keyPair() keyPair()} builder creates {@code Ed448} keys,\n         * and is essentially an alias for\n         * <code>{@link io.jsonwebtoken.security.Jwks.CRV Jwks.CRV}.{@link io.jsonwebtoken.security.Jwks.CRV#Ed448 Ed448}.{@link KeyPairBuilderSupplier#keyPair() keyPair()}</code>.</p>\n         *\n         * <p>If you would like to generate an {@code Ed25519} {@code KeyPair} for use with the {@code EdDSA} algorithm,\n         * you may use the\n         * <code>{@link io.jsonwebtoken.security.Jwks.CRV Jwks.CRV}.{@link io.jsonwebtoken.security.Jwks.CRV#Ed25519 Ed25519}.{@link KeyPairBuilderSupplier#keyPair() keyPair()}</code>\n         * builder instead.</p>\n         *\n         * <p><b><sup>1</sup>This algorithm requires at least JDK 15 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath.</b></p>\n         */\n        public static final SignatureAlgorithm EdDSA = Jwts.get(REGISTRY, \"EdDSA\");\n    }\n\n    /**\n     * Constants for all standard JWA (RFC 7518) <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4\">\n     * Cryptographic Algorithms for Key Management</a>. Each standard algorithm is available as a\n     * ({@code public static final}) constant for direct type-safe reference in application code. For example:\n     * <blockquote><pre>\n     * Jwts.builder()\n     *    // ... etc ...\n     *    .encryptWith(aKey, <b>Jwts.KEY.ECDH_ES_A256KW</b>, Jwts.ENC.A256GCM)\n     *    .build();</pre></blockquote>\n     * <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>\n     *\n     * @see #get()\n     * @since 0.12.0\n     */\n    public static final class KEY {\n\n        private static final String IMPL_CLASSNAME = \"io.jsonwebtoken.impl.security.StandardKeyAlgorithms\";\n        private static final Registry<String, KeyAlgorithm<?, ?>> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);\n\n        /**\n         * Returns all standard JWA standard <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4\">Cryptographic\n         * Algorithms for Key Management</a>..\n         *\n         * @return all standard JWA Key Management algorithms.\n         */\n        public static Registry<String, KeyAlgorithm<?, ?>> get() {\n            return REGISTRY;\n        }\n\n        /**\n         * Key algorithm reflecting direct use of a shared symmetric key as the JWE AEAD encryption key, as defined\n         * by <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.5\">RFC 7518 (JWA), Section 4.5</a>.  This\n         * algorithm does not produce encrypted key ciphertext.\n         */\n        public static final KeyAlgorithm<SecretKey, SecretKey> DIRECT = Jwts.get(REGISTRY, \"dir\");\n\n        /**\n         * AES Key Wrap algorithm with default initial value using a 128-bit key, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.4\">RFC 7518 (JWA), Section 4.4</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with a 128-bit shared symmetric key using the\n         *     AES Key Wrap algorithm, producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with the 128-bit shared symmetric key,\n         *     using the AES Key Unwrap algorithm, producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final SecretKeyAlgorithm A128KW = Jwts.get(REGISTRY, \"A128KW\");\n\n        /**\n         * AES Key Wrap algorithm with default initial value using a 192-bit key, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.4\">RFC 7518 (JWA), Section 4.4</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with a 192-bit shared symmetric key using the\n         *     AES Key Wrap algorithm, producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with the 192-bit shared symmetric key,\n         *     using the AES Key Unwrap algorithm, producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final SecretKeyAlgorithm A192KW = Jwts.get(REGISTRY, \"A192KW\");\n\n        /**\n         * AES Key Wrap algorithm with default initial value using a 256-bit key, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.4\">RFC 7518 (JWA), Section 4.4</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with a 256-bit shared symmetric key using the\n         *     AES Key Wrap algorithm, producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with the 256-bit shared symmetric key,\n         *     using the AES Key Unwrap algorithm, producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final SecretKeyAlgorithm A256KW = Jwts.get(REGISTRY, \"A256KW\");\n\n        /**\n         * Key wrap algorithm with AES GCM using a 128-bit key, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7\">RFC 7518 (JWA), Section 4.7</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Generates a new secure-random 96-bit Initialization Vector to use during key wrap/encryption.</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with a 128-bit shared symmetric key using the\n         *     AES GCM Key Wrap algorithm with the generated Initialization Vector, producing encrypted key ciphertext\n         *     and GCM authentication tag.</li>\n         *     <li>Sets the generated initialization vector as the required\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1\">&quot;iv&quot;\n         *     (Initialization Vector) Header Parameter</a></li>\n         *     <li>Sets the resulting GCM authentication tag as the required\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2\">&quot;tag&quot;\n         *     (Authentication Tag) Header Parameter</a></li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Obtains the required initialization vector from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1\">&quot;iv&quot;\n         *     (Initialization Vector) Header Parameter</a></li>\n         *     <li>Obtains the required GCM authentication tag from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2\">&quot;tag&quot;\n         *     (Authentication Tag) Header Parameter</a></li>\n         *     <li>Decrypts the encrypted key ciphertext with the 128-bit shared symmetric key, the initialization vector\n         *     and GCM authentication tag using the AES GCM Key Unwrap algorithm, producing the decryption key\n         *     plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final SecretKeyAlgorithm A128GCMKW = Jwts.get(REGISTRY, \"A128GCMKW\");\n\n        /**\n         * Key wrap algorithm with AES GCM using a 192-bit key, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7\">RFC 7518 (JWA), Section 4.7</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Generates a new secure-random 96-bit Initialization Vector to use during key wrap/encryption.</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with a 192-bit shared symmetric key using the\n         *     AES GCM Key Wrap algorithm with the generated Initialization Vector, producing encrypted key ciphertext\n         *     and GCM authentication tag.</li>\n         *     <li>Sets the generated initialization vector as the required\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1\">&quot;iv&quot;\n         *     (Initialization Vector) Header Parameter</a></li>\n         *     <li>Sets the resulting GCM authentication tag as the required\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2\">&quot;tag&quot;\n         *     (Authentication Tag) Header Parameter</a></li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Obtains the required initialization vector from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1\">&quot;iv&quot;\n         *     (Initialization Vector) Header Parameter</a></li>\n         *     <li>Obtains the required GCM authentication tag from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2\">&quot;tag&quot;\n         *     (Authentication Tag) Header Parameter</a></li>\n         *     <li>Decrypts the encrypted key ciphertext with the 192-bit shared symmetric key, the initialization vector\n         *     and GCM authentication tag using the AES GCM Key Unwrap algorithm, producing the decryption key \\\n         *     plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final SecretKeyAlgorithm A192GCMKW = Jwts.get(REGISTRY, \"A192GCMKW\");\n\n        /**\n         * Key wrap algorithm with AES GCM using a 256-bit key, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7\">RFC 7518 (JWA), Section 4.7</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Generates a new secure-random 96-bit Initialization Vector to use during key wrap/encryption.</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with a 256-bit shared symmetric key using the\n         *     AES GCM Key Wrap algorithm with the generated Initialization Vector, producing encrypted key ciphertext\n         *     and GCM authentication tag.</li>\n         *     <li>Sets the generated initialization vector as the required\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1\">&quot;iv&quot;\n         *     (Initialization Vector) Header Parameter</a></li>\n         *     <li>Sets the resulting GCM authentication tag as the required\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2\">&quot;tag&quot;\n         *     (Authentication Tag) Header Parameter</a></li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Obtains the required initialization vector from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1\">&quot;iv&quot;\n         *     (Initialization Vector) Header Parameter</a></li>\n         *     <li>Obtains the required GCM authentication tag from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2\">&quot;tag&quot;\n         *     (Authentication Tag) Header Parameter</a></li>\n         *     <li>Decrypts the encrypted key ciphertext with the 256-bit shared symmetric key, the initialization vector\n         *     and GCM authentication tag using the AES GCM Key Unwrap algorithm, producing the decryption key \\\n         *     plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final SecretKeyAlgorithm A256GCMKW = Jwts.get(REGISTRY, \"A256GCMKW\");\n\n        /**\n         * Key encryption algorithm using <code>PBES2 with HMAC SHA-256 and &quot;A128KW&quot; wrapping</code>\n         * as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8\">RFC 7518 (JWA), Section 4.8</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Determines the number of PBDKF2 iterations via the JWE header's\n         *     {@link JweHeader#getPbes2Count() pbes2Count} value.  If that value is not set, a suitable number of\n         *     iterations will be chosen based on\n         *     <a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\">OWASP\n         *     PBKDF2 recommendations</a> and then that value is set as the JWE header {@code pbes2Count} value.</li>\n         *     <li>Generates a new secure-random salt input and sets it as the JWE header\n         *     {@link JweHeader#getPbes2Salt() pbes2Salt} value.</li>\n         *     <li>Derives a 128-bit Key Encryption Key with the PBES2-HS256 password-based key derivation algorithm,\n         *     using the provided password, iteration count, and input salt as arguments.</li>\n         *     <li>Generates a new secure-random Content Encryption {@link SecretKey} suitable for use with a\n         *      specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated Content Encryption {@code SecretKey} with the {@code A128KW} key wrap\n         *      algorithm using the 128-bit derived password-based Key Encryption Key from step {@code #3},\n         *      producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     Content Encryption {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated\n         *     {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the required PBKDF2 input salt from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1\">&quot;p2s&quot;\n         *     (PBES2 Salt Input) Header Parameter</a></li>\n         *     <li>Obtains the required PBKDF2 iteration count from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2\">&quot;p2c&quot;\n         *     (PBES2 Count) Header Parameter</a></li>\n         *     <li>Derives the 128-bit Key Encryption Key with the PBES2-HS256 password-based key derivation algorithm,\n         *     using the provided password, obtained salt input, and obtained iteration count as arguments.</li>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with with the {@code A128KW} key unwrap\n         *      algorithm using the 128-bit derived password-based Key Encryption Key from step {@code #3},\n         *      producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<Password, Password> PBES2_HS256_A128KW = Jwts.get(REGISTRY, \"PBES2-HS256+A128KW\");\n\n        /**\n         * Key encryption algorithm using <code>PBES2 with HMAC SHA-384 and &quot;A192KW&quot; wrapping</code>\n         * as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8\">RFC 7518 (JWA), Section 4.8</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Determines the number of PBDKF2 iterations via the JWE header's\n         *     {@link JweHeader#getPbes2Count() pbes2Count} value.  If that value is not set, a suitable number of\n         *     iterations will be chosen based on\n         *     <a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\">OWASP\n         *     PBKDF2 recommendations</a> and then that value is set as the JWE header {@code pbes2Count} value.</li>\n         *     <li>Generates a new secure-random salt input and sets it as the JWE header\n         *     {@link JweHeader#getPbes2Salt() pbes2Salt} value.</li>\n         *     <li>Derives a 192-bit Key Encryption Key with the PBES2-HS384 password-based key derivation algorithm,\n         *     using the provided password, iteration count, and input salt as arguments.</li>\n         *     <li>Generates a new secure-random Content Encryption {@link SecretKey} suitable for use with a\n         *      specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated Content Encryption {@code SecretKey} with the {@code A192KW} key wrap\n         *      algorithm using the 192-bit derived password-based Key Encryption Key from step {@code #3},\n         *      producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     Content Encryption {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated\n         *     {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the required PBKDF2 input salt from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1\">&quot;p2s&quot;\n         *     (PBES2 Salt Input) Header Parameter</a></li>\n         *     <li>Obtains the required PBKDF2 iteration count from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2\">&quot;p2c&quot;\n         *     (PBES2 Count) Header Parameter</a></li>\n         *     <li>Derives the 192-bit Key Encryption Key with the PBES2-HS384 password-based key derivation algorithm,\n         *     using the provided password, obtained salt input, and obtained iteration count as arguments.</li>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with with the {@code A192KW} key unwrap\n         *      algorithm using the 192-bit derived password-based Key Encryption Key from step {@code #3},\n         *      producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<Password, Password> PBES2_HS384_A192KW = Jwts.get(REGISTRY, \"PBES2-HS384+A192KW\");\n\n        /**\n         * Key encryption algorithm using <code>PBES2 with HMAC SHA-512 and &quot;A256KW&quot; wrapping</code>\n         * as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8\">RFC 7518 (JWA), Section 4.8</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Determines the number of PBDKF2 iterations via the JWE header's\n         *     {@link JweHeader#getPbes2Count() pbes2Count} value.  If that value is not set, a suitable number of\n         *     iterations will be chosen based on\n         *     <a href=\"https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\">OWASP\n         *     PBKDF2 recommendations</a> and then that value is set as the JWE header {@code pbes2Count} value.</li>\n         *     <li>Generates a new secure-random salt input and sets it as the JWE header\n         *     {@link JweHeader#getPbes2Salt() pbes2Salt} value.</li>\n         *     <li>Derives a 256-bit Key Encryption Key with the PBES2-HS512 password-based key derivation algorithm,\n         *     using the provided password, iteration count, and input salt as arguments.</li>\n         *     <li>Generates a new secure-random Content Encryption {@link SecretKey} suitable for use with a\n         *      specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated Content Encryption {@code SecretKey} with the {@code A256KW} key wrap\n         *      algorithm using the 256-bit derived password-based Key Encryption Key from step {@code #3},\n         *      producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     Content Encryption {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated\n         *     {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the required PBKDF2 input salt from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1\">&quot;p2s&quot;\n         *     (PBES2 Salt Input) Header Parameter</a></li>\n         *     <li>Obtains the required PBKDF2 iteration count from the\n         *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2\">&quot;p2c&quot;\n         *     (PBES2 Count) Header Parameter</a></li>\n         *     <li>Derives the 256-bit Key Encryption Key with the PBES2-HS512 password-based key derivation algorithm,\n         *     using the provided password, obtained salt input, and obtained iteration count as arguments.</li>\n         *     <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with with the {@code A256KW} key unwrap\n         *      algorithm using the 256-bit derived password-based Key Encryption Key from step {@code #3},\n         *      producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<Password, Password> PBES2_HS512_A256KW = Jwts.get(REGISTRY, \"PBES2-HS512+A256KW\");\n\n        /**\n         * Key Encryption with {@code RSAES-PKCS1-v1_5}, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.2\">RFC 7518 (JWA), Section 4.2</a>.\n         * This algorithm requires a key size of 2048 bits or larger.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with the RSA key wrap algorithm, using the JWE\n         *     recipient's RSA Public Key, producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Receives the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with the RSA key unwrap algorithm, using the JWE recipient's\n         *     RSA Private Key, producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}. </li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<PublicKey, PrivateKey> RSA1_5 = Jwts.get(REGISTRY, \"RSA1_5\");\n\n        /**\n         * Key Encryption with {@code RSAES OAEP using default parameters}, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.3\">RFC 7518 (JWA), Section 4.3</a>.\n         * This algorithm requires a key size of 2048 bits or larger.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with the RSA OAEP with SHA-1 and MGF1 key wrap algorithm,\n         *     using the JWE recipient's RSA Public Key, producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Receives the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with the RSA OAEP with SHA-1 and MGF1 key unwrap algorithm,\n         *     using the JWE recipient's RSA Private Key, producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}. </li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<PublicKey, PrivateKey> RSA_OAEP = Jwts.get(REGISTRY, \"RSA-OAEP\");\n\n        /**\n         * Key Encryption with {@code RSAES OAEP using SHA-256 and MGF1 with SHA-256}, as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.3\">RFC 7518 (JWA), Section 4.3</a>.\n         * This algorithm requires a key size of 2048 bits or larger.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *     specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with the RSA OAEP with SHA-256 and MGF1 key wrap\n         *     algorithm, using the JWE recipient's RSA Public Key, producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Receives the encrypted key ciphertext embedded in the received JWE.</li>\n         *     <li>Decrypts the encrypted key ciphertext with the RSA OAEP with SHA-256 and MGF1 key unwrap algorithm,\n         *     using the JWE recipient's RSA Private Key, producing the decryption key plaintext.</li>\n         *     <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *     JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}. </li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<PublicKey, PrivateKey> RSA_OAEP_256 = Jwts.get(REGISTRY, \"RSA-OAEP-256\");\n\n        /**\n         * Key Agreement with {@code ECDH-ES using Concat KDF} as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6\">RFC 7518 (JWA), Section 4.6</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the\n         *     JWE recipient's EC Public Key.</li>\n         *     <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key\n         *     and the JWE recipient's EC Public Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> a symmetric Content\n         *     Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *     generated shared secret and any available\n         *     {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *     {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *     <li>Sets the generated EC key pair's Public Key as the required\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>\n         *     <li>Returns the derived symmetric {@code SecretKey} for JJWT to use to encrypt the entire JWE with the\n         *     associated {@link AeadAlgorithm}. Encrypted key ciphertext is not produced with this algorithm, so\n         *     the resulting JWE will not contain any embedded key ciphertext.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the required ephemeral Elliptic Curve Public Key from the\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a>.</li>\n         *     <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>\n         *     <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key\n         *      and the JWE recipient's EC Private Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> the symmetric Content\n         *      Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *      obtained shared secret and any available\n         *      {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *      {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *      <li>Returns the derived symmetric {@code SecretKey} for JJWT to use to decrypt the entire\n         *      JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES = Jwts.get(REGISTRY, \"ECDH-ES\");\n\n        /**\n         * Key Agreement with Key Wrapping via\n         * <code>ECDH-ES using Concat KDF and CEK wrapped with &quot;A128KW&quot;</code> as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6\">RFC 7518 (JWA), Section 4.6</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the\n         *     JWE recipient's EC Public Key.</li>\n         *     <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key\n         *     and the JWE recipient's EC Public Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> a 128-bit symmetric Key\n         *     Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *     generated shared secret and any available\n         *     {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *     {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *     <li>Sets the generated EC key pair's Public Key as the required\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *      specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with the {@code A128KW} key wrap\n         *      algorithm using the derived symmetric Key Encryption Key from step {@code #3}, producing encrypted key ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the required ephemeral Elliptic Curve Public Key from the\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a>.</li>\n         *     <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>\n         *     <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key\n         *      and the JWE recipient's EC Private Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> the symmetric Key\n         *      Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *      obtained shared secret and any available\n         *      {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *      {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *      <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *      <li>Decrypts the encrypted key ciphertext with the AES Key Unwrap algorithm using the\n         *      128-bit derived symmetric key from step {@code #4}, producing the decryption key plaintext.</li>\n         *      <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *      JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES_A128KW = Jwts.get(REGISTRY, \"ECDH-ES+A128KW\");\n\n        /**\n         * Key Agreement with Key Wrapping via\n         * <code>ECDH-ES using Concat KDF and CEK wrapped with &quot;A192KW&quot;</code> as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6\">RFC 7518 (JWA), Section 4.6</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the\n         *     JWE recipient's EC Public Key.</li>\n         *     <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key\n         *     and the JWE recipient's EC Public Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> a 192-bit symmetric Key\n         *     Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *     generated shared secret and any available\n         *     {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *     {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *     <li>Sets the generated EC key pair's Public Key as the required\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *      specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with the {@code A192KW} key wrap\n         *      algorithm using the derived symmetric Key Encryption Key from step {@code #3}, producing encrypted key\n         *      ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the required ephemeral Elliptic Curve Public Key from the\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a>.</li>\n         *     <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>\n         *     <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key\n         *      and the JWE recipient's EC Private Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> the 192-bit symmetric\n         *      Key Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *      obtained shared secret and any available\n         *      {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *      {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *      <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *      <li>Decrypts the encrypted key ciphertext with the AES Key Unwrap algorithm using the\n         *      192-bit derived symmetric key from step {@code #4}, producing the decryption key plaintext.</li>\n         *      <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *      JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES_A192KW = Jwts.get(REGISTRY, \"ECDH-ES+A192KW\");\n\n        /**\n         * Key Agreement with Key Wrapping via\n         * <code>ECDH-ES using Concat KDF and CEK wrapped with &quot;A256KW&quot;</code> as defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6\">RFC 7518 (JWA), Section 4.6</a>.\n         *\n         * <p>During JWE creation, this algorithm:</p>\n         * <ol>\n         *     <li>Generates a new secure-random Elliptic Curve public/private key pair on the same curve as the\n         *     JWE recipient's EC Public Key.</li>\n         *     <li>Generates a shared secret with the ECDH key agreement algorithm using the generated EC Private Key\n         *     and the JWE recipient's EC Public Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> a 256-bit symmetric Key\n         *     Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *     generated shared secret and any available\n         *     {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *     {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *     <li>Sets the generated EC key pair's Public Key as the required\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a> to be transmitted in the JWE.</li>\n         *     <li>Generates a new secure-random content encryption {@link SecretKey} suitable for use with a\n         *      specified {@link AeadAlgorithm} (using {@link AeadAlgorithm#key()}).</li>\n         *     <li>Encrypts this newly-generated {@code SecretKey} with the {@code A256KW} key wrap\n         *      algorithm using the derived symmetric Key Encryption Key from step {@code #3}, producing encrypted key\n         *      ciphertext.</li>\n         *     <li>Returns the encrypted key ciphertext for inclusion in the final JWE as well as the newly-generated\n         *     {@code SecretKey} for JJWT to use to encrypt the entire JWE with associated {@link AeadAlgorithm}.</li>\n         * </ol>\n         * <p>For JWE decryption, this algorithm:</p>\n         * <ol>\n         *     <li>Obtains the required ephemeral Elliptic Curve Public Key from the\n         *      <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.1.1\">&quot;epk&quot;\n         *      (Ephemeral Public Key) Header Parameter</a>.</li>\n         *     <li>Validates that the ephemeral Public Key is on the same curve as the recipient's EC Private Key.</li>\n         *     <li>Obtains the shared secret with the ECDH key agreement algorithm using the obtained EC Public Key\n         *      and the JWE recipient's EC Private Key.</li>\n         *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\">Derives</a> the 256-bit symmetric\n         *      Key Encryption {@code SecretKey} with the Concat KDF algorithm using the\n         *      obtained shared secret and any available\n         *      {@link JweHeader#getAgreementPartyUInfo() PartyUInfo} and\n         *      {@link JweHeader#getAgreementPartyVInfo() PartyVInfo}.</li>\n         *      <li>Obtains the encrypted key ciphertext embedded in the received JWE.</li>\n         *      <li>Decrypts the encrypted key ciphertext with the AES Key Unwrap algorithm using the\n         *      256-bit derived symmetric key from step {@code #4}, producing the decryption key plaintext.</li>\n         *      <li>Returns the decryption key plaintext as a {@link SecretKey} for JJWT to use to decrypt the entire\n         *      JWE using the JWE's identified &quot;enc&quot; {@link AeadAlgorithm}.</li>\n         * </ol>\n         */\n        public static final KeyAlgorithm<PublicKey, PrivateKey> ECDH_ES_A256KW = Jwts.get(REGISTRY, \"ECDH-ES+A256KW\");\n\n        //prevent instantiation\n        private KEY() {\n        }\n    }\n\n    /**\n     * Constants for JWA (RFC 7518) compression algorithms referenced in the {@code zip} header defined in the\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.3\">JSON Web Encryption Compression Algorithms\n     * Registry</a>. Each algorithm is available as a ({@code public static final}) constant for\n     * direct type-safe reference in application code. For example:\n     * <blockquote><pre>\n     * Jwts.builder()\n     *    // ... etc ...\n     *    .compressWith(<b>Jwts.ZIP.DEF</b>)\n     *    .build();</pre></blockquote>\n     * <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>\n     *\n     * @see #get()\n     * @since 0.12.0\n     */\n    public static final class ZIP {\n\n        private static final String IMPL_CLASSNAME = \"io.jsonwebtoken.impl.io.StandardCompressionAlgorithms\";\n        private static final Registry<String, CompressionAlgorithm> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);\n\n        /**\n         * Returns various useful <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.3\">\n         * Compression Algorithms</a>.\n         *\n         * @return various standard and non-standard useful compression algorithms.\n         */\n        public static Registry<String, CompressionAlgorithm> get() {\n            return REGISTRY;\n        }\n\n        /**\n         * The JWE-standard <a href=\"https://www.rfc-editor.org/rfc/rfc1951\">DEFLATE</a>\n         * compression algorithm with a {@code zip} header value of {@code \"DEF\"}.\n         *\n         * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.3\">JWE RFC 7516, Section 4.1.3</a>\n         */\n        public static final CompressionAlgorithm DEF = get().forKey(\"DEF\");\n\n        /**\n         * A commonly used, but <b>NOT JWA-STANDARD</b>\n         * <a href=\"https://en.wikipedia.org/wiki/Gzip\">gzip</a> compression algorithm with a {@code zip} header value\n         * of {@code \"GZIP\"}.\n         *\n         * <p><b>Compatibility Warning</b></p>\n         *\n         * <p><b>This is not a standard JWE compression algorithm</b>.  Be sure to use this only when you are confident\n         * that all parties accessing the token support the &quot;GZIP&quot; identifier and associated algorithm.</p>\n         *\n         * <p>If you're concerned about compatibility, {@link #DEF DEF} is the only JWA standards-compliant algorithm.</p>\n         *\n         * @see #DEF\n         */\n        public static final CompressionAlgorithm GZIP = get().forKey(\"GZIP\");\n\n        //prevent instantiation\n        private ZIP() {\n        }\n    }\n\n    // @since 0.12.7\n    private static final Supplier<JwtBuilder> JWT_BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.DefaultJwtBuilder$Supplier\");\n\n    // @since 0.12.7\n    private static final Supplier<JwtParserBuilder> JWT_PARSER_BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.DefaultJwtParserBuilder$Supplier\");\n\n    // @since 0.12.7\n    private static final Supplier<HeaderBuilder> HEADER_BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.DefaultJwtHeaderBuilder$Supplier\");\n\n    // @since 0.12.7\n    private static final Supplier<ClaimsBuilder> CLAIMS_BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.DefaultClaimsBuilder$Supplier\");\n\n    /**\n     * A {@link Builder} that dynamically determines the type of {@link Header} to create based on builder state.\n     *\n     * @since 0.12.0\n     */\n    public interface HeaderBuilder extends JweHeaderMutator<HeaderBuilder>, X509Builder<HeaderBuilder>, Builder<Header> {\n    }\n\n    /**\n     * Returns a new {@link HeaderBuilder} that can build any type of {@link Header} instance depending on\n     * which builder properties are set.\n     *\n     * @return a new {@link HeaderBuilder} that can build any type of {@link Header} instance depending on\n     * which builder properties are set.\n     * @since 0.12.0\n     */\n    public static HeaderBuilder header() {\n        return HEADER_BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * Returns a new {@link Claims} builder instance to be used to populate JWT claims, which in aggregate will be\n     * the JWT payload.\n     *\n     * @return a new {@link Claims} builder instance to be used to populate JWT claims, which in aggregate will be\n     * the JWT payload.\n     */\n    public static ClaimsBuilder claims() {\n        return CLAIMS_BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * <p><b>Deprecated since 0.12.0 in favor of\n     * {@code Jwts.}{@link #claims()}{@code .add(map).build()}</b>.\n     * This method will be removed before 1.0.</p>\n     *\n     * <p>Returns a new {@link Claims} instance populated with the specified name/value pairs.</p>\n     *\n     * @param claims the name/value pairs to populate the new Claims instance.\n     * @return a new {@link Claims} instance populated with the specified name/value pairs.\n     * @deprecated since 0.12.0 in favor of {@code Jwts.}{@link #claims()}{@code .putAll(map).build()}.\n     * This method will be removed before 1.0.\n     */\n    @Deprecated\n    public static Claims claims(Map<String, Object> claims) {\n        return claims().add(claims).build();\n    }\n\n    /**\n     * Returns a new {@link JwtBuilder} instance that can be configured and then used to create JWT compact serialized\n     * strings.\n     *\n     * @return a new {@link JwtBuilder} instance that can be configured and then used to create JWT compact serialized\n     * strings.\n     */\n    public static JwtBuilder builder() {\n        return JWT_BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * Returns a new {@link JwtParserBuilder} instance that can be configured to create an immutable/thread-safe {@link JwtParser}.\n     *\n     * @return a new {@link JwtParser} instance that can be configured create an immutable/thread-safe {@link JwtParser}.\n     */\n    public static JwtParserBuilder parser() {\n        return JWT_PARSER_BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * Private constructor, prevent instantiation.\n     */\n    private Jwts() {\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/Locator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport java.security.Key;\n\n/**\n * A {@link Locator} can return an object referenced in a JWT {@link Header} that is necessary to process\n * the associated JWT.\n *\n * <p>For example, a {@code Locator} implementation can inspect a header's {@code kid} (Key ID) parameter, and use the\n * discovered {@code kid} value to lookup and return the associated {@link Key} instance.  JJWT could then use this\n * {@code key} to decrypt a JWE or verify a JWS signature.</p>\n *\n * @param <T> the type of object that may be returned from the {@link #locate(Header)} method\n * @since 0.12.0\n */\npublic interface Locator<T> {\n\n    /**\n     * Returns an object referenced in the specified {@code header}, or {@code null} if the object couldn't be found.\n     *\n     * @param header the JWT header to inspect; may be an instance of {@link Header}, {@link JwsHeader} or\n     *               {@link JweHeader} depending on if the respective JWT is an unprotected JWT, JWS or JWE.\n     * @return an object referenced in the specified {@code header}, or {@code null} if the object couldn't be found.\n     */\n    T locate(Header header);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/LocatorAdapter.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * Adapter pattern implementation for the {@link Locator} interface.  Subclasses can override any of the\n * {@link #doLocate(Header)}, {@link #locate(ProtectedHeader)}, {@link #locate(JwsHeader)}, or\n * {@link #locate(JweHeader)} methods for type-specific logic if desired when the encountered header is an\n * unprotected JWT, or an integrity-protected JWT (either a JWS or JWE).\n *\n * @param <T> the type of object to locate\n * @since 0.12.0\n */\npublic abstract class LocatorAdapter<T> implements Locator<T> {\n\n    /**\n     * Constructs a new instance, where all default method implementations return {@code null}.\n     */\n    public LocatorAdapter() {\n    }\n\n    /**\n     * Inspects the specified header, and delegates to the {@link #locate(ProtectedHeader)} method if the header\n     * is protected (either a {@link JwsHeader} or {@link JweHeader}), or the {@link #doLocate(Header)} method\n     * if the header is not integrity protected.\n     *\n     * @param header the JWT header to inspect; may be an instance of {@link Header}, {@link JwsHeader}, or\n     *               {@link JweHeader} depending on if the respective JWT is an unprotected JWT, JWS or JWE.\n     * @return an object referenced in the specified header, or {@code null} if the referenced object cannot be found\n     * or does not exist.\n     */\n    @Override\n    public final T locate(Header header) {\n        Assert.notNull(header, \"Header cannot be null.\");\n        if (header instanceof ProtectedHeader) {\n            ProtectedHeader protectedHeader = (ProtectedHeader) header;\n            return locate(protectedHeader);\n        }\n        return doLocate(header);\n    }\n\n    /**\n     * Returns an object referenced in the specified {@link ProtectedHeader}, or {@code null} if the referenced\n     * object cannot be found or does not exist.  This is a convenience method that delegates to\n     * {@link #locate(JwsHeader)} if the {@code header} is a {@link JwsHeader} or {@link #locate(JweHeader)} if the\n     * {@code header} is a {@link JweHeader}.\n     *\n     * @param header the protected header of an encountered JWS or JWE.\n     * @return an object referenced in the specified {@link ProtectedHeader}, or {@code null} if the referenced\n     * object cannot be found or does not exist.\n     */\n    protected T locate(ProtectedHeader header) {\n        if (header instanceof JwsHeader) {\n            return locate((JwsHeader) header);\n        } else {\n            Assert.isInstanceOf(JweHeader.class, header, \"Unrecognized ProtectedHeader type.\");\n            return locate((JweHeader) header);\n        }\n    }\n\n    /**\n     * Returns an object referenced in the specified JWE header, or {@code null} if the referenced\n     * object cannot be found or does not exist.  Default implementation simply returns {@code null}.\n     *\n     * @param header the header of an encountered JWE.\n     * @return an object referenced in the specified JWE header, or {@code null} if the referenced\n     * object cannot be found or does not exist.\n     */\n    protected T locate(JweHeader header) {\n        return null;\n    }\n\n    /**\n     * Returns an object referenced in the specified JWS header, or {@code null} if the referenced\n     * object cannot be found or does not exist. Default implementation simply returns {@code null}.\n     *\n     * @param header the header of an encountered JWS.\n     * @return an object referenced in the specified JWS header, or {@code null} if the referenced\n     * object cannot be found or does not exist.\n     */\n    protected T locate(JwsHeader header) {\n        return null;\n    }\n\n    /**\n     * Returns an object referenced in the specified unprotected JWT header, or {@code null} if the referenced\n     * object cannot be found or does not exist. Default implementation simply returns {@code null}.\n     *\n     * @param header the header of an encountered JWT.\n     * @return an object referenced in the specified unprotected JWT header, or {@code null} if the referenced\n     * object cannot be found or does not exist.\n     */\n    @SuppressWarnings(\"unused\")\n    protected T doLocate(Header header) {\n        return null;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/MalformedJwtException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception indicating that a JWT was not correctly constructed and should be rejected.\n *\n * @since 0.2\n */\npublic class MalformedJwtException extends JwtException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public MalformedJwtException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public MalformedJwtException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/MissingClaimException.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception thrown when discovering that a required claim is not present, indicating the JWT is\n * invalid and may not be used.\n *\n * @since 0.6\n */\npublic class MissingClaimException extends InvalidClaimException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param header     the header associated with the claims that did not contain the required claim\n     * @param claims     the claims that did not contain the required claim\n     * @param claimName  the name of the claim that could not be validated\n     * @param claimValue the value of the claim that could not be validated\n     * @param message    the message explaining why the exception is thrown.\n     */\n    public MissingClaimException(Header header, Claims claims, String claimName, Object claimValue, String message) {\n        super(header, claims, claimName, claimValue, message);\n    }\n\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param header     the header associated with the claims that did not contain the required claim\n     * @param claims     the claims that did not contain the required claim\n     * @param claimName  the name of the claim that could not be validated\n     * @param claimValue the value of the claim that could not be validated\n     * @param message    the message explaining why the exception is thrown.\n     * @param cause      the underlying cause that resulted in this exception being thrown.\n     * @deprecated since 0.12.0 since it is not used in JJWT's codebase\n     */\n    @Deprecated\n    public MissingClaimException(Header header, Claims claims, String claimName, Object claimValue, String message, Throwable cause) {\n        super(header, claims, claimName, claimValue, message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/PrematureJwtException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception indicating that a JWT was accepted before it is allowed to be accessed and must be rejected.\n *\n * @since 0.3\n */\npublic class PrematureJwtException extends ClaimJwtException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param header  jwt header\n     * @param claims  jwt claims (body)\n     * @param message the message explaining why the exception is thrown.\n     */\n    public PrematureJwtException(Header header, Claims claims, String message) {\n        super(header, claims, message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param header  jwt header\n     * @param claims  jwt claims (body)\n     * @param message exception message\n     * @param cause   cause\n     * @since 0.5\n     * @deprecated since 0.12.0 since it is not used in JJWT's codebase\n     */\n    @Deprecated\n    public PrematureJwtException(Header header, Claims claims, String message, Throwable cause) {\n        super(header, claims, message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/ProtectedHeader.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.security.PublicJwk;\nimport io.jsonwebtoken.security.X509Accessor;\n\nimport java.net.URI;\nimport java.util.Set;\n\n/**\n * A JWT header that is integrity protected, either by JWS digital signature or JWE AEAD encryption.\n *\n * @see JwsHeader\n * @see JweHeader\n * @since 0.12.0\n */\npublic interface ProtectedHeader extends Header, X509Accessor {\n\n    /**\n     * Returns the {@code jku} (JWK Set URL) value that refers to a\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-5\">JWK Set</a>\n     * resource containing JSON-encoded Public Keys, or {@code null} if not present.  When present in a\n     * {@link JwsHeader}, the first public key in the JWK Set <em>must</em> be the public key complement of the private\n     * key used to sign the JWS. When present in a {@link JweHeader}, the first public key in the JWK Set <em>must</em>\n     * be the public key used during encryption.\n     *\n     * @return a URI that refers to a <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-5\">JWK Set</a>\n     * resource for a set of JSON-encoded Public Keys, or {@code null} if not present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.2\">JWS JWK Set URL</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.4\">JWE JWK Set URL</a>\n     */\n    URI getJwkSetUrl();\n\n    /**\n     * Returns the {@code jwk} (JSON Web Key) associated with the JWT.  When present in a {@link JwsHeader}, the\n     * {@code jwk} is the public key complement of the private key used to digitally sign the JWS.  When present in a\n     * {@link JweHeader}, the {@code jwk} is the public key to which the JWE was encrypted, and may be used to\n     * determine the private key needed to decrypt the JWE.\n     *\n     * @return the {@code jwk} (JSON Web Key) associated with the header.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.3\">JWS {@code jwk} (JSON Web Key) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.5\">JWE {@code jwk} (JSON Web Key) Header Parameter</a>\n     */\n    PublicJwk<?> getJwk();\n\n    /**\n     * Returns the JWT case-sensitive {@code kid} (Key ID) header value or {@code null} if not present.\n     *\n     * <p>The keyId header parameter is a hint indicating which key was used to secure a JWS or JWE.  This\n     * parameter allows originators to explicitly signal a change of key to recipients.  The structure of the keyId\n     * value is unspecified. Its value is a CaSe-SeNsItIvE string.</p>\n     *\n     * <p>When used with a JWK, the keyId value is used to match a JWK {@code keyId} parameter value.</p>\n     *\n     * @return the case-sensitive {@code kid} header value or {@code null} if not present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.4\">JWS Key ID</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.6\">JWE Key ID</a>\n     */\n    String getKeyId();\n\n    /**\n     * Returns the header parameter names that use extensions to the JWT or JWA specification(s) that <em>MUST</em>\n     * be understood and supported by the JWT recipient, or {@code null} if not present.\n     *\n     * @return the header parameter names that use extensions to the JWT or JWA specification(s) that <em>MUST</em>\n     * be understood and supported by the JWT recipient, or {@code null} if not present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11\">JWS {@code crit} (Critical) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.13\">JWS {@code crit} (Critical) Header Parameter</a>\n     */\n    Set<String> getCritical();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/ProtectedHeaderMutator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.lang.Conjunctor;\nimport io.jsonwebtoken.lang.NestedCollection;\nimport io.jsonwebtoken.security.PublicJwk;\nimport io.jsonwebtoken.security.X509Mutator;\n\nimport java.net.URI;\n\n/**\n * Mutation (modifications) to a {@link ProtectedHeader Header} instance.\n *\n * @param <T> the mutator subtype, for method chaining\n * @since 0.12.0\n */\npublic interface ProtectedHeaderMutator<T extends ProtectedHeaderMutator<T>> extends HeaderMutator<T>, X509Mutator<T> {\n\n    /**\n     * Configures names of header parameters used by JWT or JWA specification extensions that <em>MUST</em> be\n     * understood and supported by the JWT recipient. When finished, use the collection's\n     * {@link Conjunctor#and() and()} method to continue header configuration, for example:\n     * <blockquote><pre>\n     * headerBuilder\n     *     .critical().add(\"headerName\").<b>{@link Conjunctor#and() and()} // return parent</b>\n     * // resume header configuration...</pre></blockquote>\n     *\n     * @return the {@link NestedCollection} to use for {@code crit} configuration.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11\">JWS <code>crit</code> (Critical) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.13\">JWS <code>crit</code> (Critical) Header Parameter</a>\n     */\n    NestedCollection<String, T> critical();\n\n    /**\n     * Sets the {@code jwk} (JSON Web Key) associated with the JWT.  When set for a {@link JwsHeader}, the\n     * {@code jwk} is the public key complement of the private key used to digitally sign the JWS.  When set for a\n     * {@link JweHeader}, the {@code jwk} is the public key to which the JWE was encrypted, and may be used to\n     * determine the private key needed to decrypt the JWE.\n     *\n     * @param jwk the {@code jwk} (JSON Web Key) associated with the header.\n     * @return the header for method chaining\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.3\">JWS <code>jwk</code> (JSON Web Key) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.5\">JWE <code>jwk</code> (JSON Web Key) Header Parameter</a>\n     */\n    T jwk(PublicJwk<?> jwk);\n\n    /**\n     * Sets the {@code jku} (JWK Set URL) value that refers to a\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-5\">JWK Set</a>\n     * resource containing JSON-encoded Public Keys, or {@code null} if not present.  When set for a\n     * {@link JwsHeader}, the first public key in the JWK Set <em>must</em> be the public key complement of the\n     * private key used to sign the JWS. When set for a {@link JweHeader}, the first public key in the JWK Set\n     * <em>must</em> be the public key used during encryption.\n     *\n     * @param uri a URI that refers to a <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-5\">JWK Set</a>\n     *            resource containing JSON-encoded Public Keys\n     * @return the header for method chaining\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.2\">JWS JWK Set URL</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.4\">JWE JWK Set URL</a>\n     */\n    T jwkSetUrl(URI uri);\n\n    /**\n     * Sets the JWT case-sensitive {@code kid} (Key ID) header value. A {@code null} value will remove the property\n     * from the JSON map.\n     *\n     * <p>The keyId header parameter is a hint indicating which key was used to secure a JWS or JWE.  This parameter\n     * allows originators to explicitly signal a change of key to recipients.  The structure of the keyId value is\n     * unspecified. Its value MUST be a case-sensitive string.</p>\n     *\n     * <p>When used with a JWK, the keyId value is used to match a JWK {@code keyId} parameter value.</p>\n     *\n     * @param kid the case-sensitive JWS {@code kid} header value or {@code null} to remove the property from the JSON map.\n     * @return the header instance for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.4\">JWS Key ID</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.6\">JWE Key ID</a>\n     */\n    T keyId(String kid);\n\n    /**\n     * Deprecated since 0.12.0, delegates to {@link #keyId(String)}.\n     *\n     * @param kid the case-sensitive JWS {@code kid} header value or {@code null} to remove the property from the JSON map.\n     * @return the instance for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.4\">JWS Key ID</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.6\">JWE Key ID</a>\n     * @deprecated since 0.12.0 in favor of the more modern builder-style {@link #keyId(String)} method.\n     */\n    @Deprecated\n    T setKeyId(String kid);\n\n    /**\n     * Deprecated as of 0.12.0, there is no need to set this any longer as the {@code JwtBuilder} will\n     * always set the {@code alg} header as necessary.\n     *\n     * @param alg the JWS or JWE algorithm {@code alg} value or {@code null} to remove the property from the JSON map.\n     * @return the instance for method chaining.\n     * @since 0.1\n     * @deprecated since 0.12.0 and will be removed before the 1.0 release.\n     */\n    @Deprecated\n    T setAlgorithm(String alg);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/ProtectedJwt.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.security.DigestSupplier;\n\n/**\n * A {@code ProtectedJwt} is a {@link Jwt} that is integrity protected via a cryptographic algorithm that produces\n * a cryptographic digest, such as a MAC, Digital Signature or Authentication Tag.\n *\n * <p><b>Cryptographic Digest</b></p>\n * <p>This interface extends DigestSupplier to make available the {@code ProtectedJwt}'s associated cryptographic\n * digest:</p>\n * <ul>\n *     <li>If the JWT is a {@link Jws}, {@link #getDigest() getDigest() } returns the JWS signature.</li>\n *     <li>If the JWT is a {@link Jwe}, {@link #getDigest() getDigest() } returns the AAD Authentication Tag.</li>\n * </ul>\n *\n * @param <H> the type of the JWT protected header\n * @param <P> the type of the JWT payload, either a content byte array or a {@link Claims} instance.\n * @since 0.12.0\n */\npublic interface ProtectedJwt<H extends ProtectedHeader, P> extends Jwt<H, P>, DigestSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/RequiredTypeException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception thrown when attempting to obtain a value from a JWT or JWK and the existing value does not match the\n * expected type.\n *\n * @since 0.6\n */\npublic class RequiredTypeException extends JwtException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public RequiredTypeException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public RequiredTypeException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/SignatureAlgorithm.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.Keys;\nimport io.jsonwebtoken.security.SignatureException;\nimport io.jsonwebtoken.security.WeakKeyException;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.interfaces.ECKey;\nimport java.security.interfaces.RSAKey;\nimport java.util.Arrays;\nimport java.util.Collections;\nimport java.util.List;\n\n/**\n * Type-safe representation of standard JWT signature algorithm names as defined in the\n * <a href=\"https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-31\">JSON Web Algorithms</a> specification.\n *\n * @since 0.1\n * @deprecated since 0.12.0; use {@link Jwts.SIG} instead.\n */\n@Deprecated\npublic enum SignatureAlgorithm {\n\n    /**\n     * JWA name for {@code No digital signature or MAC performed}\n     */\n    NONE(\"none\", \"No digital signature or MAC performed\", \"None\", null, false, 0, 0),\n\n    /**\n     * JWA algorithm name for {@code HMAC using SHA-256}\n     */\n    HS256(\"HS256\", \"HMAC using SHA-256\", \"HMAC\", \"HmacSHA256\", true, 256, 256, \"1.2.840.113549.2.9\"),\n\n    /**\n     * JWA algorithm name for {@code HMAC using SHA-384}\n     */\n    HS384(\"HS384\", \"HMAC using SHA-384\", \"HMAC\", \"HmacSHA384\", true, 384, 384, \"1.2.840.113549.2.10\"),\n\n    /**\n     * JWA algorithm name for {@code HMAC using SHA-512}\n     */\n    HS512(\"HS512\", \"HMAC using SHA-512\", \"HMAC\", \"HmacSHA512\", true, 512, 512, \"1.2.840.113549.2.11\"),\n\n    /**\n     * JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-256}\n     */\n    RS256(\"RS256\", \"RSASSA-PKCS-v1_5 using SHA-256\", \"RSA\", \"SHA256withRSA\", true, 256, 2048),\n\n    /**\n     * JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-384}\n     */\n    RS384(\"RS384\", \"RSASSA-PKCS-v1_5 using SHA-384\", \"RSA\", \"SHA384withRSA\", true, 384, 2048),\n\n    /**\n     * JWA algorithm name for {@code RSASSA-PKCS-v1_5 using SHA-512}\n     */\n    RS512(\"RS512\", \"RSASSA-PKCS-v1_5 using SHA-512\", \"RSA\", \"SHA512withRSA\", true, 512, 2048),\n\n    /**\n     * JWA algorithm name for {@code ECDSA using P-256 and SHA-256}\n     */\n    ES256(\"ES256\", \"ECDSA using P-256 and SHA-256\", \"ECDSA\", \"SHA256withECDSA\", true, 256, 256),\n\n    /**\n     * JWA algorithm name for {@code ECDSA using P-384 and SHA-384}\n     */\n    ES384(\"ES384\", \"ECDSA using P-384 and SHA-384\", \"ECDSA\", \"SHA384withECDSA\", true, 384, 384),\n\n    /**\n     * JWA algorithm name for {@code ECDSA using P-521 and SHA-512}\n     */\n    ES512(\"ES512\", \"ECDSA using P-521 and SHA-512\", \"ECDSA\", \"SHA512withECDSA\", true, 512, 521),\n\n    /**\n     * JWA algorithm name for {@code RSASSA-PSS using SHA-256 and MGF1 with SHA-256}.  <b>This algorithm requires\n     * Java 11 or later or a JCA provider like BouncyCastle to be in the runtime classpath.</b>  If on Java 10 or\n     * earlier, BouncyCastle will be used automatically if found in the runtime classpath.\n     */\n    PS256(\"PS256\", \"RSASSA-PSS using SHA-256 and MGF1 with SHA-256\", \"RSA\", \"RSASSA-PSS\", false, 256, 2048),\n\n    /**\n     * JWA algorithm name for {@code RSASSA-PSS using SHA-384 and MGF1 with SHA-384}.  <b>This algorithm requires\n     * Java 11 or later or a JCA provider like BouncyCastle to be in the runtime classpath.</b>  If on Java 10 or\n     * earlier, BouncyCastle will be used automatically if found in the runtime classpath.\n     */\n    PS384(\"PS384\", \"RSASSA-PSS using SHA-384 and MGF1 with SHA-384\", \"RSA\", \"RSASSA-PSS\", false, 384, 2048),\n\n    /**\n     * JWA algorithm name for {@code RSASSA-PSS using SHA-512 and MGF1 with SHA-512}. <b>This algorithm requires\n     * Java 11 or later or a JCA provider like BouncyCastle to be in the runtime classpath.</b>  If on Java 10 or\n     * earlier, BouncyCastle will be used automatically if found in the runtime classpath.\n     */\n    PS512(\"PS512\", \"RSASSA-PSS using SHA-512 and MGF1 with SHA-512\", \"RSA\", \"RSASSA-PSS\", false, 512, 2048);\n\n    //purposefully ordered higher to lower:\n    private static final List<SignatureAlgorithm> PREFERRED_HMAC_ALGS = Collections.unmodifiableList(Arrays.asList(\n            SignatureAlgorithm.HS512, SignatureAlgorithm.HS384, SignatureAlgorithm.HS256));\n    //purposefully ordered higher to lower:\n    private static final List<SignatureAlgorithm> PREFERRED_EC_ALGS = Collections.unmodifiableList(Arrays.asList(\n            SignatureAlgorithm.ES512, SignatureAlgorithm.ES384, SignatureAlgorithm.ES256));\n\n    private final String value;\n    private final String description;\n    private final String familyName;\n    private final String jcaName;\n    private final boolean jdkStandard;\n    private final int digestLength;\n    private final int minKeyLength;\n    /**\n     * Algorithm name as given by {@link Key#getAlgorithm()} if the key was loaded from a pkcs12 Keystore.\n     *\n     * @deprecated This is just a workaround for https://bugs.openjdk.java.net/browse/JDK-8243551\n     */\n    @Deprecated\n    private final String pkcs12Name;\n\n    SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard,\n                       int digestLength, int minKeyLength) {\n        this(value, description, familyName, jcaName, jdkStandard, digestLength, minKeyLength, jcaName);\n    }\n\n    SignatureAlgorithm(String value, String description, String familyName, String jcaName, boolean jdkStandard,\n                       int digestLength, int minKeyLength, String pkcs12Name) {\n        this.value = value;\n        this.description = description;\n        this.familyName = familyName;\n        this.jcaName = jcaName;\n        this.jdkStandard = jdkStandard;\n        this.digestLength = digestLength;\n        this.minKeyLength = minKeyLength;\n        this.pkcs12Name = pkcs12Name;\n    }\n\n    /**\n     * Returns the JWA algorithm name constant.\n     *\n     * @return the JWA algorithm name constant.\n     */\n    public String getValue() {\n        return value;\n    }\n\n    /**\n     * Returns the JWA algorithm description.\n     *\n     * @return the JWA algorithm description.\n     */\n    public String getDescription() {\n        return description;\n    }\n\n\n    /**\n     * Returns the cryptographic family name of the signature algorithm.  The value returned is according to the\n     * following table:\n     *\n     * <table>\n     * <caption>Crypto Family</caption>\n     * <thead>\n     * <tr>\n     * <th>SignatureAlgorithm</th>\n     * <th>Family Name</th>\n     * </tr>\n     * </thead>\n     * <tbody>\n     * <tr>\n     * <td>HS256</td>\n     * <td>HMAC</td>\n     * </tr>\n     * <tr>\n     * <td>HS384</td>\n     * <td>HMAC</td>\n     * </tr>\n     * <tr>\n     * <td>HS512</td>\n     * <td>HMAC</td>\n     * </tr>\n     * <tr>\n     * <td>RS256</td>\n     * <td>RSA</td>\n     * </tr>\n     * <tr>\n     * <td>RS384</td>\n     * <td>RSA</td>\n     * </tr>\n     * <tr>\n     * <td>RS512</td>\n     * <td>RSA</td>\n     * </tr>\n     * <tr>\n     * <td>PS256</td>\n     * <td>RSA</td>\n     * </tr>\n     * <tr>\n     * <td>PS384</td>\n     * <td>RSA</td>\n     * </tr>\n     * <tr>\n     * <td>PS512</td>\n     * <td>RSA</td>\n     * </tr>\n     * <tr>\n     * <td>ES256</td>\n     * <td>ECDSA</td>\n     * </tr>\n     * <tr>\n     * <td>ES384</td>\n     * <td>ECDSA</td>\n     * </tr>\n     * <tr>\n     * <td>ES512</td>\n     * <td>ECDSA</td>\n     * </tr>\n     * </tbody>\n     * </table>\n     *\n     * @return Returns the cryptographic family name of the signature algorithm.\n     * @since 0.5\n     */\n    public String getFamilyName() {\n        return familyName;\n    }\n\n    /**\n     * Returns the name of the JCA algorithm used to compute the signature.\n     *\n     * @return the name of the JCA algorithm used to compute the signature.\n     */\n    public String getJcaName() {\n        return jcaName;\n    }\n\n    /**\n     * Returns {@code true} if the algorithm is supported by standard JDK distributions or {@code false} if the\n     * algorithm implementation is not in the JDK and must be provided by a separate runtime JCA Provider (like\n     * BouncyCastle for example).\n     *\n     * @return {@code true} if the algorithm is supported by standard JDK distributions or {@code false} if the\n     * algorithm implementation is not in the JDK and must be provided by a separate runtime JCA Provider (like\n     * BouncyCastle for example).\n     */\n    public boolean isJdkStandard() {\n        return jdkStandard;\n    }\n\n    /**\n     * Returns {@code true} if the enum instance represents an HMAC signature algorithm, {@code false} otherwise.\n     *\n     * @return {@code true} if the enum instance represents an HMAC signature algorithm, {@code false} otherwise.\n     */\n    public boolean isHmac() {\n        return familyName.equals(\"HMAC\");\n    }\n\n    /**\n     * Returns {@code true} if the enum instance represents an RSA public/private key pair signature algorithm,\n     * {@code false} otherwise.\n     *\n     * @return {@code true} if the enum instance represents an RSA public/private key pair signature algorithm,\n     * {@code false} otherwise.\n     */\n    public boolean isRsa() {\n        return familyName.equals(\"RSA\");\n    }\n\n    /**\n     * Returns {@code true} if the enum instance represents an Elliptic Curve ECDSA signature algorithm, {@code false}\n     * otherwise.\n     *\n     * @return {@code true} if the enum instance represents an Elliptic Curve ECDSA signature algorithm, {@code false}\n     * otherwise.\n     */\n    public boolean isEllipticCurve() {\n        return familyName.equals(\"ECDSA\");\n    }\n\n    /**\n     * Returns the minimum key length in bits (not bytes) that may be used with this algorithm according to the\n     * <a href=\"https://tools.ietf.org/html/rfc7518\">JWT JWA Specification (RFC 7518)</a>.\n     *\n     * @return the minimum key length in bits (not bytes) that may be used with this algorithm according to the\n     * <a href=\"https://tools.ietf.org/html/rfc7518\">JWT JWA Specification (RFC 7518)</a>.\n     * @since 0.10.0\n     */\n    public int getMinKeyLength() {\n        return this.minKeyLength;\n    }\n\n    /**\n     * Returns quietly if the specified key is allowed to create signatures using this algorithm\n     * according to the <a href=\"https://tools.ietf.org/html/rfc7518\">JWT JWA Specification (RFC 7518)</a> or throws an\n     * {@link InvalidKeyException} if the key is not allowed or not secure enough for this algorithm.\n     *\n     * @param key the key to check for validity.\n     * @throws InvalidKeyException if the key is not allowed or not secure enough for this algorithm.\n     * @since 0.10.0\n     */\n    public void assertValidSigningKey(Key key) throws InvalidKeyException {\n        assertValid(key, true);\n    }\n\n    /**\n     * Returns quietly if the specified key is allowed to verify signatures using this algorithm\n     * according to the <a href=\"https://tools.ietf.org/html/rfc7518\">JWT JWA Specification (RFC 7518)</a> or throws an\n     * {@link InvalidKeyException} if the key is not allowed or not secure enough for this algorithm.\n     *\n     * @param key the key to check for validity.\n     * @throws InvalidKeyException if the key is not allowed or not secure enough for this algorithm.\n     * @since 0.10.0\n     */\n    public void assertValidVerificationKey(Key key) throws InvalidKeyException {\n        assertValid(key, false);\n    }\n\n    /**\n     * @since 0.10.0 to support assertValid(Key, boolean)\n     */\n    private static String keyType(boolean signing) {\n        return signing ? \"signing\" : \"verification\";\n    }\n\n    /**\n     * @since 0.10.0\n     */\n    private void assertValid(Key key, boolean signing) throws InvalidKeyException {\n\n        if (this == NONE) {\n\n            String msg = \"The 'NONE' signature algorithm does not support cryptographic keys.\";\n            throw new InvalidKeyException(msg);\n\n        } else if (isHmac()) {\n\n            if (!(key instanceof SecretKey)) {\n                String msg = this.familyName + \" \" + keyType(signing) + \" keys must be SecretKey instances.\";\n                throw new InvalidKeyException(msg);\n            }\n            SecretKey secretKey = (SecretKey) key;\n\n            byte[] encoded = secretKey.getEncoded();\n            if (encoded == null) {\n                throw new InvalidKeyException(\"The \" + keyType(signing) + \" key's encoded bytes cannot be null.\");\n            }\n\n            String alg = secretKey.getAlgorithm();\n            if (alg == null) {\n                throw new InvalidKeyException(\"The \" + keyType(signing) + \" key's algorithm cannot be null.\");\n            }\n\n            // These next checks use equalsIgnoreCase per https://github.com/jwtk/jjwt/issues/381#issuecomment-412912272\n            if (!HS256.jcaName.equalsIgnoreCase(alg) &&\n                    !HS384.jcaName.equalsIgnoreCase(alg) &&\n                    !HS512.jcaName.equalsIgnoreCase(alg) &&\n                    !HS256.pkcs12Name.equals(alg) &&\n                    !HS384.pkcs12Name.equals(alg) &&\n                    !HS512.pkcs12Name.equals(alg)) {\n                throw new InvalidKeyException(\"The \" + keyType(signing) + \" key's algorithm '\" + alg +\n                        \"' does not equal a valid HmacSHA* algorithm name and cannot be used with \" + name() + \".\");\n            }\n\n            int size = encoded.length * 8; //size in bits\n            if (size < this.minKeyLength) {\n                String msg = \"The \" + keyType(signing) + \" key's size is \" + size + \" bits which \" +\n                        \"is not secure enough for the \" + name() + \" algorithm.  The JWT \" +\n                        \"JWA Specification (RFC 7518, Section 3.2) states that keys used with \" + name() + \" MUST have a \" +\n                        \"size >= \" + minKeyLength + \" bits (the key size must be greater than or equal to the hash \" +\n                        \"output size).  Consider using the \" + Keys.class.getName() + \" class's \" +\n                        \"'secretKeyFor(SignatureAlgorithm.\" + name() + \")' method to create a key guaranteed to be \" +\n                        \"secure enough for \" + name() + \".  See \" +\n                        \"https://tools.ietf.org/html/rfc7518#section-3.2 for more information.\";\n                throw new WeakKeyException(msg);\n            }\n\n        } else { //EC or RSA\n\n            if (signing) {\n                if (!(key instanceof PrivateKey)) {\n                    String msg = familyName + \" signing keys must be PrivateKey instances.\";\n                    throw new InvalidKeyException(msg);\n                }\n            }\n\n            if (isEllipticCurve()) {\n\n                if (!(key instanceof ECKey)) {\n                    String msg = familyName + \" \" + keyType(signing) + \" keys must be ECKey instances.\";\n                    throw new InvalidKeyException(msg);\n                }\n\n                ECKey ecKey = (ECKey) key;\n                int size = ecKey.getParams().getOrder().bitLength();\n                if (size < this.minKeyLength) {\n                    String msg = \"The \" + keyType(signing) + \" key's size (ECParameterSpec order) is \" + size +\n                            \" bits which is not secure enough for the \" + name() + \" algorithm.  The JWT \" +\n                            \"JWA Specification (RFC 7518, Section 3.4) states that keys used with \" +\n                            name() + \" MUST have a size >= \" + this.minKeyLength +\n                            \" bits.  Consider using the \" + Keys.class.getName() + \" class's \" +\n                            \"'keyPairFor(SignatureAlgorithm.\" + name() + \")' method to create a key pair guaranteed \" +\n                            \"to be secure enough for \" + name() + \".  See \" +\n                            \"https://tools.ietf.org/html/rfc7518#section-3.4 for more information.\";\n                    throw new WeakKeyException(msg);\n                }\n\n            } else { //RSA\n\n                if (!(key instanceof RSAKey)) {\n                    String msg = familyName + \" \" + keyType(signing) + \" keys must be RSAKey instances.\";\n                    throw new InvalidKeyException(msg);\n                }\n\n                RSAKey rsaKey = (RSAKey) key;\n                int size = rsaKey.getModulus().bitLength();\n                if (size < this.minKeyLength) {\n\n                    String section = name().startsWith(\"P\") ? \"3.5\" : \"3.3\";\n\n                    String msg = \"The \" + keyType(signing) + \" key's size is \" + size + \" bits which is not secure \" +\n                            \"enough for the \" + name() + \" algorithm.  The JWT JWA Specification (RFC 7518, Section \" +\n                            section + \") states that keys used with \" + name() + \" MUST have a size >= \" +\n                            this.minKeyLength + \" bits.  Consider using the \" + Keys.class.getName() + \" class's \" +\n                            \"'keyPairFor(SignatureAlgorithm.\" + name() + \")' method to create a key pair guaranteed \" +\n                            \"to be secure enough for \" + name() + \".  See \" +\n                            \"https://tools.ietf.org/html/rfc7518#section-\" + section + \" for more information.\";\n                    throw new WeakKeyException(msg);\n                }\n            }\n        }\n    }\n\n    /**\n     * Returns the recommended signature algorithm to be used with the specified key according to the following\n     * heuristics:\n     *\n     * <table>\n     * <caption>Key Signature Algorithm</caption>\n     * <thead>\n     * <tr>\n     * <th>If the Key is a:</th>\n     * <th>And:</th>\n     * <th>With a key size of:</th>\n     * <th>The returned SignatureAlgorithm will be:</th>\n     * </tr>\n     * </thead>\n     * <tbody>\n     * <tr>\n     * <td>{@link SecretKey}</td>\n     * <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals(\"HmacSHA256\")</code><sup>1</sup></td>\n     * <td>256 &lt;= size &lt;= 383 <sup>2</sup></td>\n     * <td>{@link SignatureAlgorithm#HS256 HS256}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link SecretKey}</td>\n     * <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals(\"HmacSHA384\")</code><sup>1</sup></td>\n     * <td>384 &lt;= size &lt;= 511</td>\n     * <td>{@link SignatureAlgorithm#HS384 HS384}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link SecretKey}</td>\n     * <td><code>{@link Key#getAlgorithm() getAlgorithm()}.equals(\"HmacSHA512\")</code><sup>1</sup></td>\n     * <td>512 &lt;= size</td>\n     * <td>{@link SignatureAlgorithm#HS512 HS512}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link ECKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>256 &lt;= size &lt;= 383 <sup>3</sup></td>\n     * <td>{@link SignatureAlgorithm#ES256 ES256}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link ECKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>384 &lt;= size &lt;= 511</td>\n     * <td>{@link SignatureAlgorithm#ES384 ES384}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link ECKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>4096 &lt;= size</td>\n     * <td>{@link SignatureAlgorithm#ES512 ES512}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link RSAKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>2048 &lt;= size &lt;= 3071 <sup>4,5</sup></td>\n     * <td>{@link SignatureAlgorithm#RS256 RS256}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link RSAKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>3072 &lt;= size &lt;= 4095 <sup>5</sup></td>\n     * <td>{@link SignatureAlgorithm#RS384 RS384}</td>\n     * </tr>\n     * <tr>\n     * <td>{@link RSAKey}</td>\n     * <td><code>instanceof {@link PrivateKey}</code></td>\n     * <td>4096 &lt;= size <sup>5</sup></td>\n     * <td>{@link SignatureAlgorithm#RS512 RS512}</td>\n     * </tr>\n     * </tbody>\n     * </table>\n     * <p>Notes:</p>\n     * <ol>\n     * <li>{@code SecretKey} instances must have an {@link Key#getAlgorithm() algorithm} name equal\n     * to {@code HmacSHA256}, {@code HmacSHA384} or {@code HmacSHA512}.  If not, the key bytes might not be\n     * suitable for HMAC signatures will be rejected with a {@link InvalidKeyException}. </li>\n     * <li>The JWT <a href=\"https://tools.ietf.org/html/rfc7518#section-3.2\">JWA Specification (RFC 7518,\n     * Section 3.2)</a> mandates that HMAC-SHA-* signing keys <em>MUST</em> be 256 bits or greater.\n     * {@code SecretKey}s with key lengths less than 256 bits will be rejected with an\n     * {@link WeakKeyException}.</li>\n     * <li>The JWT <a href=\"https://tools.ietf.org/html/rfc7518#section-3.4\">JWA Specification (RFC 7518,\n     * Section 3.4)</a> mandates that ECDSA signing key lengths <em>MUST</em> be 256 bits or greater.\n     * {@code ECKey}s with key lengths less than 256 bits will be rejected with a\n     * {@link WeakKeyException}.</li>\n     * <li>The JWT <a href=\"https://tools.ietf.org/html/rfc7518#section-3.3\">JWA Specification (RFC 7518,\n     * Section 3.3)</a> mandates that RSA signing key lengths <em>MUST</em> be 2048 bits or greater.\n     * {@code RSAKey}s with key lengths less than 2048 bits will be rejected with a\n     * {@link WeakKeyException}.</li>\n     * <li>Technically any RSA key of length &gt;= 2048 bits may be used with the {@link #RS256}, {@link #RS384}, and\n     * {@link #RS512} algorithms, so we assume an RSA signature algorithm based on the key length to\n     * parallel similar decisions in the JWT specification for HMAC and ECDSA signature algorithms.\n     * This is not required - just a convenience.</li>\n     * </ol>\n     * <p>This implementation does not return the {@link #PS256}, {@link #PS256}, {@link #PS256} RSA variant for any\n     * specified {@link RSAKey} because:\n     * <ul>\n     * <li>The JWT <a href=\"https://tools.ietf.org/html/rfc7518#section-3.1\">JWA Specification (RFC 7518,\n     * Section 3.1)</a> indicates that {@link #RS256}, {@link #RS384}, and {@link #RS512} are\n     * recommended algorithms while the {@code PS}* variants are simply marked as optional.</li>\n     * <li>The {@link #RS256}, {@link #RS384}, and {@link #RS512} algorithms are available in the JDK by default\n     * while the {@code PS}* variants require an additional JCA Provider (like BouncyCastle).</li>\n     * </ul>\n     *\n     * <p>Finally, this method will throw an {@link InvalidKeyException} for any key that does not match the\n     * heuristics and requirements documented above, since that inevitably means the Key is either insufficient or\n     * explicitly disallowed by the JWT specification.</p>\n     *\n     * @param key the key to inspect\n     * @return the recommended signature algorithm to be used with the specified key\n     * @throws InvalidKeyException for any key that does not match the heuristics and requirements documented above,\n     *                             since that inevitably means the Key is either insufficient or explicitly disallowed by the JWT specification.\n     * @since 0.10.0\n     */\n    public static SignatureAlgorithm forSigningKey(Key key) throws InvalidKeyException {\n\n        if (key == null) {\n            throw new InvalidKeyException(\"Key argument cannot be null.\");\n        }\n\n        if (!(key instanceof SecretKey ||\n                (key instanceof PrivateKey && (key instanceof ECKey || key instanceof RSAKey)))) {\n            String msg = \"JWT standard signing algorithms require either 1) a SecretKey for HMAC-SHA algorithms or \" +\n                    \"2) a private RSAKey for RSA algorithms or 3) a private ECKey for Elliptic Curve algorithms.  \" +\n                    \"The specified key is of type \" + key.getClass().getName();\n            throw new InvalidKeyException(msg);\n        }\n\n        if (key instanceof SecretKey) {\n\n            SecretKey secretKey = (SecretKey) key;\n            int bitLength = io.jsonwebtoken.lang.Arrays.length(secretKey.getEncoded()) * Byte.SIZE;\n\n            for (SignatureAlgorithm alg : PREFERRED_HMAC_ALGS) {\n                // ensure compatibility check is based on key length. See https://github.com/jwtk/jjwt/issues/381\n                if (bitLength >= alg.minKeyLength) {\n                    return alg;\n                }\n            }\n\n            String msg = \"The specified SecretKey is not strong enough to be used with JWT HMAC signature \" +\n                    \"algorithms.  The JWT specification requires HMAC keys to be >= 256 bits long.  The specified \" +\n                    \"key is \" + bitLength + \" bits.  See https://tools.ietf.org/html/rfc7518#section-3.2 for more \" +\n                    \"information.\";\n            throw new WeakKeyException(msg);\n        }\n\n        if (key instanceof RSAKey) {\n\n            RSAKey rsaKey = (RSAKey) key;\n            int bitLength = rsaKey.getModulus().bitLength();\n\n            if (bitLength >= 4096) {\n                RS512.assertValidSigningKey(key);\n                return RS512;\n            } else if (bitLength >= 3072) {\n                RS384.assertValidSigningKey(key);\n                return RS384;\n            } else if (bitLength >= RS256.minKeyLength) {\n                RS256.assertValidSigningKey(key);\n                return RS256;\n            }\n\n            String msg = \"The specified RSA signing key is not strong enough to be used with JWT RSA signature \" +\n                    \"algorithms.  The JWT specification requires RSA keys to be >= 2048 bits long.  The specified RSA \" +\n                    \"key is \" + bitLength + \" bits.  See https://tools.ietf.org/html/rfc7518#section-3.3 for more \" +\n                    \"information.\";\n            throw new WeakKeyException(msg);\n        }\n\n        // if we've made it this far in the method, the key is an ECKey due to the instanceof assertions at the\n        // top of the method\n\n        ECKey ecKey = (ECKey) key;\n        int bitLength = ecKey.getParams().getOrder().bitLength();\n\n        for (SignatureAlgorithm alg : PREFERRED_EC_ALGS) {\n            if (bitLength >= alg.minKeyLength) {\n                alg.assertValidSigningKey(key);\n                return alg;\n            }\n        }\n\n        String msg = \"The specified Elliptic Curve signing key is not strong enough to be used with JWT ECDSA \" +\n                \"signature algorithms.  The JWT specification requires ECDSA keys to be >= 256 bits long.  \" +\n                \"The specified ECDSA key is \" + bitLength + \" bits.  See \" +\n                \"https://tools.ietf.org/html/rfc7518#section-3.4 for more information.\";\n        throw new WeakKeyException(msg);\n    }\n\n    /**\n     * Looks up and returns the corresponding {@code SignatureAlgorithm} enum instance based on a\n     * case-<em>insensitive</em> name comparison.\n     *\n     * @param value The case-insensitive name of the {@code SignatureAlgorithm} instance to return\n     * @return the corresponding {@code SignatureAlgorithm} enum instance based on a\n     * case-<em>insensitive</em> name comparison.\n     * @throws SignatureException if the specified value does not match any {@code SignatureAlgorithm}\n     *                            name.\n     */\n    public static SignatureAlgorithm forName(String value) throws SignatureException {\n        for (SignatureAlgorithm alg : values()) {\n            if (alg.getValue().equalsIgnoreCase(value)) {\n                return alg;\n            }\n        }\n\n        throw new SignatureException(\"Unsupported signature algorithm '\" + value + \"'\");\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/SignatureException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.security.SecurityException;\n\n/**\n * Exception indicating that either calculating a signature or verifying an existing signature of a JWT failed.\n *\n * @since 0.1\n * @deprecated in favor of {@link io.jsonwebtoken.security.SignatureException}; this class will be removed before 1.0\n */\n@Deprecated\npublic class SignatureException extends SecurityException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public SignatureException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public SignatureException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/SigningKeyResolver.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport java.security.Key;\n\n/**\n * A {@code SigningKeyResolver} can be used by a {@link io.jsonwebtoken.JwtParser JwtParser} to find a signing key that\n * should be used to verify a JWS signature.\n *\n * <p>A {@code SigningKeyResolver} is necessary when the signing key is not already known before parsing the JWT and the\n * JWT header or payload (byte array or Claims) must be inspected first to determine how to look up the signing key.\n * Once returned by the resolver, the JwtParser will then verify the JWS signature with the returned key.  For\n * example:</p>\n *\n * <pre>\n * Jws&lt;Claims&gt; jws = Jwts.parser().setSigningKeyResolver(new SigningKeyResolverAdapter() {\n *         &#64;Override\n *         public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n *             //inspect the header or claims, lookup and return the signing key\n *             return getSigningKeyBytes(header, claims); //implement me\n *         }})\n *     .build().parseSignedClaims(compact);\n * </pre>\n *\n * <p>A {@code SigningKeyResolver} is invoked once during parsing before the signature is verified.</p>\n *\n * <h2>Using an Adapter</h2>\n *\n * <p>If you only need to resolve a signing key for a particular JWS (either a content or Claims JWS), consider using\n * the {@link io.jsonwebtoken.SigningKeyResolverAdapter} and overriding only the method you need to support instead of\n * implementing this interface directly.</p>\n *\n * @see io.jsonwebtoken.JwtParserBuilder#keyLocator(Locator)\n * @since 0.4\n * @deprecated since 0.12.0. Implement {@link Locator} instead.\n */\n@Deprecated\npublic interface SigningKeyResolver {\n\n    /**\n     * Returns the signing key that should be used to validate a digital signature for the Claims JWS with the specified\n     * header and claims.\n     *\n     * @param header the header of the JWS to validate\n     * @param claims the Claims payload of the JWS to validate\n     * @return the signing key that should be used to validate a digital signature for the Claims JWS with the specified\n     * header and claims.\n     */\n    Key resolveSigningKey(JwsHeader header, Claims claims);\n\n    /**\n     * Returns the signing key that should be used to validate a digital signature for the content JWS with the\n     * specified header and byte array payload.\n     *\n     * @param header  the header of the JWS to validate\n     * @param content the byte array payload of the JWS to validate\n     * @return the signing key that should be used to validate a digital signature for the content JWS with the\n     * specified header and byte array payload.\n     */\n    Key resolveSigningKey(JwsHeader header, byte[] content);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/SigningKeyResolverAdapter.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport javax.crypto.spec.SecretKeySpec;\nimport java.security.Key;\n\n/**\n * <h2>Deprecation Notice</h2>\n *\n * <p>As of JJWT 0.12.0, various Resolver concepts (including the {@code SigningKeyResolver}) have been\n * unified into a single {@link Locator} interface.  For key location, (for both signing and encryption keys),\n * use the {@link JwtParserBuilder#keyLocator(Locator)} to configure a parser with your desired Key locator instead\n * of using a {@code SigningKeyResolver}. Also see {@link LocatorAdapter} for the Adapter pattern parallel of this\n * class. <b>This {@code SigningKeyResolverAdapter} class will be removed before the 1.0 release.</b></p>\n *\n * <p><b>Previous Documentation</b></p>\n *\n * <p>An <a href=\"http://en.wikipedia.org/wiki/Adapter_pattern\">Adapter</a> implementation of the\n * {@link SigningKeyResolver} interface that allows subclasses to process only the type of JWS body that\n * is known/expected for a particular case.</p>\n *\n * <p>The {@link #resolveSigningKey(JwsHeader, Claims)} and {@link #resolveSigningKey(JwsHeader, byte[])} method\n * implementations delegate to the\n * {@link #resolveSigningKeyBytes(JwsHeader, Claims)} and {@link #resolveSigningKeyBytes(JwsHeader, byte[])} methods\n * respectively.  The latter two methods simply throw exceptions:  they represent scenarios expected by\n * calling code in known situations, and it is expected that you override the implementation in those known situations;\n * non-overridden *KeyBytes methods indicates that the JWS input was unexpected.</p>\n *\n * <p>If either {@link #resolveSigningKey(JwsHeader, byte[])} or {@link #resolveSigningKey(JwsHeader, Claims)}\n * are not overridden, one (or both) of the *KeyBytes variants must be overridden depending on your expected\n * use case.  You do not have to override any method that does not represent an expected condition.</p>\n *\n * @see io.jsonwebtoken.JwtParserBuilder#keyLocator(Locator)\n * @see LocatorAdapter\n * @since 0.4\n * @deprecated since 0.12.0. Use {@link LocatorAdapter LocatorAdapter} with\n * {@link JwtParserBuilder#keyLocator(Locator)}\n */\n@SuppressWarnings(\"DeprecatedIsStillUsed\")\n@Deprecated\npublic class SigningKeyResolverAdapter implements SigningKeyResolver {\n\n    /**\n     * Default constructor.\n     */\n    public SigningKeyResolverAdapter() {\n\n    }\n\n    @Override\n    public Key resolveSigningKey(JwsHeader header, Claims claims) {\n        SignatureAlgorithm alg = SignatureAlgorithm.forName(header.getAlgorithm());\n        Assert.isTrue(alg.isHmac(), \"The default resolveSigningKey(JwsHeader, Claims) implementation cannot \" +\n                \"be used for asymmetric key algorithms (RSA, Elliptic Curve).  \" +\n                \"Override the resolveSigningKey(JwsHeader, Claims) method instead and return a \" +\n                \"Key instance appropriate for the \" + alg.name() + \" algorithm.\");\n        byte[] keyBytes = resolveSigningKeyBytes(header, claims);\n        return new SecretKeySpec(keyBytes, alg.getJcaName());\n    }\n\n    @Override\n    public Key resolveSigningKey(JwsHeader header, byte[] content) {\n        SignatureAlgorithm alg = SignatureAlgorithm.forName(header.getAlgorithm());\n        Assert.isTrue(alg.isHmac(), \"The default resolveSigningKey(JwsHeader, byte[]) implementation cannot \" +\n                \"be used for asymmetric key algorithms (RSA, Elliptic Curve).  \" +\n                \"Override the resolveSigningKey(JwsHeader, byte[]) method instead and return a \" +\n                \"Key instance appropriate for the \" + alg.name() + \" algorithm.\");\n        byte[] keyBytes = resolveSigningKeyBytes(header, content);\n        return new SecretKeySpec(keyBytes, alg.getJcaName());\n    }\n\n    /**\n     * Convenience method invoked by {@link #resolveSigningKey(JwsHeader, Claims)} that obtains the necessary signing\n     * key bytes.  This implementation simply throws an exception: if the JWS parsed is a Claims JWS, you must\n     * override this method or the {@link #resolveSigningKey(JwsHeader, Claims)} method instead.\n     *\n     * <p><b>NOTE:</b> You cannot override this method when validating RSA signatures.  If you expect RSA signatures,\n     * you must override the {@link #resolveSigningKey(JwsHeader, Claims)} method instead.</p>\n     *\n     * @param header the parsed {@link JwsHeader}\n     * @param claims the parsed {@link Claims}\n     * @return the signing key bytes to use to verify the JWS signature.\n     */\n    public byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n        throw new UnsupportedJwtException(\"The specified SigningKeyResolver implementation does not support \" +\n                \"Claims JWS signing key resolution.  Consider overriding either the \" +\n                \"resolveSigningKey(JwsHeader, Claims) method or, for HMAC algorithms, the \" +\n                \"resolveSigningKeyBytes(JwsHeader, Claims) method.\");\n    }\n\n    /**\n     * Convenience method invoked by {@link #resolveSigningKey(JwsHeader, byte[])} that obtains the necessary signing\n     * key bytes.  This implementation simply throws an exception: if the JWS parsed is a content JWS, you must\n     * override this method or the {@link #resolveSigningKey(JwsHeader, byte[])} method instead.\n     *\n     * @param header  the parsed {@link JwsHeader}\n     * @param content the byte array payload\n     * @return the signing key bytes to use to verify the JWS signature.\n     */\n    @SuppressWarnings(\"unused\")\n    public byte[] resolveSigningKeyBytes(JwsHeader header, byte[] content) {\n        throw new UnsupportedJwtException(\"The specified SigningKeyResolver implementation does not support \" +\n                \"content JWS signing key resolution.  Consider overriding either the \" +\n                \"resolveSigningKey(JwsHeader, byte[]) method or, for HMAC algorithms, the \" +\n                \"resolveSigningKeyBytes(JwsHeader, byte[]) method.\");\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/SupportedJwtVisitor.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * A {@code JwtVisitor} that guarantees only supported JWT instances are handled, rejecting\n * all other (unsupported) JWTs with {@link UnsupportedJwtException}s.  A JWT is considered supported\n * only if the type-specific handler method is overridden by a subclass.\n *\n * @param <T> the type of value returned from the subclass handler method implementation.\n * @since 0.12.0\n */\npublic class SupportedJwtVisitor<T> implements JwtVisitor<T> {\n\n    /**\n     * Default constructor, does not initialize any internal state.\n     */\n    public SupportedJwtVisitor() {\n    }\n\n    /**\n     * Handles an encountered unsecured JWT by delegating to either {@link #onUnsecuredContent(Jwt)} or\n     * {@link #onUnsecuredClaims(Jwt)} depending on the payload type.\n     *\n     * @param jwt the parsed unsecured JWT\n     * @return the value returned by either {@link #onUnsecuredContent(Jwt)} or {@link #onUnsecuredClaims(Jwt)}\n     * depending on the payload type.\n     * @throws UnsupportedJwtException if the payload is neither a {@code byte[]} nor {@code Claims}, or either\n     *                                 delegate method throws the same.\n     */\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public T visit(Jwt<?, ?> jwt) {\n        Assert.notNull(jwt, \"JWT cannot be null.\");\n        Object payload = jwt.getPayload();\n        if (payload instanceof byte[]) {\n            return onUnsecuredContent((Jwt<Header, byte[]>) jwt);\n        } else {\n            // only other type we support:\n            Assert.stateIsInstance(Claims.class, payload, \"Unexpected payload data type: \");\n            return onUnsecuredClaims((Jwt<Header, Claims>) jwt);\n        }\n    }\n\n    /**\n     * Handles an encountered unsecured content JWT - one that is not cryptographically signed nor\n     * encrypted, and has a byte[] array payload. If the JWT creator has set the (optional)\n     * {@link Header#getContentType()} value, the application may inspect that value to determine how to convert\n     * the byte array to the final type as desired.\n     *\n     * <p>The default implementation immediately throws an {@link UnsupportedJwtException}; it is expected that\n     * subclasses will override this method if the application needs to support this type of JWT.</p>\n     *\n     * @param jwt the parsed unsecured content JWT\n     * @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.\n     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.\n     */\n    public T onUnsecuredContent(Jwt<Header, byte[]> jwt) throws UnsupportedJwtException {\n        throw new UnsupportedJwtException(\"Unexpected unsecured content JWT.\");\n    }\n\n    /**\n     * Handles an encountered unsecured Claims JWT - one that is not cryptographically signed nor\n     * encrypted, and has a {@link Claims} payload.\n     *\n     * <p>The default implementation immediately throws an {@link UnsupportedJwtException}; it is expected that\n     * subclasses will override this method if the application needs to support this type of JWT.</p>\n     *\n     * @param jwt the parsed unsecured content JWT\n     * @return any object to be used after inspecting the JWT, or {@code null} if no return value is necessary.\n     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.\n     */\n    public T onUnsecuredClaims(Jwt<Header, Claims> jwt) {\n        throw new UnsupportedJwtException(\"Unexpected unsecured Claims JWT.\");\n    }\n\n    /**\n     * Handles an encountered JSON Web Token (aka 'JWS') message that has been cryptographically verified/authenticated\n     * by delegating to either {@link #onVerifiedContent(Jws)} or {@link #onVerifiedClaims(Jws)} depending on the payload\n     * type.\n     *\n     * @param jws the parsed verified/authenticated JWS.\n     * @return the value returned by either {@link #onVerifiedContent(Jws)} or {@link #onVerifiedClaims(Jws)}\n     * depending on the payload type.\n     * @throws UnsupportedJwtException if the payload is neither a {@code byte[]} nor {@code Claims}, or either\n     *                                 delegate method throws the same.\n     */\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public T visit(Jws<?> jws) {\n        Assert.notNull(jws, \"JWS cannot be null.\");\n        Object payload = jws.getPayload();\n        if (payload instanceof byte[]) {\n            return onVerifiedContent((Jws<byte[]>) jws);\n        } else {\n            Assert.stateIsInstance(Claims.class, payload, \"Unexpected payload data type: \");\n            return onVerifiedClaims((Jws<Claims>) jws);\n        }\n    }\n\n    /**\n     * Handles an encountered JWS message that has been cryptographically verified/authenticated and has\n     * a byte[] array payload. If the JWT creator has set the (optional) {@link Header#getContentType()} value, the\n     * application may inspect that value to determine how to convert the byte array to the final type as desired.\n     *\n     * <p>The default implementation immediately throws an {@link UnsupportedJwtException}; it is expected that\n     * subclasses will override this method if the application needs to support this type of JWT.</p>\n     *\n     * @param jws the parsed verified/authenticated JWS.\n     * @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.\n     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.\n     */\n    public T onVerifiedContent(Jws<byte[]> jws) {\n        throw new UnsupportedJwtException(\"Unexpected content JWS.\");\n    }\n\n    /**\n     * Handles an encountered JWS message that has been cryptographically verified/authenticated and has a\n     * {@link Claims} payload.\n     *\n     * <p>The default implementation immediately throws an {@link UnsupportedJwtException}; it is expected that\n     * subclasses will override this method if the application needs to support this type of JWT.</p>\n     *\n     * @param jws the parsed signed (and verified) Claims JWS\n     * @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.\n     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.\n     */\n    public T onVerifiedClaims(Jws<Claims> jws) {\n        throw new UnsupportedJwtException(\"Unexpected Claims JWS.\");\n    }\n\n    /**\n     * Handles an encountered JSON Web Encryption (aka 'JWE') message that has been authenticated and decrypted by\n     * delegating to either {@link #onDecryptedContent(Jwe)} or {@link #onDecryptedClaims(Jwe)} depending on the\n     * payload type.\n     *\n     * @param jwe the parsed authenticated and decrypted JWE.\n     * @return the value returned by either {@link #onDecryptedContent(Jwe)} or {@link #onDecryptedClaims(Jwe)}\n     * depending on the payload type.\n     * @throws UnsupportedJwtException if the payload is neither a {@code byte[]} nor {@code Claims}, or either\n     *                                 delegate method throws the same.\n     */\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public T visit(Jwe<?> jwe) {\n        Assert.notNull(jwe, \"JWE cannot be null.\");\n        Object payload = jwe.getPayload();\n        if (payload instanceof byte[]) {\n            return onDecryptedContent((Jwe<byte[]>) jwe);\n        } else {\n            Assert.stateIsInstance(Claims.class, payload, \"Unexpected payload data type: \");\n            return onDecryptedClaims((Jwe<Claims>) jwe);\n        }\n    }\n\n    /**\n     * Handles an encountered JWE message that has been authenticated and decrypted, and has byte[] array payload. If\n     * the JWT creator has set the (optional) {@link Header#getContentType()} value, the application may inspect that\n     * value to determine how to convert the byte array to the final type as desired.\n     *\n     * <p>The default implementation immediately throws an {@link UnsupportedJwtException}; it is expected that\n     * subclasses will override this method if the application needs to support this type of JWT.</p>\n     *\n     * @param jwe the parsed authenticated and decrypted content JWE.\n     * @return any object to be used after inspecting the JWS, or {@code null} if no return value is necessary.\n     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.\n     */\n    public T onDecryptedContent(Jwe<byte[]> jwe) {\n        throw new UnsupportedJwtException(\"Unexpected content JWE.\");\n    }\n\n    /**\n     * Handles an encountered JWE message that has been authenticated and decrypted, and has a {@link Claims} payload.\n     *\n     * <p>The default implementation immediately throws an {@link UnsupportedJwtException}; it is expected that\n     * subclasses will override this method if the application needs to support this type of JWT.</p>\n     *\n     * @param jwe the parsed authenticated and decrypted content JWE.\n     * @return any object to be used after inspecting the JWE, or {@code null} if no return value is necessary.\n     * @throws UnsupportedJwtException by default, expecting the subclass implementation to override as necessary.\n     */\n    public T onDecryptedClaims(Jwe<Claims> jwe) {\n        throw new UnsupportedJwtException(\"Unexpected Claims JWE.\");\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/UnsupportedJwtException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken;\n\n/**\n * Exception thrown when receiving a JWT in a particular format/configuration that does not match the format expected\n * by the application.\n *\n * <p>For example, this exception would be thrown if parsing an unprotected content JWT when the application\n * requires a cryptographically signed Claims JWS instead.</p>\n *\n * @since 0.2\n */\npublic class UnsupportedJwtException extends JwtException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public UnsupportedJwtException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public UnsupportedJwtException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/AbstractDeserializer.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.Reader;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * Convenient base class to use to implement {@link Deserializer}s, with subclasses only needing to implement\n * {@link #doDeserialize(Reader)}.\n *\n * @param <T> the type of object returned after deserialization\n * @since 0.12.0\n */\npublic abstract class AbstractDeserializer<T> implements Deserializer<T> {\n\n    /**\n     * EOF (End of File) marker, equal to {@code -1}.\n     */\n    protected static final int EOF = -1;\n\n    private static final byte[] EMPTY_BYTES = new byte[0];\n\n    /**\n     * Default constructor, does not initialize any internal state.\n     */\n    protected AbstractDeserializer() {\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public final T deserialize(byte[] bytes) throws DeserializationException {\n        bytes = bytes == null ? EMPTY_BYTES : bytes; // null safe\n        InputStream in = new ByteArrayInputStream(bytes);\n        Reader reader = new InputStreamReader(in, StandardCharsets.UTF_8);\n        return deserialize(reader);\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public final T deserialize(Reader reader) throws DeserializationException {\n        Assert.notNull(reader, \"Reader argument cannot be null.\");\n        try {\n            return doDeserialize(reader);\n        } catch (Throwable t) {\n            if (t instanceof DeserializationException) {\n                throw (DeserializationException) t;\n            }\n            String msg = \"Unable to deserialize: \" + t.getMessage();\n            throw new DeserializationException(msg, t);\n        }\n    }\n\n    /**\n     * Reads the specified character stream and returns the corresponding Java object.\n     *\n     * @param reader the reader to use to read the character stream\n     * @return the deserialized Java object\n     * @throws Exception if there is a problem reading the stream or creating the expected Java object\n     */\n    protected abstract T doDeserialize(Reader reader) throws Exception;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/AbstractSerializer.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Objects;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.OutputStream;\n\n/**\n * Convenient base class to use to implement {@link Serializer}s, with subclasses only needing to implement\n * * {@link #doSerialize(Object, OutputStream)}.\n *\n * @param <T> the type of object to serialize\n * @since 0.12.0\n */\npublic abstract class AbstractSerializer<T> implements Serializer<T> {\n\n    /**\n     * Default constructor, does not initialize any internal state.\n     */\n    protected AbstractSerializer() {\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public final byte[] serialize(T t) throws SerializationException {\n        ByteArrayOutputStream out = new ByteArrayOutputStream();\n        serialize(t, out);\n        return out.toByteArray();\n    }\n\n    /**\n     * {@inheritDoc}\n     */\n    @Override\n    public final void serialize(T t, OutputStream out) throws SerializationException {\n        try {\n            doSerialize(t, out);\n        } catch (Throwable e) {\n            if (e instanceof SerializationException) {\n                throw (SerializationException) e;\n            }\n            String msg = \"Unable to serialize object of type \" + Objects.nullSafeClassName(t) + \": \" + e.getMessage();\n            throw new SerializationException(msg, e);\n        }\n    }\n\n    /**\n     * Converts the specified Java object into a formatted data byte stream, writing the bytes to the specified\n     * {@code out}put stream.\n     *\n     * @param t   the object to convert to a byte stream\n     * @param out the stream to write to\n     * @throws Exception if there is a problem converting the object to a byte stream or writing the\n     *                   bytes to the {@code out}put stream.\n     * @since 0.12.0\n     */\n    protected abstract void doSerialize(T t, OutputStream out) throws Exception;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Base64.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport java.util.Arrays;\n\n/**\n * A very fast and memory efficient class to encode and decode to and from BASE64 or BASE64URL in full accordance\n * with <a href=\"https://tools.ietf.org/html/rfc4648\">RFC 4648</a>.\n *\n * <p>Based initially on MigBase64 with continued modifications for Base64 URL support and JDK-standard code formatting.</p>\n *\n * <p>This encode/decode algorithm doesn't create any temporary arrays as many other codecs do, it only\n * allocates the resulting array. This produces less garbage and it is possible to handle arrays twice\n * as large as algorithms that create a temporary array.</p>\n *\n * <p>There is also a \"fast\" version of all decode methods that works the same way as the normal ones, but\n * has a few demands on the decoded input. Normally though, these fast versions should be used if the source if\n * the input is known and it hasn't bee tampered with.</p>\n *\n * @author Mikael Grev\n * @author Les Hazlewood\n * @since 0.10.0\n */\n@SuppressWarnings(\"Duplicates\")\nfinal class Base64 { //final and package-protected on purpose\n\n    private static final char[] BASE64_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\".toCharArray();\n    private static final char[] BASE64URL_ALPHABET = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_\".toCharArray();\n    private static final int[] BASE64_IALPHABET = new int[256];\n    private static final int[] BASE64URL_IALPHABET = new int[256];\n    private static final int IALPHABET_MAX_INDEX = BASE64_IALPHABET.length - 1;\n\n    static {\n        Arrays.fill(BASE64_IALPHABET, -1);\n        System.arraycopy(BASE64_IALPHABET, 0, BASE64URL_IALPHABET, 0, BASE64_IALPHABET.length);\n        for (int i = 0, iS = BASE64_ALPHABET.length; i < iS; i++) {\n            BASE64_IALPHABET[BASE64_ALPHABET[i]] = i;\n            BASE64URL_IALPHABET[BASE64URL_ALPHABET[i]] = i;\n        }\n        BASE64_IALPHABET['='] = 0;\n        BASE64URL_IALPHABET['='] = 0;\n    }\n\n    static final Base64 DEFAULT = new Base64(false);\n    static final Base64 URL_SAFE = new Base64(true);\n\n    private final boolean urlsafe;\n    private final char[] ALPHABET;\n    private final int[] IALPHABET;\n\n    private Base64(boolean urlsafe) {\n        this.urlsafe = urlsafe;\n        this.ALPHABET = urlsafe ? BASE64URL_ALPHABET : BASE64_ALPHABET;\n        this.IALPHABET = urlsafe ? BASE64URL_IALPHABET : BASE64_IALPHABET;\n    }\n\n    // ****************************************************************************************\n    // *  char[] version\n    // ****************************************************************************************\n\n    private String getName() {\n        return urlsafe ? \"base64url\" : \"base64\"; // RFC 4648 codec names are all lowercase\n    }\n\n    /**\n     * Encodes a raw byte array into a BASE64 <code>char[]</code> representation in accordance with RFC 2045.\n     *\n     * @param sArr    The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.\n     * @param lineSep Optional \"\\r\\n\" after 76 characters, unless end of file.<br>\n     *                No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a\n     *                little faster.\n     * @return A BASE64 encoded array. Never <code>null</code>.\n     */\n    private char[] encodeToChar(byte[] sArr, boolean lineSep) {\n\n        // Check special case\n        int sLen = sArr != null ? sArr.length : 0;\n        if (sLen == 0) {\n            return new char[0];\n        }\n\n        int eLen = (sLen / 3) * 3; // # of bytes that can encode evenly into 24-bit chunks\n        int left = sLen - eLen;    // # of bytes that remain after 24-bit chunking. Always 0, 1 or 2\n\n        int cCnt = (((sLen - 1) / 3 + 1) << 2); // # of base64-encoded characters including padding\n        int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned char array with padding and any line separators\n\n        int padCount = 0;\n        if (left == 2) {\n            padCount = 1;\n        } else if (left == 1) {\n            padCount = 2;\n        }\n\n        char[] dArr = new char[urlsafe ? (dLen - padCount) : dLen];\n\n        // Encode even 24-bits\n        for (int s = 0, d = 0, cc = 0; s < eLen; ) {\n\n            // Copy next three bytes into lower 24 bits of int, paying attention to sign.\n            int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);\n\n            // Encode the int into four chars\n            dArr[d++] = ALPHABET[(i >>> 18) & 0x3f];\n            dArr[d++] = ALPHABET[(i >>> 12) & 0x3f];\n            dArr[d++] = ALPHABET[(i >>> 6) & 0x3f];\n            dArr[d++] = ALPHABET[i & 0x3f];\n\n            // Add optional line separator\n            if (lineSep && ++cc == 19 && d < dLen - 2) {\n                dArr[d++] = '\\r';\n                dArr[d++] = '\\n';\n                cc = 0;\n            }\n        }\n\n        // Pad and encode last bits if source isn't even 24 bits.\n        if (left > 0) {\n            // Prepare the int\n            int i = ((sArr[eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sLen - 1] & 0xff) << 2) : 0);\n\n            // Set last four chars\n            dArr[dLen - 4] = ALPHABET[i >> 12];\n            dArr[dLen - 3] = ALPHABET[(i >>> 6) & 0x3f];\n            //dArr[dLen - 2] = left == 2 ? ALPHABET[i & 0x3f] : '=';\n            //dArr[dLen - 1] = '=';\n            if (left == 2) {\n                dArr[dLen - 2] = ALPHABET[i & 0x3f];\n            } else if (!urlsafe) { // if not urlsafe, we need to include the padding characters\n                dArr[dLen - 2] = '=';\n            }\n            if (!urlsafe) { // include padding\n                dArr[dLen - 1] = '=';\n            }\n        }\n        return dArr;\n    }\n\n    /*\n     * Decodes a BASE64 encoded char array. All illegal characters will be ignored and can handle both arrays with\n     * and without line separators.\n     *\n     * @param sArr The source array. <code>null</code> or length 0 will return an empty array.\n     * @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters\n     * (including '=') isn't divideable by 4.  (I.e. definitely corrupted).\n     *\n    public final byte[] decode(char[] sArr) {\n        // Check special case\n        int sLen = sArr != null ? sArr.length : 0;\n        if (sLen == 0) {\n            return new byte[0];\n        }\n\n        // Count illegal characters (including '\\r', '\\n') to know what size the returned array will be,\n        // so we don't have to reallocate & copy it later.\n        int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)\n        for (int i = 0; i < sLen; i++) { // If input is \"pure\" (I.e. no line separators or illegal chars) base64 this loop can be commented out.\n            if (IALPHABET[sArr[i]] < 0) {\n                sepCnt++;\n            }\n        }\n\n        // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.\n        if ((sLen - sepCnt) % 4 != 0) {\n            return null;\n        }\n\n        int pad = 0;\n        for (int i = sLen; i > 1 && IALPHABET[sArr[--i]] <= 0; ) {\n            if (sArr[i] == '=') {\n                pad++;\n            }\n        }\n\n        int len = ((sLen - sepCnt) * 6 >> 3) - pad;\n\n        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length\n\n        for (int s = 0, d = 0; d < len; ) {\n            // Assemble three bytes into an int from four \"valid\" characters.\n            int i = 0;\n            for (int j = 0; j < 4; j++) {   // j only increased if a valid char was found.\n                int c = IALPHABET[sArr[s++]];\n                if (c >= 0) {\n                    i |= c << (18 - j * 6);\n                } else {\n                    j--;\n                }\n            }\n            // Add the bytes\n            dArr[d++] = (byte) (i >> 16);\n            if (d < len) {\n                dArr[d++] = (byte) (i >> 8);\n                if (d < len) {\n                    dArr[d++] = (byte) i;\n                }\n            }\n        }\n        return dArr;\n    }\n    */\n\n    private int ctoi(char c) {\n        int i = c > IALPHABET_MAX_INDEX ? -1 : IALPHABET[c];\n        if (i < 0) {\n            String msg = \"Illegal \" + getName() + \" character: '\" + c + \"'\";\n            throw new DecodingException(msg);\n        }\n        return i;\n    }\n\n    /**\n     * Decodes a BASE64-encoded {@code CharSequence} that is known to be reasonably well formatted. The preconditions\n     * are:<br>\n     * + The sequence must have a line length of 76 chars OR no line separators at all (one line).<br>\n     * + Line separator must be \"\\r\\n\", as specified in RFC 2045\n     * + The sequence must not contain illegal characters within the encoded string<br>\n     * + The sequence CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>\n     *\n     * @param seq The source sequence. Length 0 will return an empty array. <code>null</code> will throw an exception.\n     * @return The decoded array of bytes. May be of length 0.\n     * @throws DecodingException on illegal input\n     */\n    byte[] decodeFast(CharSequence seq) throws DecodingException {\n\n        // Check special case\n        int sLen = seq != null ? seq.length() : 0;\n        if (sLen == 0) {\n            return new byte[0];\n        }\n\n        int sIx = 0, eIx = sLen - 1;    // Start and end index after trimming.\n\n        // Trim illegal chars from start\n        while (sIx < eIx && IALPHABET[seq.charAt(sIx)] < 0) {\n            sIx++;\n        }\n\n        // Trim illegal chars from end\n        while (eIx > 0 && IALPHABET[seq.charAt(eIx)] < 0) {\n            eIx--;\n        }\n\n        // get the padding count (=) (0, 1 or 2)\n        int pad = seq.charAt(eIx) == '=' ? (seq.charAt(eIx - 1) == '=' ? 2 : 1) : 0;  // Count '=' at end.\n        int cCnt = eIx - sIx + 1;   // Content count including possible separators\n        int sepCnt = sLen > 76 ? (seq.charAt(76) == '\\r' ? cCnt / 78 : 0) << 1 : 0;\n\n        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes\n        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length\n\n        // Decode all but the last 0 - 2 bytes.\n        int d = 0;\n        for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) {\n\n            // Assemble three bytes into an int from four \"valid\" characters.\n            int i = ctoi(seq.charAt(sIx++)) << 18 | ctoi(seq.charAt(sIx++)) << 12 | ctoi(seq.charAt(sIx++)) << 6 | ctoi(seq.charAt(sIx++));\n\n            // Add the bytes\n            dArr[d++] = (byte) (i >> 16);\n            dArr[d++] = (byte) (i >> 8);\n            dArr[d++] = (byte) i;\n\n            // If line separator, jump over it.\n            if (sepCnt > 0 && ++cc == 19) {\n                sIx += 2;\n                cc = 0;\n            }\n        }\n\n        if (d < len) {\n            // Decode last 1-3 bytes (incl '=') into 1-3 bytes\n            int i = 0;\n            for (int j = 0; sIx <= eIx - pad; j++) {\n                i |= ctoi(seq.charAt(sIx++)) << (18 - j * 6);\n            }\n\n            for (int r = 16; d < len; r -= 8) {\n                dArr[d++] = (byte) (i >> r);\n            }\n        }\n\n        return dArr;\n    }\n\n    // ****************************************************************************************\n    // *  byte[] version\n    // ****************************************************************************************\n\n    /*\n     * Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.\n     *\n     * @param sArr    The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.\n     * @param lineSep Optional \"\\r\\n\" after 76 characters, unless end of file.<br>\n     *                No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a\n     *                little faster.\n     * @return A BASE64 encoded array. Never <code>null</code>.\n     *\n    public final byte[] encodeToByte(byte[] sArr, boolean lineSep) {\n        return encodeToByte(sArr, 0, sArr != null ? sArr.length : 0, lineSep);\n    }\n\n    /**\n     * Encodes a raw byte array into a BASE64 <code>byte[]</code> representation i accordance with RFC 2045.\n     *\n     * @param sArr    The bytes to convert. If <code>null</code> an empty array will be returned.\n     * @param sOff    The starting position in the bytes to convert.\n     * @param sLen    The number of bytes to convert. If 0 an empty array will be returned.\n     * @param lineSep Optional \"\\r\\n\" after 76 characters, unless end of file.<br>\n     *                No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a\n     *                little faster.\n     * @return A BASE64 encoded array. Never <code>null</code>.\n     *\n    public final byte[] encodeToByte(byte[] sArr, int sOff, int sLen, boolean lineSep) {\n\n        // Check special case\n        if (sArr == null || sLen == 0) {\n            return new byte[0];\n        }\n\n        int eLen = (sLen / 3) * 3;                              // Length of even 24-bits.\n        int cCnt = ((sLen - 1) / 3 + 1) << 2;                   // Returned character count\n        int dLen = cCnt + (lineSep ? (cCnt - 1) / 76 << 1 : 0); // Length of returned array\n        byte[] dArr = new byte[dLen];\n\n        // Encode even 24-bits\n        for (int s = sOff, d = 0, cc = 0; s < sOff + eLen; ) {\n\n            // Copy next three bytes into lower 24 bits of int, paying attention to sign.\n            int i = (sArr[s++] & 0xff) << 16 | (sArr[s++] & 0xff) << 8 | (sArr[s++] & 0xff);\n\n            // Encode the int into four chars\n            dArr[d++] = (byte) ALPHABET[(i >>> 18) & 0x3f];\n            dArr[d++] = (byte) ALPHABET[(i >>> 12) & 0x3f];\n            dArr[d++] = (byte) ALPHABET[(i >>> 6) & 0x3f];\n            dArr[d++] = (byte) ALPHABET[i & 0x3f];\n\n            // Add optional line separator\n            if (lineSep && ++cc == 19 && d < dLen - 2) {\n                dArr[d++] = '\\r';\n                dArr[d++] = '\\n';\n                cc = 0;\n            }\n        }\n\n        // Pad and encode last bits if source isn't an even 24 bits.\n        int left = sLen - eLen; // 0 - 2.\n        if (left > 0) {\n            // Prepare the int\n            int i = ((sArr[sOff + eLen] & 0xff) << 10) | (left == 2 ? ((sArr[sOff + sLen - 1] & 0xff) << 2) : 0);\n\n            // Set last four chars\n            dArr[dLen - 4] = (byte) ALPHABET[i >> 12];\n            dArr[dLen - 3] = (byte) ALPHABET[(i >>> 6) & 0x3f];\n            dArr[dLen - 2] = left == 2 ? (byte) ALPHABET[i & 0x3f] : (byte) '=';\n            dArr[dLen - 1] = '=';\n        }\n        return dArr;\n    }\n\n    /**\n     * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with\n     * and without line separators.\n     *\n     * @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.\n     * @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters\n     * (including '=') isn't divideable by 4. (I.e. definitely corrupted).\n     *\n    public final byte[] decode(byte[] sArr) {\n        return decode(sArr, 0, sArr.length);\n    }\n\n    /**\n     * Decodes a BASE64 encoded byte array. All illegal characters will be ignored and can handle both arrays with\n     * and without line separators.\n     *\n     * @param sArr The source array. <code>null</code> will throw an exception.\n     * @param sOff The starting position in the source array.\n     * @param sLen The number of bytes to decode from the source array. Length 0 will return an empty array.\n     * @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters\n     * (including '=') isn't divideable by 4. (I.e. definitely corrupted).\n     *\n    public final byte[] decode(byte[] sArr, int sOff, int sLen) {\n\n        // Count illegal characters (including '\\r', '\\n') to know what size the returned array will be,\n        // so we don't have to reallocate & copy it later.\n        int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)\n        for (int i = 0; i < sLen; i++) {     // If input is \"pure\" (I.e. no line separators or illegal chars) base64 this loop can be commented out.\n            if (IALPHABET[sArr[sOff + i] & 0xff] < 0) {\n                sepCnt++;\n            }\n        }\n\n        // Check so that legal chars (including '=') are evenly divisible by 4 as specified in RFC 2045.\n        if ((sLen - sepCnt) % 4 != 0) {\n            return null;\n        }\n\n        int pad = 0;\n        for (int i = sLen; i > 1 && IALPHABET[sArr[sOff + --i] & 0xff] <= 0; ) {\n            if (sArr[sOff + i] == '=') {\n                pad++;\n            }\n        }\n\n        int len = ((sLen - sepCnt) * 6 >> 3) - pad;\n\n        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length\n\n        for (int s = 0, d = 0; d < len; ) {\n            // Assemble three bytes into an int from four \"valid\" characters.\n            int i = 0;\n            for (int j = 0; j < 4; j++) {   // j only increased if a valid char was found.\n                int c = IALPHABET[sArr[sOff + s++] & 0xff];\n                if (c >= 0) {\n                    i |= c << (18 - j * 6);\n                } else {\n                    j--;\n                }\n            }\n\n            // Add the bytes\n            dArr[d++] = (byte) (i >> 16);\n            if (d < len) {\n                dArr[d++] = (byte) (i >> 8);\n                if (d < len) {\n                    dArr[d++] = (byte) i;\n                }\n            }\n        }\n\n        return dArr;\n    }\n\n\n    /*\n     * Decodes a BASE64 encoded byte array that is known to be reasonably well formatted. The method is about twice as\n     * fast as {@link #decode(byte[])}. The preconditions are:<br>\n     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>\n     * + Line separator must be \"\\r\\n\", as specified in RFC 2045\n     * + The array must not contain illegal characters within the encoded string<br>\n     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>\n     *\n     * @param sArr The source array. Length 0 will return an empty array. <code>null</code> will throw an exception.\n     * @return The decoded array of bytes. May be of length 0.\n     *\n    public final byte[] decodeFast(byte[] sArr) {\n\n        // Check special case\n        int sLen = sArr.length;\n        if (sLen == 0) {\n            return new byte[0];\n        }\n\n        int sIx = 0, eIx = sLen - 1;    // Start and end index after trimming.\n\n        // Trim illegal chars from start\n        while (sIx < eIx && IALPHABET[sArr[sIx] & 0xff] < 0) {\n            sIx++;\n        }\n\n        // Trim illegal chars from end\n        while (eIx > 0 && IALPHABET[sArr[eIx] & 0xff] < 0) {\n            eIx--;\n        }\n\n        // get the padding count (=) (0, 1 or 2)\n        int pad = sArr[eIx] == '=' ? (sArr[eIx - 1] == '=' ? 2 : 1) : 0;  // Count '=' at end.\n        int cCnt = eIx - sIx + 1;   // Content count including possible separators\n        int sepCnt = sLen > 76 ? (sArr[76] == '\\r' ? cCnt / 78 : 0) << 1 : 0;\n\n        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes\n        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length\n\n        // Decode all but the last 0 - 2 bytes.\n        int d = 0;\n        for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) {\n\n            // Assemble three bytes into an int from four \"valid\" characters.\n            int i = IALPHABET[sArr[sIx++]] << 18 | IALPHABET[sArr[sIx++]] << 12 | IALPHABET[sArr[sIx++]] << 6 | IALPHABET[sArr[sIx++]];\n\n            // Add the bytes\n            dArr[d++] = (byte) (i >> 16);\n            dArr[d++] = (byte) (i >> 8);\n            dArr[d++] = (byte) i;\n\n            // If line separator, jump over it.\n            if (sepCnt > 0 && ++cc == 19) {\n                sIx += 2;\n                cc = 0;\n            }\n        }\n\n        if (d < len) {\n            // Decode last 1-3 bytes (incl '=') into 1-3 bytes\n            int i = 0;\n            for (int j = 0; sIx <= eIx - pad; j++) {\n                i |= IALPHABET[sArr[sIx++]] << (18 - j * 6);\n            }\n\n            for (int r = 16; d < len; r -= 8) {\n                dArr[d++] = (byte) (i >> r);\n            }\n        }\n\n        return dArr;\n    }\n    */\n\n    // ****************************************************************************************\n    // * String version\n    // ****************************************************************************************\n\n    /**\n     * Encodes a raw byte array into a BASE64 <code>String</code> representation i accordance with RFC 2045.\n     *\n     * @param sArr    The bytes to convert. If <code>null</code> or length 0 an empty array will be returned.\n     * @param lineSep Optional \"\\r\\n\" after 76 characters, unless end of file.<br>\n     *                No line separator will be in breach of RFC 2045 which specifies max 76 per line but will be a\n     *                little faster.\n     * @return A BASE64 encoded array. Never <code>null</code>.\n     */\n    String encodeToString(byte[] sArr, boolean lineSep) {\n        // Reuse char[] since we can't create a String incrementally anyway and StringBuffer/Builder would be slower.\n        return new String(encodeToChar(sArr, lineSep));\n    }\n\n    /*\n     * Decodes a BASE64 encoded <code>String</code>. All illegal characters will be ignored and can handle both strings with\n     * and without line separators.<br>\n     * <b>Note!</b> It can be up to about 2x the speed to call <code>decode(str.toCharArray())</code> instead. That\n     * will create a temporary array though. This version will use <code>str.charAt(i)</code> to iterate the string.\n     *\n     * @param str The source string. <code>null</code> or length 0 will return an empty array.\n     * @return The decoded array of bytes. May be of length 0. Will be <code>null</code> if the legal characters\n     * (including '=') isn't divideable by 4.  (I.e. definitely corrupted).\n     *\n    public final byte[] decode(String str) {\n\n        // Check special case\n        int sLen = str != null ? str.length() : 0;\n        if (sLen == 0) {\n            return new byte[0];\n        }\n\n        // Count illegal characters (including '\\r', '\\n') to know what size the returned array will be,\n        // so we don't have to reallocate & copy it later.\n        int sepCnt = 0; // Number of separator characters. (Actually illegal characters, but that's a bonus...)\n        for (int i = 0; i < sLen; i++) { // If input is \"pure\" (I.e. no line separators or illegal chars) base64 this loop can be commented out.\n            if (IALPHABET[str.charAt(i)] < 0) {\n                sepCnt++;\n            }\n        }\n\n        // Check so that legal chars (including '=') are evenly divideable by 4 as specified in RFC 2045.\n        if ((sLen - sepCnt) % 4 != 0) {\n            return null;\n        }\n\n        // Count '=' at end\n        int pad = 0;\n        for (int i = sLen; i > 1 && IALPHABET[str.charAt(--i)] <= 0; ) {\n            if (str.charAt(i) == '=') {\n                pad++;\n            }\n        }\n\n        int len = ((sLen - sepCnt) * 6 >> 3) - pad;\n\n        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length\n\n        for (int s = 0, d = 0; d < len; ) {\n            // Assemble three bytes into an int from four \"valid\" characters.\n            int i = 0;\n            for (int j = 0; j < 4; j++) {   // j only increased if a valid char was found.\n                int c = IALPHABET[str.charAt(s++)];\n                if (c >= 0) {\n                    i |= c << (18 - j * 6);\n                } else {\n                    j--;\n                }\n            }\n            // Add the bytes\n            dArr[d++] = (byte) (i >> 16);\n            if (d < len) {\n                dArr[d++] = (byte) (i >> 8);\n                if (d < len) {\n                    dArr[d++] = (byte) i;\n                }\n            }\n        }\n        return dArr;\n    }\n\n    /**\n     * Decodes a BASE64 encoded string that is known to be resonably well formatted. The method is about twice as\n     * fast as {@link #decode(String)}. The preconditions are:<br>\n     * + The array must have a line length of 76 chars OR no line separators at all (one line).<br>\n     * + Line separator must be \"\\r\\n\", as specified in RFC 2045\n     * + The array must not contain illegal characters within the encoded string<br>\n     * + The array CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>\n     *\n     * @param s The source string. Length 0 will return an empty array. <code>null</code> will throw an exception.\n     * @return The decoded array of bytes. May be of length 0.\n     *\n    public final byte[] decodeFast(String s) {\n\n        // Check special case\n        int sLen = s.length();\n        if (sLen == 0) {\n            return new byte[0];\n        }\n\n        int sIx = 0, eIx = sLen - 1;    // Start and end index after trimming.\n\n        // Trim illegal chars from start\n        while (sIx < eIx && IALPHABET[s.charAt(sIx) & 0xff] < 0) {\n            sIx++;\n        }\n\n        // Trim illegal chars from end\n        while (eIx > 0 && IALPHABET[s.charAt(eIx) & 0xff] < 0) {\n            eIx--;\n        }\n\n        // get the padding count (=) (0, 1 or 2)\n        int pad = s.charAt(eIx) == '=' ? (s.charAt(eIx - 1) == '=' ? 2 : 1) : 0;  // Count '=' at end.\n        int cCnt = eIx - sIx + 1;   // Content count including possible separators\n        int sepCnt = sLen > 76 ? (s.charAt(76) == '\\r' ? cCnt / 78 : 0) << 1 : 0;\n\n        int len = ((cCnt - sepCnt) * 6 >> 3) - pad; // The number of decoded bytes\n        byte[] dArr = new byte[len];       // Preallocate byte[] of exact length\n\n        // Decode all but the last 0 - 2 bytes.\n        int d = 0;\n        for (int cc = 0, eLen = (len / 3) * 3; d < eLen; ) {\n            // Assemble three bytes into an int from four \"valid\" characters.\n            int i = IALPHABET[s.charAt(sIx++)] << 18 | IALPHABET[s.charAt(sIx++)] << 12 | IALPHABET[s.charAt(sIx++)] << 6 | IALPHABET[s.charAt(sIx++)];\n\n            // Add the bytes\n            dArr[d++] = (byte) (i >> 16);\n            dArr[d++] = (byte) (i >> 8);\n            dArr[d++] = (byte) i;\n\n            // If line separator, jump over it.\n            if (sepCnt > 0 && ++cc == 19) {\n                sIx += 2;\n                cc = 0;\n            }\n        }\n\n        if (d < len) {\n            // Decode last 1-3 bytes (incl '=') into 1-3 bytes\n            int i = 0;\n            for (int j = 0; sIx <= eIx - pad; j++) {\n                i |= IALPHABET[s.charAt(sIx++)] << (18 - j * 6);\n            }\n\n            for (int r = 16; d < len; r -= 8) {\n                dArr[d++] = (byte) (i >> r);\n            }\n        }\n\n        return dArr;\n    }\n    */\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Base64Decoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-4\">Base64</a> decoder guaranteed to\n * work in all &gt;= Java 7 JDK and Android environments.\n *\n * @since 0.10.0\n */\nclass Base64Decoder extends Base64Support implements Decoder<CharSequence, byte[]> {\n\n    Base64Decoder() {\n        super(Base64.DEFAULT);\n    }\n\n    Base64Decoder(Base64 base64) {\n        super(base64);\n    }\n\n    @Override\n    public byte[] decode(CharSequence s) throws DecodingException {\n        Assert.notNull(s, \"String argument cannot be null\");\n        return this.base64.decodeFast(s);\n    }\n}"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Base64Encoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-4\">Base64</a> encoder guaranteed to\n * work in all &gt;= Java 7 JDK and Android environments.\n *\n * @since 0.10.0\n */\nclass Base64Encoder extends Base64Support implements Encoder<byte[], String> {\n\n    Base64Encoder() {\n        this(Base64.DEFAULT);\n    }\n\n    Base64Encoder(Base64 base64) {\n        super(base64);\n    }\n\n    @Override\n    public String encode(byte[] bytes) throws EncodingException {\n        Assert.notNull(bytes, \"byte array argument cannot be null\");\n        return this.base64.encodeToString(bytes, false);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Base64Support.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * Parent class for Base64 encoders and decoders.\n *\n * @since 0.10.0\n */\nclass Base64Support {\n\n    protected final Base64 base64;\n\n    Base64Support(Base64 base64) {\n        Assert.notNull(base64, \"Base64 argument cannot be null\");\n        this.base64 = base64;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Base64UrlDecoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-5\">Base64Url</a> decoder guaranteed to\n * work in all &gt;= Java 7 JDK and Android environments.\n *\n * @since 0.10.0\n */\nclass Base64UrlDecoder extends Base64Decoder {\n\n    Base64UrlDecoder() {\n        super(Base64.URL_SAFE);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Base64UrlEncoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-5\">Base64Url</a> encoder guaranteed to\n * work in all &gt;= Java 7 JDK and Android environments.\n *\n * @since 0.10.0\n */\nclass Base64UrlEncoder extends Base64Encoder {\n\n    Base64UrlEncoder() {\n        super(Base64.URL_SAFE);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/CodecException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * An exception thrown when encountering a problem during encoding or decoding.\n *\n * @since 0.10.0\n */\npublic class CodecException extends IOException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public CodecException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public CodecException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/CompressionAlgorithm.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.JwtBuilder;\nimport io.jsonwebtoken.JwtParserBuilder;\nimport io.jsonwebtoken.Jwts;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n/**\n * Compresses and decompresses byte streams.\n *\n * <p><b>&quot;zip&quot; identifier</b></p>\n *\n * <p>{@code CompressionAlgorithm} extends {@code Identifiable}; the value returned from\n * {@link Identifiable#getId() getId()} will be used as the JWT\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.3\"><code>zip</code></a> header value.</p>\n *\n * <p><b>Custom Implementations</b></p>\n *\n * <p>A custom implementation of this interface may be used when creating a JWT by calling the\n * {@link JwtBuilder#compressWith(CompressionAlgorithm)} method.</p>\n *\n * <p>To ensure that parsing is possible, the parser must be aware of the implementation by adding it to the\n * {@link JwtParserBuilder#zip()} collection during parser construction.</p>\n *\n * @see Jwts.ZIP#DEF\n * @see Jwts.ZIP#GZIP\n * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.3\">JSON Web Encryption Compression Algorithms Registry</a>\n * @since 0.12.0\n */\npublic interface CompressionAlgorithm extends Identifiable {\n\n    /**\n     * Wraps the specified {@code OutputStream} to ensure any stream bytes are compressed as they are written.\n     *\n     * @param out the stream to wrap for compression\n     * @return the stream to use for writing\n     */\n    OutputStream compress(OutputStream out);\n\n    /**\n     * Wraps the specified {@code InputStream} to ensure any stream bytes are decompressed as they are read.\n     *\n     * @param in the stream to wrap for decompression\n     * @return the stream to use for reading\n     */\n    InputStream decompress(InputStream in);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Decoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * A decoder converts an already-encoded data value to a desired data type.\n *\n * @param <T> decoding input type\n * @param <R> decoding output type\n * @since 0.10.0\n */\npublic interface Decoder<T, R> {\n\n    /**\n     * Convert the specified encoded data value into the desired data type.\n     *\n     * @param t the encoded data\n     * @return the resulting expected data\n     * @throws DecodingException if there is a problem during decoding.\n     */\n    R decode(T t) throws DecodingException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Decoders.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * Constant definitions for various decoding algorithms.\n *\n * @see #BASE64\n * @see #BASE64URL\n * @since 0.10.0\n */\npublic final class Decoders {\n\n    /**\n     * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-4\">Base64</a> decoder guaranteed to\n     * work in all &gt;= Java 7 JDK and Android environments.\n     */\n    public static final Decoder<CharSequence, byte[]> BASE64 = new ExceptionPropagatingDecoder<>(new Base64Decoder());\n\n    /**\n     * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-5\">Base64Url</a> decoder guaranteed to\n     * work in all &gt;= Java 7 JDK and Android environments.\n     */\n    public static final Decoder<CharSequence, byte[]> BASE64URL = new ExceptionPropagatingDecoder<>(new Base64UrlDecoder());\n\n    private Decoders() { //prevent instantiation\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/DecodingException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * An exception thrown when encountering a problem during decoding.\n *\n * @since 0.10.0\n */\npublic class DecodingException extends CodecException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public DecodingException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public DecodingException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/DeserializationException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * Exception thrown when reconstituting a serialized byte array into a Java object.\n *\n * @since 0.10.0\n */\npublic class DeserializationException extends SerialException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param msg the message explaining why the exception is thrown.\n     */\n    public DeserializationException(String msg) {\n        super(msg);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public DeserializationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Deserializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport java.io.Reader;\n\n/**\n * A {@code Deserializer} is able to convert serialized byte streams into Java objects.\n *\n * @param <T> the type of object to be returned as a result of deserialization.\n * @since 0.10.0\n */\npublic interface Deserializer<T> {\n\n    /**\n     * Convert the specified formatted data byte array into a Java object.\n     *\n     * @param bytes the formatted data byte array to convert\n     * @return the reconstituted Java object\n     * @throws DeserializationException if there is a problem converting the byte array to an object.\n     * @deprecated since 0.12.0 in favor of {@link #deserialize(Reader)}\n     */\n    @Deprecated\n    T deserialize(byte[] bytes) throws DeserializationException;\n\n    /**\n     * Reads the specified character stream and returns the corresponding Java object.\n     *\n     * @param reader the reader to use to read the character stream\n     * @return the deserialized Java object\n     * @throws DeserializationException if there is a problem reading the stream or creating the expected Java object\n     * @since 0.12.0\n     */\n    T deserialize(Reader reader) throws DeserializationException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Encoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * An encoder converts data of one type into another formatted data value.\n *\n * @param <T> the type of data to convert\n * @param <R> the type of the resulting formatted data\n * @since 0.10.0\n */\npublic interface Encoder<T, R> {\n\n    /**\n     * Convert the specified data into another formatted data value.\n     *\n     * @param t the data to convert\n     * @return the resulting formatted data value\n     * @throws EncodingException if there is a problem during encoding\n     */\n    R encode(T t) throws EncodingException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Encoders.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * Constant definitions for various encoding algorithms.\n *\n * @see #BASE64\n * @see #BASE64URL\n * @since 0.10.0\n */\npublic final class Encoders {\n\n    /**\n     * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-4\">Base64</a> encoder guaranteed to\n     * work in all &gt;= Java 7 JDK and Android environments.\n     */\n    public static final Encoder<byte[], String> BASE64 = new ExceptionPropagatingEncoder<>(new Base64Encoder());\n\n    /**\n     * Very fast <a href=\"https://datatracker.ietf.org/doc/html/rfc4648#section-5\">Base64Url</a> encoder guaranteed to\n     * work in all &gt;= Java 7 JDK and Android environments.\n     */\n    public static final Encoder<byte[], String> BASE64URL = new ExceptionPropagatingEncoder<>(new Base64UrlEncoder());\n\n    private Encoders() { //prevent instantiation\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/EncodingException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * An exception thrown when encountering a problem during encoding.\n *\n * @since 0.10.0\n */\npublic class EncodingException extends CodecException {\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public EncodingException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/ExceptionPropagatingDecoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * Decoder that ensures any exceptions thrown that are <em>not</em> {@link DecodingException}s are wrapped\n * and re-thrown as a {@code DecodingException}.\n *\n * @since 0.10.0\n */\nclass ExceptionPropagatingDecoder<T, R> implements Decoder<T, R> {\n\n    private final Decoder<T, R> decoder;\n\n    /**\n     * Creates a new instance, wrapping the specified {@code decoder} to invoke during {@link #decode(Object)}.\n     *\n     * @param decoder the decoder to wrap and call during {@link #decode(Object)}\n     */\n    ExceptionPropagatingDecoder(Decoder<T, R> decoder) {\n        Assert.notNull(decoder, \"Decoder cannot be null.\");\n        this.decoder = decoder;\n    }\n\n    /**\n     * Decode the specified encoded data, delegating to the wrapped Decoder, wrapping any\n     * non-{@link DecodingException} as a {@code DecodingException}.\n     *\n     * @param t the encoded data\n     * @return the decoded data\n     * @throws DecodingException if there is an unexpected problem during decoding.\n     */\n    @Override\n    public R decode(T t) throws DecodingException {\n        Assert.notNull(t, \"Decode argument cannot be null.\");\n        try {\n            return decoder.decode(t);\n        } catch (DecodingException e) {\n            throw e; //propagate\n        } catch (Exception e) {\n            String msg = \"Unable to decode input: \" + e.getMessage();\n            throw new DecodingException(msg, e);\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/ExceptionPropagatingEncoder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * Encoder that ensures any exceptions thrown that are <em>not</em> {@link EncodingException}s are wrapped\n * and re-thrown as a {@code EncodingException}.\n *\n * @since 0.10.0\n */\nclass ExceptionPropagatingEncoder<T, R> implements Encoder<T, R> {\n\n    private final Encoder<T, R> encoder;\n\n    /**\n     * Creates a new instance, wrapping the specified {@code encoder} to invoke during {@link #encode(Object)}.\n     *\n     * @param encoder the encoder to wrap and call during {@link #encode(Object)}\n     */\n    ExceptionPropagatingEncoder(Encoder<T, R> encoder) {\n        Assert.notNull(encoder, \"Encoder cannot be null.\");\n        this.encoder = encoder;\n    }\n\n    /**\n     * Encoded the specified  data, delegating to the wrapped Encoder, wrapping any\n     * non-{@link EncodingException} as an {@code EncodingException}.\n     *\n     * @param t the data to encode\n     * @return the encoded data\n     * @throws EncodingException if there is an unexpected problem during encoding.\n     */\n    @Override\n    public R encode(T t) throws EncodingException {\n        Assert.notNull(t, \"Encode argument cannot be null.\");\n        try {\n            return this.encoder.encode(t);\n        } catch (EncodingException e) {\n            throw e; //propagate\n        } catch (Exception e) {\n            String msg = \"Unable to encode input: \" + e.getMessage();\n            throw new EncodingException(msg, e);\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/IOException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.JwtException;\n\n/**\n * JJWT's base exception for problems during data input or output operations, such as serialization,\n * deserialization, marshalling, unmarshalling, etc.\n *\n * @since 0.10.0\n */\npublic class IOException extends JwtException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param msg the message explaining why the exception is thrown.\n     */\n    public IOException(String msg) {\n        super(msg);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public IOException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Parser.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport java.io.InputStream;\nimport java.io.Reader;\n\n/**\n * A Parser converts a character stream into a Java object.\n *\n * @param <T> the instance type created after parsing\n * @since 0.12.0\n */\npublic interface Parser<T> {\n\n    /**\n     * Parse the specified character sequence into a Java object.\n     *\n     * @param input the character sequence to parse into a Java object.\n     * @return the Java object represented by the specified {@code input} stream.\n     */\n    T parse(CharSequence input);\n\n    /**\n     * Parse the specified character sequence with the specified bounds into a Java object.\n     *\n     * @param input The character sequence, may be {@code null}\n     * @param start The start index in the character sequence, inclusive\n     * @param end   The end index in the character sequence, exclusive\n     * @return the Java object represented by the specified sequence bounds\n     * @throws IllegalArgumentException if the start index is negative, or if the end index is smaller than the start index\n     */\n    T parse(CharSequence input, int start, int end);\n\n    /**\n     * Parse the specified character sequence into a Java object.\n     *\n     * @param reader the reader to use to parse a Java object.\n     * @return the Java object represented by the specified {@code input} stream.\n     */\n    T parse(Reader reader);\n\n    /**\n     * Parses the specified {@link InputStream} assuming {@link java.nio.charset.StandardCharsets#UTF_8 UTF_8} encoding.\n     * This is a convenience alias for:\n     *\n     * <blockquote><pre>{@link #parse(Reader) parse}(new {@link java.io.InputStreamReader\n     * InputStreamReader}(in, {@link java.nio.charset.StandardCharsets#UTF_8\n     * StandardCharsets.UTF_8});</pre></blockquote>\n     *\n     * @param in the UTF-8 InputStream.\n     * @return the Java object represented by the specified {@link InputStream}.\n     */\n    T parse(InputStream in);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/ParserBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport io.jsonwebtoken.lang.Builder;\n\nimport java.security.Provider;\nimport java.util.Map;\n\n/**\n * A {@code ParserBuilder} configures and creates new {@link Parser} instances.\n *\n * @param <T> The resulting parser's {@link Parser#parse parse} output type\n * @param <B> builder type used for method chaining\n * @since 0.12.0\n */\npublic interface ParserBuilder<T, B extends ParserBuilder<T, B>> extends Builder<Parser<T>> {\n\n    /**\n     * Sets the JCA Provider to use during cryptographic operations, or {@code null} if the\n     * JCA subsystem preferred provider should be used.\n     *\n     * @param provider the JCA Provider to use during cryptographic key factory operations, or {@code null}\n     *                 if the JCA subsystem preferred provider should be used.\n     * @return the builder for method chaining.\n     */\n    B provider(Provider provider);\n\n    /**\n     * Uses the specified {@code Deserializer} to convert JSON Strings (UTF-8 byte streams) into Java Map objects.  The\n     * resulting Maps are then used to construct respective JWT objects (JWTs, JWKs, etc).\n     *\n     * <p>If this method is not called, JJWT will use whatever Deserializer it can find at runtime, checking for the\n     * presence of well-known implementations such as Jackson, Gson, and org.json.  If one of these is not found\n     * in the runtime classpath, an exception will be thrown when the {@link #build()} method is called.\n     *\n     * @param deserializer the Deserializer to use when converting JSON Strings (UTF-8 byte streams) into Map objects.\n     * @return the builder for method chaining.\n     */\n    B json(Deserializer<Map<String, ?>> deserializer);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/SerialException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * An exception thrown during serialization or deserialization.\n *\n * @since 0.10.0\n */\npublic class SerialException extends IOException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param msg the message explaining why the exception is thrown.\n     */\n    public SerialException(String msg) {\n        super(msg);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public SerialException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/SerializationException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\n/**\n * Exception thrown when converting a Java object to a formatted byte array.\n *\n * @since 0.10.0\n */\npublic class SerializationException extends SerialException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param msg the message explaining why the exception is thrown.\n     */\n    public SerializationException(String msg) {\n        super(msg);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public SerializationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/io/Serializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io;\n\nimport java.io.OutputStream;\n\n/**\n * A {@code Serializer} is able to convert a Java object into a formatted byte stream.  It is expected this byte stream\n * can be reconstituted back into a Java object with a matching {@link Deserializer}.\n *\n * @param <T> The type of object to serialize.\n * @since 0.10.0\n */\npublic interface Serializer<T> {\n\n    /**\n     * Converts the specified Java object into a formatted data byte array.\n     *\n     * @param t the object to serialize\n     * @return the serialized byte array representing the specified object.\n     * @throws SerializationException if there is a problem converting the object to a byte array.\n     * @deprecated since 0.12.0 in favor of {@link #serialize(Object, OutputStream)}\n     */\n    @Deprecated\n    byte[] serialize(T t) throws SerializationException;\n\n    /**\n     * Converts the specified Java object into a formatted data byte stream, writing the bytes to the specified\n     * {@code out}put stream.\n     *\n     * @param t   the object to convert to a byte stream\n     * @param out the stream to write to\n     * @throws SerializationException if there is a problem converting the object to a byte stream or writing the\n     *                                bytes to the {@code out}put stream.\n     * @since 0.12.0\n     */\n    void serialize(T t, OutputStream out) throws SerializationException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Arrays.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.lang.reflect.Array;\nimport java.util.List;\n\n/**\n * Utility methods to work with array instances.\n *\n * @since 0.6\n */\npublic final class Arrays {\n\n    private Arrays() {\n    } //prevent instantiation\n\n    /**\n     * Returns the length of the array, or {@code 0} if the array is {@code null}.\n     *\n     * @param a   the possibly-null array\n     * @param <T> the type of elements in the array\n     * @return the length of the array, or zero if the array is null.\n     */\n    public static <T> int length(T[] a) {\n        return a == null ? 0 : a.length;\n    }\n\n    /**\n     * Converts the specified array to a {@link List}. If the array is empty, an empty list will be returned.\n     *\n     * @param a   the array to represent as a list\n     * @param <T> the type of elements in the array\n     * @return the array as a list, or an empty list if the array is empty.\n     */\n    public static <T> List<T> asList(T[] a) {\n        return Objects.isEmpty(a) ? Collections.<T>emptyList() : java.util.Arrays.asList(a);\n    }\n\n    /**\n     * Returns the length of the specified byte array, or {@code 0} if the byte array is {@code null}.\n     *\n     * @param bytes the array to check\n     * @return the length of the specified byte array, or {@code 0} if the byte array is {@code null}.\n     */\n    public static int length(byte[] bytes) {\n        return bytes != null ? bytes.length : 0;\n    }\n\n    /**\n     * Returns the byte array unaltered if it is non-null and has a positive length, otherwise {@code null}.\n     *\n     * @param bytes the byte array to check.\n     * @return the byte array unaltered if it is non-null and has a positive length, otherwise {@code null}.\n     */\n    public static byte[] clean(byte[] bytes) {\n        return length(bytes) > 0 ? bytes : null;\n    }\n\n    /**\n     * Creates a shallow copy of the specified object or array.\n     *\n     * @param obj the object to copy\n     * @return a shallow copy of the specified object or array.\n     */\n    public static Object copy(Object obj) {\n        if (obj == null) {\n            return null;\n        }\n        Assert.isTrue(Objects.isArray(obj), \"Argument must be an array.\");\n        if (obj instanceof Object[]) {\n            return ((Object[]) obj).clone();\n        }\n        if (obj instanceof boolean[]) {\n            return ((boolean[]) obj).clone();\n        }\n        if (obj instanceof byte[]) {\n            return ((byte[]) obj).clone();\n        }\n        if (obj instanceof char[]) {\n            return ((char[]) obj).clone();\n        }\n        if (obj instanceof double[]) {\n            return ((double[]) obj).clone();\n        }\n        if (obj instanceof float[]) {\n            return ((float[]) obj).clone();\n        }\n        if (obj instanceof int[]) {\n            return ((int[]) obj).clone();\n        }\n        if (obj instanceof long[]) {\n            return ((long[]) obj).clone();\n        }\n        if (obj instanceof short[]) {\n            return ((short[]) obj).clone();\n        }\n        Class<?> componentType = obj.getClass().getComponentType();\n        int length = Array.getLength(obj);\n        Object[] copy = (Object[]) Array.newInstance(componentType, length);\n        for (int i = 0; i < length; i++) {\n            copy[i] = Array.get(obj, i);\n        }\n        return copy;\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Assert.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * Utility methods for providing argument and state assertions to reduce repeating these patterns and otherwise\n * increasing cyclomatic complexity.\n */\npublic final class Assert {\n\n    private Assert() {\n    } //prevent instantiation\n\n    /**\n     * Assert a boolean expression, throwing <code>IllegalArgumentException</code>\n     * if the test result is <code>false</code>.\n     * <pre class=\"code\">Assert.isTrue(i &gt; 0, \"The value must be greater than zero\");</pre>\n     *\n     * @param expression a boolean expression\n     * @param message    the exception message to use if the assertion fails\n     * @throws IllegalArgumentException if expression is <code>false</code>\n     */\n    public static void isTrue(boolean expression, String message) {\n        if (!expression) {\n            throw new IllegalArgumentException(message);\n        }\n    }\n\n    /**\n     * Assert a boolean expression, throwing <code>IllegalArgumentException</code>\n     * if the test result is <code>false</code>.\n     * <pre class=\"code\">Assert.isTrue(i &gt; 0);</pre>\n     *\n     * @param expression a boolean expression\n     * @throws IllegalArgumentException if expression is <code>false</code>\n     */\n    public static void isTrue(boolean expression) {\n        isTrue(expression, \"[Assertion failed] - this expression must be true\");\n    }\n\n    /**\n     * Assert that an object is <code>null</code> .\n     * <pre class=\"code\">Assert.isNull(value, \"The value must be null\");</pre>\n     *\n     * @param object  the object to check\n     * @param message the exception message to use if the assertion fails\n     * @throws IllegalArgumentException if the object is not <code>null</code>\n     */\n    public static void isNull(Object object, String message) {\n        if (object != null) {\n            throw new IllegalArgumentException(message);\n        }\n    }\n\n    /**\n     * Assert that an object is <code>null</code> .\n     * <pre class=\"code\">Assert.isNull(value);</pre>\n     *\n     * @param object the object to check\n     * @throws IllegalArgumentException if the object is not <code>null</code>\n     */\n    public static void isNull(Object object) {\n        isNull(object, \"[Assertion failed] - the object argument must be null\");\n    }\n\n    /**\n     * Assert that an object is not <code>null</code> .\n     * <pre class=\"code\">Assert.notNull(clazz, \"The class must not be null\");</pre>\n     *\n     * @param object  the object to check\n     * @param <T>     the type of object\n     * @param message the exception message to use if the assertion fails\n     * @return the non-null object\n     * @throws IllegalArgumentException if the object is <code>null</code>\n     */\n    public static <T> T notNull(T object, String message) {\n        if (object == null) {\n            throw new IllegalArgumentException(message);\n        }\n        return object;\n    }\n\n    /**\n     * Assert that an object is not <code>null</code> .\n     * <pre class=\"code\">Assert.notNull(clazz);</pre>\n     *\n     * @param object the object to check\n     * @throws IllegalArgumentException if the object is <code>null</code>\n     */\n    public static void notNull(Object object) {\n        notNull(object, \"[Assertion failed] - this argument is required; it must not be null\");\n    }\n\n    /**\n     * Assert that the given String is not empty; that is,\n     * it must not be <code>null</code> and not the empty String.\n     * <pre class=\"code\">Assert.hasLength(name, \"Name must not be empty\");</pre>\n     *\n     * @param text    the String to check\n     * @param message the exception message to use if the assertion fails\n     * @see Strings#hasLength\n     */\n    public static void hasLength(String text, String message) {\n        if (!Strings.hasLength(text)) {\n            throw new IllegalArgumentException(message);\n        }\n    }\n\n    /**\n     * Assert that the given String is not empty; that is,\n     * it must not be <code>null</code> and not the empty String.\n     * <pre class=\"code\">Assert.hasLength(name);</pre>\n     *\n     * @param text the String to check\n     * @see Strings#hasLength\n     */\n    public static void hasLength(String text) {\n        hasLength(text,\n                \"[Assertion failed] - this String argument must have length; it must not be null or empty\");\n    }\n\n    /**\n     * Assert that the given String has valid text content; that is, it must not\n     * be <code>null</code> and must contain at least one non-whitespace character.\n     * <pre class=\"code\">Assert.hasText(name, \"'name' must not be empty\");</pre>\n     *\n     * @param <T>     the type of CharSequence\n     * @param text    the CharSequence to check\n     * @param message the exception message to use if the assertion fails\n     * @return the CharSequence if it has text\n     * @see Strings#hasText\n     */\n    public static <T extends CharSequence> T hasText(T text, String message) {\n        if (!Strings.hasText(text)) {\n            throw new IllegalArgumentException(message);\n        }\n        return text;\n    }\n\n    /**\n     * Assert that the given String has valid text content; that is, it must not\n     * be <code>null</code> and must contain at least one non-whitespace character.\n     * <pre class=\"code\">Assert.hasText(name, \"'name' must not be empty\");</pre>\n     *\n     * @param text the String to check\n     * @see Strings#hasText\n     */\n    public static void hasText(String text) {\n        hasText(text,\n                \"[Assertion failed] - this String argument must have text; it must not be null, empty, or blank\");\n    }\n\n    /**\n     * Assert that the given text does not contain the given substring.\n     * <pre class=\"code\">Assert.doesNotContain(name, \"rod\", \"Name must not contain 'rod'\");</pre>\n     *\n     * @param textToSearch the text to search\n     * @param substring    the substring to find within the text\n     * @param message      the exception message to use if the assertion fails\n     */\n    public static void doesNotContain(String textToSearch, String substring, String message) {\n        if (Strings.hasLength(textToSearch) && Strings.hasLength(substring) &&\n                textToSearch.indexOf(substring) != -1) {\n            throw new IllegalArgumentException(message);\n        }\n    }\n\n    /**\n     * Assert that the given text does not contain the given substring.\n     * <pre class=\"code\">Assert.doesNotContain(name, \"rod\");</pre>\n     *\n     * @param textToSearch the text to search\n     * @param substring    the substring to find within the text\n     */\n    public static void doesNotContain(String textToSearch, String substring) {\n        doesNotContain(textToSearch, substring,\n                \"[Assertion failed] - this String argument must not contain the substring [\" + substring + \"]\");\n    }\n\n\n    /**\n     * Assert that an array has elements; that is, it must not be\n     * <code>null</code> and must have at least one element.\n     * <pre class=\"code\">Assert.notEmpty(array, \"The array must have elements\");</pre>\n     *\n     * @param array   the array to check\n     * @param message the exception message to use if the assertion fails\n     * @return the non-empty array for immediate use\n     * @throws IllegalArgumentException if the object array is <code>null</code> or has no elements\n     */\n    public static Object[] notEmpty(Object[] array, String message) {\n        if (Objects.isEmpty(array)) {\n            throw new IllegalArgumentException(message);\n        }\n        return array;\n    }\n\n    /**\n     * Assert that an array has elements; that is, it must not be\n     * <code>null</code> and must have at least one element.\n     * <pre class=\"code\">Assert.notEmpty(array);</pre>\n     *\n     * @param array the array to check\n     * @throws IllegalArgumentException if the object array is <code>null</code> or has no elements\n     */\n    public static void notEmpty(Object[] array) {\n        notEmpty(array, \"[Assertion failed] - this array must not be empty: it must contain at least 1 element\");\n    }\n\n    /**\n     * Assert that the specified byte array is not null and has at least one byte element.\n     *\n     * @param array the byte array to check\n     * @param msg   the exception message to use if the assertion fails\n     * @return the byte array if the assertion passes\n     * @throws IllegalArgumentException if the byte array is null or empty\n     * @since 0.12.0\n     */\n    public static byte[] notEmpty(byte[] array, String msg) {\n        if (Objects.isEmpty(array)) {\n            throw new IllegalArgumentException(msg);\n        }\n        return array;\n    }\n\n    /**\n     * Assert that the specified character array is not null and has at least one byte element.\n     *\n     * @param chars the character array to check\n     * @param msg   the exception message to use if the assertion fails\n     * @return the character array if the assertion passes\n     * @throws IllegalArgumentException if the character array is null or empty\n     * @since 0.12.0\n     */\n    public static char[] notEmpty(char[] chars, String msg) {\n        if (Objects.isEmpty(chars)) {\n            throw new IllegalArgumentException(msg);\n        }\n        return chars;\n    }\n\n    /**\n     * Assert that an array has no null elements.\n     * Note: Does not complain if the array is empty!\n     * <pre class=\"code\">Assert.noNullElements(array, \"The array must have non-null elements\");</pre>\n     *\n     * @param array   the array to check\n     * @param message the exception message to use if the assertion fails\n     * @throws IllegalArgumentException if the object array contains a <code>null</code> element\n     */\n    public static void noNullElements(Object[] array, String message) {\n        if (array != null) {\n            for (int i = 0; i < array.length; i++) {\n                if (array[i] == null) {\n                    throw new IllegalArgumentException(message);\n                }\n            }\n        }\n    }\n\n    /**\n     * Assert that an array has no null elements.\n     * Note: Does not complain if the array is empty!\n     * <pre class=\"code\">Assert.noNullElements(array);</pre>\n     *\n     * @param array the array to check\n     * @throws IllegalArgumentException if the object array contains a <code>null</code> element\n     */\n    public static void noNullElements(Object[] array) {\n        noNullElements(array, \"[Assertion failed] - this array must not contain any null elements\");\n    }\n\n    /**\n     * Assert that a collection has elements; that is, it must not be\n     * <code>null</code> and must have at least one element.\n     * <pre class=\"code\">Assert.notEmpty(collection, \"Collection must have elements\");</pre>\n     *\n     * @param collection the collection to check\n     * @param <T>        the type of collection\n     * @param message    the exception message to use if the assertion fails\n     * @return the non-null, non-empty collection\n     * @throws IllegalArgumentException if the collection is <code>null</code> or has no elements\n     */\n    public static <T extends Collection<?>> T notEmpty(T collection, String message) {\n        if (Collections.isEmpty(collection)) {\n            throw new IllegalArgumentException(message);\n        }\n        return collection;\n    }\n\n    /**\n     * Assert that a collection has elements; that is, it must not be\n     * <code>null</code> and must have at least one element.\n     * <pre class=\"code\">Assert.notEmpty(collection, \"Collection must have elements\");</pre>\n     *\n     * @param collection the collection to check\n     * @throws IllegalArgumentException if the collection is <code>null</code> or has no elements\n     */\n    public static void notEmpty(Collection<?> collection) {\n        notEmpty(collection,\n                \"[Assertion failed] - this collection must not be empty: it must contain at least 1 element\");\n    }\n\n    /**\n     * Assert that a Map has entries; that is, it must not be <code>null</code>\n     * and must have at least one entry.\n     * <pre class=\"code\">Assert.notEmpty(map, \"Map must have entries\");</pre>\n     *\n     * @param map     the map to check\n     * @param <T>     the type of Map to check\n     * @param message the exception message to use if the assertion fails\n     * @return the non-null, non-empty map\n     * @throws IllegalArgumentException if the map is <code>null</code> or has no entries\n     */\n    public static <T extends Map<?, ?>> T notEmpty(T map, String message) {\n        if (Collections.isEmpty(map)) {\n            throw new IllegalArgumentException(message);\n        }\n        return map;\n    }\n\n    /**\n     * Assert that a Map has entries; that is, it must not be <code>null</code>\n     * and must have at least one entry.\n     * <pre class=\"code\">Assert.notEmpty(map);</pre>\n     *\n     * @param map the map to check\n     * @throws IllegalArgumentException if the map is <code>null</code> or has no entries\n     */\n    public static void notEmpty(Map map) {\n        notEmpty(map, \"[Assertion failed] - this map must not be empty; it must contain at least one entry\");\n    }\n\n\n    /**\n     * Assert that the provided object is an instance of the provided class.\n     * <pre class=\"code\">Assert.instanceOf(Foo.class, foo);</pre>\n     *\n     * @param <T>   the type of instance expected\n     * @param clazz the required class\n     * @param obj   the object to check\n     * @return the expected instance of type {@code T}\n     * @throws IllegalArgumentException if the object is not an instance of clazz\n     * @see Class#isInstance\n     */\n    public static <T> T isInstanceOf(Class<T> clazz, Object obj) {\n        return isInstanceOf(clazz, obj, \"\");\n    }\n\n    /**\n     * Assert that the provided object is an instance of the provided class.\n     * <pre class=\"code\">Assert.instanceOf(Foo.class, foo);</pre>\n     *\n     * @param type    the type to check against\n     * @param <T>     the object's expected type\n     * @param obj     the object to check\n     * @param message a message which will be prepended to the message produced by\n     *                the function itself, and which may be used to provide context. It should\n     *                normally end in a \": \" or \". \" so that the function generate message looks\n     *                ok when prepended to it.\n     * @return the non-null object IFF it is an instance of the specified {@code type}.\n     * @throws IllegalArgumentException if the object is not an instance of clazz\n     * @see Class#isInstance\n     */\n    public static <T> T isInstanceOf(Class<T> type, Object obj, String message) {\n        notNull(type, \"Type to check against must not be null\");\n        if (!type.isInstance(obj)) {\n            throw new IllegalArgumentException(message +\n                    \"Object of class [\" + (obj != null ? obj.getClass().getName() : \"null\") +\n                    \"] must be an instance of \" + type);\n        }\n        return type.cast(obj);\n    }\n\n    /**\n     * Asserts that the provided object is an instance of the provided class, throwing an\n     * {@link IllegalStateException} otherwise.\n     * <pre class=\"code\">Assert.stateIsInstance(Foo.class, foo);</pre>\n     *\n     * @param type    the type to check against\n     * @param <T>     the object's expected type\n     * @param obj     the object to check\n     * @param message a message which will be prepended to the message produced by\n     *                the function itself, and which may be used to provide context. It should\n     *                normally end in a \": \" or \". \" so that the function generate message looks\n     *                ok when prepended to it.\n     * @return the non-null object IFF it is an instance of the specified {@code type}.\n     * @throws IllegalStateException if the object is not an instance of clazz\n     * @see Class#isInstance\n     */\n    public static <T> T stateIsInstance(Class<T> type, Object obj, String message) {\n        notNull(type, \"Type to check cannot be null.\");\n        if (!type.isInstance(obj)) {\n            String msg = message + \"Object of class [\" + Objects.nullSafeClassName(obj) +\n                    \"] must be an instance of \" + type;\n            throw new IllegalStateException(msg);\n        }\n        return type.cast(obj);\n    }\n\n    /**\n     * Assert that <code>superType.isAssignableFrom(subType)</code> is <code>true</code>.\n     * <pre class=\"code\">Assert.isAssignable(Number.class, myClass);</pre>\n     *\n     * @param superType the super type to check\n     * @param subType   the sub type to check\n     * @throws IllegalArgumentException if the classes are not assignable\n     */\n    public static void isAssignable(Class superType, Class subType) {\n        isAssignable(superType, subType, \"\");\n    }\n\n    /**\n     * Assert that <code>superType.isAssignableFrom(subType)</code> is <code>true</code>.\n     * <pre class=\"code\">Assert.isAssignable(Number.class, myClass);</pre>\n     *\n     * @param superType the super type to check against\n     * @param subType   the sub type to check\n     * @param message   a message which will be prepended to the message produced by\n     *                  the function itself, and which may be used to provide context. It should\n     *                  normally end in a \": \" or \". \" so that the function generate message looks\n     *                  ok when prepended to it.\n     * @throws IllegalArgumentException if the classes are not assignable\n     */\n    public static void isAssignable(Class superType, Class subType, String message) {\n        notNull(superType, \"Type to check against must not be null\");\n        if (subType == null || !superType.isAssignableFrom(subType)) {\n            throw new IllegalArgumentException(message + subType + \" is not assignable to \" + superType);\n        }\n    }\n\n    /**\n     * Asserts that a specified {@code value} is equal to the given {@code requirement}, throwing\n     * an {@link IllegalArgumentException} with the given message if not.\n     *\n     * @param <T>         the type of argument\n     * @param value       the value to check\n     * @param requirement the requirement that {@code value} must be greater than\n     * @param msg         the message to use for the {@code IllegalArgumentException} if thrown.\n     * @return {@code value} if greater than the specified {@code requirement}.\n     * @since 0.12.0\n     */\n    public static <T extends Comparable<T>> T eq(T value, T requirement, String msg) {\n        if (compareTo(value, requirement) != 0) {\n            throw new IllegalArgumentException(msg);\n        }\n        return value;\n    }\n\n    private static <T extends Comparable<T>> int compareTo(T value, T requirement) {\n        notNull(value, \"value cannot be null.\");\n        notNull(requirement, \"requirement cannot be null.\");\n        return value.compareTo(requirement);\n    }\n\n    /**\n     * Asserts that a specified {@code value} is greater than the given {@code requirement}, throwing\n     * an {@link IllegalArgumentException} with the given message if not.\n     *\n     * @param <T>         the type of value to check and return if the requirement is met\n     * @param value       the value to check\n     * @param requirement the requirement that {@code value} must be greater than\n     * @param msg         the message to use for the {@code IllegalArgumentException} if thrown.\n     * @return {@code value} if greater than the specified {@code requirement}.\n     * @since 0.12.0\n     */\n    public static <T extends Comparable<T>> T gt(T value, T requirement, String msg) {\n        if (!(compareTo(value, requirement) > 0)) {\n            throw new IllegalArgumentException(msg);\n        }\n        return value;\n    }\n\n    /**\n     * Asserts that a specified {@code value} is less than or equal to the given {@code requirement}, throwing\n     * an {@link IllegalArgumentException} with the given message if not.\n     *\n     * @param <T>         the type of value to check and return if the requirement is met\n     * @param value       the value to check\n     * @param requirement the requirement that {@code value} must be greater than\n     * @param msg         the message to use for the {@code IllegalArgumentException} if thrown.\n     * @return {@code value} if greater than the specified {@code requirement}.\n     * @since 0.12.0\n     */\n    public static <T extends Comparable<T>> T lte(T value, T requirement, String msg) {\n        if (compareTo(value, requirement) > 0) {\n            throw new IllegalArgumentException(msg);\n        }\n        return value;\n    }\n\n\n    /**\n     * Assert a boolean expression, throwing <code>IllegalStateException</code>\n     * if the test result is <code>false</code>. Call isTrue if you wish to\n     * throw IllegalArgumentException on an assertion failure.\n     * <pre class=\"code\">Assert.state(id == null, \"The id property must not already be initialized\");</pre>\n     *\n     * @param expression a boolean expression\n     * @param message    the exception message to use if the assertion fails\n     * @throws IllegalStateException if expression is <code>false</code>\n     */\n    public static void state(boolean expression, String message) {\n        if (!expression) {\n            throw new IllegalStateException(message);\n        }\n    }\n\n    /**\n     * Assert a boolean expression, throwing {@link IllegalStateException}\n     * if the test result is <code>false</code>.\n     * <p>Call {@link #isTrue(boolean)} if you wish to\n     * throw {@link IllegalArgumentException} on an assertion failure.\n     * <pre class=\"code\">Assert.state(id == null);</pre>\n     *\n     * @param expression a boolean expression\n     * @throws IllegalStateException if the supplied expression is <code>false</code>\n     */\n    public static void state(boolean expression) {\n        state(expression, \"[Assertion failed] - this state invariant must be true\");\n    }\n\n    /**\n     * Asserts that the specified {@code value} is not null, otherwise throws an\n     * {@link IllegalStateException} with the specified {@code msg}.  Intended to be used with\n     * code invariants (as opposed to method arguments, like {@link #notNull(Object)}).\n     *\n     * @param value value to assert is not null\n     * @param msg   exception message to use if {@code value} is null\n     * @param <T>   value type\n     * @return the non-null value\n     * @throws IllegalStateException with the specified {@code msg} if {@code value} is null.\n     * @since 0.12.0\n     */\n    public static <T> T stateNotNull(T value, String msg) throws IllegalStateException {\n        if (value == null) {\n            throw new IllegalStateException(msg);\n        }\n        return value;\n    }\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Builder.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\n/**\n * Type-safe interface that reflects the <a href=\"https://en.wikipedia.org/wiki/Builder_pattern\">Builder pattern</a>.\n *\n * @param <T> The type of object that will be created when {@link #build()} is invoked.\n * @since 0.12.0\n */\npublic interface Builder<T> {\n\n    /**\n     * Creates and returns a new instance of type {@code T}.\n     *\n     * @return a new instance of type {@code T}.\n     */\n    T build();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Classes.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.io.InputStream;\nimport java.lang.reflect.Constructor;\nimport java.lang.reflect.Field;\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\nimport java.net.URL;\n\n/**\n * Utility methods for working with {@link Class}es.\n *\n * @since 0.1\n */\npublic final class Classes {\n\n    private Classes() {\n    } //prevent instantiation\n\n    private static final ClassLoaderAccessor THREAD_CL_ACCESSOR = new ExceptionIgnoringAccessor() {\n        @Override\n        protected ClassLoader doGetClassLoader() {\n            return Thread.currentThread().getContextClassLoader();\n        }\n    };\n\n    private static final ClassLoaderAccessor CLASS_CL_ACCESSOR = new ExceptionIgnoringAccessor() {\n        @Override\n        protected ClassLoader doGetClassLoader() {\n            return Classes.class.getClassLoader();\n        }\n    };\n\n    private static final ClassLoaderAccessor SYSTEM_CL_ACCESSOR = new ExceptionIgnoringAccessor() {\n        @Override\n        protected ClassLoader doGetClassLoader() {\n            return ClassLoader.getSystemClassLoader();\n        }\n    };\n\n    /**\n     * Attempts to load the specified class name from the current thread's\n     * {@link Thread#getContextClassLoader() context class loader}, then the\n     * current ClassLoader (<code>Classes.class.getClassLoader()</code>), then the system/application\n     * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order.  If any of them cannot locate\n     * the specified class, an <code>UnknownClassException</code> is thrown (our RuntimeException equivalent of\n     * the JRE's <code>ClassNotFoundException</code>.\n     *\n     * @param fqcn the fully qualified class name to load\n     * @param <T>  The type of Class returned\n     * @return the located class\n     * @throws UnknownClassException if the class cannot be found.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> Class<T> forName(String fqcn) throws UnknownClassException {\n\n        Class<?> clazz = THREAD_CL_ACCESSOR.loadClass(fqcn);\n\n        if (clazz == null) {\n            clazz = CLASS_CL_ACCESSOR.loadClass(fqcn);\n        }\n\n        if (clazz == null) {\n            clazz = SYSTEM_CL_ACCESSOR.loadClass(fqcn);\n        }\n\n        if (clazz == null) {\n            String msg = \"Unable to load class named [\" + fqcn + \"] from the thread context, current, or \" +\n                    \"system/application ClassLoaders.  All heuristics have been exhausted.  Class could not be found.\";\n\n            if (fqcn != null && fqcn.startsWith(\"io.jsonwebtoken.impl\")) {\n                msg += \"  Have you remembered to include the jjwt-impl.jar in your runtime classpath?\";\n            }\n\n            throw new UnknownClassException(msg);\n        }\n\n        return (Class<T>) clazz;\n    }\n\n    /**\n     * Returns the specified resource by checking the current thread's\n     * {@link Thread#getContextClassLoader() context class loader}, then the\n     * current ClassLoader (<code>Classes.class.getClassLoader()</code>), then the system/application\n     * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order, using\n     * {@link ClassLoader#getResourceAsStream(String) getResourceAsStream(name)}.\n     *\n     * @param name the name of the resource to acquire from the classloader(s).\n     * @return the InputStream of the resource found, or <code>null</code> if the resource cannot be found from any\n     * of the three mentioned ClassLoaders.\n     * @since 0.8\n     */\n    public static InputStream getResourceAsStream(String name) {\n\n        InputStream is = THREAD_CL_ACCESSOR.getResourceStream(name);\n\n        if (is == null) {\n            is = CLASS_CL_ACCESSOR.getResourceStream(name);\n        }\n\n        if (is == null) {\n            is = SYSTEM_CL_ACCESSOR.getResourceStream(name);\n        }\n\n        return is;\n    }\n\n    /**\n     * Returns the specified resource URL by checking the current thread's\n     * {@link Thread#getContextClassLoader() context class loader}, then the\n     * current ClassLoader (<code>Classes.class.getClassLoader()</code>), then the system/application\n     * ClassLoader (<code>ClassLoader.getSystemClassLoader()</code>, in that order, using\n     * {@link ClassLoader#getResource(String) getResource(name)}.\n     *\n     * @param name the name of the resource to acquire from the classloader(s).\n     * @return the URL of the resource found, or <code>null</code> if the resource cannot be found from any\n     * of the three mentioned ClassLoaders.\n     * @since 0.12.0\n     */\n    private static URL getResource(String name) {\n        URL url = THREAD_CL_ACCESSOR.getResource(name);\n        if (url == null) {\n            url = CLASS_CL_ACCESSOR.getResource(name);\n        }\n        if (url == null) {\n            return SYSTEM_CL_ACCESSOR.getResource(name);\n        }\n        return url;\n    }\n\n    /**\n     * Returns {@code true} if the specified {@code fullyQualifiedClassName} can be found in any of the thread\n     * context, class, or system classloaders, or {@code false} otherwise.\n     *\n     * @param fullyQualifiedClassName the fully qualified class name to check\n     * @return {@code true} if the specified {@code fullyQualifiedClassName} can be found in any of the thread\n     * context, class, or system classloaders, or {@code false} otherwise.\n     */\n    public static boolean isAvailable(String fullyQualifiedClassName) {\n        try {\n            forName(fullyQualifiedClassName);\n            return true;\n        } catch (UnknownClassException e) {\n            return false;\n        }\n    }\n\n    /**\n     * Creates and returns a new instance of the class with the specified fully qualified class name using the\n     * classes default no-argument constructor.\n     *\n     * @param fqcn the fully qualified class name\n     * @param <T>  the type of object created\n     * @return a new instance of the specified class name\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T newInstance(String fqcn) {\n        return (T) newInstance(forName(fqcn));\n    }\n\n    /**\n     * Creates and returns a new instance of the specified fully qualified class name using the\n     * specified {@code args} arguments provided to the constructor with {@code ctorArgTypes}\n     *\n     * @param fqcn         the fully qualified class name\n     * @param ctorArgTypes the argument types of the constructor to invoke\n     * @param args         the arguments to supply when invoking the constructor\n     * @param <T>          the type of object created\n     * @return the newly created object\n     */\n    public static <T> T newInstance(String fqcn, Class<?>[] ctorArgTypes, Object... args) {\n        Class<T> clazz = forName(fqcn);\n        Constructor<T> ctor = getConstructor(clazz, ctorArgTypes);\n        return instantiate(ctor, args);\n    }\n\n    /**\n     * Creates and returns a new instance of the specified fully qualified class name using a constructor that matches\n     * the specified {@code args} arguments.\n     *\n     * @param fqcn fully qualified class name\n     * @param args the arguments to supply to the constructor\n     * @param <T>  the type of the object created\n     * @return the newly created object\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T newInstance(String fqcn, Object... args) {\n        return (T) newInstance(forName(fqcn), args);\n    }\n\n    /**\n     * Creates a new instance of the specified {@code clazz} via {@code clazz.newInstance()}.\n     *\n     * @param clazz the class to invoke\n     * @param <T>   the type of the object created\n     * @return the newly created object\n     */\n    public static <T> T newInstance(Class<T> clazz) {\n        if (clazz == null) {\n            String msg = \"Class method parameter cannot be null.\";\n            throw new IllegalArgumentException(msg);\n        }\n        try {\n            return clazz.newInstance();\n        } catch (Exception e) {\n            throw new InstantiationException(\"Unable to instantiate class [\" + clazz.getName() + \"]\", e);\n        }\n    }\n\n    /**\n     * Returns a new instance of the specified {@code clazz}, invoking the associated constructor with the specified\n     * {@code args} arguments.\n     *\n     * @param clazz the class to invoke\n     * @param args  the arguments matching an associated class constructor\n     * @param <T>   the type of the created object\n     * @return the newly created object\n     */\n    public static <T> T newInstance(Class<T> clazz, Object... args) {\n        Class<?>[] argTypes = new Class[args.length];\n        for (int i = 0; i < args.length; i++) {\n            argTypes[i] = args[i].getClass();\n        }\n        Constructor<T> ctor = getConstructor(clazz, argTypes);\n        return instantiate(ctor, args);\n    }\n\n    /**\n     * Returns the {@link Constructor} for the specified {@code Class} with arguments matching the specified\n     * {@code argTypes}.\n     *\n     * @param clazz    the class to inspect\n     * @param argTypes the argument types for the desired constructor\n     * @param <T>      the type of object to create\n     * @return the constructor matching the specified argument types\n     * @throws IllegalStateException if the constructor for the specified {@code argTypes} does not exist.\n     */\n    public static <T> Constructor<T> getConstructor(Class<T> clazz, Class<?>... argTypes) throws IllegalStateException {\n        try {\n            return clazz.getConstructor(argTypes);\n        } catch (NoSuchMethodException e) {\n            throw new IllegalStateException(e);\n        }\n\n    }\n\n    /**\n     * Creates a new object using the specified {@link Constructor}, invoking it with the specified constructor\n     * {@code args} arguments.\n     *\n     * @param ctor the constructor to invoke\n     * @param args the arguments to supply to the constructor\n     * @param <T>  the type of object to create\n     * @return the new object instance\n     * @throws InstantiationException if the constructor cannot be invoked successfully\n     */\n    public static <T> T instantiate(Constructor<T> ctor, Object... args) {\n        try {\n            return ctor.newInstance(args);\n        } catch (Exception e) {\n            String msg = \"Unable to instantiate instance with constructor [\" + ctor + \"]\";\n            throw new InstantiationException(msg, e);\n        }\n    }\n\n    /**\n     * Invokes the fully qualified class name's method named {@code methodName} with parameters of type {@code argTypes}\n     * using the {@code args} as the method arguments.\n     *\n     * @param fqcn       fully qualified class name to locate\n     * @param methodName name of the method to invoke on the class\n     * @param argTypes   the method argument types supported by the {@code methodName} method\n     * @param args       the runtime arguments to use when invoking the located class method\n     * @param <T>        the expected type of the object returned from the invoked method.\n     * @return the result returned by the invoked method\n     * @since 0.10.0\n     */\n    public static <T> T invokeStatic(String fqcn, String methodName, Class<?>[] argTypes, Object... args) {\n        try {\n            Class<?> clazz = Classes.forName(fqcn);\n            return invokeStatic(clazz, methodName, argTypes, args);\n        } catch (Exception e) {\n            String msg = \"Unable to invoke class method \" + fqcn + \"#\" + methodName + \".  Ensure the necessary \" +\n                    \"implementation is in the runtime classpath.\";\n            throw new IllegalStateException(msg, e);\n        }\n    }\n\n    /**\n     * Invokes the {@code clazz}'s matching static method (named {@code methodName} with exact argument types\n     * of {@code argTypes}) with the given {@code args} arguments, and returns the method return value.\n     *\n     * @param clazz      the class to invoke\n     * @param methodName the name of the static method on {@code clazz} to invoke\n     * @param argTypes   the types of the arguments accepted by the method\n     * @param args       the actual runtime arguments to use when invoking the method\n     * @param <T>        the type of object expected to be returned from the method\n     * @return the result returned by the invoked method.\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T invokeStatic(Class<?> clazz, String methodName, Class<?>[] argTypes, Object... args) {\n        try {\n            Method method = clazz.getDeclaredMethod(methodName, argTypes);\n            method.setAccessible(true);\n            return (T) method.invoke(null, args);\n        } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {\n            Throwable cause = e.getCause();\n            if (cause instanceof RuntimeException) {\n                throw ((RuntimeException) cause); //propagate\n            }\n            String msg = \"Unable to invoke class method \" + clazz.getName() + \"#\" + methodName +\n                    \". Ensure the necessary implementation is in the runtime classpath.\";\n            throw new IllegalStateException(msg, e);\n        }\n    }\n\n    /**\n     * Returns the {@code instance}'s named (declared) field value.\n     *\n     * @param instance  the instance with the internal field\n     * @param fieldName the name of the field to inspect\n     * @param fieldType the type of field to inspect\n     * @param <T>       field instance value type\n     * @return the field value\n     */\n    public static <T> T getFieldValue(Object instance, String fieldName, Class<T> fieldType) {\n        if (instance == null) return null;\n        try {\n            Field field = instance.getClass().getDeclaredField(fieldName);\n            field.setAccessible(true);\n            Object o = field.get(instance);\n            return fieldType.cast(o);\n        } catch (Throwable t) {\n            String msg = \"Unable to read field \" + instance.getClass().getName() +\n                    \"#\" + fieldName + \": \" + t.getMessage();\n            throw new IllegalStateException(msg, t);\n        }\n    }\n\n    /**\n     * @since 1.0\n     */\n    private interface ClassLoaderAccessor {\n        Class<?> loadClass(String fqcn);\n\n        URL getResource(String name);\n\n        InputStream getResourceStream(String name);\n    }\n\n    /**\n     * @since 1.0\n     */\n    private static abstract class ExceptionIgnoringAccessor implements ClassLoaderAccessor {\n\n        public Class<?> loadClass(String fqcn) {\n            Class<?> clazz = null;\n            ClassLoader cl = getClassLoader();\n            if (cl != null) {\n                try {\n                    clazz = cl.loadClass(fqcn);\n                } catch (ClassNotFoundException e) {\n                    //Class couldn't be found by loader\n                }\n            }\n            return clazz;\n        }\n\n        @Override\n        public URL getResource(String name) {\n            URL url = null;\n            ClassLoader cl = getClassLoader();\n            if (cl != null) {\n                url = cl.getResource(name);\n            }\n            return url;\n        }\n\n        public InputStream getResourceStream(String name) {\n            InputStream is = null;\n            ClassLoader cl = getClassLoader();\n            if (cl != null) {\n                is = cl.getResourceAsStream(name);\n            }\n            return is;\n        }\n\n        protected final ClassLoader getClassLoader() {\n            try {\n                return doGetClassLoader();\n            } catch (Throwable t) {\n                //Unable to get ClassLoader\n            }\n            return null;\n        }\n\n        protected abstract ClassLoader doGetClassLoader() throws Throwable;\n    }\n}\n\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/CollectionMutator.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.util.Collection;\n\n/**\n * Mutation (modifications) to a {@link java.util.Collection} instance while also supporting method chaining. The\n * {@link Collection#add(Object)}, {@link Collection#addAll(Collection)}, {@link Collection#remove(Object)}, and\n * {@link Collection#clear()} methods do not support method chaining, so this interface enables that behavior.\n *\n * @param <E> the type of elements in the collection\n * @param <M> the mutator subtype, for method chaining\n * @since 0.12.0\n */\npublic interface CollectionMutator<E, M extends CollectionMutator<E, M>> {\n\n    /**\n     * Adds the specified element to the collection.\n     *\n     * @param e the element to add.\n     * @return the mutator/builder for method chaining.\n     */\n    M add(E e);\n\n    /**\n     * Adds the elements to the collection in iteration order.\n     *\n     * @param c the collection to add\n     * @return the mutator/builder for method chaining.\n     */\n    M add(Collection<? extends E> c);\n\n    /**\n     * Removes all elements in the collection.\n     *\n     * @return the mutator/builder for method chaining.\n     */\n    M clear();\n\n    /**\n     * Removes the specified element from the collection.\n     *\n     * @param e the element to remove.\n     * @return the mutator/builder for method chaining.\n     */\n    M remove(E e);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Collections.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.Iterator;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Properties;\nimport java.util.Set;\n\n/**\n * Utility methods for working with {@link Collection}s, {@link List}s, {@link Set}s, and {@link Maps}.\n */\n@SuppressWarnings({\"unused\", \"rawtypes\"})\npublic final class Collections {\n\n    private Collections() {\n    } //prevent instantiation\n\n    /**\n     * Returns a type-safe immutable empty {@code List}.\n     *\n     * @param <T> list element type\n     * @return a type-safe immutable empty {@code List}.\n     */\n    public static <T> List<T> emptyList() {\n        return java.util.Collections.emptyList();\n    }\n\n    /**\n     * Returns a type-safe immutable empty {@code Set}.\n     *\n     * @param <T> set element type\n     * @return a type-safe immutable empty {@code Set}.\n     */\n    @SuppressWarnings(\"unused\")\n    public static <T> Set<T> emptySet() {\n        return java.util.Collections.emptySet();\n    }\n\n    /**\n     * Returns a type-safe immutable empty {@code Map}.\n     *\n     * @param <K> map key type\n     * @param <V> map value type\n     * @return a type-safe immutable empty {@code Map}.\n     */\n    @SuppressWarnings(\"unused\")\n    public static <K, V> Map<K, V> emptyMap() {\n        return java.util.Collections.emptyMap();\n    }\n\n    /**\n     * Returns a type-safe immutable {@code List} containing the specified array elements.\n     *\n     * @param elements array elements to include in the list\n     * @param <T>      list element type\n     * @return a type-safe immutable {@code List} containing the specified array elements.\n     */\n    @SafeVarargs\n    public static <T> List<T> of(T... elements) {\n        if (elements == null || elements.length == 0) {\n            return java.util.Collections.emptyList();\n        }\n        return java.util.Collections.unmodifiableList(Arrays.asList(elements));\n    }\n\n    /**\n     * Returns the specified collection as a {@link Set} instance.\n     *\n     * @param c   the collection to be converted\n     * @param <T> collection element type\n     * @return    a type-safe immutable {@code Set} containing the specified collection elements.\n     * @since 0.12.0\n     */\n    public static <T> Set<T> asSet(Collection<T> c) {\n        if (isEmpty(c)) {\n            return java.util.Collections.emptySet();\n        }\n        return java.util.Collections.unmodifiableSet(new LinkedHashSet<T>(c));\n    }\n\n    /**\n     * Returns a type-safe immutable {@code Set} containing the specified array elements.\n     *\n     * @param elements array elements to include in the set\n     * @param <T>      set element type\n     * @return a type-safe immutable {@code Set} containing the specified array elements.\n     */\n    @SafeVarargs\n    public static <T> Set<T> setOf(T... elements) {\n        if (elements == null || elements.length == 0) {\n            return java.util.Collections.emptySet();\n        }\n        Set<T> set = new LinkedHashSet<>(Arrays.asList(elements));\n        return immutable(set);\n    }\n\n    /**\n     * Shorter null-safe convenience alias for {@link java.util.Collections#unmodifiableList(List)} so both classes\n     * don't need to be imported.\n     *\n     * @param m   map to wrap in an immutable/unmodifiable collection\n     * @param <K> map key type\n     * @param <V> map value type\n     * @return an immutable wrapper for {@code m}.\n     * @since 0.12.0\n     */\n    public static <K, V> Map<K, V> immutable(Map<K, V> m) {\n        return m != null ? java.util.Collections.unmodifiableMap(m) : null;\n    }\n\n    /**\n     * Shorter null-safe convenience alias for {@link java.util.Collections#unmodifiableSet(Set)} so both classes don't\n     * need to be imported.\n     *\n     * @param set set to wrap in an immutable Set\n     * @param <T> set element type\n     * @return an immutable wrapper for {@code set}\n     */\n    public static <T> Set<T> immutable(Set<T> set) {\n        return set != null ? java.util.Collections.unmodifiableSet(set) : null;\n    }\n\n    /**\n     * Shorter null-safe convenience alias for {@link java.util.Collections#unmodifiableList(List)} so both classes\n     * don't need to be imported.\n     *\n     * @param list list to wrap in an immutable List\n     * @param <T>  list element type\n     * @return an immutable wrapper for {@code list}\n     */\n    public static <T> List<T> immutable(List<T> list) {\n        return list != null ? java.util.Collections.unmodifiableList(list) : null;\n    }\n\n    /**\n     * Null-safe factory method that returns an immutable/unmodifiable view of the specified collection instance.\n     * Works for {@link List}, {@link Set} and {@link Collection} arguments.\n     *\n     * @param c   collection to wrap in an immutable/unmodifiable collection\n     * @param <C> type of collection\n     * @param <T> type of elements in the collection\n     * @return an immutable wrapper for {@code l}.\n     * @since 0.12.0\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T, C extends Collection<T>> C immutable(C c) {\n        if (c == null) {\n            return null;\n        } else if (c instanceof Set) {\n            return (C) java.util.Collections.unmodifiableSet((Set<T>) c);\n        } else if (c instanceof List) {\n            return (C) java.util.Collections.unmodifiableList((List<T>) c);\n        } else {\n            return (C) java.util.Collections.unmodifiableCollection(c);\n        }\n    }\n\n    /**\n     * Returns a non-null set, either {@code s} if it is not null, or {@link #emptySet()} otherwise.\n     *\n     * @param s   the set to check for null\n     * @param <T> type of elements in the set\n     * @return a non-null set, either {@code s} if it is not null, or {@link #emptySet()} otherwise.\n     * @since 0.12.0\n     */\n    public static <T> Set<T> nullSafe(Set<T> s) {\n        return s == null ? Collections.<T>emptySet() : s;\n    }\n\n    /**\n     * Returns a non-null collection, either {@code c} if it is not null, or {@link #emptyList()} otherwise.\n     *\n     * @param c   the collection to check for null\n     * @param <T> type of elements in the collection\n     * @return a non-null collection, either {@code c} if it is not null, or {@link #emptyList()} otherwise.\n     * @since 0.12.0\n     */\n    public static <T> Collection<T> nullSafe(Collection<T> c) {\n        return c == null ? Collections.<T>emptyList() : c;\n    }\n\n    /**\n     * Return <code>true</code> if the supplied Collection is <code>null</code>\n     * or empty. Otherwise, return <code>false</code>.\n     *\n     * @param collection the Collection to check\n     * @return whether the given Collection is empty\n     */\n    public static boolean isEmpty(Collection<?> collection) {\n        return size(collection) == 0;\n    }\n\n    /**\n     * Returns the collection's size or {@code 0} if the collection is {@code null}.\n     *\n     * @param collection the collection to check.\n     * @return the collection's size or {@code 0} if the collection is {@code null}.\n     * @since 0.9.2\n     */\n    public static int size(Collection<?> collection) {\n        return collection == null ? 0 : collection.size();\n    }\n\n    /**\n     * Returns the map's size or {@code 0} if the map is {@code null}.\n     *\n     * @param map the map to check\n     * @return the map's size or {@code 0} if the map is {@code null}.\n     * @since 0.9.2\n     */\n    public static int size(Map<?, ?> map) {\n        return map == null ? 0 : map.size();\n    }\n\n    /**\n     * Return <code>true</code> if the supplied Map is <code>null</code>\n     * or empty. Otherwise, return <code>false</code>.\n     *\n     * @param map the Map to check\n     * @return whether the given Map is empty\n     */\n    public static boolean isEmpty(Map<?, ?> map) {\n        return size(map) == 0;\n    }\n\n    /**\n     * Convert the supplied array into a List. A primitive array gets\n     * converted into a List of the appropriate wrapper type.\n     * <p>A <code>null</code> source value will be converted to an\n     * empty List.\n     *\n     * @param source the (potentially primitive) array\n     * @return the converted List result\n     * @see Objects#toObjectArray(Object)\n     */\n    public static List arrayToList(Object source) {\n        return Arrays.asList(Objects.toObjectArray(source));\n    }\n\n    /**\n     * Concatenate the specified set with the specified array elements, resulting in a new {@link LinkedHashSet} with\n     * the array elements appended to the end of the existing Set.\n     *\n     * @param c        the set to append to\n     * @param elements the array elements to append to the end of the set\n     * @param <T>      set element type\n     * @return a new {@link LinkedHashSet} with the array elements appended to the end of the original set.\n     */\n    @SafeVarargs\n    public static <T> Set<T> concat(Set<T> c, T... elements) {\n        int size = Math.max(1, Collections.size(c) + io.jsonwebtoken.lang.Arrays.length(elements));\n        Set<T> set = new LinkedHashSet<>(size);\n        set.addAll(c);\n        java.util.Collections.addAll(set, elements);\n        return immutable(set);\n    }\n\n    /**\n     * Merge the given array into the given Collection.\n     *\n     * @param array      the array to merge (may be <code>null</code>)\n     * @param collection the target Collection to merge the array into\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static void mergeArrayIntoCollection(Object array, Collection collection) {\n        if (collection == null) {\n            throw new IllegalArgumentException(\"Collection must not be null\");\n        }\n        Object[] arr = Objects.toObjectArray(array);\n        java.util.Collections.addAll(collection, arr);\n    }\n\n    /**\n     * Merge the given Properties instance into the given Map,\n     * copying all properties (key-value pairs) over.\n     * <p>Uses <code>Properties.propertyNames()</code> to even catch\n     * default properties linked into the original Properties instance.\n     *\n     * @param props the Properties instance to merge (may be <code>null</code>)\n     * @param map   the target Map to merge the properties into\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static void mergePropertiesIntoMap(Properties props, Map map) {\n        if (map == null) {\n            throw new IllegalArgumentException(\"Map must not be null\");\n        }\n        if (props != null) {\n            for (Enumeration en = props.propertyNames(); en.hasMoreElements(); ) {\n                String key = (String) en.nextElement();\n                Object value = props.getProperty(key);\n                if (value == null) {\n                    // Potentially a non-String value...\n                    value = props.get(key);\n                }\n                map.put(key, value);\n            }\n        }\n    }\n\n\n    /**\n     * Check whether the given Iterator contains the given element.\n     *\n     * @param iterator the Iterator to check\n     * @param element  the element to look for\n     * @return <code>true</code> if found, <code>false</code> else\n     */\n    public static boolean contains(Iterator iterator, Object element) {\n        if (iterator != null) {\n            while (iterator.hasNext()) {\n                Object candidate = iterator.next();\n                if (Objects.nullSafeEquals(candidate, element)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Check whether the given Enumeration contains the given element.\n     *\n     * @param enumeration the Enumeration to check\n     * @param element     the element to look for\n     * @return <code>true</code> if found, <code>false</code> else\n     */\n    public static boolean contains(Enumeration enumeration, Object element) {\n        if (enumeration != null) {\n            while (enumeration.hasMoreElements()) {\n                Object candidate = enumeration.nextElement();\n                if (Objects.nullSafeEquals(candidate, element)) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Check whether the given Collection contains the given element instance.\n     * <p>Enforces the given instance to be present, rather than returning\n     * <code>true</code> for an equal element as well.\n     *\n     * @param collection the Collection to check\n     * @param element    the element to look for\n     * @return <code>true</code> if found, <code>false</code> else\n     */\n    public static boolean containsInstance(Collection collection, Object element) {\n        if (collection != null) {\n            for (Object candidate : collection) {\n                if (candidate == element) {\n                    return true;\n                }\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return <code>true</code> if any element in '<code>candidates</code>' is\n     * contained in '<code>source</code>'; otherwise returns <code>false</code>.\n     *\n     * @param source     the source Collection\n     * @param candidates the candidates to search for\n     * @return whether any of the candidates has been found\n     */\n    public static boolean containsAny(Collection source, Collection candidates) {\n        if (isEmpty(source) || isEmpty(candidates)) {\n            return false;\n        }\n        for (Object candidate : candidates) {\n            if (source.contains(candidate)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return the first element in '<code>candidates</code>' that is contained in\n     * '<code>source</code>'. If no element in '<code>candidates</code>' is present in\n     * '<code>source</code>' returns <code>null</code>. Iteration order is\n     * {@link Collection} implementation specific.\n     *\n     * @param source     the source Collection\n     * @param candidates the candidates to search for\n     * @return the first present object, or <code>null</code> if not found\n     */\n    public static Object findFirstMatch(Collection source, Collection candidates) {\n        if (isEmpty(source) || isEmpty(candidates)) {\n            return null;\n        }\n        for (Object candidate : candidates) {\n            if (source.contains(candidate)) {\n                return candidate;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Find a single value of the given type in the given Collection.\n     *\n     * @param collection the Collection to search\n     * @param type       the type to look for\n     * @param <T>        the generic type parameter for {@code type}\n     * @return a value of the given type found if there is a clear match,\n     * or <code>null</code> if none or more than one such value found\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <T> T findValueOfType(Collection<?> collection, Class<T> type) {\n        if (isEmpty(collection)) {\n            return null;\n        }\n        T value = null;\n        for (Object element : collection) {\n            if (type == null || type.isInstance(element)) {\n                if (value != null) {\n                    // More than one value found... no clear single value.\n                    return null;\n                }\n                value = (T) element;\n            }\n        }\n        return value;\n    }\n\n    /**\n     * Find a single value of one of the given types in the given Collection:\n     * searching the Collection for a value of the first type, then\n     * searching for a value of the second type, etc.\n     *\n     * @param collection the collection to search\n     * @param types      the types to look for, in prioritized order\n     * @return a value of one of the given types found if there is a clear match,\n     * or <code>null</code> if none or more than one such value found\n     */\n    public static Object findValueOfType(Collection<?> collection, Class<?>[] types) {\n        if (isEmpty(collection) || Objects.isEmpty(types)) {\n            return null;\n        }\n        for (Class<?> type : types) {\n            Object value = findValueOfType(collection, type);\n            if (value != null) {\n                return value;\n            }\n        }\n        return null;\n    }\n\n    /**\n     * Determine whether the given Collection only contains a single unique object.\n     *\n     * @param collection the Collection to check\n     * @return <code>true</code> if the collection contains a single reference or\n     * multiple references to the same instance, <code>false</code> else\n     */\n    public static boolean hasUniqueObject(Collection collection) {\n        if (isEmpty(collection)) {\n            return false;\n        }\n        boolean hasCandidate = false;\n        Object candidate = null;\n        for (Object elem : collection) {\n            if (!hasCandidate) {\n                hasCandidate = true;\n                candidate = elem;\n            } else if (candidate != elem) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Find the common element type of the given Collection, if any.\n     *\n     * @param collection the Collection to check\n     * @return the common element type, or <code>null</code> if no clear\n     * common type has been found (or the collection was empty)\n     */\n    public static Class<?> findCommonElementType(Collection collection) {\n        if (isEmpty(collection)) {\n            return null;\n        }\n        Class<?> candidate = null;\n        for (Object val : collection) {\n            if (val != null) {\n                if (candidate == null) {\n                    candidate = val.getClass();\n                } else if (candidate != val.getClass()) {\n                    return null;\n                }\n            }\n        }\n        return candidate;\n    }\n\n    /**\n     * Marshal the elements from the given enumeration into an array of the given type.\n     * Enumeration elements must be assignable to the type of the given array. The array\n     * returned will be a different instance than the array given.\n     *\n     * @param enumeration the collection to convert to an array\n     * @param array       an array instance that matches the type of array to return\n     * @param <A>         the element type of the array that will be created\n     * @param <E>         the element type contained within the enumeration.\n     * @return a new array of type {@code A} that contains the elements in the specified {@code enumeration}.\n     */\n    public static <A, E extends A> A[] toArray(Enumeration<E> enumeration, A[] array) {\n        ArrayList<A> elements = new ArrayList<>();\n        while (enumeration.hasMoreElements()) {\n            elements.add(enumeration.nextElement());\n        }\n        return elements.toArray(array);\n    }\n\n    /**\n     * Adapt an enumeration to an iterator.\n     *\n     * @param enumeration the enumeration\n     * @param <E>         the type of elements in the enumeration\n     * @return the iterator\n     */\n    public static <E> Iterator<E> toIterator(Enumeration<E> enumeration) {\n        return new EnumerationIterator<>(enumeration);\n    }\n\n    /**\n     * Iterator wrapping an Enumeration.\n     */\n    private static class EnumerationIterator<E> implements Iterator<E> {\n\n        private final Enumeration<E> enumeration;\n\n        public EnumerationIterator(Enumeration<E> enumeration) {\n            this.enumeration = enumeration;\n        }\n\n        public boolean hasNext() {\n            return this.enumeration.hasMoreElements();\n        }\n\n        public E next() {\n            return this.enumeration.nextElement();\n        }\n\n        public void remove() throws UnsupportedOperationException {\n            throw new UnsupportedOperationException(\"Not supported\");\n        }\n    }\n}\n\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Conjunctor.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\n/**\n * A {@code Conjunctor} supplies a joined object. It is typically used for nested builders to return\n * to the source/original builder.\n *\n * @param <T> the type of joined object to return.\n * @since 0.12.0\n */\npublic interface Conjunctor<T> {\n\n    /**\n     * Returns the joined object.\n     *\n     * @return the joined object.\n     */\n    T and();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/DateFormats.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.text.DateFormat;\nimport java.text.ParseException;\nimport java.text.SimpleDateFormat;\nimport java.util.Date;\nimport java.util.TimeZone;\n\n/**\n * Utility methods to format and parse date strings.\n *\n * @since 0.10.0\n */\npublic final class DateFormats {\n\n    private DateFormats() {\n    } // prevent instantiation\n\n    private static final String ISO_8601_PATTERN = \"yyyy-MM-dd'T'HH:mm:ss'Z'\";\n\n    private static final String ISO_8601_MILLIS_PATTERN = \"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\";\n\n    private static final ThreadLocal<DateFormat> ISO_8601 = new ThreadLocal<DateFormat>() {\n        @Override\n        protected DateFormat initialValue() {\n            SimpleDateFormat format = new SimpleDateFormat(ISO_8601_PATTERN);\n            format.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n            return format;\n        }\n    };\n\n    private static final ThreadLocal<DateFormat> ISO_8601_MILLIS = new ThreadLocal<DateFormat>() {\n        @Override\n        protected DateFormat initialValue() {\n            SimpleDateFormat format = new SimpleDateFormat(ISO_8601_MILLIS_PATTERN);\n            format.setTimeZone(TimeZone.getTimeZone(\"UTC\"));\n            return format;\n        }\n    };\n\n    /**\n     * Return an ISO-8601-formatted string with millisecond precision representing the\n     * specified {@code date}.\n     *\n     * @param date the date for which to create an ISO-8601-formatted string\n     * @return the date represented as an ISO-8601-formatted string with millisecond precision.\n     */\n    public static String formatIso8601(Date date) {\n        return formatIso8601(date, true);\n    }\n\n    /**\n     * Returns an ISO-8601-formatted string with optional millisecond precision for the specified\n     * {@code date}.\n     *\n     * @param date          the date for which to create an ISO-8601-formatted string\n     * @param includeMillis whether to include millisecond notation within the string.\n     * @return the date represented as an ISO-8601-formatted string with optional millisecond precision.\n     */\n    public static String formatIso8601(Date date, boolean includeMillis) {\n        if (includeMillis) {\n            return ISO_8601_MILLIS.get().format(date);\n        }\n        return ISO_8601.get().format(date);\n    }\n\n    /**\n     * Parse the specified ISO-8601-formatted date string and return the corresponding {@link Date} instance.  The\n     * date string may optionally contain millisecond notation, and those milliseconds will be represented accordingly.\n     *\n     * @param s the ISO-8601-formatted string to parse\n     * @return the string's corresponding {@link Date} instance.\n     * @throws ParseException if the specified date string is not a validly-formatted ISO-8601 string.\n     */\n    public static Date parseIso8601Date(String s) throws ParseException {\n        Assert.notNull(s, \"String argument cannot be null.\");\n        if (s.lastIndexOf('.') > -1) { //assume ISO-8601 with milliseconds\n            return ISO_8601_MILLIS.get().parse(s);\n        } else { //assume ISO-8601 without millis:\n            return ISO_8601.get().parse(s);\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/InstantiationException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\n/**\n * {@link RuntimeException} equivalent of {@link java.lang.InstantiationException}.\n *\n * @since 0.1\n */\npublic class InstantiationException extends RuntimeException {\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public InstantiationException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/MapMutator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.util.Map;\n\n/**\n * Mutation (modifications) to a {@link Map} instance while also supporting method chaining. The Map interface's\n * {@link Map#put(Object, Object)}, {@link Map#remove(Object)}, {@link Map#putAll(Map)}, and {@link Map#clear()}\n * mutation methods do not support method chaining, so this interface enables that behavior.\n *\n * @param <K> map key type\n * @param <V> map value type\n * @param <T> the mutator subtype, for method chaining\n * @since 0.12.0\n */\npublic interface MapMutator<K, V, T extends MapMutator<K, V, T>> {\n\n    /**\n     * Removes the map entry with the specified key.\n     * <p>This method is the same as {@link Map#remove Map.remove}, but instead returns the mutator instance for\n     * method chaining.</p>\n     *\n     * @param key the key for the map entry to remove.\n     * @return the mutator/builder for method chaining.\n     */\n    T delete(K key);\n\n    /**\n     * Removes all entries from the map. The map will be empty after this call returns.\n     * <p>This method is the same as {@link Map#clear Map.clear}, but instead returns the mutator instance for\n     * method chaining.</p>\n     *\n     * @return the mutator/builder for method chaining.\n     */\n    T empty();\n\n    /**\n     * Sets the specified key/value pair in the map, overwriting any existing entry with the same key.\n     * A {@code null} or empty value will remove the entry from the map entirely.\n     *\n     * <p>This method is the same as {@link Map#put Map.put}, but instead returns the mutator instance for\n     * method chaining.</p>\n     *\n     * @param key   the map key\n     * @param value the value to set for the specified header parameter name\n     * @return the mutator/builder for method chaining.\n     */\n    T add(K key, V value);\n\n    /**\n     * Sets the specified key/value pairs in the map, overwriting any existing entries with the same keys.\n     * If any pair has a {@code null} or empty value, that pair will be removed from the map entirely.\n     *\n     * <p>This method is the same as {@link Map#putAll Map.putAll}, but instead returns the mutator instance for\n     * method chaining.</p>\n     *\n     * @param m the map to add\n     * @return the mutator/builder for method chaining.\n     */\n    T add(Map<? extends K, ? extends V> m);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Maps.java",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.util.Collections;\nimport java.util.HashMap;\nimport java.util.Map;\n\n/**\n * Utility class to help with the manipulation of working with Maps.\n *\n * @since 0.11.0\n */\npublic final class Maps {\n\n    private Maps() {\n    } //prevent instantiation\n\n    /**\n     * Creates a new map builder with a single entry.\n     * <p> Typical usage: <pre>{@code\n     * Map<K,V> result = Maps.of(\"key1\", value1)\n     *     .and(\"key2\", value2)\n     *     // ...\n     *     .build();\n     * }</pre>\n     *\n     * @param key   the key of an map entry to be added\n     * @param value the value of map entry to be added\n     * @param <K> the maps key type\n     * @param <V> the maps value type\n     * @return a new map builder with a single entry.\n     */\n    public static <K, V> MapBuilder<K, V> of(K key, V value) {\n        return new HashMapBuilder<K, V>().and(key, value);\n    }\n\n    /**\n     * Utility Builder class for fluently building maps:\n     * <p> Typical usage: <pre>{@code\n     * Map<K,V> result = Maps.of(\"key1\", value1)\n     *     .and(\"key2\", value2)\n     *     // ...\n     *     .build();\n     * }</pre>\n     *\n     * @param <K> the maps key type\n     * @param <V> the maps value type\n     */\n    public interface MapBuilder<K, V> extends Builder<Map<K, V>> {\n        /**\n         * Add a new entry to this map builder\n         *\n         * @param key   the key of an map entry to be added\n         * @param value the value of map entry to be added\n         * @return the current MapBuilder to allow for method chaining.\n         */\n        MapBuilder<K, V> and(K key, V value);\n\n        /**\n         * Returns the resulting Map object from this MapBuilder.\n         *\n         * @return the resulting Map object from this MapBuilder.\n         */\n        Map<K, V> build();\n    }\n\n    private static class HashMapBuilder<K, V> implements MapBuilder<K, V> {\n\n        private final Map<K, V> data = new HashMap<>();\n\n        public MapBuilder<K, V> and(K key, V value) {\n            data.put(key, value);\n            return this;\n        }\n\n        public Map<K, V> build() {\n            return Collections.unmodifiableMap(data);\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/NestedCollection.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\n/**\n * A {@link CollectionMutator} that can return access to its parent via the {@link Conjunctor#and() and()} method for\n * continued configuration.  For example:\n * <blockquote><pre>\n * builder\n *     .aNestedCollection()// etc...\n *     <b>.and() // return parent</b>\n * // resume parent configuration...</pre></blockquote>\n *\n * @param <E> the type of elements in the collection\n * @param <P> the parent to return\n * @since 0.12.0\n */\npublic interface NestedCollection<E, P> extends CollectionMutator<E, NestedCollection<E, P>>, Conjunctor<P> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Objects.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.io.Closeable;\nimport java.io.Flushable;\nimport java.io.IOException;\nimport java.lang.reflect.Array;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Map;\n\n/**\n * Utility methods for working with object instances to reduce pattern repetition and otherwise\n * increased cyclomatic complexity.\n */\npublic final class Objects {\n\n    private Objects() {\n    } //prevent instantiation\n\n    private static final int INITIAL_HASH = 7;\n    private static final int MULTIPLIER = 31;\n\n    private static final String EMPTY_STRING = \"\";\n    private static final String NULL_STRING = \"null\";\n    private static final String ARRAY_START = \"{\";\n    private static final String ARRAY_END = \"}\";\n    private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;\n    private static final String ARRAY_ELEMENT_SEPARATOR = \", \";\n\n    /**\n     * Return whether the given throwable is a checked exception:\n     * that is, neither a RuntimeException nor an Error.\n     *\n     * @param ex the throwable to check\n     * @return whether the throwable is a checked exception\n     * @see java.lang.Exception\n     * @see java.lang.RuntimeException\n     * @see java.lang.Error\n     */\n    public static boolean isCheckedException(Throwable ex) {\n        return !(ex instanceof RuntimeException || ex instanceof Error);\n    }\n\n    /**\n     * Check whether the given exception is compatible with the exceptions\n     * declared in a throws clause.\n     *\n     * @param ex                 the exception to checked\n     * @param declaredExceptions the exceptions declared in the throws clause\n     * @return whether the given exception is compatible\n     */\n    public static boolean isCompatibleWithThrowsClause(Throwable ex, Class[] declaredExceptions) {\n        if (!isCheckedException(ex)) {\n            return true;\n        }\n        if (declaredExceptions != null) {\n            int i = 0;\n            while (i < declaredExceptions.length) {\n                if (declaredExceptions[i].isAssignableFrom(ex.getClass())) {\n                    return true;\n                }\n                i++;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Returns {@code true} if the specified argument is an Object or primitive array, {@code false} otherwise.\n     *\n     * @param obj the object instance to check\n     * @return {@code true} if the specified argument is an Object or primitive array, {@code false} otherwise.\n     */\n    public static boolean isArray(Object obj) {\n        return (obj != null && obj.getClass().isArray());\n    }\n\n    /**\n     * Returns {@code true} if the specified argument:\n     * <ol>\n     *     <li>is {@code null}, or</li>\n     *     <li>is a CharSequence and {@link Strings#hasText(CharSequence)} is {@code false}, or</li>\n     *     <li>is a Collection or Map with zero size, or</li>\n     *     <li>is an empty array</li>\n     * </ol>\n     * <p>or {@code false} otherwise.</p>\n     *\n     * @param v object to check\n     * @return {@code true} if the specified argument is empty, {@code false} otherwise.\n     * @since 0.12.0\n     */\n    public static boolean isEmpty(Object v) {\n        return v == null ||\n                (v instanceof CharSequence && !Strings.hasText((CharSequence) v)) ||\n                (v instanceof Collection && Collections.isEmpty((Collection<?>) v)) ||\n                (v instanceof Map && Collections.isEmpty((Map<?, ?>) v)) ||\n                (v.getClass().isArray() && Array.getLength(v) == 0);\n    }\n\n    /**\n     * {@code true} if the specified array is null or zero length, {@code false} if populated.\n     *\n     * @param array the array to check\n     * @return {@code true} if the specified array is null or zero length, {@code false} if populated.\n     */\n    public static boolean isEmpty(Object[] array) {\n        return (array == null || array.length == 0);\n    }\n\n    /**\n     * Returns {@code true} if the specified byte array is null or of zero length, {@code false} if populated.\n     *\n     * @param array the byte array to check\n     * @return {@code true} if the specified byte array is null or of zero length, {@code false} if populated.\n     */\n    public static boolean isEmpty(byte[] array) {\n        return array == null || array.length == 0;\n    }\n\n    /**\n     * Returns {@code true} if the specified character array is null or of zero length, {@code false} otherwise.\n     *\n     * @param chars the character array to check\n     * @return {@code true} if the specified character array is null or of zero length, {@code false} otherwise.\n     */\n    public static boolean isEmpty(char[] chars) {\n        return chars == null || chars.length == 0;\n    }\n\n    /**\n     * Check whether the given array contains the given element.\n     *\n     * @param array   the array to check (may be <code>null</code>,\n     *                in which case the return value will always be <code>false</code>)\n     * @param element the element to check for\n     * @return whether the element has been found in the given array\n     */\n    public static boolean containsElement(Object[] array, Object element) {\n        if (array == null) {\n            return false;\n        }\n        for (Object arrayEle : array) {\n            if (nullSafeEquals(arrayEle, element)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Check whether the given array of enum constants contains a constant with the given name,\n     * ignoring case when determining a match.\n     *\n     * @param enumValues the enum values to check, typically the product of a call to MyEnum.values()\n     * @param constant   the constant name to find (must not be null or empty string)\n     * @return whether the constant has been found in the given array\n     */\n    public static boolean containsConstant(Enum<?>[] enumValues, String constant) {\n        return containsConstant(enumValues, constant, false);\n    }\n\n    /**\n     * Check whether the given array of enum constants contains a constant with the given name.\n     *\n     * @param enumValues    the enum values to check, typically the product of a call to MyEnum.values()\n     * @param constant      the constant name to find (must not be null or empty string)\n     * @param caseSensitive whether case is significant in determining a match\n     * @return whether the constant has been found in the given array\n     */\n    public static boolean containsConstant(Enum<?>[] enumValues, String constant, boolean caseSensitive) {\n        for (Enum<?> candidate : enumValues) {\n            if (caseSensitive ?\n                    candidate.toString().equals(constant) :\n                    candidate.toString().equalsIgnoreCase(constant)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Case insensitive alternative to {@link Enum#valueOf(Class, String)}.\n     *\n     * @param <E>        the concrete Enum type\n     * @param enumValues the array of all Enum constants in question, usually per Enum.values()\n     * @param constant   the constant to get the enum value of\n     * @return the enum constant of the specified enum type with the specified case-insensitive name\n     * @throws IllegalArgumentException if the given constant is not found in the given array\n     *                                  of enum values. Use {@link #containsConstant(Enum[], String)} as a guard to\n     *                                  avoid this exception.\n     */\n    public static <E extends Enum<?>> E caseInsensitiveValueOf(E[] enumValues, String constant) {\n        for (E candidate : enumValues) {\n            if (candidate.toString().equalsIgnoreCase(constant)) {\n                return candidate;\n            }\n        }\n        throw new IllegalArgumentException(\n                String.format(\"constant [%s] does not exist in enum type %s\",\n                        constant, enumValues.getClass().getComponentType().getName()));\n    }\n\n    /**\n     * Append the given object to the given array, returning a new array\n     * consisting of the input array contents plus the given object.\n     *\n     * @param array the array to append to (can be <code>null</code>)\n     * @param <A>   the type of each element in the specified {@code array}\n     * @param obj   the object to append\n     * @param <O>   the type of the specified object, which must be equal to or extend the <code>&lt;A&gt;</code> type.\n     * @return the new array (of the same component type; never <code>null</code>)\n     */\n    public static <A, O extends A> A[] addObjectToArray(A[] array, O obj) {\n        Class<?> compType = Object.class;\n        if (array != null) {\n            compType = array.getClass().getComponentType();\n        } else if (obj != null) {\n            compType = obj.getClass();\n        }\n        int newArrLength = (array != null ? array.length + 1 : 1);\n        @SuppressWarnings(\"unchecked\")\n        A[] newArr = (A[]) Array.newInstance(compType, newArrLength);\n        if (array != null) {\n            System.arraycopy(array, 0, newArr, 0, array.length);\n        }\n        newArr[newArr.length - 1] = obj;\n        return newArr;\n    }\n\n    /**\n     * Convert the given array (which may be a primitive array) to an\n     * object array (if necessary of primitive wrapper objects).\n     * <p>A <code>null</code> source value will be converted to an\n     * empty Object array.\n     *\n     * @param source the (potentially primitive) array\n     * @return the corresponding object array (never <code>null</code>)\n     * @throws IllegalArgumentException if the parameter is not an array\n     */\n    public static Object[] toObjectArray(Object source) {\n        if (source instanceof Object[]) {\n            return (Object[]) source;\n        }\n        if (source == null) {\n            return new Object[0];\n        }\n        if (!source.getClass().isArray()) {\n            throw new IllegalArgumentException(\"Source is not an array: \" + source);\n        }\n        int length = Array.getLength(source);\n        if (length == 0) {\n            return new Object[0];\n        }\n        Class wrapperType = Array.get(source, 0).getClass();\n        Object[] newArray = (Object[]) Array.newInstance(wrapperType, length);\n        for (int i = 0; i < length; i++) {\n            newArray[i] = Array.get(source, i);\n        }\n        return newArray;\n    }\n\n\n    //---------------------------------------------------------------------\n    // Convenience methods for content-based equality/hash-code handling\n    //---------------------------------------------------------------------\n\n    /**\n     * Determine if the given objects are equal, returning <code>true</code>\n     * if both are <code>null</code> or <code>false</code> if only one is\n     * <code>null</code>.\n     * <p>Compares arrays with <code>Arrays.equals</code>, performing an equality\n     * check based on the array elements rather than the array reference.\n     *\n     * @param o1 first Object to compare\n     * @param o2 second Object to compare\n     * @return whether the given objects are equal\n     * @see java.util.Arrays#equals\n     */\n    public static boolean nullSafeEquals(Object o1, Object o2) {\n        if (o1 == o2) {\n            return true;\n        }\n        if (o1 == null || o2 == null) {\n            return false;\n        }\n        if (o1.equals(o2)) {\n            return true;\n        }\n        if (o1.getClass().isArray() && o2.getClass().isArray()) {\n            if (o1 instanceof Object[] && o2 instanceof Object[]) {\n                return Arrays.equals((Object[]) o1, (Object[]) o2);\n            }\n            if (o1 instanceof boolean[] && o2 instanceof boolean[]) {\n                return Arrays.equals((boolean[]) o1, (boolean[]) o2);\n            }\n            if (o1 instanceof byte[] && o2 instanceof byte[]) {\n                return Arrays.equals((byte[]) o1, (byte[]) o2);\n            }\n            if (o1 instanceof char[] && o2 instanceof char[]) {\n                return Arrays.equals((char[]) o1, (char[]) o2);\n            }\n            if (o1 instanceof double[] && o2 instanceof double[]) {\n                return Arrays.equals((double[]) o1, (double[]) o2);\n            }\n            if (o1 instanceof float[] && o2 instanceof float[]) {\n                return Arrays.equals((float[]) o1, (float[]) o2);\n            }\n            if (o1 instanceof int[] && o2 instanceof int[]) {\n                return Arrays.equals((int[]) o1, (int[]) o2);\n            }\n            if (o1 instanceof long[] && o2 instanceof long[]) {\n                return Arrays.equals((long[]) o1, (long[]) o2);\n            }\n            if (o1 instanceof short[] && o2 instanceof short[]) {\n                return Arrays.equals((short[]) o1, (short[]) o2);\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Return as hash code for the given object; typically the value of\n     * <code>{@link Object#hashCode()}</code>. If the object is an array,\n     * this method will delegate to any of the <code>nullSafeHashCode</code>\n     * methods for arrays in this class. If the object is <code>null</code>,\n     * this method returns 0.\n     *\n     * @param obj the object to use for obtaining a hashcode\n     * @return the object's hashcode, which could be 0 if the object is null.\n     * @see #nullSafeHashCode(Object[])\n     * @see #nullSafeHashCode(boolean[])\n     * @see #nullSafeHashCode(byte[])\n     * @see #nullSafeHashCode(char[])\n     * @see #nullSafeHashCode(double[])\n     * @see #nullSafeHashCode(float[])\n     * @see #nullSafeHashCode(int[])\n     * @see #nullSafeHashCode(long[])\n     * @see #nullSafeHashCode(short[])\n     */\n    public static int nullSafeHashCode(Object obj) {\n        if (obj == null) {\n            return 0;\n        }\n        if (obj.getClass().isArray()) {\n            if (obj instanceof Object[]) {\n                return nullSafeHashCode((Object[]) obj);\n            }\n            if (obj instanceof boolean[]) {\n                return nullSafeHashCode((boolean[]) obj);\n            }\n            if (obj instanceof byte[]) {\n                return nullSafeHashCode((byte[]) obj);\n            }\n            if (obj instanceof char[]) {\n                return nullSafeHashCode((char[]) obj);\n            }\n            if (obj instanceof double[]) {\n                return nullSafeHashCode((double[]) obj);\n            }\n            if (obj instanceof float[]) {\n                return nullSafeHashCode((float[]) obj);\n            }\n            if (obj instanceof int[]) {\n                return nullSafeHashCode((int[]) obj);\n            }\n            if (obj instanceof long[]) {\n                return nullSafeHashCode((long[]) obj);\n            }\n            if (obj instanceof short[]) {\n                return nullSafeHashCode((short[]) obj);\n            }\n        }\n        return obj.hashCode();\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the array to obtain a hashcode\n     * @return the array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(Object... array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + nullSafeHashCode(array[i]);\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the boolean array to obtain a hashcode\n     * @return the boolean array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(boolean[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + hashCode(array[i]);\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the byte array to obtain a hashcode\n     * @return the byte array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(byte[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + array[i];\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the char array to obtain a hashcode\n     * @return the char array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(char[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + array[i];\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the double array to obtain a hashcode\n     * @return the double array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(double[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + hashCode(array[i]);\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the float array to obtain a hashcode\n     * @return the float array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(float[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + hashCode(array[i]);\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the int array to obtain a hashcode\n     * @return the int array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(int[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + array[i];\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the long array to obtain a hashcode\n     * @return the long array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(long[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + hashCode(array[i]);\n        }\n        return hash;\n    }\n\n    /**\n     * Return a hash code based on the contents of the specified array.\n     * If <code>array</code> is <code>null</code>, this method returns 0.\n     *\n     * @param array the short array to obtain a hashcode\n     * @return the short array's hashcode, which could be 0 if the array is null.\n     */\n    public static int nullSafeHashCode(short[] array) {\n        if (array == null) {\n            return 0;\n        }\n        int hash = INITIAL_HASH;\n        int arraySize = array.length;\n        for (int i = 0; i < arraySize; i++) {\n            hash = MULTIPLIER * hash + array[i];\n        }\n        return hash;\n    }\n\n    /**\n     * Return the same value as <code>{@link Boolean#hashCode()}</code>.\n     *\n     * @param bool the boolean to get a hashcode\n     * @return the same value as {@link Boolean#hashCode()}.\n     * @see Boolean#hashCode()\n     */\n    public static int hashCode(boolean bool) {\n        return bool ? 1231 : 1237;\n    }\n\n    /**\n     * Return the same value as <code>{@link Double#hashCode()}</code>.\n     *\n     * @param dbl the double to get a hashcode\n     * @return the same value as {@link Double#hashCode()}.\n     * @see Double#hashCode()\n     */\n    public static int hashCode(double dbl) {\n        long bits = Double.doubleToLongBits(dbl);\n        return hashCode(bits);\n    }\n\n    /**\n     * Return the same value as <code>{@link Float#hashCode()}</code>.\n     *\n     * @param flt the float to get a hashcode\n     * @return the same value as {@link Float#hashCode()}.\n     * @see Float#hashCode()\n     */\n    public static int hashCode(float flt) {\n        return Float.floatToIntBits(flt);\n    }\n\n    /**\n     * Return the same value as <code>{@link Long#hashCode()}</code>.\n     *\n     * @param lng the long to get a hashcode\n     * @return the same value as {@link Long#hashCode()}.\n     * @see Long#hashCode()\n     */\n    public static int hashCode(long lng) {\n        return (int) (lng ^ (lng >>> 32));\n    }\n\n\n    //---------------------------------------------------------------------\n    // Convenience methods for toString output\n    //---------------------------------------------------------------------\n\n    /**\n     * Return a String representation of an object's overall identity.\n     *\n     * @param obj the object (which may be <code>null</code>).\n     * @return the object's identity as String representation, or an empty String if the object was <code>null</code>.\n     */\n    public static String identityToString(Object obj) {\n        if (obj == null) {\n            return EMPTY_STRING;\n        }\n        return obj.getClass().getName() + \"@\" + getIdentityHexString(obj);\n    }\n\n    /**\n     * Return a hex String form of an object's identity hash code.\n     *\n     * @param obj the object\n     * @return the object's identity code in hex notation\n     */\n    public static String getIdentityHexString(Object obj) {\n        return Integer.toHexString(System.identityHashCode(obj));\n    }\n\n    /**\n     * Return a content-based String representation if <code>obj</code> is\n     * not <code>null</code>; otherwise returns an empty String.\n     * <p>Differs from {@link #nullSafeToString(Object)} in that it returns\n     * an empty String rather than \"null\" for a <code>null</code> value.\n     *\n     * @param obj the object to build a display String for\n     * @return a display String representation of <code>obj</code>\n     * @see #nullSafeToString(Object)\n     */\n    public static String getDisplayString(Object obj) {\n        if (obj == null) {\n            return EMPTY_STRING;\n        }\n        return nullSafeToString(obj);\n    }\n\n    /**\n     * Determine the class name for the given object.\n     * <p>Returns <code>\"null\"</code> if <code>obj</code> is <code>null</code>.\n     *\n     * @param obj the object to introspect (may be <code>null</code>)\n     * @return the corresponding class name\n     */\n    public static String nullSafeClassName(Object obj) {\n        return (obj != null ? obj.getClass().getName() : NULL_STRING);\n    }\n\n    /**\n     * Return a String representation of the specified Object.\n     * <p>Builds a String representation of the contents in case of an array.\n     * Returns <code>\"null\"</code> if <code>obj</code> is <code>null</code>.\n     *\n     * @param obj the object to build a String representation for\n     * @return a String representation of <code>obj</code>\n     */\n    public static String nullSafeToString(Object obj) {\n        if (obj == null) {\n            return NULL_STRING;\n        }\n        if (obj instanceof String) {\n            return (String) obj;\n        }\n        if (obj instanceof Object[]) {\n            return nullSafeToString((Object[]) obj);\n        }\n        if (obj instanceof boolean[]) {\n            return nullSafeToString((boolean[]) obj);\n        }\n        if (obj instanceof byte[]) {\n            return nullSafeToString((byte[]) obj);\n        }\n        if (obj instanceof char[]) {\n            return nullSafeToString((char[]) obj);\n        }\n        if (obj instanceof double[]) {\n            return nullSafeToString((double[]) obj);\n        }\n        if (obj instanceof float[]) {\n            return nullSafeToString((float[]) obj);\n        }\n        if (obj instanceof int[]) {\n            return nullSafeToString((int[]) obj);\n        }\n        if (obj instanceof long[]) {\n            return nullSafeToString((long[]) obj);\n        }\n        if (obj instanceof short[]) {\n            return nullSafeToString((short[]) obj);\n        }\n        String str = obj.toString();\n        return (str != null ? str : EMPTY_STRING);\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(Object[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n            sb.append(String.valueOf(array[i]));\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(boolean[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n\n            sb.append(array[i]);\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(byte[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n            sb.append(array[i]);\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(char[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n            sb.append(\"'\").append(array[i]).append(\"'\");\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(double[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n\n            sb.append(array[i]);\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(float[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n\n            sb.append(array[i]);\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(int[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n            sb.append(array[i]);\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(long[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n            sb.append(array[i]);\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Return a String representation of the contents of the specified array.\n     * <p>The String representation consists of a list of the array's elements,\n     * enclosed in curly braces (<code>\"{}\"</code>). Adjacent elements are separated\n     * by the characters <code>\", \"</code> (a comma followed by a space). Returns\n     * <code>\"null\"</code> if <code>array</code> is <code>null</code>.\n     *\n     * @param array the array to build a String representation for\n     * @return a String representation of <code>array</code>\n     */\n    public static String nullSafeToString(short[] array) {\n        if (array == null) {\n            return NULL_STRING;\n        }\n        int length = array.length;\n        if (length == 0) {\n            return EMPTY_ARRAY;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < length; i++) {\n            if (i == 0) {\n                sb.append(ARRAY_START);\n            } else {\n                sb.append(ARRAY_ELEMENT_SEPARATOR);\n            }\n            sb.append(array[i]);\n        }\n        sb.append(ARRAY_END);\n        return sb.toString();\n    }\n\n    /**\n     * Iterate over the specified {@link Closeable} instances, invoking\n     * {@link Closeable#close()} on each one, ignoring any potential {@link IOException}s.\n     *\n     * @param closeables the closeables to close.\n     */\n    public static void nullSafeClose(Closeable... closeables) {\n        if (closeables == null) {\n            return;\n        }\n\n        for (Closeable closeable : closeables) {\n            if (closeable != null) {\n                try {\n                    closeable.close();\n                } catch (IOException e) {\n                    //Ignore the exception during close.\n                }\n            }\n        }\n    }\n\n    /**\n     * Iterate over the specified {@link Flushable} instances, invoking\n     * {@link Flushable#flush()} on each one, ignoring any potential {@link IOException}s.\n     *\n     * @param flushables the flushables to flush.\n     * @since 0.12.0\n     */\n    public static void nullSafeFlush(Flushable... flushables) {\n        if (flushables == null) return;\n        for (Flushable flushable : flushables) {\n            if (flushable != null) {\n                try {\n                    flushable.flush();\n                } catch (IOException ignored) {\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Registry.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.util.Map;\n\n/**\n * An immutable (read-only) repository of key-value pairs. In addition to {@link Map} read methods, this interface also\n * provides guaranteed/expected lookup via the {@link #forKey(Object)} method.\n *\n * <p><b>Immutability</b></p>\n *\n * <p>Registries are immutable and cannot be changed.  {@code Registry} extends the\n * {@link Map} interface purely out of convenience: to allow easy key/value\n * pair access and iteration, and other conveniences provided by the Map interface, as well as for seamless use with\n * existing Map-based APIs.  Attempting to call any of\n * the {@link Map} interface's mutation methods however (such as {@link Map#put(Object, Object) put},\n * {@link Map#remove(Object) remove}, {@link Map#clear() clear}, etc) will throw an\n * {@link UnsupportedOperationException}.</p>\n *\n * @param <K> key type\n * @param <V> value type\n * @since 0.12.0\n */\npublic interface Registry<K, V> extends Map<K, V> {\n\n    /**\n     * Returns the value assigned the specified key or throws an {@code IllegalArgumentException} if there is no\n     * associated value.  If a value is not required, consider using the {@link #get(Object)} method instead.\n     *\n     * @param key the registry key assigned to the required value\n     * @return the value assigned the specified key\n     * @throws IllegalArgumentException if there is no value assigned the specified key\n     * @see #get(Object)\n     */\n    V forKey(K key) throws IllegalArgumentException;\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/RuntimeEnvironment.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.security.Provider;\nimport java.security.Security;\nimport java.util.concurrent.atomic.AtomicBoolean;\n\n/**\n * No longer used by JJWT.  Will be removed before the 1.0 final release.\n *\n * @deprecated since 0.12.0. will be removed before the 1.0 final release.\n */\n@Deprecated\npublic final class RuntimeEnvironment {\n\n    private RuntimeEnvironment() {\n    } //prevent instantiation\n\n    private static final String BC_PROVIDER_CLASS_NAME = \"org.bouncycastle.jce.provider.BouncyCastleProvider\";\n\n    private static final AtomicBoolean bcLoaded = new AtomicBoolean(false);\n\n    /**\n     * {@code true} if BouncyCastle is in the runtime classpath, {@code false} otherwise.\n     *\n     * @deprecated since 0.12.0. will be removed before the 1.0 final release.\n     */\n    @Deprecated\n    public static final boolean BOUNCY_CASTLE_AVAILABLE = Classes.isAvailable(BC_PROVIDER_CLASS_NAME);\n\n    /**\n     * Register BouncyCastle as a JCA provider in the system's {@link Security#getProviders() Security Providers} list\n     * if BouncyCastle is in the runtime classpath.\n     *\n     * @deprecated since 0.12.0. will be removed before the 1.0 final release.\n     */\n    @Deprecated\n    public static void enableBouncyCastleIfPossible() {\n\n        if (!BOUNCY_CASTLE_AVAILABLE || bcLoaded.get()) {\n            return;\n        }\n\n        try {\n            Class<Provider> clazz = Classes.forName(BC_PROVIDER_CLASS_NAME);\n\n            //check to see if the user has already registered the BC provider:\n\n            Provider[] providers = Security.getProviders();\n\n            for (Provider provider : providers) {\n                if (clazz.isInstance(provider)) {\n                    bcLoaded.set(true);\n                    return;\n                }\n            }\n\n            //bc provider not enabled - add it:\n            Provider provider = Classes.newInstance(clazz);\n            Security.addProvider(provider);\n            bcLoaded.set(true);\n\n        } catch (UnknownClassException e) {\n            //not available\n        }\n    }\n\n    static {\n        enableBouncyCastleIfPossible();\n    }\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Strings.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.Charset;\nimport java.nio.charset.StandardCharsets;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Enumeration;\nimport java.util.Iterator;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.Locale;\nimport java.util.Properties;\nimport java.util.Set;\nimport java.util.StringTokenizer;\nimport java.util.TreeSet;\n\n/**\n * Utility methods for working with Strings to reduce pattern repetition and otherwise\n * increased cyclomatic complexity.\n */\npublic final class Strings {\n\n    /**\n     * Empty String, equal to <code>&quot;&quot;</code>.\n     */\n    public static final String EMPTY = \"\";\n\n    private static final CharBuffer EMPTY_BUF = CharBuffer.wrap(EMPTY);\n\n    private static final String FOLDER_SEPARATOR = \"/\";\n\n    private static final String WINDOWS_FOLDER_SEPARATOR = \"\\\\\";\n\n    private static final String TOP_PATH = \"..\";\n\n    private static final String CURRENT_PATH = \".\";\n\n    private static final char EXTENSION_SEPARATOR = '.';\n\n    /**\n     * Convenience alias for {@link StandardCharsets#UTF_8}.\n     */\n    public static final Charset UTF_8 = StandardCharsets.UTF_8;\n\n    private Strings() {\n    } //prevent instantiation\n\n    //---------------------------------------------------------------------\n    // General convenience methods for working with Strings\n    //---------------------------------------------------------------------\n\n    /**\n     * Check that the given CharSequence is neither <code>null</code> nor of length 0.\n     * Note: Will return <code>true</code> for a CharSequence that purely consists of whitespace.\n     * <pre>\n     * Strings.hasLength(null) = false\n     * Strings.hasLength(\"\") = false\n     * Strings.hasLength(\" \") = true\n     * Strings.hasLength(\"Hello\") = true\n     * </pre>\n     *\n     * @param str the CharSequence to check (may be <code>null</code>)\n     * @return <code>true</code> if the CharSequence is not null and has length\n     * @see #hasText(String)\n     */\n    public static boolean hasLength(CharSequence str) {\n        return (str != null && str.length() > 0);\n    }\n\n    /**\n     * Check that the given String is neither <code>null</code> nor of length 0.\n     * Note: Will return <code>true</code> for a String that purely consists of whitespace.\n     *\n     * @param str the String to check (may be <code>null</code>)\n     * @return <code>true</code> if the String is not null and has length\n     * @see #hasLength(CharSequence)\n     */\n    public static boolean hasLength(String str) {\n        return hasLength((CharSequence) str);\n    }\n\n    /**\n     * Check whether the given CharSequence has actual text.\n     * More specifically, returns <code>true</code> if the string not <code>null</code>,\n     * its length is greater than 0, and it contains at least one non-whitespace character.\n     * <pre>\n     * Strings.hasText(null) = false\n     * Strings.hasText(\"\") = false\n     * Strings.hasText(\" \") = false\n     * Strings.hasText(\"12345\") = true\n     * Strings.hasText(\" 12345 \") = true\n     * </pre>\n     *\n     * @param str the CharSequence to check (may be <code>null</code>)\n     * @return <code>true</code> if the CharSequence is not <code>null</code>,\n     * its length is greater than 0, and it does not contain whitespace only\n     * @see java.lang.Character#isWhitespace\n     */\n    public static boolean hasText(CharSequence str) {\n        if (!hasLength(str)) {\n            return false;\n        }\n        int strLen = str.length();\n        for (int i = 0; i < strLen; i++) {\n            if (!Character.isWhitespace(str.charAt(i))) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Check whether the given String has actual text.\n     * More specifically, returns <code>true</code> if the string not <code>null</code>,\n     * its length is greater than 0, and it contains at least one non-whitespace character.\n     *\n     * @param str the String to check (may be <code>null</code>)\n     * @return <code>true</code> if the String is not <code>null</code>, its length is\n     * greater than 0, and it does not contain whitespace only\n     * @see #hasText(CharSequence)\n     */\n    public static boolean hasText(String str) {\n        return hasText((CharSequence) str);\n    }\n\n    /**\n     * Check whether the given CharSequence contains any whitespace characters.\n     *\n     * @param str the CharSequence to check (may be <code>null</code>)\n     * @return <code>true</code> if the CharSequence is not empty and\n     * contains at least 1 whitespace character\n     * @see java.lang.Character#isWhitespace\n     */\n    public static boolean containsWhitespace(CharSequence str) {\n        if (!hasLength(str)) {\n            return false;\n        }\n        int strLen = str.length();\n        for (int i = 0; i < strLen; i++) {\n            if (Character.isWhitespace(str.charAt(i))) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    /**\n     * Check whether the given String contains any whitespace characters.\n     *\n     * @param str the String to check (may be <code>null</code>)\n     * @return <code>true</code> if the String is not empty and\n     * contains at least 1 whitespace character\n     * @see #containsWhitespace(CharSequence)\n     */\n    public static boolean containsWhitespace(String str) {\n        return containsWhitespace((CharSequence) str);\n    }\n\n    /**\n     * Trim leading and trailing whitespace from the given String.\n     *\n     * @param str the String to check\n     * @return the trimmed String\n     * @see java.lang.Character#isWhitespace\n     */\n    public static String trimWhitespace(String str) {\n        return (String) trimWhitespace((CharSequence) str);\n    }\n\n\n    private static CharSequence trimWhitespace(CharSequence str) {\n        if (!hasLength(str)) {\n            return str;\n        }\n        final int length = str.length();\n\n        int start = 0;\n        while (start < length && Character.isWhitespace(str.charAt(start))) {\n            start++;\n        }\n\n        int end = length;\n        while (start < length && Character.isWhitespace(str.charAt(end - 1))) {\n            end--;\n        }\n\n        return ((start > 0) || (end < length)) ? str.subSequence(start, end) : str;\n    }\n\n    /**\n     * Returns the specified string without leading or trailing whitespace, or {@code null} if there are no remaining\n     * characters.\n     *\n     * @param str the string to clean\n     * @return the specified string without leading or trailing whitespace, or {@code null} if there are no remaining\n     * characters.\n     */\n    public static String clean(String str) {\n        CharSequence result = clean((CharSequence) str);\n\n        return result != null ? result.toString() : null;\n    }\n\n    /**\n     * Returns the specified {@code CharSequence} without leading or trailing whitespace, or {@code null} if there are\n     * no remaining characters.\n     *\n     * @param str the {@code CharSequence} to clean\n     * @return the specified string without leading or trailing whitespace, or {@code null} if there are no remaining\n     * characters.\n     */\n    public static CharSequence clean(CharSequence str) {\n        str = trimWhitespace(str);\n        if (!hasLength(str)) {\n            return null;\n        }\n        return str;\n    }\n\n    /**\n     * Returns the specified string's UTF-8 bytes, or {@code null} if the string is {@code null}.\n     *\n     * @param s the string to obtain UTF-8 bytes\n     * @return the specified string's UTF-8 bytes, or {@code null} if the string is {@code null}.\n     * @since 0.12.0\n     */\n    public static byte[] utf8(CharSequence s) {\n        if (s == null) return null;\n        CharBuffer cb = s instanceof CharBuffer ? (CharBuffer) s : CharBuffer.wrap(s);\n        cb.mark();\n        ByteBuffer buf = UTF_8.encode(cb);\n        byte[] bytes = new byte[buf.remaining()];\n        buf.get(bytes);\n        cb.reset();\n        return bytes;\n    }\n\n    /**\n     * Returns {@code new String(utf8Bytes, StandardCharsets.UTF_8)}.\n     *\n     * @param utf8Bytes UTF-8 bytes to use with the {@code String} constructor.\n     * @return {@code new String(utf8Bytes, StandardCharsets.UTF_8)}.\n     * @since 0.12.0\n     */\n    public static String utf8(byte[] utf8Bytes) {\n        return new String(utf8Bytes, UTF_8);\n    }\n\n    /**\n     * Returns {@code new String(asciiBytes, StandardCharsets.US_ASCII)}.\n     *\n     * @param asciiBytes US_ASCII bytes to use with the {@code String} constructor.\n     * @return {@code new String(asciiBytes, StandardCharsets.US_ASCII)}.\n     * @since 0.12.0\n     */\n    public static String ascii(byte[] asciiBytes) {\n        return new String(asciiBytes, StandardCharsets.US_ASCII);\n    }\n\n    /**\n     * Returns the {@link StandardCharsets#US_ASCII US_ASCII}-encoded bytes of the specified {@code CharSequence}.\n     *\n     * @param s the {@code CharSequence} to encode to {@code US_ASCII}.\n     * @return the {@link StandardCharsets#US_ASCII US_ASCII}-encoded bytes of the specified {@code CharSequence}.\n     */\n    public static byte[] ascii(CharSequence s) {\n        byte[] bytes = null;\n        if (s != null) {\n            CharBuffer cb = s instanceof CharBuffer ? (CharBuffer) s : CharBuffer.wrap(s);\n            ByteBuffer buf = StandardCharsets.US_ASCII.encode(cb);\n            bytes = new byte[buf.remaining()];\n            buf.get(bytes);\n        }\n        return bytes;\n    }\n\n    /**\n     * Returns a {@code CharBuffer} that wraps {@code seq}, or an empty buffer if {@code seq} is null. If\n     * {@code seq} is already a {@code CharBuffer}, it is returned unmodified.\n     *\n     * @param seq the {@code CharSequence} to wrap.\n     * @return a {@code CharBuffer} that wraps {@code seq}, or an empty buffer if {@code seq} is null.\n     */\n    public static CharBuffer wrap(CharSequence seq) {\n        if (!hasLength(seq)) return EMPTY_BUF;\n        if (seq instanceof CharBuffer) return (CharBuffer) seq;\n        return CharBuffer.wrap(seq);\n    }\n\n    /**\n     * Returns a String representation (1s and 0s) of the specified byte.\n     *\n     * @param b the byte to represent as 1s and 0s.\n     * @return a String representation (1s and 0s) of the specified byte.\n     */\n    public static String toBinary(byte b) {\n        String bString = Integer.toBinaryString(b & 0xFF);\n        return String.format(\"%8s\", bString).replace((char) Character.SPACE_SEPARATOR, '0');\n    }\n\n    /**\n     * Returns a String representation (1s and 0s) of the specified byte array.\n     *\n     * @param bytes the bytes to represent as 1s and 0s.\n     * @return a String representation (1s and 0s) of the specified byte array.\n     */\n    public static String toBinary(byte[] bytes) {\n        StringBuilder sb = new StringBuilder(19); //16 characters + 3 space characters\n        for (byte b : bytes) {\n            if (sb.length() > 0) {\n                sb.append((char) Character.SPACE_SEPARATOR);\n            }\n            String val = toBinary(b);\n            sb.append(val);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Returns a hexadecimal String representation of the specified byte array.\n     *\n     * @param bytes the bytes to represent as a hexidecimal string.\n     * @return a hexadecimal String representation of the specified byte array.\n     */\n    public static String toHex(byte[] bytes) {\n        StringBuilder result = new StringBuilder();\n        for (byte temp : bytes) {\n            if (result.length() > 0) {\n                result.append((char) Character.SPACE_SEPARATOR);\n            }\n            result.append(String.format(\"%02x\", temp));\n        }\n        return result.toString();\n    }\n\n    /**\n     * Trim <i>all</i> whitespace from the given String:\n     * leading, trailing, and intermediate characters.\n     *\n     * @param str the String to check\n     * @return the trimmed String\n     * @see java.lang.Character#isWhitespace\n     */\n    public static String trimAllWhitespace(String str) {\n        if (!hasLength(str)) {\n            return str;\n        }\n        StringBuilder sb = new StringBuilder(str);\n        int index = 0;\n        while (sb.length() > index) {\n            if (Character.isWhitespace(sb.charAt(index))) {\n                sb.deleteCharAt(index);\n            } else {\n                index++;\n            }\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Trim leading whitespace from the given String.\n     *\n     * @param str the String to check\n     * @return the trimmed String\n     * @see java.lang.Character#isWhitespace\n     */\n    public static String trimLeadingWhitespace(String str) {\n        if (!hasLength(str)) {\n            return str;\n        }\n        StringBuilder sb = new StringBuilder(str);\n        while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {\n            sb.deleteCharAt(0);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Trim trailing whitespace from the given String.\n     *\n     * @param str the String to check\n     * @return the trimmed String\n     * @see java.lang.Character#isWhitespace\n     */\n    public static String trimTrailingWhitespace(String str) {\n        if (!hasLength(str)) {\n            return str;\n        }\n        StringBuilder sb = new StringBuilder(str);\n        while (sb.length() > 0 && Character.isWhitespace(sb.charAt(sb.length() - 1))) {\n            sb.deleteCharAt(sb.length() - 1);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Trim all occurrences of the supplied leading character from the given String.\n     *\n     * @param str              the String to check\n     * @param leadingCharacter the leading character to be trimmed\n     * @return the trimmed String\n     */\n    public static String trimLeadingCharacter(String str, char leadingCharacter) {\n        if (!hasLength(str)) {\n            return str;\n        }\n        StringBuilder sb = new StringBuilder(str);\n        while (sb.length() > 0 && sb.charAt(0) == leadingCharacter) {\n            sb.deleteCharAt(0);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Trim all occurrences of the supplied trailing character from the given String.\n     *\n     * @param str               the String to check\n     * @param trailingCharacter the trailing character to be trimmed\n     * @return the trimmed String\n     */\n    public static String trimTrailingCharacter(String str, char trailingCharacter) {\n        if (!hasLength(str)) {\n            return str;\n        }\n        StringBuilder sb = new StringBuilder(str);\n        while (sb.length() > 0 && sb.charAt(sb.length() - 1) == trailingCharacter) {\n            sb.deleteCharAt(sb.length() - 1);\n        }\n        return sb.toString();\n    }\n\n\n    /**\n     * Returns {@code true} if the given string starts with the specified case-insensitive prefix, {@code false} otherwise.\n     *\n     * @param str    the String to check\n     * @param prefix the prefix to look for\n     * @return {@code true} if the given string starts with the specified case-insensitive prefix, {@code false} otherwise.\n     * @see java.lang.String#startsWith\n     */\n    public static boolean startsWithIgnoreCase(String str, String prefix) {\n        if (str == null || prefix == null) {\n            return false;\n        }\n        if (str.length() < prefix.length()) {\n            return false;\n        }\n        if (str.startsWith(prefix)) {\n            return true;\n        }\n        String lcStr = str.substring(0, prefix.length()).toLowerCase();\n        String lcPrefix = prefix.toLowerCase();\n        return lcStr.equals(lcPrefix);\n    }\n\n    /**\n     * Returns {@code true} if the given string ends with the specified case-insensitive suffix, {@code false} otherwise.\n     *\n     * @param str    the String to check\n     * @param suffix the suffix to look for\n     * @return {@code true} if the given string ends with the specified case-insensitive suffix, {@code false} otherwise.\n     * @see java.lang.String#endsWith\n     */\n    public static boolean endsWithIgnoreCase(String str, String suffix) {\n        if (str == null || suffix == null) {\n            return false;\n        }\n        if (str.endsWith(suffix)) {\n            return true;\n        }\n        if (str.length() < suffix.length()) {\n            return false;\n        }\n\n        String lcStr = str.substring(str.length() - suffix.length()).toLowerCase();\n        String lcSuffix = suffix.toLowerCase();\n        return lcStr.equals(lcSuffix);\n    }\n\n    /**\n     * Returns {@code true} if the given string matches the given substring at the given index, {@code false} otherwise.\n     *\n     * @param str       the original string (or StringBuilder)\n     * @param index     the index in the original string to start matching against\n     * @param substring the substring to match at the given index\n     * @return {@code true} if the given string matches the given substring at the given index, {@code false} otherwise.\n     */\n    public static boolean substringMatch(CharSequence str, int index, CharSequence substring) {\n        for (int j = 0; j < substring.length(); j++) {\n            int i = index + j;\n            if (i >= str.length() || str.charAt(i) != substring.charAt(j)) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Returns the number of occurrences the substring {@code sub} appears in string {@code str}.\n     *\n     * @param str string to search in. Return 0 if this is null.\n     * @param sub string to search for. Return 0 if this is null.\n     * @return the number of occurrences the substring {@code sub} appears in string {@code str}.\n     */\n    public static int countOccurrencesOf(String str, String sub) {\n        if (str == null || sub == null || str.length() == 0 || sub.length() == 0) {\n            return 0;\n        }\n        int count = 0;\n        int pos = 0;\n        int idx;\n        while ((idx = str.indexOf(sub, pos)) != -1) {\n            ++count;\n            pos = idx + sub.length();\n        }\n        return count;\n    }\n\n    /**\n     * Replace all occurrences of a substring within a string with\n     * another string.\n     *\n     * @param inString   String to examine\n     * @param oldPattern String to replace\n     * @param newPattern String to insert\n     * @return a String with the replacements\n     */\n    public static String replace(String inString, String oldPattern, String newPattern) {\n        if (!hasLength(inString) || !hasLength(oldPattern) || newPattern == null) {\n            return inString;\n        }\n        StringBuilder sb = new StringBuilder();\n        int pos = 0; // our position in the old string\n        int index = inString.indexOf(oldPattern);\n        // the index of an occurrence we've found, or -1\n        int patLen = oldPattern.length();\n        while (index >= 0) {\n            sb.append(inString.substring(pos, index));\n            sb.append(newPattern);\n            pos = index + patLen;\n            index = inString.indexOf(oldPattern, pos);\n        }\n        sb.append(inString.substring(pos));\n        // remember to append any characters to the right of a match\n        return sb.toString();\n    }\n\n    /**\n     * Delete all occurrences of the given substring.\n     *\n     * @param inString the original String\n     * @param pattern  the pattern to delete all occurrences of\n     * @return the resulting String\n     */\n    public static String delete(String inString, String pattern) {\n        return replace(inString, pattern, \"\");\n    }\n\n    /**\n     * Delete any character in a given String.\n     *\n     * @param inString      the original String\n     * @param charsToDelete a set of characters to delete.\n     *                      E.g. \"az\\n\" will delete 'a's, 'z's and new lines.\n     * @return the resulting String\n     */\n    public static String deleteAny(String inString, String charsToDelete) {\n        if (!hasLength(inString) || !hasLength(charsToDelete)) {\n            return inString;\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < inString.length(); i++) {\n            char c = inString.charAt(i);\n            if (charsToDelete.indexOf(c) == -1) {\n                sb.append(c);\n            }\n        }\n        return sb.toString();\n    }\n\n\n    //---------------------------------------------------------------------\n    // Convenience methods for working with formatted Strings\n    //---------------------------------------------------------------------\n\n    /**\n     * Quote the given String with single quotes.\n     *\n     * @param str the input String (e.g. \"myString\")\n     * @return the quoted String (e.g. \"'myString'\"),\n     * or <code>null</code> if the input was <code>null</code>\n     */\n    public static String quote(String str) {\n        return (str != null ? \"'\" + str + \"'\" : null);\n    }\n\n    /**\n     * Turn the given Object into a String with single quotes\n     * if it is a String; keeping the Object as-is else.\n     *\n     * @param obj the input Object (e.g. \"myString\")\n     * @return the quoted String (e.g. \"'myString'\"),\n     * or the input object as-is if not a String\n     */\n    public static Object quoteIfString(Object obj) {\n        return (obj instanceof String ? quote((String) obj) : obj);\n    }\n\n    /**\n     * Unqualify a string qualified by a '.' dot character. For example,\n     * \"this.name.is.qualified\", returns \"qualified\".\n     *\n     * @param qualifiedName the qualified name\n     * @return an unqualified string by stripping all previous text before (and including) the last period character.\n     */\n    public static String unqualify(String qualifiedName) {\n        return unqualify(qualifiedName, '.');\n    }\n\n    /**\n     * Unqualify a string qualified by a separator character. For example,\n     * \"this:name:is:qualified\" returns \"qualified\" if using a ':' separator.\n     *\n     * @param qualifiedName the qualified name\n     * @param separator     the separator\n     * @return an unqualified string by stripping all previous text before and including the last {@code separator} character.\n     */\n    public static String unqualify(String qualifiedName, char separator) {\n        return qualifiedName.substring(qualifiedName.lastIndexOf(separator) + 1);\n    }\n\n    /**\n     * Capitalize a <code>String</code>, changing the first letter to\n     * upper case as per {@link Character#toUpperCase(char)}.\n     * No other letters are changed.\n     *\n     * @param str the String to capitalize, may be <code>null</code>\n     * @return the capitalized String, <code>null</code> if null\n     */\n    public static String capitalize(String str) {\n        return changeFirstCharacterCase(str, true);\n    }\n\n    /**\n     * Uncapitalize a <code>String</code>, changing the first letter to\n     * lower case as per {@link Character#toLowerCase(char)}.\n     * No other letters are changed.\n     *\n     * @param str the String to uncapitalize, may be <code>null</code>\n     * @return the uncapitalized String, <code>null</code> if null\n     */\n    public static String uncapitalize(String str) {\n        return changeFirstCharacterCase(str, false);\n    }\n\n    private static String changeFirstCharacterCase(String str, boolean capitalize) {\n        if (str == null || str.length() == 0) {\n            return str;\n        }\n        StringBuilder sb = new StringBuilder(str.length());\n        if (capitalize) {\n            sb.append(Character.toUpperCase(str.charAt(0)));\n        } else {\n            sb.append(Character.toLowerCase(str.charAt(0)));\n        }\n        sb.append(str.substring(1));\n        return sb.toString();\n    }\n\n    /**\n     * Extract the filename from the given path,\n     * e.g. \"mypath/myfile.txt\" -&gt; \"myfile.txt\".\n     *\n     * @param path the file path (may be <code>null</code>)\n     * @return the extracted filename, or <code>null</code> if none\n     */\n    public static String getFilename(String path) {\n        if (path == null) {\n            return null;\n        }\n        int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);\n        return (separatorIndex != -1 ? path.substring(separatorIndex + 1) : path);\n    }\n\n    /**\n     * Extract the filename extension from the given path,\n     * e.g. \"mypath/myfile.txt\" -&gt; \"txt\".\n     *\n     * @param path the file path (may be <code>null</code>)\n     * @return the extracted filename extension, or <code>null</code> if none\n     */\n    public static String getFilenameExtension(String path) {\n        if (path == null) {\n            return null;\n        }\n        int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);\n        if (extIndex == -1) {\n            return null;\n        }\n        int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);\n        if (folderIndex > extIndex) {\n            return null;\n        }\n        return path.substring(extIndex + 1);\n    }\n\n    /**\n     * Strip the filename extension from the given path,\n     * e.g. \"mypath/myfile.txt\" -&gt; \"mypath/myfile\".\n     *\n     * @param path the file path (may be <code>null</code>)\n     * @return the path with stripped filename extension,\n     * or <code>null</code> if none\n     */\n    public static String stripFilenameExtension(String path) {\n        if (path == null) {\n            return null;\n        }\n        int extIndex = path.lastIndexOf(EXTENSION_SEPARATOR);\n        if (extIndex == -1) {\n            return path;\n        }\n        int folderIndex = path.lastIndexOf(FOLDER_SEPARATOR);\n        if (folderIndex > extIndex) {\n            return path;\n        }\n        return path.substring(0, extIndex);\n    }\n\n    /**\n     * Apply the given relative path to the given path,\n     * assuming standard Java folder separation (i.e. \"/\" separators).\n     *\n     * @param path         the path to start from (usually a full file path)\n     * @param relativePath the relative path to apply\n     *                     (relative to the full file path above)\n     * @return the full file path that results from applying the relative path\n     */\n    public static String applyRelativePath(String path, String relativePath) {\n        int separatorIndex = path.lastIndexOf(FOLDER_SEPARATOR);\n        if (separatorIndex != -1) {\n            String newPath = path.substring(0, separatorIndex);\n            if (!relativePath.startsWith(FOLDER_SEPARATOR)) {\n                newPath += FOLDER_SEPARATOR;\n            }\n            return newPath + relativePath;\n        } else {\n            return relativePath;\n        }\n    }\n\n    /**\n     * Normalize the path by suppressing sequences like \"path/..\" and\n     * inner simple dots.\n     * <p>The result is convenient for path comparison. For other uses,\n     * notice that Windows separators (\"\\\") are replaced by simple slashes.\n     *\n     * @param path the original path\n     * @return the normalized path\n     */\n    public static String cleanPath(String path) {\n        if (path == null) {\n            return null;\n        }\n        String pathToUse = replace(path, WINDOWS_FOLDER_SEPARATOR, FOLDER_SEPARATOR);\n\n        // Strip prefix from path to analyze, to not treat it as part of the\n        // first path element. This is necessary to correctly parse paths like\n        // \"file:core/../core/io/Resource.class\", where the \"..\" should just\n        // strip the first \"core\" directory while keeping the \"file:\" prefix.\n        int prefixIndex = pathToUse.indexOf(\":\");\n        String prefix = \"\";\n        if (prefixIndex != -1) {\n            prefix = pathToUse.substring(0, prefixIndex + 1);\n            pathToUse = pathToUse.substring(prefixIndex + 1);\n        }\n        if (pathToUse.startsWith(FOLDER_SEPARATOR)) {\n            prefix = prefix + FOLDER_SEPARATOR;\n            pathToUse = pathToUse.substring(1);\n        }\n\n        String[] pathArray = delimitedListToStringArray(pathToUse, FOLDER_SEPARATOR);\n        List<String> pathElements = new LinkedList<String>();\n        int tops = 0;\n\n        for (int i = pathArray.length - 1; i >= 0; i--) {\n            String element = pathArray[i];\n            if (CURRENT_PATH.equals(element)) {\n                // Points to current directory - drop it.\n            } else if (TOP_PATH.equals(element)) {\n                // Registering top path found.\n                tops++;\n            } else {\n                if (tops > 0) {\n                    // Merging path element with element corresponding to top path.\n                    tops--;\n                } else {\n                    // Normal path element found.\n                    pathElements.add(0, element);\n                }\n            }\n        }\n\n        // Remaining top paths need to be retained.\n        for (int i = 0; i < tops; i++) {\n            pathElements.add(0, TOP_PATH);\n        }\n\n        return prefix + collectionToDelimitedString(pathElements, FOLDER_SEPARATOR);\n    }\n\n    /**\n     * Compare two paths after normalization of them.\n     *\n     * @param path1 first path for comparison\n     * @param path2 second path for comparison\n     * @return whether the two paths are equivalent after normalization\n     */\n    public static boolean pathEquals(String path1, String path2) {\n        return cleanPath(path1).equals(cleanPath(path2));\n    }\n\n    /**\n     * Parse the given <code>localeString</code> value into a {@link java.util.Locale}.\n     * <p>This is the inverse operation of {@link java.util.Locale#toString Locale's toString}.\n     *\n     * @param localeString the locale string, following <code>Locale's</code>\n     *                     <code>toString()</code> format (\"en\", \"en_UK\", etc);\n     *                     also accepts spaces as separators, as an alternative to underscores\n     * @return a corresponding <code>Locale</code> instance\n     */\n    public static Locale parseLocaleString(String localeString) {\n        String[] parts = tokenizeToStringArray(localeString, \"_ \", false, false);\n        String language = (parts.length > 0 ? parts[0] : \"\");\n        String country = (parts.length > 1 ? parts[1] : \"\");\n        validateLocalePart(language);\n        validateLocalePart(country);\n        String variant = \"\";\n        if (parts.length >= 2) {\n            // There is definitely a variant, and it is everything after the country\n            // code sans the separator between the country code and the variant.\n            int endIndexOfCountryCode = localeString.indexOf(country) + country.length();\n            // Strip off any leading '_' and whitespace, what's left is the variant.\n            variant = trimLeadingWhitespace(localeString.substring(endIndexOfCountryCode));\n            if (variant.startsWith(\"_\")) {\n                variant = trimLeadingCharacter(variant, '_');\n            }\n        }\n        return (language.length() > 0 ? new Locale(language, country, variant) : null);\n    }\n\n    private static void validateLocalePart(String localePart) {\n        for (int i = 0; i < localePart.length(); i++) {\n            char ch = localePart.charAt(i);\n            if (ch != '_' && ch != ' ' && !Character.isLetterOrDigit(ch)) {\n                throw new IllegalArgumentException(\"Locale part \\\"\" + localePart + \"\\\" contains invalid characters\");\n            }\n        }\n    }\n\n    /**\n     * Determine the RFC 3066 compliant language tag,\n     * as used for the HTTP \"Accept-Language\" header.\n     *\n     * @param locale the Locale to transform to a language tag\n     * @return the RFC 3066 compliant language tag as String\n     */\n    public static String toLanguageTag(Locale locale) {\n        return locale.getLanguage() + (hasText(locale.getCountry()) ? \"-\" + locale.getCountry() : \"\");\n    }\n\n\n    //---------------------------------------------------------------------\n    // Convenience methods for working with String arrays\n    //---------------------------------------------------------------------\n\n    /**\n     * Append the given String to the given String array, returning a new array\n     * consisting of the input array contents plus the given String.\n     *\n     * @param array the array to append to (can be <code>null</code>)\n     * @param str   the String to append\n     * @return the new array (never <code>null</code>)\n     */\n    public static String[] addStringToArray(String[] array, String str) {\n        if (Objects.isEmpty(array)) {\n            return new String[]{str};\n        }\n        String[] newArr = new String[array.length + 1];\n        System.arraycopy(array, 0, newArr, 0, array.length);\n        newArr[array.length] = str;\n        return newArr;\n    }\n\n    /**\n     * Concatenate the given String arrays into one,\n     * with overlapping array elements included twice.\n     * <p>The order of elements in the original arrays is preserved.\n     *\n     * @param array1 the first array (can be <code>null</code>)\n     * @param array2 the second array (can be <code>null</code>)\n     * @return the new array (<code>null</code> if both given arrays were <code>null</code>)\n     */\n    public static String[] concatenateStringArrays(String[] array1, String[] array2) {\n        if (Objects.isEmpty(array1)) {\n            return array2;\n        }\n        if (Objects.isEmpty(array2)) {\n            return array1;\n        }\n        String[] newArr = new String[array1.length + array2.length];\n        System.arraycopy(array1, 0, newArr, 0, array1.length);\n        System.arraycopy(array2, 0, newArr, array1.length, array2.length);\n        return newArr;\n    }\n\n    /**\n     * Merge the given String arrays into one, with overlapping\n     * array elements only included once.\n     * <p>The order of elements in the original arrays is preserved\n     * (with the exception of overlapping elements, which are only\n     * included on their first occurrence).\n     *\n     * @param array1 the first array (can be <code>null</code>)\n     * @param array2 the second array (can be <code>null</code>)\n     * @return the new array (<code>null</code> if both given arrays were <code>null</code>)\n     */\n    public static String[] mergeStringArrays(String[] array1, String[] array2) {\n        if (Objects.isEmpty(array1)) {\n            return array2;\n        }\n        if (Objects.isEmpty(array2)) {\n            return array1;\n        }\n        List<String> result = new ArrayList<String>();\n        result.addAll(Arrays.asList(array1));\n        for (String str : array2) {\n            if (!result.contains(str)) {\n                result.add(str);\n            }\n        }\n        return toStringArray(result);\n    }\n\n    /**\n     * Turn given source String array into sorted array.\n     *\n     * @param array the source array\n     * @return the sorted array (never <code>null</code>)\n     */\n    public static String[] sortStringArray(String[] array) {\n        if (Objects.isEmpty(array)) {\n            return new String[0];\n        }\n        Arrays.sort(array);\n        return array;\n    }\n\n    /**\n     * Copy the given Collection into a String array.\n     * The Collection must contain String elements only.\n     *\n     * @param collection the Collection to copy\n     * @return the String array (<code>null</code> if the passed-in\n     * Collection was <code>null</code>)\n     */\n    public static String[] toStringArray(Collection<String> collection) {\n        if (collection == null) {\n            return null;\n        }\n        return collection.toArray(new String[collection.size()]);\n    }\n\n    /**\n     * Copy the given Enumeration into a String array.\n     * The Enumeration must contain String elements only.\n     *\n     * @param enumeration the Enumeration to copy\n     * @return the String array (<code>null</code> if the passed-in\n     * Enumeration was <code>null</code>)\n     */\n    public static String[] toStringArray(Enumeration<String> enumeration) {\n        if (enumeration == null) {\n            return null;\n        }\n        List<String> list = java.util.Collections.list(enumeration);\n        return list.toArray(new String[list.size()]);\n    }\n\n    /**\n     * Trim the elements of the given String array,\n     * calling <code>String.trim()</code> on each of them.\n     *\n     * @param array the original String array\n     * @return the resulting array (of the same size) with trimmed elements\n     */\n    public static String[] trimArrayElements(String[] array) {\n        if (Objects.isEmpty(array)) {\n            return new String[0];\n        }\n        String[] result = new String[array.length];\n        for (int i = 0; i < array.length; i++) {\n            String element = array[i];\n            result[i] = (element != null ? element.trim() : null);\n        }\n        return result;\n    }\n\n    /**\n     * Remove duplicate Strings from the given array.\n     * Also sorts the array, as it uses a TreeSet.\n     *\n     * @param array the String array\n     * @return an array without duplicates, in natural sort order\n     */\n    public static String[] removeDuplicateStrings(String[] array) {\n        if (Objects.isEmpty(array)) {\n            return array;\n        }\n        Set<String> set = new TreeSet<String>();\n        for (String element : array) {\n            set.add(element);\n        }\n        return toStringArray(set);\n    }\n\n    /**\n     * Split a String at the first occurrence of the delimiter.\n     * Does not include the delimiter in the result.\n     *\n     * @param toSplit   the string to split\n     * @param delimiter to split the string up with\n     * @return a two element array with index 0 being before the delimiter, and\n     * index 1 being after the delimiter (neither element includes the delimiter);\n     * or <code>null</code> if the delimiter wasn't found in the given input String\n     */\n    public static String[] split(String toSplit, String delimiter) {\n        if (!hasLength(toSplit) || !hasLength(delimiter)) {\n            return null;\n        }\n        int offset = toSplit.indexOf(delimiter);\n        if (offset < 0) {\n            return null;\n        }\n        String beforeDelimiter = toSplit.substring(0, offset);\n        String afterDelimiter = toSplit.substring(offset + delimiter.length());\n        return new String[]{beforeDelimiter, afterDelimiter};\n    }\n\n    /**\n     * Take an array Strings and split each element based on the given delimiter.\n     * A <code>Properties</code> instance is then generated, with the left of the\n     * delimiter providing the key, and the right of the delimiter providing the value.\n     * <p>Will trim both the key and value before adding them to the\n     * <code>Properties</code> instance.\n     *\n     * @param array     the array to process\n     * @param delimiter to split each element using (typically the equals symbol)\n     * @return a <code>Properties</code> instance representing the array contents,\n     * or <code>null</code> if the array to process was null or empty\n     */\n    public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter) {\n        return splitArrayElementsIntoProperties(array, delimiter, null);\n    }\n\n    /**\n     * Take an array Strings and split each element based on the given delimiter.\n     * A <code>Properties</code> instance is then generated, with the left of the\n     * delimiter providing the key, and the right of the delimiter providing the value.\n     * <p>Will trim both the key and value before adding them to the\n     * <code>Properties</code> instance.\n     *\n     * @param array         the array to process\n     * @param delimiter     to split each element using (typically the equals symbol)\n     * @param charsToDelete one or more characters to remove from each element\n     *                      prior to attempting the split operation (typically the quotation mark\n     *                      symbol), or <code>null</code> if no removal should occur\n     * @return a <code>Properties</code> instance representing the array contents,\n     * or <code>null</code> if the array to process was <code>null</code> or empty\n     */\n    public static Properties splitArrayElementsIntoProperties(String[] array, String delimiter, String charsToDelete) {\n\n        if (Objects.isEmpty(array)) {\n            return null;\n        }\n        Properties result = new Properties();\n        for (String element : array) {\n            if (charsToDelete != null) {\n                element = deleteAny(element, charsToDelete);\n            }\n            String[] splittedElement = split(element, delimiter);\n            if (splittedElement == null) {\n                continue;\n            }\n            result.setProperty(splittedElement[0].trim(), splittedElement[1].trim());\n        }\n        return result;\n    }\n\n    /**\n     * Tokenize the given String into a String array via a StringTokenizer.\n     * Trims tokens and omits empty tokens.\n     * <p>The given delimiters string is supposed to consist of any number of\n     * delimiter characters. Each of those characters can be used to separate\n     * tokens. A delimiter is always a single character; for multi-character\n     * delimiters, consider using <code>delimitedListToStringArray</code>\n     *\n     * @param str        the String to tokenize\n     * @param delimiters the delimiter characters, assembled as String\n     *                   (each of those characters is individually considered as delimiter).\n     * @return an array of the tokens\n     * @see java.util.StringTokenizer\n     * @see java.lang.String#trim()\n     * @see #delimitedListToStringArray\n     */\n    public static String[] tokenizeToStringArray(String str, String delimiters) {\n        return tokenizeToStringArray(str, delimiters, true, true);\n    }\n\n    /**\n     * Tokenize the given String into a String array via a StringTokenizer.\n     * <p>The given delimiters string is supposed to consist of any number of\n     * delimiter characters. Each of those characters can be used to separate\n     * tokens. A delimiter is always a single character; for multi-character\n     * delimiters, consider using <code>delimitedListToStringArray</code>\n     *\n     * @param str               the String to tokenize\n     * @param delimiters        the delimiter characters, assembled as String\n     *                          (each of those characters is individually considered as delimiter)\n     * @param trimTokens        trim the tokens via String's <code>trim</code>\n     * @param ignoreEmptyTokens omit empty tokens from the result array\n     *                          (only applies to tokens that are empty after trimming; StringTokenizer\n     *                          will not consider subsequent delimiters as token in the first place).\n     * @return an array of the tokens (<code>null</code> if the input String\n     * was <code>null</code>)\n     * @see java.util.StringTokenizer\n     * @see java.lang.String#trim()\n     * @see #delimitedListToStringArray\n     */\n    public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, boolean ignoreEmptyTokens) {\n\n        if (str == null) {\n            return null;\n        }\n        StringTokenizer st = new StringTokenizer(str, delimiters);\n        List<String> tokens = new ArrayList<String>();\n        while (st.hasMoreTokens()) {\n            String token = st.nextToken();\n            if (trimTokens) {\n                token = token.trim();\n            }\n            if (!ignoreEmptyTokens || token.length() > 0) {\n                tokens.add(token);\n            }\n        }\n        return toStringArray(tokens);\n    }\n\n    /**\n     * Take a String which is a delimited list and convert it to a String array.\n     * <p>A single delimiter can consists of more than one character: It will still\n     * be considered as single delimiter string, rather than as bunch of potential\n     * delimiter characters - in contrast to <code>tokenizeToStringArray</code>.\n     *\n     * @param str       the input String\n     * @param delimiter the delimiter between elements (this is a single delimiter,\n     *                  rather than a bunch individual delimiter characters)\n     * @return an array of the tokens in the list\n     * @see #tokenizeToStringArray\n     */\n    public static String[] delimitedListToStringArray(String str, String delimiter) {\n        return delimitedListToStringArray(str, delimiter, null);\n    }\n\n    /**\n     * Take a String which is a delimited list and convert it to a String array.\n     * <p>A single delimiter can consists of more than one character: It will still\n     * be considered as single delimiter string, rather than as bunch of potential\n     * delimiter characters - in contrast to <code>tokenizeToStringArray</code>.\n     *\n     * @param str           the input String\n     * @param delimiter     the delimiter between elements (this is a single delimiter,\n     *                      rather than a bunch individual delimiter characters)\n     * @param charsToDelete a set of characters to delete. Useful for deleting unwanted\n     *                      line breaks: e.g. \"\\r\\n\\f\" will delete all new lines and line feeds in a String.\n     * @return an array of the tokens in the list\n     * @see #tokenizeToStringArray\n     */\n    public static String[] delimitedListToStringArray(String str, String delimiter, String charsToDelete) {\n        if (str == null) {\n            return new String[0];\n        }\n        if (delimiter == null) {\n            return new String[]{str};\n        }\n        List<String> result = new ArrayList<String>();\n        if (\"\".equals(delimiter)) {\n            for (int i = 0; i < str.length(); i++) {\n                result.add(deleteAny(str.substring(i, i + 1), charsToDelete));\n            }\n        } else {\n            int pos = 0;\n            int delPos;\n            while ((delPos = str.indexOf(delimiter, pos)) != -1) {\n                result.add(deleteAny(str.substring(pos, delPos), charsToDelete));\n                pos = delPos + delimiter.length();\n            }\n            if (str.length() > 0 && pos <= str.length()) {\n                // Add rest of String, but not in case of empty input.\n                result.add(deleteAny(str.substring(pos), charsToDelete));\n            }\n        }\n        return toStringArray(result);\n    }\n\n    /**\n     * Convert a CSV list into an array of Strings.\n     *\n     * @param str the input String\n     * @return an array of Strings, or the empty array in case of empty input\n     */\n    public static String[] commaDelimitedListToStringArray(String str) {\n        return delimitedListToStringArray(str, \",\");\n    }\n\n    /**\n     * Convenience method to convert a CSV string list to a set.\n     * Note that this will suppress duplicates.\n     *\n     * @param str the input String\n     * @return a Set of String entries in the list\n     */\n    public static Set<String> commaDelimitedListToSet(String str) {\n        Set<String> set = new TreeSet<String>();\n        String[] tokens = commaDelimitedListToStringArray(str);\n        for (String token : tokens) {\n            set.add(token);\n        }\n        return set;\n    }\n\n    /**\n     * Convenience method to return a Collection as a delimited (e.g. CSV)\n     * String. E.g. useful for <code>toString()</code> implementations.\n     *\n     * @param coll   the Collection to display\n     * @param delim  the delimiter to use (probably a \",\")\n     * @param prefix the String to start each element with\n     * @param suffix the String to end each element with\n     * @return the delimited String\n     */\n    public static String collectionToDelimitedString(Collection<?> coll, String delim, String prefix, String suffix) {\n        if (Collections.isEmpty(coll)) {\n            return \"\";\n        }\n        StringBuilder sb = new StringBuilder();\n        Iterator<?> it = coll.iterator();\n        while (it.hasNext()) {\n            sb.append(prefix).append(it.next()).append(suffix);\n            if (it.hasNext()) {\n                sb.append(delim);\n            }\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Convenience method to return a Collection as a delimited (e.g. CSV)\n     * String. E.g. useful for <code>toString()</code> implementations.\n     *\n     * @param coll  the Collection to display\n     * @param delim the delimiter to use (probably a \",\")\n     * @return the delimited String\n     */\n    public static String collectionToDelimitedString(Collection<?> coll, String delim) {\n        return collectionToDelimitedString(coll, delim, \"\", \"\");\n    }\n\n    /**\n     * Convenience method to return a Collection as a CSV String.\n     * E.g. useful for <code>toString()</code> implementations.\n     *\n     * @param coll the Collection to display\n     * @return the delimited String\n     */\n    public static String collectionToCommaDelimitedString(Collection<?> coll) {\n        return collectionToDelimitedString(coll, \",\");\n    }\n\n    /**\n     * Convenience method to return a String array as a delimited (e.g. CSV)\n     * String. E.g. useful for <code>toString()</code> implementations.\n     *\n     * @param arr   the array to display\n     * @param delim the delimiter to use (probably a \",\")\n     * @return the delimited String\n     */\n    public static String arrayToDelimitedString(Object[] arr, String delim) {\n        if (Objects.isEmpty(arr)) {\n            return \"\";\n        }\n        if (arr.length == 1) {\n            return Objects.nullSafeToString(arr[0]);\n        }\n        StringBuilder sb = new StringBuilder();\n        for (int i = 0; i < arr.length; i++) {\n            if (i > 0) {\n                sb.append(delim);\n            }\n            sb.append(arr[i]);\n        }\n        return sb.toString();\n    }\n\n    /**\n     * Convenience method to return a String array as a CSV String.\n     * E.g. useful for <code>toString()</code> implementations.\n     *\n     * @param arr the array to display\n     * @return the delimited String\n     */\n    public static String arrayToCommaDelimitedString(Object[] arr) {\n        return arrayToDelimitedString(arr, \",\");\n    }\n\n    /**\n     * Appends a space character (<code>' '</code>) if the argument is not empty, otherwise does nothing.  This method\n     * can be thought of as &quot;non-empty space&quot;.  Using this method allows reduction of this:\n     * <blockquote><pre>\n     * if (sb.length != 0) {\n     *     sb.append(' ');\n     * }\n     * sb.append(nextWord);</pre></blockquote>\n     * <p>To this:</p>\n     * <blockquote><pre>\n     * nespace(sb).append(nextWord);</pre></blockquote>\n     *\n     * @param sb the string builder to append a space to if non-empty\n     * @return the string builder argument for method chaining.\n     * @since 0.12.0\n     */\n    public static StringBuilder nespace(StringBuilder sb) {\n        if (sb == null) {\n            return null;\n        }\n        if (sb.length() != 0) {\n            sb.append(' ');\n        }\n        return sb;\n    }\n\n}\n\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/Supplier.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\n/**\n * Represents a supplier of results.\n *\n * <p>There is no requirement that a new or distinct result be returned each time the supplier is invoked.</p>\n *\n * <p>This interface is the equivalent of a JDK 8 {@code java.util.function.Supplier}, backported for JJWT's use in\n * JDK 7 environments.</p>\n *\n * @param <T> the type of object returned by this supplier\n * @since 0.12.0\n */\npublic interface Supplier<T> {\n\n    /**\n     * Returns a result.\n     *\n     * @return a result.\n     */\n    T get();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/lang/UnknownClassException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang;\n\n/**\n * A <code>RuntimeException</code> equivalent of the JDK's\n * <code>ClassNotFoundException</code>, to maintain a RuntimeException paradigm.\n *\n * @since 0.1\n */\npublic class UnknownClassException extends RuntimeException {\n\n    /*\n    /**\n     * Creates a new UnknownClassException.\n     *\n    public UnknownClassException() {\n        super();\n    }*/\n\n    /**\n     * Constructs a new UnknownClassException.\n     *\n     * @param message the reason for the exception\n     */\n    public UnknownClassException(String message) {\n        super(message);\n    }\n\n    /*\n     * Constructs a new UnknownClassException.\n     *\n     * @param cause the underlying Throwable that caused this exception to be thrown.\n     *\n    public UnknownClassException(Throwable cause) {\n        super(cause);\n    }\n    */\n\n    /**\n     * Constructs a new UnknownClassException.\n     *\n     * @param message the reason for the exception\n     * @param cause   the underlying Throwable that caused this exception to be thrown.\n     */\n    public UnknownClassException(String message, Throwable cause) {\n        // TODO: remove in v1.0, this constructor is only exposed to allow for backward compatible behavior\n        super(message, cause);\n    }\n\n}"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/AeadAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.Jwts;\n\nimport javax.crypto.SecretKey;\nimport java.io.OutputStream;\n\n/**\n * A cryptographic algorithm that performs\n * <a href=\"https://en.wikipedia.org/wiki/Authenticated_encryption\">Authenticated encryption with additional data</a>.\n * Per <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.2\">JWE RFC 7516, Section 4.1.2</a>, all JWEs\n * <em>MUST</em> use an AEAD algorithm to encrypt or decrypt the JWE payload/content.  Consequently, all\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-5.1\">JWA &quot;enc&quot; algorithms</a> are AEAD\n * algorithms, and they are accessible as concrete instances via {@link Jwts.ENC}.\n *\n * <p><b>&quot;enc&quot; identifier</b></p>\n *\n * <p>{@code AeadAlgorithm} extends {@code Identifiable}: the value returned from {@link Identifiable#getId() getId()}\n * will be used as the JWE &quot;enc&quot; protected header value.</p>\n *\n * <p><b>Key Strength</b></p>\n *\n * <p>Encryption strength is in part attributed to how difficult it is to discover the encryption key.  As such,\n * cryptographic algorithms often require keys of a minimum length to ensure the keys are difficult to discover\n * and the algorithm's security properties are maintained.</p>\n *\n * <p>The {@code AeadAlgorithm} interface extends the {@link KeyLengthSupplier} interface to represent the length\n * in bits a key must have to be used with its implementation.  If you do not want to worry about lengths and\n * parameters of keys required for an algorithm, it is often easier to automatically generate a key that adheres\n * to the algorithms requirements, as discussed below.</p>\n *\n * <p><b>Key Generation</b></p>\n *\n * <p>{@code AeadAlgorithm} extends {@link KeyBuilderSupplier} to enable {@link SecretKey} generation. Each AEAD\n * algorithm instance will return a {@link KeyBuilder} that ensures any created keys will have a sufficient length\n * and algorithm parameters required by that algorithm.  For example:</p>\n *\n * <pre><code>\n *     SecretKey key = aeadAlgorithm.key().build();\n * </code></pre>\n *\n * <p>The resulting {@code key} is guaranteed to have the correct algorithm parameters and strength/length necessary for\n * that exact {@code aeadAlgorithm} instance.</p>\n *\n * @see Jwts.ENC\n * @see Identifiable#getId()\n * @see KeyLengthSupplier\n * @see KeyBuilderSupplier\n * @see KeyBuilder\n * @since 0.12.0\n */\npublic interface AeadAlgorithm extends Identifiable, KeyLengthSupplier, KeyBuilderSupplier<SecretKey, SecretKeyBuilder> {\n\n    /**\n     * Encrypts plaintext and signs any {@link AeadRequest#getAssociatedData() associated data}, placing the resulting\n     * ciphertext, initialization vector and authentication tag in the provided {@code result}.\n     *\n     * @param req the encryption request representing the plaintext to be encrypted, any additional\n     *            integrity-protected data and the encryption key.\n     * @param res the result to write ciphertext, initialization vector and AAD authentication tag (aka digest)\n     * @throws SecurityException if there is an encryption problem or AAD authenticity cannot be guaranteed.\n     */\n    void encrypt(AeadRequest req, AeadResult res) throws SecurityException;\n\n    /**\n     * Decrypts ciphertext and authenticates any {@link DecryptAeadRequest#getAssociatedData() associated data},\n     * writing the decrypted plaintext to the provided {@code out}put stream.\n     *\n     * @param request the decryption request representing the ciphertext to be decrypted, any additional\n     *                integrity-protected data, authentication tag, initialization vector, and decryption key\n     * @param out     the OutputStream for writing decrypted plaintext\n     * @throws SecurityException if there is a decryption problem or authenticity assertions fail.\n     */\n    void decrypt(DecryptAeadRequest request, OutputStream out) throws SecurityException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/AeadRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\n\n/**\n * A request to an {@link AeadAlgorithm} to perform authenticated encryption with a supplied symmetric\n * {@link SecretKey}, allowing for additional data to be authenticated and integrity-protected.\n *\n * @see SecureRequest\n * @see AssociatedDataSupplier\n * @since 0.12.0\n */\npublic interface AeadRequest extends SecureRequest<InputStream, SecretKey>, AssociatedDataSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/AeadResult.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.io.OutputStream;\n\n/**\n * The result of authenticated encryption, providing access to the ciphertext {@link #getOutputStream() output stream}\n * and resulting {@link #setTag(byte[]) AAD tag} and {@link #setIv(byte[]) initialization vector}.\n * The AAD tag and initialization vector must be supplied with the ciphertext to decrypt.\n *\n * @since 0.12.0\n */\npublic interface AeadResult {\n\n    /**\n     * Returns the {@code OutputStream} the AeadAlgorithm will use to write the resulting ciphertext during\n     * encryption or plaintext during decryption.\n     *\n     * @return the {@code OutputStream} the AeadAlgorithm will use to write the resulting ciphertext during\n     * encryption or plaintext during decryption.\n     */\n    OutputStream getOutputStream();\n\n    /**\n     * Sets the AEAD authentication tag.\n     *\n     * @param tag the AEAD authentication tag.\n     * @return the AeadResult for method chaining.\n     */\n    AeadResult setTag(byte[] tag);\n\n    /**\n     * Sets the initialization vector used during encryption.\n     *\n     * @param iv the initialization vector used during encryption.\n     * @return the AeadResult for method chaining.\n     */\n    AeadResult setIv(byte[] iv);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/AssociatedDataSupplier.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.io.InputStream;\n\n/**\n * Provides any &quot;associated data&quot; that must be integrity protected (but not encrypted) when performing\n * <a href=\"https://en.wikipedia.org/wiki/Authenticated_encryption\">AEAD encryption or decryption</a>.\n *\n * @see #getAssociatedData()\n * @since 0.12.0\n */\npublic interface AssociatedDataSupplier {\n\n    /**\n     * Returns any data that must be integrity protected (but not encrypted) when performing\n     * <a href=\"https://en.wikipedia.org/wiki/Authenticated_encryption\">AEAD encryption or decryption</a>, or\n     * {@code null} if no additional data must be integrity protected.\n     *\n     * @return any data that must be integrity protected (but not encrypted) when performing\n     * <a href=\"https://en.wikipedia.org/wiki/Authenticated_encryption\">AEAD encryption or decryption</a>, or\n     * {@code null} if no additional data must be integrity protected.\n     */\n    InputStream getAssociatedData();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/AsymmetricJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Key;\n\n/**\n * JWK representation of an asymmetric (public or private) cryptographic key.\n *\n * @param <K> the type of {@link java.security.PublicKey} or {@link java.security.PrivateKey} represented by this JWK.\n * @since 0.12.0\n */\npublic interface AsymmetricJwk<K extends Key> extends Jwk<K>, X509Accessor {\n\n    /**\n     * Returns the JWK\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.2\">{@code use} (Public Key Use)\n     * parameter</a> value or {@code null} if not present. {@code use} values are CaSe-SeNsItIvE.\n     *\n     * <p>The JWK specification <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.2\">defines</a> the\n     * following {@code use} values:</p>\n     *\n     * <table>\n     * <caption>JWK Key Use Values</caption>\n     * <thead>\n     * <tr>\n     * <th>Value</th>\n     * <th>Key Use</th>\n     * </tr>\n     * </thead>\n     * <tbody>\n     * <tr>\n     * <td><b>{@code sig}</b></td>\n     * <td>signature</td>\n     * </tr>\n     * <tr>\n     * <td><b>{@code enc}</b></td>\n     * <td>encryption</td>\n     * </tr>\n     * </tbody>\n     * </table>\n     *\n     * <p>Other values <em>MAY</em> be used.  For best interoperability with other applications however, it is\n     * recommended to use only the values above.</p>\n     *\n     * <p>When a key is used to wrap another key and a public key use designation for the first key is desired, the\n     * {@code enc} (encryption) key use value is used, since key wrapping is a kind of encryption.  The\n     * {@code enc} value is also to be used for public keys used for key agreement operations.</p>\n     *\n     * <p><b>Public Key Use vs Key Operations</b></p>\n     *\n     * <p>Per\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">JWK RFC 7517, Section 4.3, last paragraph</a>,\n     * the {@code use} (Public Key Use) and {@link #getOperations() key_ops (Key Operations)} members\n     * <em>SHOULD NOT</em> be used together; however, if both are used, the information they convey <em>MUST</em> be\n     * consistent.  Applications should specify which of these members they use, if either is to be used by the\n     * application.</p>\n     *\n     * @return the JWK {@code use} value or {@code null} if not present.\n     */\n    String getPublicKeyUse();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/AsymmetricJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Key;\n\n/**\n * A {@link JwkBuilder} that builds asymmetric (public or private) JWKs.\n *\n * @param <K> the type of Java key provided by the JWK.\n * @param <J> the type of asymmetric JWK created\n * @param <T> the type of the builder, for subtype method chaining\n * @since 0.12.0\n */\npublic interface AsymmetricJwkBuilder<K extends Key, J extends AsymmetricJwk<K>, T extends AsymmetricJwkBuilder<K, J, T>>\n        extends JwkBuilder<K, J, T>, X509Builder<T> {\n\n    /**\n     * Sets the JWK\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.2\">{@code use} (Public Key Use)\n     * parameter</a> value. {@code use} values are CaSe-SeNsItIvE.  A {@code null} value will remove the property\n     * from the JWK.\n     *\n     * <p>The JWK specification <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.2\">defines</a> the\n     * following {@code use} values:</p>\n     *\n     * <table>\n     * <caption>JWK Key Use Values</caption>\n     * <thead>\n     * <tr>\n     * <th>Value</th>\n     * <th>Key Use</th>\n     * </tr>\n     * </thead>\n     * <tbody>\n     * <tr>\n     * <td><b>{@code sig}</b></td>\n     * <td>signature</td>\n     * </tr>\n     * <tr>\n     * <td><b>{@code enc}</b></td>\n     * <td>encryption</td>\n     * </tr>\n     * </tbody>\n     * </table>\n     *\n     * <p>Other values <em>MAY</em> be used.  For best interoperability with other applications however, it is\n     * recommended to use only the values above.</p>\n     *\n     * <p>When a key is used to wrap another key and a public key use designation for the first key is desired, the\n     * {@code enc} (encryption) key use value is used, since key wrapping is a kind of encryption.  The\n     * {@code enc} value is also to be used for public keys used for key agreement operations.</p>\n     *\n     * <p><b>Public Key Use vs Key Operations</b></p>\n     *\n     * <p>Per\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">JWK RFC 7517, Section 4.3, last paragraph</a>,\n     * the <code>use (Public Key Use)</code> and {@link #operations() key_ops (Key Operations)} members\n     * <em>SHOULD NOT</em> be used together; however, if both are used, the information they convey <em>MUST</em> be\n     * consistent. Applications should specify which of these members they use, if either is to be used by the\n     * application.</p>\n     *\n     * @param use the JWK {@code use} value.\n     * @return the builder for method chaining.\n     * @throws IllegalArgumentException if the {@code use} value is {@code null} or empty.\n     */\n    T publicKeyUse(String use) throws IllegalArgumentException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/Curve.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\n\n/**\n * A cryptographic Elliptic Curve for use with digital signature or key agreement algorithms.\n *\n * <p><b>Curve Identifier</b></p>\n *\n * <p>This interface extends {@link Identifiable}; the value returned from {@link #getId()} will\n * be used as the JWK\n * <a href=\"https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.1\"><code>crv</code></a> value.</p>\n *\n * <p><b>KeyPair Generation</b></p>\n *\n * <p>A secure-random KeyPair of sufficient strength on the curve may be obtained with its {@link #keyPair()} builder.</p>\n *\n * <p><b>Standard Implementations</b></p>\n *\n * <p>Constants for all JWA standard Curves are available via the {@link Jwks.CRV} registry.</p>\n *\n * @see Jwks.CRV\n * @since 0.12.0\n */\npublic interface Curve extends Identifiable, KeyPairBuilderSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/DecryptAeadRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\n\n/**\n * A request to an {@link AeadAlgorithm} to decrypt ciphertext and perform integrity-protection with a supplied\n * decryption {@link SecretKey}. Extends both {@link IvSupplier} and {@link DigestSupplier} to\n * ensure the respective required IV and AAD tag returned from an {@link AeadResult} are available for decryption.\n *\n * @since 0.12.0\n */\npublic interface DecryptAeadRequest extends AeadRequest, IvSupplier, DigestSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/DecryptionKeyRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Key;\n\n/**\n * A {@link KeyRequest} to obtain a decryption key that will be used to decrypt a JWE using an {@link AeadAlgorithm}.\n * The AEAD algorithm used for decryption is accessible via {@link #getEncryptionAlgorithm()}.\n *\n * <p>The key used to perform cryptographic operations, for example a direct shared key, or a\n * JWE &quot;key decryption key&quot; will be accessible via {@link #getKey()}. This is always required and\n * never {@code null}.</p>\n *\n * <p>Any encrypted key material (what the JWE specification calls the\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-2\">JWE Encrypted Key</a>) will\n * be accessible via {@link #getPayload()}. If present, the {@link KeyAlgorithm} will decrypt it to obtain the resulting\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-2\">Content Encryption Key (CEK)</a>.\n * This may be empty however depending on which {@link KeyAlgorithm} was used during JWE encryption.</p>\n *\n * <p>Finally, any public information necessary by the called {@link KeyAlgorithm} to decrypt any\n * {@code JWE Encrypted Key} (such as an initialization vector, authentication tag, ephemeral key, etc) is expected\n * to be available in the JWE protected header, accessible via {@link #getHeader()}.</p>\n *\n * @param <K> the type of {@link Key} used during the request to obtain the resulting decryption key.\n * @since 0.12.0\n */\npublic interface DecryptionKeyRequest<K extends Key> extends SecureRequest<byte[], K>, KeyRequest<byte[]> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/DigestAlgorithm.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.lang.Registry;\n\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * A {@code DigestAlgorithm} is a\n * <a href=\"https://en.wikipedia.org/wiki/Cryptographic_hash_function\">Cryptographic Hash Function</a>\n * that computes and verifies cryptographic digests.  There are three types of {@code DigestAlgorithm}s represented\n * by subtypes, and RFC-standard implementations are available as constants in {@link Registry} singletons:\n *\n * <table>\n *     <caption>Types of {@code DigestAlgorithm}s</caption>\n *     <thead>\n *         <tr>\n *             <th>Subtype</th>\n *             <th>Standard Implementation Registry</th>\n *             <th>Security Model</th>\n *         </tr>\n *     </thead>\n *     <tbody>\n *         <tr>\n *             <td>{@link HashAlgorithm}</td>\n *             <td>{@link Jwks.HASH}</td>\n *             <td>Unsecured (unkeyed), does not require a key to compute or verify digests.</td>\n *         </tr>\n *         <tr>\n *             <td>{@link MacAlgorithm}</td>\n *             <td>{@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}</td>\n *             <td>Requires a {@link SecretKey} to both compute and verify digests (aka\n *                 &quot;Message Authentication Codes&quot;).</td>\n *         </tr>\n *         <tr>\n *             <td>{@link SignatureAlgorithm}</td>\n *             <td>{@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}</td>\n *             <td>Requires a {@link PrivateKey} to compute and {@link PublicKey} to verify digests\n *                 (aka &quot;Digital Signatures&quot;).</td>\n *         </tr>\n *     </tbody>\n * </table>\n *\n * <p><b>Standard Identifier</b></p>\n *\n * <p>{@code DigestAlgorithm} extends {@link Identifiable}: the value returned from\n * {@link Identifiable#getId() getId()} will be used as the JWT standard identifier where required.</p>\n *\n * <p>For example,\n * when a {@link MacAlgorithm} or {@link SignatureAlgorithm} is used to secure a JWS, the value returned from\n * {@code algorithm.getId()} will be used as the JWS <code>&quot;alg&quot;</code> protected header value.  Or when a\n * {@link HashAlgorithm} is used to compute a {@link JwkThumbprint}, it's {@code algorithm.getId()} value will be\n * used within the thumbprint's {@link JwkThumbprint#toURI() URI} per JWT RFC requirements.</p>\n *\n * @param <R> the type of {@link Request} used when computing a digest.\n * @param <V> the type of {@link VerifyDigestRequest} used when verifying a digest.\n * @see Jwks.HASH\n * @see io.jsonwebtoken.Jwts.SIG Jwts.SIG\n * @since 0.12.0\n */\npublic interface DigestAlgorithm<R extends Request<InputStream>, V extends VerifyDigestRequest> extends Identifiable {\n\n    /**\n     * Returns a cryptographic digest of the request {@link Request#getPayload() payload}.\n     *\n     * @param request the request containing the data to be hashed, mac'd or signed.\n     * @return a cryptographic digest of the request {@link Request#getPayload() payload}.\n     * @throws SecurityException if there is invalid key input or a problem during digest creation.\n     */\n    byte[] digest(R request) throws SecurityException;\n\n    /**\n     * Returns {@code true} if the provided {@link VerifyDigestRequest#getDigest() digest} matches the expected value\n     * for the given {@link VerifyDigestRequest#getPayload() payload}, {@code false} otherwise.\n     *\n     * @param request the request containing the {@link VerifyDigestRequest#getDigest() digest} to verify for the\n     *                associated {@link VerifyDigestRequest#getPayload() payload}.\n     * @return {@code true} if the provided {@link VerifyDigestRequest#getDigest() digest} matches the expected value\n     * for the given {@link VerifyDigestRequest#getPayload() payload}, {@code false} otherwise.\n     * @throws SecurityException if there is an invalid key input or a problem that won't allow digest verification.\n     */\n    boolean verify(V request) throws SecurityException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/DigestSupplier.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * A {@code DigestSupplier} provides access to the result of a cryptographic digest algorithm, such as a\n * Message Digest, MAC, Signature, or Authentication Tag.\n *\n * @since 0.12.0\n */\npublic interface DigestSupplier {\n\n    /**\n     * Returns a cryptographic digest result, such as a Message Digest, MAC, Signature, or Authentication Tag\n     * depending on the cryptographic algorithm that produced it.\n     *\n     * @return a cryptographic digest result, such as a Message Digest, MAC, Signature, or Authentication Tag\n     * * depending on the cryptographic algorithm that produced it.\n     */\n    byte[] getDigest();\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/DynamicJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.cert.X509Certificate;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.List;\n\n/**\n * A {@link JwkBuilder} that coerces to a more type-specific builder based on the {@link Key} that will be\n * represented as a JWK.\n *\n * @param <K> the type of Java {@link Key} represented by the created {@link Jwk}.\n * @param <J> the type of {@link Jwk} created by the builder\n * @since 0.12.0\n */\npublic interface DynamicJwkBuilder<K extends Key, J extends Jwk<K>> extends JwkBuilder<K, J, DynamicJwkBuilder<K, J>> {\n\n    /**\n     * Ensures the builder will create a {@link PublicJwk} for the specified Java {@link X509Certificate} chain.\n     * The first {@code X509Certificate} in the chain (at array index 0) <em>MUST</em> contain a {@link PublicKey}\n     * instance when calling the certificate's {@link X509Certificate#getPublicKey() getPublicKey()} method.\n     *\n     * <p>This method is provided for congruence with the other {@code chain} methods and is expected to be used when\n     * the calling code has a variable {@code PublicKey} reference. Based on the argument type, it will\n     * delegate to one of the following methods if possible:\n     * <ul>\n     *     <li>{@link #rsaChain(List)}</li>\n     *     <li>{@link #ecChain(List)}</li>\n     *     <li>{@link #octetChain(List)}</li>\n     * </ul>\n     *\n     * <p>If the specified {@code chain} argument is not capable of being supported by one of those methods, an\n     * {@link UnsupportedKeyException} will be thrown.</p>\n     *\n     * <p><b>Type Parameters</b></p>\n     *\n     * <p>In addition to the public key type <code>A</code>, the public key's associated private key type\n     * <code>B</code> is parameterized as well. This ensures that any subsequent call to the builder's\n     * {@link PublicJwkBuilder#privateKey(PrivateKey) privateKey} method will be type-safe.  For example:</p>\n     *\n     * <blockquote><pre>Jwks.builder().&lt;EdECPublicKey, <b>EdECPrivateKey</b>&gt;chain(edECPublicKeyX509CertificateChain)\n     *     .privateKey(<b>aPrivateKey</b>) // &lt;-- must be an EdECPrivateKey instance\n     *     ... etc ...\n     *     .build();</pre></blockquote>\n     *\n     * @param <A>   the type of {@link PublicKey} provided by the created public JWK.\n     * @param <B>   the type of {@link PrivateKey} that may be paired with the {@link PublicKey} to produce a\n     *              {@link PrivateJwk} if desired.\n     * @param chain the {@link X509Certificate} chain to inspect to find the {@link PublicKey} to represent as a\n     *              {@link PublicJwk}.\n     * @return the builder coerced as a {@link PublicJwkBuilder} for continued method chaining.\n     * @throws UnsupportedKeyException if the specified key is not a supported type and cannot be used to delegate to\n     *                                 other {@code key} methods.\n     * @see PublicJwk\n     * @see PrivateJwk\n     */\n    <A extends PublicKey, B extends PrivateKey> PublicJwkBuilder<A, B, ?, ?, ?, ?> chain(List<X509Certificate> chain)\n            throws UnsupportedKeyException;\n\n    /**\n     * Ensures the builder will create a {@link SecretJwk} for the specified Java {@link SecretKey}.\n     *\n     * @param key the {@link SecretKey} to represent as a {@link SecretJwk}.\n     * @return the builder coerced as a {@link SecretJwkBuilder}.\n     */\n    SecretJwkBuilder key(SecretKey key);\n\n    /**\n     * Ensures the builder will create an {@link RsaPublicJwk} for the specified Java {@link RSAPublicKey}.\n     *\n     * @param key the {@link RSAPublicKey} to represent as a {@link RsaPublicJwk}.\n     * @return the builder coerced as an {@link RsaPublicJwkBuilder}.\n     */\n    RsaPublicJwkBuilder key(RSAPublicKey key);\n\n    /**\n     * Ensures the builder will create an {@link RsaPrivateJwk} for the specified Java {@link RSAPrivateKey}. If\n     * possible, it is recommended to also call the resulting builder's\n     * {@link RsaPrivateJwkBuilder#publicKey(PublicKey) publicKey} method with the private key's matching\n     * {@link PublicKey} for better performance.  See the\n     * {@link RsaPrivateJwkBuilder#publicKey(PublicKey) publicKey} and {@link PrivateJwk} JavaDoc for more\n     * information.\n     *\n     * @param key the {@link RSAPublicKey} to represent as a {@link RsaPublicJwk}.\n     * @return the builder coerced as an {@link RsaPrivateJwkBuilder}.\n     */\n    RsaPrivateJwkBuilder key(RSAPrivateKey key);\n\n    /**\n     * Ensures the builder will create an {@link EcPublicJwk} for the specified Java {@link ECPublicKey}.\n     *\n     * @param key the {@link ECPublicKey} to represent as a {@link EcPublicJwk}.\n     * @return the builder coerced as an {@link EcPublicJwkBuilder}.\n     */\n    EcPublicJwkBuilder key(ECPublicKey key);\n\n    /**\n     * Ensures the builder will create an {@link EcPrivateJwk} for the specified Java {@link ECPrivateKey}. If\n     * possible, it is recommended to also call the resulting builder's\n     * {@link EcPrivateJwkBuilder#publicKey(PublicKey) publicKey} method with the private key's matching\n     * {@link PublicKey} for better performance.  See the\n     * {@link EcPrivateJwkBuilder#publicKey(PublicKey) publicKey} and {@link PrivateJwk} JavaDoc for more\n     * information.\n     *\n     * @param key the {@link ECPublicKey} to represent as an {@link EcPublicJwk}.\n     * @return the builder coerced as a {@link EcPrivateJwkBuilder}.\n     */\n    EcPrivateJwkBuilder key(ECPrivateKey key);\n\n    /**\n     * Ensures the builder will create a {@link PublicJwk} for the specified Java {@link PublicKey} argument. This\n     * method is provided for congruence with the other {@code key} methods and is expected to be used when\n     * the calling code has an untyped {@code PublicKey} reference. Based on the argument type, it will delegate to one\n     * of the following methods if possible:\n     * <ul>\n     *     <li>{@link #key(RSAPublicKey)}</li>\n     *     <li>{@link #key(ECPublicKey)}</li>\n     *     <li>{@link #octetKey(PublicKey)}</li>\n     * </ul>\n     *\n     * <p>If the specified {@code key} argument is not capable of being supported by one of those methods, an\n     * {@link UnsupportedKeyException} will be thrown.</p>\n     *\n     * <p><b>Type Parameters</b></p>\n     *\n     * <p>In addition to the public key type <code>A</code>, the public key's associated private key type\n     * <code>B</code> is parameterized as well. This ensures that any subsequent call to the builder's\n     * {@link PublicJwkBuilder#privateKey(PrivateKey) privateKey} method will be type-safe.  For example:</p>\n     *\n     * <blockquote><pre>Jwks.builder().&lt;EdECPublicKey, <b>EdECPrivateKey</b>&gt;key(anEdECPublicKey)\n     *     .privateKey(<b>aPrivateKey</b>) // &lt;-- must be an EdECPrivateKey instance\n     *     ... etc ...\n     *     .build();</pre></blockquote>\n     *\n     * @param <A> the type of {@link PublicKey} provided by the created public JWK.\n     * @param <B> the type of {@link PrivateKey} that may be paired with the {@link PublicKey} to produce a\n     *            {@link PrivateJwk} if desired.\n     * @param key the {@link PublicKey} to represent as a {@link PublicJwk}.\n     * @return the builder coerced as a {@link PublicJwkBuilder} for continued method chaining.\n     * @throws UnsupportedKeyException if the specified key is not a supported type and cannot be used to delegate to\n     *                                 other {@code key} methods.\n     * @see PublicJwk\n     * @see PrivateJwk\n     */\n    <A extends PublicKey, B extends PrivateKey> PublicJwkBuilder<A, B, ?, ?, ?, ?> key(A key) throws UnsupportedKeyException;\n\n    /**\n     * Ensures the builder will create a {@link PrivateJwk} for the specified Java {@link PrivateKey} argument. This\n     * method is provided for congruence with the other {@code key} methods and is expected to be used when\n     * the calling code has an untyped {@code PrivateKey} reference. Based on the argument type, it will delegate to one\n     * of the following methods if possible:\n     * <ul>\n     *     <li>{@link #key(RSAPrivateKey)}</li>\n     *     <li>{@link #key(ECPrivateKey)}</li>\n     *     <li>{@link #octetKey(PrivateKey)}</li>\n     * </ul>\n     *\n     * <p>If the specified {@code key} argument is not capable of being supported by one of those methods, an\n     * {@link UnsupportedKeyException} will be thrown.</p>\n     *\n     * <p><b>Type Parameters</b></p>\n     *\n     * <p>In addition to the private key type <code>B</code>, the private key's associated public key type\n     * <code>A</code> is parameterized as well. This ensures that any subsequent call to the builder's\n     * {@link PrivateJwkBuilder#publicKey(PublicKey) publicKey} method will be type-safe.  For example:</p>\n     *\n     * <blockquote><pre>Jwks.builder().&lt;<b>EdECPublicKey</b>, EdECPrivateKey&gt;key(anEdECPrivateKey)\n     *     .publicKey(<b>aPublicKey</b>) // &lt;-- must be an EdECPublicKey instance\n     *     ... etc ...\n     *     .build();</pre></blockquote>\n     *\n     * @param <A> the type of {@link PublicKey} paired with the {@code key} argument to produce the {@link PrivateJwk}.\n     * @param <B> the type of the {@link PrivateKey} argument.\n     * @param key the {@link PrivateKey} to represent as a {@link PrivateJwk}.\n     * @return the builder coerced as a {@link PrivateJwkBuilder} for continued method chaining.\n     * @throws UnsupportedKeyException if the specified key is not a supported type and cannot be used to delegate to\n     *                                 other {@code key} methods.\n     * @see PublicJwk\n     * @see PrivateJwk\n     */\n    <A extends PublicKey, B extends PrivateKey> PrivateJwkBuilder<B, A, ?, ?, ?> key(B key) throws UnsupportedKeyException;\n\n    /**\n     * Ensures the builder will create a {@link PrivateJwk} for the specified Java {@link KeyPair} argument. This\n     * method is provided for congruence with the other {@code keyPair} methods and is expected to be used when\n     * the calling code has a variable {@code PrivateKey} reference. Based on the argument's {@code PrivateKey} type,\n     * it will delegate to one of the following methods if possible:\n     * <ul>\n     *     <li>{@link #key(RSAPrivateKey)}</li>\n     *     <li>{@link #key(ECPrivateKey)}</li>\n     *     <li>{@link #octetKey(PrivateKey)}</li>\n     * </ul>\n     * <p>and automatically set the resulting builder's {@link PrivateJwkBuilder#publicKey(PublicKey) publicKey} with\n     * the pair's {@code PublicKey}.</p>\n     *\n     * <p>If the specified {@code key} argument is not capable of being supported by one of those methods, an\n     * {@link UnsupportedKeyException} will be thrown.</p>\n     *\n     * <p><b>Type Parameters</b></p>\n     *\n     * <p>In addition to the private key type <code>B</code>, the private key's associated public key type\n     * <code>A</code> is parameterized as well. This ensures that any subsequent call to the builder's\n     * {@link PrivateJwkBuilder#publicKey(PublicKey) publicKey} method will be type-safe.  For example:</p>\n     *\n     * <blockquote><pre>Jwks.builder().&lt;<b>EdECPublicKey</b>, EdECPrivateKey&gt;keyPair(anEdECKeyPair)\n     *     .publicKey(<b>aPublicKey</b>) // &lt;-- must be an EdECPublicKey instance\n     *     ... etc ...\n     *     .build();</pre></blockquote>\n     *\n     * @param <A>     the {@code keyPair} argument's {@link PublicKey} type\n     * @param <B>     the {@code keyPair} argument's {@link PrivateKey} type\n     * @param keyPair the {@code KeyPair} containing the public and private key\n     * @return the builder coerced as a {@link PrivateJwkBuilder} for continued method chaining.\n     * @throws UnsupportedKeyException if the specified {@code KeyPair}'s keys are not supported and cannot be used to\n     *                                 delegate to other {@code key} methods.\n     * @see PublicJwk\n     * @see PrivateJwk\n     */\n    <A extends PublicKey, B extends PrivateKey> PrivateJwkBuilder<B, A, ?, ?, ?> keyPair(KeyPair keyPair)\n            throws UnsupportedKeyException;\n\n    /**\n     * Ensures the builder will create an {@link OctetPublicJwk} for the specified Edwards-curve {@code PublicKey}\n     * argument.  The {@code PublicKey} must be an instance of one of the following:\n     * <ul>\n     *     <li><a href=\"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/interfaces/XECPublicKey.html\">java.security.interfaces.XECPublicKey</a>, introduced in JDK 11</li>\n     *     <li><a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPublicKey.html\">java.security.interfaces.EdECPublicKey</a>, introduced in JDK 15</li>\n     *     <li>A {@code PublicKey} with a valid Edwards Curve DER {@link Key#getEncoded() encoding}, such as those\n     *         provided by BouncyCastle on earlier JDKs.</li>\n     * </ul>\n     *\n     * <p><b>Type Parameters</b></p>\n     *\n     * <p>In addition to the public key type <code>A</code>, the public key's associated private key type\n     * <code>B</code> is parameterized as well. This ensures that any subsequent call to the builder's\n     * {@link PublicJwkBuilder#privateKey(PrivateKey) privateKey} method will be type-safe.  For example:</p>\n     *\n     * <blockquote><pre>Jwks.builder().&lt;EdECPublicKey, <b>EdECPrivateKey</b>&gt;key(anEdECPublicKey)\n     *     .privateKey(<b>aPrivateKey</b>) // &lt;-- must be an EdECPrivateKey instance\n     *     ... etc ...\n     *     .build();</pre></blockquote>\n     *\n     * @param <A> the type of Edwards-curve {@link PublicKey} provided by the created public JWK.\n     * @param <B> the type of Edwards-curve {@link PrivateKey} that may be paired with the {@link PublicKey} to produce\n     *            an {@link OctetPrivateJwk} if desired.\n     * @param key the Edwards-curve {@link PublicKey} to represent as an {@link OctetPublicJwk}.\n     * @return the builder coerced as a {@link OctetPublicJwkBuilder} for continued method chaining.\n     * @throws UnsupportedKeyException if the specified key is not a supported Edwards-curve key.\n     * @see <a href=\"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/interfaces/XECPublicKey.html\">java.security.interfaces.XECPublicKey</a>\n     * @see <a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPublicKey.html\">java.security.interfaces.EdECPublicKey</a>\n     */\n    <A extends PublicKey, B extends PrivateKey> OctetPublicJwkBuilder<A, B> octetKey(A key);\n\n    /**\n     * Ensures the builder will create an {@link OctetPrivateJwk} for the specified Edwards-curve {@code PrivateKey}\n     * argument.  The {@code PrivateKey} must be an instance of one of the following:\n     * <ul>\n     *     <li><a href=\"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/interfaces/XECPrivateKey.html\">\n     *         java.security.interfaces.XECPrivateKey</a>, introduced in JDK 11</li>\n     *     <li><a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPrivateKey.html\">\n     *         java.security.interfaces.EdECPrivateKey</a>, introduced in JDK 15</li>\n     *     <li>A {@code PrivateKey} with a valid Edwards Curve DER {@link Key#getEncoded() encoding}, such as those\n     *         provided by BouncyCastle on earlier JDKs.</li>\n     * </ul>\n     *\n     * <p><b>Type Parameters</b></p>\n     *\n     * <p>In addition to the private key type <code>B</code>, the private key's associated public key type\n     * <code>A</code> is parameterized as well. This ensures that any subsequent call to the builder's\n     * {@link PrivateJwkBuilder#publicKey(PublicKey) publicKey} method will be type-safe.  For example:</p>\n     *\n     * <blockquote><pre>Jwks.builder().&lt;<b>EdECPublicKey</b>, EdECPrivateKey&gt;key(anEdECPrivateKey)\n     *     .publicKey(<b>aPublicKey</b>) // &lt;-- must be an EdECPublicKey instance\n     *     ... etc ...\n     *     .build();</pre></blockquote>\n     *\n     * @param <A> the type of the Edwards-curve {@link PrivateKey} argument.\n     * @param <B> the type of Edwards-curve {@link PublicKey} paired with the {@code key} argument to produce the\n     *            {@link OctetPrivateJwk}.\n     * @param key the Edwards-curve {@link PrivateKey} to represent as an {@link OctetPrivateJwk}.\n     * @return the builder coerced as an {@link OctetPrivateJwkBuilder} for continued method chaining.\n     * @throws UnsupportedKeyException if the specified key is not a supported Edwards-curve key.\n     * @see <a href=\"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/interfaces/XECPrivateKey.html\">java.security.interfaces.XECPrivateKey</a>\n     * @see <a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPrivateKey.html\">java.security.interfaces.EdECPrivateKey</a>\n     */\n    <A extends PrivateKey, B extends PublicKey> OctetPrivateJwkBuilder<A, B> octetKey(A key);\n\n    /**\n     * Ensures the builder will create an {@link OctetPublicJwk} for the specified Java {@link X509Certificate} chain.\n     * The first {@code X509Certificate} in the chain (at list index 0) <em>MUST</em>\n     * {@link X509Certificate#getPublicKey() contain} an Edwards-curve public key as defined by\n     * {@link #octetKey(PublicKey)}.\n     *\n     * @param <A>   the type of Edwards-curve {@link PublicKey} contained in the first {@code X509Certificate}.\n     * @param <B>   the type of Edwards-curve {@link PrivateKey} that may be paired with the {@link PublicKey} to produce\n     *              an {@link OctetPrivateJwk} if desired.\n     * @param chain the {@link X509Certificate} chain to inspect to find the Edwards-curve {@code PublicKey} to\n     *              represent as an {@link OctetPublicJwk}.\n     * @return the builder coerced as an {@link OctetPublicJwkBuilder} for continued method chaining.\n     */\n    <A extends PublicKey, B extends PrivateKey> OctetPublicJwkBuilder<A, B> octetChain(List<X509Certificate> chain);\n\n    /**\n     * Ensures the builder will create an {@link OctetPrivateJwk} for the specified Java Edwards-curve\n     * {@link KeyPair}.  The pair's {@link KeyPair#getPublic() public key} <em>MUST</em> be an\n     * Edwards-curve public key as defined by {@link #octetKey(PublicKey)}.  The pair's\n     * {@link KeyPair#getPrivate() private key} <em>MUST</em> be an Edwards-curve private key as defined by\n     * {@link #octetKey(PrivateKey)}.\n     *\n     * @param <A>     the type of Edwards-curve {@link PublicKey} contained in the key pair.\n     * @param <B>     the type of the Edwards-curve {@link PrivateKey} contained in the key pair.\n     * @param keyPair the Edwards-curve {@link KeyPair} to represent as an {@link OctetPrivateJwk}.\n     * @return the builder coerced as an {@link OctetPrivateJwkBuilder} for continued method chaining.\n     * @throws IllegalArgumentException if the {@code keyPair} does not contain Edwards-curve public and private key\n     *                                  instances.\n     */\n    <A extends PrivateKey, B extends PublicKey> OctetPrivateJwkBuilder<A, B> octetKeyPair(KeyPair keyPair);\n\n    /**\n     * Ensures the builder will create an {@link EcPublicJwk} for the specified Java {@link X509Certificate} chain.\n     * The first {@code X509Certificate} in the chain (at list index 0) <em>MUST</em> contain an {@link ECPublicKey}\n     * instance when calling the certificate's {@link X509Certificate#getPublicKey() getPublicKey()} method.\n     *\n     * @param chain the {@link X509Certificate} chain to inspect to find the {@link ECPublicKey} to represent as a\n     *              {@link EcPublicJwk}.\n     * @return the builder coerced as an {@link EcPublicJwkBuilder}.\n     */\n    EcPublicJwkBuilder ecChain(List<X509Certificate> chain);\n\n    /**\n     * Ensures the builder will create an {@link EcPrivateJwk} for the specified Java Elliptic Curve\n     * {@link KeyPair}.  The pair's {@link KeyPair#getPublic() public key} <em>MUST</em> be an\n     * {@link ECPublicKey} instance.  The pair's {@link KeyPair#getPrivate() private key} <em>MUST</em> be an\n     * {@link ECPrivateKey} instance.\n     *\n     * @param keyPair the EC {@link KeyPair} to represent as an {@link EcPrivateJwk}.\n     * @return the builder coerced as an {@link EcPrivateJwkBuilder}.\n     * @throws IllegalArgumentException if the {@code keyPair} does not contain {@link ECPublicKey} and\n     *                                  {@link ECPrivateKey} instances.\n     */\n    EcPrivateJwkBuilder ecKeyPair(KeyPair keyPair) throws IllegalArgumentException;\n\n    /**\n     * Ensures the builder will create an {@link RsaPublicJwk} for the specified Java {@link X509Certificate} chain.\n     * The first {@code X509Certificate} in the chain (at list index 0) <em>MUST</em> contain an {@link RSAPublicKey}\n     * instance when calling the certificate's {@link X509Certificate#getPublicKey() getPublicKey()} method.\n     *\n     * @param chain the {@link X509Certificate} chain to inspect to find the {@link RSAPublicKey} to represent as a\n     *              {@link RsaPublicJwk}.\n     * @return the builder coerced as an {@link RsaPublicJwkBuilder}.\n     */\n    RsaPublicJwkBuilder rsaChain(List<X509Certificate> chain);\n\n    /**\n     * Ensures the builder will create an {@link RsaPrivateJwk} for the specified Java RSA\n     * {@link KeyPair}.  The pair's {@link KeyPair#getPublic() public key} <em>MUST</em> be an\n     * {@link RSAPublicKey} instance.  The pair's {@link KeyPair#getPrivate() private key} <em>MUST</em> be an\n     * {@link RSAPrivateKey} instance.\n     *\n     * @param keyPair the RSA {@link KeyPair} to represent as an {@link RsaPrivateJwk}.\n     * @return the builder coerced as an {@link RsaPrivateJwkBuilder}.\n     * @throws IllegalArgumentException if the {@code keyPair} does not contain {@link RSAPublicKey} and\n     *                                  {@link RSAPrivateKey} instances.\n     */\n    RsaPrivateJwkBuilder rsaKeyPair(KeyPair keyPair) throws IllegalArgumentException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/EcPrivateJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\n\n/**\n * JWK representation of an {@link ECPrivateKey} as defined by the JWA (RFC 7518) specification sections on\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2\">Parameters for Elliptic Curve Keys</a> and\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.2\">Parameters for Elliptic Curve Private Keys</a>.\n *\n * <p>Note that the various EC-specific properties are not available as separate dedicated getter methods, as most Java\n * applications should rarely, if ever, need to access these individual key properties since they typically represent\n * internal key material and/or serialization details. If you need to access these key properties, it is usually\n * recommended to obtain the corresponding {@link ECPrivateKey} instance returned by {@link #toKey()} and\n * query that instead.</p>\n *\n * <p>Even so, because these properties exist and are readable by nature of every JWK being a\n * {@link java.util.Map Map}, they are still accessible via the standard {@code Map} {@link #get(Object) get} method\n * using an appropriate JWK parameter id, for example:</p>\n * <blockquote><pre>\n * jwk.get(&quot;x&quot;);\n * jwk.get(&quot;y&quot;);\n * // ... etc ...</pre></blockquote>\n *\n * @since 0.12.0\n */\npublic interface EcPrivateJwk extends PrivateJwk<ECPrivateKey, ECPublicKey, EcPublicJwk> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/EcPrivateJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\n\n/**\n * A {@link PrivateJwkBuilder} that creates {@link EcPrivateJwk}s.\n *\n * @since 0.12.0\n */\npublic interface EcPrivateJwkBuilder extends PrivateJwkBuilder<ECPrivateKey, ECPublicKey, EcPublicJwk, EcPrivateJwk, EcPrivateJwkBuilder> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/EcPublicJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.ECPublicKey;\n\n/**\n * JWK representation of an {@link ECPublicKey} as defined by the JWA (RFC 7518) specification sections on\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2\">Parameters for Elliptic Curve Keys</a> and\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.1\">Parameters for Elliptic Curve Public Keys</a>.\n *\n * <p>Note that the various EC-specific properties are not available as separate dedicated getter methods, as most Java\n * applications should rarely, if ever, need to access these individual key properties since they typically represent\n * internal key material and/or serialization details. If you need to access these key properties, it is usually\n * recommended to obtain the corresponding {@link ECPublicKey} instance returned by {@link #toKey()} and\n * query that instead.</p>\n *\n * <p>Even so, because these properties exist and are readable by nature of every JWK being a\n * {@link java.util.Map Map}, they are still accessible via the standard {@code Map} {@link #get(Object) get} method\n * using an appropriate JWK parameter id, for example:</p>\n * <blockquote><pre>\n * jwk.get(&quot;x&quot;);\n * jwk.get(&quot;y&quot;);\n * // ... etc ...</pre></blockquote>\n *\n * @since 0.12.0\n */\npublic interface EcPublicJwk extends PublicJwk<ECPublicKey> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/EcPublicJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\n\n/**\n * A {@link PublicJwkBuilder} that creates {@link EcPublicJwk}s.\n *\n * @since 0.12.0\n */\npublic interface EcPublicJwkBuilder extends PublicJwkBuilder<ECPublicKey, ECPrivateKey, EcPublicJwk, EcPrivateJwk, EcPrivateJwkBuilder, EcPublicJwkBuilder> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/HashAlgorithm.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\n\nimport java.io.InputStream;\n\n/**\n * A {@link DigestAlgorithm} that computes and verifies digests without the use of a cryptographic key, such as for\n * thumbprints and <a href=\"https://en.wikipedia.org/wiki/Fingerprint_(computing)\">digital fingerprint</a>s.\n *\n * <p><b>Standard Identifier</b></p>\n *\n * <p>{@code HashAlgorithm} extends {@link Identifiable}: the value returned from\n * {@link Identifiable#getId() getId()} in all JWT standard hash algorithms will return one of the\n * &quot;{@code Hash Name String}&quot; values defined in the IANA\n * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml\">Named Information Hash\n * Algorithm Registry</a>. This is to ensure the correct algorithm ID is used within other JWT-standard identifiers,\n * such as within <a href=\"https://www.rfc-editor.org/rfc/rfc9278.html\">JWK Thumbprint URI</a>s.</p>\n *\n * <p><b>IANA Standard Implementations</b></p>\n *\n * <p>Constant definitions and utility methods for common (<em>but not all</em>)\n * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA Hash\n * Algorithms</a> are available via {@link Jwks.HASH}.</p>\n *\n * @see Jwks.HASH\n * @since 0.12.0\n */\npublic interface HashAlgorithm extends DigestAlgorithm<Request<InputStream>, VerifyDigestRequest> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/InvalidKeyException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * A {@code KeyException} thrown when encountering a key that is not suitable for the required functionality, or\n * when attempting to use a Key in an incorrect or prohibited manner.\n *\n * @since 0.10.0\n */\npublic class InvalidKeyException extends KeyException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public InvalidKeyException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     * @since 0.12.0\n     */\n    public InvalidKeyException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/IvSupplier.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * An {@code IvSupplier} provides access to the secure-random Initialization Vector used during\n * encryption, which must in turn be presented for use during decryption.  To maintain the security integrity of cryptographic\n * algorithms, a <em>new</em> secure-random Initialization Vector <em>MUST</em> be generated for every individual\n * encryption attempt.\n *\n * @since 0.12.0\n */\npublic interface IvSupplier {\n\n    /**\n     * Returns the secure-random Initialization Vector used during encryption, which must in turn be presented for\n     * use during decryption.\n     *\n     * @return the secure-random Initialization Vector used during encryption, which must in turn be presented for\n     * use during decryption.\n     */\n    byte[] getIv();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/Jwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.lang.Supplier;\n\nimport java.security.Key;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * A JWK is an immutable set of name/value pairs that represent a cryptographic key as defined by\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html\">RFC 7517: JSON Web Key (JWK)</a>.  The {@code Jwk}\n * interface represents properties common to all JWKs.  Subtypes will have additional properties specific to\n * different types of cryptographic keys (e.g. Secret, Asymmetric, RSA, Elliptic Curve, etc).\n *\n * <p><b>Immutability</b></p>\n *\n * <p>JWKs are immutable and cannot be changed after they are created.  {@code Jwk} extends the\n * {@link Map} interface purely out of convenience: to allow easy marshalling to JSON as well as name/value\n * pair access and key/value iteration, and other conveniences provided by the Map interface.  Attempting to call any of\n * the {@link Map} interface's mutation methods however (such as {@link Map#put(Object, Object) put},\n * {@link Map#remove(Object) remove}, {@link Map#clear() clear}, etc) will throw an\n * {@link UnsupportedOperationException}.</p>\n *\n * <p><b>Identification</b></p>\n *\n * <p>{@code Jwk} extends {@link Identifiable} to support the\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.5\">JWK {@code kid} parameter</a>. Calling\n * {@link #getId() aJwk.getId()} is the type-safe idiomatic approach to the alternative equivalent of\n * {@code aJwk.get(\"kid\")}. Either approach will return an id if one was originally set on the JWK, or {@code null} if\n * an id does not exist.</p>\n *\n * <p><b>Private and Secret Value Safety</b></p>\n *\n * <p>JWKs often represent secret or private key data which should never be exposed publicly, nor mistakenly printed\n * to application logs or {@code System.out.println} calls.  As a result, all JJWT JWK\n * private or secret values are 'wrapped' in a {@link io.jsonwebtoken.lang.Supplier Supplier} instance to ensure\n * any attempt to call {@link String#toString() toString()} on the value will print a redacted value instead of an\n * actual private or secret value.</p>\n *\n * <p>For example, a {@link SecretJwk} will have an internal &quot;{@code k}&quot; member whose value reflects raw\n * key material that should always be kept secret.  If the following is called:</p>\n * <blockquote><pre>\n * System.out.println(aSecretJwk.get(&quot;k&quot;));</pre></blockquote>\n * <p>You would see the following:</p>\n * <blockquote><pre>\n * &lt;redacted&gt;</pre></blockquote>\n * <p>instead of the actual/raw {@code k} value.</p>\n *\n * <p>Similarly, if attempting to print the entire JWK:</p>\n * <blockquote><pre>\n * System.out.println(aSecretJwk);</pre></blockquote>\n * <p>You would see the following substring in the output:</p>\n * <blockquote><pre>\n * k=&lt;redacted&gt;</pre></blockquote>\n * <p>instead of the actual/raw {@code k} value.</p>\n *\n * <p>Finally, because all private or secret values are wrapped as {@link io.jsonwebtoken.lang.Supplier}\n * instances, if you really wanted the <em>real</em> internal value, you could just call the supplier's\n * {@link Supplier#get() get()} method:</p>\n * <blockquote><pre>\n * String k = ((Supplier&lt;String&gt;)aSecretJwk.get(&quot;k&quot;)).get();</pre></blockquote>\n * <p>but <b><em>BE CAREFUL</em></b>: obtaining the raw value in your application code exposes greater security\n * risk - you must ensure to keep that value safe and out of console or log output.  It is almost always better to\n * interact with the JWK's {@link #toKey() toKey()} instance directly instead of accessing\n * JWK internal serialization parameters.</p>\n *\n * @param <K> The type of Java {@link Key} represented by this JWK\n * @since 0.12.0\n */\npublic interface Jwk<K extends Key> extends Identifiable, Map<String, Object> {\n\n    /**\n     * Returns the JWK\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.4\">{@code alg} (Algorithm)</a> value\n     * or {@code null} if not present.\n     *\n     * @return the JWK {@code alg} value or {@code null} if not present.\n     */\n    String getAlgorithm();\n\n    /**\n     * Returns the JWK <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">{@code key_ops}\n     * (Key Operations) parameter</a> values or {@code null} if not present.  All JWK standard Key Operations are\n     * available via the {@link Jwks.OP} registry, but other (custom) values <em>MAY</em> be present in the returned\n     * set.\n     *\n     * @return the JWK {@code key_ops} value or {@code null} if not present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\"><code>key_ops</code>(Key Operations) Parameter</a>\n     */\n    Set<KeyOperation> getOperations();\n\n    /**\n     * Returns the required JWK\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.1\">{@code kty} (Key Type)\n     * parameter</a> value. A value is required and may not be {@code null}.\n     *\n     * <p>The JWA specification <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.1\">defines</a> the\n     * following {@code kty} values:</p>\n     *\n     * <table>\n     * <caption>JWK Key Types</caption>\n     * <thead>\n     * <tr>\n     * <th>Value</th>\n     * <th>Key Type</th>\n     * </tr>\n     * </thead>\n     * <tbody>\n     * <tr>\n     * <td><b>{@code EC}</b></td>\n     * <td>Elliptic Curve [<a href=\"https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf\">DSS</a>]</td>\n     * </tr>\n     * <tr>\n     * <td><b>{@code RSA}</b></td>\n     * <td>RSA [<a href=\"https://datatracker.ietf.org/doc/html/rfc3447\">RFC 3447</a>]</td>\n     * </tr>\n     * <tr>\n     * <td><b>{@code oct}</b></td>\n     * <td>Octet sequence (used to represent symmetric keys)</td>\n     * </tr>\n     * <tr>\n     * <td><b>{@code OKP}</b></td>\n     * <td><a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-2\">Octet Key Pair</a> (used to represent Edwards\n     * Elliptic Curve keys)</td>\n     * </tr>\n     * </tbody>\n     * </table>\n     *\n     * @return the JWK {@code kty} (Key Type) value.\n     */\n    String getType();\n\n    /**\n     * Computes and returns the canonical <a href=\"https://www.rfc-editor.org/rfc/rfc7638\">JWK Thumbprint</a> of this\n     * JWK using the {@code SHA-256} hash algorithm.  This is a convenience method that delegates to\n     * {@link #thumbprint(HashAlgorithm)} with a {@code SHA-256} {@link HashAlgorithm} instance.\n     *\n     * @return the canonical <a href=\"https://www.rfc-editor.org/rfc/rfc7638\">JWK Thumbprint</a> of this\n     * JWK using the {@code SHA-256} hash algorithm.\n     * @see #thumbprint(HashAlgorithm)\n     */\n    JwkThumbprint thumbprint();\n\n    /**\n     * Computes and returns the canonical <a href=\"https://www.rfc-editor.org/rfc/rfc7638\">JWK Thumbprint</a> of this\n     * JWK using the specified hash algorithm.\n     *\n     * @param alg the hash algorithm to use to compute the digest of the canonical JWK Thumbprint JSON form of this JWK.\n     * @return the canonical <a href=\"https://www.rfc-editor.org/rfc/rfc7638\">JWK Thumbprint</a> of this\n     * JWK using the specified hash algorithm.\n     */\n    JwkThumbprint thumbprint(HashAlgorithm alg);\n\n    /**\n     * Represents the JWK as its corresponding Java {@link Key} instance for use with Java cryptographic\n     * APIs.\n     *\n     * @return the JWK's corresponding Java {@link Key} instance for use with Java cryptographic APIs.\n     */\n    K toKey();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/JwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.lang.Conjunctor;\nimport io.jsonwebtoken.lang.MapMutator;\nimport io.jsonwebtoken.lang.NestedCollection;\n\nimport java.security.Key;\n\n/**\n * A {@link SecurityBuilder} that produces a JWK.  A JWK is an immutable set of name/value pairs that represent a\n * cryptographic key as defined by\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html\">RFC 7517: JSON Web Key (JWK)</a>.\n * The {@code JwkBuilder} interface represents common JWK properties that may be specified for any type of JWK.\n * Builder subtypes support additional JWK properties specific to different types of cryptographic keys\n * (e.g. Secret, Asymmetric, RSA, Elliptic Curve, etc).\n *\n * @param <K> the type of Java {@link Key} represented by the constructed JWK.\n * @param <J> the type of {@link Jwk} created by the builder\n * @param <T> the type of the builder, for subtype method chaining\n * @see SecretJwkBuilder\n * @see RsaPublicJwkBuilder\n * @see RsaPrivateJwkBuilder\n * @see EcPublicJwkBuilder\n * @see EcPrivateJwkBuilder\n * @see OctetPublicJwkBuilder\n * @see OctetPrivateJwkBuilder\n * @since 0.12.0\n */\npublic interface JwkBuilder<K extends Key, J extends Jwk<K>, T extends JwkBuilder<K, J, T>>\n        extends MapMutator<String, Object, T>, SecurityBuilder<J, T>, KeyOperationPolicied<T> {\n\n    /**\n     * Sets the JWK <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.4\">{@code alg} (Algorithm)\n     * Parameter</a>.\n     *\n     * <p>The {@code alg} (algorithm) parameter identifies the algorithm intended for use with the key.  The\n     * value specified should either be one of the values in the IANA\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1\">JSON Web Signature and Encryption\n     * Algorithms</a> registry or be a value that contains a {@code Collision-Resistant Name}.  The {@code alg}\n     * must be a CaSe-SeNsItIvE ASCII string.</p>\n     *\n     * @param alg the JWK {@code alg} value.\n     * @return the builder for method chaining.\n     * @throws IllegalArgumentException if {@code alg} is {@code null} or empty.\n     */\n    T algorithm(String alg) throws IllegalArgumentException;\n\n    /**\n     * Sets the JWK <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.5\">{@code kid} (Key ID)\n     * Parameter</a>.\n     *\n     * <p>The {@code kid} (key ID) parameter is used to match a specific key.  This is used, for instance,\n     * to choose among a set of keys within a {@code JWK Set} during key rollover.  The structure of the\n     * {@code kid} value is unspecified.  When {@code kid} values are used within a JWK Set, different keys\n     * within the {@code JWK Set} <em>SHOULD</em> use distinct {@code kid} values. (One example in which\n     * different keys might use the same {@code kid} value is if they have different {@code kty} (key type)\n     * values but are considered to be equivalent alternatives by the application using them.)</p>\n     *\n     * <p>The {@code kid} value is a CaSe-SeNsItIvE string, and it is optional. When used with JWS or JWE,\n     * the {@code kid} value is used to match a JWS or JWE {@code kid} Header Parameter value.</p>\n     *\n     * @param kid the JWK {@code kid} value.\n     * @return the builder for method chaining.\n     * @throws IllegalArgumentException if the argument is {@code null} or empty.\n     */\n    T id(String kid) throws IllegalArgumentException;\n\n    /**\n     * Sets the JWK's {@link #id(String) kid} value to be the Base64URL-encoding of its {@code SHA-256}\n     * {@link Jwk#thumbprint(HashAlgorithm) thumbprint}.  That is, the constructed JWK's {@code kid} value will equal\n     * <code>jwk.{@link Jwk#thumbprint(HashAlgorithm) thumbprint}({@link Jwks.HASH}.{@link Jwks.HASH#SHA256 SHA256}).{@link JwkThumbprint#toString() toString()}</code>.\n     *\n     * <p>This is a convenience method that delegates to {@link #idFromThumbprint(HashAlgorithm)} using\n     * {@link Jwks.HASH}{@code .}{@link Jwks.HASH#SHA256 SHA256}.</p>\n     *\n     * @return the builder for method chaining.\n     */\n    T idFromThumbprint();\n\n    /**\n     * Sets the JWK's {@link #id(String) kid} value to be the Base64URL-encoding of its\n     * {@link Jwk#thumbprint(HashAlgorithm) thumbprint} using the specified {@link HashAlgorithm}.  That is, the\n     * constructed JWK's {@code kid} value will equal\n     * <code>{@link Jwk#thumbprint(HashAlgorithm) thumbprint}(alg).{@link JwkThumbprint#toString() toString()}.</code>\n     *\n     * @param alg the hash algorithm to use to compute the thumbprint.\n     * @return the builder for method chaining.\n     * @see Jwks.HASH\n     */\n    T idFromThumbprint(HashAlgorithm alg);\n\n    /**\n     * Configures the <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">key operations</a> for which\n     * the key is intended to be used. When finished, use the collection's {@link Conjunctor#and() and()} method to\n     * return to the JWK builder, for example:\n     * <blockquote><pre>\n     * jwkBuilder.operations().add(aKeyOperation)<b>.{@link Conjunctor#and() and()} // etc...</b></pre></blockquote>\n     *\n     * <p>The {@code add()} method(s) will throw an {@link IllegalArgumentException} if any of the specified\n     * {@code KeyOperation}s are not permitted by the JWK's\n     * {@link #operationPolicy(KeyOperationPolicy) operationPolicy}. See that documentation for more\n     * information on security vulnerabilities when using the same key with multiple algorithms.</p>\n     *\n     * <p><b>Standard {@code KeyOperation}s and Overrides</b></p>\n     *\n     * <p>All RFC-standard JWK Key Operations in the {@link Jwks.OP} registry are supported via the builder's default\n     * {@link #operationPolicy(KeyOperationPolicy) operationPolicy}, but other (custom) values\n     * <em>MAY</em> be specified (for example, using a {@link Jwks.OP#builder()}).</p>\n     *\n     * <p>If the {@code JwkBuilder} is being used to rebuild or parse an existing JWK however, any custom operations\n     * should be enabled by configuring an {@link #operationPolicy(KeyOperationPolicy) operationPolicy}\n     * that includes the custom values (e.g. via\n     * {@link Jwks.OP#policy()}.{@link KeyOperationPolicyBuilder#add(KeyOperation) add(customKeyOperation)}).</p>\n     *\n     * <p>For best interoperability with other applications however, it is recommended to use only the {@link Jwks.OP}\n     * constants.</p>\n     *\n     * @return the {@link NestedCollection} to use for {@code key_ops} configuration.\n     * @see Jwks.OP\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">RFC 7517: key_ops (Key Operations) Parameter</a>\n     */\n    NestedCollection<KeyOperation, T> operations();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/JwkParserBuilder.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.io.ParserBuilder;\n\n/**\n * A builder to construct a {@link Parser} that can parse {@link Jwk}s.\n * Example usage:\n * <blockquote><pre>\n * Jwk&lt;?&gt; jwk = Jwks.parser()\n *         .provider(aJcaProvider)     // optional\n *         .deserializer(deserializer) // optional\n *         .operationPolicy(policy)    // optional\n *         .build()\n *         .parse(jwkString);</pre></blockquote>\n *\n * @since 0.12.0\n */\npublic interface JwkParserBuilder extends ParserBuilder<Jwk<?>, JwkParserBuilder>, KeyOperationPolicied<JwkParserBuilder> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/JwkSet.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * A JWK Set is an immutable JSON Object that represents a Set of {@link Jwk}s as defined by\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-5\">RFC 7517 JWK Set Format</a>. Per that specification,\n * any number of name/value pairs may be present in a {@code JwkSet}, but only a non-empty {@link #getKeys() keys}\n * set <em>MUST</em> be present.\n *\n * <p><b>Immutability</b></p>\n *\n * <p>JWK Sets are immutable and cannot be changed after they are created.  {@code JwkSet} extends the\n * {@link Map} interface purely out of convenience: to allow easy marshalling to JSON as well as name/value\n * pair access and key/value iteration, and other conveniences provided by the Map interface.  Attempting to call any of\n * the {@link Map} interface's mutation methods however (such as {@link Map#put(Object, Object) put},\n * {@link Map#remove(Object) remove}, {@link Map#clear() clear}, etc) will throw an\n * {@link UnsupportedOperationException}.</p>\n *\n * @since 0.12.0\n */\npublic interface JwkSet extends Map<String, Object>, Iterable<Jwk<?>> {\n\n    /**\n     * Returns the non-null, non-empty set of JWKs contained within the {@code JwkSet}.\n     *\n     * @return the non-null, non-empty set of JWKs contained within the {@code JwkSet}.\n     */\n    Set<Jwk<?>> getKeys();\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/JwkSetBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.lang.MapMutator;\n\nimport java.security.Provider;\nimport java.util.Collection;\n\n/**\n * A builder that produces {@link JwkSet}s containing {@link Jwk}s. {@code Jwk}s with any key\n * {@link Jwk#getOperations() operations} will be validated by\n * the {@link #operationPolicy(KeyOperationPolicy) operationPolicy} first before being added.\n *\n * @see #operationPolicy(KeyOperationPolicy)\n * @see #provider(Provider)\n * @since 0.12.0\n */\npublic interface JwkSetBuilder extends MapMutator<String, Object, JwkSetBuilder>,\n        SecurityBuilder<JwkSet, JwkSetBuilder>, KeyOperationPolicied<JwkSetBuilder> {\n\n    /**\n     * Appends the specified {@code jwk} to the set. If the {@code jwk} has any key\n     * {@link Jwk#getOperations() operations}, it will be validated with the\n     * {@link #operationPolicy(KeyOperationPolicy) operationPolicy} first before being added.\n     *\n     * @param jwk the jwk to add to the JWK Set. A {@code null} {@code jwk} is ignored.\n     * @return the builder for method chaining\n     */\n    JwkSetBuilder add(Jwk<?> jwk);\n\n    /**\n     * Appends the specified {@code Jwk} collection to the JWK Set. If any {@code Jwk} in the collection has\n     * any key {@link Jwk#getOperations() operations}, it will be validated with the\n     * {@link #operationPolicy(KeyOperationPolicy) operationPolicy} first before being added.\n     *\n     * @param c the collection of {@code Jwk}s to add to the JWK Set. A {@code null} or empty collection is ignored.\n     * @return the builder for method chaining\n     */\n    JwkSetBuilder add(Collection<Jwk<?>> c);\n\n    /**\n     * Sets the {@code JwkSet} {@code keys} parameter value; per standard Java setter idioms, this is a\n     * <em>full replacement</em> operation, removing any previous keys from the set.  A {@code null} or empty\n     * collection removes all keys from the set.\n     *\n     * @param c the (possibly null or empty) collection of {@code Jwk}s to set as the JWK set {@code keys} parameter\n     *          value.\n     * @return the builder for method chaining\n     */\n    JwkSetBuilder keys(Collection<Jwk<?>> c);\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/JwkSetParserBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.io.ParserBuilder;\n\n/**\n * A builder to construct a {@link Parser} that can parse {@link JwkSet}s.\n * Example usage:\n * <blockquote><pre>\n * JwkSet jwkSet = Jwks.setParser()\n *         .provider(aJcaProvider)      // optional\n *         .json(deserializer)          // optional\n *         .operationPolicy(policy)     // optional\n *         .ignoreUnsupported(aBoolean) // optional\n *         .build()\n *         .parse(jwkSetString);</pre></blockquote>\n *\n * @since 0.12.0\n */\npublic interface JwkSetParserBuilder extends ParserBuilder<JwkSet, JwkSetParserBuilder>, KeyOperationPolicied<JwkSetParserBuilder> {\n\n    /**\n     * Sets whether the parser should ignore any encountered JWK it does not support, either because the JWK has an\n     * unrecognized {@link Jwk#getType() key type} or the JWK was malformed (missing required parameters, etc).\n     * The default value is {@code true} per\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-5\">RFC 7517, Section 5</a>, last paragraph:\n     * <blockquote><pre>\n     *    Implementations SHOULD ignore JWKs within a JWK Set that use \"kty\"\n     *    (key type) values that are not understood by them, that are missing\n     *    required members, or for which values are out of the supported\n     *    ranges.\n     * </pre></blockquote>\n     *\n     * <p>This value may be set to {@code false} for applications that prefer stricter parsing constraints\n     * and wish to react to any {@link MalformedKeyException}s or {@link UnsupportedKeyException}s that could\n     * occur.</p>\n     *\n     * @param ignore whether to ignore unsupported or malformed JWKs encountered during parsing.\n     * @return the builder for method chaining.\n     */\n    JwkSetParserBuilder ignoreUnsupported(boolean ignore);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/JwkThumbprint.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.net.URI;\n\n/**\n * A canonical cryptographic digest of a JWK as defined by the\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7638\">JSON Web Key (JWK) Thumbprint</a> specification.\n *\n * @since 0.12.0\n */\npublic interface JwkThumbprint {\n\n    /**\n     * Returns the {@link HashAlgorithm} used to compute the thumbprint.\n     *\n     * @return the {@link HashAlgorithm} used to compute the thumbprint.\n     */\n    HashAlgorithm getHashAlgorithm();\n\n    /**\n     * Returns the actual thumbprint (aka digest) byte array value.\n     *\n     * @return the actual thumbprint (aka digest) byte array value.\n     */\n    byte[] toByteArray();\n\n    /**\n     * Returns the canonical URI representation of this thumbprint as defined by the\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc9278.html\">JWK Thumbprint URI</a> specification.\n     *\n     * @return a canonical JWK Thumbprint URI\n     */\n    URI toURI();\n\n    /**\n     * Returns the {@link #toByteArray()} value as a Base64URL-encoded string.\n     */\n    String toString();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/Jwks.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.lang.Classes;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Supplier;\n\n/**\n * Utility methods for creating\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html\">JWKs (JSON Web Keys)</a> with a type-safe builder.\n *\n * <p><b>Standard JWK Thumbprint Algorithm References</b></p>\n * <p>Standard <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA Hash\n * Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid\n * <a href=\"https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier\">JWK Thumbprint URIs</a>\n * are available via the {@link Jwks.HASH} registry constants to allow for easy code-completion in IDEs. For example, when\n * typing:</p>\n * <blockquote><pre>\n * Jwks.{@link Jwks.HASH HASH}.// press hotkeys to suggest individual hash algorithms or utility methods</pre></blockquote>\n *\n * @see #builder()\n * @since 0.12.0\n */\npublic final class Jwks {\n\n    private Jwks() {\n    } //prevent instantiation\n\n    private static final String JWKS_BRIDGE_FQCN = \"io.jsonwebtoken.impl.security.JwksBridge\";\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    private static final Supplier<DynamicJwkBuilder<?, ?>> BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.security.DefaultDynamicJwkBuilder$Supplier\");\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    private static final Supplier<JwkParserBuilder> PARSER_BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.security.DefaultJwkParserBuilder$Supplier\");\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    private static final Supplier<JwkSetBuilder> SET_BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.security.DefaultJwkSetBuilder$Supplier\");\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    private static final Supplier<JwkSetParserBuilder> SET_PARSER_BUILDER_SUPPLIER =\n            Classes.newInstance(\"io.jsonwebtoken.impl.security.DefaultJwkSetParserBuilder$Supplier\");\n\n    /**\n     * Return a new JWK builder instance, allowing for type-safe JWK builder coercion based on a specified key or key pair.\n     *\n     * @return a new JWK builder instance, allowing for type-safe JWK builder coercion based on a specified key or key pair.\n     */\n    public static DynamicJwkBuilder<?, ?> builder() {\n        return BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * Returns a new builder used to create {@link Parser}s that parse JSON into {@link Jwk} instances. For example:\n     * <blockquote><pre>\n     * Jwk&lt;?&gt; jwk = Jwks.parser()\n     *         //.provider(aJcaProvider)     // optional\n     *         //.deserializer(deserializer) // optional\n     *         //.operationPolicy(policy)    // optional\n     *         .build()\n     *         .parse(jwkString);</pre></blockquote>\n     *\n     * @return a new builder used to create {@link Parser}s that parse JSON into {@link Jwk} instances.\n     */\n    public static JwkParserBuilder parser() {\n        return PARSER_BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * Return a new builder used to create {@link JwkSet}s.  For example:\n     * <blockquote><pre>\n     * JwkSet jwkSet = Jwks.set()\n     *     //.provider(aJcaProvider)     // optional\n     *     //.operationPolicy(policy)    // optional\n     *     .add(aJwk)                    // appends a key\n     *     .add(aCollection)             // appends multiple keys\n     *     //.keys(allJwks)              // sets/replaces all keys\n     *     .build()\n     * </pre></blockquote>\n     *\n     * @return a new builder used to create {@link JwkSet}s\n     */\n    public static JwkSetBuilder set() {\n        return SET_BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * Returns a new builder used to create {@link Parser}s that parse JSON into {@link JwkSet} instances. For example:\n     * <blockquote><pre>\n     * JwkSet jwkSet = Jwks.setParser()\n     *         //.provider(aJcaProvider)     // optional\n     *         //.deserializer(deserializer) // optional\n     *         //.operationPolicy(policy)    // optional\n     *         .build()\n     *         .parse(jwkSetString);</pre></blockquote>\n     *\n     * @return a new builder used to create {@link Parser}s that parse JSON into {@link JwkSet} instances.\n     */\n    public static JwkSetParserBuilder setParser() {\n        return SET_PARSER_BUILDER_SUPPLIER.get();\n    }\n\n    /**\n     * Converts the specified {@link PublicJwk} into JSON. Because {@link PublicJwk}s do not contain secret or private\n     * key material, they are safe to be printed to application logs or {@code System.out}.\n     *\n     * @param publicJwk the {@code PublicJwk} to convert to JSON\n     * @return the JWK's canonical JSON value\n     */\n    public static String json(PublicJwk<?> publicJwk) {\n        return UNSAFE_JSON(publicJwk); // safe by nature of it being a Public JWK\n    }\n\n    /**\n     * <b>WARNING - UNSAFE OPERATION - RETURN VALUES CONTAIN RAW KEY MATERIAL, DO NOT LOG OR PRINT TO SYSTEM.OUT.</b>\n     * Converts the specified JWK into JSON, including raw key material.  If the specified JWK\n     * is a {@link SecretJwk} or a {@link PrivateJwk}, be very careful with the return value, ensuring it is not\n     * printed to application logs or system.out.\n     *\n     * @param jwk the JWK to convert to JSON\n     * @return the JWK's canonical JSON value\n     */\n    public static String UNSAFE_JSON(Jwk<?> jwk) {\n        return Classes.invokeStatic(JWKS_BRIDGE_FQCN, \"UNSAFE_JSON\", new Class[]{Jwk.class}, jwk);\n    }\n\n    /**\n     * Constants for all standard JWK\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.2.1.1\">crv (Curve)</a> parameter values\n     * defined in the <a href=\"https://datatracker.ietf.org/doc/html/rfc7518#section-7.6\">JSON Web Key Elliptic\n     * Curve Registry</a> (including its\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-5\">Edwards Elliptic Curve additions</a>).\n     * Each standard algorithm is available as a ({@code public static final}) constant for direct type-safe\n     * reference in application code. For example:\n     * <blockquote><pre>\n     * Jwks.CRV.P256.keyPair().build();</pre></blockquote>\n     * <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>\n     *\n     * @see #get()\n     * @since 0.12.0\n     */\n    public static final class CRV {\n\n        private static final String IMPL_CLASSNAME = \"io.jsonwebtoken.impl.security.StandardCurves\";\n        private static final Registry<String, Curve> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);\n\n        /**\n         * Returns a registry of all standard Elliptic Curves in the {@code JSON Web Key Elliptic Curve Registry}\n         * defined by <a href=\"https://datatracker.ietf.org/doc/html/rfc7518#section-7.6\">RFC 7518, Section 7.6</a>\n         * (for Weierstrass Elliptic Curves) and\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-5\">RFC 8037, Section 5</a> (for Edwards Elliptic Curves).\n         *\n         * @return a registry of all standard Elliptic Curves in the {@code JSON Web Key Elliptic Curve Registry}.\n         */\n        public static Registry<String, Curve> get() {\n            return REGISTRY;\n        }\n\n        /**\n         * {@code P-256} Elliptic Curve defined by\n         * <a href=\"https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.1\">RFC 7518, Section 6.2.1.1</a>\n         * using the native Java JCA {@code secp256r1} algorithm.\n         *\n         * @see <a href=\"https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html\">Java Security Standard Algorithm Names</a>\n         */\n        public static final Curve P256 = get().forKey(\"P-256\");\n\n        /**\n         * {@code P-384} Elliptic Curve defined by\n         * <a href=\"https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.1\">RFC 7518, Section 6.2.1.1</a>\n         * using the native Java JCA {@code secp384r1} algorithm.\n         *\n         * @see <a href=\"https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html\">Java Security Standard Algorithm Names</a>\n         */\n        public static final Curve P384 = get().forKey(\"P-384\");\n\n        /**\n         * {@code P-521} Elliptic Curve defined by\n         * <a href=\"https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.1\">RFC 7518, Section 6.2.1.1</a>\n         * using the native Java JCA {@code secp521r1} algorithm.\n         *\n         * @see <a href=\"https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html\">Java Security Standard Algorithm Names</a>\n         */\n        public static final Curve P521 = get().forKey(\"P-521\");\n\n        /**\n         * {@code Ed25519} Elliptic Curve defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-3.1\">RFC 8037, Section 3.1</a>\n         * using the native Java JCA {@code Ed25519}<b><sup>1</sup></b> algorithm.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 14 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         *\n         * @see <a href=\"https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html\">Java Security Standard Algorithm Names</a>\n         */\n        public static final Curve Ed25519 = get().forKey(\"Ed25519\");\n\n        /**\n         * {@code Ed448} Elliptic Curve defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-3.1\">RFC 8037, Section 3.1</a>\n         * using the native Java JCA {@code Ed448}<b><sup>1</sup></b> algorithm.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 15 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 14 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         *\n         * @see <a href=\"https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html\">Java Security Standard Algorithm Names</a>\n         */\n        public static final Curve Ed448 = get().forKey(\"Ed448\");\n\n        /**\n         * {@code X25519} Elliptic Curve defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-3.2\">RFC 8037, Section 3.2</a>\n         * using the native Java JCA {@code X25519}<b><sup>1</sup></b> algorithm.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         *\n         * @see <a href=\"https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html\">Java Security Standard Algorithm Names</a>\n         */\n        public static final Curve X25519 = get().forKey(\"X25519\");\n\n        /**\n         * {@code X448} Elliptic Curve defined by\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-3.2\">RFC 8037, Section 3.2</a>\n         * using the native Java JCA {@code X448}<b><sup>1</sup></b> algorithm.\n         *\n         * <p><b><sup>1</sup></b> Requires Java 11 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath. If on Java 10 or earlier, BouncyCastle will be used automatically if found in the runtime\n         * classpath.</p>\n         *\n         * @see <a href=\"https://docs.oracle.com/en/java/javase/17/docs/specs/security/standard-names.html\">Java Security Standard Algorithm Names</a>\n         */\n        public static final Curve X448 = get().forKey(\"X448\");\n\n        //prevent instantiation\n        private CRV() {\n        }\n    }\n\n    /**\n     * Various (<em>but not all</em>)\n     * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA Hash\n     * Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier\">JWK Thumbprint URIs</a>.\n     * Each algorithm is made available as a ({@code public static final}) constant for direct type-safe\n     * reference in application code. For example:\n     * <blockquote><pre>\n     * Jwks.{@link Jwks#builder}()\n     *     // ... etc ...\n     *     .{@link JwkBuilder#idFromThumbprint(HashAlgorithm) idFromThumbprint}(Jwts.HASH.{@link Jwks.HASH#SHA256 SHA256}) // &lt;---\n     *     .build()</pre></blockquote>\n     * <p>or</p>\n     * <blockquote><pre>\n     * HashAlgorithm hashAlg = Jwks.HASH.{@link Jwks.HASH#SHA256 SHA256};\n     * {@link JwkThumbprint} thumbprint = aJwk.{@link Jwk#thumbprint(HashAlgorithm) thumbprint}(hashAlg);\n     * String <a href=\"https://www.rfc-editor.org/rfc/rfc9278#section-3\">rfcMandatoryPrefix</a> = \"urn:ietf:params:oauth:jwk-thumbprint:\" + hashAlg.getId();\n     * assert thumbprint.toURI().toString().startsWith(rfcMandatoryPrefix);\n     * </pre></blockquote>\n     * <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>\n     *\n     * @see #get()\n     * @since 0.12.0\n     */\n    public static final class HASH {\n\n        private static final String IMPL_CLASSNAME = \"io.jsonwebtoken.impl.security.StandardHashAlgorithms\";\n        private static final Registry<String, HashAlgorithm> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);\n\n        /**\n         * Returns a registry of various (<em>but not all</em>)\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA Hash\n         * Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier\">JWK Thumbprint URIs</a>.\n         *\n         * @return a registry of various (<em>but not all</em>)\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA Hash\n         * Algorithms</a> commonly used to compute {@link JwkThumbprint JWK Thumbprint}s and ensure valid\n         * <a href=\"https://www.rfc-editor.org/rfc/rfc9278#name-hash-algorithms-identifier\">JWK Thumbprint URIs</a>.\n         */\n        public static Registry<String, HashAlgorithm> get() {\n            return REGISTRY;\n        }\n\n        /**\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA\n         * hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)\n         * value of {@code sha-256}. It is a {@code HashAlgorithm} alias for the native\n         * Java JCA {@code SHA-256} {@code MessageDigest} algorithm.\n         */\n        public static final HashAlgorithm SHA256 = get().forKey(\"sha-256\");\n\n        /**\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA\n         * hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)\n         * value of {@code sha-384}. It is a {@code HashAlgorithm} alias for the native\n         * Java JCA {@code SHA-384} {@code MessageDigest} algorithm.\n         */\n        public static final HashAlgorithm SHA384 = get().forKey(\"sha-384\");\n\n        /**\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA\n         * hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)\n         * value of {@code sha-512}. It is a {@code HashAlgorithm} alias for the native\n         * Java JCA {@code SHA-512} {@code MessageDigest} algorithm.\n         */\n        public static final HashAlgorithm SHA512 = get().forKey(\"sha-512\");\n\n        /**\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA\n         * hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)\n         * value of {@code sha3-256}. It is a {@code HashAlgorithm} alias for the native\n         * Java JCA {@code SHA3-256} {@code MessageDigest} algorithm.\n         * <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath.</b></p>\n         */\n        public static final HashAlgorithm SHA3_256 = get().forKey(\"sha3-256\");\n\n        /**\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA\n         * hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)\n         * value of {@code sha3-384}. It is a {@code HashAlgorithm} alias for the native\n         * Java JCA {@code SHA3-384} {@code MessageDigest} algorithm.\n         * <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath.</b></p>\n         */\n        public static final HashAlgorithm SHA3_384 = get().forKey(\"sha3-384\");\n\n        /**\n         * <a href=\"https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg\">IANA\n         * hash algorithm</a> with an {@link Identifiable#getId() id} (aka IANA &quot;{@code Hash Name String}&quot;)\n         * value of {@code sha3-512}. It is a {@code HashAlgorithm} alias for the native\n         * Java JCA {@code SHA3-512} {@code MessageDigest} algorithm.\n         * <p><b>This algorithm requires at least JDK 9 or a compatible JCA Provider (like BouncyCastle) in the runtime\n         * classpath.</b></p>\n         */\n        public static final HashAlgorithm SHA3_512 = get().forKey(\"sha3-512\");\n\n        //prevent instantiation\n        private HASH() {\n        }\n    }\n\n    /**\n     * Constants for all standard JWK\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">key_ops (Key Operations)</a> parameter values\n     * defined in the <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3\">JSON Web Key Operations\n     * Registry</a>. Each standard key operation is available as a ({@code public static final}) constant for\n     * direct type-safe reference in application code. For example:\n     * <blockquote><pre>\n     * Jwks.builder()\n     *     .operations(Jwks.OP.SIGN)\n     *     // ... etc ...\n     *     .build();</pre></blockquote>\n     * <p>They are also available together as a {@link Registry} instance via the {@link #get()} method.</p>\n     *\n     * @see #get()\n     * @since 0.12.0\n     */\n    public static final class OP {\n\n        private static final String IMPL_CLASSNAME = \"io.jsonwebtoken.impl.security.StandardKeyOperations\";\n        private static final Registry<String, KeyOperation> REGISTRY = Classes.newInstance(IMPL_CLASSNAME);\n\n        // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n        private static final Supplier<KeyOperationBuilder> BUILDER_SUPPLIER =\n                Classes.newInstance(\"io.jsonwebtoken.impl.security.DefaultKeyOperationBuilder$Supplier\");\n\n        // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n        private static final Supplier<KeyOperationPolicyBuilder> POLICY_BUILDER_SUPPLIER =\n                Classes.newInstance(\"io.jsonwebtoken.impl.security.DefaultKeyOperationPolicyBuilder$Supplier\");\n\n        /**\n         * Creates a new {@link KeyOperationBuilder} for creating custom {@link KeyOperation} instances.\n         *\n         * @return a new {@link KeyOperationBuilder} for creating custom {@link KeyOperation} instances.\n         */\n        public static KeyOperationBuilder builder() {\n            return BUILDER_SUPPLIER.get();\n        }\n\n        /**\n         * Creates a new {@link KeyOperationPolicyBuilder} for creating custom {@link KeyOperationPolicy} instances.\n         *\n         * @return a new {@link KeyOperationPolicyBuilder} for creating custom {@link KeyOperationPolicy} instances.\n         */\n        public static KeyOperationPolicyBuilder policy() {\n            return POLICY_BUILDER_SUPPLIER.get();\n        }\n\n        /**\n         * Returns a registry of all standard Key Operations in the {@code JSON Web Key Operations Registry}\n         * defined by <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3\">RFC 7517, Section 8.3</a>.\n         *\n         * @return a registry of all standard Key Operations in the {@code JSON Web Key Operations Registry}.\n         */\n        public static Registry<String, KeyOperation> get() {\n            return REGISTRY;\n        }\n\n        /**\n         * {@code sign} operation indicating a key is intended to be used to compute digital signatures or\n         * MACs. It's related operation is {@link #VERIFY}.\n         *\n         * @see #VERIFY\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation SIGN = get().forKey(\"sign\");\n\n        /**\n         * {@code verify} operation indicating a key is intended to be used to verify digital signatures or\n         * MACs. It's related operation is {@link #SIGN}.\n         *\n         * @see #SIGN\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation VERIFY = get().forKey(\"verify\");\n\n        /**\n         * {@code encrypt} operation indicating a key is intended to be used to encrypt content. It's\n         * related operation is {@link #DECRYPT}.\n         *\n         * @see #DECRYPT\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation ENCRYPT = get().forKey(\"encrypt\");\n\n        /**\n         * {@code decrypt} operation indicating a key is intended to be used to decrypt content. It's\n         * related operation is {@link #ENCRYPT}.\n         *\n         * @see #ENCRYPT\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation DECRYPT = get().forKey(\"decrypt\");\n\n        /**\n         * {@code wrapKey} operation indicating a key is intended to be used to encrypt another key. It's\n         * related operation is {@link #UNWRAP_KEY}.\n         *\n         * @see #UNWRAP_KEY\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation WRAP_KEY = get().forKey(\"wrapKey\");\n\n        /**\n         * {@code unwrapKey} operation indicating a key is intended to be used to decrypt another key and validate\n         * decryption, if applicable. It's related operation is\n         * {@link #WRAP_KEY}.\n         *\n         * @see #WRAP_KEY\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation UNWRAP_KEY = get().forKey(\"unwrapKey\");\n\n        /**\n         * {@code deriveKey} operation indicating a key is intended to be used to derive another key. It does not have\n         * a related operation.\n         *\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation DERIVE_KEY = get().forKey(\"deriveKey\");\n\n        /**\n         * {@code deriveBits} operation indicating a key is intended to be used to derive bits that are not to be\n         * used as key. It does not have a related operation.\n         *\n         * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3.2\">Key Operation Registry Contents</a>\n         */\n        public static final KeyOperation DERIVE_BITS = get().forKey(\"deriveBits\");\n\n        //prevent instantiation\n        private OP() {\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.Jwts;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\n\n/**\n * A {@code KeyAlgorithm} produces the {@link SecretKey} used to encrypt or decrypt a JWE. The {@code KeyAlgorithm}\n * used for a particular JWE is {@link #getId() identified} in the JWE's\n * <a href=\"https://tools.ietf.org/html/rfc7516#section-4.1.1\">{@code alg} header</a>.  The {@code KeyAlgorithm}\n * interface is JJWT's idiomatic approach to the JWE specification's\n * <a href=\"https://tools.ietf.org/html/rfc7516#section-2\">{@code Key Management Mode}</a> concept.\n *\n * <p>All standard Key Algorithms are defined in\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1\">JWA (RFC 7518), Section 4.1</a>,\n * and they are all available as concrete instances via {@link Jwts.KEY}.</p>\n *\n * <p><b>&quot;alg&quot; identifier</b></p>\n *\n * <p>{@code KeyAlgorithm} extends {@code Identifiable}: the value returned from\n * {@link Identifiable#getId() keyAlgorithm.getId()} will be used as the\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.1\">JWE &quot;alg&quot; protected header</a> value.</p>\n *\n * @param <E> The type of key to use to obtain the AEAD encryption key\n * @param <D> The type of key to use to obtain the AEAD decryption key\n * @see Jwts.KEY\n * @see <a href=\"https://tools.ietf.org/html/rfc7516#section-2\">RFC 7561, Section 2: JWE Key (Management) Algorithms</a>\n * @since 0.12.0\n */\n@SuppressWarnings(\"JavadocLinkAsPlainText\")\npublic interface KeyAlgorithm<E extends Key, D extends Key> extends Identifiable {\n\n    /**\n     * Return the {@link SecretKey} that should be used to encrypt a JWE via the request's specified\n     * {@link KeyRequest#getEncryptionAlgorithm() AeadAlgorithm}.  The encryption key will\n     * be available via the result's {@link KeyResult#getKey() result.getKey()} method.\n     *\n     * <p>If the key algorithm uses key encryption or key agreement to produce an encrypted key value that must be\n     * included in the JWE, the encrypted key ciphertext will be available via the result's\n     * {@link KeyResult#getPayload() result.getPayload()} method.  If the key algorithm does not produce encrypted\n     * key ciphertext, {@link KeyResult#getPayload() result.getPayload()} will be a non-null empty byte array.</p>\n     *\n     * @param request the {@code KeyRequest} containing information necessary to produce a {@code SecretKey} for\n     *                {@link AeadAlgorithm AEAD} encryption.\n     * @return the {@link SecretKey} that should be used to encrypt a JWE via the request's specified\n     * {@link KeyRequest#getEncryptionAlgorithm() AeadAlgorithm}, along with any optional encrypted key ciphertext.\n     * @throws SecurityException if there is a problem obtaining or encrypting the AEAD {@code SecretKey}.\n     */\n    KeyResult getEncryptionKey(KeyRequest<E> request) throws SecurityException;\n\n    /**\n     * Return the {@link SecretKey} that should be used to decrypt a JWE via the request's specified\n     * {@link DecryptionKeyRequest#getEncryptionAlgorithm() AeadAlgorithm}.\n     *\n     * <p>If the key algorithm used key encryption or key agreement to produce an encrypted key value, the encrypted\n     * key ciphertext will be available via the request's {@link DecryptionKeyRequest#getPayload() result.getPayload()}\n     * method. If the key algorithm did not produce encrypted key ciphertext,\n     * {@link DecryptionKeyRequest#getPayload() request.getPayload()} will return a non-null empty byte array.</p>\n     *\n     * @param request the {@code DecryptionKeyRequest} containing information necessary to obtain a\n     *                {@code SecretKey} for {@link AeadAlgorithm AEAD} decryption.\n     * @return the {@link SecretKey} that should be used to decrypt a JWE via the request's specified\n     * {@link DecryptionKeyRequest#getEncryptionAlgorithm() AeadAlgorithm}.\n     * @throws SecurityException if there is a problem obtaining or decrypting the AEAD {@code SecretKey}.\n     */\n    SecretKey getDecryptionKey(DecryptionKeyRequest<D> request) throws SecurityException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\n\n/**\n * A {@code KeyBuilder} produces new {@link Key}s suitable for use with an associated cryptographic algorithm.\n * A new {@link Key} is created each time the builder's {@link #build()} method is called.\n *\n * <p>{@code KeyBuilder}s are provided by components that implement the {@link KeyBuilderSupplier} interface,\n * ensuring the resulting {@link SecretKey}s are compatible with their associated cryptographic algorithm.</p>\n *\n * @param <K> the type of key to build\n * @param <B> the type of the builder, for subtype method chaining\n * @see KeyBuilderSupplier\n * @since 0.12.0\n */\npublic interface KeyBuilder<K extends Key, B extends KeyBuilder<K, B>> extends SecurityBuilder<K, B> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyBuilderSupplier.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Key;\n\n/**\n * Interface implemented by components that support building/creating new {@link Key}s suitable for use with\n * their associated cryptographic algorithm implementation.\n *\n * @param <K> type of {@link Key} created by the builder\n * @param <B> type of builder to create each time {@link #key()} is called.\n * @see #key()\n * @see KeyBuilder\n * @since 0.12.0\n */\npublic interface KeyBuilderSupplier<K extends Key, B extends KeyBuilder<K, B>> {\n\n    /**\n     * Returns a new {@link KeyBuilder} instance that will produce new secure-random keys with a length sufficient\n     * to be used by the component's associated cryptographic algorithm.\n     *\n     * @return a new {@link KeyBuilder} instance that will produce new secure-random keys with a length sufficient\n     * to be used by the component's associated cryptographic algorithm.\n     */\n    B key();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * General-purpose exception when encountering a problem with a cryptographic {@link java.security.Key}\n * or {@link Jwk}.\n *\n * @since 0.10.0\n */\npublic class KeyException extends SecurityException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public KeyException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param msg   the message explaining why the exception is thrown.\n     * @param cause the underlying cause that resulted in this exception being thrown.\n     */\n    public KeyException(String msg, Throwable cause) {\n        super(msg, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyLengthSupplier.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * Provides access to the required length in bits <em>(not bytes)</em> of keys usable with the associated algorithm.\n *\n * @since 0.12.0\n */\npublic interface KeyLengthSupplier {\n\n    /**\n     * Returns the required length in bits <em>(not bytes)</em> of keys usable with the associated algorithm.\n     *\n     * @return the required length in bits <em>(not bytes)</em> of keys usable with the associated algorithm.\n     */\n    int getKeyBitLength();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyOperation.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\n\n/**\n * A {@code KeyOperation} identifies a behavior for which a key may be used. Key validation\n * algorithms may inspect a key's operations and reject the key if it is being used in a manner inconsistent\n * with its indicated operations.\n *\n * <p><b>KeyOperation Identifier</b></p>\n *\n * <p>This interface extends {@link Identifiable}; the value returned from {@link #getId()} is a\n * CaSe-SeNsItIvE value that uniquely identifies the operation among other KeyOperation instances.</p>\n *\n * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">JWK key_ops (Key Operations) Parameter</a>\n * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-8.3\">JSON Web Key Operations Registry</a>\n * @since 0.12.0\n */\npublic interface KeyOperation extends Identifiable {\n\n    /**\n     * Returns a brief description of the key operation behavior.\n     *\n     * @return a brief description of the key operation behavior.\n     */\n    String getDescription();\n\n    /**\n     * Returns {@code true} if the specified {@code operation} is an acceptable use case for the key already assigned\n     * this operation, {@code false} otherwise. As described in the\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">JWK key_ops (Key Operations) Parameter</a>\n     * specification, Key validation algorithms will likely reject keys with inconsistent or unrelated operations\n     * because of the security vulnerabilities that could occur otherwise.\n     *\n     * @param operation the key operation to check if it is related to (consistent or compatible with) this operation.\n     * @return {@code true} if the specified {@code operation} is an acceptable use case for the key already assigned\n     * this operation, {@code false} otherwise.\n     */\n    boolean isRelated(KeyOperation operation);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyOperationBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.lang.Builder;\n\n/**\n * A {@code KeyOperationBuilder} produces {@link KeyOperation} instances that may be added to a JWK's\n * {@link JwkBuilder#operations() key operations} parameter. This is primarily only useful for creating\n * custom (non-standard) {@code KeyOperation}s for use with a custom {@link KeyOperationPolicy}, as all standard ones\n * are available already via the {@link Jwks.OP} registry singleton.\n *\n * @see Jwks.OP#builder()\n * @see Jwks.OP#policy()\n * @see JwkBuilder#operationPolicy(KeyOperationPolicy)\n * @since 0.12.0\n */\npublic interface KeyOperationBuilder extends Builder<KeyOperation> {\n\n    /**\n     * Sets the CaSe-SeNsItIvE {@link KeyOperation#getId() id} expected to be unique compared to all other\n     * {@code KeyOperation}s.\n     *\n     * @param id the key operation id\n     * @return the builder for method chaining\n     */\n    KeyOperationBuilder id(String id);\n\n    /**\n     * Sets the key operation {@link KeyOperation#getDescription() description}.\n     *\n     * @param description the key operation description\n     * @return the builder for method chaining\n     */\n    KeyOperationBuilder description(String description);\n\n    /**\n     * Indicates that the {@code KeyOperation} with the given {@link KeyOperation#getId() id} is cryptographically\n     * related (and complementary) to this one, and may be specified together in a JWK's\n     * {@link Jwk#getOperations() operations} set.\n     *\n     * <p>More concretely, calling this method will ensure the following:</p>\n     * <blockquote><pre>\n     *     KeyOperation built = Jwks.operation()&#47;*...*&#47;.related(otherId).build();\n     *     KeyOperation other = getKeyOperation(otherId);\n     *     assert built.isRelated(other);</pre></blockquote>\n     *\n     * <p>A {@link JwkBuilder}'s key operation {@link JwkBuilder#operationPolicy(KeyOperationPolicy) policy} is likely\n     * to {@link KeyOperationPolicyBuilder#unrelated() reject} any <em>un</em>related operations specified\n     * together due to the potential security vulnerabilities that could occur.</p>\n     *\n     * <p>This method may be called multiple times to add/append a related {@code id} to the constructed\n     * {@code KeyOperation}'s total set of related ids.</p>\n     *\n     * @param id the id of a KeyOperation that will be considered cryptographically related to this one.\n     * @return the builder for method chaining.\n     * @see JwkBuilder#operationPolicy(KeyOperationPolicy)\n     */\n    KeyOperationBuilder related(String id);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyOperationPolicied.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * A marker interface that indicates the implementing instance supports the ability to configure a\n * {@link KeyOperationPolicy} used to validate JWK instances.\n *\n * @param <T> the implementing instance for method chaining\n */\npublic interface KeyOperationPolicied<T extends KeyOperationPolicied<T>> {\n\n    /**\n     * Sets the key operation policy that determines which {@link KeyOperation}s may be assigned to a\n     * JWK. Unless overridden by this method, the default RFC-recommended policy is used where:\n     * <ul>\n     *     <li>All {@link Jwks.OP RFC-standard key operations} are supported.</li>\n     *     <li>Multiple unrelated operations may <b>not</b> be assigned to the JWK per the\n     *     <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.3\">RFC 7517, Section 4.3</a> recommendation:\n     * <blockquote><pre>\n     * Multiple unrelated key operations SHOULD NOT be specified for a key\n     * because of the potential vulnerabilities associated with using the\n     * same key with multiple algorithms.  Thus, the combinations \"{@link Jwks.OP#SIGN sign}\"\n     * with \"{@link Jwks.OP#VERIFY verify}\", \"{@link Jwks.OP#ENCRYPT encrypt}\" with \"{@link Jwks.OP#DECRYPT decrypt}\", and \"{@link Jwks.OP#WRAP_KEY wrapKey}\" with\n     * \"{@link Jwks.OP#UNWRAP_KEY unwrapKey}\" are permitted, but other combinations SHOULD NOT be used.</pre></blockquote>\n     * </li>\n     * </ul>\n     *\n     * <p>If you wish to enable a different policy, perhaps to support additional custom {@code KeyOperation} values,\n     * one can be created by using the {@link Jwks.OP#policy()} builder, or by implementing the\n     * {@link KeyOperationPolicy} interface directly.</p>\n     *\n     * @param policy the policy that determines which {@link KeyOperation}s may be assigned to a JWK.\n     * @return the builder for method chaining.\n     * @throws IllegalArgumentException if {@code policy} is null\n     */\n    T operationPolicy(KeyOperationPolicy policy) throws IllegalArgumentException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyOperationPolicy.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.util.Collection;\n\n/**\n * A key operation policy determines which {@link KeyOperation}s may be assigned to a JWK.\n *\n * @see JwkBuilder#operationPolicy(KeyOperationPolicy)\n * @since 0.12.0\n */\npublic interface KeyOperationPolicy {\n\n    /**\n     * Returns all supported {@code KeyOperation}s that may be assigned to a JWK.\n     *\n     * @return all supported {@code KeyOperation}s that may be assigned to a JWK.\n     */\n    Collection<KeyOperation> getOperations();\n\n    /**\n     * Returns quietly if all of the specified key operations are allowed to be assigned to a JWK,\n     * or throws an {@link IllegalArgumentException} otherwise.\n     *\n     * @param ops the operations to validate\n     */\n    @SuppressWarnings(\"GrazieInspection\")\n    void validate(Collection<? extends KeyOperation> ops) throws IllegalArgumentException;\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyOperationPolicyBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.lang.Builder;\nimport io.jsonwebtoken.lang.CollectionMutator;\n\nimport java.util.Collection;\n\n\n/**\n * A {@code KeyOperationPolicyBuilder} produces a {@link KeyOperationPolicy} that determines\n * which {@link KeyOperation}s may be assigned to a JWK. Custom {@code KeyOperation}s (such as those created by a\n * {@link Jwks.OP#builder()}) may be added to a policy via the {@link #add(KeyOperation)} or {@link #add(Collection)}\n * methods.\n *\n * @see Jwks.OP#policy()\n * @see JwkBuilder#operationPolicy(KeyOperationPolicy)\n * @see Jwks.OP#builder()\n * @since 0.12.0\n */\npublic interface KeyOperationPolicyBuilder extends CollectionMutator<KeyOperation, KeyOperationPolicyBuilder>,\n        Builder<KeyOperationPolicy> {\n\n    /**\n     * Allows a JWK to have unrelated {@link KeyOperation}s in its {@code key_ops} parameter values. <b>Be careful\n     * when calling this method - one should fully understand the security implications of using the same key\n     * with multiple algorithms in your application.</b>\n     * <p>If this method is not called, unrelated key operations are disabled by default per the recommendations in\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-4.3\">RFC 7517, Section 4.3</a>:</p>\n     * <blockquote><pre>\n     * Multiple unrelated key operations SHOULD NOT be specified for a key\n     * because of the potential vulnerabilities associated with using the\n     * same key with multiple algorithms.</pre></blockquote>\n     *\n     * @return the builder for method chaining\n     * @see <a href=\"https://datatracker.ietf.org/doc/html/rfc7517#section-4.3\">&quot;key_ops&quot; (Key Operations)\n     * Parameter</a>\n     */\n    KeyOperationPolicyBuilder unrelated();\n\n    /**\n     * Adds the specified key operation to the policy's total set of supported key operations\n     * used to validate a key's intended usage, replacing any existing one with an identical (CaSe-SeNsItIvE)\n     * {@link Identifiable#getId() id}.\n     *\n     * <p><b>Standard {@code KeyOperation}s and Overrides</b></p>\n     *\n     * <p>The RFC standard {@link Jwks.OP} key operations are supported by default and do not need\n     * to be added via this method, but beware: <b>If the {@code op} argument has a JWK standard\n     * {@link Identifiable#getId() id}, it will replace the JJWT standard operation implementation</b>.\n     * This is to allow application developers to favor their own implementations over JJWT's default implementations\n     * if necessary (for example, to support legacy or custom behavior).</p>\n     *\n     * <p>If a custom {@code KeyOperation} is desired, one may be easily created with a {@link Jwks.OP#builder()}.</p>\n     *\n     * @param op a key operation to add to the policy's total set of supported operations, replacing any\n     *           existing one with the same exact (CaSe-SeNsItIvE) {@link KeyOperation#getId() id}.\n     * @return the builder for method chaining.\n     * @see Jwks.OP\n     * @see Jwks.OP#builder()\n     * @see JwkBuilder#operationPolicy(KeyOperationPolicy)\n     * @see JwkBuilder#operations()\n     */\n    @Override\n    // for better JavaDoc\n    KeyOperationPolicyBuilder add(KeyOperation op);\n\n    /**\n     * Adds the specified key operations to the policy's total set of supported key operations\n     * used to validate a key's intended usage, replacing any existing ones with identical\n     * {@link Identifiable#getId() id}s.\n     *\n     * <p>There may be only one registered {@code KeyOperation} per CaSe-SeNsItIvE {@code id}, and the\n     * {@code ops} collection is added in iteration order; if a duplicate id is found when iterating the {@code ops}\n     * collection, the later operation will evict any existing operation with the same {@code id}.</p>\n     *\n     * <p><b>Standard {@code KeyOperation}s and Overrides</b></p>\n     *\n     * <p>The RFC standard {@link Jwks.OP} key operations are supported by default and do not need\n     * to be added via this method, but beware: <b>any operation in the {@code ops} argument with a\n     * JWK standard {@link Identifiable#getId() id} will replace the JJWT standard operation implementation</b>.\n     * This is to allow application developers to favor their own implementations over JJWT's default implementations\n     * if necessary (for example, to support legacy or custom behavior).</p>\n     *\n     * <p>If custom {@code KeyOperation}s are desired, they may be easily created with a {@link Jwks.OP#builder()}.</p>\n     *\n     * @param ops collection of key operations to add to the policy's total set of supported operations, replacing any\n     *            existing ones with the same exact (CaSe-SeNsItIvE) {@link KeyOperation#getId() id}s.\n     * @return the builder for method chaining.\n     * @see Jwks.OP\n     * @see Jwks.OP#builder()\n     * @see JwkBuilder#operationPolicy(KeyOperationPolicy)\n     * @see JwkBuilder#operations()\n     */\n    @Override\n    // for better JavaDoc\n    KeyOperationPolicyBuilder add(Collection<? extends KeyOperation> ops);\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyPair.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * Generics-capable and type-safe alternative to {@link java.security.KeyPair}.  Instances may be\n * converted to {@link java.security.KeyPair} if desired via {@link #toJavaKeyPair()}.\n *\n * @param <A> The type of {@link PublicKey} in the key pair.\n * @param <B> The type of {@link PrivateKey} in the key pair.\n * @since 0.12.0\n */\npublic interface KeyPair<A extends PublicKey, B extends PrivateKey> {\n\n    /**\n     * Returns the pair's public key.\n     *\n     * @return the pair's public key.\n     */\n    A getPublic();\n\n    /**\n     * Returns the pair's private key.\n     *\n     * @return the pair's private key.\n     */\n    B getPrivate();\n\n    /**\n     * Returns this instance as a {@link java.security.KeyPair} instance.\n     *\n     * @return this instance as a {@link java.security.KeyPair} instance.\n     */\n    java.security.KeyPair toJavaKeyPair();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyPairBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.KeyPair;\n\n/**\n * A {@code KeyPairBuilder} produces new {@link KeyPair}s suitable for use with an associated cryptographic algorithm.\n * A new {@link KeyPair} is created each time the builder's {@link #build()} method is called.\n *\n * <p>{@code KeyPairBuilder}s are provided by components that implement the {@link KeyPairBuilderSupplier} interface,\n * ensuring the resulting {@link KeyPair}s are compatible with their associated cryptographic algorithm.</p>\n *\n * @see KeyPairBuilderSupplier\n * @since 0.12.0\n */\npublic interface KeyPairBuilder extends SecurityBuilder<KeyPair, KeyPairBuilder> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyPairBuilderSupplier.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.KeyPair;\n\n/**\n * Interface implemented by components that support building/creating new {@link KeyPair}s suitable for use with their\n * associated cryptographic algorithm implementation.\n *\n * @see #keyPair()\n * @see KeyPairBuilder\n * @since 0.12.0\n */\npublic interface KeyPairBuilderSupplier {\n\n    /**\n     * Returns a new {@link KeyPairBuilder} that will create new secure-random {@link KeyPair}s with a length and\n     * parameters sufficient for use with the component's associated cryptographic algorithm.\n     *\n     * @return a new {@link KeyPairBuilder} that will create new secure-random {@link KeyPair}s with a length and\n     * parameters sufficient for use with the component's associated cryptographic algorithm.\n     */\n    KeyPairBuilder keyPair();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.JweHeader;\n\n/**\n * A request to a {@link KeyAlgorithm} to obtain the key necessary for AEAD encryption or decryption.  The exact\n * {@link AeadAlgorithm} that will be used is accessible via {@link #getEncryptionAlgorithm()}.\n *\n * <p><b>Encryption Requests</b></p>\n * <p>For an encryption key request, {@link #getPayload()} will return\n * the encryption key to use.  Additionally, any <em>public</em> information specific to the called\n * {@link KeyAlgorithm} implementation that is required to be transmitted in the JWE (such as an initialization vector,\n * authentication tag or ephemeral key, etc) may be added to the JWE protected header, accessible via\n * {@link #getHeader()}. Although the JWE header is checked for authenticity and integrity, it itself is\n * <em>not</em> encrypted, so {@link KeyAlgorithm}s should never place any secret or private information in the\n * header.</p>\n *\n * <p><b>Decryption Requests</b></p>\n * <p>For a decryption request, the {@code KeyRequest} instance will be\n * a {@link DecryptionKeyRequest} instance, {@link #getPayload()} will return the encrypted key ciphertext (a\n * byte array), and the decryption key will be available via {@link DecryptionKeyRequest#getKey()}.  Additionally,\n * any public information necessary by the called {@link KeyAlgorithm} (such as an initialization vector,\n * authentication tag, ephemeral key, etc) is expected to be available in the JWE protected header, accessible\n * via {@link #getHeader()}.</p>\n *\n * @param <T> the type of object relevant during key algorithm cryptographic operations.\n * @see DecryptionKeyRequest\n * @since 0.12.0\n */\npublic interface KeyRequest<T> extends Request<T> {\n\n    /**\n     * Returns the {@link AeadAlgorithm} that will be called for encryption or decryption after processing the\n     * {@code KeyRequest}.  {@link KeyAlgorithm} implementations that generate an ephemeral {@code SecretKey} to use\n     * as what the <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-2\">JWE specification calls</a> a\n     * &quot;Content Encryption Key (CEK)&quot; should call the {@code AeadAlgorithm}'s\n     * {@link AeadAlgorithm#key() key()} builder to create a key suitable for that exact {@code AeadAlgorithm}.\n     *\n     * @return the {@link AeadAlgorithm} that will be called for encryption or decryption after processing the\n     * {@code KeyRequest}.\n     */\n    AeadAlgorithm getEncryptionAlgorithm();\n\n    /**\n     * Returns the {@link JweHeader} that will be used to construct the final JWE header, available for\n     * reading or writing any {@link KeyAlgorithm}-specific information.\n     *\n     * <p>For an encryption key request, any <em>public</em> information specific to the called {@code KeyAlgorithm}\n     * implementation that is required to be transmitted in the JWE (such as an initialization vector,\n     * authentication tag or ephemeral key, etc) is expected to be added to this header. Although the header is\n     * checked for authenticity and integrity, it itself is <em>not</em> encrypted, so\n     * {@link KeyAlgorithm}s should never place any secret or private information in the header.</p>\n     *\n     * <p>For a decryption request, any public information necessary by the called {@link KeyAlgorithm}\n     * (such as an initialization vector, authentication tag, ephemeral key, etc) is expected to be available in\n     * this header.</p>\n     *\n     * @return the {@link JweHeader} that will be used to construct the final JWE header, available for\n     * reading or writing any {@link KeyAlgorithm}-specific information.\n     */\n    JweHeader getHeader();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeyResult.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\n\n/**\n * The result of a {@link KeyAlgorithm} encryption key request, containing the resulting\n * {@code JWE encrypted key} and {@code JWE Content Encryption Key (CEK)}, concepts defined in\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-2\">JWE Terminology</a>.\n *\n * <p>The result {@link #getPayload() payload} is the {@code JWE encrypted key}, which will be Base64URL-encoded\n * and embedded in the resulting compact JWE string.</p>\n *\n * <p>The result {@link #getKey() key} is the {@code JWE Content Encryption Key (CEK)} which will be used to encrypt\n * the JWE.</p>\n *\n * @since 0.12.0\n */\npublic interface KeyResult extends Message<byte[]>, KeySupplier<SecretKey> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/KeySupplier.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Key;\n\n/**\n * Provides access to a cryptographic {@link Key} necessary for signing, wrapping, encryption or decryption algorithms.\n *\n * @param <K> the type of key provided by this supplier.\n * @since 0.12.0\n */\npublic interface KeySupplier<K extends Key> {\n\n    /**\n     * Returns the key to use for signing, wrapping, encryption or decryption depending on the type of operation.\n     *\n     * @return the key to use for signing, wrapping, encryption or decryption depending on the type of operation.\n     */\n    K getKey();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/Keys.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Classes;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\n\n/**\n * Utility class for securely generating {@link SecretKey}s and {@link KeyPair}s.\n *\n * @since 0.10.0\n */\npublic final class Keys {\n\n    private static final String BRIDGE_CLASSNAME = \"io.jsonwebtoken.impl.security.KeysBridge\";\n    private static final Class<?> BRIDGE_CLASS = Classes.forName(BRIDGE_CLASSNAME);\n    private static final Class<?>[] FOR_PASSWORD_ARG_TYPES = new Class[]{char[].class};\n    private static final Class<?>[] SECRET_BUILDER_ARG_TYPES = new Class[]{SecretKey.class};\n    private static final Class<?>[] PRIVATE_BUILDER_ARG_TYPES = new Class[]{PrivateKey.class};\n\n    private static <T> T invokeStatic(String method, Class<?>[] argTypes, Object... args) {\n        return Classes.invokeStatic(BRIDGE_CLASS, method, argTypes, args);\n    }\n\n    //prevent instantiation\n    private Keys() {\n    }\n\n    /**\n     * Creates a new SecretKey instance for use with HMAC-SHA algorithms based on the specified key byte array.\n     *\n     * @param bytes the key byte array\n     * @return a new SecretKey instance for use with HMAC-SHA algorithms based on the specified key byte array.\n     * @throws WeakKeyException if the key byte array length is less than 256 bits (32 bytes) as mandated by the\n     *                          <a href=\"https://tools.ietf.org/html/rfc7518#section-3.2\">JWT JWA Specification\n     *                          (RFC 7518, Section 3.2)</a>\n     */\n    public static SecretKey hmacShaKeyFor(byte[] bytes) throws WeakKeyException {\n\n        if (bytes == null) {\n            throw new InvalidKeyException(\"SecretKey byte array cannot be null.\");\n        }\n\n        int bitLength = bytes.length * 8;\n\n        //Purposefully ordered higher to lower to ensure the strongest key possible can be generated.\n        if (bitLength >= 512) {\n            return new SecretKeySpec(bytes, \"HmacSHA512\");\n        } else if (bitLength >= 384) {\n            return new SecretKeySpec(bytes, \"HmacSHA384\");\n        } else if (bitLength >= 256) {\n            return new SecretKeySpec(bytes, \"HmacSHA256\");\n        }\n\n        String msg = \"The specified key byte array is \" + bitLength + \" bits which \" +\n                \"is not secure enough for any JWT HMAC-SHA algorithm.  The JWT \" +\n                \"JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a \" +\n                \"size >= 256 bits (the key size must be greater than or equal to the hash \" +\n                \"output size).  Consider using the Jwts.SIG.HS256.key() builder (or HS384.key() \" +\n                \"or HS512.key()) to create a key guaranteed to be secure enough for your preferred HMAC-SHA \" +\n                \"algorithm.  See https://tools.ietf.org/html/rfc7518#section-3.2 for more information.\";\n        throw new WeakKeyException(msg);\n    }\n\n    /**\n     * <p><b>Deprecation Notice</b></p>\n     *\n     * <p>As of JJWT 0.12.0, symmetric (secret) key algorithm instances can generate a key of suitable\n     * length for that specific algorithm by calling their {@code key()} builder method directly. For example:</p>\n     *\n     * <pre><code>\n     * {@link Jwts.SIG#HS256}.key().build();\n     * {@link Jwts.SIG#HS384}.key().build();\n     * {@link Jwts.SIG#HS512}.key().build();\n     * </code></pre>\n     *\n     * <p>Call those methods as needed instead of this static {@code secretKeyFor} helper method - the returned\n     * {@link KeyBuilder} allows callers to specify a preferred Provider or SecureRandom on the builder if\n     * desired, whereas this {@code secretKeyFor} method does not. Consequently this helper method will be removed\n     * before the 1.0 release.</p>\n     *\n     * <p><b>Previous Documentation</b></p>\n     *\n     * <p>Returns a new {@link SecretKey} with a key length suitable for use with the specified {@link SignatureAlgorithm}.</p>\n     *\n     * <p><a href=\"https://tools.ietf.org/html/rfc7518#section-3.2\">JWA Specification (RFC 7518), Section 3.2</a>\n     * requires minimum key lengths to be used for each respective Signature Algorithm.  This method returns a\n     * secure-random generated SecretKey that adheres to the required minimum key length.  The lengths are:</p>\n     *\n     * <table>\n     *     <caption>JWA HMAC-SHA Key Length Requirements</caption>\n     * <tr>\n     * <th>Algorithm</th>\n     * <th>Key Length</th>\n     * </tr>\n     * <tr>\n     * <td>HS256</td>\n     * <td>256 bits (32 bytes)</td>\n     * </tr>\n     * <tr>\n     * <td>HS384</td>\n     * <td>384 bits (48 bytes)</td>\n     * </tr>\n     * <tr>\n     * <td>HS512</td>\n     * <td>512 bits (64 bytes)</td>\n     * </tr>\n     * </table>\n     *\n     * @param alg the {@code SignatureAlgorithm} to inspect to determine which key length to use.\n     * @return a new {@link SecretKey} instance suitable for use with the specified {@link SignatureAlgorithm}.\n     * @throws IllegalArgumentException for any input value other than {@link io.jsonwebtoken.SignatureAlgorithm#HS256},\n     *                                  {@link io.jsonwebtoken.SignatureAlgorithm#HS384}, or {@link io.jsonwebtoken.SignatureAlgorithm#HS512}\n     * @deprecated since 0.12.0.  Use your preferred {@link MacAlgorithm} instance's\n     * {@link MacAlgorithm#key() key()} builder method directly.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    public static SecretKey secretKeyFor(io.jsonwebtoken.SignatureAlgorithm alg) throws IllegalArgumentException {\n        Assert.notNull(alg, \"SignatureAlgorithm cannot be null.\");\n        SecureDigestAlgorithm<?, ?> salg = Jwts.SIG.get().get(alg.name());\n        if (!(salg instanceof MacAlgorithm)) {\n            String msg = \"The \" + alg.name() + \" algorithm does not support shared secret keys.\";\n            throw new IllegalArgumentException(msg);\n        }\n        return ((MacAlgorithm) salg).key().build();\n    }\n\n    /**\n     * <p><b>Deprecation Notice</b></p>\n     *\n     * <p>As of JJWT 0.12.0, asymmetric key algorithm instances can generate KeyPairs of suitable strength\n     * for that specific algorithm by calling their {@code keyPair()} builder method directly. For example:</p>\n     *\n     * <blockquote><pre>\n     * Jwts.SIG.{@link Jwts.SIG#RS256 RS256}.keyPair().build();\n     * Jwts.SIG.{@link Jwts.SIG#RS384 RS384}.keyPair().build();\n     * Jwts.SIG.{@link Jwts.SIG#RS512 RS512}.keyPair().build();\n     * ... etc ...\n     * Jwts.SIG.{@link Jwts.SIG#ES512 ES512}.keyPair().build();</pre></blockquote>\n     *\n     * <p>Call those methods as needed instead of this static {@code keyPairFor} helper method - the returned\n     * {@link KeyPairBuilder} allows callers to specify a preferred Provider or SecureRandom on the builder if\n     * desired, whereas this {@code keyPairFor} method does not. Consequently this helper method will be removed\n     * before the 1.0 release.</p>\n     *\n     * <p><b>Previous Documentation</b></p>\n     *\n     * <p>Returns a new {@link KeyPair} suitable for use with the specified asymmetric algorithm.</p>\n     *\n     * <p>If the {@code alg} argument is an RSA algorithm, a KeyPair is generated based on the following:</p>\n     *\n     * <table>\n     *     <caption>Generated RSA Key Sizes</caption>\n     * <tr>\n     * <th>JWA Algorithm</th>\n     * <th>Key Size</th>\n     * </tr>\n     * <tr>\n     * <td>RS256</td>\n     * <td>2048 bits</td>\n     * </tr>\n     * <tr>\n     * <td>PS256</td>\n     * <td>2048 bits</td>\n     * </tr>\n     * <tr>\n     * <td>RS384</td>\n     * <td>3072 bits</td>\n     * </tr>\n     * <tr>\n     * <td>PS384</td>\n     * <td>3072 bits</td>\n     * </tr>\n     * <tr>\n     * <td>RS512</td>\n     * <td>4096 bits</td>\n     * </tr>\n     * <tr>\n     * <td>PS512</td>\n     * <td>4096 bits</td>\n     * </tr>\n     * </table>\n     *\n     * <p>If the {@code alg} argument is an Elliptic Curve algorithm, a KeyPair is generated based on the following:</p>\n     *\n     * <table>\n     *     <caption>Generated Elliptic Curve Key Parameters</caption>\n     * <tr>\n     * <th>JWA Algorithm</th>\n     * <th>Key Size</th>\n     * <th><a href=\"https://tools.ietf.org/html/rfc7518#section-7.6.2\">JWA Curve Name</a></th>\n     * <th><a href=\"https://tools.ietf.org/html/rfc5480#section-2.1.1.1\">ASN1 OID Curve Name</a></th>\n     * </tr>\n     * <tr>\n     * <td>ES256</td>\n     * <td>256 bits</td>\n     * <td>{@code P-256}</td>\n     * <td>{@code secp256r1}</td>\n     * </tr>\n     * <tr>\n     * <td>ES384</td>\n     * <td>384 bits</td>\n     * <td>{@code P-384}</td>\n     * <td>{@code secp384r1}</td>\n     * </tr>\n     * <tr>\n     * <td>ES512</td>\n     * <td><b>521</b> bits</td>\n     * <td>{@code P-521}</td>\n     * <td>{@code secp521r1}</td>\n     * </tr>\n     * </table>\n     *\n     * @param alg the {@code SignatureAlgorithm} to inspect to determine which asymmetric algorithm to use.\n     * @return a new {@link KeyPair} suitable for use with the specified asymmetric algorithm.\n     * @throws IllegalArgumentException if {@code alg} is not an asymmetric algorithm\n     * @deprecated since 0.12.0 in favor of your preferred\n     * {@link io.jsonwebtoken.security.SignatureAlgorithm} instance's\n     * {@link SignatureAlgorithm#keyPair() keyPair()} builder method directly.\n     */\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated\n    public static KeyPair keyPairFor(io.jsonwebtoken.SignatureAlgorithm alg) throws IllegalArgumentException {\n        Assert.notNull(alg, \"SignatureAlgorithm cannot be null.\");\n        SecureDigestAlgorithm<?, ?> salg = Jwts.SIG.get().get(alg.name());\n        if (!(salg instanceof SignatureAlgorithm)) {\n            String msg = \"The \" + alg.name() + \" algorithm does not support Key Pairs.\";\n            throw new IllegalArgumentException(msg);\n        }\n        SignatureAlgorithm asalg = ((SignatureAlgorithm) salg);\n        return asalg.keyPair().build();\n    }\n\n    /**\n     * Returns a new {@link Password} instance suitable for use with password-based key derivation algorithms.\n     *\n     * <p><b>Usage Note</b>: Using {@code Password}s outside of key derivation contexts will likely\n     * fail. See the {@link Password} JavaDoc for more, and also note the <b>Password Safety</b> section below.</p>\n     *\n     * <p><b>Password Safety</b></p>\n     *\n     * <p>Instances returned by this method use a <em>clone</em> of the specified {@code password} character array\n     * argument - changes to the argument array will NOT be reflected in the returned key, and vice versa.  If you wish\n     * to clear a {@code Password} instance to ensure it is no longer usable, call its {@link Password#destroy()}\n     * method will clear/overwrite its internal cloned char array. Also note that each subsequent call to\n     * {@link Password#toCharArray()} will also return a new clone of the underlying password character array per\n     * standard JCE key behavior.</p>\n     *\n     * @param password the raw password character array to clone for use with password-based key derivation algorithms.\n     * @return a new {@link Password} instance that wraps a new clone of the specified {@code password} character array.\n     * @see Password#toCharArray()\n     * @since 0.12.0\n     */\n    public static Password password(char[] password) {\n        return invokeStatic(\"password\", FOR_PASSWORD_ARG_TYPES, new Object[]{password});\n    }\n\n    /**\n     * Returns a {@code SecretKeyBuilder} that produces the specified key, allowing association with a\n     * {@link SecretKeyBuilder#provider(Provider) provider} that must be used with the key during cryptographic\n     * operations.  For example:\n     *\n     * <blockquote><pre>\n     * SecretKey key = Keys.builder(key).provider(mandatoryProvider).build();</pre></blockquote>\n     *\n     * <p>Cryptographic algorithm implementations can inspect the resulting {@code key} instance and obtain its\n     * mandatory {@code Provider} if necessary.</p>\n     *\n     * <p>This method is primarily only useful for keys that cannot expose key material, such as PKCS11 or HSM\n     * (Hardware Security Module) keys, and require a specific {@code Provider} to be used during cryptographic\n     * operations.</p>\n     *\n     * @param key the secret key to use for cryptographic operations, potentially associated with a configured\n     *            {@link Provider}\n     * @return a new {@code SecretKeyBuilder} that produces the specified key, potentially associated with any\n     * specified provider.\n     * @since 0.12.0\n     */\n    public static SecretKeyBuilder builder(SecretKey key) {\n        Assert.notNull(key, \"SecretKey cannot be null.\");\n        return invokeStatic(\"builder\", SECRET_BUILDER_ARG_TYPES, key);\n    }\n\n    /**\n     * Returns a {@code PrivateKeyBuilder} that produces the specified key, allowing association with a\n     * {@link PrivateKeyBuilder#publicKey(PublicKey) publicKey} to obtain public key data if necessary, or a\n     * {@link SecretKeyBuilder#provider(Provider) provider} that must be used with the key during cryptographic\n     * operations.  For example:\n     *\n     * <blockquote><pre>\n     * PrivateKey key = Keys.builder(privateKey).publicKey(publicKey).provider(mandatoryProvider).build();</pre></blockquote>\n     *\n     * <p>Cryptographic algorithm implementations can inspect the resulting {@code key} instance and obtain its\n     * mandatory {@code Provider} or {@code PublicKey} if necessary.</p>\n     *\n     * <p>This method is primarily only useful for keys that cannot expose key material, such as PKCS11 or HSM\n     * (Hardware Security Module) keys, and require a specific {@code Provider} or public key data to be used\n     * during cryptographic operations.</p>\n     *\n     * @param key the private key to use for cryptographic operations, potentially associated with a configured\n     *            {@link Provider} or {@link PublicKey}.\n     * @return a new {@code PrivateKeyBuilder} that produces the specified private key, potentially associated with any\n     * specified provider or {@code PublicKey}\n     * @since 0.12.0\n     */\n    public static PrivateKeyBuilder builder(PrivateKey key) {\n        Assert.notNull(key, \"PrivateKey cannot be null.\");\n        return invokeStatic(\"builder\", PRIVATE_BUILDER_ARG_TYPES, key);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/MacAlgorithm.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\n\nimport javax.crypto.SecretKey;\n\n/**\n * A {@link SecureDigestAlgorithm} that uses symmetric {@link SecretKey}s to both compute and verify digests as\n * <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\">message authentication codes</a> (MACs).\n *\n * <p><b>Standard Identifier</b></p>\n *\n * <p>{@code MacAlgorithm} extends {@link Identifiable}: when a {@code MacAlgorithm} is used to compute the MAC of a\n * JWS, the value returned from {@link Identifiable#getId() macAlgorithm.getId()} will be set as the JWS\n * <code>&quot;alg&quot;</code> protected header value.</p>\n *\n * <p><b>Key Strength</b></p>\n *\n * <p>MAC algorithm strength is in part attributed to how difficult it is to discover the secret key.\n * As such, MAC algorithms usually require keys of a minimum length to ensure the keys are difficult to discover\n * and the algorithm's security properties are maintained.</p>\n *\n * <p>The {@code MacAlgorithm} interface extends the {@link KeyLengthSupplier} interface to represent\n * the length in bits (<em>not bytes</em>) a key must have to be used with its implementation.  If you do not want to\n * worry about lengths and parameters of keys required for an algorithm, it is often easier to automatically generate\n * a key that adheres to the algorithms requirements, as discussed below.</p>\n *\n * <p><b>Key Generation</b></p>\n *\n * <p>{@code MacAlgorithm} extends {@link KeyBuilderSupplier} to enable {@link SecretKey} generation.\n * Each {@code MacAlgorithm} algorithm instance will return a {@link KeyBuilder} that ensures any created keys will\n * have a sufficient length and any algorithm parameters required by that algorithm. For example:</p>\n *\n * <blockquote><pre>\n * SecretKey key = macAlgorithm.key().build();</pre></blockquote>\n *\n * <p>The resulting {@code key} is guaranteed to have the correct algorithm parameters and strength/length necessary for\n * that exact {@code MacAlgorithm} instance.</p>\n *\n * <p><b>JWA Standard Implementations</b></p>\n *\n * <p>Constant definitions and utility methods for all JWA (RFC 7518) standard MAC algorithms are\n * available via {@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}.</p>\n *\n * @see io.jsonwebtoken.Jwts.SIG Jwts.SIG\n * @since 0.12.0\n */\npublic interface MacAlgorithm extends SecureDigestAlgorithm<SecretKey, SecretKey>,\n        KeyBuilderSupplier<SecretKey, SecretKeyBuilder>, KeyLengthSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/MalformedKeyException.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * Exception thrown when encountering a key or key material that is incomplete or improperly configured or\n * formatted and cannot be used as expected.\n *\n * @since 0.12.0\n */\npublic class MalformedKeyException extends InvalidKeyException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public MalformedKeyException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param msg   the message explaining why the exception is thrown.\n     * @param cause the underlying cause that resulted in this exception being thrown.\n     */\n    public MalformedKeyException(String msg, Throwable cause) {\n        super(msg, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/MalformedKeySetException.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * Exception thrown when encountering a {@link JwkSet} that is incomplete or improperly configured or\n * formatted and cannot be used as expected.\n *\n * @since 0.12.0\n */\npublic class MalformedKeySetException extends SecurityException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public MalformedKeySetException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public MalformedKeySetException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/Message.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Key;\n\n/**\n * A message contains a {@link #getPayload() payload} used as input to or output from a cryptographic algorithm.\n *\n * @param <T> The type of payload in the message.\n * @since 0.12.0\n */\npublic interface Message<T> {\n\n    /**\n     * Returns the message payload used as input to or output from a cryptographic algorithm. This is almost always\n     * plaintext used for cryptographic signatures or encryption, or ciphertext for decryption, or a {@link Key}\n     * instance for wrapping or unwrapping algorithms.\n     *\n     * @return the message payload used as input to or output from a cryptographic algorithm.\n     */\n    T getPayload(); //plaintext, ciphertext or Key\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/OctetPrivateJwk.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.interfaces.ECPrivateKey;\n\n/**\n * JWK representation of an <a href=\"https://en.wikipedia.org/wiki/Edwards_curve\">Edwards Curve</a>\n * {@link PrivateKey} as defined by RFC 8037, Section 2:\n * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-2\">Key Type &quot;OKP&quot;</a>.\n *\n * <p>Unlike the {@link EcPrivateJwk} interface, which only supports\n * <a href=\"https://en.wikipedia.org/wiki/Elliptic_curve\">Weierstrass</a>-form {@link ECPrivateKey}s,\n * {@code OctetPrivateJwk} allows for multiple parameterized {@link PrivateKey} types\n * because the JDK supports two different types of Edwards Curve private keys:</p>\n * <ul>\n *     <li><a href=\"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/interfaces/XECPrivateKey.html\">java.security.interfaces.XECPrivateKey</a>, introduced in JDK 11, and</li>\n *     <li><a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPrivateKey.html\">java.security.interfaces.EdECPrivateKey</a>, introduced in JDK 15.</li>\n * </ul>\n * <p>As such, {@code OctetPrivateJwk} is parameterized to support both key types.</p>\n *\n * <p><b>Earlier JDK Versions</b></p>\n *\n * <p>Even though {@code XECPrivateKey} and {@code EdECPrivateKey} were introduced in JDK 11 and JDK 15 respectively,\n * JJWT supports Octet private JWKs in earlier versions when BouncyCastle is enabled in the application classpath.  When\n * using earlier JDK versions, the {@code OctetPrivateJwk} instance will need be parameterized with the\n * generic {@code PrivateKey} type since the latter key types would not be present.  For example:</p>\n * <blockquote><pre>\n * OctetPrivateJwk&lt;PrivateKey&gt; octetPrivateJwk = getKey();</pre></blockquote>\n *\n * <p><b>OKP-specific Properties</b></p>\n *\n * <p>Note that the various OKP-specific properties are not available as separate dedicated getter methods, as most Java\n * applications should rarely, if ever, need to access these individual key properties since they typically represent\n * internal key material and/or serialization details. If you need to access these key properties, it is usually\n * recommended to obtain the corresponding {@link PrivateKey} instance returned by {@link #toKey()} and\n * query that instead.</p>\n *\n * <p>Even so, because these properties exist and are readable by nature of every JWK being a\n * {@link java.util.Map Map}, they are still accessible via the standard {@code Map} {@link #get(Object) get} method\n * using an appropriate JWK parameter id, for example:</p>\n * <blockquote><pre>\n * jwk.get(&quot;x&quot;);\n * jwk.get(&quot;d&quot;);\n * // ... etc ...</pre></blockquote>\n *\n * @param <K> The type of Edwards-curve {@link PrivateKey} represented by this JWK (e.g. XECPrivateKey, EdECPrivateKey, etc).\n * @param <L> The type of Edwards-curve {@link PublicKey} represented by the JWK's corresponding\n *            {@link #toPublicJwk() public JWK}, for example XECPublicKey, EdECPublicKey, etc.\n * @since 0.12.0\n */\npublic interface OctetPrivateJwk<K extends PrivateKey, L extends PublicKey> extends PrivateJwk<K, L, OctetPublicJwk<L>> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/OctetPrivateJwkBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * A {@link PrivateJwkBuilder} that creates {@link OctetPrivateJwk} instances.\n *\n * @param <K> The type of {@link PrivateKey} represented by the constructed {@link OctetPrivateJwk} instance.\n * @param <L> The type of {@link PublicKey} available from the constructed {@link OctetPrivateJwk}'s associated {@link PrivateJwk#toPublicJwk() public JWK} properties.\n * @since 0.12.0\n */\npublic interface OctetPrivateJwkBuilder<K extends PrivateKey, L extends PublicKey> extends\n        PrivateJwkBuilder<K, L, OctetPublicJwk<L>, OctetPrivateJwk<K, L>, OctetPrivateJwkBuilder<K, L>> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/OctetPublicJwk.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PublicKey;\nimport java.security.interfaces.ECPublicKey;\n\n/**\n * JWK representation of an <a href=\"https://en.wikipedia.org/wiki/Edwards_curve\">Edwards Curve</a>\n * {@link PublicKey} as defined by RFC 8037, Section 2:\n * <a href=\"https://www.rfc-editor.org/rfc/rfc8037#section-2\">Key Type &quot;OKP&quot;</a>.\n *\n * <p>Unlike the {@link EcPublicJwk} interface, which only supports\n * <a href=\"https://en.wikipedia.org/wiki/Elliptic_curve\">Weierstrass</a>-form {@link ECPublicKey}s,\n * {@code OctetPublicJwk} allows for multiple parameterized {@link PublicKey} types\n * because the JDK supports two different types of Edwards Curve public keys:</p>\n * <ul>\n *     <li><a href=\"https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/security/interfaces/XECPublicKey.html\">java.security.interfaces.XECPublicKey</a>, introduced in JDK 11, and</li>\n *     <li><a href=\"https://docs.oracle.com/en/java/javase/15/docs/api/java.base/java/security/interfaces/EdECPublicKey.html\">java.security.interfaces.EdECPublicKey</a>, introduced in JDK 15.</li>\n * </ul>\n * <p>As such, {@code OctetPublicJwk} is parameterized to support both key types.</p>\n *\n * <p><b>Earlier JDK Versions</b></p>\n *\n * <p>Even though {@code XECPublicKey} and {@code EdECPublicKey} were introduced in JDK 11 and JDK 15 respectively,\n * JJWT supports Octet public JWKs in earlier versions when BouncyCastle is enabled in the application classpath.  When\n * using earlier JDK versions, the {@code OctetPublicJwk} instance will need be parameterized with the\n * generic {@code PublicKey} type since the latter key types would not be present.  For example:</p>\n * <pre><code>OctetPublicJwk&lt;PublicKey&gt; octetPublicJwk = getKey();</code></pre>\n *\n * <p><b>OKP-specific Properties</b></p>\n *\n * <p>Note that the various OKP-specific properties are not available as separate dedicated getter methods, as most Java\n * applications should rarely, if ever, need to access these individual key properties since they typically represent\n * internal key material and/or serialization details. If you need to access these key properties, it is usually\n * recommended to obtain the corresponding {@link PublicKey} instance returned by {@link #toKey()} and\n * query that instead.</p>\n *\n * <p>Even so, because these properties exist and are readable by nature of every JWK being a\n * {@link java.util.Map Map}, they are still accessible via the standard {@code Map} {@link #get(Object) get} method\n * using an appropriate JWK parameter id, for example:</p>\n * <blockquote><pre>\n * jwk.get(&quot;x&quot;);\n * // ... etc ...</pre></blockquote>\n *\n * @param <K> The type of Edwards-curve {@link PublicKey} represented by this JWK (e.g. XECPublicKey, EdECPublicKey, etc).\n * @since 0.12.0\n */\npublic interface OctetPublicJwk<K extends PublicKey> extends PublicJwk<K> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/OctetPublicJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * A {@link PublicJwkBuilder} that creates {@link OctetPublicJwk} instances.\n *\n * @param <A> the type of {@link PublicKey} provided by the created {@link OctetPublicJwk} (e.g. XECPublicKey, EdECPublicKey, etc).\n * @param <B> the type of {@link PrivateKey} that may be paired with the {@link PublicKey} to produce an\n *            {@link OctetPrivateJwk} if desired. For example, XECPrivateKey, EdECPrivateKey, etc.\n * @since 0.12.0\n */\npublic interface OctetPublicJwkBuilder<A extends PublicKey, B extends PrivateKey>\n        extends PublicJwkBuilder<A, B, OctetPublicJwk<A>, OctetPrivateJwk<B, A>, OctetPrivateJwkBuilder<B, A>, OctetPublicJwkBuilder<A, B>> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/Password.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\nimport javax.security.auth.Destroyable;\n\n/**\n * A {@code Key} suitable for use with password-based key derivation algorithms.\n *\n * <p><b>Usage Warning</b></p>\n *\n * <p>Because raw passwords should never be used as direct inputs for cryptographic operations (such as authenticated\n * hashing or encryption) - and only for derivation algorithms (like password-based encryption) - {@code Password}\n * instances will throw an exception when used in these invalid contexts.  Specifically, calling a\n * {@code Password}'s {@link Password#getEncoded() getEncoded()} method (as would be done automatically by the\n * JCA subsystem during direct cryptographic operations) will throw an\n * {@link UnsupportedOperationException UnsupportedOperationException}.</p>\n *\n * @see #toCharArray()\n * @since 0.12.0\n */\npublic interface Password extends SecretKey, Destroyable {\n\n    /**\n     * Returns a new clone of the underlying password character array for use during derivation algorithms.  Like all\n     * {@code SecretKey} implementations, if you wish to clear the backing password character array for\n     * safety/security reasons, call the {@link #destroy()} method, ensuring that both the character array is cleared\n     * and the {@code Password} instance can no longer be used.\n     *\n     * <p><b>Usage</b></p>\n     *\n     * <p>Because a new clone is returned from this method each time it is invoked, it is expected that callers will\n     * clear the resulting clone from memory as soon as possible to reduce probability of password exposure.  For\n     * example:</p>\n     *\n     * <pre><code>\n     * char[] clonedPassword = aPassword.toCharArray();\n     * try {\n     *     doSomethingWithPassword(clonedPassword);\n     * } finally {\n     *     // guarantee clone is cleared regardless of any Exception thrown:\n     *     java.util.Arrays.fill(clonedPassword, '\\u0000');\n     * }\n     * </code></pre>\n     *\n     * @return a clone of the underlying password character array.\n     */\n    char[] toCharArray();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/PrivateJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * JWK representation of a {@link PrivateKey}.\n *\n * <p><b>JWK Private Key vs Java {@code PrivateKey} differences</b></p>\n *\n * <p>Unlike the Java cryptography APIs, the JWK specification requires all public key <em>and</em> private key\n * properties to be contained within every private JWK. As such, a {@code PrivateJwk} indeed represents\n * private key values as its name implies, but it is probably more similar to the Java JCA concept of a\n * {@link java.security.KeyPair} since it contains everything for both keys.</p>\n *\n * <p>Consequently a {@code PrivateJwk} is capable of providing two additional convenience methods:</p>\n * <ul>\n *     <li>{@link #toPublicJwk()} - a method to obtain a {@link PublicJwk} instance that contains only the JWK public\n *     key properties, and</li>\n *     <li>{@link #toKeyPair()} - a method to obtain both Java {@link PublicKey} and {@link PrivateKey}s in aggregate\n *     as a {@link KeyPair} instance if desired.</li>\n * </ul>\n *\n * @param <K> The type of {@link PrivateKey} represented by this JWK\n * @param <L> The type of {@link PublicKey} represented by the JWK's corresponding {@link #toPublicJwk() public JWK}.\n * @param <M> The type of {@link PublicJwk} reflected by the JWK's public properties.\n * @since 0.12.0\n */\npublic interface PrivateJwk<K extends PrivateKey, L extends PublicKey, M extends PublicJwk<L>> extends AsymmetricJwk<K> {\n\n    /**\n     * Returns the private JWK's corresponding {@link PublicJwk}, containing only the key's public properties.\n     *\n     * @return the private JWK's corresponding {@link PublicJwk}, containing only the key's public properties.\n     */\n    M toPublicJwk();\n\n    /**\n     * Returns the key's corresponding Java {@link PrivateKey} and {@link PublicKey} in aggregate as a\n     * type-safe {@link KeyPair} instance.\n     *\n     * @return the key's corresponding Java {@link PrivateKey} and {@link PublicKey} in aggregate as a\n     * type-safe {@link KeyPair} instance.\n     */\n    KeyPair<L, K> toKeyPair();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/PrivateJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * An {@link AsymmetricJwkBuilder} that creates {@link PrivateJwk} instances.\n *\n * @param <K> the type of Java {@link PrivateKey} provided by the created private JWK.\n * @param <L> the type of Java {@link PublicKey} paired with the private key.\n * @param <M> the type of {@link PrivateJwk} created\n * @param <J> the type of {@link PublicJwk} paired with the created private JWK.\n * @param <T> the type of the builder, for subtype method chaining\n * @see #publicKey(PublicKey)\n * @since 0.12.0\n */\npublic interface PrivateJwkBuilder<K extends PrivateKey, L extends PublicKey,\n        J extends PublicJwk<L>, M extends PrivateJwk<K, L, J>,\n        T extends PrivateJwkBuilder<K, L, J, M, T>> extends AsymmetricJwkBuilder<K, M, T> {\n\n    /**\n     * Allows specifying of the {@link PublicKey} associated with the builder's existing {@link PrivateKey},\n     * offering a reasonable performance enhancement when building the final private JWK.  Application developers\n     * should prefer to use this method when possible when building private JWKs.\n     *\n     * <p>As discussed in the {@link PrivateJwk} documentation, the JWK and JWA specifications require private JWKs to\n     * contain <em>both</em> private key <em>and</em> public key data.  If a public key is not provided via this\n     * {@code publicKey} method, the builder implementation must go through the work to derive the\n     * {@code PublicKey} instance based on the {@code PrivateKey} to obtain the necessary public key information.</p>\n     *\n     * <p>Calling this method with the {@code PrivateKey}'s matching {@code PublicKey} instance eliminates the need\n     * for the builder to do that work.</p>\n     *\n     * @param publicKey the {@link PublicKey} that matches the builder's existing {@link PrivateKey}.\n     * @return the builder for method chaining.\n     */\n    T publicKey(L publicKey);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/PrivateKeyBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\n\n/**\n * A builder that allows a {@code PrivateKey} to be transparently associated with a {@link #provider(Provider)} or\n * {@link #publicKey(PublicKey)} if necessary for algorithms that require them.\n *\n * @since 0.12.0\n */\npublic interface PrivateKeyBuilder extends KeyBuilder<PrivateKey, PrivateKeyBuilder> {\n\n    /**\n     * Sets the private key's corresponding {@code PublicKey} so that its public key material will be available to\n     * algorithms that require it.\n     *\n     * @param publicKey the private key's corresponding {@code PublicKey}\n     * @return the builder for method chaining.\n     */\n    PrivateKeyBuilder publicKey(PublicKey publicKey);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/PublicJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PublicKey;\n\n/**\n * JWK representation of a {@link PublicKey}.\n *\n * @param <K> The type of {@link PublicKey} represented by this JWK\n * @since 0.12.0\n */\npublic interface PublicJwk<K extends PublicKey> extends AsymmetricJwk<K> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/PublicJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * An {@link AsymmetricJwkBuilder} that creates {@link PublicJwk} instances.\n *\n * @param <K> the type of {@link PublicKey} provided by the created public JWK.\n * @param <L> the type of {@link PrivateKey} that may be paired with the {@link PublicKey} to produce a {@link PrivateJwk} if desired.\n * @param <J> the type of {@link PublicJwk} created\n * @param <M> the type of {@link PrivateJwk} that matches the created {@link PublicJwk}\n * @param <P> the type of {@link PrivateJwkBuilder} that matches this builder if a {@link PrivateJwk} is desired.\n * @param <T> the type of the builder, for subtype method chaining\n * @see #privateKey(PrivateKey)\n * @since 0.12.0\n */\npublic interface PublicJwkBuilder<K extends PublicKey, L extends PrivateKey,\n        J extends PublicJwk<K>, M extends PrivateJwk<L, K, J>,\n        P extends PrivateJwkBuilder<L, K, J, M, P>,\n        T extends PublicJwkBuilder<K, L, J, M, P, T>> extends AsymmetricJwkBuilder<K, J, T> {\n\n    /**\n     * Sets the {@link PrivateKey} that pairs with the builder's existing {@link PublicKey}, converting this builder\n     * into a {@link PrivateJwkBuilder} which will produce a corresponding {@link PrivateJwk} instance.  The\n     * specified {@code privateKey} <em>MUST</em> be the exact private key paired with the builder's public key.\n     *\n     * @param privateKey the {@link PrivateKey} that pairs with the builder's existing {@link PublicKey}\n     * @return the builder coerced as a {@link PrivateJwkBuilder} which will produce a corresponding {@link PrivateJwk}.\n     */\n    P privateKey(L privateKey);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/Request.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\n/**\n * A {@code Request} aggregates various parameters that may be used by a particular cryptographic algorithm. It and\n * any of its subtypes implemented as a single object submitted to an algorithm effectively reflect the\n * <a href=\"https://java-design-patterns.com/patterns/parameter-object/\">Parameter Object</a> design pattern.  This\n * provides for a much cleaner request/result algorithm API instead of polluting the API with an excessive number of\n * overloaded methods that would exist otherwise.\n *\n * <p>The {@code Request} interface specifically allows for JCA {@link Provider} and {@link SecureRandom} instances\n * to be used during request execution, which allows more flexibility than forcing a single {@code Provider} or\n * {@code SecureRandom} for all executions. {@code Request} subtypes provide additional parameters as necessary\n * depending on the type of cryptographic algorithm invoked.</p>\n *\n * @param <T> the type of payload in the request.\n * @see #getProvider()\n * @see #getSecureRandom()\n * @since 0.12.0\n */\npublic interface Request<T> extends Message<T> {\n\n    /**\n     * Returns the JCA provider that should be used for cryptographic operations during the request or\n     * {@code null} if the JCA subsystem preferred provider should be used.\n     *\n     * @return the JCA provider that should be used for cryptographic operations during the request or\n     * {@code null} if the JCA subsystem preferred provider should be used.\n     */\n    Provider getProvider();\n\n    /**\n     * Returns the {@code SecureRandom} to use when performing cryptographic operations during the request, or\n     * {@code null} if a default {@link SecureRandom} should be used.\n     *\n     * @return the {@code SecureRandom} to use when performing cryptographic operations during the request, or\n     * {@code null} if a default {@link SecureRandom} should be used.\n     */\n    SecureRandom getSecureRandom();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/RsaPrivateJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\n\n/**\n * JWK representation of an {@link RSAPrivateKey} as defined by the JWA (RFC 7518) specification sections on\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3\">Parameters for RSA Keys</a> and\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.2\">Parameters for RSA Private Keys</a>.\n *\n * <p>Note that the various RSA-specific properties are not available as separate dedicated getter methods, as most Java\n * applications should rarely, if ever, need to access these individual key properties since they typically represent\n * internal key material and/or serialization details. If you need to access these key properties, it is usually\n * recommended to obtain the corresponding {@link RSAPrivateKey} instance returned by {@link #toKey()} and\n * query that instead.</p>\n *\n * <p>Even so, because these properties exist and are readable by nature of every JWK being a\n * {@link java.util.Map Map}, they are still accessible via the standard {@code Map} {@link #get(Object) get} method\n * using an appropriate JWK parameter id, for example:</p>\n * <blockquote><pre>\n * jwk.get(&quot;n&quot;);\n * jwk.get(&quot;e&quot;);\n * // ... etc ...</pre></blockquote>\n *\n * @since 0.12.0\n */\npublic interface RsaPrivateJwk extends PrivateJwk<RSAPrivateKey, RSAPublicKey, RsaPublicJwk> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/RsaPrivateJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\n\n/**\n * A {@link PrivateJwkBuilder} that creates {@link RsaPrivateJwk}s.\n *\n * @since 0.12.0\n */\npublic interface RsaPrivateJwkBuilder extends PrivateJwkBuilder<RSAPrivateKey, RSAPublicKey, RsaPublicJwk, RsaPrivateJwk, RsaPrivateJwkBuilder> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/RsaPublicJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.RSAPublicKey;\n\n/**\n * JWK representation of an {@link RSAPublicKey} as defined by the JWA (RFC 7518) specification sections on\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3\">Parameters for RSA Keys</a> and\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.1\">Parameters for RSA Public Keys</a>.\n *\n * <p>Note that the various RSA-specific properties are not available as separate dedicated getter methods, as most Java\n * applications should rarely, if ever, need to access these individual key properties since they typically represent\n * internal key material and/or serialization details. If you need to access these key properties, it is usually\n * recommended to obtain the corresponding {@link RSAPublicKey} instance returned by {@link #toKey()} and\n * query that instead.</p>\n *\n * <p>Even so, because these properties exist and are readable by nature of every JWK being a\n * {@link java.util.Map Map}, they are still accessible via the standard {@code Map} {@link #get(Object) get} method\n * using an appropriate JWK parameter id, for example:</p>\n * <blockquote><pre>\n * jwk.get(&quot;n&quot;);\n * jwk.get(&quot;e&quot;);\n * // ... etc ...</pre></blockquote>\n *\n * @since 0.12.0\n */\npublic interface RsaPublicJwk extends PublicJwk<RSAPublicKey> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/RsaPublicJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\n\n/**\n * A {@link PublicJwkBuilder} that creates {@link RsaPublicJwk}s.\n *\n * @since 0.12.0\n */\npublic interface RsaPublicJwkBuilder extends PublicJwkBuilder<RSAPublicKey, RSAPrivateKey, RsaPublicJwk, RsaPrivateJwk, RsaPrivateJwkBuilder, RsaPublicJwkBuilder> {\n\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecretJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\n\n/**\n * JWK representation of a {@link SecretKey} as defined by the JWA (RFC 7518) specification section on\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-6.4\">Parameters for Symmetric Keys</a>.\n *\n * <p>Note that the {@code SecretKey}-specific properties are not available as separate dedicated getter methods, as\n * most Java applications should rarely, if ever, need to access these individual key properties since they typically\n * internal key material and/or serialization details. If you need to access these key properties, it is usually\n * recommended to obtain the corresponding {@link SecretKey} instance returned by {@link #toKey()} and\n * query that instead.</p>\n *\n * @since 0.12.0\n */\npublic interface SecretJwk extends Jwk<SecretKey> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecretJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\n\n/**\n * A {@link JwkBuilder} that creates {@link SecretJwk}s.\n *\n * @since 0.12.0\n */\npublic interface SecretJwkBuilder extends JwkBuilder<SecretKey, SecretJwk, SecretJwkBuilder> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecretKeyAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\n\n/**\n * A {@link KeyAlgorithm} that uses symmetric {@link SecretKey}s to obtain AEAD encryption and decryption keys.\n *\n * @since 0.12.0\n */\npublic interface SecretKeyAlgorithm extends KeyAlgorithm<SecretKey, SecretKey>, KeyBuilderSupplier<SecretKey, SecretKeyBuilder>, KeyLengthSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecretKeyBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport javax.crypto.SecretKey;\n\n/**\n * A {@link KeyBuilder} that creates new secure-random {@link SecretKey}s with a length sufficient to be used by\n * the security algorithm that produced this builder.\n *\n * @since 0.12.0\n */\npublic interface SecretKeyBuilder extends KeyBuilder<SecretKey, SecretKeyBuilder> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecureDigestAlgorithm.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\n\nimport java.io.InputStream;\nimport java.security.Key;\n\n/**\n * A {@link DigestAlgorithm} that requires a {@link Key} to compute and verify the authenticity of digests using either\n * <a href=\"https://en.wikipedia.org/wiki/Digital_signature\">digital signature</a> or\n * <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\">message\n * authentication code</a> algorithms.\n *\n * <p><b>Standard Identifier</b></p>\n *\n * <p>{@code SecureDigestAlgorithm} extends {@link Identifiable}: when a {@code SecureDigestAlgorithm} is used to\n * compute the digital signature or MAC of a JWS, the value returned from\n * {@link Identifiable#getId() secureDigestAlgorithm.getId()} will be set as the JWS\n * <code>&quot;alg&quot;</code> protected header value.</p>\n *\n * <p><b>Standard Implementations</b></p>\n *\n * <p>Constant definitions and utility methods for all JWA (RFC 7518) standard\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3\">Cryptographic Algorithms for Digital Signatures and\n * MACs</a> are available via {@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}.</p>\n *\n * <p><b>&quot;alg&quot; identifier</b></p>\n *\n * <p>{@code SecureDigestAlgorithm} extends {@link Identifiable}: the value returned from\n * {@link Identifiable#getId() getId()} will be used as the JWS &quot;alg&quot; protected header value.</p>\n *\n * @param <S> the type of {@link Key} used to create digital signatures or message authentication codes\n * @param <V> the type of {@link Key} used to verify digital signatures or message authentication codes\n * @see MacAlgorithm\n * @see SignatureAlgorithm\n * @since 0.12.0\n */\npublic interface SecureDigestAlgorithm<S extends Key, V extends Key>\n        extends DigestAlgorithm<SecureRequest<InputStream, S>, VerifySecureDigestRequest<V>> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecureRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.Key;\n\n/**\n * A request to a cryptographic algorithm requiring a {@link Key}.\n *\n * @param <T> the type of payload in the request\n * @param <K> they type of key used by the algorithm during the request\n * @since 0.12.0\n */\npublic interface SecureRequest<T, K extends Key> extends Request<T>, KeySupplier<K> {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecurityBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.lang.Builder;\n\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\n/**\n * A Security-specific {@link Builder} that allows configuration of common JCA API parameters that might be used\n * during instance creation, such as a {@link java.security.Provider} or {@link java.security.SecureRandom}.\n *\n * @param <T> The type of object that will be created each time {@link #build()} is invoked.\n * @param <B> the type of SecurityBuilder returned for method chaining\n * @see #provider(Provider)\n * @see #random(SecureRandom)\n * @since 0.12.0\n */\npublic interface SecurityBuilder<T, B extends SecurityBuilder<T, B>> extends Builder<T> {\n\n    /**\n     * Sets the JCA Security {@link Provider} to use if necessary when calling {@link #build()}.  This is an optional\n     * property - if not specified, the default JCA Provider will be used.\n     *\n     * @param provider the JCA Security Provider instance to use if necessary when building the new instance.\n     * @return the builder for method chaining.\n     */\n    B provider(Provider provider);\n\n    /**\n     * Sets the {@link SecureRandom} to use if necessary when calling {@link #build()}.  This is an optional property\n     * - if not specified and one is required, a default {@code SecureRandom} will be used.\n     *\n     * @param random the {@link SecureRandom} instance to use if necessary when building the new instance.\n     * @return the builder for method chaining.\n     */\n    B random(SecureRandom random);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SecurityException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.JwtException;\n\n/**\n * A {@code JwtException} attributed to a problem with security-related elements, such as\n * cryptographic keys, algorithms, or the underlying Java JCA API.\n *\n * @since 0.10.0\n */\npublic class SecurityException extends JwtException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public SecurityException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public SecurityException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SignatureAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.Identifiable;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\n/**\n * A <a href=\"https://en.wikipedia.org/wiki/Digital_signature\">digital signature</a> algorithm computes and\n * verifies digests using asymmetric public/private key cryptography.\n *\n * <p><b>Standard Identifier</b></p>\n *\n * <p>{@code SignatureAlgorithm} extends {@link Identifiable}: when a {@code SignatureAlgorithm} is used to compute\n * a JWS digital signature, the value returned from {@link Identifiable#getId() signatureAlgorithm.getId()} will be\n * set as the JWS <code>&quot;alg&quot;</code> protected header value.</p>\n *\n * <p><b>Key Pair Generation</b></p>\n *\n * <p>{@code SignatureAlgorithm} extends {@link KeyPairBuilderSupplier} to enable\n * {@link KeyPair} generation. Each {@code SignatureAlgorithm} instance will return a\n * {@link KeyPairBuilder} that ensures any created key pairs will have a sufficient length and algorithm parameters\n * required by that algorithm.  For example:</p>\n *\n * <blockquote><pre>\n * KeyPair pair = signatureAlgorithm.keyPair().build();</pre></blockquote>\n *\n * <p>The resulting {@code pair} is guaranteed to have the correct algorithm parameters and length/strength necessary\n * for that exact {@code signatureAlgorithm} instance.</p>\n *\n * <p><b>JWA Standard Implementations</b></p>\n *\n * <p>Constant definitions and utility methods for all JWA (RFC 7518) standard signature algorithms are\n * available via {@link io.jsonwebtoken.Jwts.SIG Jwts.SIG}.</p>\n *\n * @see io.jsonwebtoken.Jwts.SIG Jwts.SIG\n * @since 0.12.0\n */\npublic interface SignatureAlgorithm extends SecureDigestAlgorithm<PrivateKey, PublicKey>, KeyPairBuilderSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/SignatureException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * Exception thrown if there is problem calculating or verifying a digital signature or message authentication code.\n *\n * @since 0.10.0\n */\n@SuppressWarnings(\"deprecation\")\npublic class SignatureException extends io.jsonwebtoken.SignatureException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public SignatureException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param message the message explaining why the exception is thrown.\n     * @param cause   the underlying cause that resulted in this exception being thrown.\n     */\n    public SignatureException(String message, Throwable cause) {\n        super(message, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/UnsupportedKeyException.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * Exception thrown when encountering a key or key material that is not supported or recognized.\n *\n * @since 0.12.0\n */\npublic class UnsupportedKeyException extends KeyException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public UnsupportedKeyException(String message) {\n        super(message);\n    }\n\n    /**\n     * Creates a new instance with the specified explanation message and underlying cause.\n     *\n     * @param msg   the message explaining why the exception is thrown.\n     * @param cause the underlying cause that resulted in this exception being thrown.\n     */\n    public UnsupportedKeyException(String msg, Throwable cause) {\n        super(msg, cause);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/VerifyDigestRequest.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.io.InputStream;\n\n/**\n * A request to verify a previously-computed cryptographic digest (available via {@link #getDigest()}) against the\n * digest to be computed for the specified {@link #getPayload() payload}.\n *\n * <p>Secure digest algorithms that use keys to perform\n * <a href=\"https://en.wikipedia.org/wiki/Digital_signature\">digital signature</a> or\n * <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\">message\n * authentication code</a> verification will use {@link VerifySecureDigestRequest} instead.</p>\n *\n * @see VerifySecureDigestRequest\n * @since 0.12.0\n */\npublic interface VerifyDigestRequest extends Request<InputStream>, DigestSupplier {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/VerifySecureDigestRequest.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.io.InputStream;\nimport java.security.Key;\n\n/**\n * A request to a {@link SecureDigestAlgorithm} to verify a previously-computed\n * <a href=\"https://en.wikipedia.org/wiki/Digital_signature\">digital signature</a> or\n * <a href=\"https://en.wikipedia.org/wiki/Message_authentication_code\">message\n * authentication code</a>.\n *\n * <p>The content to verify will be available via {@link #getPayload()}, the previously-computed signature or MAC will\n * be available via {@link #getDigest()}, and the verification key will be available via {@link #getKey()}.</p>\n *\n * @param <K> the type of {@link Key} used to verify a digital signature or message authentication code\n * @since 0.12.0\n */\npublic interface VerifySecureDigestRequest<K extends Key> extends SecureRequest<InputStream, K>, VerifyDigestRequest {\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/WeakKeyException.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\n/**\n * Exception thrown when encountering a key that is not strong enough (of sufficient length) to be used with\n * a particular algorithm or in a particular security context.\n *\n * @since 0.10.0\n */\npublic class WeakKeyException extends InvalidKeyException {\n\n    /**\n     * Creates a new instance with the specified explanation message.\n     *\n     * @param message the message explaining why the exception is thrown.\n     */\n    public WeakKeyException(String message) {\n        super(message);\n    }\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/X509Accessor.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.JwsHeader;\n\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\n\n/**\n * Accessor methods of X.509-specific properties of a\n * {@link io.jsonwebtoken.ProtectedHeader ProtectedHeader} or {@link AsymmetricJwk}, guaranteeing consistent behavior\n * across similar but distinct JWT concepts with identical parameter names.\n *\n * @see io.jsonwebtoken.ProtectedHeader\n * @see AsymmetricJwk\n * @since 0.12.0\n */\npublic interface X509Accessor {\n\n    /**\n     * Returns the {@code x5u} (X.509 URL) that refers to a resource for the associated X.509 public key certificate\n     * or certificate chain, or {@code null} if not present.\n     *\n     * <p>When present, the URI <em>MUST</em> refer to a resource for an X.509 public key certificate or certificate\n     * chain that conforms to <a href=\"https://datatracker.ietf.org/doc/html/rfc5280\">RFC 5280</a> in PEM-encoded form,\n     * with each certificate delimited as specified in\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc4945#section-6.1\">Section 6.1 of RFC 4945</a>.\n     * The key in the first certificate <em>MUST</em> match the public key represented by other members of the\n     * associated ProtectedHeader or JWK.  The protocol used to acquire the resource <em>MUST</em> provide integrity\n     * protection; an HTTP GET request to retrieve the certificate <em>MUST</em> use\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc2818\">HTTP over TLS</a>; the identity of the server\n     * <em>MUST</em> be validated, as per\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc6125#section-6\">Section 6 of RFC 6125</a>.</p>\n     *\n     * <ul>\n     *     <li>When present in a {@link JwsHeader}, the certificate or first certificate in the chain corresponds\n     *         the public key complement of the private key used to digitally sign the JWS.</li>\n     *     <li>When present in a {@link JweHeader}, the certificate or certificate chain corresponds to the\n     *         public key to which the JWE was encrypted, and may be used to determine the private key needed to\n     *         decrypt the JWE.</li>\n     *     <li>When present in an {@link AsymmetricJwk}, the certificate or first certificate in the chain\n     *         <em>MUST</em> contain the public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @return the {@code x5u} (X.509 URL) that refers to a resource for the associated X.509 public key certificate or\n     * certificate chain.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.6\">JWK {@code x5u} (X.509 URL) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.5\">JWS {@code x5u} (X.509 URL) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.7\">JWE {@code x5u} (X.509 URL) Header Parameter</a>\n     */\n    URI getX509Url();\n\n    /**\n     * Returns the associated {@code x5c} (X.509 Certificate Chain), or {@code null} if not present. The initial\n     * certificate <em>MAY</em> be followed by additional certificates, with each subsequent certificate being the\n     * one used to certify the previous one.\n     *\n     * <ul>\n     *     <li>When present in a {@link JwsHeader}, the first certificate (at list index 0) <em>MUST</em> contain\n     *         the public key complement of the private key used to digitally sign the JWS.</li>\n     *     <li>When present in a {@link JweHeader}, the first certificate (at list index 0) <em>MUST</em> contain\n     *         the public key to which the JWE was encrypted, and may be used to determine the private key needed to\n     *         decrypt the JWE.</li>\n     *     <li>When present in an {@link AsymmetricJwk}, the first certificate (at list index 0)\n     *         <em>MUST</em> contain the public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @return the associated {@code x5c} (X.509 Certificate Chain), or {@code null} if not present.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.7\">JWK <code>x5c</code> (X.509 Certificate Chain) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.6\">JWS <code>x5c</code> (X.509 Certificate Chain) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.8\">JWE <code>x5c</code> (X.509 Certificate Chain) Header Parameter</a>\n     */\n    List<X509Certificate> getX509Chain();\n\n    /**\n     * Returns the {@code x5t} (X.509 Certificate SHA-1 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     * associated X.509 Certificate, or {@code null} if not present.\n     *\n     * <p>Note that certificate thumbprints are also sometimes known as certificate fingerprints.</p>\n     *\n     * <ul>\n     *     <li>When present in a {@link JwsHeader}, it is the SHA-1 thumbprint of the X.509 certificate complement\n     *         of the private key used to digitally sign the JWS.</li>\n     *     <li>When present in a {@link JweHeader}, it is the SHA-1 thumbprint of the X.509 Certificate containing\n     *         the public key to which the JWE was encrypted, and may be used to determine the private key\n     *         needed to decrypt the JWE.</li>\n     *     <li>When present in an {@link AsymmetricJwk}, it is the SHA-1 thumbprint of the X.509 certificate\n     *         containing the public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @return the {@code x5t} (X.509 Certificate SHA-1 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     * associated X.509 Certificate, or {@code null} if not present\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.8\">JWK <code>x5t</code> (X.509 Certificate SHA-1 Thumbprint) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.7\">JWS <code>x5t</code> (X.509 Certificate SHA-1 Thumbprint) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.9\">JWE <code>x5t</code> (X.509 Certificate SHA-1 Thumbprint) Header Parameter</a>\n     */\n    byte[] getX509Sha1Thumbprint();\n\n    /**\n     * Returns the {@code x5t#S256} (X.509 Certificate SHA-256 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     * associated X.509 Certificate, or {@code null} if not present.\n     *\n     * <p>Note that certificate thumbprints are also sometimes known as certificate fingerprints.</p>\n     *\n     * <ul>\n     *     <li>When present in a {@link JwsHeader}, it is the SHA-256 thumbprint of the X.509 certificate complement\n     *         of the private key used to digitally sign the JWS.</li>\n     *     <li>When present in a {@link JweHeader}, it is the SHA-256 thumbprint of the X.509 Certificate containing\n     *         the public key to which the JWE was encrypted, and may be used to determine the private key\n     *         needed to decrypt the JWE.</li>\n     *     <li>When present in an {@link AsymmetricJwk}, it is the SHA-256 thumbprint of the X.509 certificate\n     *         containing the public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @return the {@code x5t#S256} (X.509 Certificate SHA-256 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     * associated X.509 Certificate, or {@code null} if not present\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.9\">JWK <code>x5t#S256</code> (X.509 Certificate SHA-256 Thumbprint) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.8\">JWS <code>x5t#S256</code> (X.509 Certificate SHA-256 Thumbprint) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.10\">JWE <code>x5t#S256</code> (X.509 Certificate SHA-256 Thumbprint) Header Parameter</a>\n     */\n    byte[] getX509Sha256Thumbprint();\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/X509Builder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport java.security.cert.X509Certificate;\nimport java.util.List;\n\n/**\n * Additional X.509-specific builder methods for constructing an associated JWT Header or JWK, enabling method chaining.\n *\n * @param <T> the mutator subtype, for method chaining\n * @since 0.12.0\n */\npublic interface X509Builder<T extends X509Builder<T>> extends X509Mutator<T> {\n\n    /**\n     * If the {@code enable} argument is {@code true}, compute the SHA-1 thumbprint of the first\n     * {@link X509Certificate} in the configured {@link #x509Chain(List) x509CertificateChain}, and set\n     * the resulting value as the {@link #x509Sha1Thumbprint(byte[])} parameter.\n     *\n     * <p>If no chain has been configured, or {@code enable} is {@code false}, the builder will not compute nor add a\n     * {@code x5t} value.</p>\n     *\n     * @param enable whether to compute the SHA-1 thumbprint on the first available X.509 Certificate and set\n     *               the resulting value as the {@code x5t} value.\n     * @return the builder for method chaining.\n     */\n    T x509Sha1Thumbprint(boolean enable);\n\n    /**\n     * If the {@code enable} argument is {@code true}, compute the SHA-256 thumbprint of the first\n     * {@link X509Certificate} in the configured {@link #x509Chain(List) x509CertificateChain}, and set\n     * the resulting value as the {@link #x509Sha256Thumbprint(byte[])} parameter.\n     *\n     * <p>If no chain has been configured, or {@code enable} is {@code false}, the builder will not compute nor add a\n     * {@code x5t#S256} value.</p>\n     *\n     * @param enable whether to compute the SHA-256 thumbprint on the first available X.509 Certificate and set\n     *               the resulting value as the {@code x5t#S256} value.\n     * @return the builder for method chaining.\n     */\n    T x509Sha256Thumbprint(boolean enable);\n}\n"
  },
  {
    "path": "api/src/main/java/io/jsonwebtoken/security/X509Mutator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.JwsHeader;\n\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\n\n/**\n * Mutation (modifications) of X.509-specific properties of an associated JWT Header or JWK, enabling method chaining.\n *\n * @param <T> the mutator subtype, for method chaining\n * @since 0.12.0\n */\npublic interface X509Mutator<T extends X509Mutator<T>> {\n\n    /**\n     * Sets the {@code x5u} (X.509 URL) that refers to a resource containing the X.509 public key certificate or\n     * certificate chain of the associated JWT or JWK. A {@code null} value will remove the property from the JSON map.\n     *\n     * <p>The URI <em>MUST</em> refer to a resource for an X.509 public key certificate or certificate chain that\n     * conforms to <a href=\"https://datatracker.ietf.org/doc/html/rfc5280\">RFC 5280</a> in PEM-encoded form, with\n     * each certificate delimited as specified in\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc4945#section-6.1\">Section 6.1 of RFC 4945</a>.\n     * The key in the first certificate <em>MUST</em> match the public key represented by other members of the\n     * associated JWT or JWK.  The protocol used to acquire the resource <em>MUST</em> provide integrity protection;\n     * an HTTP GET request to retrieve the certificate <em>MUST</em> use\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc2818\">HTTP over TLS</a>; the identity of the server\n     * <em>MUST</em> be validated, as per\n     * <a href=\"https://datatracker.ietf.org/doc/html/rfc6125#section-6\">Section 6 of RFC 6125</a>.</p>\n     *\n     * <ul>\n     *     <li>When set for a {@link JwsHeader}, the certificate or first certificate in the chain contains\n     *         the public key complement of the private key used to digitally sign the JWS.</li>\n     *     <li>When set for {@link JweHeader}, the certificate or first certificate in the chain contains the\n     *         public key to which the JWE was encrypted, and may be used to determine the private key needed to\n     *         decrypt the JWE.</li>\n     *     <li>When set for an {@link AsymmetricJwk}, the certificate or first certificate in the chain\n     *         <em>MUST</em> contain the public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @param uri the {@code x5u} (X.509 URL) that refers to a resource for the X.509 public key certificate or\n     *            certificate chain associated with the JWT or JWK.\n     * @return the mutator/builder for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.6\">JWK <code>x5u</code> (X.509 URL) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.5\">JWS <code>x5u</code> (X.509 URL) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.7\">JWE <code>x5u</code> (X.509 URL) Header Parameter</a>\n     */\n    T x509Url(URI uri);\n\n    /**\n     * Sets the {@code x5c} (X.509 Certificate Chain) of the associated JWT or JWK. A {@code null} value will remove the\n     * property from the JSON map. The initial certificate <em>MAY</em> be followed by additional certificates, with\n     * each subsequent certificate being the one used to certify the previous one.\n     *\n     * <ul>\n     *     <li>When set for a {@link JwsHeader}, the first certificate (at list index 0) <em>MUST</em> contain\n     *         the public key complement of the private key used to digitally sign the JWS.</li>\n     *     <li>When set for {@link JweHeader}, the first certificate (at list index 0) <em>MUST</em> contain the\n     *         public key to which the JWE was encrypted, and may be used to determine the private key needed to\n     *         decrypt the JWE.</li>\n     *     <li>When set for an {@link AsymmetricJwk}, the first certificate (at list index 0) <em>MUST</em> contain\n     *         the public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @param chain the {@code x5c} (X.509 Certificate Chain) of the associated JWT or JWK.\n     * @return the header/builder for method chaining.\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.7\">JWK <code>x5c</code> (X.509 Certificate Chain) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.6\">JWS <code>x5c</code> (X.509 Certificate Chain) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.8\">JWE <code>x5c</code> (X.509 Certificate Chain) Header Parameter</a>\n     */\n    T x509Chain(List<X509Certificate> chain);\n\n    /**\n     * Sets the {@code x5t} (X.509 Certificate SHA-1 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     * X.509 Certificate associated with the JWT or JWK. A {@code null} value will remove the\n     * property from the JSON map.\n     *\n     * <p>Note that certificate thumbprints are also sometimes known as certificate fingerprints.</p>\n     *\n     * <ul>\n     *     <li>When set for a {@link JwsHeader}, it is the SHA-1 thumbprint of the X.509 certificate complement of\n     *         the private key used to digitally sign the JWS.</li>\n     *     <li>When set for {@link JweHeader}, it is the thumbprint of the X.509 Certificate containing the\n     *         public key to which the JWE was encrypted, and may be used to determine the private key needed to\n     *         decrypt the JWE.</li>\n     *     <li>When set for an {@link AsymmetricJwk}, it is the thumbprint of the X.509 certificate containing the\n     *         public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @param thumbprint the {@code x5t} (X.509 Certificate SHA-1 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     *                   X.509 Certificate associated with the JWT or JWK\n     * @return the header for method chaining\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.8\">JWK <code>x5t</code> (X.509 Certificate SHA-1 Thumbprint) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.7\">JWS <code>x5t</code> (X.509 Certificate SHA-1 Thumbprint) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.9\">JWE <code>x5t</code> (X.509 Certificate SHA-1 Thumbprint) Header Parameter</a>\n     */\n    T x509Sha1Thumbprint(byte[] thumbprint);\n\n    /**\n     * Sets the {@code x5t#S256} (X.509 Certificate SHA-256 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     * X.509 Certificate associated with the JWT or JWK. A {@code null} value will remove the\n     * property from the JSON map.\n     *\n     * <p>Note that certificate thumbprints are also sometimes known as certificate fingerprints.</p>\n     *\n     * <ul>\n     *     <li>When set for a {@link JwsHeader}, it is the SHA-256 thumbprint of the X.509 certificate complement\n     *         of the private key used to digitally sign the JWS.</li>\n     *     <li>When set for {@link JweHeader}, it is the SHA-256 thumbprint of the X.509 Certificate containing the\n     *         public key to which the JWE was encrypted, and may be used to determine the private key needed to\n     *         decrypt the JWE.</li>\n     *     <li>When set for a {@link AsymmetricJwk}, it is the SHA-256 thumbprint of the X.509 certificate\n     *         containing the public key represented by the JWK.</li>\n     * </ul>\n     *\n     * @param thumbprint the {@code x5t} (X.509 Certificate SHA-1 Thumbprint) (a.k.a. digest) of the DER-encoding of the\n     *                   X.509 Certificate associated with the JWT or JWK\n     * @return the header for method chaining\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7517.html#section-4.9\">JWK <code>x5t#S256</code> (X.509 Certificate SHA-256 Thumbprint) Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.8\">JWS <code>x5t#S256</code> (X.509 Certificate SHA-256 Thumbprint) Header Parameter</a>\n     * @see <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.10\">JWE <code>x5t#S256</code> (X.509 Certificate SHA-256 Thumbprint) Header Parameter</a>\n     */\n    T x509Sha256Thumbprint(byte[] thumbprint);\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/CompressionExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass CompressionExceptionTest {\n\n    @Test\n    void testDefaultConstructor() {\n        def exception = new CompressionException(\"my message\")\n        assertEquals \"my message\", exception.getMessage()\n    }\n\n    @Test\n    void testConstructorWithCause() {\n        def ioException = new IOException(\"root error\")\n        def exception = new CompressionException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/ExpiredJwtExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass ExpiredJwtExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n\n        replay header, claims\n\n        def ex = new ExpiredJwtException(header, claims, msg)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n    }\n\n    @Test\n    void testOverloadedConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n        def cause = new NullPointerException()\n\n        replay header, claims\n\n        def ex = new ExpiredJwtException(header, claims, msg, cause)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n        assertSame ex.cause, cause\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/IncorrectClaimExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass IncorrectClaimExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n\n        def claimName = 'cName'\n        def claimValue = 'cValue'\n\n        replay header, claims\n\n        def ex = new IncorrectClaimException(header, claims, claimName, claimValue, msg)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n        assertEquals ex.claimName, claimName\n        assertEquals ex.claimValue, claimValue\n    }\n\n    @Test\n    void testOverloadedConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n        def cause = new NullPointerException()\n\n        def claimName = 'cName'\n        def claimValue = 'cValue'\n\n        replay header, claims\n\n        def ex = new IncorrectClaimException(header, claims, claimName, claimValue, msg, cause)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n        assertSame ex.cause, cause\n        assertEquals ex.claimName, claimName\n        assertEquals ex.claimValue, claimValue\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/InvalidClaimExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass InvalidClaimExceptionTest {\n\n    @Test\n    void testOverloadedConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n        def cause = new NullPointerException()\n\n        def claimName = 'cName'\n        def claimValue = 'cValue'\n\n        replay header, claims\n\n        def ex = new InvalidClaimException(header, claims, claimName, claimValue, msg, cause)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n        assertSame ex.cause, cause\n        assertEquals ex.claimName, claimName\n        assertEquals ex.claimValue, claimValue\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/JwtHandlerAdapterTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass JwtHandlerAdapterTest {\n\n    private JwtHandlerAdapter handler\n\n    @Before\n    void setUp() {\n        handler = new JwtHandlerAdapter() {}\n    }\n\n    @Test\n    void testOnContentJwt() {\n        try {\n            handler.onUnsecuredContent(null)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured content JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testOnClaimsJwt() {\n        try {\n            handler.onUnsecuredClaims(null)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured Claims JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testOnContentJws() {\n        try {\n            handler.onVerifiedContent(null)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected content JWS.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testOnClaimsJws() {\n        try {\n            handler.onVerifiedClaims(null)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected Claims JWS.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testOnContentJwe() {\n        try {\n            handler.onDecryptedContent(null)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected content JWE.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testOnClaimsJwe() {\n        try {\n            handler.onDecryptedClaims(null)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected Claims JWE.', e.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/MalformedJwtExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass MalformedJwtExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def exception = new MalformedJwtException(\"my message\")\n        assertEquals \"my message\", exception.getMessage()\n    }\n\n    @Test\n    void testCauseConstructor() {\n        def ioException = new IOException(\"root error\")\n        def exception = new MalformedJwtException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/MissingClaimExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass MissingClaimExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n\n        def claimName = 'cName'\n        def claimValue = 'cValue'\n\n        replay header, claims\n\n        def ex = new MissingClaimException(header, claims, claimName, claimValue, msg)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n        assertEquals ex.claimName, claimName\n        assertEquals ex.claimValue, claimValue\n    }\n\n    @Test\n    void testOverloadedConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n        def cause = new NullPointerException()\n\n        def claimName = 'cName'\n        def claimValue = 'cValue'\n\n        replay header, claims\n\n        def ex = new MissingClaimException(header, claims, claimName, claimValue, msg, cause)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n        assertSame ex.cause, cause\n        assertEquals ex.claimName, claimName\n        assertEquals ex.claimValue, claimValue\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/PrematureJwtExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass PrematureJwtExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n\n        replay header, claims\n\n        def ex = new PrematureJwtException(header, claims, msg)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n    }\n\n    @Test\n    void testOverloadedConstructor() {\n        def header = createMock(Header)\n        def claims = createMock(Claims)\n        def msg = 'foo'\n        def cause = new NullPointerException()\n\n        replay header, claims\n\n        def ex = new PrematureJwtException(header, claims, msg, cause)\n\n        verify header, claims\n\n        assertSame ex.header, header\n        assertSame ex.claims, claims\n        assertEquals ex.message, msg\n        assertSame ex.cause, cause\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/RequiredTypeExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass RequiredTypeExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def msg = 'foo'\n        def ex = new RequiredTypeException(msg)\n        assertEquals ex.message, msg\n    }\n\n    @Test\n    void testOverloadedConstructor() {\n        def msg = 'foo'\n        def cause = new NullPointerException()\n        def ex = new RequiredTypeException(msg, cause)\n        assertEquals ex.message, msg\n        assertSame ex.cause, cause\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/SignatureExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass SignatureExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def exception = new SignatureException(\"my message\")\n        assertEquals \"my message\", exception.getMessage()\n    }\n\n    @Test\n    void testCauseConstructor() {\n        def ioException = new IOException(\"root error\")\n        def exception = new SignatureException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/SigningKeyResolverAdapterTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.StandardCharsets\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass SigningKeyResolverAdapterTest {\n\n    @Test(expected=UnsupportedJwtException) //should throw since called but not overridden\n    void testDefaultResolveSigningKeyBytesFromClaims() {\n        def header = createMock(JwsHeader)\n        def claims = createMock(Claims)\n        new SigningKeyResolverAdapter().resolveSigningKeyBytes(header, claims)\n    }\n\n    @Test(expected=UnsupportedJwtException) //should throw since called but not overridden\n    void testDefaultResolveSigningKeyBytesFromStringPayload() {\n        def header = createMock(JwsHeader)\n        new SigningKeyResolverAdapter().resolveSigningKeyBytes(header, \"hi\".getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testResolveSigningKeyHmac() {\n\n        JwsHeader header = createMock(JwsHeader)\n        Claims claims = createMock(Claims)\n\n        byte[] bytes = new byte[32]\n        new Random().nextBytes(bytes)\n\n        expect(header.getAlgorithm()).andReturn(\"HS256\")\n\n        replay header, claims\n\n        def adapter = new SigningKeyResolverAdapter() {\n            @Override\n            byte[] resolveSigningKeyBytes(JwsHeader h, Claims c) {\n                assertSame header, h\n                assertSame claims, c\n                return bytes\n            }\n        }\n\n        def key = adapter.resolveSigningKey(header, claims)\n\n        verify header, claims\n\n        assertTrue key instanceof SecretKeySpec\n        assertEquals 'HmacSHA256', key.algorithm\n        assertTrue Arrays.equals(bytes, key.encoded)\n    }\n\n    @Test(expected=IllegalArgumentException)\n    void testResolveSigningKeyDefaultWithoutHmac() {\n        JwsHeader header = createMock(JwsHeader)\n        Claims claims = createMock(Claims)\n        expect(header.getAlgorithm()).andReturn(\"RS256\")\n        replay header, claims\n        new SigningKeyResolverAdapter().resolveSigningKey(header, claims)\n    }\n\n    @Test\n    void testResolveSigningKeyPayloadHmac() {\n\n        JwsHeader header = createMock(JwsHeader)\n\n        byte[] keyBytes = new byte[32]\n        new Random().nextBytes(keyBytes)\n        byte[] payloadBytes = 'hi'.getBytes(StandardCharsets.UTF_8)\n\n        expect(header.getAlgorithm()).andReturn(\"HS256\")\n\n        replay header\n\n        def adapter = new SigningKeyResolverAdapter() {\n            @Override\n            byte[] resolveSigningKeyBytes(JwsHeader h, byte[] payload) {\n                assertSame header, h\n                assertArrayEquals payloadBytes, payload\n                return keyBytes\n            }\n        }\n\n        def key = adapter.resolveSigningKey(header, payloadBytes)\n\n        verify header\n\n        assertTrue key instanceof SecretKeySpec\n        assertEquals 'HmacSHA256', key.algorithm\n        assertTrue Arrays.equals(keyBytes, key.encoded)\n    }\n\n    @Test(expected=IllegalArgumentException)\n    void testResolveSigningKeyPayloadWithoutHmac() {\n        JwsHeader header = createMock(JwsHeader)\n        expect(header.getAlgorithm()).andReturn(\"RS256\")\n        replay header\n        new SigningKeyResolverAdapter().resolveSigningKey(header, 'hi'.getBytes(StandardCharsets.UTF_8))\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/UnsupportedJwtExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass UnsupportedJwtExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def exception = new UnsupportedJwtException(\"my message\")\n        assertEquals \"my message\", exception.getMessage()\n    }\n\n    @Test\n    void testCauseConstructor() {\n        def ioException = new IOException(\"root error\")\n        def exception = new UnsupportedJwtException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/AbstractDeserializerTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass AbstractDeserializerTest {\n\n    @Test\n    void deserializeNullByteArray() {\n        boolean invoked = false\n        def deser = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                assertEquals EOF, reader.read()\n                invoked = true\n            }\n        }\n        deser.deserialize((byte[]) null)\n        assertTrue invoked\n    }\n\n    @Test\n    void deserializeEmptyByteArray() {\n        boolean invoked = false\n        def deser = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                assertEquals EOF, reader.read()\n                invoked = true\n            }\n        }\n        deser.deserialize(new byte[0])\n        assertTrue invoked\n    }\n\n    @Test\n    void deserializeByteArray() {\n        byte b = 0x01\n        def bytes = new byte[1]\n        bytes[0] = b\n        def des = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                assertEquals b, reader.read()\n                return 42\n            }\n        }\n        assertEquals 42, des.deserialize(bytes)\n    }\n\n    @Test\n    void deserializeException() {\n\n        def ex = new RuntimeException('foo')\n        def des = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                throw ex\n            }\n        }\n\n        try {\n            des.deserialize(new byte[0])\n        } catch (DeserializationException expected) {\n            String msg = 'Unable to deserialize: foo'\n            assertEquals msg, expected.message\n            assertSame ex, expected.cause\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/AbstractSerializerTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass AbstractSerializerTest {\n\n    @Test\n    void serializeByteArray() {\n        def value = 42\n        def ser = new AbstractSerializer() {\n            @Override\n            protected void doSerialize(Object o, OutputStream out) throws Exception {\n                assertEquals value, o\n                out.write(0x01)\n            }\n        }\n\n        def out = ser.serialize(value)\n        assertEquals 0x01, out[0]\n    }\n\n    @Test\n    void serializeException() {\n\n        def ex = new RuntimeException('foo')\n        def ser = new AbstractSerializer() {\n            @Override\n            protected void doSerialize(Object o, OutputStream out) throws Exception {\n                throw ex\n            }\n        }\n\n        try {\n            ser.serialize(42, new ByteArrayOutputStream())\n        } catch (SerializationException expected) {\n            String msg = 'Unable to serialize object of type java.lang.Integer: foo'\n            assertEquals msg, expected.message\n            assertSame ex, expected.cause\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/Base64DecoderTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass Base64DecoderTest {\n\n    @Test(expected = IllegalArgumentException)\n    void testDecodeWithNullArgument() {\n        new Base64Decoder().decode(null)\n    }\n\n    @Test\n    void decode() {\n        String encoded = 'SGVsbG8g5LiW55WM' // Hello 世界\n        byte[] bytes = new Base64Decoder().decode(encoded)\n        String result = new String(bytes, Strings.UTF_8)\n        assertEquals 'Hello 世界', result\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/Base64EncoderTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass Base64EncoderTest {\n\n    @Test(expected = IllegalArgumentException)\n    void testEncodeWithNullArgument() {\n        new Base64Encoder().encode(null)\n    }\n\n    @Test\n    void encode() {\n        String input = 'Hello 世界'\n        byte[] bytes = input.getBytes(Strings.UTF_8)\n        String encoded = new Base64Encoder().encode(bytes)\n        assertEquals 'SGVsbG8g5LiW55WM', encoded\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/Base64Test.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass Base64Test {\n\n    private static final String PLAINTEXT =\n            '''Bacon ipsum dolor amet venison beef pork chop, doner jowl pastrami ground round alcatra.\n               Beef leberkas filet mignon ball tip pork spare ribs kevin short loin ribeye ground round\n               biltong jerky short ribs corned beef. Strip steak turducken meatball porchetta beef ribs\n               shoulder pork belly doner salami corned beef kielbasa cow filet mignon drumstick. Bacon\n               tenderloin pancetta flank frankfurter ham kevin leberkas meatball turducken beef ribs.\n               Cupim short loin short ribs shankle tenderloin. Ham ribeye hamburger flank tenderloin\n               cupim t-bone, shank tri-tip venison salami sausage pancetta. Pork belly chuck salami\n               alcatra sirloin.\n               \n               以ケ ホゥ婧詃 橎ちゅぬ蛣埣 禧ざしゃ蟨廩 椥䤥グ曣わ 基覧 滯っ䶧きょメ Ủ䧞以ケ妣 择禤槜谣お 姨のドゥ,\n               らボみょば䪩 苯礊觊ツュ婃 䩦ディふげセ げセりょ 禤槜 Ủ䧞以ケ妣 せがみゅちょ䰯 择禤槜谣お 難ゞ滧 蝥ちゃ,\n               滯っ䶧きょメ らボみょば䪩 礯みゃ楦と饥 椥䤥グ ウァ槚 訤をりゃしゑ びゃ驨も氩簥 栨キョ奎婨榞 ヌに楃 以ケ,\n               姚奊べ 椥䤥グ曣わ 栨キョ奎婨榞 ちょ䰯 Ủ䧞以ケ妣 誧姨のドゥろ よ苯礊 く涥, りゅぽ槞 馣ぢゃ尦䦎ぎ\n               大た䏩䰥ぐ 郎きや楺橯 䧎キェ, 難ゞ滧 栧择 谯䧟簨訧ぎょ 椥䤥グ曣わ'''\n\n    @Test\n    void testBase64Name() {\n        assertEquals 'base64', Base64.DEFAULT.getName() // RFC 4648 codec name is all lowercase\n    }\n\n    @Test\n    void testBase64UrlName() {\n        assertEquals 'base64url', Base64.URL_SAFE.getName() // RFC 4648 codec name is all lowercase\n    }\n\n    @Test\n    void testEncodeToStringWithNullArgument() {\n        String s = Base64.DEFAULT.encodeToString(null, false)\n        assertEquals 0, s.toCharArray().length\n    }\n\n    @Test\n    void testEncodeToStringWithEmptyByteArray() {\n        byte[] bytes = new byte[0]\n        String s = Base64.DEFAULT.encodeToString(bytes, false)\n        assertEquals 0, s.toCharArray().length\n    }\n\n    @Test\n    void testLineSeparators() {\n        byte[] bytes = PLAINTEXT.getBytes(Strings.UTF_8)\n        String encoded = Base64.DEFAULT.encodeToString(bytes, true)\n\n        def r = new StringReader(encoded)\n        String line\n        while ((line = r.readLine()) != null) {\n            assertTrue line.length() <= 76\n        }\n    }\n\n    @Test\n    void testDecodeFastWithNullArgument() {\n        byte[] bytes = Base64.DEFAULT.decodeFast(null)\n        assertEquals 0, bytes.length\n    }\n\n    @Test\n    void testDecodeFastWithEmptyCharArray() {\n        byte[] bytes = Base64.DEFAULT.decodeFast(Strings.EMPTY)\n        assertEquals 0, bytes.length\n    }\n\n    @Test\n    void testDecodeFastWithSurroundingIllegalCharacters() {\n        String expected = 'Hello 世界'\n        def encoded = '***SGVsbG8g5LiW55WM!!!'\n        byte[] bytes = Base64.DEFAULT.decodeFast(encoded)\n        String result = new String(bytes, Strings.UTF_8)\n        assertEquals expected, result\n    }\n\n    @Test\n    void testDecodeFastWithIntermediateIllegalInboundCharacters() {\n        def encoded = 'SGVsbG8g*5LiW55WM'\n        try {\n            Base64.DEFAULT.decodeFast(encoded)\n            fail()\n        } catch (DecodingException de) {\n            assertEquals 'Illegal base64 character: \\'*\\'', de.getMessage()\n        }\n    }\n\n    @Test\n    void testDecodeFastWithIntermediateIllegalOutOfBoundCharacters() {\n        def encoded = 'SGVsbG8g世5LiW55WM'\n        try {\n            Base64.DEFAULT.decodeFast(encoded)\n            fail()\n        } catch (DecodingException de) {\n            assertEquals 'Illegal base64 character: \\'世\\'', de.getMessage()\n        }\n    }\n\n    @Test\n    void testDecodeFastWithIntermediateIllegalSpaceCharacters() {\n        def encoded = 'SGVsbG8g 5LiW55WM'\n        try {\n            Base64.DEFAULT.decodeFast(encoded)\n            fail()\n        } catch (DecodingException de) {\n            assertEquals 'Illegal base64 character: \\' \\'', de.getMessage()\n        }\n    }\n\n    @Test\n    void testDecodeFastWithLineSeparators() {\n\n        byte[] bytes = PLAINTEXT.getBytes(Strings.UTF_8)\n        String encoded = Base64.DEFAULT.encodeToString(bytes, true)\n\n        byte[] resultBytes = Base64.DEFAULT.decodeFast(encoded)\n\n        assertTrue Arrays.equals(bytes, resultBytes)\n        assertEquals PLAINTEXT, new String(resultBytes, Strings.UTF_8)\n    }\n\n    private static String encode(String s) {\n        byte[] bytes = s.getBytes(Strings.UTF_8)\n        return Base64.DEFAULT.encodeToString(bytes, false)\n    }\n\n    private static String decode(String s) {\n        byte[] bytes = Base64.DEFAULT.decodeFast(s)\n        return new String(bytes, Strings.UTF_8)\n    }\n\n    @Test\n    // https://tools.ietf.org/html/rfc4648#page-12\n    void testRfc4648Base64TestVectors() {\n\n        assertEquals \"\", encode(\"\")\n        assertEquals \"\", decode(\"\")\n\n        assertEquals \"Zg==\", encode(\"f\")\n        assertEquals \"f\", decode(\"Zg==\")\n\n        assertEquals \"Zm8=\", encode(\"fo\")\n        assertEquals \"fo\", decode(\"Zm8=\")\n\n        assertEquals \"Zm9v\", encode(\"foo\")\n        assertEquals \"foo\", decode(\"Zm9v\")\n\n        assertEquals \"Zm9vYg==\", encode(\"foob\")\n        assertEquals \"foob\", decode(\"Zm9vYg==\")\n\n        assertEquals \"Zm9vYmE=\", encode(\"fooba\")\n        assertEquals \"fooba\", decode(\"Zm9vYmE=\")\n\n        assertEquals \"Zm9vYmFy\", encode(\"foobar\")\n        assertEquals \"foobar\", decode(\"Zm9vYmFy\")\n\n        def input = 'special: [\\r\\n \\t], ascii[32..126]: [ !\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~]\\n'\n        def expected = \"c3BlY2lhbDogWw0KIAldLCBhc2NpaVszMi4uMTI2XTogWyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+XQo=\"\n        assertEquals expected, encode(input)\n        assertEquals input, decode(expected)\n    }\n\n    private static String urlEncode(String s) {\n        byte[] bytes = s.getBytes(Strings.UTF_8)\n        return Base64.URL_SAFE.encodeToString(bytes, false)\n    }\n\n    private static String urlDecode(String s) {\n        byte[] bytes = Base64.URL_SAFE.decodeFast(s)\n        return new String(bytes, Strings.UTF_8)\n    }\n\n    @Test\n    //same test vectors above, but with padding removed & some specials swapped: https://brockallen.com/2014/10/17/base64url-encoding/\n    void testRfc4648Base64UrlTestVectors() {\n\n        assertEquals \"\", urlEncode(\"\")\n        assertEquals \"\", urlDecode(\"\")\n\n        assertEquals \"Zg\", urlEncode(\"f\") //base64 = 2 padding chars, base64url = no padding needed\n        assertEquals \"f\", urlDecode(\"Zg\")\n\n        assertEquals \"Zm8\", urlEncode(\"fo\") //base64 = 1 padding char, base64url = no padding needed\n        assertEquals \"fo\", urlDecode(\"Zm8\")\n\n        assertEquals \"Zm9v\", urlEncode(\"foo\")\n        assertEquals \"foo\", urlDecode(\"Zm9v\")\n\n        assertEquals \"Zm9vYg\", urlEncode(\"foob\") //base64 = 2 padding chars, base64url = no padding needed\n        assertEquals \"foob\", urlDecode(\"Zm9vYg\")\n\n        assertEquals \"Zm9vYmE\", urlEncode(\"fooba\") //base64 = 1 padding char, base64url = no padding needed\n        assertEquals \"fooba\", urlDecode(\"Zm9vYmE\")\n\n        assertEquals \"Zm9vYmFy\", urlEncode(\"foobar\")\n        assertEquals \"foobar\", urlDecode(\"Zm9vYmFy\")\n\n        def input = 'special: [\\r\\n \\t], ascii[32..126]: [ !\"#$%&\\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~]\\n'\n        def expected = \"c3BlY2lhbDogWw0KIAldLCBhc2NpaVszMi4uMTI2XTogWyAhIiMkJSYnKCkqKywtLi8wMTIzNDU2Nzg5Ojs8PT4/QEFCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaW1xdXl9gYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXp7fH1+XQo=\"\n                .replace(\"=\", \"\")\n                .replace(\"+\", \"-\")\n                .replace(\"/\", \"_\")\n        assertEquals expected, urlEncode(input)\n        assertEquals input, urlDecode(expected)\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/CodecExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass CodecExceptionTest {\n\n    @Test\n    void testConstructorWithCause() {\n        def ioException = new java.io.IOException(\"root error\")\n        def exception = new CodecException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/DecodersTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertTrue\n\nclass DecodersTest {\n\n    @Test\n    void testPrivateCtor() {\n        new Decoders() //not allowed in java, including here only to pass test coverage assertions\n    }\n\n    @Test\n    void testBase64() {\n        assertTrue Decoders.BASE64 instanceof ExceptionPropagatingDecoder\n        assertTrue Decoders.BASE64.decoder instanceof Base64Decoder\n    }\n\n    @Test\n    void testBase64Url() {\n        assertTrue Decoders.BASE64URL instanceof ExceptionPropagatingDecoder\n        assertTrue Decoders.BASE64URL.decoder instanceof Base64UrlDecoder\n    }\n\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/DecodingExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass DecodingExceptionTest {\n\n    @Test\n    void testConstructorWithCause() {\n        def ioException = new java.io.IOException(\"root error\")\n        def exception = new DecodingException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/DeserializationExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass DeserializationExceptionTest {\n\n    @Test\n    void testDefaultConstructor() {\n        def exception = new DeserializationException(\"my message\")\n        assertEquals \"my message\", exception.getMessage()\n    }\n\n    @Test\n    void testConstructorWithCause() {\n        def ioException = new java.io.IOException(\"root error\")\n        def exception = new DeserializationException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/EncodersTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertTrue\n\nclass EncodersTest {\n\n    @Test\n    void testPrivateCtor() {\n        new Encoders() //not allowed in java, including here only to pass test coverage assertions\n    }\n\n    @Test\n    void testBase64() {\n        assertTrue Encoders.BASE64 instanceof ExceptionPropagatingEncoder\n        assertTrue Encoders.BASE64.encoder instanceof Base64Encoder\n    }\n\n    @Test\n    void testBase64Url() {\n        assertTrue Encoders.BASE64URL instanceof ExceptionPropagatingEncoder\n        assertTrue Encoders.BASE64URL.encoder instanceof Base64UrlEncoder\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/EncodingExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass EncodingExceptionTest {\n\n    @Test\n    void testConstructorWithCause() {\n        def ioException = new java.io.IOException(\"root error\")\n        def exception = new EncodingException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/ExceptionPropagatingDecoderTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass ExceptionPropagatingDecoderTest {\n\n    @Test(expected = IllegalArgumentException)\n    void testWithNullConstructorArgument() {\n        new ExceptionPropagatingDecoder(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testEncodeWithNullArgument() {\n        def decoder = new ExceptionPropagatingDecoder<>(new Base64UrlDecoder())\n        decoder.decode(null)\n    }\n\n    @Test\n    void testEncodePropagatesDecodingException() {\n        def decoder = new ExceptionPropagatingDecoder(new Decoder() {\n            @Override\n            Object decode(Object o) throws DecodingException {\n                throw new DecodingException(\"problem\", new java.io.IOException(\"dummy\"))\n            }\n        })\n        try {\n            decoder.decode(\"hello\")\n            fail()\n        } catch (DecodingException ex) {\n            assertEquals \"problem\", ex.getMessage()\n        }\n    }\n\n    @Test\n    void testEncodeWithNonEncodingExceptionIsWrappedAsEncodingException() {\n\n        def causeEx = new RuntimeException(\"whatevs\")\n\n        def decoder = new ExceptionPropagatingDecoder(new Decoder() {\n            @Override\n            Object decode(Object o) throws EncodingException {\n                throw causeEx\n            }\n        })\n        try {\n            decoder.decode(\"hello\")\n            fail()\n        } catch (DecodingException ex) {\n            assertEquals \"Unable to decode input: whatevs\", ex.getMessage()\n            assertSame causeEx, ex.getCause()\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/ExceptionPropagatingEncoderTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass ExceptionPropagatingEncoderTest {\n\n\n    @Test(expected = IllegalArgumentException)\n    void testWithNullConstructorArgument() {\n        new ExceptionPropagatingEncoder(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testEncodeWithNullArgument() {\n        def encoder = new ExceptionPropagatingEncoder<>(new Base64UrlEncoder())\n        encoder.encode(null)\n    }\n\n    @Test\n    void testEncodePropagatesEncodingException() {\n        def encoder = new ExceptionPropagatingEncoder(new Encoder() {\n            @Override\n            Object encode(Object o) throws EncodingException {\n                throw new EncodingException(\"problem\", new java.io.IOException(\"dummy\"))\n            }\n        })\n        try {\n            encoder.encode(\"hello\")\n            fail()\n        } catch (EncodingException ex) {\n            assertEquals \"problem\", ex.getMessage()\n        }\n    }\n\n    @Test\n    void testEncodeWithNonEncodingExceptionIsWrappedAsEncodingException() {\n\n        def causeEx = new RuntimeException(\"whatevs\")\n\n        def encoder = new ExceptionPropagatingEncoder(new Encoder() {\n            @Override\n            Object encode(Object o) throws EncodingException {\n                throw causeEx;\n            }\n        })\n        try {\n            encoder.encode(\"hello\")\n            fail()\n        } catch (EncodingException ex) {\n            assertEquals \"Unable to encode input: whatevs\", ex.getMessage()\n            assertSame causeEx, ex.getCause()\n        }\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/io/SerializationExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass SerializationExceptionTest {\n\n    @Test\n    void testDefaultConstructor() {\n        def exception = new SerializationException(\"my message\")\n        assertEquals \"my message\", exception.getMessage()\n    }\n\n    @Test\n    void testConstructorWithCause() {\n        def ioException = new java.io.IOException(\"root error\")\n        def exception = new SerializationException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/lang/ArraysTest.groovy",
    "content": "/*\n * Copyright © 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang\n\n\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.*\n\n/**\n * @since 0.12.0\n */\nclass ArraysTest {\n\n    @Test\n    void testPrivateCtor() {\n        new Arrays() //not allowed in java, including here only to pass test coverage assertions\n    }\n\n    @Test\n    void testCleanWithNull() {\n        assertNull Arrays.clean(null)\n    }\n\n    @Test\n    void testCleanWithEmpty() {\n        assertNull Arrays.clean(new byte[0])\n    }\n\n    @Test\n    void testCleanWithElements() {\n        byte[] bytes = \"hello\".getBytes(StandardCharsets.UTF_8)\n        assertSame bytes, Arrays.clean(bytes)\n    }\n\n    @Test\n    void testByteArrayLengthWithNull() {\n        assertEquals 0, Arrays.length((byte[]) null)\n    }\n\n    @Test\n    void testByteArrayLengthWithEmpty() {\n        assertEquals 0, Arrays.length(new byte[0])\n    }\n\n    @Test\n    void testByteArrayLengthWithElements() {\n        byte[] bytes = \"hello\".getBytes(StandardCharsets.UTF_8)\n        assertEquals 5, Arrays.length(bytes)\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/lang/CollectionsTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass CollectionsTest {\n\n    @Test\n    void testAsSetFromNull() {\n        assertSame java.util.Collections.emptySet(), Collections.asSet(null)\n    }\n\n    @Test\n    void testAsSetFromEmpty() {\n        def list = []\n        assertSame java.util.Collections.emptySet(), Collections.asSet(list)\n    }\n\n    @Test\n    void testAsSetFromSet() {\n        def set = Collections.setOf('foo')\n        assertEquals set, Collections.asSet(set)\n    }\n\n    @Test\n    void testAsSetFromList() {\n        def list = Collections.of('one', 'two')\n        def set = Collections.asSet(list)\n        assertTrue set.containsAll(list)\n        try {\n            set.add('another')\n            fail()\n        } catch (UnsupportedOperationException ignored) { // expected, asSet returns immutable instances\n        }\n    }\n\n    @Test\n    void testNullSafeSetWithNullArgument() {\n        def set = Collections.nullSafe((Set)null)\n        assertNotNull set\n        assertTrue set.isEmpty()\n    }\n\n    @Test\n    void testNullSafeSetWithEmptyArgument() {\n        def a = new LinkedHashSet()\n        def b = Collections.nullSafe(a)\n        assertSame a, b\n    }\n\n    @Test\n    void testNullSafeSetWithNonEmptyArgument() {\n        def a = [\"hello\"] as Set<String>\n        def b = Collections.nullSafe(a)\n        assertSame a, b\n    }\n\n    @Test\n    void testNullSafeCollectionWithNullArgument() {\n        Collection c = Collections.nullSafe((Collection)null)\n        assertNotNull c\n        assertTrue c.isEmpty()\n    }\n\n    @Test\n    void testNullSafeCollectionWithEmptyArgument() {\n        Collection a = new LinkedHashSet() as Collection\n        def b = Collections.nullSafe(a)\n        assertSame a, b\n    }\n\n    @Test\n    void testNullSafeCollectionWithNonEmptyArgument() {\n        Collection a = [\"hello\"] as Collection<String>\n        def b = Collections.nullSafe(a)\n        assertSame a, b\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/lang/DateFormatsTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang\n\nimport org.junit.Test\n\nimport java.text.SimpleDateFormat\n\nimport static org.junit.Assert.*\n\nclass DateFormatsTest {\n\n    @Test //https://github.com/jwtk/jjwt/issues/291\n    void testUtcTimezone() {\n\n        def iso8601 = DateFormats.ISO_8601.get()\n        def iso8601Millis = DateFormats.ISO_8601_MILLIS.get()\n\n        assertTrue iso8601 instanceof SimpleDateFormat\n        assertTrue iso8601Millis instanceof SimpleDateFormat\n\n        def utc = TimeZone.getTimeZone(\"UTC\")\n\n        assertEquals utc, iso8601.getTimeZone()\n        assertEquals utc, iso8601Millis.getTimeZone()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/lang/MapsTest.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang\n\nimport org.junit.Test\n\nimport static org.hamcrest.MatcherAssert.assertThat\nimport static org.hamcrest.CoreMatchers.is\n\nclass MapsTest {\n\n    @Test\n    void testSingleMapOf() {\n        assertThat Maps.of(\"aKey\", \"aValue\").build(), is([aKey: \"aValue\"])\n    }\n\n    @Test\n    void testMapOfAnd() {\n        assertThat Maps.of(\"aKey1\", \"aValue1\")\n                        .and(\"aKey2\", \"aValue2\")\n                        .and(\"aKey3\", \"aValue3\")\n                        .build(),\n                is([aKey1: \"aValue1\", aKey2: \"aValue2\", aKey3: \"aValue3\"])\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/lang/StringsTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass StringsTest {\n\n    @Test\n    void testHasText() {\n        assertFalse Strings.hasText(null)\n        assertFalse Strings.hasText(\"\")\n        assertFalse Strings.hasText(\"   \")\n        assertTrue Strings.hasText(\"  foo   \")\n        assertTrue Strings.hasText(\"foo\")\n    }\n\n    @Test\n    void testClean() {\n        assertEquals \"this is a test\", Strings.clean(\"this is a test\")\n        assertEquals \"this is a test\", Strings.clean(\"   this is a test\")\n        assertEquals \"this is a test\", Strings.clean(\"   this is a test   \")\n        assertEquals \"this is a test\", Strings.clean(\"\\nthis is a test \\t  \")\n        assertNull Strings.clean(null)\n        assertNull Strings.clean(\"\")\n        assertNull Strings.clean(\"\\t\")\n        assertNull Strings.clean(\"      \")\n    }\n\n    @Test\n    void testCleanCharSequence() {\n        def result = Strings.clean(new StringBuilder(\"this is a test\"))\n        assertNotNull result\n        assertEquals \"this is a test\", result.toString()\n\n        result = Strings.clean(new StringBuilder(\"   this is a test\"))\n        assertNotNull result\n        assertEquals \"this is a test\", result.toString()\n\n        result = Strings.clean(new StringBuilder(\"   this is a test   \"))\n        assertNotNull result\n        assertEquals \"this is a test\", result.toString()\n\n        result = Strings.clean(new StringBuilder(\"\\nthis is a test \\t  \"))\n        assertNotNull result\n        assertEquals \"this is a test\", result.toString()\n\n        assertNull Strings.clean((StringBuilder) null)\n        assertNull Strings.clean(new StringBuilder(\"\"))\n        assertNull Strings.clean(new StringBuilder(\"\\t\"))\n        assertNull Strings.clean(new StringBuilder(\"      \"))\n    }\n\n\n    @Test\n    void testTrimWhitespace() {\n        assertEquals \"\", Strings.trimWhitespace(\"      \")\n    }\n\n    @Test\n    void testNespaceNull() {\n        assertNull Strings.nespace(null)\n    }\n\n    @Test\n    void testNespaceEmpty() {\n        StringBuilder sb = new StringBuilder()\n        Strings.nespace(sb)\n        assertEquals 0, sb.length() // didn't add space because it's already empty\n        assertEquals '', sb.toString()\n    }\n\n    @Test\n    void testNespaceNonEmpty() {\n        StringBuilder sb = new StringBuilder()\n        sb.append(\"Hello\")\n        Strings.nespace(sb).append(\"World\")\n        assertEquals 'Hello World', sb.toString()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/security/InvalidKeyExceptionTest.groovy",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass InvalidKeyExceptionTest {\n\n    @Test\n    void testDefaultConstructor() {\n        def msg = \"my message\"\n        def exception = new InvalidKeyException(msg)\n        assertEquals msg, exception.getMessage()\n    }\n\n    @Test\n    void testConstructorWithCause() {\n        def rootMsg = 'root error'\n        def msg = 'wrapping'\n        def ioException = new IOException(rootMsg)\n        def exception = new InvalidKeyException(msg, ioException)\n        assertEquals msg, exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/security/MalformedKeyExceptionTest.groovy",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass MalformedKeyExceptionTest {\n\n    @Test\n    void testDefaultConstructor() {\n        def msg = \"my message\"\n        def exception = new MalformedKeyException(msg)\n        assertEquals msg, exception.getMessage()\n    }\n\n    @Test\n    void testConstructorWithCause() {\n        def rootMsg = 'root error'\n        def msg = 'wrapping'\n        def ioException = new IOException(rootMsg)\n        def exception = new MalformedKeyException(msg, ioException)\n        assertEquals msg, exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}\n"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/security/SignatureExceptionTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass SignatureExceptionTest {\n\n    @Test\n    void testStringConstructor() {\n        def exception = new SignatureException(\"my message\")\n        assertEquals \"my message\", exception.getMessage()\n    }\n\n    @Test\n    void testCauseConstructor() {\n        def ioException = new IOException(\"root error\")\n        def exception = new SignatureException(\"wrapping\", ioException)\n        assertEquals \"wrapping\", exception.getMessage()\n        assertEquals ioException, exception.getCause()\n    }\n}"
  },
  {
    "path": "api/src/test/groovy/io/jsonwebtoken/security/UnsupportedKeyExceptionTest.groovy",
    "content": "/*\n * Copyright © 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass UnsupportedKeyExceptionTest {\n\n    @Test\n    void testCauseWithMessage() {\n        def cause = new IllegalStateException()\n        def msg = 'foo'\n        def ex = new UnsupportedKeyException(msg, cause)\n        assertEquals msg, ex.getMessage()\n        assertSame cause, ex.getCause()\n    }\n}\n"
  },
  {
    "path": "bom/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2018 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt-bom</artifactId>\n    <name>JJWT :: BOM</name>\n    <packaging>pom</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/..</jjwt.root>\n    </properties>\n\n\n    <dependencyManagement>\n        <dependencies>\n            <!-- Core -->\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt</artifactId>\n                <version>0.14.0-SNAPSHOT</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-api</artifactId>\n                <version>0.14.0-SNAPSHOT</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-impl</artifactId>\n                <version>0.14.0-SNAPSHOT</version>\n            </dependency>\n\n            <!-- Extensions -->\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-orgjson</artifactId>\n                <version>0.14.0-SNAPSHOT</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-gson</artifactId>\n                <version>0.14.0-SNAPSHOT</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-jackson</artifactId>\n                <version>0.14.0-SNAPSHOT</version>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n</project>\n"
  },
  {
    "path": "extensions/gson/bnd.bnd",
    "content": "Fragment-Host: io.jsonwebtoken.jjwt-api \n"
  },
  {
    "path": "extensions/gson/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2018 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt-gson</artifactId>\n    <name>JJWT :: Extensions :: Gson</name>\n    <packaging>jar</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/../..</jjwt.root>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.google.code.gson</groupId>\n            <artifactId>gson</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <plugin>\n                <groupId>com.github.siom79.japicmp</groupId>\n                <artifactId>japicmp-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n\n</project>"
  },
  {
    "path": "extensions/gson/src/main/java/io/jsonwebtoken/gson/io/GsonDeserializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.gson.io;\n\nimport com.google.gson.Gson;\nimport io.jsonwebtoken.io.AbstractDeserializer;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.Reader;\n\npublic class GsonDeserializer<T> extends AbstractDeserializer<T> {\n\n    private final Class<T> returnType;\n    protected final Gson gson;\n\n    public GsonDeserializer() {\n        this(GsonSerializer.DEFAULT_GSON);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public GsonDeserializer(Gson gson) {\n        this(gson, (Class<T>) Object.class);\n    }\n\n    private GsonDeserializer(Gson gson, Class<T> returnType) {\n        Assert.notNull(gson, \"gson cannot be null.\");\n        Assert.notNull(returnType, \"Return type cannot be null.\");\n        this.gson = gson;\n        this.returnType = returnType;\n    }\n\n    @Override\n    protected T doDeserialize(Reader reader) {\n        return gson.fromJson(reader, returnType);\n    }\n}\n"
  },
  {
    "path": "extensions/gson/src/main/java/io/jsonwebtoken/gson/io/GsonSerializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.gson.io;\n\nimport com.google.gson.Gson;\nimport com.google.gson.GsonBuilder;\nimport com.google.gson.ToNumberPolicy;\nimport io.jsonwebtoken.io.AbstractSerializer;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Supplier;\n\nimport java.io.OutputStream;\nimport java.io.OutputStreamWriter;\nimport java.io.Writer;\nimport java.nio.charset.StandardCharsets;\n\npublic class GsonSerializer<T> extends AbstractSerializer<T> {\n\n    static final Gson DEFAULT_GSON = new GsonBuilder()\n            .setObjectToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)\n            .setNumberToNumberStrategy(ToNumberPolicy.LONG_OR_DOUBLE)\n            .registerTypeHierarchyAdapter(Supplier.class, GsonSupplierSerializer.INSTANCE)\n            .disableHtmlEscaping().create();\n\n    protected final Gson gson;\n\n    public GsonSerializer() {\n        this(DEFAULT_GSON);\n    }\n\n    public GsonSerializer(Gson gson) {\n        Assert.notNull(gson, \"gson cannot be null.\");\n        this.gson = gson;\n\n        //ensure the necessary type adapter has been registered, and if not, throw an error:\n        String json = this.gson.toJson(TestSupplier.INSTANCE);\n        if (json.contains(\"value\")) {\n            String msg = \"Invalid Gson instance - it has not been registered with the necessary \" +\n                    Supplier.class.getName() + \" type adapter.  When using the GsonBuilder, ensure this \" +\n                    \"type adapter is registered by calling gsonBuilder.registerTypeHierarchyAdapter(\" +\n                    Supplier.class.getName() + \".class, \" +\n                    GsonSupplierSerializer.class.getName() + \".INSTANCE) before calling gsonBuilder.create()\";\n            throw new IllegalArgumentException(msg);\n        }\n    }\n\n    @Override\n    protected void doSerialize(T t, OutputStream out) {\n        Writer writer = new OutputStreamWriter(out, StandardCharsets.UTF_8);\n        try {\n            Object o = t;\n            if (o instanceof byte[]) {\n                o = Encoders.BASE64.encode((byte[]) o);\n            } else if (o instanceof char[]) {\n                o = new String((char[]) o);\n            }\n            writeValue(o, writer);\n        } finally {\n            Objects.nullSafeClose(writer);\n        }\n    }\n\n    protected void writeValue(Object o, java.io.Writer writer) {\n        this.gson.toJson(o, writer);\n    }\n\n    private static class TestSupplier<T> implements Supplier<T> {\n\n        private static final TestSupplier<String> INSTANCE = new TestSupplier<>(\"test\");\n        private final T value;\n\n        private TestSupplier(T value) {\n            this.value = value;\n        }\n\n        @Override\n        public T get() {\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "extensions/gson/src/main/java/io/jsonwebtoken/gson/io/GsonSupplierSerializer.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.gson.io;\n\nimport com.google.gson.JsonElement;\nimport com.google.gson.JsonSerializationContext;\nimport com.google.gson.JsonSerializer;\nimport io.jsonwebtoken.lang.Supplier;\n\nimport java.lang.reflect.Type;\n\npublic final class GsonSupplierSerializer implements JsonSerializer<Supplier<?>> {\n\n    public static final GsonSupplierSerializer INSTANCE = new GsonSupplierSerializer();\n\n    @Override\n    public JsonElement serialize(Supplier<?> supplier, Type type, JsonSerializationContext ctx) {\n        Object value = supplier.get();\n        return ctx.serialize(value);\n    }\n}\n"
  },
  {
    "path": "extensions/gson/src/main/resources/META-INF/services/io.jsonwebtoken.io.Deserializer",
    "content": "io.jsonwebtoken.gson.io.GsonDeserializer"
  },
  {
    "path": "extensions/gson/src/main/resources/META-INF/services/io.jsonwebtoken.io.Serializer",
    "content": "io.jsonwebtoken.gson.io.GsonSerializer"
  },
  {
    "path": "extensions/gson/src/test/groovy/io/jsonwebtoken/gson/io/GsonDeserializerTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//file:noinspection GrDeprecatedAPIUsage\npackage io.jsonwebtoken.gson.io\n\nimport com.google.gson.Gson\nimport io.jsonwebtoken.io.DeserializationException\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass GsonDeserializerTest {\n\n    private GsonDeserializer deserializer\n\n    private def deser(byte[] data) {\n        def ins = new ByteArrayInputStream(data)\n        def reader = new InputStreamReader(ins, Strings.UTF_8)\n        deserializer.deserialize(reader)\n    }\n\n    private def deser(String s) {\n        return deser(Strings.utf8(s))\n    }\n\n    @Before\n    void setUp() {\n        deserializer = new GsonDeserializer()\n    }\n\n    @Test\n    void loadService() {\n        def deserializer = ServiceLoader.load(Deserializer).iterator().next()\n        assertTrue deserializer instanceof GsonDeserializer\n    }\n\n    @Test\n    void testDefaultConstructor() {\n        assertNotNull deserializer.gson\n    }\n\n    @Test\n    void testGsonConstructor() {\n        def customGSON = new Gson()\n        deserializer = new GsonDeserializer(customGSON)\n        assertSame customGSON, deserializer.gson\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testGsonConstructorNullArgument() {\n        new GsonDeserializer(null)\n    }\n\n    @Test\n    void testDeserialize() {\n        def expected = [hello: '世界']\n        assertEquals expected, deser('{\"hello\":\"世界\"}')\n    }\n\n    @Test\n    void testDeserializeThrows() {\n        def ex = new IOException('foo')\n        deserializer = new GsonDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                throw ex\n            }\n        }\n        try {\n            deser('{\"hello\":\"世界\"}')\n            fail()\n        } catch (DeserializationException expected) {\n            String msg = 'Unable to deserialize: foo'\n            assertEquals msg, expected.message\n            assertSame ex, expected.cause\n        }\n    }\n\n    @Test\n    void testLong() {\n        def json = '{\"hello\":42}'\n        def m = deser(json) as Map\n        def val = m.hello\n        assertTrue val instanceof Long\n        assertEquals 42L, val\n    }\n\n    @Test\n    void testDouble() {\n        // one more than Long can handle:\n        def dval = 42.0 as double\n        def json = '{\"hello\":' + dval + '}'\n        def m = deser(json) as Map\n        def val = m.hello\n        assertTrue val instanceof Double\n        assertEquals(dval, ((Double) val).doubleValue(), 0)\n    }\n}\n"
  },
  {
    "path": "extensions/gson/src/test/groovy/io/jsonwebtoken/gson/io/GsonSerializerTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//file:noinspection GrDeprecatedAPIUsage\npackage io.jsonwebtoken.gson.io\n\nimport com.google.gson.Gson\nimport com.google.gson.GsonBuilder\nimport io.jsonwebtoken.io.SerializationException\nimport io.jsonwebtoken.io.Serializer\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.lang.Supplier\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass GsonSerializerTest {\n\n    private GsonSerializer s\n\n    @Before\n    void setUp() {\n        s = new GsonSerializer()\n    }\n\n    private String ser(Object o) {\n        return Strings.utf8(s.serialize(o))\n    }\n\n    @Test\n    void loadService() {\n        def serializer = ServiceLoader.load(Serializer).iterator().next()\n        assert serializer instanceof GsonSerializer\n    }\n\n    @Test\n    void testDefaultConstructor() {\n        assertNotNull s.gson\n    }\n\n    @Test\n    void testGsonConstructor() {\n        def customGSON = new GsonBuilder()\n                .registerTypeHierarchyAdapter(Supplier.class, GsonSupplierSerializer.INSTANCE)\n                .disableHtmlEscaping().create()\n        s = new GsonSerializer(customGSON)\n        assertSame customGSON, s.gson\n    }\n\n    @Test\n    void testSerialize() {\n        assertEquals '\"hello\"', ser('hello')\n    }\n\n    private byte[] bytes(def o) {\n        ByteArrayOutputStream out = new ByteArrayOutputStream()\n        s.serialize(o, out)\n        return out.toByteArray()\n    }\n\n    private String json(def o) {\n        return Strings.utf8(bytes(o))\n    }\n\n    @Test\n    void testGsonConstructorWithIncorrectlyConfiguredGson() {\n        try {\n            //noinspection GroovyResultOfObjectAllocationIgnored\n            new GsonSerializer<>(new Gson())\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = 'Invalid Gson instance - it has not been registered with the necessary ' +\n                    'io.jsonwebtoken.lang.Supplier type adapter.  When using the GsonBuilder, ensure this type ' +\n                    'adapter is registered by calling ' +\n                    'gsonBuilder.registerTypeHierarchyAdapter(io.jsonwebtoken.lang.Supplier.class, ' +\n                    'io.jsonwebtoken.gson.io.GsonSupplierSerializer.INSTANCE) before calling gsonBuilder.create()'\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testConstructorWithNullArgument() {\n        new GsonSerializer<>(null)\n    }\n\n    @Test\n    void testByte() {\n        byte[] expected = Strings.utf8(\"120\") //ascii(\"x\") = 120\n        byte[] result = bytes(Strings.utf8(\"x\")[0]) //single byte\n        assertArrayEquals expected, result\n    }\n\n    @Test\n    void testByteArray() { //expect Base64 string by default:\n        String expected = '\"aGk=\"' as String //base64(hi) --> aGk=\n        assertEquals expected, json(Strings.utf8('hi'))\n    }\n\n    @Test\n    void testEmptyByteArray() { //expect Base64 string by default:\n        byte[] result = bytes(new byte[0])\n        assertEquals '\"\"', Strings.utf8(result)\n    }\n\n    @Test\n    void testChar() { //expect Base64 string by default:\n        assertEquals '\"h\"', json('h' as char)\n    }\n\n    @Test\n    void testCharArray() { //expect string by default:\n        assertEquals '\"hi\"', json('hi'.toCharArray())\n    }\n\n    @Test\n    void testWrite() {\n        assertEquals '{\"hello\":\"世界\"}', json([hello: '世界'])\n    }\n\n    @Test\n    void testWriteFailure() {\n        def ex = new IOException('foo')\n        s = new GsonSerializer() {\n            @Override\n            protected void doSerialize(Object o, OutputStream out) {\n                throw ex\n            }\n        }\n        try {\n            ser([hello: 'world'])\n            fail()\n        } catch (SerializationException expected) {\n            String msg = 'Unable to serialize object of type java.util.LinkedHashMap: foo'\n            assertEquals msg, expected.message\n            assertSame ex, expected.cause\n        }\n    }\n\n    @Test\n    void testIOExceptionConvertedToSerializationException() {\n        def ex = new IOException('foo')\n        s = new GsonSerializer() {\n            @Override\n            protected void doSerialize(Object o, OutputStream out) {\n                throw ex\n            }\n        }\n        try {\n            ser(new Object())\n            fail()\n        } catch (SerializationException expected) {\n            String causeMsg = 'foo'\n            String msg = \"Unable to serialize object of type java.lang.Object: $causeMsg\"\n            assertEquals causeMsg, expected.cause.message\n            assertEquals msg, expected.message\n        }\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/bnd.bnd",
    "content": "Fragment-Host: io.jsonwebtoken.jjwt-api \n"
  },
  {
    "path": "extensions/jackson/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2018 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt-jackson</artifactId>\n    <name>JJWT :: Extensions :: Jackson</name>\n    <packaging>jar</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/../..</jjwt.root>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>com.fasterxml.jackson.core</groupId>\n            <artifactId>jackson-databind</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- The following plugin section is used in jjwt-jackson and jjwt-orgjson, to repackage (and verify)\n                 binary compatibility with previous versions. In v0.11.0 the implementations changed packages to\n                 avoid split package issues with Java 9+ see: https://github.com/jwtk/jjwt/issues/399 -->\n            <!-- TODO: remove these deprecated packages and this config before v1.0 -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-shade-plugin</artifactId>\n                <configuration>\n                    <relocations>\n                        <relocation>\n                            <pattern>io.jsonwebtoken.jackson.io</pattern>\n                            <shadedPattern>io.jsonwebtoken.io</shadedPattern>\n                            <includes>io.jsonwebtoken.jackson.io.*</includes>\n                        </relocation>\n                    </relocations>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>com.github.siom79.japicmp</groupId>\n                <artifactId>japicmp-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonDeserializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.jackson.io;\n\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationContext;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.deser.std.UntypedObjectDeserializer;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport io.jsonwebtoken.io.AbstractDeserializer;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.util.Collections;\nimport java.util.Map;\n\n/**\n * Deserializer using a Jackson {@link ObjectMapper}.\n *\n * @since 0.10.0\n */\npublic class JacksonDeserializer<T> extends AbstractDeserializer<T> {\n\n    private final Class<T> returnType;\n\n    private final ObjectMapper objectMapper;\n\n    /**\n     * Constructor using JJWT's default {@link ObjectMapper} singleton for deserialization.\n     */\n    public JacksonDeserializer() {\n        this(JacksonSerializer.DEFAULT_OBJECT_MAPPER);\n    }\n\n    /**\n     * Creates a new JacksonDeserializer where the values of the claims can be parsed into given types. A common usage\n     * example is to parse custom User object out of a claim, for example the claims:\n     * <pre>{@code\n     * {\n     *     \"issuer\": \"https://issuer.example.com\",\n     *     \"user\": {\n     *         \"firstName\": \"Jill\",\n     *         \"lastName\": \"Coder\"\n     *     }\n     * }}</pre>\n     * Passing a map of {@code [\"user\": User.class]} to this constructor would result in the {@code user} claim being\n     * transformed to an instance of your custom {@code User} class, instead of the default of {@code Map}.\n     * <p>\n     * Because custom type parsing requires modifying the state of a Jackson {@code ObjectMapper}, this\n     * constructor creates a new internal {@code ObjectMapper} instance and customizes it to support the\n     * specified {@code claimTypeMap}.  This ensures that the JJWT parsing behavior does not unexpectedly\n     * modify the state of another application-specific {@code ObjectMapper}.\n     * <p>\n     * If you would like to use your own {@code ObjectMapper} instance that also supports custom types for\n     * JWT {@code Claims}, you will need to first customize your {@code ObjectMapper} instance by registering\n     * your custom types and then use the {@link #JacksonDeserializer(ObjectMapper)} constructor instead.\n     *\n     * @param claimTypeMap The claim name-to-class map used to deserialize claims into the given type\n     */\n    public JacksonDeserializer(Map<String, Class<?>> claimTypeMap) {\n        // DO NOT specify JacksonSerializer.DEFAULT_OBJECT_MAPPER here as that would modify the shared instance\n        this(JacksonSerializer.newObjectMapper(), claimTypeMap);\n    }\n\n    /**\n     * Constructor using the specified Jackson {@link ObjectMapper}.\n     *\n     * @param objectMapper the ObjectMapper to use for deserialization.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public JacksonDeserializer(ObjectMapper objectMapper) {\n        this(objectMapper, (Class<T>) Object.class);\n    }\n\n    /**\n     * Creates a new JacksonDeserializer where the values of the claims can be parsed into given types by registering\n     * a type-converting {@link  com.fasterxml.jackson.databind.Module Module} on the specified {@link ObjectMapper}.\n     * A common usage example is to parse custom User object out of a claim, for example the claims:\n     * <pre>{@code\n     * {\n     *     \"issuer\": \"https://issuer.example.com\",\n     *     \"user\": {\n     *         \"firstName\": \"Jill\",\n     *         \"lastName\": \"Coder\"\n     *     }\n     * }}</pre>\n     * Passing a map of {@code [\"user\": User.class]} to this constructor would result in the {@code user} claim being\n     * transformed to an instance of your custom {@code User} class, instead of the default of {@code Map}.\n     * <p>\n     * Because custom type parsing requires modifying the state of a Jackson {@code ObjectMapper}, this\n     * constructor modifies the specified {@code objectMapper} argument and customizes it to support the\n     * specified {@code claimTypeMap}.\n     * <p>\n     * If you do not want your {@code ObjectMapper} instance modified, but also want to support custom types for\n     * JWT {@code Claims}, you will need to first customize your {@code ObjectMapper} instance by registering\n     * your custom types separately and then use the {@link #JacksonDeserializer(ObjectMapper)} constructor instead\n     * (which does not modify the {@code objectMapper} argument).\n     *\n     * @param objectMapper the objectMapper to modify by registering a custom type-converting\n     *                     {@link com.fasterxml.jackson.databind.Module Module}\n     * @param claimTypeMap The claim name-to-class map used to deserialize claims into the given type\n     * @since 0.13.0\n     */\n    public JacksonDeserializer(ObjectMapper objectMapper, Map<String, Class<?>> claimTypeMap) {\n        this(objectMapper);\n        Assert.notNull(claimTypeMap, \"Claim type map cannot be null.\");\n        // register a new Deserializer on the ObjectMapper instance:\n        SimpleModule module = new SimpleModule();\n        module.addDeserializer(Object.class, new MappedTypeDeserializer(Collections.unmodifiableMap(claimTypeMap)));\n        objectMapper.registerModule(module);\n    }\n\n    private JacksonDeserializer(ObjectMapper objectMapper, Class<T> returnType) {\n        Assert.notNull(objectMapper, \"ObjectMapper cannot be null.\");\n        Assert.notNull(returnType, \"Return type cannot be null.\");\n        this.objectMapper = objectMapper;\n        this.returnType = returnType;\n    }\n\n    @Override\n    protected T doDeserialize(Reader reader) throws Exception {\n        return objectMapper.readValue(reader, returnType);\n    }\n\n    /**\n     * A Jackson {@link com.fasterxml.jackson.databind.JsonDeserializer JsonDeserializer}, that will convert claim\n     * values to types based on {@code claimTypeMap}.\n     */\n    private static class MappedTypeDeserializer extends UntypedObjectDeserializer {\n\n        private final Map<String, Class<?>> claimTypeMap;\n\n        private MappedTypeDeserializer(Map<String, Class<?>> claimTypeMap) {\n            super(null, null);\n            this.claimTypeMap = claimTypeMap;\n        }\n\n        @Override\n        public Object deserialize(JsonParser parser, DeserializationContext context) throws IOException {\n            // check if the current claim key is mapped, if so traverse it's value\n            String name = parser.currentName();\n            if (claimTypeMap != null && name != null && claimTypeMap.containsKey(name)) {\n                Class<?> type = claimTypeMap.get(name);\n                //noinspection resource\n                return parser.readValueAsTree().traverse(parser.getCodec()).readValueAs(type);\n            }\n            // otherwise default to super\n            return super.deserialize(parser, context);\n        }\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonSerializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.jackson.io;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.core.JsonParser;\nimport com.fasterxml.jackson.databind.DeserializationFeature;\nimport com.fasterxml.jackson.databind.Module;\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport com.fasterxml.jackson.databind.ObjectWriter;\nimport com.fasterxml.jackson.databind.module.SimpleModule;\nimport io.jsonwebtoken.io.AbstractSerializer;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.OutputStream;\n\n/**\n * Serializer using a Jackson {@link ObjectMapper}.\n *\n * @since 0.10.0\n */\npublic class JacksonSerializer<T> extends AbstractSerializer<T> {\n\n    static final String MODULE_ID = \"jjwt-jackson\";\n    static final Module MODULE;\n\n    static {\n        SimpleModule module = new SimpleModule(MODULE_ID);\n        module.addSerializer(JacksonSupplierSerializer.INSTANCE);\n        MODULE = module;\n    }\n\n    static final ObjectMapper DEFAULT_OBJECT_MAPPER = newObjectMapper();\n\n    /**\n     * Creates and returns a new ObjectMapper with the {@code jjwt-jackson} module registered and\n     * {@code JsonParser.Feature.STRICT_DUPLICATE_DETECTION} enabled (set to true) and\n     * {@code DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES} disabled (set to false).\n     *\n     * @return a new ObjectMapper with the {@code jjwt-jackson} module registered and\n     * {@code JsonParser.Feature.STRICT_DUPLICATE_DETECTION} enabled (set to true) and\n     * {@code DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES} disabled (set to false).\n     *\n     * @since 0.12.4\n     */\n    // package protected on purpose, do not expose to the public API\n    static ObjectMapper newObjectMapper() {\n        return new ObjectMapper()\n                .registerModule(MODULE)\n                .configure(JsonParser.Feature.STRICT_DUPLICATE_DETECTION, true) // https://github.com/jwtk/jjwt/issues/877\n                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); // https://github.com/jwtk/jjwt/issues/893\n    }\n\n    protected final ObjectMapper objectMapper;\n\n    /**\n     * Constructor using JJWT's default {@link ObjectMapper} singleton for serialization.\n     */\n    public JacksonSerializer() {\n        this(DEFAULT_OBJECT_MAPPER);\n    }\n\n    /**\n     * Creates a new Jackson Serializer that uses the specified {@link ObjectMapper} for serialization.\n     *\n     * @param objectMapper the ObjectMapper to use for serialization.\n     */\n    public JacksonSerializer(ObjectMapper objectMapper) {\n        Assert.notNull(objectMapper, \"ObjectMapper cannot be null.\");\n        this.objectMapper = objectMapper.registerModule(MODULE);\n    }\n\n    @Override\n    protected void doSerialize(T t, OutputStream out) throws Exception {\n        Assert.notNull(out, \"OutputStream cannot be null.\");\n        ObjectWriter writer = this.objectMapper.writer().without(JsonGenerator.Feature.AUTO_CLOSE_TARGET);\n        writer.writeValue(out, t);\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/src/main/java/io/jsonwebtoken/jackson/io/JacksonSupplierSerializer.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.jackson.io;\n\nimport com.fasterxml.jackson.core.JsonGenerator;\nimport com.fasterxml.jackson.databind.JsonSerializer;\nimport com.fasterxml.jackson.databind.SerializerProvider;\nimport com.fasterxml.jackson.databind.ser.std.StdSerializer;\nimport io.jsonwebtoken.lang.Supplier;\n\nimport java.io.IOException;\n\nfinal class JacksonSupplierSerializer extends StdSerializer<Supplier<?>> {\n\n    static final JacksonSupplierSerializer INSTANCE = new JacksonSupplierSerializer();\n\n    public JacksonSupplierSerializer() {\n        super(Supplier.class, false);\n    }\n\n    @Override\n    public void serialize(Supplier<?> supplier, JsonGenerator generator, SerializerProvider provider) throws IOException {\n        Object value = supplier.get();\n\n        if (value == null) {\n            provider.defaultSerializeNull(generator);\n            return;\n        }\n\n        Class<?> clazz = value.getClass();\n        JsonSerializer<Object> ser = provider.findTypedValueSerializer(clazz, true, null);\n        ser.serialize(value, generator, provider);\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/src/main/resources/META-INF/services/io.jsonwebtoken.io.Deserializer",
    "content": "io.jsonwebtoken.jackson.io.JacksonDeserializer"
  },
  {
    "path": "extensions/jackson/src/main/resources/META-INF/services/io.jsonwebtoken.io.Serializer",
    "content": "io.jsonwebtoken.jackson.io.JacksonSerializer"
  },
  {
    "path": "extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonDeserializerTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//file:noinspection GrDeprecatedAPIUsage\npackage io.jsonwebtoken.jackson.io\n\nimport com.fasterxml.jackson.core.JsonParseException\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport io.jsonwebtoken.io.DeserializationException\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.jackson.io.stubs.CustomBean\nimport io.jsonwebtoken.lang.Maps\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass JacksonDeserializerTest {\n\n    private JacksonDeserializer deserializer\n\n    @Before\n    void setUp() {\n        deserializer = new JacksonDeserializer()\n    }\n\n    @Test\n    void loadService() {\n        def deserializer = ServiceLoader.load(Deserializer).iterator().next()\n        assertTrue deserializer instanceof JacksonDeserializer\n    }\n\n    @Test\n    void testDefaultConstructor() {\n        assertSame JacksonSerializer.DEFAULT_OBJECT_MAPPER, deserializer.objectMapper\n    }\n\n    @Test\n    void testObjectMapperConstructor() {\n        def customOM = new ObjectMapper()\n        deserializer = new JacksonDeserializer<>(customOM)\n        assertSame customOM, deserializer.objectMapper\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testObjectMapperConstructorWithNullArgument() {\n        new JacksonDeserializer<>((ObjectMapper) null)\n    }\n\n    @Test\n    void testDeserialize() {\n        def reader = new StringReader('{\"hello\":\"世界\"}')\n        def expected = [hello: '世界']\n        def result = deserializer.deserialize(reader)\n        assertEquals expected, result\n    }\n\n    @Test\n    void testDeserializeWithCustomObject() {\n\n        long currentTime = System.currentTimeMillis()\n\n        String json = \"\"\"\n             {\n                \"oneKey\":\"oneValue\", \n                \"custom\": {\n                    \"stringValue\": \"s-value\",\n                    \"intValue\": \"11\",\n                    \"dateValue\": ${currentTime},\n                    \"shortValue\": 22,\n                    \"longValue\": 33,\n                    \"byteValue\": 15,\n                    \"byteArrayValue\": \"${base64('bytes')}\",\n                    \"nestedValue\": {\n                        \"stringValue\": \"nested-value\",\n                        \"intValue\": \"111\",\n                        \"dateValue\": ${currentTime + 1},\n                        \"shortValue\": 222,\n                        \"longValue\": 333,\n                        \"byteValue\": 10,\n                        \"byteArrayValue\": \"${base64('bytes2')}\"\n                    }\n                }\n            }\n            \"\"\"\n\n        CustomBean expectedCustomBean = new CustomBean()\n                .setByteArrayValue(\"bytes\".getBytes(\"UTF-8\"))\n                .setByteValue(0xF as byte)\n                .setDateValue(new Date(currentTime))\n                .setIntValue(11)\n                .setShortValue(22 as short)\n                .setLongValue(33L)\n                .setStringValue(\"s-value\")\n                .setNestedValue(new CustomBean()\n                        .setByteArrayValue(\"bytes2\".getBytes(\"UTF-8\"))\n                        .setByteValue(0xA as byte)\n                        .setDateValue(new Date(currentTime + 1))\n                        .setIntValue(111)\n                        .setShortValue(222 as short)\n                        .setLongValue(333L)\n                        .setStringValue(\"nested-value\")\n                )\n\n        def expected = [oneKey: \"oneValue\", custom: expectedCustomBean]\n        def result = new JacksonDeserializer(Maps.of(\"custom\", CustomBean).build())\n                .deserialize(new StringReader(json))\n        assertEquals expected, result\n    }\n\n    /**\n     * Asserts https://github.com/jwtk/jjwt/issues/877\n     * @since 0.12.4\n     */\n    @Test\n    void testStrictDuplicateDetection() {\n        // 'bKey' is repeated twice:\n        String json = \"\"\"\n             {\n                \"aKey\":\"oneValue\", \n                \"bKey\": 15,\n                \"bKey\": \"hello\"\n             }\n            \"\"\"\n        try {\n            new JacksonDeserializer<>().deserialize(new StringReader(json))\n            fail()\n        } catch (DeserializationException expected) {\n            String causeMsg = \"Duplicate field 'bKey'\\n at [Source: (StringReader); line: 5, column: 23]\"\n            String msg = \"Unable to deserialize: $causeMsg\"\n            assertEquals msg, expected.getMessage()\n            assertTrue expected.getCause() instanceof JsonParseException\n            assertEquals causeMsg, expected.getCause().getMessage()\n        }\n    }\n\n    /**\n     * Asserts https://github.com/jwtk/jjwt/issues/893\n     */\n    @Test\n    void testIgnoreUnknownPropertiesWhenDeserializeWithCustomObject() {\n        \n        long currentTime = System.currentTimeMillis()\n\n        String json = \"\"\"\n             {\n                \"oneKey\":\"oneValue\", \n                \"custom\": {\n                    \"stringValue\": \"s-value\",\n                    \"intValue\": \"11\",\n                    \"dateValue\": ${currentTime},\n                    \"shortValue\": 22,\n                    \"longValue\": 33,\n                    \"byteValue\": 15,\n                    \"byteArrayValue\": \"${base64('bytes')}\",\n                    \"unknown\": \"unknown\",\n                    \"nestedValue\": {\n                        \"stringValue\": \"nested-value\",\n                        \"intValue\": \"111\",\n                        \"dateValue\": ${currentTime + 1},\n                        \"shortValue\": 222,\n                        \"longValue\": 333,\n                        \"byteValue\": 10,\n                        \"byteArrayValue\": \"${base64('bytes2')}\",\n                        \"unknown\": \"unknown\"\n                    }\n                }\n            }\n            \"\"\"\n\n        CustomBean expectedCustomBean = new CustomBean()\n                .setByteArrayValue(\"bytes\".getBytes(\"UTF-8\"))\n                .setByteValue(0xF as byte)\n                .setDateValue(new Date(currentTime))\n                .setIntValue(11)\n                .setShortValue(22 as short)\n                .setLongValue(33L)\n                .setStringValue(\"s-value\")\n                .setNestedValue(new CustomBean()\n                        .setByteArrayValue(\"bytes2\".getBytes(\"UTF-8\"))\n                        .setByteValue(0xA as byte)\n                        .setDateValue(new Date(currentTime + 1))\n                        .setIntValue(111)\n                        .setShortValue(222 as short)\n                        .setLongValue(333L)\n                        .setStringValue(\"nested-value\")\n                )\n\n        def expected = [oneKey: \"oneValue\", custom: expectedCustomBean]\n        def result = new JacksonDeserializer(Maps.of(\"custom\", CustomBean).build())\n                .deserialize(new StringReader(json))\n        assertEquals expected, result\n    }\n\n    /**\n     * For: https://github.com/jwtk/jjwt/issues/564\n     */\n    @Test\n    void testMappedTypeDeserializerWithMapNullCheck() {\n\n        // mimic map implementations that do NOT allow for null keys, or containsKey(null)\n        Map typeMap = new HashMap() {\n            @Override\n            boolean containsKey(Object key) {\n                if (key == null) {\n                    throw new NullPointerException(\"key is null, expected for this test\")\n                }\n                return super.containsKey(key)\n            }\n        }\n\n        // TODO: the following does NOT work with Java 1.7\n        // when we stop supporting that version we can use a partial mock instead\n        // the `typeMap.put(\"custom\", CustomBean)` line below results in an NPE, (only on 1.7)\n\n//        Map typeMap = partialMockBuilder(HashMap)\n//            .addMockedMethod(\"containsKey\")\n//            .createNiceMock()\n//\n//        expect(typeMap.containsKey(null)).andThrow(new NullPointerException(\"key is null, expected for this test\"))\n//        replay(typeMap)\n\n        typeMap.put(\"custom\", CustomBean)\n\n        def deserializer = new JacksonDeserializer(typeMap)\n        def reader = new StringReader('{\"alg\":\"HS256\"}')\n        def result = deserializer.deserialize(reader)\n        assertEquals([\"alg\": \"HS256\"], result)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testNullClaimTypeMap() {\n        new JacksonDeserializer((Map) null)\n    }\n\n    @Test\n    void testDeserializeFailsWithException() {\n\n        def ex = new IOException('foo')\n\n        deserializer = new JacksonDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                throw ex\n            }\n        }\n        try {\n            deserializer.deserialize(new StringReader('{\"hello\":\"世界\"}'))\n            fail()\n        } catch (DeserializationException se) {\n            String msg = 'Unable to deserialize: foo'\n            assertEquals msg, se.getMessage()\n            assertSame ex, se.getCause()\n        }\n    }\n\n    private static String base64(String input) {\n        return Encoders.BASE64.encode(input.getBytes('UTF-8'))\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonSerializerTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.jackson.io\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport io.jsonwebtoken.io.Serializer\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass JacksonSerializerTest {\n\n    private JacksonSerializer ser\n\n    @Before\n    void setUp() {\n        ser = new JacksonSerializer()\n    }\n\n    byte[] serialize(def value) {\n        def os = new ByteArrayOutputStream()\n        ser.serialize(value, os)\n        return os.toByteArray()\n    }\n\n    @Test\n    void loadService() {\n        def serializer = ServiceLoader.load(Serializer).iterator().next()\n        assertTrue serializer instanceof JacksonSerializer\n    }\n\n    @Test\n    void testDefaultConstructor() {\n        assertSame JacksonSerializer.DEFAULT_OBJECT_MAPPER, ser.objectMapper\n    }\n\n    @Test\n    void testObjectMapperConstructor() {\n        ObjectMapper customOM = new ObjectMapper()\n        ser = new JacksonSerializer(customOM)\n        assertSame customOM, ser.objectMapper\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testObjectMapperConstructorWithNullArgument() {\n        new JacksonSerializer<>(null)\n    }\n\n    @Test\n    void testObjectMapperConstructorAutoRegistersModule() {\n        ObjectMapper om = createMock(ObjectMapper)\n        expect(om.registerModule(same(JacksonSerializer.MODULE))).andReturn(om)\n        replay om\n        //noinspection GroovyResultOfObjectAllocationIgnored\n        new JacksonSerializer<>(om)\n        verify om\n    }\n\n    @Test\n    void testSerialize() {\n        byte[] expected = '{\"hello\":\"世界\"}'.getBytes(Strings.UTF_8)\n        byte[] result = ser.serialize([hello: '世界'])\n        assertTrue Arrays.equals(expected, result)\n    }\n\n    @Test\n    void testByte() {\n        byte[] expected = Strings.utf8(\"120\") //ascii(\"x\") = 120\n        byte[] bytes = Strings.utf8(\"x\")\n        assertArrayEquals expected, serialize(bytes[0]) // single byte\n    }\n\n    @Test\n    void testByteArray() { //expect Base64 string by default:\n        byte[] bytes = Strings.utf8(\"hi\")\n        String expected = '\"aGk=\"' as String //base64(hi) --> aGk=\n        assertEquals expected, Strings.utf8(serialize(bytes))\n    }\n\n    @Test\n    void testEmptyByteArray() { //expect Base64 string by default:\n        byte[] bytes = new byte[0]\n        byte[] result = serialize(bytes)\n        assertEquals '\"\"', Strings.utf8(result)\n    }\n\n    @Test\n    void testChar() { //expect Base64 string by default:\n        byte[] result = serialize('h' as char)\n        assertEquals \"\\\"h\\\"\", Strings.utf8(result)\n    }\n\n    @Test\n    void testCharArray() { //expect Base64 string by default:\n        byte[] result = serialize('hi'.toCharArray())\n        assertEquals \"\\\"hi\\\"\", Strings.utf8(result)\n    }\n\n    @Test\n    void testWriteObject() {\n        byte[] expected = Strings.utf8('{\"hello\":\"世界\"}' as String)\n        byte[] result = serialize([hello: '世界'])\n        assertArrayEquals expected, result\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/JacksonSupplierSerializerTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.jackson.io\n\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.lang.Supplier\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass JacksonSupplierSerializerTest {\n\n    @Test\n    void testSupplierNullValue() {\n        def serializer = new JacksonSerializer()\n        def supplier = new Supplier() {\n            @Override\n            Object get() {\n                return null\n            }\n        }\n        ByteArrayOutputStream out = new ByteArrayOutputStream()\n        serializer.serialize(supplier, out)\n        assertEquals 'null', Strings.utf8(out.toByteArray())\n    }\n\n    @Test\n    void testSupplierStringValue() {\n        def serializer = new JacksonSerializer()\n        def supplier = new Supplier() {\n            @Override\n            Object get() {\n                return 'hello'\n            }\n        }\n        ByteArrayOutputStream out = new ByteArrayOutputStream()\n        serializer.serialize(supplier, out)\n        assertEquals '\"hello\"', Strings.utf8(out.toByteArray())\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/TestSupplier.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.jackson.io\n\nimport io.jsonwebtoken.lang.Supplier\n\nclass TestSupplier<T> implements Supplier<T> {\n\n    private static final TestSupplier<String> INSTANCE = new TestSupplier<>(\"test\")\n    private final T value;\n\n    private TestSupplier(T value) {\n        this.value = value;\n    }\n\n    @Override\n    T get() {\n        return value;\n    }\n}\n"
  },
  {
    "path": "extensions/jackson/src/test/groovy/io/jsonwebtoken/jackson/io/stubs/CustomBean.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.jackson.io.stubs\n\nclass CustomBean {\n\n    private String stringValue\n    private int intValue\n    private Date dateValue\n    private short shortValue\n    private long longValue\n    private byte byteValue\n    private byte[] byteArrayValue\n    private CustomBean nestedValue\n\n    String getStringValue() {\n        return stringValue\n    }\n\n    CustomBean setStringValue(String stringValue) {\n        this.stringValue = stringValue\n        return this\n    }\n\n    int getIntValue() {\n        return intValue\n    }\n\n    CustomBean setIntValue(int intValue) {\n        this.intValue = intValue\n        return this\n    }\n\n    Date getDateValue() {\n        return dateValue\n    }\n\n    CustomBean setDateValue(Date dateValue) {\n        this.dateValue = dateValue\n        return this\n    }\n\n    short getShortValue() {\n        return shortValue\n    }\n\n    CustomBean setShortValue(short shortValue) {\n        this.shortValue = shortValue\n        return this\n    }\n\n    long getLongValue() {\n        return longValue\n    }\n\n    CustomBean setLongValue(long longValue) {\n        this.longValue = longValue\n        return this\n    }\n\n    byte getByteValue() {\n        return byteValue\n    }\n\n    CustomBean setByteValue(byte byteValue) {\n        this.byteValue = byteValue\n        return this\n    }\n\n    byte[] getByteArrayValue() {\n        return byteArrayValue\n    }\n\n    CustomBean setByteArrayValue(byte[] byteArrayValue) {\n        this.byteArrayValue = byteArrayValue\n        return this\n    }\n\n    CustomBean getNestedValue() {\n        return nestedValue\n    }\n\n    CustomBean setNestedValue(CustomBean nestedValue) {\n        this.nestedValue = nestedValue\n        return this\n    }\n\n    boolean equals(o) {\n        if (this.is(o)) return true\n        if (getClass() != o.class) return false\n\n        CustomBean that = (CustomBean) o\n\n        if (byteValue != that.byteValue) return false\n        if (intValue != that.intValue) return false\n        if (longValue != that.longValue) return false\n        if (shortValue != that.shortValue) return false\n        if (!Arrays.equals(byteArrayValue, that.byteArrayValue)) return false\n        if (dateValue != that.dateValue) return false\n        if (nestedValue != that.nestedValue) return false\n        if (stringValue != that.stringValue) return false\n\n        return true\n    }\n\n    int hashCode() {\n        int result\n        result = stringValue.hashCode()\n        result = 31 * result + intValue\n        result = 31 * result + dateValue.hashCode()\n        result = 31 * result + (int) shortValue\n        result = 31 * result + (int) (longValue ^ (longValue >>> 32))\n        result = 31 * result + (int) byteValue\n        result = 31 * result + Arrays.hashCode(byteArrayValue)\n        result = 31 * result + nestedValue.hashCode()\n        return result\n    }\n\n\n    @Override\n    String toString() {\n        return \"CustomBean{\" +\n                \"stringValue='\" + stringValue + '\\'' +\n                \", intValue=\" + intValue +\n                \", dateValue=\" + dateValue?.time +\n                \", shortValue=\" + shortValue +\n                \", longValue=\" + longValue +\n                \", byteValue=\" + byteValue +\n//                \", byteArrayValue=\" + Arrays.toString(byteArrayValue) +\n                \", nestedValue=\" + nestedValue +\n                '}'\n    }\n}\n"
  },
  {
    "path": "extensions/orgjson/bnd.bnd",
    "content": "Fragment-Host: io.jsonwebtoken.jjwt-api \n"
  },
  {
    "path": "extensions/orgjson/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2018 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt-orgjson</artifactId>\n    <name>JJWT :: Extensions :: org.json</name>\n    <packaging>jar</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/../..</jjwt.root>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>org.json</groupId>\n            <artifactId>json</artifactId>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <plugins>\n            <!-- The following plugin section is used in jjwt-jackson and jjwt-orgjson, to repackage (and verify)\n                 binary compatibility with previous versions. In v0.11.0 the implementations changed packages to\n                 avoid split package issues with Java 9+ see: https://github.com/jwtk/jjwt/issues/399 -->\n            <!-- TODO: remove these deprecated packages and this config before v1.0 -->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-shade-plugin</artifactId>\n                <configuration>\n                    <relocations>\n                        <relocation>\n                            <pattern>io.jsonwebtoken.orgjson.io</pattern>\n                            <shadedPattern>io.jsonwebtoken.io</shadedPattern>\n                            <includes>io.jsonwebtoken.orgjson.io.*</includes>\n                        </relocation>\n                    </relocations>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>com.github.siom79.japicmp</groupId>\n                <artifactId>japicmp-maven-plugin</artifactId>\n            </plugin>\n        </plugins>\n    </build>\n</project>"
  },
  {
    "path": "extensions/orgjson/src/main/java/io/jsonwebtoken/orgjson/io/OrgJsonDeserializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.orgjson.io;\n\nimport io.jsonwebtoken.io.AbstractDeserializer;\nimport io.jsonwebtoken.lang.Assert;\nimport org.json.JSONArray;\nimport org.json.JSONException;\nimport org.json.JSONObject;\nimport org.json.JSONTokener;\n\nimport java.io.CharArrayReader;\nimport java.io.IOException;\nimport java.io.Reader;\nimport java.util.ArrayList;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.List;\nimport java.util.Map;\n\n/**\n * @since 0.10.0\n */\npublic class OrgJsonDeserializer extends AbstractDeserializer<Object> {\n\n    private final JSONTokenerFactory TOKENER_FACTORY;\n\n    public OrgJsonDeserializer() {\n        this(JSONTokenerFactory.INSTANCE);\n    }\n\n    private OrgJsonDeserializer(JSONTokenerFactory factory) {\n        this.TOKENER_FACTORY = Assert.notNull(factory, \"JSONTokenerFactory cannot be null.\");\n    }\n\n    @Override\n    protected Object doDeserialize(Reader reader) {\n        return parse(reader);\n    }\n\n    private Object parse(Reader reader) throws JSONException {\n\n        JSONTokener tokener = this.TOKENER_FACTORY.newTokener(reader);\n        Assert.notNull(tokener, \"JSONTokener cannot be null.\");\n\n        char c = tokener.nextClean(); //peak ahead\n        tokener.back(); //revert\n\n        if (c == '{') { //json object\n            JSONObject o = new JSONObject(tokener);\n            return toMap(o);\n        } else if (c == '[') {\n            JSONArray a = new JSONArray(tokener);\n            return toList(a);\n        } else {\n            //raw json value\n            Object value = tokener.nextValue();\n            return convertIfNecessary(value);\n        }\n    }\n\n    private Map<String, Object> toMap(JSONObject o) {\n        Map<String, Object> map = new LinkedHashMap<>();\n        // https://github.com/jwtk/jjwt/issues/380: use .keys() and *not* .keySet() for Android compatibility:\n        Iterator<String> iterator = o.keys();\n        while (iterator.hasNext()) {\n            String key = iterator.next();\n            Object value = o.get(key);\n            value = convertIfNecessary(value);\n            map.put(key, value);\n        }\n        return map;\n    }\n\n    private List<Object> toList(JSONArray a) {\n        int length = a.length();\n        List<Object> list = new ArrayList<>(length);\n        // https://github.com/jwtk/jjwt/issues/380: use a.get(i) and *not* a.toList() for Android compatibility:\n        for (int i = 0; i < length; i++) {\n            Object value = a.get(i);\n            value = convertIfNecessary(value);\n            list.add(value);\n        }\n        return list;\n    }\n\n    private Object convertIfNecessary(Object v) {\n        Object value = v;\n        if (JSONObject.NULL.equals(value)) {\n            value = null;\n        } else if (value instanceof JSONArray) {\n            value = toList((JSONArray) value);\n        } else if (value instanceof JSONObject) {\n            value = toMap((JSONObject) value);\n        }\n        return value;\n    }\n\n    /**\n     * A factory to create {@link JSONTokener} instances from {@link Reader}s.\n     *\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/882\">JJWT Issue 882</a>.\n     * @since 0.12.4\n     */\n    static class JSONTokenerFactory { // package-protected on purpose. Not to be exposed as part of public API\n\n        private static final Reader TEST_READER = new CharArrayReader(\"{}\".toCharArray());\n\n        private static final JSONTokenerFactory INSTANCE = new JSONTokenerFactory();\n\n        private final boolean readerCtorAvailable;\n\n        // package protected visibility for testing only:\n        JSONTokenerFactory() {\n            boolean avail = true;\n            try {\n                testTokener(TEST_READER);\n            } catch (NoSuchMethodError err) {\n                avail = false;\n            }\n            this.readerCtorAvailable = avail;\n        }\n\n        // visible for testing only\n        protected void testTokener(@SuppressWarnings(\"SameParameterValue\") Reader reader) throws NoSuchMethodError {\n            new JSONTokener(reader);\n        }\n\n        /**\n         * Reads all content from the specified reader and returns it as a single String.\n         *\n         * @param reader the reader to read characters from\n         * @return the reader content as a single string\n         */\n        private static String toString(Reader reader) throws IOException {\n            StringBuilder sb = new StringBuilder(4096);\n            char[] buf = new char[4096];\n            int n = 0;\n            while (EOF != n) {\n                n = reader.read(buf);\n                if (n > 0) sb.append(buf, 0, n);\n            }\n            return sb.toString();\n        }\n\n        private JSONTokener newTokener(Reader reader) {\n            if (this.readerCtorAvailable) {\n                return new JSONTokener(reader);\n            }\n            // otherwise not available, likely Android or earlier org.json version, fall back to String ctor:\n            String s;\n            try {\n                s = toString(reader);\n            } catch (IOException ex) {\n                String msg = \"Unable to obtain JSON String from Reader: \" + ex.getMessage();\n                throw new JSONException(msg, ex);\n            }\n            return new JSONTokener(s);\n        }\n    }\n}\n"
  },
  {
    "path": "extensions/orgjson/src/main/java/io/jsonwebtoken/orgjson/io/OrgJsonSerializer.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.orgjson.io;\n\nimport io.jsonwebtoken.io.AbstractSerializer;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Classes;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.DateFormats;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.lang.Supplier;\nimport org.json.JSONArray;\nimport org.json.JSONObject;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.math.BigDecimal;\nimport java.math.BigInteger;\nimport java.util.Calendar;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.Map;\n\n/**\n * @since 0.10.0\n */\npublic class OrgJsonSerializer<T> extends AbstractSerializer<T> {\n\n    // we need reflection for these because of Android - see https://github.com/jwtk/jjwt/issues/388\n    private static final String JSON_WRITER_CLASS_NAME = \"org.json.JSONWriter\";\n    private static final Class<?>[] VALUE_TO_STRING_ARG_TYPES = new Class[]{Object.class};\n    private static final String JSON_STRING_CLASS_NAME = \"org.json.JSONString\";\n    private static final Class<?> JSON_STRING_CLASS;\n\n    static { // see see https://github.com/jwtk/jjwt/issues/388\n        if (Classes.isAvailable(JSON_STRING_CLASS_NAME)) {\n            JSON_STRING_CLASS = Classes.forName(JSON_STRING_CLASS_NAME);\n        } else {\n            JSON_STRING_CLASS = null;\n        }\n    }\n\n    @Override\n    protected void doSerialize(T t, OutputStream out) throws Exception {\n        Object o = toJSONInstance(t);\n        String s = toString(o);\n        byte[] bytes = Strings.utf8(s);\n        out.write(bytes);\n    }\n\n    /**\n     * @since 0.10.5 see https://github.com/jwtk/jjwt/issues/388\n     */\n    private static boolean isJSONString(Object o) {\n        if (JSON_STRING_CLASS != null) {\n            return JSON_STRING_CLASS.isInstance(o);\n        }\n        return false;\n    }\n\n    private Object toJSONInstance(Object object) throws IOException {\n\n        if (object == null) {\n            return JSONObject.NULL;\n        }\n\n        if (object instanceof Supplier) {\n            object = ((Supplier<?>) object).get();\n        }\n\n        if (object instanceof JSONObject || object instanceof JSONArray\n                || JSONObject.NULL.equals(object) || isJSONString(object)\n                || object instanceof Byte || object instanceof Character\n                || object instanceof Short || object instanceof Integer\n                || object instanceof Long || object instanceof Boolean\n                || object instanceof Float || object instanceof Double\n                || object instanceof String || object instanceof BigInteger\n                || object instanceof BigDecimal || object instanceof Enum) {\n            return object;\n        }\n\n        if (object instanceof Calendar) {\n            object = ((Calendar) object).getTime(); //sets object to date, will be converted in next if-statement:\n        }\n\n        if (object instanceof Date) {\n            Date date = (Date) object;\n            return DateFormats.formatIso8601(date);\n        }\n\n        if (object instanceof byte[]) {\n            return Encoders.BASE64.encode((byte[]) object);\n        }\n\n        if (object instanceof char[]) {\n            return new String((char[]) object);\n        }\n\n        if (object instanceof Map) {\n            Map<?, ?> map = (Map<?, ?>) object;\n            return toJSONObject(map);\n        }\n\n        if (Objects.isArray(object)) {\n            object = Collections.arrayToList(object); //sets object to List, will be converted in next if-statement:\n        }\n\n        if (object instanceof Collection) {\n            Collection<?> coll = (Collection<?>) object;\n            return toJSONArray(coll);\n        }\n\n        //not an immediately JSON-compatible object and probably a JavaBean (or similar).  We can't convert that\n        //directly without using a marshaller of some sort:\n        String msg = \"Unable to serialize object of type \" + object.getClass().getName() + \" to JSON using known heuristics.\";\n        throw new IOException(msg);\n    }\n\n    private JSONObject toJSONObject(Map<?, ?> m) throws IOException {\n\n        JSONObject obj = new JSONObject();\n\n        for (Map.Entry<?, ?> entry : m.entrySet()) {\n            Object k = entry.getKey();\n            Object value = entry.getValue();\n\n            String key = String.valueOf(k);\n            value = toJSONInstance(value);\n            obj.put(key, value);\n        }\n\n        return obj;\n    }\n\n    private JSONArray toJSONArray(Collection<?> c) throws IOException {\n\n        JSONArray array = new JSONArray();\n\n        for (Object o : c) {\n            o = toJSONInstance(o);\n            array.put(o);\n        }\n\n        return array;\n    }\n\n    /**\n     * Serializes the specified org.json instance a JSON String.\n     *\n     * @param o the org.json instance to convert to a String\n     * @return the JSON String\n     */\n    protected String toString(Object o) {\n        // https://github.com/jwtk/jjwt/issues/380 for Android compatibility (Android doesn't have org.json.JSONWriter):\n        // This instanceof check is a sneaky (hacky?) heuristic: A JwtBuilder only ever provides Map<String,Object>\n        // instances to its serializer instances, so by the time this method is invoked, 'o' will always be a\n        // JSONObject.\n        //\n        // This is sufficient for all JJWT-supported scenarios on Android since Android users shouldn't ever use\n        // JJWT's internal Serializer implementation for general JSON serialization.  That is, its intended use\n        // is within the context of JwtBuilder execution and not for application use beyond that.\n        if (o instanceof JSONObject) {\n            return o.toString();\n        }\n        // we still call JSONWriter for all other values 'just in case', and this works for all valid JSON values\n        // This would fail on Android unless they include the newer org.json dependency and ignore Android's.\n        return Classes.invokeStatic(JSON_WRITER_CLASS_NAME, \"valueToString\", VALUE_TO_STRING_ARG_TYPES, o);\n    }\n\n    /**\n     * Serializes the specified org.json instance a byte array.\n     *\n     * @param o the org.json instance to serialize\n     * @return the JSON byte array\n     * @deprecated not called by JJWT\n     */\n    @Deprecated\n    protected byte[] toBytes(Object o) {\n        String s = toString(o);\n        return Strings.utf8(s);\n    }\n}\n"
  },
  {
    "path": "extensions/orgjson/src/main/resources/META-INF/services/io.jsonwebtoken.io.Deserializer",
    "content": "io.jsonwebtoken.orgjson.io.OrgJsonDeserializer"
  },
  {
    "path": "extensions/orgjson/src/main/resources/META-INF/services/io.jsonwebtoken.io.Serializer",
    "content": "io.jsonwebtoken.orgjson.io.OrgJsonSerializer"
  },
  {
    "path": "extensions/orgjson/src/test/groovy/io/jsonwebtoken/orgjson/io/AndroidOrgJsonSerializerTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.orgjson.io\n\nimport io.jsonwebtoken.lang.Classes\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.powermock.core.classloader.annotations.PrepareForTest\nimport org.powermock.modules.junit4.PowerMockRunner\n\nimport static org.easymock.EasyMock.eq\nimport static org.easymock.EasyMock.expect\nimport static org.junit.Assert.assertFalse\nimport static org.powermock.api.easymock.PowerMock.*\n\n@RunWith(PowerMockRunner.class)\n@PrepareForTest([Classes])\nclass AndroidOrgJsonSerializerTest {\n\n    @Test\n    void testJSONStringNotAvailable() {\n\n        mockStatic(Classes)\n\n        expect(Classes.isAvailable(eq('org.json.JSONString'))).andReturn(false)\n\n        replay Classes\n\n        assertFalse OrgJsonSerializer.isJSONString('foo')\n\n        verify Classes\n    }\n\n}\n"
  },
  {
    "path": "extensions/orgjson/src/test/groovy/io/jsonwebtoken/orgjson/io/OrgJsonDeserializerTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//file:noinspection GrDeprecatedAPIUsage\npackage io.jsonwebtoken.orgjson.io\n\nimport io.jsonwebtoken.io.DeserializationException\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.io.IOException\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass OrgJsonDeserializerTest {\n\n    private OrgJsonDeserializer des\n\n    private static Reader reader(byte[] data) {\n        def ins = new ByteArrayInputStream(data)\n        return new InputStreamReader(ins, Strings.UTF_8)\n    }\n\n    private static Reader reader(String s) {\n        return reader(Strings.utf8(s))\n    }\n\n    private Object fromBytes(byte[] data) {\n        def reader = reader(data)\n        return des.deserialize(reader)\n    }\n\n    private Object read(String s) {\n        return fromBytes(Strings.utf8(s))\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testNullArgument() {\n        des.deserialize((Reader) null)\n    }\n\n    @Test(expected = DeserializationException)\n    void testEmptyByteArray() {\n        fromBytes(new byte[0])\n    }\n\n    @Test(expected = DeserializationException)\n    void testInvalidJson() {\n        read('\"')\n    }\n\n    @Test\n    void testLiteralNull() {\n        assertNull read('null')\n    }\n\n    @Test\n    void testLiteralTrue() {\n        assertTrue read('true') as boolean\n    }\n\n    @Test\n    void testLiteralFalse() {\n        assertFalse read('false') as boolean\n    }\n\n    @Test\n    void testLiteralInteger() {\n        assertEquals 1 as Integer, read('1')\n    }\n\n    @Test\n    void testLiteralDecimal() {\n        assertEquals 3.14159 as Double, read('3.14159') as BigDecimal, 0d\n    }\n\n    @Test\n    void testEmptyArray() {\n        def value = read('[]')\n        assert value instanceof List\n        assertEquals 0, value.size()\n    }\n\n    @Test\n    void testSimpleArray() {\n        def value = read('[1, 2]')\n        assert value instanceof List\n        def expected = [1, 2]\n        assertEquals expected, value\n    }\n\n    @Test\n    void testArrayWithNullElements() {\n        def value = read('[1, null, 3]')\n        assert value instanceof List\n        def expected = [1, null, 3]\n        assertEquals expected, value\n    }\n\n    @Test\n    void testEmptyObject() {\n        def value = read('{}')\n        assert value instanceof Map\n        assertEquals 0, value.size()\n    }\n\n    @Test\n    void testSimpleObject() {\n        def value = read('{\"hello\": \"世界\"}')\n        assert value instanceof Map\n        def expected = [hello: '世界']\n        assertEquals expected, value\n    }\n\n    @Test\n    void testObjectWithKeyHavingNullValue() {\n        def value = read('{\"hello\": \"世界\", \"test\": null}')\n        assert value instanceof Map\n        def expected = [hello: '世界', test: null]\n        assertEquals expected, value\n    }\n\n    @Test\n    void testObjectWithKeyHavingArrayValue() {\n        def value = read('{\"hello\": \"世界\", \"test\": [1, 2]}')\n        assert value instanceof Map\n        def expected = [hello: '世界', test: [1, 2]]\n        assertEquals expected, value\n    }\n\n    @Test\n    void testObjectWithKeyHavingObjectValue() {\n        def value = read('{\"hello\": \"世界\", \"test\": {\"foo\": \"bar\"}}')\n        assert value instanceof Map\n        def expected = [hello: '世界', test: [foo: 'bar']]\n        assertEquals expected, value\n    }\n\n    @Before\n    void setUp() {\n        des = new OrgJsonDeserializer()\n    }\n\n    @Test\n    void loadService() {\n        def deserializer = ServiceLoader.load(Deserializer).iterator().next()\n        assert deserializer instanceof OrgJsonDeserializer\n    }\n\n    @Test\n    void deserialize() {\n        def m = [hello: 42]\n        assertEquals m, des.deserialize(Strings.utf8('{\"hello\":42}'))\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void deserializeNull() {\n        des.deserialize((Reader) null)\n    }\n\n    @Test(expected = DeserializationException)\n    void deserializeEmpty() {\n        read('')\n    }\n\n    @Test\n    void throwableConvertsToDeserializationException() {\n\n        def t = new Throwable(\"foo\")\n\n        des = new OrgJsonDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) {\n                throw t\n            }\n        }\n\n        try {\n            des.deserialize(Strings.utf8('whatever'))\n            fail()\n        } catch (DeserializationException expected) {\n            String msg = 'Unable to deserialize: foo'\n            assertEquals msg, expected.message\n        }\n    }\n\n    /**\n     * Asserts that, when the JSONTokener(Reader) constructor isn't available (e.g. on Android), that the Reader is\n     * converted to a String and the JSONTokener(String) constructor is used instead.\n     * @since 0.12.4\n     */\n    @Test\n    void jsonTokenerMissingReaderConstructor() {\n\n        def json = '{\"hello\": \"世界\", \"test\": [1, 2]}'\n        def expected = read(json) // 'normal' reading\n\n        des = new OrgJsonDeserializer(new NoReaderCtorTokenerFactory())\n\n        def reader = reader('{\"hello\": \"世界\", \"test\": [1, 2]}')\n\n        def result = des.deserialize(reader) // should still work\n\n        assertEquals expected, result\n    }\n\n    /**\n     * Asserts that, when the JSONTokener(Reader) constructor isn't available, and conversion of the Reader to a String\n     * fails, that a JSONException is thrown\n     * @since 0.12.4\n     */\n    @Test\n    void readerFallbackToStringFails() {\n        def causeMsg = 'Reader failed.'\n        def cause = new java.io.IOException(causeMsg)\n        def reader = new Reader() {\n            @Override\n            int read(char[] cbuf, int off, int len) throws IOException {\n                throw cause\n            }\n\n            @Override\n            void close() throws IOException {\n            }\n        }\n\n        des = new OrgJsonDeserializer(new NoReaderCtorTokenerFactory())\n\n        try {\n            des.deserialize(reader)\n            fail()\n        } catch (DeserializationException expected) {\n            def jsonEx = expected.getCause()\n            String msg = \"Unable to obtain JSON String from Reader: $causeMsg\"\n            assertEquals msg, jsonEx.getMessage()\n            assertSame cause, jsonEx.getCause()\n        }\n    }\n\n    private static class NoReaderCtorTokenerFactory extends OrgJsonDeserializer.JSONTokenerFactory {\n        @Override\n        protected void testTokener(Reader reader) throws NoSuchMethodError {\n            throw new NoSuchMethodError('Android says nope!')\n        }\n    }\n\n}\n"
  },
  {
    "path": "extensions/orgjson/src/test/groovy/io/jsonwebtoken/orgjson/io/OrgJsonSerializerTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//file:noinspection GrDeprecatedAPIUsage\npackage io.jsonwebtoken.orgjson.io\n\nimport io.jsonwebtoken.SignatureAlgorithm\nimport io.jsonwebtoken.io.SerializationException\nimport io.jsonwebtoken.io.Serializer\nimport io.jsonwebtoken.lang.DateFormats\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.lang.Supplier\nimport org.json.JSONObject\nimport org.json.JSONString\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass OrgJsonSerializerTest {\n\n    private OrgJsonSerializer s\n\n    @Before\n    void setUp() {\n        s = new OrgJsonSerializer()\n    }\n\n    private String ser(Object o) {\n        return Strings.utf8(s.serialize(o))\n    }\n\n    @Test\n    void loadService() {\n        def serializer = ServiceLoader.load(Serializer).iterator().next()\n        assertTrue serializer instanceof OrgJsonSerializer\n    }\n\n    @Test\n    void testInvalidArgument() {\n        try {\n            ser(new Object())\n            fail()\n        } catch (SerializationException expected) {\n            String causeMsg = 'Unable to serialize object of type java.lang.Object to JSON using known heuristics.'\n            String msg = \"Unable to serialize object of type java.lang.Object: $causeMsg\"\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testNull() {\n        assertEquals 'null', ser(null)\n    }\n\n    @Test\n    void testJSONObjectNull() {\n        assertEquals 'null', ser(JSONObject.NULL)\n    }\n\n    @Test\n    void testJSONString() {\n        def jsonString = new JSONString() {\n            @Override\n            String toJSONString() {\n                return '\"foo\"'\n            }\n        }\n        assertEquals '\"foo\"', ser(jsonString)\n    }\n\n    @Test\n    void testTrue() {\n        assertEquals 'true', ser(Boolean.TRUE)\n    }\n\n    @Test\n    void testFalse() {\n        assertEquals 'false', ser(Boolean.FALSE)\n    }\n\n    @Test\n    void testByte() {\n        assertEquals '120', ser(\"x\".getBytes(Strings.UTF_8)[0]) //ascii(\"x\") == 120\n    }\n\n    @Test\n    void testByteArray() { //expect Base64 string by default:\n        byte[] bytes = \"hi\".getBytes(Strings.UTF_8)\n        String expected = '\"aGk=\"' as String //base64(hi) --> aGk=\n        assertEquals expected, ser(bytes)\n    }\n\n    @Test\n    void testEmptyByteArray() { //base64 --> zero bytes == zero-length string:\n        assertEquals \"\\\"\\\"\", ser(new byte[0])\n    }\n\n    @Test\n    void testChar() {\n        assertEquals \"\\\"h\\\"\", ser('h' as char)\n    }\n\n    @Test\n    void testCharArray() {\n        assertEquals \"\\\"hi\\\"\", ser(\"hi\".toCharArray())\n    }\n\n    @Test\n    void testEmptyCharArray() { //no chars == empty string:\n        assertEquals \"\\\"\\\"\", ser(new char[0])\n    }\n\n    @Test\n    void testShort() {\n        assertEquals '8', ser(8 as short)\n    }\n\n    @Test\n    void testInteger() {\n        assertEquals '1', ser(1 as Integer)\n    }\n\n    @Test\n    void testLong() {\n        assertEquals '42', ser(42 as Long)\n    }\n\n    @Test\n    void testBigInteger() {\n        assertEquals '42', ser(BigInteger.valueOf(42 as Long))\n    }\n\n    @Test\n    void testFloat() {\n        assertEquals '3.14159', ser(3.14159 as Float)\n    }\n\n    @Test\n    void testDouble() {\n        assertEquals '3.14159', ser(3.14159 as Double)\n    }\n\n    @Test\n    void testBigDecimal() {\n        assertEquals '3.14159', ser(BigDecimal.valueOf(3.14159 as Double))\n    }\n\n    @Test\n    void testEnum() {\n        assertEquals '\"HS256\"', ser(SignatureAlgorithm.HS256)\n    }\n\n    @Test\n    void testSupplier() {\n        def supplier = new Supplier() {\n            @Override\n            Object get() {\n                return 'test'\n            }\n        }\n        assertEquals '\"test\"', ser(supplier)\n    }\n\n    @Test\n    void testEmptyString() {\n        assertEquals '\"\"', ser('')\n    }\n\n    @Test\n    void testWhitespaceString() {\n        String value = \" \\n\\r\\t \"\n        assertEquals '\" \\\\n\\\\r\\\\t \"' as String, ser(value)\n    }\n\n    @Test\n    void testSimpleString() {\n        assertEquals '\"hello 世界\"', ser('hello 世界')\n    }\n\n    @Test\n    void testDate() {\n        Date now = new Date()\n        String formatted = DateFormats.formatIso8601(now)\n        assertEquals \"\\\"$formatted\\\"\" as String, ser(now)\n    }\n\n    @Test\n    void testCalendar() {\n        def cal = Calendar.getInstance(TimeZone.getTimeZone(\"UTC\"))\n        def now = cal.getTime()\n        String formatted = DateFormats.formatIso8601(now)\n        assertEquals \"\\\"$formatted\\\"\" as String, ser(cal)\n    }\n\n    @Test\n    void testSimpleIntArray() {\n        assertEquals '[1,2]', ser([1, 2] as int[])\n    }\n\n    @Test\n    void testIntegerArrayWithNullElements() {\n        assertEquals '[1,null]', ser([1, null] as Integer[])\n    }\n\n    @Test\n    void testIntegerList() {\n        assertEquals '[1,2]', ser([1, 2] as List)\n    }\n\n    @Test\n    void testEmptyObject() {\n        assertEquals '{}', ser([:])\n    }\n\n    @Test\n    void testSimpleObject() {\n        assertEquals '{\"hello\":\"世界\"}', ser([hello: '世界'])\n    }\n\n    @Test\n    void testObjectWithKeyHavingNullValue() {\n        //depending on the test platform, and that JSON doesn't require members to be ordered, either of the\n        //two strings are fine (they're the same data, just the member order is different):\n        String acceptable1 = '{\"hello\":\"世界\",\"test\":null}'\n        String acceptable2 = '{\"test\":null,\"hello\":\"世界\"}'\n        String result = ser([test: null, hello: '世界'])\n        assertTrue acceptable1.equals(result) || acceptable2.equals(result)\n    }\n\n    @Test\n    void testObjectWithKeyHavingArrayValue() {\n        //depending on the test platform, and that JSON doesn't require members to be ordered, either of the\n        //two strings are fine (they're the same data, just the member order is different):\n        String acceptable1 = '{\"test\":[1,2],\"hello\":\"世界\"}'\n        String acceptable2 = '{\"hello\":\"世界\",\"test\":[1,2]}'\n        String result = ser([test: [1, 2], hello: '世界'])\n        assertTrue acceptable1.equals(result) || acceptable2.equals(result)\n    }\n\n    @Test\n    void testObjectWithKeyHavingObjectValue() {\n        //depending on the test platform, and that JSON doesn't require members to be ordered, either of the\n        //two strings are fine (they're the same data, just the member order is different):\n        String acceptable1 = '{\"test\":{\"foo\":\"bar\"},\"hello\":\"世界\"}'\n        String acceptable2 = '{\"hello\":\"世界\",\"test\":{\"foo\":\"bar\"}}'\n        String result = ser([test: [foo: 'bar'], hello: '世界'])\n        assertTrue acceptable1.equals(result) || acceptable2.equals(result)\n    }\n\n    @Test\n    void testListWithNullElements() {\n        assertEquals '[1,null,null]', ser([1, null, null] as List)\n    }\n\n    @Test\n    void testListWithSingleNullElement() {\n        assertEquals '[null]', ser([null] as List)\n    }\n\n    @Test\n    void testListWithNestedObject() {\n        assertEquals '[1,null,{\"hello\":\"世界\"}]', ser([1, null, [hello: '世界']])\n    }\n\n    @Test\n    void testSerialize() {\n        assertEquals '\"hello\"', ser('hello')\n    }\n\n    @Test\n    void testIOExceptionConvertedToSerializationException() {\n        try {\n            ser(new Object())\n            fail()\n        } catch (SerializationException expected) {\n            String causeMsg = 'Unable to serialize object of type java.lang.Object to JSON using known heuristics.'\n            String msg = \"Unable to serialize object of type java.lang.Object: $causeMsg\"\n            assertEquals causeMsg, expected.cause.message\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testToBytes() {\n        assertEquals 'null', Strings.utf8(s.toBytes(null))\n        assertEquals '\"hello\"', Strings.utf8(s.toBytes('hello'))\n    }\n}\n"
  },
  {
    "path": "extensions/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2018 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt-extensions</artifactId>\n    <name>JJWT :: Extensions</name>\n    <packaging>pom</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/..</jjwt.root>\n    </properties>\n\n    <modules>\n        <module>jackson</module>\n        <module>orgjson</module>\n        <module>gson</module>\n    </modules>\n</project>"
  },
  {
    "path": "impl/bnd.bnd",
    "content": "Fragment-Host: io.jsonwebtoken.jjwt-api \n"
  },
  {
    "path": "impl/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2018 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt-impl</artifactId>\n    <name>JJWT :: Impl</name>\n    <packaging>jar</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/..</jjwt.root>\n        <!-- This module is not intended for direct use or extension, no need to worry about perfect JavaDoc: -->\n        <maven.javadoc.additionalOptions>-Xdoclint:none</maven.javadoc.additionalOptions>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-api</artifactId>\n        </dependency>\n\n        <dependency>\n            <groupId>org.bouncycastle</groupId>\n            <artifactId>${bcprov.artifactId}</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.bouncycastle</groupId>\n            <artifactId>${bcpkix.artifactId}</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-jackson</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-orgjson</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-gson</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n</project>"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/AbstractAudienceCollection.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.ClaimsMutator;\nimport io.jsonwebtoken.impl.lang.DefaultNestedCollection;\n\nimport java.util.Collection;\n\n/**\n * Abstract NestedCollection that requires the AudienceCollection interface to be implemented.\n *\n * @param <P> type of parent to return\n * @since 0.12.0\n */\nabstract class AbstractAudienceCollection<P> extends DefaultNestedCollection<String, P>\n        implements ClaimsMutator.AudienceCollection<P> {\n    protected AbstractAudienceCollection(P parent, Collection<? extends String> seed) {\n        super(parent, seed);\n    }\n}"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/AbstractTextCodec.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.Encoder;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.nio.charset.Charset;\n\n/**\n * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@link Encoder} orr {@link Decoder} instead.\n */\n@Deprecated\npublic abstract class AbstractTextCodec implements TextCodec {\n\n    protected static final Charset UTF8 = Charset.forName(\"UTF-8\");\n    protected static final Charset US_ASCII = Charset.forName(\"US-ASCII\");\n\n    @Override\n    public String encode(String data) {\n        Assert.hasText(data, \"String argument to encode cannot be null or empty.\");\n        byte[] bytes = data.getBytes(UTF8);\n        return encode(bytes);\n    }\n\n    @Override\n    public String decodeToString(String encoded) {\n        byte[] bytes = decode(encoded);\n        return new String(bytes, UTF8);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/AbstractX509Context.java",
    "content": "/*\n * Copyright (C) 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.security.AbstractAsymmetricJwk;\nimport io.jsonwebtoken.security.X509Mutator;\n\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\nimport java.util.Set;\n\npublic class AbstractX509Context<T extends X509Mutator<T>> extends ParameterMap implements X509Context<T> {\n\n    public AbstractX509Context(Set<Parameter<?>> params) {\n        super(params);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected T self() {\n        return (T) this;\n    }\n\n    @Override\n    public URI getX509Url() {\n        return get(AbstractAsymmetricJwk.X5U);\n    }\n\n    @Override\n    public T x509Url(URI uri) {\n        put(AbstractAsymmetricJwk.X5U, uri);\n        return self();\n    }\n\n    @Override\n    public List<X509Certificate> getX509Chain() {\n        return get(AbstractAsymmetricJwk.X5C);\n    }\n\n    @Override\n    public T x509Chain(List<X509Certificate> chain) {\n        put(AbstractAsymmetricJwk.X5C, chain);\n        return self();\n    }\n\n    @Override\n    public byte[] getX509Sha1Thumbprint() {\n        return get(AbstractAsymmetricJwk.X5T);\n    }\n\n    @Override\n    public T x509Sha1Thumbprint(byte[] thumbprint) {\n        put(AbstractAsymmetricJwk.X5T, thumbprint);\n        return self();\n    }\n\n    @Override\n    public byte[] getX509Sha256Thumbprint() {\n        return get(AbstractAsymmetricJwk.X5T_S256);\n    }\n\n    @Override\n    public T x509Sha256Thumbprint(byte[] thumbprint) {\n        put(AbstractAsymmetricJwk.X5T_S256, thumbprint);\n        return self();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/AndroidBase64Codec.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.Encoders;\n\n/**\n * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@code io.jsonwebtoken.io.Encoders#BASE64}\n * or {@code io.jsonwebtoken.io.Decoders#BASE64} instead.\n */\n@Deprecated\npublic class AndroidBase64Codec extends AbstractTextCodec {\n\n    @Override\n    public String encode(byte[] data) {\n        return Encoders.BASE64.encode(data);\n    }\n\n    @Override\n    public byte[] decode(String encoded) {\n        return Decoders.BASE64.decode(encoded);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/Base64Codec.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.Encoders;\n\n/**\n * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@code io.jsonwebtoken.io.Encoders#BASE64}\n * or {@code io.jsonwebtoken.io.Decoders#BASE64}\n */\n@Deprecated\npublic class Base64Codec extends AbstractTextCodec {\n\n    public String encode(byte[] data) {\n        return Encoders.BASE64.encode(data);\n    }\n\n    @Override\n    public byte[] decode(String encoded) {\n        return Decoders.BASE64.decode(encoded);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/Base64UrlCodec.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.Encoders;\n\n/**\n * @deprecated since 0.10.0 - will be removed before 1.0.0. Use {@link Encoders#BASE64URL Encoder.BASE64URL}\n * or {@link Decoders#BASE64URL Decoder.BASE64URL} instead.\n */\n@Deprecated\npublic class Base64UrlCodec extends AbstractTextCodec {\n\n    @Override\n    public String encode(byte[] data) {\n        return Encoders.BASE64URL.encode(data);\n    }\n\n    @Override\n    public byte[] decode(String encoded) {\n        return Decoders.BASE64URL.decode(encoded);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/CompressionCodecLocator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.CompressionCodecResolver;\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.Locator;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.lang.Assert;\n\n//TODO: delete when deleting CompressionCodecResolver\npublic class CompressionCodecLocator implements Function<Header, CompressionAlgorithm>, Locator<CompressionAlgorithm> {\n\n    private final CompressionCodecResolver resolver;\n\n    public CompressionCodecLocator(CompressionCodecResolver resolver) {\n        this.resolver = Assert.notNull(resolver, \"CompressionCodecResolver cannot be null.\");\n    }\n\n    @Override\n    public CompressionAlgorithm apply(Header header) {\n        return locate(header);\n    }\n\n    @Override\n    public CompressionAlgorithm locate(Header header) {\n        return resolver.resolveCompressionCodec(header);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultClaims.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.RequiredTypeException;\nimport io.jsonwebtoken.impl.lang.JwtDateConverter;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Registry;\n\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class DefaultClaims extends ParameterMap implements Claims {\n\n    private static final String CONVERSION_ERROR_MSG = \"Cannot convert existing claim value of type '%s' to desired type \" +\n            \"'%s'. JJWT only converts simple String, Date, Long, Integer, Short and Byte types automatically. \" +\n            \"Anything more complex is expected to be already converted to your desired type by the JSON Deserializer \" +\n            \"implementation. You may specify a custom Deserializer for a JwtParser with the desired conversion \" +\n            \"configuration via the JwtParserBuilder.deserializer() method. \" +\n            \"See https://github.com/jwtk/jjwt#custom-json-processor for more information. If using Jackson, you can \" +\n            \"specify custom claim POJO types as described in https://github.com/jwtk/jjwt#json-jackson-custom-types\";\n\n    static final Parameter<String> ISSUER = Parameters.string(Claims.ISSUER, \"Issuer\");\n    static final Parameter<String> SUBJECT = Parameters.string(Claims.SUBJECT, \"Subject\");\n    static final Parameter<Set<String>> AUDIENCE = Parameters.stringSet(Claims.AUDIENCE, \"Audience\");\n    static final Parameter<Date> EXPIRATION = Parameters.rfcDate(Claims.EXPIRATION, \"Expiration Time\");\n    static final Parameter<Date> NOT_BEFORE = Parameters.rfcDate(Claims.NOT_BEFORE, \"Not Before\");\n    static final Parameter<Date> ISSUED_AT = Parameters.rfcDate(Claims.ISSUED_AT, \"Issued At\");\n    static final Parameter<String> JTI = Parameters.string(Claims.ID, \"JWT ID\");\n\n    static final Registry<String, Parameter<?>> PARAMS =\n            Parameters.registry(ISSUER, SUBJECT, AUDIENCE, EXPIRATION, NOT_BEFORE, ISSUED_AT, JTI);\n\n    protected DefaultClaims() { // visibility for testing\n        super(PARAMS);\n    }\n\n    public DefaultClaims(ParameterMap m) {\n        super(m.PARAMS, m);\n    }\n\n    public DefaultClaims(Map<String, ?> map) {\n        super(PARAMS, map);\n    }\n\n    @Override\n    public String getName() {\n        return \"JWT Claims\";\n    }\n\n    @Override\n    public String getIssuer() {\n        return get(ISSUER);\n    }\n\n    @Override\n    public String getSubject() {\n        return get(SUBJECT);\n    }\n\n    @Override\n    public Set<String> getAudience() {\n        return get(AUDIENCE);\n    }\n\n    @Override\n    public Date getExpiration() {\n        return get(EXPIRATION);\n    }\n\n    @Override\n    public Date getNotBefore() {\n        return get(NOT_BEFORE);\n    }\n\n    @Override\n    public Date getIssuedAt() {\n        return get(ISSUED_AT);\n    }\n\n    @Override\n    public String getId() {\n        return get(JTI);\n    }\n\n    @Override\n    public <T> T get(String claimName, Class<T> requiredType) {\n        Assert.notNull(requiredType, \"requiredType argument cannot be null.\");\n\n        Object value = this.idiomaticValues.get(claimName);\n        if (requiredType.isInstance(value)) {\n            return requiredType.cast(value);\n        }\n\n        value = get(claimName);\n        if (value == null) {\n            return null;\n        }\n\n        if (Date.class.equals(requiredType)) {\n            try {\n                value = JwtDateConverter.toDate(value); // NOT specDate logic\n            } catch (Exception e) {\n                String msg = \"Cannot create Date from '\" + claimName + \"' value '\" + value + \"'. Cause: \" + e.getMessage();\n                throw new IllegalArgumentException(msg, e);\n            }\n        }\n\n        return castClaimValue(claimName, value, requiredType);\n    }\n\n    private <T> T castClaimValue(String name, Object value, Class<T> requiredType) {\n\n        if (value instanceof Long || value instanceof Integer || value instanceof Short || value instanceof Byte) {\n            long longValue = ((Number) value).longValue();\n            if (Long.class.equals(requiredType)) {\n                value = longValue;\n            } else if (Integer.class.equals(requiredType) && Integer.MIN_VALUE <= longValue && longValue <= Integer.MAX_VALUE) {\n                value = (int) longValue;\n            } else if (requiredType == Short.class && Short.MIN_VALUE <= longValue && longValue <= Short.MAX_VALUE) {\n                value = (short) longValue;\n            } else if (requiredType == Byte.class && Byte.MIN_VALUE <= longValue && longValue <= Byte.MAX_VALUE) {\n                value = (byte) longValue;\n            }\n        }\n\n        if (value instanceof Long &&\n                (requiredType.equals(Integer.class) || requiredType.equals(Short.class) || requiredType.equals(Byte.class))) {\n            String msg = \"Claim '\" + name + \"' value is too large or too small to be represented as a \" +\n                    requiredType.getName() + \" instance (would cause numeric overflow).\";\n            throw new RequiredTypeException(msg);\n        }\n\n        if (!requiredType.isInstance(value)) {\n            throw new RequiredTypeException(String.format(CONVERSION_ERROR_MSG, value.getClass(), requiredType));\n        }\n\n        return requiredType.cast(value);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultClaimsBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.ClaimsBuilder;\n\n/**\n * @since 0.12.0\n */\npublic final class DefaultClaimsBuilder extends DelegatingClaimsMutator<ClaimsBuilder>\n        implements ClaimsBuilder {\n\n    public DefaultClaimsBuilder() {\n        super();\n    }\n\n    @Override\n    public Claims build() {\n        // ensure a new instance is returned so that the builder may be re-used:\n        return new DefaultClaims(this.DELEGATE);\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwts class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<ClaimsBuilder> {\n        @Override\n        public ClaimsBuilder get() {\n            return new DefaultClaimsBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultClock.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Clock;\n\nimport java.util.Date;\n\n/**\n * Default {@link Clock} implementation.\n *\n * @since 0.7.0\n */\npublic class DefaultClock implements Clock {\n\n    /**\n     * Default static instance that may be shared.  It is thread-safe.\n     */\n    public static final Clock INSTANCE = new DefaultClock();\n\n    /**\n     * Simply returns <code>new {@link Date}()</code>.\n     *\n     * @return a new {@link Date} instance.\n     */\n    @Override\n    public Date now() {\n        return new Date();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultHeader.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.impl.lang.CompactMediaTypeIdConverter;\nimport io.jsonwebtoken.impl.lang.Nameable;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Map;\n\npublic class DefaultHeader extends ParameterMap implements Header {\n\n    static final Parameter<String> TYPE = Parameters.string(Header.TYPE, \"Type\");\n    static final Parameter<String> CONTENT_TYPE = Parameters.builder(String.class)\n            .setId(Header.CONTENT_TYPE).setName(\"Content Type\")\n            .setConverter(CompactMediaTypeIdConverter.INSTANCE).build();\n    static final Parameter<String> ALGORITHM = Parameters.string(Header.ALGORITHM, \"Algorithm\");\n    static final Parameter<String> COMPRESSION_ALGORITHM =\n            Parameters.string(Header.COMPRESSION_ALGORITHM, \"Compression Algorithm\");\n    @SuppressWarnings(\"DeprecatedIsStillUsed\")\n    @Deprecated // TODO: remove for 1.0.0:\n    static final Parameter<String> DEPRECATED_COMPRESSION_ALGORITHM =\n            Parameters.string(Header.DEPRECATED_COMPRESSION_ALGORITHM, \"Deprecated Compression Algorithm\");\n\n    static final Registry<String, Parameter<?>> PARAMS =\n            Parameters.registry(TYPE, CONTENT_TYPE, ALGORITHM, COMPRESSION_ALGORITHM, DEPRECATED_COMPRESSION_ALGORITHM);\n\n    public DefaultHeader(Map<String, ?> values) {\n        super(PARAMS, values);\n    }\n\n    protected DefaultHeader(Registry<String, Parameter<?>> registry, Map<String, ?> values) {\n        super(registry, values);\n    }\n\n    @Override\n    public String getName() {\n        return \"JWT header\";\n    }\n\n    static String nameOf(Header header) {\n        return Assert.hasText(Assert.isInstanceOf(Nameable.class, header).getName(),\n                \"Header name cannot be null or empty.\");\n    }\n\n    @Override\n    public String getType() {\n        return get(TYPE);\n    }\n\n    @Override\n    public String getContentType() {\n        return get(CONTENT_TYPE);\n    }\n\n    @Override\n    public String getAlgorithm() {\n        return get(ALGORITHM);\n    }\n\n    @Override\n    public String getCompressionAlgorithm() {\n        String s = get(COMPRESSION_ALGORITHM);\n        if (!Strings.hasText(s)) {\n            s = get(DEPRECATED_COMPRESSION_ALGORITHM);\n        }\n        return s;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJwe.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Jwe;\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.JwtVisitor;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\n\nimport java.security.MessageDigest;\n\npublic class DefaultJwe<P> extends DefaultProtectedJwt<JweHeader, P> implements Jwe<P> {\n\n    private static final String DIGEST_NAME = \"tag\";\n\n    private final byte[] iv;\n\n    public DefaultJwe(JweHeader header, P payload, byte[] iv, byte[] aadTag) {\n        super(header, payload, aadTag, DIGEST_NAME);\n        this.iv = Assert.notEmpty(iv, \"Initialization vector cannot be null or empty.\");\n    }\n\n    @Override\n    public byte[] getInitializationVector() {\n        return this.iv.clone();\n    }\n\n    @Override\n    protected StringBuilder toStringBuilder() {\n        return super.toStringBuilder().append(\",iv=\").append(Encoders.BASE64URL.encode(this.iv));\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) {\n            return true;\n        }\n        if (obj instanceof Jwe) {\n            Jwe<?> jwe = (Jwe<?>) obj;\n            return super.equals(jwe) && MessageDigest.isEqual(this.iv, jwe.getInitializationVector());\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.nullSafeHashCode(getHeader(), getPayload(), this.iv, this.digest);\n    }\n\n    @Override\n    public <T> T accept(JwtVisitor<T> v) {\n        return v.visit(this);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJweHeader.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.impl.lang.Converters;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.impl.lang.PositiveIntegerConverter;\nimport io.jsonwebtoken.impl.lang.RequiredBitLengthConverter;\nimport io.jsonwebtoken.impl.security.JwkConverter;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.util.Map;\n\n/**\n * Header implementation satisfying JWE header parameter requirements.\n *\n * @since 0.12.0\n */\npublic class DefaultJweHeader extends DefaultProtectedHeader implements JweHeader {\n\n    static final Parameter<String> ENCRYPTION_ALGORITHM = Parameters.string(\"enc\", \"Encryption Algorithm\");\n\n    public static final Parameter<PublicJwk<?>> EPK = Parameters.builder(JwkConverter.PUBLIC_JWK_CLASS)\n            .setId(\"epk\").setName(\"Ephemeral Public Key\")\n            .setConverter(JwkConverter.PUBLIC_JWK).build();\n    static final Parameter<byte[]> APU = Parameters.bytes(\"apu\", \"Agreement PartyUInfo\").build();\n    static final Parameter<byte[]> APV = Parameters.bytes(\"apv\", \"Agreement PartyVInfo\").build();\n\n    // https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.1 says 96 bits required:\n    public static final Parameter<byte[]> IV = Parameters.bytes(\"iv\", \"Initialization Vector\")\n            .setConverter(new RequiredBitLengthConverter(Converters.BASE64URL_BYTES, 96)).build();\n\n    // https://www.rfc-editor.org/rfc/rfc7518.html#section-4.7.1.2 says 128 bits required:\n    public static final Parameter<byte[]> TAG = Parameters.bytes(\"tag\", \"Authentication Tag\")\n            .setConverter(new RequiredBitLengthConverter(Converters.BASE64URL_BYTES, 128)).build();\n\n    // https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1 says at least 64 bits (8 bytes) is required:\n    public static final Parameter<byte[]> P2S = Parameters.bytes(\"p2s\", \"PBES2 Salt Input\")\n            .setConverter(new RequiredBitLengthConverter(Converters.BASE64URL_BYTES, 64, false)).build();\n    public static final Parameter<Integer> P2C = Parameters.builder(Integer.class)\n            .setConverter(PositiveIntegerConverter.INSTANCE).setId(\"p2c\").setName(\"PBES2 Count\").build();\n\n    static final Registry<String, Parameter<?>> PARAMS =\n            Parameters.registry(DefaultProtectedHeader.PARAMS, ENCRYPTION_ALGORITHM, EPK, APU, APV, IV, TAG, P2S, P2C);\n\n    static boolean isCandidate(ParameterMap map) {\n        String id = map.get(DefaultHeader.ALGORITHM);\n        return Strings.hasText(id) && !id.equalsIgnoreCase(Jwts.SIG.NONE.getId()) && // alg cannot be empty or 'none'\n                Strings.hasText(map.get(ENCRYPTION_ALGORITHM)); // enc cannot be empty\n//        return Strings.hasText(map.get(ENCRYPTION_ALGORITHM)) || // MUST have at least an `enc` header\n//                !Collections.isEmpty(map.get(EPK)) ||\n//                !Bytes.isEmpty(map.get(APU)) ||\n//                !Bytes.isEmpty(map.get(APV)) ||\n//                !Bytes.isEmpty(map.get(IV)) ||\n//                !Bytes.isEmpty(map.get(TAG)) ||\n//                !Bytes.isEmpty(map.get(P2S)) ||\n//                (map.get(P2C) != null && map.get(P2C) > 0);\n\n    }\n\n    public DefaultJweHeader(Map<String, ?> map) {\n        super(PARAMS, map);\n    }\n\n    @Override\n    public String getName() {\n        return \"JWE header\";\n    }\n\n    @Override\n    public String getEncryptionAlgorithm() {\n        return get(ENCRYPTION_ALGORITHM);\n    }\n\n    @Override\n    public PublicJwk<?> getEphemeralPublicKey() {\n        return get(EPK);\n    }\n\n    @Override\n    public byte[] getAgreementPartyUInfo() {\n        return get(APU);\n    }\n\n    @Override\n    public byte[] getAgreementPartyVInfo() {\n        return get(APV);\n    }\n\n    @Override\n    public byte[] getInitializationVector() {\n        return get(IV);\n    }\n\n    @Override\n    public byte[] getAuthenticationTag() {\n        return get(TAG);\n    }\n\n    public byte[] getPbes2Salt() {\n        return get(P2S);\n    }\n\n    @Override\n    public Integer getPbes2Count() {\n        return get(P2C);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJweHeaderBuilder.java",
    "content": "/*\n * Copyright (C) 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.JweHeaderMutator;\nimport io.jsonwebtoken.security.X509Builder;\n\n/**\n * @param <T> return type for method chaining\n * @since 0.12.0\n */\npublic class DefaultJweHeaderBuilder<T extends JweHeaderMutator<T> & X509Builder<T>>\n        extends DefaultJweHeaderMutator<T> implements X509Builder<T> {\n\n    protected DefaultJweHeaderBuilder() {\n        super();\n    }\n\n    protected DefaultJweHeaderBuilder(DefaultJweHeaderMutator<?> src) {\n        super(src);\n    }\n\n    @Override\n    public T x509Sha1Thumbprint(boolean enable) {\n        this.x509.x509Sha1Thumbprint(enable);\n        return self();\n    }\n\n    @Override\n    public T x509Sha256Thumbprint(boolean enable) {\n        this.x509.x509Sha256Thumbprint(enable);\n        return self();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJweHeaderMutator.java",
    "content": "/*\n * Copyright (C) 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.JweHeaderMutator;\nimport io.jsonwebtoken.impl.lang.DefaultNestedCollection;\nimport io.jsonwebtoken.impl.lang.DelegatingMapMutator;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.security.X509BuilderSupport;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.NestedCollection;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\n\n/**\n * @param <T> return type for method chaining\n * @since 0.12.0\n */\npublic class DefaultJweHeaderMutator<T extends JweHeaderMutator<T>>\n        extends DelegatingMapMutator<String, Object, ParameterMap, T> implements JweHeaderMutator<T> {\n\n    protected X509BuilderSupport x509;\n\n    public DefaultJweHeaderMutator() {\n        // Any type of header can be created, but JWE parameters reflect all potential standard ones, so we use those\n        // params to catch any value being set, especially through generic 'put' or 'putAll' methods:\n        super(new ParameterMap(DefaultJweHeader.PARAMS));\n        clear(); // initialize new X509Builder\n    }\n\n    public DefaultJweHeaderMutator(DefaultJweHeaderMutator<?> src) {\n        super(src.DELEGATE);\n        this.x509 = src.x509;\n    }\n\n    // =============================================================\n    // MapMutator methods\n    // =============================================================\n\n    private <F> T put(Parameter<F> param, F value) {\n        this.DELEGATE.put(param, value);\n        return self();\n    }\n\n    @Override\n    public void clear() {\n        super.clear();\n        this.x509 = new X509BuilderSupport(this.DELEGATE, IllegalStateException.class);\n    }\n\n    // =============================================================\n    // JWT Header methods\n    // =============================================================\n\n//    @Override\n//    public T algorithm(String alg) {\n//        return put(DefaultHeader.ALGORITHM, alg);\n//    }\n\n    @Override\n    public T contentType(String cty) {\n        return put(DefaultHeader.CONTENT_TYPE, cty);\n    }\n\n    @Override\n    public T type(String typ) {\n        return put(DefaultHeader.TYPE, typ);\n    }\n\n    @Override\n    public T setType(String typ) {\n        return type(typ);\n    }\n\n    @Override\n    public T setContentType(String cty) {\n        return contentType(cty);\n    }\n\n    @Override\n    public T setCompressionAlgorithm(String zip) {\n        return put(DefaultHeader.COMPRESSION_ALGORITHM, zip);\n    }\n\n    // =============================================================\n    // Protected Header methods\n    // =============================================================\n\n    @Override\n    public NestedCollection<String, T> critical() {\n        return new DefaultNestedCollection<String, T>(self(), this.DELEGATE.get(DefaultProtectedHeader.CRIT)) {\n            @Override\n            protected void changed() {\n                put(DefaultProtectedHeader.CRIT, Collections.asSet(getCollection()));\n            }\n        };\n    }\n\n    @Override\n    public T jwk(PublicJwk<?> jwk) {\n        return put(DefaultProtectedHeader.JWK, jwk);\n    }\n\n    @Override\n    public T jwkSetUrl(URI uri) {\n        return put(DefaultProtectedHeader.JKU, uri);\n    }\n\n    @Override\n    public T keyId(String kid) {\n        return put(DefaultProtectedHeader.KID, kid);\n    }\n\n    @Override\n    public T setKeyId(String kid) {\n        return keyId(kid);\n    }\n\n    @Override\n    public T setAlgorithm(String alg) {\n        return put(DefaultHeader.ALGORITHM, alg);\n    }\n\n    // =============================================================\n    // X.509 methods\n    // =============================================================\n\n    @Override\n    public T x509Url(URI uri) {\n        this.x509.x509Url(uri);\n        return self();\n    }\n\n    @Override\n    public T x509Chain(List<X509Certificate> chain) {\n        this.x509.x509Chain(chain);\n        return self();\n    }\n\n    @Override\n    public T x509Sha1Thumbprint(byte[] thumbprint) {\n        this.x509.x509Sha1Thumbprint(thumbprint);\n        return self();\n    }\n\n    @Override\n    public T x509Sha256Thumbprint(byte[] thumbprint) {\n        this.x509.x509Sha256Thumbprint(thumbprint);\n        return self();\n    }\n\n    // =============================================================\n    // JWE Header methods\n    // =============================================================\n\n    @Override\n    public T agreementPartyUInfo(byte[] info) {\n        return put(DefaultJweHeader.APU, info);\n    }\n\n    @Override\n    public T agreementPartyUInfo(String info) {\n        return agreementPartyUInfo(Strings.utf8(Strings.clean(info)));\n    }\n\n    @Override\n    public T agreementPartyVInfo(byte[] info) {\n        return put(DefaultJweHeader.APV, info);\n    }\n\n    @Override\n    public T agreementPartyVInfo(String info) {\n        return agreementPartyVInfo(Strings.utf8(Strings.clean(info)));\n    }\n\n    @Override\n    public T pbes2Count(int count) {\n        return put(DefaultJweHeader.P2C, count);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJws.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Jws;\nimport io.jsonwebtoken.JwsHeader;\nimport io.jsonwebtoken.JwtVisitor;\n\npublic class DefaultJws<P> extends DefaultProtectedJwt<JwsHeader, P> implements Jws<P> {\n\n    private static final String DIGEST_NAME = \"signature\";\n\n    private final String signature;\n\n    public DefaultJws(JwsHeader header, P payload, byte[] signature, String b64UrlSig) {\n        super(header, payload, signature, DIGEST_NAME);\n        this.signature = b64UrlSig;\n    }\n\n    @Override\n    public String getSignature() {\n        return this.signature;\n    }\n\n    @Override\n    public <T> T accept(JwtVisitor<T> v) {\n        return v.visit(this);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJwsHeader.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.JwsHeader;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Registry;\n\nimport java.util.Map;\nimport java.util.Set;\n\npublic class DefaultJwsHeader extends DefaultProtectedHeader implements JwsHeader {\n\n    // https://datatracker.ietf.org/doc/html/rfc7797#section-3 :\n    static final Parameter<Boolean> B64 = Parameters.builder(Boolean.class)\n            .setId(\"b64\").setName(\"Base64url-Encode Payload\").build();\n\n    static final Registry<String, Parameter<?>> PARAMS = Parameters.registry(DefaultProtectedHeader.PARAMS, B64);\n\n    public DefaultJwsHeader(Map<String, ?> map) {\n        super(PARAMS, map);\n    }\n\n    @Override\n    public String getName() {\n        return \"JWS header\";\n    }\n\n    @Override\n    public boolean isPayloadEncoded() {\n        Set<String> crit = Collections.nullSafe(getCritical());\n        Boolean b64 = get(B64);\n        return b64 == null || b64 || !crit.contains(B64.getId());\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJwt.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.Jwt;\nimport io.jsonwebtoken.JwtVisitor;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\n\npublic class DefaultJwt<H extends Header, P> implements Jwt<H, P> {\n\n    private final H header;\n    private final P payload;\n\n    public DefaultJwt(H header, P payload) {\n        this.header = Assert.notNull(header, \"header cannot be null.\");\n        this.payload = Assert.notNull(payload, \"payload cannot be null.\");\n    }\n\n    @Override\n    public H getHeader() {\n        return header;\n    }\n\n    @Override\n    public P getBody() {\n        return getPayload();\n    }\n\n    @Override\n    public P getPayload() {\n        return this.payload;\n    }\n\n    protected StringBuilder toStringBuilder() {\n        StringBuilder sb = new StringBuilder(100);\n        sb.append(\"header=\").append(header).append(\",payload=\");\n        if (payload instanceof byte[]) {\n            String encoded = Encoders.BASE64URL.encode((byte[]) payload);\n            sb.append(encoded);\n        } else {\n            sb.append(payload);\n        }\n        return sb;\n    }\n\n    @Override\n    public final String toString() {\n        return toStringBuilder().toString();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) {\n            return true;\n        }\n        if (obj instanceof Jwt) {\n            Jwt<?, ?> jwt = (Jwt<?, ?>) obj;\n            return Objects.nullSafeEquals(header, jwt.getHeader()) &&\n                    Objects.nullSafeEquals(payload, jwt.getPayload());\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.nullSafeHashCode(header, payload);\n    }\n\n    @Override\n    public <T> T accept(JwtVisitor<T> v) {\n        return v.visit(this);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtBuilder.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.JwsHeader;\nimport io.jsonwebtoken.JwtBuilder;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.impl.io.Base64UrlStreamEncoder;\nimport io.jsonwebtoken.impl.io.ByteBase64UrlStreamEncoder;\nimport io.jsonwebtoken.impl.io.CountingInputStream;\nimport io.jsonwebtoken.impl.io.EncodingOutputStream;\nimport io.jsonwebtoken.impl.io.NamedSerializer;\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.io.UncloseableInputStream;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.impl.lang.Functions;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Services;\nimport io.jsonwebtoken.impl.security.DefaultAeadRequest;\nimport io.jsonwebtoken.impl.security.DefaultAeadResult;\nimport io.jsonwebtoken.impl.security.DefaultKeyRequest;\nimport io.jsonwebtoken.impl.security.DefaultSecureRequest;\nimport io.jsonwebtoken.impl.security.Pbes2HsAkwAlgorithm;\nimport io.jsonwebtoken.impl.security.ProviderKey;\nimport io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithms;\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.Encoder;\nimport io.jsonwebtoken.io.Serializer;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.AeadRequest;\nimport io.jsonwebtoken.security.AeadResult;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.KeyResult;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SecurityException;\nimport io.jsonwebtoken.security.SignatureException;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.SequenceInputStream;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.util.Date;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class DefaultJwtBuilder implements JwtBuilder {\n\n    private static final String PUB_KEY_SIGN_MSG = \"PublicKeys may not be used to create digital signatures. \" +\n            \"PrivateKeys are used to sign, and PublicKeys are used to verify.\";\n\n    private static final String PRIV_KEY_ENC_MSG = \"PrivateKeys may not be used to encrypt data. PublicKeys are \" +\n            \"used to encrypt, and PrivateKeys are used to decrypt.\";\n\n    protected Provider provider;\n    protected SecureRandom secureRandom;\n\n    private final DefaultBuilderHeader headerBuilder;\n    private final DefaultBuilderClaims claimsBuilder;\n\n    private Payload payload = Payload.EMPTY;\n\n    private SecureDigestAlgorithm<Key, ?> sigAlg = Jwts.SIG.NONE;\n    private Function<SecureRequest<InputStream, Key>, byte[]> signFunction;\n\n    private AeadAlgorithm enc; // MUST be Symmetric AEAD per https://tools.ietf.org/html/rfc7516#section-4.1.2\n\n    private KeyAlgorithm<Key, ?> keyAlg;\n    private Function<KeyRequest<Key>, KeyResult> keyAlgFunction;\n\n    private Key key;\n\n    private Serializer<Map<String, ?>> serializer;\n\n    protected Encoder<OutputStream, OutputStream> encoder = Base64UrlStreamEncoder.INSTANCE;\n    private boolean encodePayload = true;\n    protected CompressionAlgorithm compressionAlgorithm;\n\n    public DefaultJwtBuilder() {\n        this.headerBuilder = new DefaultBuilderHeader(this);\n        this.claimsBuilder = new DefaultBuilderClaims(this);\n    }\n\n    @Override\n    public BuilderHeader header() {\n        return this.headerBuilder;\n    }\n\n    @Override\n    public BuilderClaims claims() {\n        return this.claimsBuilder;\n    }\n\n    @Override\n    public JwtBuilder provider(Provider provider) {\n        this.provider = provider;\n        return this;\n    }\n\n    @Override\n    public JwtBuilder random(SecureRandom secureRandom) {\n        this.secureRandom = secureRandom;\n        return this;\n    }\n\n    @Override\n    public JwtBuilder serializeToJsonWith(final Serializer<Map<String, ?>> serializer) {\n        return json(serializer);\n    }\n\n    @Override\n    public JwtBuilder json(Serializer<Map<String, ?>> serializer) {\n        this.serializer = Assert.notNull(serializer, \"JSON Serializer cannot be null.\");\n        return this;\n    }\n\n    @Override\n    public JwtBuilder base64UrlEncodeWith(Encoder<byte[], String> encoder) {\n        return b64Url(new ByteBase64UrlStreamEncoder(encoder));\n    }\n\n    @Override\n    public JwtBuilder b64Url(Encoder<OutputStream, OutputStream> encoder) {\n        Assert.notNull(encoder, \"encoder cannot be null.\");\n        this.encoder = encoder;\n        return this;\n    }\n\n    @Override\n    public JwtBuilder encodePayload(boolean b64) {\n        this.encodePayload = b64;\n        // clear out any previous values. They will be applied appropriately during compact()\n        String critParamId = DefaultProtectedHeader.CRIT.getId();\n        String b64Id = DefaultJwsHeader.B64.getId();\n        Set<String> crit = this.headerBuilder.get(DefaultProtectedHeader.CRIT);\n        crit = new LinkedHashSet<>(Collections.nullSafe(crit));\n        crit.remove(b64Id);\n        return header().delete(b64Id).add(critParamId, crit).and();\n    }\n\n    @Override\n    public JwtBuilder setHeader(Map<String, ?> map) {\n        return header().empty().add(map).and();\n    }\n\n    @Override\n    public JwtBuilder setHeaderParams(Map<String, ?> params) {\n        return header().add(params).and();\n    }\n\n    @Override\n    public JwtBuilder setHeaderParam(String name, Object value) {\n        return header().add(name, value).and();\n    }\n\n    protected static <K extends Key> SecureDigestAlgorithm<K, ?> forSigningKey(K key) {\n        Assert.notNull(key, \"Key cannot be null.\");\n        SecureDigestAlgorithm<K, ?> alg = StandardSecureDigestAlgorithms.findBySigningKey(key);\n        if (alg == null) {\n            String msg = \"Unable to determine a suitable MAC or Signature algorithm for the specified key using \" +\n                    \"available heuristics: either the key size is too weak be used with available algorithms, or the \" +\n                    \"key size is unavailable (e.g. if using a PKCS11 or HSM (Hardware Security Module) key store). \" +\n                    \"If you are using a PKCS11 or HSM keystore, consider using the \" +\n                    \"JwtBuilder.signWith(Key, SecureDigestAlgorithm) method instead.\";\n            throw new UnsupportedKeyException(msg);\n        }\n        return alg;\n    }\n\n    @Override\n    public JwtBuilder signWith(Key key) throws InvalidKeyException {\n        Assert.notNull(key, \"Key argument cannot be null.\");\n        SecureDigestAlgorithm<Key, ?> alg = forSigningKey(key); // https://github.com/jwtk/jjwt/issues/381\n        return signWith(key, alg);\n    }\n\n    @Override\n    public <K extends Key> JwtBuilder signWith(K key, final SecureDigestAlgorithm<? super K, ?> alg)\n            throws InvalidKeyException {\n\n        Assert.notNull(key, \"Key argument cannot be null.\");\n        if (key instanceof PublicKey) { // it's always wrong/insecure to try to create signatures with PublicKeys:\n            throw new IllegalArgumentException(PUB_KEY_SIGN_MSG);\n        }\n        // Implementation note:  Ordinarily Passwords should not be used to create secure digests because they usually\n        // lack the length or entropy necessary for secure cryptographic operations, and are prone to misuse.\n        // However, we DO NOT prevent them as arguments here (like the above PublicKey check) because\n        // it is conceivable that a custom SecureDigestAlgorithm implementation would allow Password instances\n        // so that it might perform its own internal key-derivation logic producing a key that is then used to create a\n        // secure hash.\n        //\n        // Even so, a fallback safety check is that JJWT's only out-of-the-box Password implementation\n        // (io.jsonwebtoken.impl.security.PasswordSpec) explicitly forbids calls to password.getEncoded() in all\n        // scenarios to avoid potential misuse, so a digest algorithm implementation would explicitly need to avoid\n        // this by calling toCharArray() instead.\n        //\n        // TLDR; the digest algorithm implementation has the final say whether a password instance is valid\n\n        Assert.notNull(alg, \"SignatureAlgorithm cannot be null.\");\n        String id = Assert.hasText(alg.getId(), \"SignatureAlgorithm id cannot be null or empty.\");\n        if (Jwts.SIG.NONE.getId().equalsIgnoreCase(id)) {\n            String msg = \"The 'none' JWS algorithm cannot be used to sign JWTs.\";\n            throw new IllegalArgumentException(msg);\n        }\n        this.key = key;\n        //noinspection unchecked\n        this.sigAlg = (SecureDigestAlgorithm<Key, ?>) alg;\n        this.signFunction = Functions.wrap(new Function<SecureRequest<InputStream, Key>, byte[]>() {\n            @Override\n            public byte[] apply(SecureRequest<InputStream, Key> request) {\n                return sigAlg.digest(request);\n            }\n        }, SignatureException.class, \"Unable to compute %s signature.\", id);\n        return this;\n    }\n\n    @SuppressWarnings({\"deprecation\", \"unchecked\"}) // TODO: remove method for 1.0\n    @Override\n    public JwtBuilder signWith(Key key, io.jsonwebtoken.SignatureAlgorithm alg) throws InvalidKeyException {\n        Assert.notNull(alg, \"SignatureAlgorithm cannot be null.\");\n        alg.assertValidSigningKey(key); //since 0.10.0 for https://github.com/jwtk/jjwt/issues/334\n        return signWith(key, (SecureDigestAlgorithm<? super Key, ?>) Jwts.SIG.get().forKey(alg.getValue()));\n    }\n\n    @SuppressWarnings(\"deprecation\") // TODO: remove method for 1.0\n    @Override\n    public JwtBuilder signWith(io.jsonwebtoken.SignatureAlgorithm alg, byte[] secretKeyBytes) throws InvalidKeyException {\n        Assert.notNull(alg, \"SignatureAlgorithm cannot be null.\");\n        Assert.notEmpty(secretKeyBytes, \"secret key byte array cannot be null or empty.\");\n        Assert.isTrue(alg.isHmac(), \"Key bytes may only be specified for HMAC signatures.  \" +\n                \"If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.\");\n        SecretKey key = new SecretKeySpec(secretKeyBytes, alg.getJcaName());\n        return signWith(key, alg);\n    }\n\n    @SuppressWarnings(\"deprecation\") // TODO: remove method for 1.0\n    @Override\n    public JwtBuilder signWith(io.jsonwebtoken.SignatureAlgorithm alg, String base64EncodedSecretKey) throws InvalidKeyException {\n        Assert.hasText(base64EncodedSecretKey, \"base64-encoded secret key cannot be null or empty.\");\n        Assert.isTrue(alg.isHmac(), \"Base64-encoded key bytes may only be specified for HMAC signatures.  \" +\n                \"If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.\");\n        byte[] bytes = Decoders.BASE64.decode(base64EncodedSecretKey);\n        return signWith(alg, bytes);\n    }\n\n    @SuppressWarnings(\"deprecation\") // TODO: remove method for 1.0\n    @Override\n    public JwtBuilder signWith(io.jsonwebtoken.SignatureAlgorithm alg, Key key) {\n        return signWith(key, alg);\n    }\n\n    @Override\n    public JwtBuilder encryptWith(SecretKey key, AeadAlgorithm enc) {\n        if (key instanceof Password) {\n            return encryptWith((Password) key, new Pbes2HsAkwAlgorithm(enc.getKeyBitLength()), enc);\n        }\n        return encryptWith(key, Jwts.KEY.DIRECT, enc);\n    }\n\n    @Override\n    public <K extends Key> JwtBuilder encryptWith(final K key, final KeyAlgorithm<? super K, ?> keyAlg, final AeadAlgorithm enc) {\n        this.enc = Assert.notNull(enc, \"Encryption algorithm cannot be null.\");\n        Assert.hasText(enc.getId(), \"Encryption algorithm id cannot be null or empty.\");\n\n        Assert.notNull(key, \"Encryption key cannot be null.\");\n        if (key instanceof PrivateKey) {\n            throw new IllegalArgumentException(PRIV_KEY_ENC_MSG);\n        }\n        Assert.notNull(keyAlg, \"KeyAlgorithm cannot be null.\");\n        final String algId = Assert.hasText(keyAlg.getId(), \"KeyAlgorithm id cannot be null or empty.\");\n\n        this.key = key;\n        //noinspection unchecked\n        this.keyAlg = (KeyAlgorithm<Key, ?>) keyAlg;\n        final KeyAlgorithm<Key, ?> alg = this.keyAlg;\n\n        final String cekMsg = \"Unable to obtain content encryption key from key management algorithm '%s'.\";\n        this.keyAlgFunction = Functions.wrap(new Function<KeyRequest<Key>, KeyResult>() {\n            @Override\n            public KeyResult apply(KeyRequest<Key> request) {\n                return alg.getEncryptionKey(request);\n            }\n        }, SecurityException.class, cekMsg, algId);\n\n        return this;\n    }\n\n    @Override\n    public JwtBuilder compressWith(CompressionAlgorithm alg) {\n        Assert.notNull(alg, \"CompressionAlgorithm cannot be null\");\n        Assert.hasText(alg.getId(), \"CompressionAlgorithm id cannot be null or empty.\");\n        this.compressionAlgorithm = alg;\n        // clear out any previous value that might have been there.  It'll be added back to match this\n        // specific algorithm in the compact() method implementation\n        return header().delete(DefaultHeader.COMPRESSION_ALGORITHM.getId()).and();\n    }\n\n    @Override\n    public JwtBuilder setPayload(String payload) {\n        return content(payload);\n    }\n\n    @Override\n    public JwtBuilder content(String content) {\n        if (Strings.hasText(content)) {\n            this.payload = new Payload(content, null);\n        }\n        return this;\n    }\n\n    @Override\n    public JwtBuilder content(byte[] content) {\n        if (!Bytes.isEmpty(content)) {\n            this.payload = new Payload(content, null);\n        }\n        return this;\n    }\n\n    @Override\n    public JwtBuilder content(InputStream in) {\n        if (in != null) {\n            this.payload = new Payload(in, null);\n        }\n        return this;\n    }\n\n    @Override\n    public JwtBuilder content(byte[] content, String cty) {\n        Assert.notEmpty(content, \"content byte array cannot be null or empty.\");\n        Assert.hasText(cty, \"Content Type String cannot be null or empty.\");\n        this.payload = new Payload(content, cty);\n        // clear out any previous value - it will be set appropriately during compact()\n        return header().delete(DefaultHeader.CONTENT_TYPE.getId()).and();\n    }\n\n    @Override\n    public JwtBuilder content(String content, String cty) throws IllegalArgumentException {\n        Assert.hasText(content, \"Content string cannot be null or empty.\");\n        Assert.hasText(cty, \"ContentType string cannot be null or empty.\");\n        this.payload = new Payload(content, cty);\n        // clear out any previous value - it will be set appropriately during compact()\n        return header().delete(DefaultHeader.CONTENT_TYPE.getId()).and();\n    }\n\n    @Override\n    public JwtBuilder content(InputStream in, String cty) throws IllegalArgumentException {\n        Assert.notNull(in, \"Payload InputStream cannot be null.\");\n        Assert.hasText(cty, \"ContentType string cannot be null or empty.\");\n        this.payload = new Payload(in, cty);\n        // clear out any previous value - it will be set appropriately during compact()\n        return header().delete(DefaultHeader.CONTENT_TYPE.getId()).and();\n    }\n\n    @Override\n    public JwtBuilder setClaims(Map<String, ?> claims) {\n        Assert.notNull(claims, \"Claims map cannot be null.\");\n        return claims().empty().add(claims).and();\n    }\n\n    @Override\n    public JwtBuilder addClaims(Map<String, ?> claims) {\n        return claims(claims);\n    }\n\n    @Override\n    public JwtBuilder claims(Map<String, ?> claims) {\n        return claims().add(claims).and();\n    }\n\n    @Override\n    public JwtBuilder claim(String name, Object value) {\n        return claims().add(name, value).and();\n    }\n\n    @Override\n    public JwtBuilder setIssuer(String iss) {\n        return issuer(iss);\n    }\n\n    @Override\n    public JwtBuilder issuer(String iss) {\n        return claims().issuer(iss).and();\n    }\n\n    @Override\n    public JwtBuilder setSubject(String sub) {\n        return subject(sub);\n    }\n\n    @Override\n    public JwtBuilder subject(String sub) {\n        return claims().subject(sub).and();\n    }\n\n    @Override\n    public JwtBuilder setAudience(String aud) {\n        //noinspection deprecation\n        return claims().setAudience(aud).and();\n    }\n\n    @Override\n    public AudienceCollection<JwtBuilder> audience() {\n        return new DelegateAudienceCollection<>((JwtBuilder) this, claims().audience());\n    }\n\n    @Override\n    public JwtBuilder setExpiration(Date exp) {\n        return expiration(exp);\n    }\n\n    @Override\n    public JwtBuilder expiration(Date exp) {\n        return claims().expiration(exp).and();\n    }\n\n    @Override\n    public JwtBuilder setNotBefore(Date nbf) {\n        return notBefore(nbf);\n    }\n\n    @Override\n    public JwtBuilder notBefore(Date nbf) {\n        return claims().notBefore(nbf).and();\n    }\n\n    @Override\n    public JwtBuilder setIssuedAt(Date iat) {\n        return issuedAt(iat);\n    }\n\n    @Override\n    public JwtBuilder issuedAt(Date iat) {\n        return claims().issuedAt(iat).and();\n    }\n\n    @Override\n    public JwtBuilder setId(String jti) {\n        return id(jti);\n    }\n\n    @Override\n    public JwtBuilder id(String jti) {\n        return claims().id(jti).and();\n    }\n\n    private void assertPayloadEncoding(String type) {\n        if (!this.encodePayload) {\n            String msg = \"Payload encoding may not be disabled for \" + type + \"s, only JWSs.\";\n            throw new IllegalArgumentException(msg);\n        }\n    }\n\n    @Override\n    public String compact() {\n\n        final boolean jwe = this.enc != null;\n\n        if (jwe && signFunction != null) {\n            String msg = \"Both 'signWith' and 'encryptWith' cannot be specified. Choose either one.\";\n            throw new IllegalStateException(msg);\n        }\n\n        Payload payload = Assert.stateNotNull(this.payload, \"Payload instance null, internal error\");\n        final Claims claims = this.claimsBuilder.build();\n\n        if (jwe && payload.isEmpty() && Collections.isEmpty(claims)) { // JWE payload can never be empty:\n            String msg = \"Encrypted JWTs must have either 'claims' or non-empty 'content'.\";\n            throw new IllegalStateException(msg);\n        } // otherwise JWS and Unprotected JWT payloads can be empty\n\n        if (!payload.isEmpty() && !Collections.isEmpty(claims)) {\n            throw new IllegalStateException(\"Both 'content' and 'claims' cannot be specified. Choose either one.\");\n        }\n\n        if (this.serializer == null) { // try to find one based on the services available\n            //noinspection unchecked\n            json(Services.get(Serializer.class));\n        }\n\n        if (!Collections.isEmpty(claims)) { // normalize so we have one object to deal with:\n            payload = new Payload(claims);\n        }\n        if (compressionAlgorithm != null && !payload.isEmpty()) {\n            payload.setZip(compressionAlgorithm);\n            this.headerBuilder.put(DefaultHeader.COMPRESSION_ALGORITHM.getId(), compressionAlgorithm.getId());\n        }\n\n        if (Strings.hasText(payload.getContentType())) {\n            // We retain the value from the content* calls to prevent accidental removal from\n            // header().empty() or header().delete calls\n            this.headerBuilder.contentType(payload.getContentType());\n        }\n\n        Provider keyProvider = ProviderKey.getProvider(this.key, this.provider);\n        Key key = ProviderKey.getKey(this.key);\n        if (jwe) {\n            return encrypt(payload, key, keyProvider);\n        } else if (key != null) {\n            return sign(payload, key, keyProvider);\n        } else {\n            return unprotected(payload);\n        }\n    }\n\n    // automatically closes the OutputStream\n    private void writeAndClose(String name, Map<String, ?> map, OutputStream out) {\n        try {\n            Serializer<Map<String, ?>> named = new NamedSerializer(name, this.serializer);\n            named.serialize(map, out);\n        } finally {\n            Objects.nullSafeClose(out);\n        }\n    }\n\n    private void writeAndClose(String name, final Payload payload, OutputStream out) {\n        out = payload.compress(out); // compression if necessary\n        if (payload.isClaims()) {\n            writeAndClose(name, payload.getRequiredClaims(), out);\n        } else {\n            try {\n                InputStream in = payload.toInputStream();\n                Streams.copy(in, out, new byte[4096], \"Unable to copy payload.\");\n            } finally {\n                Objects.nullSafeClose(out);\n            }\n        }\n    }\n\n    private String sign(final Payload payload, final Key key, final Provider provider) {\n\n        Assert.stateNotNull(key, \"Key is required.\"); // set by signWithWith*\n        Assert.stateNotNull(sigAlg, \"SignatureAlgorithm is required.\"); // invariant\n        Assert.stateNotNull(signFunction, \"Signature Algorithm function cannot be null.\");\n        Assert.stateNotNull(payload, \"Payload argument cannot be null.\");\n\n        final ByteArrayOutputStream jws = new ByteArrayOutputStream(4096);\n\n        // ----- header -----\n        this.headerBuilder.add(DefaultHeader.ALGORITHM.getId(), sigAlg.getId());\n        if (!this.encodePayload) { // b64 extension:\n            String id = DefaultJwsHeader.B64.getId();\n            this.headerBuilder.critical().add(id).and().add(id, false);\n        }\n        final JwsHeader header = Assert.isInstanceOf(JwsHeader.class, this.headerBuilder.build());\n        encodeAndWrite(\"JWS Protected Header\", header, jws);\n\n        // ----- separator -----\n        jws.write(DefaultJwtParser.SEPARATOR_CHAR);\n\n        // ----- payload -----\n        // Logic defined by table in https://datatracker.ietf.org/doc/html/rfc7797#section-3 :\n        InputStream signingInput;\n        InputStream payloadStream = null; // not needed unless b64 is enabled\n        if (this.encodePayload) {\n            encodeAndWrite(\"JWS Payload\", payload, jws);\n            signingInput = Streams.of(jws.toByteArray());\n        } else { // b64\n\n            // First, ensure we have the base64url header bytes + the SEPARATOR_CHAR byte:\n            InputStream prefixStream = Streams.of(jws.toByteArray());\n\n            // Next, b64 extension requires the raw (non-encoded) payload to be included directly in the signing input,\n            // so we ensure we have an input stream for that:\n            payloadStream = toInputStream(\"JWS Unencoded Payload\", payload);\n\n            if (!payload.isClaims()) {\n                payloadStream = new CountingInputStream(payloadStream); // we'll need to assert if it's empty later\n            }\n            if (payloadStream.markSupported()) {\n                payloadStream.mark(0); // to rewind\n            }\n\n            // (base64url header bytes + separator char) + raw payload bytes:\n            // and don't let the SequenceInputStream close the payloadStream in case reset is needed:\n            signingInput = new SequenceInputStream(prefixStream, new UncloseableInputStream(payloadStream));\n        }\n\n        byte[] signature;\n        try {\n            SecureRequest<InputStream, Key> request = new DefaultSecureRequest<>(signingInput, provider, secureRandom, key);\n            signature = signFunction.apply(request);\n\n            // now that we've calculated the signature, if using the b64 extension, and the payload is\n            // attached ('non-detached'), we need to include it in the jws before the signature token.\n            // (Note that if encodePayload is true, the payload has already been written to jws at this point, so\n            // we only need to write if encodePayload is false and the payload is attached):\n            if (!this.encodePayload) {\n                if (!payload.isCompressed() // don't print raw compressed bytes\n                        && (payload.isClaims() || payload.isString())) {\n                    // now add the payload to the jws output:\n                    Streams.copy(payloadStream, jws, new byte[8192], \"Unable to copy attached Payload InputStream.\");\n                }\n                if (payloadStream instanceof CountingInputStream && ((CountingInputStream) payloadStream).getCount() <= 0) {\n                    String msg = \"'b64' Unencoded payload option has been specified, but payload is empty.\";\n                    throw new IllegalStateException(msg);\n                }\n            }\n        } finally {\n            Streams.reset(payloadStream);\n        }\n\n        // ----- separator -----\n        jws.write(DefaultJwtParser.SEPARATOR_CHAR);\n\n        // ----- signature -----\n        encodeAndWrite(\"JWS Signature\", signature, jws);\n\n        return Strings.utf8(jws.toByteArray());\n    }\n\n    private String unprotected(final Payload content) {\n\n        Assert.stateNotNull(content, \"Content argument cannot be null.\");\n        assertPayloadEncoding(\"unprotected JWT\");\n\n        this.headerBuilder.add(DefaultHeader.ALGORITHM.getId(), Jwts.SIG.NONE.getId());\n\n        final ByteArrayOutputStream jwt = new ByteArrayOutputStream(512);\n\n        // ----- header -----\n        final Header header = this.headerBuilder.build();\n        encodeAndWrite(\"JWT Header\", header, jwt);\n\n        // ----- separator -----\n        jwt.write(DefaultJwtParser.SEPARATOR_CHAR);\n\n        // ----- payload -----\n        encodeAndWrite(\"JWT Payload\", content, jwt);\n\n        // ----- period terminator -----\n        jwt.write(DefaultJwtParser.SEPARATOR_CHAR); // https://www.rfc-editor.org/rfc/rfc7519#section-6.1\n\n        return Strings.ascii(jwt.toByteArray());\n    }\n\n    private void encrypt(final AeadRequest req, final AeadResult res) throws SecurityException {\n        Function<Object, Object> fn = Functions.wrap(new Function<Object, Object>() {\n            @Override\n            public Object apply(Object o) {\n                enc.encrypt(req, res);\n                return null;\n            }\n        }, SecurityException.class, \"%s encryption failed.\", enc.getId());\n        fn.apply(null);\n    }\n\n    private String encrypt(final Payload content, final Key key, final Provider keyProvider) {\n\n        Assert.stateNotNull(content, \"Payload argument cannot be null.\");\n        Assert.stateNotNull(key, \"Key is required.\"); // set by encryptWith*\n        Assert.stateNotNull(enc, \"Encryption algorithm is required.\"); // set by encryptWith*\n        Assert.stateNotNull(keyAlg, \"KeyAlgorithm is required.\"); //set by encryptWith*\n        Assert.stateNotNull(keyAlgFunction, \"KeyAlgorithm function cannot be null.\");\n        assertPayloadEncoding(\"JWE\");\n\n        InputStream plaintext = toInputStream(\"JWE Payload\", content);\n\n        //only expose (mutable) JweHeader functionality to KeyAlgorithm instances, not the full headerBuilder\n        // (which exposes this JwtBuilder and shouldn't be referenced by KeyAlgorithms):\n        JweHeader delegate = new DefaultMutableJweHeader(this.headerBuilder);\n        KeyRequest<Key> keyRequest = new DefaultKeyRequest<>(key, keyProvider, this.secureRandom, delegate, enc);\n        KeyResult keyResult = keyAlgFunction.apply(keyRequest);\n        Assert.stateNotNull(keyResult, \"KeyAlgorithm must return a KeyResult.\");\n\n        SecretKey cek = Assert.notNull(keyResult.getKey(), \"KeyResult must return a content encryption key.\");\n        byte[] encryptedCek = Assert.notNull(keyResult.getPayload(),\n                \"KeyResult must return an encrypted key byte array, even if empty.\");\n\n        this.headerBuilder.add(DefaultHeader.ALGORITHM.getId(), keyAlg.getId());\n        this.headerBuilder.put(DefaultJweHeader.ENCRYPTION_ALGORITHM.getId(), enc.getId());\n\n        final JweHeader header = Assert.isInstanceOf(JweHeader.class, this.headerBuilder.build(),\n                \"Invalid header created: \");\n\n        // ----- header -----\n        ByteArrayOutputStream jwe = new ByteArrayOutputStream(8192);\n        encodeAndWrite(\"JWE Protected Header\", header, jwe);\n\n        // JWE RFC requires AAD to be the ASCII bytes of the Base64URL-encoded header. Since the header bytes are\n        // already Base64URL-encoded at this point (via the encoder.wrap call just above), and Base64Url-encoding uses\n        // only ASCII characters, we don't need to use StandardCharsets.US_ASCII to explicitly convert here - just\n        // use the already-encoded (ascii) bytes:\n        InputStream aad = Streams.of(jwe.toByteArray());\n\n        // During encryption, the configured Provider applies to the KeyAlgorithm, not the AeadAlgorithm, mostly\n        // because all JVMs support the standard AeadAlgorithms (especially with BouncyCastle in the classpath).\n        // As such, the provider here is intentionally omitted (null):\n        // TODO: add encProvider(Provider) builder method that applies to this request only?\n        ByteArrayOutputStream ciphertextOut = new ByteArrayOutputStream(8192);\n        AeadRequest req = new DefaultAeadRequest(plaintext, null, secureRandom, cek, aad);\n        DefaultAeadResult res = new DefaultAeadResult(ciphertextOut);\n        encrypt(req, res);\n\n        byte[] iv = Assert.notEmpty(res.getIv(), \"Encryption result must have a non-empty initialization vector.\");\n        byte[] tag = Assert.notEmpty(res.getDigest(), \"Encryption result must have a non-empty authentication tag.\");\n        byte[] ciphertext = Assert.notEmpty(ciphertextOut.toByteArray(), \"Encryption result must have non-empty ciphertext.\");\n\n        jwe.write(DefaultJwtParser.SEPARATOR_CHAR);\n        encodeAndWrite(\"JWE Encrypted CEK\", encryptedCek, jwe);\n\n        jwe.write(DefaultJwtParser.SEPARATOR_CHAR);\n        encodeAndWrite(\"JWE Initialization Vector\", iv, jwe);\n\n        jwe.write(DefaultJwtParser.SEPARATOR_CHAR);\n        encodeAndWrite(\"JWE Ciphertext\", ciphertext, jwe);\n\n        jwe.write(DefaultJwtParser.SEPARATOR_CHAR);\n        encodeAndWrite(\"JWE AAD Tag\", tag, jwe);\n\n        return Strings.utf8(jwe.toByteArray());\n    }\n\n    private static class DefaultBuilderClaims extends DelegatingClaimsMutator<BuilderClaims> implements BuilderClaims {\n\n        private final JwtBuilder builder;\n\n        private DefaultBuilderClaims(JwtBuilder builder) {\n            super();\n            this.builder = builder;\n        }\n\n        @Override\n        public JwtBuilder and() {\n            return this.builder;\n        }\n\n        private io.jsonwebtoken.Claims build() {\n            return new DefaultClaims(this.DELEGATE);\n        }\n    }\n\n    private static class DefaultBuilderHeader extends DefaultJweHeaderBuilder<BuilderHeader> implements BuilderHeader {\n\n        private final JwtBuilder builder;\n\n        private DefaultBuilderHeader(JwtBuilder builder) {\n            super();\n            this.builder = Assert.notNull(builder, \"JwtBuilder cannot be null.\");\n        }\n\n        @Override\n        public JwtBuilder and() {\n            return builder;\n        }\n\n        @SuppressWarnings(\"SameParameterValue\")\n        private <T> T get(Parameter<T> param) {\n            return this.DELEGATE.get(param);\n        }\n\n        private Header build() {\n            return new DefaultJwtHeaderBuilder(this).build();\n        }\n    }\n\n    private OutputStream encode(OutputStream out, String name) {\n        out = this.encoder.encode(out);\n        return new EncodingOutputStream(out, \"base64url\", name);\n    }\n\n    private void encodeAndWrite(String name, Map<String, ?> map, OutputStream out) {\n        out = encode(out, name);\n        writeAndClose(name, map, out);\n    }\n\n    private void encodeAndWrite(String name, Payload payload, OutputStream out) {\n        out = encode(out, name);\n        writeAndClose(name, payload, out);\n    }\n\n    private void encodeAndWrite(String name, byte[] data, OutputStream out) {\n        out = encode(out, name);\n        Streams.writeAndClose(out, data, \"Unable to write bytes\");\n    }\n\n    private InputStream toInputStream(final String name, Payload payload) {\n        if (payload.isClaims() || payload.isCompressed()) {\n            ByteArrayOutputStream claimsOut = new ByteArrayOutputStream(8192);\n            writeAndClose(name, payload, claimsOut);\n            return Streams.of(claimsOut.toByteArray());\n        } else {\n            // No claims and not compressed, so just get the direct InputStream:\n            return Assert.stateNotNull(payload.toInputStream(), \"Payload InputStream cannot be null.\");\n        }\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwts class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<JwtBuilder> {\n        @Override\n        public JwtBuilder get() {\n            return new DefaultJwtBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtHeaderBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.lang.Collections;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\n/**\n * @since 0.12.0\n */\npublic class DefaultJwtHeaderBuilder extends DefaultJweHeaderBuilder<Jwts.HeaderBuilder> implements Jwts.HeaderBuilder {\n\n    public DefaultJwtHeaderBuilder() {\n        super();\n    }\n\n    public DefaultJwtHeaderBuilder(DefaultJweHeaderMutator<?> src) {\n        super(src);\n    }\n\n    // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11 and\n    // https://datatracker.ietf.org/doc/html/rfc7516#section-4.1.13, 'crit' values MUST NOT include:\n    //\n    // 1. Any header parameter names defined in the JWS or JWE specifications\n    // 2. Any header parameter names that are not included in the final header\n    private static ParameterMap sanitizeCrit(ParameterMap m, boolean protectedHeader) {\n        Set<String> crit = m.get(DefaultProtectedHeader.CRIT);\n        if (crit == null) return m; // nothing to do\n\n        //Use a copy constructor to ensure subsequent changes to builder state do not change the constructed header:\n        m = new ParameterMap(DefaultJweHeader.PARAMS, m, true);\n        m.remove(DefaultProtectedHeader.CRIT.getId()); // remove the unsanitized value\n\n        // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, non-protected headers are not allowed to\n        // have a 'crit' header parameter, so we're done, exit early:\n        if (!protectedHeader) return m;\n\n        //otherwise we have a protected header (JWS or JWE), so remove unnecessary entries per the RFC sections above:\n        Set<String> newCrit = new LinkedHashSet<>(crit);\n        for (String val : crit) {\n            if (DefaultJweHeader.PARAMS.containsKey(val) || // Defined in JWS or JWE spec, can't be in crit set (#1)\n                    !m.containsKey(val)) { // not in the actual header, can't be in crit set either (#2)\n                newCrit.remove(val);\n            }\n        }\n        if (!Collections.isEmpty(newCrit)) { // we have a sanitized result per the RFC, so apply it:\n            m.put(DefaultProtectedHeader.CRIT, newCrit);\n        }\n        return m;\n    }\n\n    @Override\n    public Header build() {\n\n        this.x509.apply(); // apply any X.509 values as necessary based on builder state\n\n        ParameterMap m = this.DELEGATE;\n\n        // Note: conditional sequence matters here: JWE has more specific requirements than JWS, so check that first:\n        if (DefaultJweHeader.isCandidate(m)) {\n            return new DefaultJweHeader(sanitizeCrit(m, true));\n        } else if (DefaultProtectedHeader.isCandidate(m)) {\n            return new DefaultJwsHeader(sanitizeCrit(m, true));\n        } else {\n            // Per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, 'crit' header is not allowed in\n            // non-protected headers:\n            return new DefaultHeader(sanitizeCrit(m, false));\n        }\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwts class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<Jwts.HeaderBuilder> {\n        @Override\n        public Jwts.HeaderBuilder get() {\n            return new DefaultJwtHeaderBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParser.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.ClaimsBuilder;\nimport io.jsonwebtoken.Clock;\nimport io.jsonwebtoken.CompressionCodecResolver;\nimport io.jsonwebtoken.ExpiredJwtException;\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.IncorrectClaimException;\nimport io.jsonwebtoken.Jwe;\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.Jws;\nimport io.jsonwebtoken.JwsHeader;\nimport io.jsonwebtoken.Jwt;\nimport io.jsonwebtoken.JwtException;\nimport io.jsonwebtoken.JwtHandler;\nimport io.jsonwebtoken.JwtParser;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.Locator;\nimport io.jsonwebtoken.MalformedJwtException;\nimport io.jsonwebtoken.MissingClaimException;\nimport io.jsonwebtoken.PrematureJwtException;\nimport io.jsonwebtoken.ProtectedHeader;\nimport io.jsonwebtoken.SigningKeyResolver;\nimport io.jsonwebtoken.UnsupportedJwtException;\nimport io.jsonwebtoken.impl.io.AbstractParser;\nimport io.jsonwebtoken.impl.io.BytesInputStream;\nimport io.jsonwebtoken.impl.io.CharSequenceReader;\nimport io.jsonwebtoken.impl.io.JsonObjectDeserializer;\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.io.UncloseableInputStream;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.impl.lang.RedactedSupplier;\nimport io.jsonwebtoken.impl.security.DefaultDecryptAeadRequest;\nimport io.jsonwebtoken.impl.security.DefaultDecryptionKeyRequest;\nimport io.jsonwebtoken.impl.security.DefaultVerifySecureDigestRequest;\nimport io.jsonwebtoken.impl.security.LocatingKeyResolver;\nimport io.jsonwebtoken.impl.security.ProviderKey;\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.DeserializationException;\nimport io.jsonwebtoken.io.Deserializer;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.DateFormats;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.DecryptAeadRequest;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\nimport io.jsonwebtoken.security.SignatureException;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\nimport io.jsonwebtoken.security.WeakKeyException;\n\nimport javax.crypto.SecretKey;\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.io.Reader;\nimport java.io.SequenceInputStream;\nimport java.nio.ByteBuffer;\nimport java.nio.CharBuffer;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\n@SuppressWarnings(\"unchecked\")\npublic class DefaultJwtParser extends AbstractParser<Jwt<?, ?>> implements JwtParser {\n\n    static final char SEPARATOR_CHAR = '.';\n\n    private static final JwtTokenizer jwtTokenizer = new JwtTokenizer();\n\n    static final String PRIV_KEY_VERIFY_MSG = \"PrivateKeys may not be used to verify digital signatures. \" +\n            \"PrivateKeys are used to sign, and PublicKeys are used to verify.\";\n\n    static final String PUB_KEY_DECRYPT_MSG = \"PublicKeys may not be used to decrypt data. PublicKeys are \" +\n            \"used to encrypt, and PrivateKeys are used to decrypt.\";\n\n    public static final String INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE = \"Expected %s claim to be: %s, but was: %s.\";\n\n    public static final String MISSING_EXPECTED_CLAIM_VALUE_MESSAGE_TEMPLATE =\n            \"Missing expected '%s' value in '%s' claim %s.\";\n\n    public static final String MISSING_JWS_ALG_MSG = \"JWS header does not contain a required 'alg' (Algorithm) \" +\n            \"header parameter.  This header parameter is mandatory per the JWS Specification, Section 4.1.1. See \" +\n            \"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.1 for more information.\";\n\n    public static final String MISSING_JWE_ALG_MSG = \"JWE header does not contain a required 'alg' (Algorithm) \" +\n            \"header parameter.  This header parameter is mandatory per the JWE Specification, Section 4.1.1. See \" +\n            \"https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.1 for more information.\";\n\n    public static final String MISSING_JWS_DIGEST_MSG_FMT = \"The JWS header references signature algorithm '%s' but \" +\n            \"the compact JWE string is missing the required signature.\";\n\n    public static final String MISSING_JWE_DIGEST_MSG_FMT = \"The JWE header references key management algorithm '%s' \" +\n            \"but the compact JWE string is missing the required AAD authentication tag.\";\n\n    private static final String MISSING_ENC_MSG = \"JWE header does not contain a required 'enc' (Encryption \" +\n            \"Algorithm) header parameter.  This header parameter is mandatory per the JWE Specification, \" +\n            \"Section 4.1.2. See https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.2 for more information.\";\n\n    private static final String UNSECURED_DISABLED_MSG_PREFIX = \"Unsecured JWSs (those with an \" +\n            DefaultHeader.ALGORITHM + \" header value of '\" + Jwts.SIG.NONE.getId() + \"') are disallowed by \" +\n            \"default as mandated by https://www.rfc-editor.org/rfc/rfc7518.html#section-3.6. If you wish to \" +\n            \"allow them to be parsed, call the JwtParserBuilder.unsecured() method, but please read the \" +\n            \"security considerations covered in that method's JavaDoc before doing so. Header: \";\n\n    private static final String CRIT_UNSECURED_MSG = \"Unsecured JWSs (those with an \" + DefaultHeader.ALGORITHM +\n            \" header value of '\" + Jwts.SIG.NONE.getId() + \"') may not use the \" + DefaultProtectedHeader.CRIT +\n            \" header parameter per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11 (\\\"the [crit] Header \" +\n            \"Parameter MUST be integrity protected; therefore, it MUST occur only within [a] JWS Protected Header)\\\".\" +\n            \" Header: %s\";\n\n    private static final String CRIT_MISSING_MSG = \"Protected Header \" +\n            DefaultProtectedHeader.CRIT + \" set references header name '%s', but the header does not contain an \" +\n            \"associated '%s' header parameter as required by \" +\n            \"https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11. Header: %s\";\n\n    private static final String CRIT_UNSUPPORTED_MSG = \"Protected Header \" + DefaultProtectedHeader.CRIT +\n            \" set references unsupported header name '%s'. Application developers expecting to support a JWT \" +\n            \"extension using header '%s' in their application code must indicate it \" +\n            \"is supported by using the JwtParserBuilder.critical method. Header: %s\";\n\n    private static final String JWE_NONE_MSG = \"JWEs do not support key management \" + DefaultHeader.ALGORITHM +\n            \" header value '\" + Jwts.SIG.NONE.getId() + \"' per \" +\n            \"https://www.rfc-editor.org/rfc/rfc7518.html#section-4.1\";\n\n    private static final String JWS_NONE_SIG_MISMATCH_MSG = \"The JWS header references signature algorithm '\" +\n            Jwts.SIG.NONE.getId() + \"' yet the compact JWS string contains a signature. This is not permitted \" +\n            \"per https://tools.ietf.org/html/rfc7518#section-3.6.\";\n\n    private static final String B64_MISSING_PAYLOAD = \"Unable to verify JWS signature: the parser has encountered an \" +\n            \"Unencoded Payload JWS with detached payload, but the detached payload value required for signature \" +\n            \"verification has not been provided. If you expect to receive and parse Unencoded Payload JWSs in your \" +\n            \"application, the overloaded JwtParser.parseSignedContent or JwtParser.parseSignedClaims methods that \" +\n            \"accept a byte[] or InputStream must be used for these kinds of JWSs. Header: %s\";\n\n    private static final String B64_DECOMPRESSION_MSG = \"The JWT header references compression algorithm \" +\n            \"'%s', but payload decompression for Unencoded JWSs (those with an \" + DefaultJwsHeader.B64 +\n            \" header value of false) that rely on a SigningKeyResolver are disallowed \" +\n            \"by default to protect against [Denial of Service attacks](\" +\n            \"https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf).  If you \" +\n            \"wish to enable Unencoded JWS payload decompression, configure the JwtParserBuilder.\" +\n            \"keyLocator(Locator) and do not configure a SigningKeyResolver.\";\n\n    private static final String UNPROTECTED_DECOMPRESSION_MSG = \"The JWT header references compression algorithm \" +\n            \"'%s', but payload decompression for Unprotected JWTs (those with an \" + DefaultHeader.ALGORITHM +\n            \" header value of '\" + Jwts.SIG.NONE.getId() + \"') or Unencoded JWSs (those with a \" +\n            DefaultJwsHeader.B64 + \" header value of false) that also rely on a SigningKeyResolver are disallowed \" +\n            \"by default to protect against [Denial of Service attacks](\" +\n            \"https://www.usenix.org/system/files/conference/usenixsecurity15/sec15-paper-pellegrino.pdf).  If you \" +\n            \"wish to enable Unsecure JWS or Unencoded JWS payload decompression, call the JwtParserBuilder.\" +\n            \"unsecuredDecompression() method, but please read the security considerations covered in that \" +\n            \"method's JavaDoc before doing so.\";\n\n    private final Provider provider;\n\n    @SuppressWarnings(\"deprecation\")\n    private final SigningKeyResolver signingKeyResolver;\n\n    private final boolean unsecured;\n\n    private final boolean unsecuredDecompression;\n\n    private final Function<JwsHeader, SecureDigestAlgorithm<?, ?>> sigAlgs;\n\n    private final Function<JweHeader, AeadAlgorithm> encAlgs;\n\n    private final Function<JweHeader, KeyAlgorithm<?, ?>> keyAlgs;\n\n    private final Function<Header, CompressionAlgorithm> zipAlgs;\n\n    private final Locator<? extends Key> keyLocator;\n\n    private final Decoder<InputStream, InputStream> decoder;\n\n    private final Deserializer<Map<String, ?>> deserializer;\n\n    private final ClaimsBuilder expectedClaims;\n\n    private final Clock clock;\n\n    private final Set<String> critical;\n\n    private final long allowedClockSkewMillis;\n\n    //SigningKeyResolver will be removed for 1.0:\n    @SuppressWarnings(\"deprecation\")\n    DefaultJwtParser(Provider provider,\n                     SigningKeyResolver signingKeyResolver,\n                     boolean unsecured,\n                     boolean unsecuredDecompression,\n                     Locator<? extends Key> keyLocator,\n                     Clock clock,\n                     Set<String> critical,\n                     long allowedClockSkewMillis,\n                     DefaultClaims expectedClaims,\n                     Decoder<InputStream, InputStream> base64UrlDecoder,\n                     Deserializer<Map<String, ?>> deserializer,\n                     CompressionCodecResolver compressionCodecResolver,\n                     Registry<String, CompressionAlgorithm> zipAlgs,\n                     Registry<String, SecureDigestAlgorithm<?, ?>> sigAlgs,\n                     Registry<String, KeyAlgorithm<?, ?>> keyAlgs,\n                     Registry<String, AeadAlgorithm> encAlgs) {\n        this.provider = provider;\n        this.unsecured = unsecured;\n        this.unsecuredDecompression = unsecuredDecompression;\n        this.signingKeyResolver = signingKeyResolver;\n        this.keyLocator = Assert.notNull(keyLocator, \"Key Locator cannot be null.\");\n        this.clock = Assert.notNull(clock, \"Clock cannot be null.\");\n        this.critical = Collections.nullSafe(critical);\n        this.allowedClockSkewMillis = allowedClockSkewMillis;\n        this.expectedClaims = Jwts.claims().add(expectedClaims);\n        this.decoder = Assert.notNull(base64UrlDecoder, \"base64UrlDecoder cannot be null.\");\n        this.deserializer = Assert.notNull(deserializer, \"JSON Deserializer cannot be null.\");\n        this.sigAlgs = new IdLocator<>(DefaultHeader.ALGORITHM, sigAlgs, \"mac or signature\", \"signature verification\", MISSING_JWS_ALG_MSG);\n        this.keyAlgs = new IdLocator<>(DefaultHeader.ALGORITHM, keyAlgs, \"key management\", \"decryption\", MISSING_JWE_ALG_MSG);\n        this.encAlgs = new IdLocator<>(DefaultJweHeader.ENCRYPTION_ALGORITHM, encAlgs, \"encryption\", \"decryption\", MISSING_ENC_MSG);\n        this.zipAlgs = compressionCodecResolver != null ? new CompressionCodecLocator(compressionCodecResolver) :\n                new IdLocator<>(DefaultHeader.COMPRESSION_ALGORITHM, zipAlgs, \"compression\", \"decompression\", null);\n    }\n\n    @Override\n    public boolean isSigned(CharSequence compact) {\n        if (!Strings.hasText(compact)) {\n            return false;\n        }\n        try {\n            final TokenizedJwt tokenized = jwtTokenizer.tokenize(new CharSequenceReader(compact));\n            return !(tokenized instanceof TokenizedJwe) && Strings.hasText(tokenized.getDigest());\n        } catch (MalformedJwtException e) {\n            return false;\n        }\n    }\n\n    private static boolean hasContentType(Header header) {\n        return header != null && Strings.hasText(header.getContentType());\n    }\n\n    private byte[] verifySignature(final TokenizedJwt tokenized, final JwsHeader jwsHeader, final String alg,\n                                   @SuppressWarnings(\"deprecation\") SigningKeyResolver resolver, Claims claims, Payload payload) {\n\n        Assert.notNull(resolver, \"SigningKeyResolver instance cannot be null.\");\n\n        SecureDigestAlgorithm<?, Key> algorithm;\n        try {\n            algorithm = (SecureDigestAlgorithm<?, Key>) sigAlgs.apply(jwsHeader);\n        } catch (UnsupportedJwtException e) {\n            //For backwards compatibility.  TODO: remove this try/catch block for 1.0 and let UnsupportedJwtException propagate\n            String msg = \"Unsupported signature algorithm '\" + alg + \"': \" + e.getMessage();\n            throw new SignatureException(msg, e);\n        }\n        Assert.stateNotNull(algorithm, \"JWS Signature Algorithm cannot be null.\");\n\n        //digitally signed, let's assert the signature:\n        Key key;\n        if (claims != null) {\n            key = resolver.resolveSigningKey(jwsHeader, claims);\n        } else {\n            key = resolver.resolveSigningKey(jwsHeader, payload.getBytes());\n        }\n        if (key == null) {\n            String msg = \"Cannot verify JWS signature: unable to locate signature verification key for JWS with header: \" + jwsHeader;\n            throw new UnsupportedJwtException(msg);\n        }\n        Provider provider = ProviderKey.getProvider(key, this.provider); // extract if necessary\n        key = ProviderKey.getKey(key); // unwrap if necessary, MUST be called after ProviderKey.getProvider\n        Assert.stateNotNull(key, \"ProviderKey cannot be null.\"); //ProviderKey impl doesn't allow null\n        if (key instanceof PrivateKey) {\n            throw new InvalidKeyException(PRIV_KEY_VERIFY_MSG);\n        }\n\n        final byte[] signature = decode(tokenized.getDigest(), \"JWS signature\");\n\n        //re-create the jwt part without the signature.  This is what is needed for signature verification:\n        InputStream payloadStream = null;\n        InputStream verificationInput;\n        if (jwsHeader.isPayloadEncoded()) {\n            int len = tokenized.getProtected().length() + 1 + tokenized.getPayload().length();\n            CharBuffer cb = CharBuffer.allocate(len);\n            cb.put(Strings.wrap(tokenized.getProtected()));\n            cb.put(SEPARATOR_CHAR);\n            cb.put(Strings.wrap(tokenized.getPayload()));\n            cb.rewind();\n            ByteBuffer bb = StandardCharsets.US_ASCII.encode(cb);\n            bb.rewind();\n            byte[] data = new byte[bb.remaining()];\n            bb.get(data);\n            verificationInput = Streams.of(data);\n        } else { // b64 extension\n            ByteBuffer headerBuf = StandardCharsets.US_ASCII.encode(Strings.wrap(tokenized.getProtected()));\n            headerBuf.rewind();\n            ByteBuffer buf = ByteBuffer.allocate(headerBuf.remaining() + 1);\n            buf.put(headerBuf);\n            buf.put((byte) SEPARATOR_CHAR);\n            buf.rewind();\n            byte[] data = new byte[buf.remaining()];\n            buf.get(data);\n            InputStream prefixStream = Streams.of(data);\n            payloadStream = payload.toInputStream();\n            // We wrap the payloadStream here in an UncloseableInputStream to prevent the SequenceInputStream from\n            // closing it since we'll need to rewind/reset it if decompression is enabled\n            verificationInput = new SequenceInputStream(prefixStream, new UncloseableInputStream(payloadStream));\n        }\n\n        try {\n            VerifySecureDigestRequest<Key> request =\n                    new DefaultVerifySecureDigestRequest<>(verificationInput, provider, null, key, signature);\n            if (!algorithm.verify(request)) {\n                String msg = \"JWT signature does not match locally computed signature. JWT validity cannot be \" +\n                        \"asserted and should not be trusted.\";\n                throw new SignatureException(msg);\n            }\n        } catch (WeakKeyException e) {\n            throw e;\n        } catch (InvalidKeyException | IllegalArgumentException e) {\n            String algId = algorithm.getId();\n            String msg = \"The parsed JWT indicates it was signed with the '\" + algId + \"' signature \" +\n                    \"algorithm, but the provided \" + key.getClass().getName() + \" key may \" +\n                    \"not be used to verify \" + algId + \" signatures.  Because the specified \" +\n                    \"key reflects a specific and expected algorithm, and the JWT does not reflect \" +\n                    \"this algorithm, it is likely that the JWT was not expected and therefore should not be \" +\n                    \"trusted.  Another possibility is that the parser was provided the incorrect \" +\n                    \"signature verification key, but this cannot be assumed for security reasons.\";\n            throw new UnsupportedJwtException(msg, e);\n        } finally {\n            Streams.reset(payloadStream);\n        }\n\n        return signature;\n    }\n\n    @Override\n    public Jwt<?, ?> parse(Reader reader) {\n        Assert.notNull(reader, \"Reader cannot be null.\");\n        return parse(reader, Payload.EMPTY);\n    }\n\n    private Jwt<?, ?> parse(Reader compact, Payload unencodedPayload) {\n\n        Assert.notNull(compact, \"Compact reader cannot be null.\");\n        Assert.stateNotNull(unencodedPayload, \"internal error: unencodedPayload is null.\");\n\n        final TokenizedJwt tokenized = jwtTokenizer.tokenize(compact);\n        final CharSequence base64UrlHeader = tokenized.getProtected();\n        if (!Strings.hasText(base64UrlHeader)) {\n            String msg = \"Compact JWT strings MUST always have a Base64Url protected header per \" +\n                    \"https://tools.ietf.org/html/rfc7519#section-7.2 (steps 2-4).\";\n            throw new MalformedJwtException(msg);\n        }\n\n        // =============== Header =================\n        final byte[] headerBytes = decode(base64UrlHeader, \"protected header\");\n        Map<String, ?> m = deserialize(Streams.of(headerBytes), \"protected header\");\n        Header header;\n        try {\n            header = tokenized.createHeader(m);\n        } catch (Exception e) {\n            String msg = \"Invalid protected header: \" + e.getMessage();\n            throw new MalformedJwtException(msg, e);\n        }\n\n        // https://tools.ietf.org/html/rfc7515#section-10.7 , second-to-last bullet point, note the use of 'always':\n        //\n        //   *  Require that the \"alg\" Header Parameter be carried in the JWS\n        //      Protected Header.  (This is always the case when using the JWS\n        //      Compact Serialization and is the approach taken by CMS [RFC6211].)\n        //\n        final String alg = Strings.clean(header.getAlgorithm());\n        if (!Strings.hasText(alg)) {\n            String msg = tokenized instanceof TokenizedJwe ? MISSING_JWE_ALG_MSG : MISSING_JWS_ALG_MSG;\n            throw new MalformedJwtException(msg);\n        }\n        final boolean unsecured = Jwts.SIG.NONE.getId().equalsIgnoreCase(alg);\n\n        final CharSequence base64UrlDigest = tokenized.getDigest();\n        final boolean hasDigest = Strings.hasText(base64UrlDigest);\n        if (unsecured) {\n            if (tokenized instanceof TokenizedJwe) {\n                throw new MalformedJwtException(JWE_NONE_MSG);\n            }\n            // Unsecured JWTs are disabled by default per the RFC:\n            if (!this.unsecured) {\n                String msg = UNSECURED_DISABLED_MSG_PREFIX + header;\n                throw new UnsupportedJwtException(msg);\n            }\n            if (hasDigest) {\n                throw new MalformedJwtException(JWS_NONE_SIG_MISMATCH_MSG);\n            }\n            if (header.containsKey(DefaultProtectedHeader.CRIT.getId())) {\n                String msg = String.format(CRIT_UNSECURED_MSG, header);\n                throw new MalformedJwtException(msg);\n            }\n        } else if (!hasDigest) { // something other than 'none'.  Must have a digest component:\n            String fmt = tokenized instanceof TokenizedJwe ? MISSING_JWE_DIGEST_MSG_FMT : MISSING_JWS_DIGEST_MSG_FMT;\n            String msg = String.format(fmt, alg);\n            throw new MalformedJwtException(msg);\n        }\n        // ----- crit assertions -----\n        if (header instanceof ProtectedHeader) {\n            Set<String> crit = Collections.nullSafe(((ProtectedHeader) header).getCritical());\n            Set<String> supportedCrit = this.critical;\n            String b64Id = DefaultJwsHeader.B64.getId();\n            if (!unencodedPayload.isEmpty() && !this.critical.contains(b64Id)) {\n                // The application developer explicitly indicates they're using a B64 payload, so\n                // ensure that the B64 crit header is supported, even if they forgot to configure it on the\n                // parser builder:\n                supportedCrit = new LinkedHashSet<>(Collections.size(this.critical) + 1);\n                supportedCrit.add(DefaultJwsHeader.B64.getId());\n                supportedCrit.addAll(this.critical);\n            }\n            // assert any values per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11:\n            for (String name : crit) {\n                if (!header.containsKey(name)) {\n                    String msg = String.format(CRIT_MISSING_MSG, name, name, header);\n                    throw new MalformedJwtException(msg);\n                }\n                if (!supportedCrit.contains(name)) {\n                    String msg = String.format(CRIT_UNSUPPORTED_MSG, name, name, header);\n                    throw new UnsupportedJwtException(msg);\n                }\n            }\n        }\n\n        // =============== Payload =================\n        final CharSequence payloadToken = tokenized.getPayload();\n        Payload payload;\n        boolean integrityVerified = false; // only true after successful signature verification or AEAD decryption\n\n        // check if b64 extension enabled:\n        final boolean payloadBase64UrlEncoded = !(header instanceof JwsHeader) || ((JwsHeader) header).isPayloadEncoded();\n        if (payloadBase64UrlEncoded) {\n            // standard encoding, so decode it:\n            byte[] data = decode(payloadToken, \"payload\");\n            payload = new Payload(data, header.getContentType());\n        } else {\n            // The JWT uses the b64 extension, and we already know the parser supports that extension at this point\n            // in the code execution path because of the ----- crit ----- assertions section above as well as the\n            // (JwsHeader).isPayloadEncoded() check\n            if (Strings.hasText(payloadToken)) {\n                // we need to verify what was in the token, otherwise it'd be a security issue if we ignored it\n                // and assumed the (likely safe) unencodedPayload value instead:\n                payload = new Payload(payloadToken, header.getContentType());\n            } else {\n                //no payload token (a detached payload), so we need to ensure that they've specified the payload value:\n                if (unencodedPayload.isEmpty()) {\n                    String msg = String.format(B64_MISSING_PAYLOAD, header);\n                    throw new SignatureException(msg);\n                }\n                // otherwise, use the specified payload:\n                payload = unencodedPayload;\n            }\n        }\n\n        if (tokenized instanceof TokenizedJwe && payload.isEmpty()) {\n            // Only JWS payload can be empty per https://github.com/jwtk/jjwt/pull/540\n            String msg = \"Compact JWE strings MUST always contain a payload (ciphertext).\";\n            throw new MalformedJwtException(msg);\n        }\n\n        byte[] iv = null;\n        byte[] digest = null; // either JWE AEAD tag or JWS signature after Base64Url-decoding\n        if (tokenized instanceof TokenizedJwe) {\n\n            TokenizedJwe tokenizedJwe = (TokenizedJwe) tokenized;\n            JweHeader jweHeader = Assert.stateIsInstance(JweHeader.class, header, \"Not a JweHeader. \");\n\n            // Ensure both an 'alg' and 'enc' header value exists and is supported before spending time/effort\n            // base64Url-decoding anything:\n            final AeadAlgorithm encAlg = this.encAlgs.apply(jweHeader);\n            Assert.stateNotNull(encAlg, \"JWE Encryption Algorithm cannot be null.\");\n            @SuppressWarnings(\"rawtypes\") final KeyAlgorithm keyAlg = this.keyAlgs.apply(jweHeader);\n            Assert.stateNotNull(keyAlg, \"JWE Key Algorithm cannot be null.\");\n\n            byte[] cekBytes = Bytes.EMPTY; //ignored unless using an encrypted key algorithm\n            CharSequence base64Url = tokenizedJwe.getEncryptedKey();\n            if (Strings.hasText(base64Url)) {\n                cekBytes = decode(base64Url, \"JWE encrypted key\");\n                if (Bytes.isEmpty(cekBytes)) {\n                    String msg = \"Compact JWE string represents an encrypted key, but the key is empty.\";\n                    throw new MalformedJwtException(msg);\n                }\n            }\n\n            base64Url = tokenizedJwe.getIv();\n            if (Strings.hasText(base64Url)) {\n                iv = decode(base64Url, \"JWE Initialization Vector\");\n            }\n            if (Bytes.isEmpty(iv)) {\n                String msg = \"Compact JWE strings must always contain an Initialization Vector.\";\n                throw new MalformedJwtException(msg);\n            }\n\n            // The AAD (Additional Authenticated Data) scheme for compact JWEs is to use the ASCII bytes of the\n            // raw base64url text as the AAD, and NOT the base64url-decoded bytes per\n            // https://www.rfc-editor.org/rfc/rfc7516.html#section-5.1, Step 14.\n            ByteBuffer buf = StandardCharsets.US_ASCII.encode(Strings.wrap(base64UrlHeader));\n            final byte[] aadBytes = new byte[buf.remaining()];\n            buf.get(aadBytes);\n            InputStream aad = Streams.of(aadBytes);\n\n            base64Url = base64UrlDigest;\n            //guaranteed to be non-empty via the `alg` + digest check above:\n            Assert.hasText(base64Url, \"JWE AAD Authentication Tag cannot be null or empty.\");\n            digest = decode(base64Url, \"JWE AAD Authentication Tag\");\n            if (Bytes.isEmpty(digest)) {\n                String msg = \"Compact JWE strings must always contain an AAD Authentication Tag.\";\n                throw new MalformedJwtException(msg);\n            }\n\n            Key key = this.keyLocator.locate(jweHeader);\n            if (key == null) {\n                String msg = \"Cannot decrypt JWE payload: unable to locate key for JWE with header: \" + jweHeader;\n                throw new UnsupportedJwtException(msg);\n            }\n            if (key instanceof PublicKey) {\n                throw new InvalidKeyException(PUB_KEY_DECRYPT_MSG);\n            }\n\n            // extract key-specific provider if necessary;\n            Provider provider = ProviderKey.getProvider(key, this.provider);\n            key = ProviderKey.getKey(key); // this must be called after ProviderKey.getProvider\n            DecryptionKeyRequest<Key> request =\n                    new DefaultDecryptionKeyRequest<>(cekBytes, provider, null, jweHeader, encAlg, key);\n            final SecretKey cek = keyAlg.getDecryptionKey(request);\n            if (cek == null) {\n                String msg = \"The '\" + keyAlg.getId() + \"' JWE key algorithm did not return a decryption key. \" +\n                        \"Unable to perform '\" + encAlg.getId() + \"' decryption.\";\n                throw new IllegalStateException(msg);\n            }\n\n            // During decryption, the available Provider applies to the KeyAlgorithm, not the AeadAlgorithm, mostly\n            // because all JVMs support the standard AeadAlgorithms (especially with BouncyCastle in the classpath).\n            // As such, the provider here is intentionally omitted (null):\n            // TODO: add encProvider(Provider) builder method that applies to this request only?\n            InputStream ciphertext = payload.toInputStream();\n            ByteArrayOutputStream plaintext = new ByteArrayOutputStream(8192);\n            DecryptAeadRequest dreq = new DefaultDecryptAeadRequest(ciphertext, cek, aad, iv, digest);\n            encAlg.decrypt(dreq, plaintext);\n            payload = new Payload(plaintext.toByteArray(), header.getContentType());\n\n            integrityVerified = true; // AEAD performs integrity verification, so no exception = verified\n\n        } else if (hasDigest && this.signingKeyResolver == null) { //TODO: for 1.0, remove the == null check\n            // not using a signing key resolver, so we can verify the signature before reading the payload, which is\n            // always safer:\n            JwsHeader jwsHeader = Assert.stateIsInstance(JwsHeader.class, header, \"Not a JwsHeader. \");\n            digest = verifySignature(tokenized, jwsHeader, alg, new LocatingKeyResolver(this.keyLocator), null, payload);\n            integrityVerified = true; // no exception means signature verified\n        }\n\n        final CompressionAlgorithm compressionAlgorithm = zipAlgs.apply(header);\n        if (compressionAlgorithm != null) {\n            if (!integrityVerified) {\n                if (!payloadBase64UrlEncoded) {\n                    String msg = String.format(B64_DECOMPRESSION_MSG, compressionAlgorithm.getId());\n                    throw new UnsupportedJwtException(msg);\n                } else if (!unsecuredDecompression) {\n                    String msg = String.format(UNPROTECTED_DECOMPRESSION_MSG, compressionAlgorithm.getId());\n                    throw new UnsupportedJwtException(msg);\n                }\n            }\n            payload = payload.decompress(compressionAlgorithm);\n        }\n\n        Claims claims = null;\n        byte[] payloadBytes = payload.getBytes();\n        if (payload.isConsumable()) {\n            InputStream in = null;\n            try {\n                in = payload.toInputStream();\n\n                if (!hasContentType(header)) {   // If there is a content type set, then the application using JJWT is expected\n                    //                          to convert the byte payload themselves based on this content type\n                    //                          https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :\n                    //\n                    //                          \"This parameter is ignored by JWS implementations; any processing of this\n                    //                          parameter is performed by the JWS application.\"\n                    //\n                    Map<String, ?> claimsMap = null;\n                    try {\n                        // if deserialization fails, we'll need to rewind to convert to a byte array.  So if\n                        // mark/reset isn't possible, we'll need to buffer:\n                        if (!in.markSupported()) {\n                            in = new BufferedInputStream(in);\n                            in.mark(0);\n                        }\n                        claimsMap = deserialize(new UncloseableInputStream(in) /* Don't close in case we need to rewind */, \"claims\");\n                    } catch (DeserializationException |\n                             MalformedJwtException ignored) { // not JSON, treat it as a byte[]\n//                String msg = \"Invalid claims: \" + e.getMessage();\n//                throw new MalformedJwtException(msg, e);\n                    } finally {\n                        Streams.reset(in);\n                    }\n                    if (claimsMap != null) {\n                        try {\n                            claims = new DefaultClaims(claimsMap);\n                        } catch (Throwable t) {\n                            String msg = \"Invalid claims: \" + t.getMessage();\n                            throw new MalformedJwtException(msg);\n                        }\n                    }\n                }\n                if (claims == null) {\n                    // consumable, but not claims, so convert to byte array:\n                    payloadBytes = Streams.bytes(in, \"Unable to convert payload to byte array.\");\n                }\n            } finally { // always ensure closed per https://github.com/jwtk/jjwt/issues/949\n                Objects.nullSafeClose(in);\n            }\n        }\n\n        // =============== Post-SKR Signature Check =================\n        if (hasDigest && signingKeyResolver != null) { // TODO: remove for 1.0\n            // A SigningKeyResolver has been configured, and due to it's API, we have to verify the signature after\n            // parsing the body.  This can be a security risk, so it needs to be removed before 1.0\n            JwsHeader jwsHeader = Assert.stateIsInstance(JwsHeader.class, header, \"Not a JwsHeader. \");\n            digest = verifySignature(tokenized, jwsHeader, alg, this.signingKeyResolver, claims, payload);\n            //noinspection UnusedAssignment\n            integrityVerified = true; // no exception means verified successfully\n        }\n\n        Jwt<?, ?> jwt;\n        Object body = claims != null ? claims : payloadBytes;\n        if (header instanceof JweHeader) {\n            jwt = new DefaultJwe<>((JweHeader) header, body, iv, digest);\n        } else if (hasDigest) {\n            JwsHeader jwsHeader = Assert.isInstanceOf(JwsHeader.class, header, \"JwsHeader required.\");\n            jwt = new DefaultJws<>(jwsHeader, body, digest, base64UrlDigest.toString());\n        } else {\n            //noinspection rawtypes\n            jwt = new DefaultJwt(header, body);\n        }\n\n        final boolean allowSkew = this.allowedClockSkewMillis > 0;\n\n        //since 0.3:\n        if (claims != null) {\n\n            final Date now = this.clock.now();\n            long nowTime = now.getTime();\n\n            // https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.4\n            // token MUST NOT be accepted on or after any specified exp time:\n            Date exp = claims.getExpiration();\n            if (exp != null) {\n\n                long maxTime = nowTime - this.allowedClockSkewMillis;\n                Date max = allowSkew ? new Date(maxTime) : now;\n                if (max.after(exp)) {\n                    String expVal = DateFormats.formatIso8601(exp, true);\n                    String nowVal = DateFormats.formatIso8601(now, true);\n\n                    long differenceMillis = nowTime - exp.getTime();\n\n                    String msg = \"JWT expired \" + differenceMillis + \" milliseconds ago at \" + expVal + \". \" +\n                            \"Current time: \" + nowVal + \". Allowed clock skew: \" +\n                            this.allowedClockSkewMillis + \" milliseconds.\";\n                    throw new ExpiredJwtException(header, claims, msg);\n                }\n            }\n\n            // https://www.rfc-editor.org/rfc/rfc7519.html#section-4.1.5\n            // token MUST NOT be accepted before any specified nbf time:\n            Date nbf = claims.getNotBefore();\n            if (nbf != null) {\n\n                long minTime = nowTime + this.allowedClockSkewMillis;\n                Date min = allowSkew ? new Date(minTime) : now;\n                if (min.before(nbf)) {\n                    String nbfVal = DateFormats.formatIso8601(nbf, true);\n                    String nowVal = DateFormats.formatIso8601(now, true);\n\n                    long differenceMillis = nbf.getTime() - nowTime;\n\n                    String msg = \"JWT early by \" + differenceMillis + \" milliseconds before \" + nbfVal +\n                            \". Current time: \" + nowVal + \". Allowed clock skew: \" +\n                            this.allowedClockSkewMillis + \" milliseconds.\";\n                    throw new PrematureJwtException(header, claims, msg);\n                }\n            }\n\n            validateExpectedClaims(header, claims);\n        }\n\n        return jwt;\n    }\n\n    /**\n     * @since 0.10.0\n     */\n    private static Object normalize(Object o) {\n        if (o instanceof Integer) {\n            o = ((Integer) o).longValue();\n        }\n        return o;\n    }\n\n    private void validateExpectedClaims(Header header, Claims claims) {\n\n        final Claims expected = expectedClaims.build();\n\n        for (String expectedClaimName : expected.keySet()) {\n\n            Object expectedClaimValue = normalize(expected.get(expectedClaimName));\n            Object actualClaimValue = normalize(claims.get(expectedClaimName));\n\n            if (expectedClaimValue instanceof Date) {\n                try {\n                    actualClaimValue = claims.get(expectedClaimName, Date.class);\n                } catch (Exception e) {\n                    String msg = \"JWT Claim '\" + expectedClaimName + \"' was expected to be a Date, but its value \" +\n                            \"cannot be converted to a Date using current heuristics.  Value: \" + actualClaimValue;\n                    throw new IncorrectClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);\n                }\n            }\n\n            if (actualClaimValue == null) {\n                boolean collection = expectedClaimValue instanceof Collection;\n                String msg = \"Missing '\" + expectedClaimName + \"' claim. Expected value\";\n                if (collection) {\n                    msg += \"s: \" + expectedClaimValue;\n                } else {\n                    msg += \": \" + expectedClaimValue;\n                }\n                throw new MissingClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);\n            } else if (expectedClaimValue instanceof Collection) {\n                Collection<?> expectedValues = (Collection<?>) expectedClaimValue;\n                Collection<?> actualValues = actualClaimValue instanceof Collection ? (Collection<?>) actualClaimValue :\n                        Collections.setOf(actualClaimValue);\n                for (Object expectedValue : expectedValues) {\n                    if (!Collections.contains(actualValues.iterator(), expectedValue)) {\n                        String msg = String.format(MISSING_EXPECTED_CLAIM_VALUE_MESSAGE_TEMPLATE,\n                                expectedValue, expectedClaimName, actualValues);\n                        throw new IncorrectClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);\n                    }\n                }\n            } else if (!expectedClaimValue.equals(actualClaimValue)) {\n                String msg = String.format(INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE,\n                        expectedClaimName, expectedClaimValue, actualClaimValue);\n                throw new IncorrectClaimException(header, claims, expectedClaimName, expectedClaimValue, msg);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public <T> T parse(CharSequence compact, JwtHandler<T> handler) {\n        return parse(compact, Payload.EMPTY).accept(handler);\n    }\n\n    private Jwt<?, ?> parse(CharSequence compact, Payload unencodedPayload) {\n        Assert.hasText(compact, \"JWT String argument cannot be null or empty.\");\n        return parse(new CharSequenceReader(compact), unencodedPayload);\n    }\n\n    @Override\n    public Jwt<Header, byte[]> parseContentJwt(CharSequence jwt) {\n        return parse(jwt).accept(Jwt.UNSECURED_CONTENT);\n    }\n\n    @Override\n    public Jwt<Header, Claims> parseClaimsJwt(CharSequence jwt) {\n        return parse(jwt).accept(Jwt.UNSECURED_CLAIMS);\n    }\n\n    @Override\n    public Jws<byte[]> parseContentJws(CharSequence jws) {\n        return parseSignedContent(jws);\n    }\n\n    @Override\n    public Jws<Claims> parseClaimsJws(CharSequence jws) {\n        return parseSignedClaims(jws);\n    }\n\n    @Override\n    public Jwt<Header, byte[]> parseUnsecuredContent(CharSequence jwt) throws JwtException, IllegalArgumentException {\n        return parse(jwt).accept(Jwt.UNSECURED_CONTENT);\n    }\n\n    @Override\n    public Jwt<Header, Claims> parseUnsecuredClaims(CharSequence jwt) throws JwtException, IllegalArgumentException {\n        return parse(jwt).accept(Jwt.UNSECURED_CLAIMS);\n    }\n\n    @Override\n    public Jws<byte[]> parseSignedContent(CharSequence compact) {\n        return parse(compact).accept(Jws.CONTENT);\n    }\n\n    private Jws<byte[]> parseSignedContent(CharSequence jws, Payload unencodedPayload) {\n        return parse(jws, unencodedPayload).accept(Jws.CONTENT);\n    }\n\n    @Override\n    public Jws<Claims> parseSignedClaims(CharSequence compact) {\n        return parse(compact).accept(Jws.CLAIMS);\n    }\n\n    private Jws<Claims> parseSignedClaims(CharSequence jws, Payload unencodedPayload) {\n        unencodedPayload.setClaimsExpected(true);\n        return parse(jws, unencodedPayload).accept(Jws.CLAIMS);\n    }\n\n    @Override\n    public Jws<byte[]> parseSignedContent(CharSequence jws, byte[] unencodedPayload) {\n        Assert.notEmpty(unencodedPayload, \"unencodedPayload argument cannot be null or empty.\");\n        return parseSignedContent(jws, new Payload(unencodedPayload, null));\n    }\n\n    private static Payload payloadFor(InputStream in) {\n        if (in instanceof BytesInputStream) {\n            byte[] data = Streams.bytes(in, \"Unable to obtain payload InputStream bytes.\");\n            return new Payload(data, null);\n        }\n        //if (in.markSupported()) in.mark(0);\n        return new Payload(in, null);\n    }\n\n    @Override\n    public Jws<byte[]> parseSignedContent(CharSequence jws, InputStream unencodedPayload) {\n        Assert.notNull(unencodedPayload, \"unencodedPayload InputStream cannot be null.\");\n        return parseSignedContent(jws, payloadFor(unencodedPayload));\n    }\n\n    @Override\n    public Jws<Claims> parseSignedClaims(CharSequence jws, byte[] unencodedPayload) {\n        Assert.notEmpty(unencodedPayload, \"unencodedPayload argument cannot be null or empty.\");\n        return parseSignedClaims(jws, new Payload(unencodedPayload, null));\n    }\n\n    @Override\n    public Jws<Claims> parseSignedClaims(CharSequence jws, InputStream unencodedPayload) {\n        Assert.notNull(unencodedPayload, \"unencodedPayload InputStream cannot be null.\");\n        byte[] bytes = Streams.bytes(unencodedPayload,\n                \"Unable to obtain Claims bytes from unencodedPayload InputStream\");\n        return parseSignedClaims(jws, new Payload(bytes, null));\n    }\n\n    @Override\n    public Jwe<byte[]> parseEncryptedContent(CharSequence compact) throws JwtException {\n        return parse(compact).accept(Jwe.CONTENT);\n    }\n\n    @Override\n    public Jwe<Claims> parseEncryptedClaims(CharSequence compact) throws JwtException {\n        return parse(compact).accept(Jwe.CLAIMS);\n    }\n\n    protected byte[] decode(CharSequence base64UrlEncoded, String name) {\n        try {\n            InputStream decoding = this.decoder.decode(Streams.of(Strings.utf8(base64UrlEncoded)));\n            return Streams.bytes(decoding, \"Unable to Base64Url-decode input.\");\n        } catch (Throwable t) {\n            // Don't disclose potentially-sensitive information per https://github.com/jwtk/jjwt/issues/824:\n            String value = \"payload\".equals(name) ? RedactedSupplier.REDACTED_VALUE : base64UrlEncoded.toString();\n            String msg = \"Invalid Base64Url \" + name + \": \" + value;\n            throw new MalformedJwtException(msg, t);\n        }\n    }\n\n    protected Map<String, ?> deserialize(InputStream in, final String name) {\n        try {\n            Reader reader = Streams.reader(in);\n            JsonObjectDeserializer deserializer = new JsonObjectDeserializer(this.deserializer, name);\n            return deserializer.apply(reader);\n        } finally {\n            Objects.nullSafeClose(in);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultJwtParserBuilder.java",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.ClaimsBuilder;\nimport io.jsonwebtoken.Clock;\nimport io.jsonwebtoken.CompressionCodecResolver;\nimport io.jsonwebtoken.JwtParser;\nimport io.jsonwebtoken.JwtParserBuilder;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.Locator;\nimport io.jsonwebtoken.SigningKeyResolver;\nimport io.jsonwebtoken.impl.io.DelegateStringDecoder;\nimport io.jsonwebtoken.impl.io.StandardCompressionAlgorithms;\nimport io.jsonwebtoken.impl.lang.DefaultNestedCollection;\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.impl.lang.NestedIdentifiableCollection;\nimport io.jsonwebtoken.impl.lang.Services;\nimport io.jsonwebtoken.impl.security.ConstantKeyLocator;\nimport io.jsonwebtoken.impl.security.StandardEncryptionAlgorithms;\nimport io.jsonwebtoken.impl.security.StandardKeyAlgorithms;\nimport io.jsonwebtoken.impl.security.StandardSecureDigestAlgorithms;\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.Deserializer;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.NestedCollection;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.Keys;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\n\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @since 0.11.0\n */\npublic class DefaultJwtParserBuilder implements JwtParserBuilder {\n\n    private static final int MILLISECONDS_PER_SECOND = 1000;\n\n    /**\n     * To prevent overflow per <a href=\"https://github.com/jwtk/jjwt/issues/583\">Issue 583</a>.\n     * <p>\n     * Package-protected on purpose to allow use in backwards-compatible {@link DefaultJwtParser} implementation.\n     * TODO: enable private modifier on these two variables when deleting DefaultJwtParser\n     */\n    static final long MAX_CLOCK_SKEW_MILLIS = Long.MAX_VALUE / MILLISECONDS_PER_SECOND;\n    static final String MAX_CLOCK_SKEW_ILLEGAL_MSG = \"Illegal allowedClockSkewMillis value: multiplying this \" +\n            \"value by 1000 to obtain the number of milliseconds would cause a numeric overflow.\";\n\n    private Provider provider;\n\n    private boolean unsecured = false;\n\n    private boolean unsecuredDecompression = false;\n\n    private Locator<? extends Key> keyLocator;\n\n    @SuppressWarnings(\"deprecation\") //TODO: remove for 1.0\n    private SigningKeyResolver signingKeyResolver = null;\n\n    private Registry<String, AeadAlgorithm> encAlgs = Jwts.ENC.get();\n\n    private Registry<String, KeyAlgorithm<?, ?>> keyAlgs = Jwts.KEY.get();\n\n    private Registry<String, SecureDigestAlgorithm<?, ?>> sigAlgs = Jwts.SIG.get();\n\n    private Registry<String, CompressionAlgorithm> zipAlgs = Jwts.ZIP.get();\n\n    @SuppressWarnings(\"deprecation\")\n    private CompressionCodecResolver compressionCodecResolver;\n\n    @SuppressWarnings(\"deprecation\")\n    private Decoder<InputStream, InputStream> decoder = new DelegateStringDecoder(Decoders.BASE64URL);\n\n    private Deserializer<Map<String, ?>> deserializer;\n\n    private final ClaimsBuilder expectedClaims = Jwts.claims();\n\n    private Clock clock = DefaultClock.INSTANCE;\n\n    private Set<String> critical = Collections.emptySet();\n\n    private long allowedClockSkewMillis = 0;\n\n    private Key signatureVerificationKey;\n    private Key decryptionKey;\n\n    @Override\n    public JwtParserBuilder unsecured() {\n        this.unsecured = true;\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder unsecuredDecompression() {\n        this.unsecuredDecompression = true;\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder provider(Provider provider) {\n        this.provider = provider;\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder deserializeJsonWith(Deserializer<Map<String, ?>> deserializer) {\n        return json(deserializer);\n    }\n\n    @Override\n    public JwtParserBuilder json(Deserializer<Map<String, ?>> reader) {\n        this.deserializer = Assert.notNull(reader, \"JSON Deserializer cannot be null.\");\n        return this;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public JwtParserBuilder base64UrlDecodeWith(final Decoder<CharSequence, byte[]> decoder) {\n        Assert.notNull(decoder, \"decoder cannot be null.\");\n        return b64Url(new DelegateStringDecoder(decoder));\n    }\n\n    @Override\n    public JwtParserBuilder b64Url(Decoder<InputStream, InputStream> decoder) {\n        Assert.notNull(decoder, \"decoder cannot be null.\");\n        this.decoder = decoder;\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder requireIssuedAt(Date issuedAt) {\n        expectedClaims.setIssuedAt(issuedAt);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder requireIssuer(String issuer) {\n        expectedClaims.setIssuer(issuer);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder requireAudience(String audience) {\n        expectedClaims.audience().add(audience).and();\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder requireSubject(String subject) {\n        expectedClaims.setSubject(subject);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder requireId(String id) {\n        expectedClaims.setId(id);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder requireExpiration(Date expiration) {\n        expectedClaims.setExpiration(expiration);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder requireNotBefore(Date notBefore) {\n        expectedClaims.setNotBefore(notBefore);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder require(String claimName, Object value) {\n        Assert.hasText(claimName, \"claim name cannot be null or empty.\");\n        Assert.notNull(value, \"The value cannot be null for claim name: \" + claimName);\n        expectedClaims.add(claimName, value);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder setClock(Clock clock) {\n        return clock(clock);\n    }\n\n    @Override\n    public JwtParserBuilder clock(Clock clock) {\n        Assert.notNull(clock, \"Clock instance cannot be null.\");\n        this.clock = clock;\n        return this;\n    }\n\n    @Override\n    public NestedCollection<String, JwtParserBuilder> critical() {\n        return new DefaultNestedCollection<String, JwtParserBuilder>(this, this.critical) {\n            @Override\n            protected void changed() {\n                critical = Collections.asSet(getCollection());\n            }\n        };\n    }\n\n    @Override\n    public JwtParserBuilder setAllowedClockSkewSeconds(long seconds) throws IllegalArgumentException {\n        return clockSkewSeconds(seconds);\n    }\n\n    @Override\n    public JwtParserBuilder clockSkewSeconds(long seconds) throws IllegalArgumentException {\n        Assert.isTrue(seconds <= MAX_CLOCK_SKEW_MILLIS, MAX_CLOCK_SKEW_ILLEGAL_MSG);\n        this.allowedClockSkewMillis = Math.max(0, seconds * MILLISECONDS_PER_SECOND);\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder setSigningKey(byte[] key) {\n        Assert.notEmpty(key, \"signature verification key cannot be null or empty.\");\n        return setSigningKey(Keys.hmacShaKeyFor(key));\n    }\n\n    @Override\n    public JwtParserBuilder setSigningKey(String base64EncodedSecretKey) {\n        Assert.hasText(base64EncodedSecretKey, \"signature verification key cannot be null or empty.\");\n        byte[] bytes = Decoders.BASE64.decode(base64EncodedSecretKey);\n        return setSigningKey(bytes);\n    }\n\n    @Override\n    public JwtParserBuilder setSigningKey(final Key key) {\n        if (key instanceof SecretKey) {\n            return verifyWith((SecretKey) key);\n        } else if (key instanceof PublicKey) {\n            return verifyWith((PublicKey) key);\n        }\n        String msg = \"JWS verification key must be either a SecretKey (for MAC algorithms) or a PublicKey \" +\n                \"(for Signature algorithms).\";\n        throw new InvalidKeyException(msg);\n    }\n\n    @Override\n    public JwtParserBuilder verifyWith(SecretKey key) {\n        return verifyWith((Key) key);\n    }\n\n    @Override\n    public JwtParserBuilder verifyWith(PublicKey key) {\n        return verifyWith((Key) key);\n    }\n\n    private JwtParserBuilder verifyWith(Key key) {\n        if (key instanceof PrivateKey) {\n            throw new IllegalArgumentException(DefaultJwtParser.PRIV_KEY_VERIFY_MSG);\n        }\n        this.signatureVerificationKey = Assert.notNull(key, \"signature verification key cannot be null.\");\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder decryptWith(SecretKey key) {\n        return decryptWith((Key) key);\n    }\n\n    @Override\n    public JwtParserBuilder decryptWith(PrivateKey key) {\n        return decryptWith((Key) key);\n    }\n\n    private JwtParserBuilder decryptWith(final Key key) {\n        if (key instanceof PublicKey) {\n            throw new IllegalArgumentException(DefaultJwtParser.PUB_KEY_DECRYPT_MSG);\n        }\n        this.decryptionKey = Assert.notNull(key, \"decryption key cannot be null.\");\n        return this;\n    }\n\n    @Override\n    public NestedCollection<CompressionAlgorithm, JwtParserBuilder> zip() {\n        return new NestedIdentifiableCollection<CompressionAlgorithm, JwtParserBuilder>(this, this.zipAlgs) {\n            @Override\n            protected void changed() {\n                zipAlgs = new IdRegistry<>(StandardCompressionAlgorithms.NAME, getValues().values());\n            }\n        };\n    }\n\n    @Override\n    public NestedCollection<AeadAlgorithm, JwtParserBuilder> enc() {\n        return new NestedIdentifiableCollection<AeadAlgorithm, JwtParserBuilder>(this, this.encAlgs) {\n            @Override\n            public void changed() {\n                encAlgs = new IdRegistry<>(StandardEncryptionAlgorithms.NAME, getValues().values());\n            }\n        };\n    }\n\n    @Override\n    public NestedCollection<SecureDigestAlgorithm<?, ?>, JwtParserBuilder> sig() {\n        return new NestedIdentifiableCollection<SecureDigestAlgorithm<?, ?>, JwtParserBuilder>(this, this.sigAlgs) {\n            @Override\n            protected void changed() {\n                sigAlgs = new IdRegistry<>(StandardSecureDigestAlgorithms.NAME, getValues().values());\n            }\n        };\n    }\n\n    @Override\n    public NestedCollection<KeyAlgorithm<?, ?>, JwtParserBuilder> key() {\n        return new NestedIdentifiableCollection<KeyAlgorithm<?, ?>, JwtParserBuilder>(this, this.keyAlgs) {\n            @Override\n            public void changed() {\n                keyAlgs = new IdRegistry<>(StandardKeyAlgorithms.NAME, getValues().values());\n            }\n        };\n    }\n\n    @SuppressWarnings(\"deprecation\") //TODO: remove for 1.0\n    @Override\n    public JwtParserBuilder setSigningKeyResolver(SigningKeyResolver signingKeyResolver) {\n        Assert.notNull(signingKeyResolver, \"SigningKeyResolver cannot be null.\");\n        this.signingKeyResolver = signingKeyResolver;\n        return this;\n    }\n\n    @Override\n    public JwtParserBuilder keyLocator(Locator<Key> keyLocator) {\n        this.keyLocator = Assert.notNull(keyLocator, \"Key locator cannot be null.\");\n        return this;\n    }\n\n    @SuppressWarnings(\"deprecation\")\n    @Override\n    public JwtParserBuilder setCompressionCodecResolver(CompressionCodecResolver resolver) {\n        this.compressionCodecResolver = Assert.notNull(resolver, \"CompressionCodecResolver cannot be null.\");\n        return this;\n    }\n\n    @Override\n    public JwtParser build() {\n\n        if (this.deserializer == null) {\n            //noinspection unchecked\n            json(Services.get(Deserializer.class));\n        }\n        if (this.signingKeyResolver != null && this.signatureVerificationKey != null) {\n            String msg = \"Both a 'signingKeyResolver and a 'verifyWith' key cannot be configured. \" +\n                    \"Choose either, or prefer `keyLocator` when possible.\";\n            throw new IllegalStateException(msg);\n        }\n        if (this.keyLocator != null) {\n            if (this.signatureVerificationKey != null) {\n                String msg = \"Both 'keyLocator' and a 'verifyWith' key cannot be configured. \" +\n                        \"Prefer 'keyLocator' if possible.\";\n                throw new IllegalStateException(msg);\n            }\n            if (this.decryptionKey != null) {\n                String msg = \"Both 'keyLocator' and a 'decryptWith' key cannot be configured. \" +\n                        \"Prefer 'keyLocator' if possible.\";\n                throw new IllegalStateException(msg);\n            }\n        }\n\n        Locator<? extends Key> keyLocator = this.keyLocator; // user configured default, don't overwrite to ensure further build() calls work as expected\n        if (keyLocator == null) {\n            keyLocator = new ConstantKeyLocator(this.signatureVerificationKey, this.decryptionKey);\n        }\n\n        if (!unsecured && unsecuredDecompression) {\n            String msg = \"'unsecuredDecompression' is only relevant if 'unsecured' is also \" +\n                    \"configured. Please read the JavaDoc of both features before enabling either \" +\n                    \"due to their security implications.\";\n            throw new IllegalStateException(msg);\n        }\n        if (this.compressionCodecResolver != null && !Jwts.ZIP.get().equals(this.zipAlgs)) {\n            String msg = \"Both 'zip()' and 'compressionCodecResolver' \" +\n                    \"cannot be configured. Choose either.\";\n            throw new IllegalStateException(msg);\n        }\n\n        // Invariants.  If these are ever violated, it's an error in this class implementation:\n        Assert.stateNotNull(keyLocator, \"Key locator should never be null.\");\n\n        final DefaultClaims expClaims = (DefaultClaims) this.expectedClaims.build();\n\n        return new DefaultJwtParser(\n                provider,\n                signingKeyResolver,\n                unsecured,\n                unsecuredDecompression,\n                keyLocator,\n                clock,\n                critical,\n                allowedClockSkewMillis,\n                expClaims,\n                decoder,\n                deserializer,\n                compressionCodecResolver,\n                zipAlgs,\n                sigAlgs,\n                keyAlgs,\n                encAlgs\n        );\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwts class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<JwtParserBuilder> {\n        @Override\n        public JwtParserBuilder get() {\n            return new DefaultJwtParserBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultMutableJweHeader.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\nimport java.util.Set;\n\npublic class DefaultMutableJweHeader extends DefaultJweHeaderMutator<DefaultMutableJweHeader> implements JweHeader {\n\n    public DefaultMutableJweHeader(DefaultJweHeaderMutator<?> src) {\n        super(src);\n    }\n\n    private <T> T get(Parameter<T> param) {\n        return this.DELEGATE.get(param);\n    }\n\n    // =============================================================\n    // JWT Header methods\n    // =============================================================\n\n    @Override\n    public String getAlgorithm() {\n        return get(DefaultHeader.ALGORITHM);\n    }\n\n    @Override\n    public String getContentType() {\n        return get(DefaultHeader.CONTENT_TYPE);\n    }\n\n    @Override\n    public String getType() {\n        return get(DefaultHeader.TYPE);\n    }\n\n    @Override\n    public String getCompressionAlgorithm() {\n        return get(DefaultHeader.COMPRESSION_ALGORITHM);\n    }\n\n    // =============================================================\n    // Protected Header methods\n    // =============================================================\n\n    @Override\n    public URI getJwkSetUrl() {\n        return get(DefaultProtectedHeader.JKU);\n    }\n\n    @Override\n    public PublicJwk<?> getJwk() {\n        return get(DefaultProtectedHeader.JWK);\n    }\n\n    @Override\n    public String getKeyId() {\n        return get(DefaultProtectedHeader.KID);\n    }\n\n    @Override\n    public Set<String> getCritical() {\n        return get(DefaultProtectedHeader.CRIT);\n    }\n\n    // =============================================================\n    // X.509 methods\n    // =============================================================\n\n    @Override\n    public URI getX509Url() {\n        return get(DefaultProtectedHeader.X5U);\n    }\n\n    @Override\n    public List<X509Certificate> getX509Chain() {\n        return get(DefaultProtectedHeader.X5C);\n    }\n\n    @Override\n    public byte[] getX509Sha1Thumbprint() {\n        return get(DefaultProtectedHeader.X5T);\n    }\n\n    @Override\n    public byte[] getX509Sha256Thumbprint() {\n        return get(DefaultProtectedHeader.X5T_S256);\n    }\n\n    // =============================================================\n    // JWE Header methods\n    // =============================================================\n\n    @Override\n    public byte[] getAgreementPartyUInfo() {\n        return get(DefaultJweHeader.APU);\n    }\n\n    @Override\n    public byte[] getAgreementPartyVInfo() {\n        return get(DefaultJweHeader.APV);\n    }\n\n    @Override\n    public Integer getPbes2Count() {\n        return get(DefaultJweHeader.P2C);\n    }\n\n    @Override\n    public String getEncryptionAlgorithm() {\n        return get(DefaultJweHeader.ENCRYPTION_ALGORITHM);\n    }\n\n    @Override\n    public PublicJwk<?> getEphemeralPublicKey() {\n        return get(DefaultJweHeader.EPK);\n    }\n\n    @Override\n    public byte[] getInitializationVector() {\n        return get(DefaultJweHeader.IV);\n    }\n\n    @Override\n    public byte[] getAuthenticationTag() {\n        return get(DefaultJweHeader.TAG);\n    }\n\n    @Override\n    public byte[] getPbes2Salt() {\n        return get(DefaultJweHeader.P2S);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultProtectedHeader.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.ProtectedHeader;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.impl.security.AbstractAsymmetricJwk;\nimport io.jsonwebtoken.impl.security.AbstractJwk;\nimport io.jsonwebtoken.impl.security.JwkConverter;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * Header implementation satisfying shared JWS and JWE header parameter requirements.  Header parameters specific to\n * either JWE or JWS will be defined in respective subclasses.\n *\n * @since 0.12.0\n */\npublic class DefaultProtectedHeader extends DefaultHeader implements ProtectedHeader {\n\n    static final Parameter<URI> JKU = Parameters.uri(\"jku\", \"JWK Set URL\");\n\n    static final Parameter<PublicJwk<?>> JWK = Parameters.builder(JwkConverter.PUBLIC_JWK_CLASS)\n            .setId(\"jwk\").setName(\"JSON Web Key\")\n            .setConverter(JwkConverter.PUBLIC_JWK).build();\n    static final Parameter<Set<String>> CRIT = Parameters.stringSet(\"crit\", \"Critical\");\n\n    static final Parameter<String> KID = AbstractJwk.KID;\n\n    static final Parameter<URI> X5U = AbstractAsymmetricJwk.X5U;\n\n    static final Parameter<List<X509Certificate>> X5C = AbstractAsymmetricJwk.X5C;\n\n    static final Parameter<byte[]> X5T = AbstractAsymmetricJwk.X5T;\n\n    static final Parameter<byte[]> X5T_S256 = AbstractAsymmetricJwk.X5T_S256;\n\n    static final Registry<String, Parameter<?>> PARAMS =\n            Parameters.registry(DefaultHeader.PARAMS, CRIT, JKU, JWK, KID, X5U, X5C, X5T, X5T_S256);\n\n    static boolean isCandidate(ParameterMap map) {\n        String id = map.get(DefaultHeader.ALGORITHM);\n        return Strings.hasText(id) && !id.equalsIgnoreCase(Jwts.SIG.NONE.getId()); // alg cannot be empty or 'none'\n//        return (Strings.hasText(id) && !Jwts.SIG.NONE.equals(Jwts.SIG.get().get(id))) ||\n//                map.get(JKU) != null ||\n//                map.get(JWK) != null ||\n//                !Collections.isEmpty(map.get(CRIT)) ||\n//                Strings.hasText(map.get(KID)) ||\n//                map.get(X5U) != null ||\n//                !Collections.isEmpty(map.get(X5C)) ||\n//                !Bytes.isEmpty(map.get(X5T)) ||\n//                !Bytes.isEmpty(map.get(X5T_S256));\n    }\n\n    protected DefaultProtectedHeader(Registry<String, Parameter<?>> registry, Map<String, ?> values) {\n        super(registry, values);\n    }\n\n    @Override\n    public String getKeyId() {\n        return get(KID);\n    }\n\n    @Override\n    public URI getJwkSetUrl() {\n        return get(JKU);\n    }\n\n    @Override\n    public PublicJwk<?> getJwk() {\n        return get(JWK);\n    }\n\n    @Override\n    public URI getX509Url() {\n        return get(AbstractAsymmetricJwk.X5U);\n    }\n\n    @Override\n    public List<X509Certificate> getX509Chain() {\n        return get(X5C);\n    }\n\n    @Override\n    public byte[] getX509Sha1Thumbprint() {\n        return get(X5T);\n    }\n\n    @Override\n    public byte[] getX509Sha256Thumbprint() {\n        return get(X5T_S256);\n    }\n\n    @Override\n    public Set<String> getCritical() {\n        return get(CRIT);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultProtectedJwt.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.ProtectedHeader;\nimport io.jsonwebtoken.ProtectedJwt;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\n\nimport java.security.MessageDigest;\n\nabstract class DefaultProtectedJwt<H extends ProtectedHeader, P> extends DefaultJwt<H, P> implements ProtectedJwt<H, P> {\n\n    protected final byte[] digest;\n\n    private final String digestName;\n\n    protected DefaultProtectedJwt(H header, P payload, byte[] digest, String digestName) {\n        super(header, payload);\n        this.digest = Assert.notEmpty(digest, \"Digest byte array cannot be null or empty.\");\n        this.digestName = Assert.hasText(digestName, \"digestName cannot be null or empty.\");\n    }\n\n    @Override\n    public byte[] getDigest() {\n        return this.digest.clone();\n    }\n\n    @Override\n    protected StringBuilder toStringBuilder() {\n        String b64Url = Encoders.BASE64URL.encode(this.digest);\n        return super.toStringBuilder().append(',').append(this.digestName).append('=').append(b64Url);\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) {\n            return true;\n        }\n        if (obj instanceof DefaultProtectedJwt) {\n            DefaultProtectedJwt<?, ?> pjwt = (DefaultProtectedJwt<?, ?>) obj;\n            return super.equals(pjwt) && MessageDigest.isEqual(this.digest, pjwt.digest);\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.nullSafeHashCode(getHeader(), getPayload(), this.digest);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultTokenizedJwe.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Header;\n\nimport java.util.Map;\n\nclass DefaultTokenizedJwe extends DefaultTokenizedJwt implements TokenizedJwe {\n\n    private final CharSequence encryptedKey;\n    private final CharSequence iv;\n\n    DefaultTokenizedJwe(CharSequence protectedHeader, CharSequence body, CharSequence digest,\n                        CharSequence encryptedKey, CharSequence iv) {\n        super(protectedHeader, body, digest);\n        this.encryptedKey = encryptedKey;\n        this.iv = iv;\n    }\n\n    @Override\n    public CharSequence getEncryptedKey() {\n        return this.encryptedKey;\n    }\n\n    @Override\n    public CharSequence getIv() {\n        return this.iv;\n    }\n\n    @Override\n    public Header createHeader(Map<String, ?> m) {\n        return new DefaultJweHeader(m);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DefaultTokenizedJwt.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Map;\n\nclass DefaultTokenizedJwt implements TokenizedJwt {\n\n    private final CharSequence protectedHeader;\n    private final CharSequence payload;\n    private final CharSequence digest;\n\n    DefaultTokenizedJwt(CharSequence protectedHeader, CharSequence payload, CharSequence digest) {\n        this.protectedHeader = protectedHeader;\n        this.payload = payload;\n        this.digest = digest;\n    }\n\n    @Override\n    public CharSequence getProtected() {\n        return this.protectedHeader;\n    }\n\n    @Override\n    public CharSequence getPayload() {\n        return this.payload;\n    }\n\n    @Override\n    public CharSequence getDigest() {\n        return this.digest;\n    }\n\n    @Override\n    public Header createHeader(Map<String, ?> m) {\n        if (Strings.hasText(getDigest())) {\n            return new DefaultJwsHeader(m);\n        }\n        return new DefaultHeader(m);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DelegateAudienceCollection.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.ClaimsMutator;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.util.Collection;\n\npublic class DelegateAudienceCollection<P> implements ClaimsMutator.AudienceCollection<P> {\n\n    private final ClaimsMutator.AudienceCollection<?> delegate;\n\n    private final P parent;\n\n    public DelegateAudienceCollection(P parent, ClaimsMutator.AudienceCollection<?> delegate) {\n        this.parent = Assert.notNull(parent, \"Parent cannot be null.\");\n        this.delegate = Assert.notNull(delegate, \"Delegate cannot be null.\");\n    }\n\n    @Override\n    public P single(String aud) {\n        delegate.single(aud);\n        return parent;\n    }\n\n    @Override\n    public ClaimsMutator.AudienceCollection<P> add(String s) {\n        delegate.add(s);\n        return this;\n    }\n\n    @Override\n    public ClaimsMutator.AudienceCollection<P> add(Collection<? extends String> c) {\n        delegate.add(c);\n        return this;\n    }\n\n    @Override\n    public ClaimsMutator.AudienceCollection<P> clear() {\n        delegate.clear();\n        return this;\n    }\n\n    @Override\n    public ClaimsMutator.AudienceCollection<P> remove(String s) {\n        delegate.remove(s);\n        return this;\n    }\n\n    @Override\n    public P and() {\n        delegate.and(); // allow any cleanup/finalization\n        return parent;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/DelegatingClaimsMutator.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.ClaimsMutator;\nimport io.jsonwebtoken.impl.lang.DelegatingMapMutator;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.MapMutator;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Date;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @param <T> subclass type\n * @since 0.12.0\n */\npublic class DelegatingClaimsMutator<T extends MapMutator<String, Object, T> & ClaimsMutator<T>>\n        extends DelegatingMapMutator<String, Object, ParameterMap, T>\n        implements ClaimsMutator<T> {\n\n    private static final Parameter<String> AUDIENCE_STRING =\n            Parameters.string(DefaultClaims.AUDIENCE.getId(), DefaultClaims.AUDIENCE.getName());\n\n    protected DelegatingClaimsMutator() {\n        super(new ParameterMap(DefaultClaims.PARAMS));\n    }\n\n    <F> T put(Parameter<F> param, F value) {\n        this.DELEGATE.put(param, value);\n        return self();\n    }\n\n    @Override // override starting in 0.12.4\n    public Object put(String key, Object value) {\n        if (AUDIENCE_STRING.getId().equals(key)) { // https://github.com/jwtk/jjwt/issues/890\n            if (value instanceof String) {\n                Object existing = get(key);\n                //noinspection deprecation\n                audience().single((String) value);\n                return existing;\n            }\n            // otherwise ensure that the Parameter type is the RFC-default data type (JSON Array of Strings):\n            getAudience();\n        }\n        // otherwise retain expected behavior:\n        return super.put(key, value);\n    }\n\n    @Override // overridden starting in 0.12.4\n    public void putAll(Map<? extends String, ?> m) {\n        if (m == null) return;\n        for (Map.Entry<? extends String, ?> entry : m.entrySet()) {\n            String s = entry.getKey();\n            put(s, entry.getValue()); // ensure local put is called per https://github.com/jwtk/jjwt/issues/890\n        }\n    }\n\n    <F> F get(Parameter<F> param) {\n        return this.DELEGATE.get(param);\n    }\n\n    @Override\n    public T setIssuer(String iss) {\n        return issuer(iss);\n    }\n\n    @Override\n    public T issuer(String iss) {\n        return put(DefaultClaims.ISSUER, iss);\n    }\n\n    @Override\n    public T setSubject(String sub) {\n        return subject(sub);\n    }\n\n    @Override\n    public T subject(String sub) {\n        return put(DefaultClaims.SUBJECT, sub);\n    }\n\n    @Override\n    public T setAudience(String aud) {\n        //noinspection deprecation\n        return audience().single(aud);\n    }\n\n    private Set<String> getAudience() {\n        // caller expects that we're working with a String<Set> so ensure that:\n        if (!this.DELEGATE.PARAMS.get(AUDIENCE_STRING.getId()).supports(Collections.emptySet())) {\n            String existing = get(AUDIENCE_STRING);\n            remove(AUDIENCE_STRING.getId()); // clear out any canonical/idiomatic values since we're replacing\n            setDelegate(this.DELEGATE.replace(DefaultClaims.AUDIENCE));\n            put(DefaultClaims.AUDIENCE, Collections.setOf(existing)); // replace as Set\n        }\n        return get(DefaultClaims.AUDIENCE);\n    }\n\n    private T audienceSingle(String aud) {\n        if (!Strings.hasText(aud)) {\n            return put(DefaultClaims.AUDIENCE, null);\n        }\n        // otherwise it's an actual single string, we need to ensure that we can represent it as a single\n        // string by swapping out the AUDIENCE param:\n        remove(AUDIENCE_STRING.getId()); //remove any existing value, as conversion will throw an exception\n        setDelegate(this.DELEGATE.replace(AUDIENCE_STRING));\n        return put(AUDIENCE_STRING, aud);\n    }\n\n    @Override\n    public AudienceCollection<T> audience() {\n        return new AbstractAudienceCollection<T>(self(), getAudience()) {\n            @Override\n            public T single(String audience) {\n                return audienceSingle(audience);\n                // DO NOT call changed() here - we don't want to replace the value with a collection\n            }\n\n            @Override\n            protected void changed() {\n                put(DefaultClaims.AUDIENCE, Collections.asSet(getCollection()));\n            }\n        };\n    }\n\n    @Override\n    public T setExpiration(Date exp) {\n        return expiration(exp);\n    }\n\n    @Override\n    public T expiration(Date exp) {\n        return put(DefaultClaims.EXPIRATION, exp);\n    }\n\n    @Override\n    public T setNotBefore(Date nbf) {\n        return notBefore(nbf);\n    }\n\n    @Override\n    public T notBefore(Date nbf) {\n        return put(DefaultClaims.NOT_BEFORE, nbf);\n    }\n\n    @Override\n    public T setIssuedAt(Date iat) {\n        return issuedAt(iat);\n    }\n\n    @Override\n    public T issuedAt(Date iat) {\n        return put(DefaultClaims.ISSUED_AT, iat);\n    }\n\n    @Override\n    public T setId(String jti) {\n        return id(jti);\n    }\n\n    @Override\n    public T id(String jti) {\n        return put(DefaultClaims.JTI, jti);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/FixedClock.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Clock;\n\nimport java.util.Date;\n\n/**\n * A {@code Clock} implementation that is constructed with a seed timestamp and always reports that same\n * timestamp.\n *\n * @since 0.7.0\n */\npublic class FixedClock implements Clock {\n\n    private final Date now;\n\n    /**\n     * Creates a new fixed clock using <code>new {@link Date Date}()</code> as the seed timestamp.  All calls to\n     * {@link #now now()} will always return this seed Date.\n     */\n    public FixedClock() {\n        this(new Date());\n    }\n\n    /**\n     * Creates a new fixed clock using the specified seed timestamp.  All calls to\n     * {@link #now now()} will always return this seed Date.\n     *\n     * @param now the specified Date to always return from all calls to {@link #now now()}.\n     */\n    public FixedClock(Date now) {\n        this.now = now;\n    }\n\n    /**\n     * Creates a new fixed clock using the specified seed timestamp.  All calls to\n     * {@link #now now()} will always return this seed Date.\n     *\n     * @param timeInMillis the specified Date in milliseconds to always return from all calls to {@link #now now()}.\n     */\n    public FixedClock(long timeInMillis) {\n        this(new Date(timeInMillis));\n    }\n\n    @Override\n    public Date now() {\n        return this.now;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/IdLocator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.Locator;\nimport io.jsonwebtoken.MalformedJwtException;\nimport io.jsonwebtoken.UnsupportedJwtException;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\n\npublic class IdLocator<H extends Header, R extends Identifiable> implements Locator<R>, Function<H, R> {\n\n    private final Parameter<String> param;\n    private final Registry<String, R> registry;\n    private final String algType;\n    private final String behavior;\n    private final String requiredMsg;\n\n    public IdLocator(Parameter<String> param, Registry<String, R> registry, String algType, String behavior, String requiredExceptionMessage) {\n        this.param = Assert.notNull(param, \"Header param cannot be null.\");\n        this.registry = Assert.notNull(registry, \"Registry cannot be null.\");\n        this.algType = Assert.hasText(algType, \"algType cannot be null or empty.\");\n        this.behavior = Assert.hasText(behavior, \"behavior cannot be null or empty.\");\n        this.requiredMsg = Strings.clean(requiredExceptionMessage);\n    }\n\n    @Override\n    public R locate(Header header) {\n\n        Object val = header.get(this.param.getId());\n        String id = val != null ? val.toString() : null;\n\n        if (!Strings.hasText(id)) {\n            if (this.requiredMsg != null) { // a msg was provided, so the value is required:\n                throw new MalformedJwtException(requiredMsg);\n            }\n            return null; // otherwise header value not required, so short circuit\n        }\n\n        try {\n            return registry.forKey(id);\n        } catch (Exception e) {\n            StringBuilder sb = new StringBuilder(\"Unsupported \")\n                    .append(DefaultHeader.nameOf(header))\n                    .append(\" \")\n                    .append(this.param)\n                    .append(\" value '\").append(id).append(\"'\");\n            if (this.registry.isEmpty()) {\n                sb.append(\": \")\n                        .append(this.behavior)\n                        .append(\" is disabled (no \")\n                        .append(this.algType)\n                        .append(\" algorithms have been configured)\");\n            }\n            sb.append(\".\");\n            String msg = sb.toString();\n            throw new UnsupportedJwtException(msg, e);\n        }\n    }\n\n    @Override\n    public R apply(H header) {\n        return locate(header);\n    }\n}"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/JwtTokenizer.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.MalformedJwtException;\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.io.IOException;\nimport java.io.Reader;\n\npublic class JwtTokenizer {\n\n    static final char DELIMITER = '.';\n\n    private static final String DELIM_ERR_MSG_PREFIX = \"Invalid compact JWT string: Compact JWSs must contain \" +\n            \"exactly 2 period characters, and compact JWEs must contain exactly 4.  Found: \";\n\n    private static int read(Reader r, char[] buf) {\n        try {\n            return r.read(buf);\n        } catch (IOException e) {\n            String msg = \"Unable to read compact JWT: \" + e.getMessage();\n            throw new MalformedJwtException(msg, e);\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public <T extends TokenizedJwt> T tokenize(Reader reader) {\n\n        Assert.notNull(reader, \"Reader argument cannot be null.\");\n\n        CharSequence protectedHeader = Strings.EMPTY; //Both JWS and JWE\n        CharSequence body = Strings.EMPTY; //JWS payload or JWE Ciphertext\n        CharSequence encryptedKey = Strings.EMPTY; //JWE only\n        CharSequence iv = Strings.EMPTY; //JWE only\n        CharSequence digest = Strings.EMPTY; //JWS Signature or JWE AAD Tag\n\n        int delimiterCount = 0;\n        char[] buf = new char[4096];\n        int len = 0;\n        StringBuilder sb = new StringBuilder(4096);\n        while (len != Streams.EOF) {\n\n            len = read(reader, buf);\n\n            for (int i = 0; i < len; i++) {\n\n                char c = buf[i];\n\n                if (Character.isWhitespace(c)) {\n                    String msg = \"Compact JWT strings may not contain whitespace.\";\n                    throw new MalformedJwtException(msg);\n                }\n\n                if (c == DELIMITER) {\n\n                    CharSequence seq = Strings.clean(sb);\n                    String token = seq != null ? seq.toString() : Strings.EMPTY;\n\n                    switch (delimiterCount) {\n                        case 0:\n                            protectedHeader = token;\n                            break;\n                        case 1:\n                            body = token; //for JWS\n                            encryptedKey = token; //for JWE\n                            break;\n                        case 2:\n                            body = Strings.EMPTY; //clear out value set for JWS\n                            iv = token;\n                            break;\n                        case 3:\n                            body = token;\n                            break;\n                    }\n\n                    delimiterCount++;\n                    sb.setLength(0);\n                } else {\n                    sb.append(c);\n                }\n            }\n        }\n\n        if (delimiterCount != 2 && delimiterCount != 4) {\n            String msg = DELIM_ERR_MSG_PREFIX + delimiterCount;\n            throw new MalformedJwtException(msg);\n        }\n\n        if (sb.length() > 0) {\n            digest = sb.toString();\n        }\n\n        if (delimiterCount == 2) {\n            return (T) new DefaultTokenizedJwt(protectedHeader, body, digest);\n        }\n\n        return (T) new DefaultTokenizedJwe(protectedHeader, body, digest, encryptedKey, iv);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/ParameterMap.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.impl.lang.Nameable;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.impl.lang.RedactedSupplier;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.AbstractSet;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class ParameterMap implements Map<String, Object>, ParameterReadable, Nameable {\n\n    protected final Registry<String, ? extends Parameter<?>> PARAMS;\n    protected final Map<String, Object> values; // canonical values formatted per RFC requirements\n    protected final Map<String, Object> idiomaticValues; // the values map with any RFC values converted to Java type-safe values where possible\n\n    private final boolean initialized;\n\n    private final boolean mutable;\n\n    public ParameterMap(Set<Parameter<?>> params) {\n        this(Parameters.registry(params));\n    }\n\n    public ParameterMap(Registry<String, ? extends Parameter<?>> registry) { // mutable constructor\n        this(registry, null, true);\n    }\n\n    /**\n     * Copy constructor producing an immutable instance.\n     *\n     * @param registry registry of idiomatic parameters relevant for the map values\n     * @param values   map values\n     */\n    public ParameterMap(Registry<String, ? extends Parameter<?>> registry, Map<String, ?> values) {\n        this(registry, Assert.notNull(values, \"Map argument cannot be null.\"), false);\n    }\n\n    public ParameterMap(Registry<String, ? extends Parameter<?>> registry, Map<String, ?> values, boolean mutable) {\n        Assert.notNull(registry, \"Parameter registry cannot be null.\");\n        Assert.notEmpty(registry.values(), \"Parameter registry cannot be empty.\");\n        this.PARAMS = registry;\n        this.values = new LinkedHashMap<>();\n        this.idiomaticValues = new LinkedHashMap<>();\n        if (!Collections.isEmpty(values)) {\n            putAll(values);\n        }\n        this.mutable = mutable;\n        this.initialized = true;\n    }\n\n    private void assertMutable() {\n        if (initialized && !mutable) {\n            String msg = getName() + \" instance is immutable and may not be modified.\";\n            throw new UnsupportedOperationException(msg);\n        }\n    }\n\n    protected ParameterMap replace(Parameter<?> param) {\n        Registry<String, ? extends Parameter<?>> registry = Parameters.replace(this.PARAMS, param);\n        return new ParameterMap(registry, this, this.mutable);\n    }\n\n    @Override\n    public String getName() {\n        return \"Map\";\n    }\n\n    @Override\n    public <T> T get(Parameter<T> param) {\n        Assert.notNull(param, \"Parameter cannot be null.\");\n        final String id = Assert.hasText(param.getId(), \"Parameter id cannot be null or empty.\");\n        Object value = idiomaticValues.get(id);\n        return param.cast(value);\n    }\n\n    @Override\n    public int size() {\n        return values.size();\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return values.isEmpty();\n    }\n\n    @Override\n    public boolean containsKey(Object o) {\n        return values.containsKey(o);\n    }\n\n    @Override\n    public boolean containsValue(Object o) {\n        return values.containsValue(o);\n    }\n\n    @Override\n    public Object get(Object o) {\n        return values.get(o);\n    }\n\n    /**\n     * Convenience method to put a value for an idiomatic param.\n     *\n     * @param param the param representing the property name to set\n     * @param value the value to set\n     * @return the previous value for the param, or {@code null} if there was no previous value\n     * @since 0.12.0\n     */\n    protected final <T> Object put(Parameter<T> param, Object value) {\n        assertMutable();\n        Assert.notNull(param, \"Parameter cannot be null.\");\n        Assert.hasText(param.getId(), \"Parameter id cannot be null or empty.\");\n        return apply(param, value);\n    }\n\n    @Override\n    public final Object put(String name, Object value) {\n        assertMutable();\n        name = Assert.notNull(Strings.clean(name), \"Member name cannot be null or empty.\");\n        Parameter<?> param = PARAMS.get(name);\n        if (param != null) {\n            // standard property, represent it idiomatically:\n            return put(param, value);\n        } else {\n            // non-standard or custom property, just apply directly:\n            return nullSafePut(name, value);\n        }\n    }\n\n    private Object nullSafePut(String name, Object value) {\n        if (value == null) {\n            return remove(name);\n        } else {\n            this.idiomaticValues.put(name, value);\n            return this.values.put(name, value);\n        }\n    }\n\n    private <T> Object apply(Parameter<T> param, Object rawValue) {\n\n        final String id = param.getId();\n\n        if (Objects.isEmpty(rawValue)) {\n            return remove(id);\n        }\n\n        T idiomaticValue; // preferred Java format\n        Object canonicalValue; // as required by the RFC\n        try {\n            idiomaticValue = param.applyFrom(rawValue);\n            Assert.notNull(idiomaticValue, \"Parameter's resulting idiomaticValue cannot be null.\");\n            canonicalValue = param.applyTo(idiomaticValue);\n            Assert.notNull(canonicalValue, \"Parameter's resulting canonicalValue cannot be null.\");\n        } catch (Exception e) {\n            StringBuilder sb = new StringBuilder(100);\n            sb.append(\"Invalid \").append(getName()).append(\" \").append(param).append(\" value\");\n            if (param.isSecret()) {\n                sb.append(\": \").append(RedactedSupplier.REDACTED_VALUE);\n            } else if (!(rawValue instanceof byte[])) {\n                // don't print raw byte array gibberish.  We can't base64[url] encode it either because that could\n                // make the exception message confusing: the developer would see an encoded string and could think\n                // that was the rawValue specified when it wasn't.\n                sb.append(\": \").append(Objects.nullSafeToString(rawValue));\n            }\n            sb.append(\". \").append(e.getMessage());\n            String msg = sb.toString();\n            throw new IllegalArgumentException(msg, e);\n        }\n        this.idiomaticValues.put(id, idiomaticValue);\n        return this.values.put(id, canonicalValue);\n    }\n\n    @Override\n    public Object remove(Object key) {\n        assertMutable();\n        this.idiomaticValues.remove(key);\n        return this.values.remove(key);\n    }\n\n    @Override\n    public void putAll(Map<? extends String, ?> m) {\n        if (m == null) {\n            return;\n        }\n        for (Map.Entry<? extends String, ?> entry : m.entrySet()) {\n            String s = entry.getKey();\n            put(s, entry.getValue());\n        }\n    }\n\n    @Override\n    public void clear() {\n        assertMutable();\n        this.values.clear();\n        this.idiomaticValues.clear();\n    }\n\n    @Override\n    public Set<String> keySet() {\n        return new KeySet();\n    }\n\n    @Override\n    public Collection<Object> values() {\n        return new ValueSet();\n    }\n\n    @Override\n    public Set<Entry<String, Object>> entrySet() {\n        return new EntrySet();\n    }\n\n    @Override\n    public String toString() {\n        return values.toString();\n    }\n\n    @Override\n    public int hashCode() {\n        return values.hashCode();\n    }\n\n    @SuppressWarnings(\"EqualsWhichDoesntCheckParameterClass\")\n    @Override\n    public boolean equals(Object obj) {\n        return values.equals(obj);\n    }\n\n    private abstract class ParameterMapSet<T> extends AbstractSet<T> {\n\n        @Override\n        public int size() {\n            return ParameterMap.this.size();\n        }\n    }\n\n    private class KeySet extends ParameterMapSet<String> {\n        @Override\n        public Iterator<String> iterator() {\n            return new KeyIterator();\n        }\n    }\n\n    private class ValueSet extends ParameterMapSet<Object> {\n        @Override\n        public Iterator<Object> iterator() {\n            return new ValueIterator();\n        }\n    }\n\n    private class EntrySet extends ParameterMapSet<Entry<String, Object>> {\n        @Override\n        public Iterator<Entry<String, Object>> iterator() {\n            return new EntryIterator();\n        }\n    }\n\n    private abstract class ParameterMapIterator<T> implements Iterator<T> {\n\n        final Iterator<Map.Entry<String, Object>> i;\n\n        transient Map.Entry<String, Object> current;\n\n        ParameterMapIterator() {\n            this.i = ParameterMap.this.values.entrySet().iterator();\n            this.current = null;\n        }\n\n        @Override\n        public boolean hasNext() {\n            return i.hasNext();\n        }\n\n        protected Map.Entry<String, Object> nextEntry() {\n            current = i.next();\n            return current;\n        }\n\n        @Override\n        public void remove() {\n            if (current == null) {\n                throw new IllegalStateException();\n            }\n            String key = current.getKey();\n            ParameterMap.this.remove(key);\n        }\n    }\n\n    private class ValueIterator extends ParameterMapIterator<Object> {\n        @Override\n        public Object next() {\n            return nextEntry().getValue();\n        }\n    }\n\n    private class KeyIterator extends ParameterMapIterator<String> {\n        @Override\n        public String next() {\n            return nextEntry().getKey();\n        }\n    }\n\n    private class EntryIterator extends ParameterMapIterator<Entry<String, Object>> {\n        @Override\n        public Entry<String, Object> next() {\n            return nextEntry();\n        }\n    }\n\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/Payload.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.CompressionCodec;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\nclass Payload {\n\n    static final Payload EMPTY = new Payload(Bytes.EMPTY, null);\n\n    private final CharSequence string;\n    private final byte[] bytes;\n    private final Claims claims;\n    private final InputStream inputStream;\n    private final boolean inputStreamEmpty;\n    private final String contentType;\n    private CompressionAlgorithm zip;\n    private boolean claimsExpected;\n\n    Payload(Claims claims) {\n        this(claims, null, null, null, null);\n    }\n\n    Payload(CharSequence content, String contentType) {\n        this(null, content, null, null, contentType);\n    }\n\n    Payload(byte[] content, String contentType) {\n        this(null, null, content, null, contentType);\n    }\n\n    Payload(InputStream inputStream, String contentType) {\n        this(null, null, null, inputStream, contentType);\n    }\n\n    private Payload(Claims claims, CharSequence string, byte[] bytes, InputStream inputStream, String contentType) {\n        this.claims = claims;\n        this.string = Strings.clean(string);\n        this.contentType = Strings.clean(contentType);\n        InputStream in = inputStream;\n        byte[] data = Bytes.nullSafe(bytes);\n        if (Strings.hasText(this.string)) {\n            data = Strings.utf8(this.string);\n        }\n        this.bytes = data;\n        if (in == null && !Bytes.isEmpty(this.bytes)) {\n            in = Streams.of(data);\n        }\n        this.inputStreamEmpty = in == null;\n        this.inputStream = this.inputStreamEmpty ? Streams.of(Bytes.EMPTY) : in;\n    }\n\n    boolean isClaims() {\n        return !Collections.isEmpty(this.claims);\n    }\n\n    Claims getRequiredClaims() {\n        return Assert.notEmpty(this.claims, \"Claims cannot be null or empty when calling this method.\");\n    }\n\n    boolean isString() {\n        return Strings.hasText(this.string);\n    }\n\n    String getContentType() {\n        return this.contentType;\n    }\n\n    public void setZip(CompressionAlgorithm zip) {\n        this.zip = zip;\n    }\n\n    boolean isCompressed() {\n        return this.zip != null;\n    }\n\n    public void setClaimsExpected(boolean claimsExpected) {\n        this.claimsExpected = claimsExpected;\n    }\n\n    /**\n     * Returns {@code true} if the payload may be fully consumed and retained in memory, {@code false} if empty,\n     * already extracted, or a potentially too-large InputStream.\n     *\n     * @return {@code true} if the payload may be fully consumed and retained in memory, {@code false} if empty,\n     * already extracted, or a potentially too-large InputStream.\n     */\n    boolean isConsumable() {\n        return !isClaims() && (isString() || !Bytes.isEmpty(this.bytes) || (inputStream != null && claimsExpected));\n    }\n\n    boolean isEmpty() {\n        return !isClaims() && !isString() && Bytes.isEmpty(this.bytes) && this.inputStreamEmpty;\n    }\n\n    public OutputStream compress(OutputStream out) {\n        return this.zip != null ? zip.compress(out) : out;\n    }\n\n    public Payload decompress(CompressionAlgorithm alg) {\n        Assert.notNull(alg, \"CompressionAlgorithm cannot be null.\");\n        Payload payload = this;\n        if (!isString() && isConsumable()) {\n            if (alg.equals(Jwts.ZIP.DEF) && !Bytes.isEmpty(this.bytes)) { // backwards compatibility\n                byte[] data = ((CompressionCodec) alg).decompress(this.bytes);\n                payload = new Payload(claims, string, data, null, getContentType());\n            } else {\n                InputStream in = toInputStream();\n                in = alg.decompress(in);\n                payload = new Payload(claims, string, bytes, in, getContentType());\n            }\n            payload.setClaimsExpected(claimsExpected);\n        }\n        // otherwise it's a String or b64/detached payload, in either case, we don't decompress since the caller is\n        // providing the bytes necessary for signature verification as-is, and there's no conversion we need to perform\n        return payload;\n    }\n\n    public byte[] getBytes() {\n        return this.bytes;\n    }\n\n    InputStream toInputStream() {\n        // should only ever call this when claims don't exist:\n        Assert.state(!isClaims(), \"Claims exist, cannot convert to InputStream directly.\");\n        return this.inputStream;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/TextCodec.java",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.Encoder;\n\n/**\n * @deprecated since 0.10.0.  Use an {@link Encoder} or {@link Decoder}\n * as needed.  This class will be removed before 1.0.0\n */\n@Deprecated\npublic interface TextCodec {\n\n    /**\n     * @deprecated since 0.10.0.  Use {@code io.jsonwebtoken.io.Encoders#BASE64} or\n     * {@code io.jsonwebtoken.io.Decoders#BASE64} instead. This class will be removed before 1.0.0\n     */\n    @Deprecated\n    TextCodec BASE64 = new Base64Codec();\n\n    /**\n     * @deprecated since 0.10.0.  Use {@code io.jsonwebtoken.io.Encoders#BASE64URL} or\n     * {@code io.jsonwebtoken.io.Decoders#BASE64URL} instead. This class will be removed before 1.0.0\n     */\n    @Deprecated\n    TextCodec BASE64URL = new Base64UrlCodec();\n\n    String encode(String data);\n\n    String encode(byte[] data);\n\n    byte[] decode(String encoded);\n\n    String decodeToString(String encoded);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/TokenizedJwe.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\npublic interface TokenizedJwe extends TokenizedJwt {\n\n    CharSequence getEncryptedKey();\n\n    CharSequence getIv();\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/TokenizedJwt.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.Header;\n\nimport java.util.Map;\n\npublic interface TokenizedJwt {\n\n    /**\n     * Protected header.\n     *\n     * @return protected header.\n     */\n    CharSequence getProtected();\n\n    /**\n     * Returns the Payload for a JWS or Ciphertext for a JWE.\n     *\n     * @return the Payload for a JWS or Ciphertext for a JWE.\n     */\n    CharSequence getPayload();\n\n    /**\n     * Returns the Signature for JWS or AAD Tag for JWE.\n     *\n     * @return the Signature for JWS or AAD Tag for JWE.\n     */\n    CharSequence getDigest();\n\n    /**\n     * Returns a new {@link Header} instance with the specified map state.\n     *\n     * @param m the header state\n     * @return a new header instance.\n     */\n    Header createHeader(Map<String, ?> m);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/X509Context.java",
    "content": "/*\n * Copyright (C) 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl;\n\nimport io.jsonwebtoken.security.X509Accessor;\nimport io.jsonwebtoken.security.X509Mutator;\n\npublic interface X509Context<T extends X509Mutator<T>> extends X509Accessor, X509Mutator<T> {\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/compression/AbstractCompressionAlgorithm.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.compression;\n\nimport io.jsonwebtoken.CompressionCodec;\nimport io.jsonwebtoken.CompressionException;\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.impl.lang.PropagatingExceptionFunction;\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\n\n/**\n * Abstract class that asserts arguments and wraps IOException with CompressionException.\n *\n * @since 0.6.0\n */\n@SuppressWarnings(\"deprecation\")\npublic abstract class AbstractCompressionAlgorithm implements CompressionAlgorithm, CompressionCodec {\n\n    private static <T, R> Function<T, R> propagate(CheckedFunction<T, R> fn, String msg) {\n        return new PropagatingExceptionFunction<>(fn, CompressionException.class, msg);\n    }\n\n    private static <T, R> Function<T, R> forCompression(CheckedFunction<T, R> fn) {\n        return propagate(fn, \"Compression failed.\");\n    }\n\n    private static <T, R> Function<T, R> forDecompression(CheckedFunction<T, R> fn) {\n        return propagate(fn, \"Decompression failed.\");\n    }\n\n    private final String id;\n    private final Function<OutputStream, OutputStream> OS_WRAP_FN;\n    private final Function<InputStream, InputStream> IS_WRAP_FN;\n    private final Function<byte[], byte[]> COMPRESS_FN;\n\n    private final Function<byte[], byte[]> DECOMPRESS_FN;\n\n    protected AbstractCompressionAlgorithm(String id) {\n        this.id = Assert.hasText(Strings.clean(id), \"id argument cannot be null or empty.\");\n        this.OS_WRAP_FN = forCompression(new CheckedFunction<OutputStream, OutputStream>() {\n            @Override\n            public OutputStream apply(OutputStream out) throws Exception {\n                return doCompress(out);\n            }\n        });\n        this.COMPRESS_FN = forCompression(new CheckedFunction<byte[], byte[]>() {\n            @Override\n            public byte[] apply(byte[] data) throws Exception {\n                return doCompress(data);\n            }\n        });\n        this.IS_WRAP_FN = forDecompression(new CheckedFunction<InputStream, InputStream>() {\n            @Override\n            public InputStream apply(InputStream is) throws Exception {\n                return doDecompress(is);\n            }\n        });\n        this.DECOMPRESS_FN = forDecompression(new CheckedFunction<byte[], byte[]>() {\n            @Override\n            public byte[] apply(byte[] data) throws Exception {\n                return doDecompress(data);\n            }\n        });\n    }\n\n    @Override\n    public String getId() {\n        return this.id;\n    }\n\n    @Override\n    public String getAlgorithmName() {\n        return getId();\n    }\n\n    @Override\n    public final OutputStream compress(final OutputStream out) throws CompressionException {\n        return OS_WRAP_FN.apply(out);\n    }\n\n    protected abstract OutputStream doCompress(OutputStream out) throws IOException;\n\n    @Override\n    public final InputStream decompress(InputStream is) throws CompressionException {\n        return IS_WRAP_FN.apply(is);\n    }\n\n    protected abstract InputStream doDecompress(InputStream is) throws IOException;\n\n    @Override\n    public final byte[] compress(byte[] content) {\n        if (Bytes.isEmpty(content)) return Bytes.EMPTY;\n        return this.COMPRESS_FN.apply(content);\n    }\n\n    private byte[] doCompress(byte[] data) throws IOException {\n        ByteArrayOutputStream out = new ByteArrayOutputStream(512);\n        OutputStream compression = compress(out);\n        try {\n            compression.write(data);\n            compression.flush();\n        } finally {\n            Objects.nullSafeClose(compression);\n        }\n        return out.toByteArray();\n    }\n\n\n    /**\n     * Asserts the compressed bytes is not null and calls {@link #doDecompress(byte[]) doDecompress}\n     *\n     * @param compressed compressed bytes\n     * @return decompressed bytes\n     * @throws CompressionException if {@link #doDecompress(byte[]) doDecompress} throws an IOException\n     */\n    @Override\n    public final byte[] decompress(byte[] compressed) {\n        if (Bytes.isEmpty(compressed)) return Bytes.EMPTY;\n        return this.DECOMPRESS_FN.apply(compressed);\n    }\n\n    /**\n     * Implement this method to do the actual work of decompressing the compressed bytes.\n     *\n     * @param compressed compressed bytes\n     * @return decompressed bytes\n     * @throws IOException if the decompression runs into an IO problem\n     */\n    protected byte[] doDecompress(byte[] compressed) throws IOException {\n        InputStream is = Streams.of(compressed);\n        InputStream decompress = decompress(is);\n        byte[] buffer = new byte[512];\n        ByteArrayOutputStream out = new ByteArrayOutputStream(buffer.length);\n        int read = 0;\n        try {\n            while (read != Streams.EOF) {\n                read = decompress.read(buffer); //assignment separate from loop invariant check for code coverage checks\n                if (read > 0) out.write(buffer, 0, read);\n            }\n        } finally {\n            Objects.nullSafeClose(decompress);\n        }\n        return out.toByteArray();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/compression/DeflateCompressionAlgorithm.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.compression;\n\nimport io.jsonwebtoken.lang.Objects;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.zip.DeflaterOutputStream;\nimport java.util.zip.InflaterInputStream;\nimport java.util.zip.InflaterOutputStream;\n\n/**\n * Codec implementing the <a href=\"https://en.wikipedia.org/wiki/DEFLATE\">deflate compression algorithm</a>.\n *\n * @since 0.6.0\n */\npublic class DeflateCompressionAlgorithm extends AbstractCompressionAlgorithm {\n\n    private static final String ID = \"DEF\";\n\n    public DeflateCompressionAlgorithm() {\n        super(ID);\n    }\n\n    @Override\n    protected OutputStream doCompress(OutputStream out) {\n        return new DeflaterOutputStream(out);\n    }\n\n    @Override\n    protected InputStream doDecompress(InputStream is) {\n        return new InflaterInputStream(is);\n    }\n\n    @Override\n    protected byte[] doDecompress(final byte[] compressed) throws IOException {\n        try {\n            return super.doDecompress(compressed);\n        } catch (IOException e1) {\n            try {\n                return doDecompressBackCompat(compressed);\n            } catch (IOException e2) {\n                throw e1; //retain/report original exception\n            }\n        }\n    }\n\n    /**\n     * This implementation was in 0.10.6 and earlier - it will be used as a fallback for backwards compatibility if\n     * {@link #doDecompress(byte[])} fails per <a href=\"https://github.com/jwtk/jjwt/issues/536\">Issue 536</a>.\n     *\n     * @param compressed the compressed byte array\n     * @return decompressed bytes\n     * @throws IOException if unable to decompress using the 0.10.6 and earlier logic\n     * @since 0.10.8\n     */\n    // package protected on purpose\n    byte[] doDecompressBackCompat(byte[] compressed) throws IOException {\n        InflaterOutputStream inflaterOutputStream = null;\n        ByteArrayOutputStream decompressedOutputStream = null;\n\n        try {\n            decompressedOutputStream = new ByteArrayOutputStream();\n            inflaterOutputStream = new InflaterOutputStream(decompressedOutputStream);\n            inflaterOutputStream.write(compressed);\n            inflaterOutputStream.flush();\n            return decompressedOutputStream.toByteArray();\n        } finally {\n            Objects.nullSafeClose(decompressedOutputStream, inflaterOutputStream);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/compression/GzipCompressionAlgorithm.java",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.compression;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.util.zip.GZIPInputStream;\nimport java.util.zip.GZIPOutputStream;\n\n/**\n * Codec implementing the <a href=\"https://en.wikipedia.org/wiki/Gzip\">gzip compression algorithm</a>.\n *\n * @since 0.6.0\n */\npublic class GzipCompressionAlgorithm extends AbstractCompressionAlgorithm {\n\n    private static final String ID = \"GZIP\";\n\n    public GzipCompressionAlgorithm() {\n        super(ID);\n    }\n\n    @Override\n    protected OutputStream doCompress(OutputStream out) throws IOException {\n        return new GZIPOutputStream(out);\n    }\n\n    @Override\n    protected InputStream doDecompress(InputStream is) throws IOException {\n        return new GZIPInputStream(is);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/AbstractParser.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.InputStream;\nimport java.io.Reader;\n\npublic abstract class AbstractParser<T> implements Parser<T> {\n\n    @Override\n    public final T parse(CharSequence input) {\n        Assert.hasText(input, \"CharSequence cannot be null or empty.\");\n        return parse(input, 0, input.length());\n    }\n\n    @Override\n    public T parse(CharSequence input, int start, int end) {\n        Assert.hasText(input, \"CharSequence cannot be null or empty.\");\n        Reader reader = new CharSequenceReader(input, start, end);\n        return parse(reader);\n    }\n\n    @Override\n    public final T parse(InputStream in) {\n        Assert.notNull(in, \"InputStream cannot be null.\");\n        Reader reader = Streams.reader(in);\n        return parse(reader);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/AbstractParserBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.lang.Services;\nimport io.jsonwebtoken.io.Deserializer;\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.io.ParserBuilder;\n\nimport java.security.Provider;\nimport java.util.Map;\n\npublic abstract class AbstractParserBuilder<T, B extends ParserBuilder<T, B>> implements ParserBuilder<T, B> {\n\n    protected Provider provider;\n\n    protected Deserializer<Map<String, ?>> deserializer;\n\n    @SuppressWarnings(\"unchecked\")\n    protected final B self() {\n        return (B) this;\n    }\n\n    @Override\n    public B provider(Provider provider) {\n        this.provider = provider;\n        return self();\n    }\n\n    @Override\n    public B json(Deserializer<Map<String, ?>> reader) {\n        this.deserializer = reader;\n        return self();\n    }\n\n    @Override\n    public final Parser<T> build() {\n        if (this.deserializer == null) {\n            //noinspection unchecked\n            this.deserializer = Services.get(Deserializer.class);\n        }\n        return doBuild();\n    }\n\n    protected abstract Parser<T> doBuild();\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/Base64Codec.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.lang.Strings;\n\n/**\n * Provides Base64 encoding and decoding as defined by <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045</a>.\n *\n * <p>\n * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose\n * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.\n * </p>\n * <p>\n * The class can be parameterized in the following manner with various constructors:\n * </p>\n * <ul>\n * <li>URL-safe mode: Default off.</li>\n * <li>Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of\n * 4 in the encoded data.\n * <li>Line separator: Default is CRLF (\"\\r\\n\")</li>\n * </ul>\n * <p>\n * The URL-safe parameter is only applied to encode operations. Decoding seamlessly handles both modes.\n * </p>\n * <p>\n * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only\n * encode/decode character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252,\n * UTF-8, etc).\n * </p>\n * <p>\n * This class is thread-safe.\n * </p>\n *\n * @see <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045</a>\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-codec/tree/585497f09b026f6602daf986723a554e051bdfe6\">commons-codec\n * 585497f09b026f6602daf986723a554e051bdfe6</a>\n */\nclass Base64Codec extends BaseNCodec {\n\n    /**\n     * BASE64 characters are 6 bits in length.\n     * They are formed by taking a block of 3 octets to form a 24-bit string,\n     * which is converted into 4 BASE64 characters.\n     */\n    private static final int BITS_PER_ENCODED_BYTE = 6;\n    private static final int BYTES_PER_UNENCODED_BLOCK = 3;\n    private static final int BYTES_PER_ENCODED_BLOCK = 4;\n\n    /**\n     * This array is a lookup table that translates 6-bit positive integer index values into their \"Base64 Alphabet\"\n     * equivalents as specified in Table 1 of RFC 2045.\n     * <p>\n     * Thanks to \"commons\" project in ws.apache.org for this code.\n     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/\n     * </p>\n     */\n    private static final byte[] STANDARD_ENCODE_TABLE = {\n            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',\n            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\n            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'\n    };\n\n    /**\n     * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and /\n     * changed to - and _ to make the encoded Base64 results more URL-SAFE.\n     * This table is only used when the Base64's mode is set to URL-SAFE.\n     */\n    private static final byte[] URL_SAFE_ENCODE_TABLE = {\n            'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',\n            'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',\n            'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',\n            'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',\n            '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'\n    };\n\n    /**\n     * This array is a lookup table that translates Unicode characters drawn from the \"Base64 Alphabet\" (as specified\n     * in Table 1 of RFC 2045) into their 6-bit positive integer equivalents. Characters that are not in the Base64\n     * alphabet but fall within the bounds of the array are translated to -1.\n     * <p>\n     * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both\n     * URL_SAFE and STANDARD base64. (The encoder, on the other hand, needs to know ahead of time what to emit).\n     * </p>\n     * <p>\n     * Thanks to \"commons\" project in ws.apache.org for this code.\n     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/\n     * </p>\n     */\n    // @formatter:off\n    private static final byte[] DECODE_TABLE = {\n          // 0   1   2   3   4   5   6   7   8   9   A   B   C   D   E   F\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 00-0f\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, // 10-1f\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, // 20-2f + - /\n            52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, // 30-3f 0-9\n            -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14, // 40-4f A-O\n            15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63, // 50-5f P-Z _\n            -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, // 60-6f a-o\n            41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51                      // 70-7a p-z\n    };\n    // @formatter:on\n\n    // The static final fields above are used for the original static byte[] methods on Base64.\n    // The private member fields below are used with the new streaming approach, which requires\n    // some state be preserved between calls of encode() and decode().\n\n    /* Base64 uses 6-bit fields. */\n    /**\n     * Mask used to extract 6 bits, used when encoding\n     */\n    private static final int MASK_6BITS = 0x3f;\n    /**\n     * Mask used to extract 4 bits, used when decoding final trailing character.\n     */\n    private static final int MASK_4BITS = 0xf;\n    /**\n     * Mask used to extract 2 bits, used when decoding final trailing character.\n     */\n    private static final int MASK_2BITS = 0x3;\n\n    /**\n     * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able\n     * to decode both STANDARD and URL_SAFE streams, but the encodeTable must be a member variable so we can switch\n     * between the two modes.\n     */\n    private final byte[] encodeTable;\n\n    /**\n     * Only one decode table currently; keep for consistency with Base32 code.\n     */\n    private final byte[] decodeTable = DECODE_TABLE;\n\n    /**\n     * Line separator for encoding. Not used when decoding. Only used if lineLength &gt; 0.\n     */\n    private final byte[] lineSeparator;\n\n    /**\n     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.\n     * {@code decodeSize = 3 + lineSeparator.length;}\n     */\n    private final int decodeSize;\n\n    /**\n     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.\n     * {@code encodeSize = 4 + lineSeparator.length;}\n     */\n    private final int encodeSize;\n\n//    /**\n//     * Decodes Base64 data into octets.\n//     * <p>\n//     * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.\n//     * </p>\n//     *\n//     * @param base64Data Byte array containing Base64 data\n//     * @return Array containing decoded data.\n//     */\n//    public static byte[] decodeBase64(final byte[] base64Data) {\n//        return new Base64().decode(base64Data);\n//    }\n//\n//    /**\n//     * Decodes a Base64 String into octets.\n//     * <p>\n//     * <b>Note:</b> this method seamlessly handles data encoded in URL-safe or normal mode.\n//     * </p>\n//     *\n//     * @param base64String String containing Base64 data\n//     * @return Array containing decoded data.\n//     * @since 1.4\n//     */\n//    public static byte[] decodeBase64(final String base64String) {\n//        return new Base64().decode(base64String);\n//    }\n//\n//    // Implementation of integer encoding used for crypto\n//\n//    /**\n//     * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.\n//     *\n//     * @param pArray a byte array containing base64 character data\n//     * @return A BigInteger\n//     * @since 1.4\n//     */\n//    public static BigInteger decodeInteger(final byte[] pArray) {\n//        return new BigInteger(1, decodeBase64(pArray));\n//    }\n//\n//    /**\n//     * Encodes binary data using the base64 algorithm but does not chunk the output.\n//     *\n//     * @param binaryData binary data to encode\n//     * @return byte[] containing Base64 characters in their UTF-8 representation.\n//     */\n//    public static byte[] encodeBase64(final byte[] binaryData) {\n//        return encodeBase64(binaryData, false);\n//    }\n//\n//    /**\n//     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.\n//     *\n//     * @param binaryData Array containing binary data to encode.\n//     * @param isChunked  if {@code true} this encoder will chunk the base64 output into 76 character blocks\n//     * @return Base64-encoded data.\n//     * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}\n//     */\n//    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) {\n//        return encodeBase64(binaryData, isChunked, false);\n//    }\n//\n//    /**\n//     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.\n//     *\n//     * @param binaryData Array containing binary data to encode.\n//     * @param isChunked  if {@code true} this encoder will chunk the base64 output into 76 character blocks\n//     * @param urlSafe    if {@code true} this encoder will emit - and _ instead of the usual + and / characters.\n//     *                   <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>\n//     * @return Base64-encoded data.\n//     * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}\n//     * @since 1.4\n//     */\n//    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) {\n//        return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);\n//    }\n//\n//    /**\n//     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.\n//     *\n//     * @param binaryData    Array containing binary data to encode.\n//     * @param isChunked     if {@code true} this encoder will chunk the base64 output into 76 character blocks\n//     * @param urlSafe       if {@code true} this encoder will emit - and _ instead of the usual + and / characters.\n//     *                      <b>Note: no padding is added when encoding using the URL-safe alphabet.</b>\n//     * @param maxResultSize The maximum result size to accept.\n//     * @return Base64-encoded data.\n//     * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than maxResultSize\n//     * @since 1.4\n//     */\n//    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked,\n//                                      final boolean urlSafe, final int maxResultSize) {\n//        if (Bytes.isEmpty(binaryData)) {\n//            return binaryData;\n//        }\n//\n//        // Create this so can use the super-class method\n//        // Also ensures that the same roundings are performed by the ctor and the code\n//        final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);\n//        final long len = b64.getEncodedLength(binaryData);\n//        if (len > maxResultSize) {\n//            throw new IllegalArgumentException(\"Input array too big, the output array would be bigger (\" +\n//                    len +\n//                    \") than the specified maximum size of \" +\n//                    maxResultSize);\n//        }\n//\n//        return b64.encode(binaryData);\n//    }\n//\n//    /**\n//     * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks\n//     *\n//     * @param binaryData binary data to encode\n//     * @return Base64 characters chunked in 76 character blocks\n//     */\n//    public static byte[] encodeBase64Chunked(final byte[] binaryData) {\n//        return encodeBase64(binaryData, true);\n//    }\n//\n//    /**\n//     * Encodes binary data using the base64 algorithm but does not chunk the output.\n//     * <p>\n//     * NOTE:  We changed the behavior of this method from multi-line chunking (commons-codec-1.4) to\n//     * single-line non-chunking (commons-codec-1.5).\n//     *\n//     * @param binaryData binary data to encode\n//     * @return String containing Base64 characters.\n//     * @since 1.4 (NOTE:  1.4 chunked the output, whereas 1.5 does not).\n//     */\n//    public static String encodeBase64String(final byte[] binaryData) {\n//        byte[] encoded = encodeBase64(binaryData, false);\n//        return new String(encoded, StandardCharsets.US_ASCII);\n//    }\n//\n//    /**\n//     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The\n//     * url-safe variation emits - and _ instead of + and / characters.\n//     * <b>Note: no padding is added.</b>\n//     *\n//     * @param binaryData binary data to encode\n//     * @return byte[] containing Base64 characters in their UTF-8 representation.\n//     * @since 1.4\n//     */\n//    public static byte[] encodeBase64URLSafe(final byte[] binaryData) {\n//        return encodeBase64(binaryData, false, true);\n//    }\n//\n//    /**\n//     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The\n//     * url-safe variation emits - and _ instead of + and / characters.\n//     * <b>Note: no padding is added.</b>\n//     *\n//     * @param binaryData binary data to encode\n//     * @return String containing Base64 characters\n//     * @since 1.4\n//     */\n//    public static String encodeBase64URLSafeString(final byte[] binaryData) {\n//        byte[] encoded = encodeBase64(binaryData, false, true);\n//        return new String(encoded, StandardCharsets.US_ASCII);\n//    }\n//\n//    /**\n//     * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature.\n//     *\n//     * @param bigInteger a BigInteger\n//     * @return A byte array containing base64 character data\n//     * @throws NullPointerException if null is passed in\n//     * @since 1.4\n//     */\n//    public static byte[] encodeInteger(final BigInteger bigInteger) {\n//        Objects.requireNonNull(bigInteger, \"bigInteger\");\n//        return encodeBase64(toIntegerBytes(bigInteger), false);\n//    }\n//\n//    /**\n//     * Returns whether or not the {@code octet} is in the base 64 alphabet.\n//     *\n//     * @param octet The value to test\n//     * @return {@code true} if the value is defined in the base 64 alphabet, {@code false} otherwise.\n//     * @since 1.4\n//     */\n//    public static boolean isBase64(final byte octet) {\n//        return octet == PAD_DEFAULT || octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1;\n//    }\n//\n//    /**\n//     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the\n//     * method treats whitespace as valid.\n//     *\n//     * @param arrayOctet byte array to test\n//     * @return {@code true} if all bytes are valid characters in the Base64 alphabet or if the byte array is empty;\n//     * {@code false}, otherwise\n//     * @since 1.5\n//     */\n//    public static boolean isBase64(final byte[] arrayOctet) {\n//        for (final byte element : arrayOctet) {\n//            if (!isBase64(element) && !Character.isWhitespace(element)) {\n//                return false;\n//            }\n//        }\n//        return true;\n//    }\n//\n//    /**\n//     * Tests a given String to see if it contains only valid characters within the Base64 alphabet. Currently the\n//     * method treats whitespace as valid.\n//     *\n//     * @param base64 String to test\n//     * @return {@code true} if all characters in the String are valid characters in the Base64 alphabet or if\n//     * the String is empty; {@code false}, otherwise\n//     * @since 1.5\n//     */\n//    public static boolean isBase64(final String base64) {\n//        return isBase64(Strings.utf8(base64));\n//    }\n//\n//    /**\n//     * Returns a byte-array representation of a {@code BigInteger} without sign bit.\n//     *\n//     * @param bigInt {@code BigInteger} to be converted\n//     * @return a byte array representation of the BigInteger parameter\n//     */\n//    static byte[] toIntegerBytes(final BigInteger bigInt) {\n//        int bitlen = bigInt.bitLength();\n//        // round bitlen\n//        bitlen = bitlen + 7 >> 3 << 3;\n//        final byte[] bigBytes = bigInt.toByteArray();\n//\n//        if (bigInt.bitLength() % 8 != 0 && bigInt.bitLength() / 8 + 1 == bitlen / 8) {\n//            return bigBytes;\n//        }\n//        // set up params for copying everything but sign bit\n//        int startSrc = 0;\n//        int len = bigBytes.length;\n//\n//        // if bigInt is exactly byte-aligned, just skip signbit in copy\n//        if (bigInt.bitLength() % 8 == 0) {\n//            startSrc = 1;\n//            len--;\n//        }\n//        final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec\n//        final byte[] resizedBytes = new byte[bitlen / 8];\n//        System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);\n//        return resizedBytes;\n//    }\n\n    /**\n     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.\n     * <p>\n     * When encoding the line length is 0 (no chunking), and the encoding table is STANDARD_ENCODE_TABLE.\n     * </p>\n     *\n     * <p>\n     * When decoding all variants are supported.\n     * </p>\n     */\n    Base64Codec() {\n        this(0);\n    }\n\n    /**\n     * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode.\n     * <p>\n     * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.\n     * </p>\n     *\n     * <p>\n     * When decoding all variants are supported.\n     * </p>\n     *\n     * @param urlSafe if {@code true}, URL-safe encoding is used. In most cases this should be set to\n     *                {@code false}.\n     * @since 1.4\n     */\n    Base64Codec(final boolean urlSafe) {\n        this(MIME_CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);\n    }\n\n    /**\n     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.\n     * <p>\n     * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is\n     * STANDARD_ENCODE_TABLE.\n     * </p>\n     * <p>\n     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.\n     * </p>\n     * <p>\n     * When decoding all variants are supported.\n     * </p>\n     *\n     * @param lineLength Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of\n     *                   4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when\n     *                   decoding.\n     * @since 1.4\n     */\n    Base64Codec(final int lineLength) {\n        this(lineLength, CHUNK_SEPARATOR);\n    }\n\n    /**\n     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.\n     * <p>\n     * When encoding the line length and line separator are given in the constructor, and the encoding table is\n     * STANDARD_ENCODE_TABLE.\n     * </p>\n     * <p>\n     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.\n     * </p>\n     * <p>\n     * When decoding all variants are supported.\n     * </p>\n     *\n     * @param lineLength    Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of\n     *                      4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when\n     *                      decoding.\n     * @param lineSeparator Each line of encoded data will end with this sequence of bytes.\n     * @throws IllegalArgumentException Thrown when the provided lineSeparator included some base64 characters.\n     * @since 1.4\n     */\n    Base64Codec(final int lineLength, final byte[] lineSeparator) {\n        this(lineLength, lineSeparator, false);\n    }\n\n    /**\n     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.\n     * <p>\n     * When encoding the line length and line separator are given in the constructor, and the encoding table is\n     * STANDARD_ENCODE_TABLE.\n     * </p>\n     * <p>\n     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.\n     * </p>\n     * <p>\n     * When decoding all variants are supported.\n     * </p>\n     *\n     * @param lineLength    Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of\n     *                      4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when\n     *                      decoding.\n     * @param lineSeparator Each line of encoded data will end with this sequence of bytes.\n     * @param urlSafe       Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode\n     *                      operations. Decoding seamlessly handles both modes.\n     *                      <b>Note: no padding is added when using the URL-safe alphabet.</b>\n     * @throws IllegalArgumentException Thrown when the {@code lineSeparator} contains Base64 characters.\n     * @since 1.4\n     */\n    Base64Codec(final int lineLength, final byte[] lineSeparator, final boolean urlSafe) {\n        this(lineLength, lineSeparator, urlSafe, DECODING_POLICY_DEFAULT);\n    }\n\n    /**\n     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.\n     * <p>\n     * When encoding the line length and line separator are given in the constructor, and the encoding table is\n     * STANDARD_ENCODE_TABLE.\n     * </p>\n     * <p>\n     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.\n     * </p>\n     * <p>\n     * When decoding all variants are supported.\n     * </p>\n     *\n     * @param lineLength     Each line of encoded data will be at most of the given length (rounded down to the nearest multiple of\n     *                       4). If lineLength &lt;= 0, then the output will not be divided into lines (chunks). Ignored when\n     *                       decoding.\n     * @param lineSeparator  Each line of encoded data will end with this sequence of bytes.\n     * @param urlSafe        Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode\n     *                       operations. Decoding seamlessly handles both modes.\n     *                       <b>Note: no padding is added when using the URL-safe alphabet.</b>\n     * @param decodingPolicy The decoding policy.\n     * @throws IllegalArgumentException Thrown when the {@code lineSeparator} contains Base64 characters.\n     * @since 1.15\n     */\n    Base64Codec(final int lineLength, final byte[] lineSeparator, final boolean urlSafe, final CodecPolicy decodingPolicy) {\n        super(BYTES_PER_UNENCODED_BLOCK, BYTES_PER_ENCODED_BLOCK, lineLength, BaseNCodec.length(lineSeparator),\n                PAD_DEFAULT, decodingPolicy);\n        // TODO could be simplified if there is no requirement to reject invalid line sep when length <=0\n        // @see test case Base64Test.testConstructors()\n        if (lineSeparator != null) {\n            if (containsAlphabetOrPad(lineSeparator)) {\n                final String sep = Strings.utf8(lineSeparator);\n                throw new IllegalArgumentException(\"lineSeparator must not contain base64 characters: [\" + sep + \"]\");\n            }\n            if (lineLength > 0) { // null line-sep forces no chunking rather than throwing IAE\n                this.encodeSize = BYTES_PER_ENCODED_BLOCK + lineSeparator.length;\n                this.lineSeparator = lineSeparator.clone();\n            } else {\n                this.encodeSize = BYTES_PER_ENCODED_BLOCK;\n                this.lineSeparator = null;\n            }\n        } else {\n            this.encodeSize = BYTES_PER_ENCODED_BLOCK;\n            this.lineSeparator = null;\n        }\n        this.decodeSize = this.encodeSize - 1;\n        this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;\n    }\n\n    // Implementation of the Encoder Interface\n\n    /**\n     * <p>\n     * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once\n     * with the data to decode, and once with inAvail set to \"-1\" to alert decoder that EOF has been reached. The \"-1\"\n     * call is not necessary when decoding, but it doesn't hurt, either.\n     * </p>\n     * <p>\n     * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are\n     * silently ignored, but has implications for other bytes, too. This method subscribes to the garbage-in,\n     * garbage-out philosophy: it will not check the provided data for validity.\n     * </p>\n     * <p>\n     * Thanks to \"commons\" project in ws.apache.org for the bitwise operations, and general approach.\n     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/\n     * </p>\n     *\n     * @param input   byte[] array of ASCII data to base64 decode.\n     * @param inPos   Position to start reading data from.\n     * @param inAvail Amount of bytes available from input for decoding.\n     * @param context the context to be used\n     */\n    @Override\n    void decode(final byte[] input, int inPos, final int inAvail, final Context context) {\n        if (context.eof) {\n            return;\n        }\n        if (inAvail < 0) {\n            context.eof = true;\n        }\n        for (int i = 0; i < inAvail; i++) {\n            final byte[] buffer = ensureBufferSize(decodeSize, context);\n            final byte b = input[inPos++];\n            if (b == pad) {\n                // We're done.\n                context.eof = true;\n                break;\n            }\n            if (b >= 0 && b < DECODE_TABLE.length) {\n                final int result = DECODE_TABLE[b];\n                if (result >= 0) {\n                    context.modulus = (context.modulus + 1) % BYTES_PER_ENCODED_BLOCK;\n                    context.ibitWorkArea = (context.ibitWorkArea << BITS_PER_ENCODED_BYTE) + result;\n                    if (context.modulus == 0) {\n                        buffer[context.pos++] = (byte) (context.ibitWorkArea >> 16 & MASK_8BITS);\n                        buffer[context.pos++] = (byte) (context.ibitWorkArea >> 8 & MASK_8BITS);\n                        buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);\n                    }\n                }\n            }\n        }\n\n        // Two forms of EOF as far as base64 decoder is concerned: actual\n        // EOF (-1) and first time '=' character is encountered in stream.\n        // This approach makes the '=' padding characters completely optional.\n        if (context.eof && context.modulus != 0) {\n            final byte[] buffer = ensureBufferSize(decodeSize, context);\n\n            // We have some spare bits remaining\n            // Output all whole multiples of 8 bits and ignore the rest\n            switch (context.modulus) {\n//              case 0 : // impossible, as excluded above\n                case 1: // 6 bits - either ignore entirely, or raise an exception\n                    validateTrailingCharacter();\n                    break;\n                case 2: // 12 bits = 8 + 4\n                    validateCharacter(MASK_4BITS, context);\n                    context.ibitWorkArea = context.ibitWorkArea >> 4; // dump the extra 4 bits\n                    buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);\n                    break;\n                case 3: // 18 bits = 8 + 8 + 2\n                    validateCharacter(MASK_2BITS, context);\n                    context.ibitWorkArea = context.ibitWorkArea >> 2; // dump 2 bits\n                    buffer[context.pos++] = (byte) (context.ibitWorkArea >> 8 & MASK_8BITS);\n                    buffer[context.pos++] = (byte) (context.ibitWorkArea & MASK_8BITS);\n                    break;\n                default:\n                    throw new IllegalStateException(\"Impossible modulus \" + context.modulus);\n            }\n        }\n    }\n\n    /**\n     * <p>\n     * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with\n     * the data to encode, and once with inAvail set to \"-1\" to alert encoder that EOF has been reached, to flush last\n     * remaining bytes (if not multiple of 3).\n     * </p>\n     * <p><b>Note: no padding is added when encoding using the URL-safe alphabet.</b></p>\n     * <p>\n     * Thanks to \"commons\" project in ws.apache.org for the bitwise operations, and general approach.\n     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/\n     * </p>\n     *\n     * @param in      byte[] array of binary data to base64 encode.\n     * @param inPos   Position to start reading data from.\n     * @param inAvail Amount of bytes available from input for encoding.\n     * @param context the context to be used\n     */\n    @Override\n    void encode(final byte[] in, int inPos, final int inAvail, final Context context) {\n        if (context.eof) {\n            return;\n        }\n        // inAvail < 0 is how we're informed of EOF in the underlying data we're\n        // encoding.\n        if (inAvail < 0) {\n            context.eof = true;\n            if (0 == context.modulus && lineLength == 0) {\n                return; // no leftovers to process and not using chunking\n            }\n            final byte[] buffer = ensureBufferSize(encodeSize, context);\n            final int savedPos = context.pos;\n            switch (context.modulus) { // 0-2\n                case 0: // nothing to do here\n                    break;\n                case 1: // 8 bits = 6 + 2\n                    // top 6 bits:\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 2 & MASK_6BITS];\n                    // remaining 2:\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea << 4 & MASK_6BITS];\n                    // URL-SAFE skips the padding to further reduce size.\n                    if (encodeTable == STANDARD_ENCODE_TABLE) {\n                        buffer[context.pos++] = pad;\n                        buffer[context.pos++] = pad;\n                    }\n                    break;\n\n                case 2: // 16 bits = 6 + 6 + 4\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 10 & MASK_6BITS];\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 4 & MASK_6BITS];\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea << 2 & MASK_6BITS];\n                    // URL-SAFE skips the padding to further reduce size.\n                    if (encodeTable == STANDARD_ENCODE_TABLE) {\n                        buffer[context.pos++] = pad;\n                    }\n                    break;\n                default:\n                    throw new IllegalStateException(\"Impossible modulus \" + context.modulus);\n            }\n            context.currentLinePos += context.pos - savedPos; // keep track of current line position\n            // if currentPos == 0 we are at the start of a line, so don't add CRLF\n            if (lineLength > 0 && context.currentLinePos > 0) {\n                System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);\n                context.pos += lineSeparator.length;\n            }\n        } else {\n            for (int i = 0; i < inAvail; i++) {\n                final byte[] buffer = ensureBufferSize(encodeSize, context);\n                context.modulus = (context.modulus + 1) % BYTES_PER_UNENCODED_BLOCK;\n                int b = in[inPos++];\n                if (b < 0) {\n                    b += 256;\n                }\n                context.ibitWorkArea = (context.ibitWorkArea << 8) + b; //  BITS_PER_BYTE\n                if (0 == context.modulus) { // 3 bytes = 24 bits = 4 * 6 bits to extract\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 18 & MASK_6BITS];\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 12 & MASK_6BITS];\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea >> 6 & MASK_6BITS];\n                    buffer[context.pos++] = encodeTable[context.ibitWorkArea & MASK_6BITS];\n                    context.currentLinePos += BYTES_PER_ENCODED_BLOCK;\n                    if (lineLength > 0 && lineLength <= context.currentLinePos) {\n                        System.arraycopy(lineSeparator, 0, buffer, context.pos, lineSeparator.length);\n                        context.pos += lineSeparator.length;\n                        context.currentLinePos = 0;\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Returns whether or not the {@code octet} is in the Base64 alphabet.\n     *\n     * @param octet The value to test\n     * @return {@code true} if the value is defined in the Base64 alphabet {@code false} otherwise.\n     */\n    @Override\n    protected boolean isInAlphabet(final byte octet) {\n        return octet >= 0 && octet < decodeTable.length && decodeTable[octet] != -1;\n    }\n\n    /**\n     * Returns our current encode mode. True if we're URL-SAFE, false otherwise.\n     *\n     * @return true if we're in URL-SAFE mode, false otherwise.\n     * @since 1.4\n     */\n    public boolean isUrlSafe() {\n        return this.encodeTable == URL_SAFE_ENCODE_TABLE;\n    }\n\n    /**\n     * Validates whether decoding the final trailing character is possible in the context\n     * of the set of possible base 64 values.\n     * <p>\n     * The character is valid if the lower bits within the provided mask are zero. This\n     * is used to test the final trailing base-64 digit is zero in the bits that will be discarded.\n     * </p>\n     *\n     * @param emptyBitsMask The mask of the lower bits that should be empty\n     * @param context       the context to be used\n     * @throws IllegalArgumentException if the bits being checked contain any non-zero value\n     */\n    private void validateCharacter(final int emptyBitsMask, final Context context) {\n        if (isStrictDecoding() && (context.ibitWorkArea & emptyBitsMask) != 0) {\n            throw new IllegalArgumentException(\n                    \"Strict decoding: Last encoded character (before the paddings if any) is a valid \" +\n                            \"base 64 alphabet but not a possible encoding. \" +\n                            \"Expected the discarded bits from the character to be zero.\");\n        }\n    }\n\n    /**\n     * Validates whether decoding allows an entire final trailing character that cannot be\n     * used for a complete byte.\n     *\n     * @throws IllegalArgumentException if strict decoding is enabled\n     */\n    private void validateTrailingCharacter() {\n        if (isStrictDecoding()) {\n            throw new IllegalArgumentException(\n                    \"Strict decoding: Last encoded character (before the paddings if any) is a valid \" +\n                            \"base 64 alphabet but not a possible encoding. \" +\n                            \"Decoding requires at least two trailing 6-bit characters to create bytes.\");\n        }\n    }\n\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/Base64InputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.InputStream;\n\n/**\n * Provides Base64 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength\n * is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate\n * constructor.\n * <p>\n * The default behavior of the Base64InputStream is to DECODE, whereas the default behavior of the Base64OutputStream\n * is to ENCODE, but this behavior can be overridden by using a different constructor.\n * </p>\n * <p>\n * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose\n * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.\n * </p>\n * <p>\n * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode\n * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).\n * </p>\n * <p>\n * You can set the decoding behavior when the input bytes contain leftover trailing bits that cannot be created by a\n * valid encoding. These can be bits that are unused from the final character or entire characters. The default mode is\n * lenient decoding.\n * </p>\n * <ul>\n * <li>Lenient: Any trailing bits are composed into 8-bit bytes where possible. The remainder are discarded.\n * <li>Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits are not part of a valid\n * encoding. Any unused bits from the final character must be zero. Impossible counts of entire final characters are not\n * allowed.\n * </ul>\n * <p>\n * When strict decoding is enabled it is expected that the decoded bytes will be re-encoded to a byte array that matches\n * the original, i.e. no changes occur on the final character. This requires that the input bytes use the same padding\n * and alphabet as the encoder.\n * </p>\n *\n * @see <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045</a>\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-codec/tree/585497f09b026f6602daf986723a554e051bdfe6\">commons-codec\n * 585497f09b026f6602daf986723a554e051bdfe6</a>\n */\npublic class Base64InputStream extends BaseNCodecInputStream {\n\n    /**\n     * Creates a Base64InputStream such that all data read is Base64-decoded from the original provided InputStream.\n     *\n     * @param inputStream InputStream to wrap.\n     */\n    public Base64InputStream(final InputStream inputStream) {\n        this(inputStream, false);\n    }\n\n    /**\n     * Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original\n     * provided InputStream.\n     *\n     * @param inputStream InputStream to wrap.\n     * @param doEncode    true if we should encode all data read from us, false if we should decode.\n     */\n    Base64InputStream(final InputStream inputStream, final boolean doEncode) {\n        super(inputStream, new Base64Codec(0, BaseNCodec.CHUNK_SEPARATOR, false, CodecPolicy.STRICT), doEncode);\n    }\n\n//    /**\n//     * Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original\n//     * provided InputStream.\n//     *\n//     * @param inputStream   InputStream to wrap.\n//     * @param doEncode      true if we should encode all data read from us, false if we should decode.\n//     * @param lineLength    If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to\n//     *                      the nearest multiple of 4). If lineLength &lt;= 0, the encoded data is not divided into lines. If\n//     *                      doEncode is false, lineLength is ignored.\n//     * @param lineSeparator If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \\r\\n).\n//     *                      If lineLength &lt;= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored.\n//     */\n//    Base64InputStream(final InputStream inputStream, final boolean doEncode, final int lineLength, final byte[] lineSeparator) {\n//        super(inputStream, new Base64Codec(lineLength, lineSeparator), doEncode);\n//    }\n//\n//    /**\n//     * Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original\n//     * provided InputStream.\n//     *\n//     * @param inputStream    InputStream to wrap.\n//     * @param doEncode       true if we should encode all data read from us, false if we should decode.\n//     * @param lineLength     If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to\n//     *                       the nearest multiple of 4). If lineLength &lt;= 0, the encoded data is not divided into lines. If\n//     *                       doEncode is false, lineLength is ignored.\n//     * @param lineSeparator  If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \\r\\n).\n//     *                       If lineLength &lt;= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored.\n//     * @param decodingPolicy The decoding policy.\n//     * @since 1.15\n//     */\n//    Base64InputStream(final InputStream inputStream, final boolean doEncode, final int lineLength, final byte[] lineSeparator,\n//                      final CodecPolicy decodingPolicy) {\n//        super(inputStream, new Base64Codec(lineLength, lineSeparator, false, decodingPolicy), doEncode);\n//    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/Base64OutputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.OutputStream;\n\n/**\n * Provides Base64 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength\n * is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate\n * constructor.\n * <p>\n * The default behavior of the Base64OutputStream is to ENCODE, whereas the default behavior of the Base64InputStream\n * is to DECODE. But this behavior can be overridden by using a different constructor.\n * </p>\n * <p>\n * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose\n * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein.\n * </p>\n * <p>\n * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode\n * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).\n * </p>\n * <p>\n * <b>Note:</b> It is mandatory to close the stream after the last byte has been written to it, otherwise the\n * final padding will be omitted and the resulting data will be incomplete/inconsistent.\n * </p>\n * <p>\n * You can set the decoding behavior when the input bytes contain leftover trailing bits that cannot be created by a\n * valid encoding. These can be bits that are unused from the final character or entire characters. The default mode is\n * lenient decoding.\n * </p>\n * <ul>\n * <li>Lenient: Any trailing bits are composed into 8-bit bytes where possible. The remainder are discarded.\n * <li>Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits are not part of a valid\n * encoding. Any unused bits from the final character must be zero. Impossible counts of entire final characters are not\n * allowed.\n * </ul>\n * <p>\n * When strict decoding is enabled it is expected that the decoded bytes will be re-encoded to a byte array that matches\n * the original, i.e. no changes occur on the final character. This requires that the input bytes use the same padding\n * and alphabet as the encoder.\n * </p>\n *\n * @see <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045</a>\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-codec/tree/585497f09b026f6602daf986723a554e051bdfe6\">commons-codec\n * 585497f09b026f6602daf986723a554e051bdfe6</a>\n */\nclass Base64OutputStream extends BaseNCodecOutputStream {\n\n    /**\n     * Creates a Base64OutputStream such that all data written is Base64-encoded to the original provided OutputStream.\n     *\n     * @param outputStream OutputStream to wrap.\n     */\n    Base64OutputStream(final OutputStream outputStream) {\n        this(outputStream, true, true);\n    }\n\n    /**\n     * Creates a Base64OutputStream such that all data written is either Base64-encoded or Base64-decoded to the\n     * original provided OutputStream.\n     *\n     * @param outputStream OutputStream to wrap.\n     * @param doEncode     true if we should encode all data written to us, false if we should decode.\n     */\n    Base64OutputStream(final OutputStream outputStream, final boolean doEncode, boolean urlSafe) {\n        super(outputStream, new Base64Codec(0, BaseNCodec.CHUNK_SEPARATOR, urlSafe), doEncode);\n    }\n\n    /**\n     * Creates a Base64OutputStream such that all data written is either Base64-encoded or Base64-decoded to the\n     * original provided OutputStream.\n     *\n     * @param outputStream  OutputStream to wrap.\n     * @param doEncode      true if we should encode all data written to us, false if we should decode.\n     * @param lineLength    If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to\n     *                      the nearest multiple of 4). If lineLength &lt;= 0, the encoded data is not divided into lines. If\n     *                      doEncode is false, lineLength is ignored.\n     * @param lineSeparator If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \\r\\n).\n     *                      If lineLength &lt;= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored.\n     */\n    Base64OutputStream(final OutputStream outputStream, final boolean doEncode, final int lineLength, final byte[] lineSeparator) {\n        super(outputStream, new Base64Codec(lineLength, lineSeparator), doEncode);\n    }\n\n    /**\n     * Creates a Base64OutputStream such that all data written is either Base64-encoded or Base64-decoded to the\n     * original provided OutputStream.\n     *\n     * @param outputStream   OutputStream to wrap.\n     * @param doEncode       true if we should encode all data written to us, false if we should decode.\n     * @param lineLength     If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to\n     *                       the nearest multiple of 4). If lineLength &lt;= 0, the encoded data is not divided into lines. If\n     *                       doEncode is false, lineLength is ignored.\n     * @param lineSeparator  If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \\r\\n).\n     *                       If lineLength &lt;= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored.\n     * @param decodingPolicy The decoding policy.\n     * @since 1.15\n     */\n    Base64OutputStream(final OutputStream outputStream, final boolean doEncode, final int lineLength,\n                       final byte[] lineSeparator, final CodecPolicy decodingPolicy) {\n        super(outputStream, new Base64Codec(lineLength, lineSeparator, false, decodingPolicy), doEncode);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/Base64UrlStreamEncoder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.io.Encoder;\nimport io.jsonwebtoken.io.EncodingException;\n\nimport java.io.OutputStream;\n\npublic final class Base64UrlStreamEncoder implements Encoder<OutputStream, OutputStream> {\n\n    public static final Base64UrlStreamEncoder INSTANCE = new Base64UrlStreamEncoder();\n\n    private Base64UrlStreamEncoder() {\n    }\n\n    @Override\n    public OutputStream encode(OutputStream outputStream) throws EncodingException {\n        return new Base64OutputStream(outputStream);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/BaseNCodec.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Arrays;\nimport java.util.Objects;\n\n/**\n * Abstract superclass for Base-N encoders and decoders.\n *\n * <p>\n * This class is thread-safe.\n * </p>\n * <p>\n * You can set the decoding behavior when the input bytes contain leftover trailing bits that cannot be created by a\n * valid encoding. These can be bits that are unused from the final character or entire characters. The default mode is\n * lenient decoding.\n * </p>\n * <ul>\n * <li>Lenient: Any trailing bits are composed into 8-bit bytes where possible. The remainder are discarded.\n * <li>Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits are not part of a valid\n * encoding. Any unused bits from the final character must be zero. Impossible counts of entire final characters are not\n * allowed.\n * </ul>\n * <p>\n * When strict decoding is enabled it is expected that the decoded bytes will be re-encoded to a byte array that matches\n * the original, i.e. no changes occur on the final character. This requires that the input bytes use the same padding\n * and alphabet as the encoder.\n * </p>\n *\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-codec/tree/585497f09b026f6602daf986723a554e051bdfe6\">commons-codec\n * 585497f09b026f6602daf986723a554e051bdfe6</a>\n */\nabstract class BaseNCodec {\n\n    /**\n     * EOF\n     */\n    static final int EOF = -1;\n\n    /**\n     * MIME chunk size per RFC 2045 section 6.8.\n     *\n     * <p>\n     * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any\n     * equal signs.\n     * </p>\n     *\n     * @see <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045 section 6.8</a>\n     */\n    public static final int MIME_CHUNK_SIZE = 76;\n\n//    /**\n//     * PEM chunk size per RFC 1421 section 4.3.2.4.\n//     *\n//     * <p>\n//     * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any\n//     * equal signs.\n//     * </p>\n//     *\n//     * @see <a href=\"http://tools.ietf.org/html/rfc1421\">RFC 1421 section 4.3.2.4</a>\n//     */\n//    public static final int PEM_CHUNK_SIZE = 64;\n\n    private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2;\n\n    /**\n     * Defines the default buffer size - currently {@value}\n     * - must be large enough for at least one encoded block+separator\n     */\n    private static final int DEFAULT_BUFFER_SIZE = 8192;\n\n    /**\n     * The maximum size buffer to allocate.\n     *\n     * <p>This is set to the same size used in the JDK {@code java.util.ArrayList}:</p>\n     * <blockquote>\n     * Some VMs reserve some header words in an array.\n     * Attempts to allocate larger arrays may result in\n     * OutOfMemoryError: Requested array size exceeds VM limit.\n     * </blockquote>\n     */\n    private static final int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;\n\n    /**\n     * Mask used to extract 8 bits, used in decoding bytes\n     */\n    protected static final int MASK_8BITS = 0xff;\n\n    /**\n     * Byte used to pad output.\n     */\n    protected static final byte PAD_DEFAULT = '='; // Allow static access to default\n\n    /**\n     * The default decoding policy.\n     *\n     * @since 1.15\n     */\n    protected static final CodecPolicy DECODING_POLICY_DEFAULT = CodecPolicy.LENIENT;\n\n    /**\n     * Chunk separator per RFC 2045 section 2.1.\n     *\n     * @see <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045 section 2.1</a>\n     */\n    static final byte[] CHUNK_SEPARATOR = {'\\r', '\\n'};\n\n    /**\n     * Pad byte. Instance variable just in case it needs to vary later.\n     */\n    protected final byte pad;\n\n    /**\n     * Number of bytes in each full block of unencoded data, e.g. 4 for Base64 and 5 for Base32\n     */\n    private final int unencodedBlockSize;\n\n    /**\n     * Number of bytes in each full block of encoded data, e.g. 3 for Base64 and 8 for Base32\n     */\n    private final int encodedBlockSize;\n\n    /**\n     * Chunksize for encoding. Not used when decoding.\n     * A value of zero or less implies no chunking of the encoded data.\n     * Rounded down to the nearest multiple of encodedBlockSize.\n     */\n    protected final int lineLength;\n\n    /**\n     * Size of chunk separator. Not used unless {@link #lineLength} &gt; 0.\n     */\n    private final int chunkSeparatorLength;\n\n    /**\n     * Defines the decoding behavior when the input bytes contain leftover trailing bits that\n     * cannot be created by a valid encoding. These can be bits that are unused from the final\n     * character or entire characters. The default mode is lenient decoding. Set this to\n     * {@code true} to enable strict decoding.\n     * <ul>\n     * <li>Lenient: Any trailing bits are composed into 8-bit bytes where possible.\n     *     The remainder are discarded.\n     * <li>Strict: The decoding will raise an {@link IllegalArgumentException} if trailing bits\n     *     are not part of a valid encoding. Any unused bits from the final character must\n     *     be zero. Impossible counts of entire final characters are not allowed.\n     * </ul>\n     * <p>\n     * When strict decoding is enabled it is expected that the decoded bytes will be re-encoded\n     * to a byte array that matches the original, i.e. no changes occur on the final\n     * character. This requires that the input bytes use the same padding and alphabet\n     * as the encoder.\n     * </p>\n     */\n    private final CodecPolicy decodingPolicy;\n\n    /**\n     * Holds thread context so classes can be thread-safe.\n     * <p>\n     * This class is not itself thread-safe; each thread must allocate its own copy.\n     *\n     * @since 1.7\n     */\n    static class Context {\n\n        /**\n         * Placeholder for the bytes we're dealing with for our based logic.\n         * Bitwise operations store and extract the encoding or decoding from this variable.\n         */\n        int ibitWorkArea;\n\n        /**\n         * Placeholder for the bytes we're dealing with for our based logic.\n         * Bitwise operations store and extract the encoding or decoding from this variable.\n         */\n        long lbitWorkArea;\n\n        /**\n         * Buffer for streaming.\n         */\n        byte[] buffer;\n\n        /**\n         * Position where next character should be written in the buffer.\n         */\n        int pos;\n\n        /**\n         * Position where next character should be read from the buffer.\n         */\n        int readPos;\n\n        /**\n         * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this object becomes useless,\n         * and must be thrown away.\n         */\n        boolean eof;\n\n        /**\n         * Variable tracks how many characters have been written to the current line. Only used when encoding. We use\n         * it to make sure each encoded line never goes beyond lineLength (if lineLength &gt; 0).\n         */\n        int currentLinePos;\n\n        /**\n         * Writes to the buffer only occur after every 3/5 reads when encoding, and every 4/8 reads when decoding. This\n         * variable helps track that.\n         */\n        int modulus;\n\n        /**\n         * Returns a String useful for debugging (especially within a debugger.)\n         *\n         * @return a String useful for debugging.\n         */\n        @Override\n        public String toString() {\n            return String.format(\"%s[buffer=%s, currentLinePos=%s, eof=%s, ibitWorkArea=%s, lbitWorkArea=%s, \" +\n                            \"modulus=%s, pos=%s, readPos=%s]\", getClass().getSimpleName(), Arrays.toString(buffer),\n                    currentLinePos, eof, ibitWorkArea, lbitWorkArea, modulus, pos, readPos);\n        }\n    }\n\n    /**\n     * Create a positive capacity at least as large the minimum required capacity.\n     * If the minimum capacity is negative then this throws an OutOfMemoryError as no array\n     * can be allocated.\n     *\n     * @param minCapacity the minimum capacity\n     * @return the capacity\n     * @throws OutOfMemoryError if the {@code minCapacity} is negative\n     */\n    private static int createPositiveCapacity(final int minCapacity) {\n        if (minCapacity < 0) {\n            // overflow\n            throw new OutOfMemoryError(\"Unable to allocate array size: \" + (minCapacity & 0xffffffffL));\n        }\n        // This is called when we require buffer expansion to a very big array.\n        // Use the conservative maximum buffer size if possible, otherwise the biggest required.\n        //\n        // Note: In this situation JDK 1.8 java.util.ArrayList returns Integer.MAX_VALUE.\n        // This excludes some VMs that can exceed MAX_BUFFER_SIZE but not allocate a full\n        // Integer.MAX_VALUE length array.\n        // The result is that we may have to allocate an array of this size more than once if\n        // the capacity must be expanded again.\n        return Math.max(minCapacity, MAX_BUFFER_SIZE);\n    }\n\n//    /**\n//     * Gets a copy of the chunk separator per RFC 2045 section 2.1.\n//     *\n//     * @return the chunk separator\n//     * @see <a href=\"http://www.ietf.org/rfc/rfc2045.txt\">RFC 2045 section 2.1</a>\n//     * @since 1.15\n//     */\n//    public static byte[] getChunkSeparator() {\n//        return CHUNK_SEPARATOR.clone();\n//    }\n\n    /**\n     * Checks if a byte value is whitespace or not.\n     *\n     * @param byteToCheck the byte to check\n     * @return true if byte is whitespace, false otherwise\n     * @see Character#isWhitespace(int)\n     * @deprecated Use {@link Character#isWhitespace(int)}.\n     */\n    @Deprecated\n    protected static boolean isWhiteSpace(final byte byteToCheck) {\n        return Character.isWhitespace(byteToCheck);\n    }\n\n    private static int compareUnsigned(int x, int y) {\n        return Integer.compare(x + Integer.MIN_VALUE, y + Integer.MIN_VALUE);\n    }\n\n    /**\n     * Increases our buffer by the {@link #DEFAULT_BUFFER_RESIZE_FACTOR}.\n     *\n     * @param context     the context to be used\n     * @param minCapacity the minimum required capacity\n     * @return the resized byte[] buffer\n     * @throws OutOfMemoryError if the {@code minCapacity} is negative\n     */\n    private static byte[] resizeBuffer(final Context context, final int minCapacity) {\n        // Overflow-conscious code treats the min and new capacity as unsigned.\n        final int oldCapacity = context.buffer.length;\n        int newCapacity = oldCapacity * DEFAULT_BUFFER_RESIZE_FACTOR;\n        if (compareUnsigned(newCapacity, minCapacity) < 0) {\n            newCapacity = minCapacity;\n        }\n        if (compareUnsigned(newCapacity, MAX_BUFFER_SIZE) > 0) {\n            newCapacity = createPositiveCapacity(minCapacity);\n        }\n\n        final byte[] b = Arrays.copyOf(context.buffer, newCapacity);\n        context.buffer = b;\n        return b;\n    }\n\n    /**\n     * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size.\n     * If {@code chunkSeparatorLength} is zero, then chunking is disabled.\n     *\n     * @param unencodedBlockSize   the size of an unencoded block (e.g. Base64 = 3)\n     * @param encodedBlockSize     the size of an encoded block (e.g. Base64 = 4)\n     * @param lineLength           if &gt; 0, use chunking with a length {@code lineLength}\n     * @param chunkSeparatorLength the chunk separator length, if relevant\n     */\n    protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize,\n                         final int lineLength, final int chunkSeparatorLength) {\n        this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, PAD_DEFAULT);\n    }\n\n    /**\n     * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size.\n     * If {@code chunkSeparatorLength} is zero, then chunking is disabled.\n     *\n     * @param unencodedBlockSize   the size of an unencoded block (e.g. Base64 = 3)\n     * @param encodedBlockSize     the size of an encoded block (e.g. Base64 = 4)\n     * @param lineLength           if &gt; 0, use chunking with a length {@code lineLength}\n     * @param chunkSeparatorLength the chunk separator length, if relevant\n     * @param pad                  byte used as padding byte.\n     */\n    protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize,\n                         final int lineLength, final int chunkSeparatorLength, final byte pad) {\n        this(unencodedBlockSize, encodedBlockSize, lineLength, chunkSeparatorLength, pad, DECODING_POLICY_DEFAULT);\n    }\n\n    /**\n     * Note {@code lineLength} is rounded down to the nearest multiple of the encoded block size.\n     * If {@code chunkSeparatorLength} is zero, then chunking is disabled.\n     *\n     * @param unencodedBlockSize   the size of an unencoded block (e.g. Base64 = 3)\n     * @param encodedBlockSize     the size of an encoded block (e.g. Base64 = 4)\n     * @param lineLength           if &gt; 0, use chunking with a length {@code lineLength}\n     * @param chunkSeparatorLength the chunk separator length, if relevant\n     * @param pad                  byte used as padding byte.\n     * @param decodingPolicy       Decoding policy.\n     * @since 1.15\n     */\n    protected BaseNCodec(final int unencodedBlockSize, final int encodedBlockSize,\n                         final int lineLength, final int chunkSeparatorLength, final byte pad,\n                         final CodecPolicy decodingPolicy) {\n        this.unencodedBlockSize = unencodedBlockSize;\n        this.encodedBlockSize = encodedBlockSize;\n        final boolean useChunking = lineLength > 0 && chunkSeparatorLength > 0;\n        this.lineLength = useChunking ? lineLength / encodedBlockSize * encodedBlockSize : 0;\n        this.chunkSeparatorLength = chunkSeparatorLength;\n        this.pad = pad;\n        this.decodingPolicy = Objects.requireNonNull(decodingPolicy, \"codecPolicy\");\n    }\n\n    /**\n     * Returns the amount of buffered data available for reading.\n     *\n     * @param context the context to be used\n     * @return The amount of buffered data available for reading.\n     */\n    int available(final Context context) {  // package protected for access from I/O streams\n        return hasData(context) ? context.pos - context.readPos : 0;\n    }\n\n    /**\n     * Tests a given byte array to see if it contains any characters within the alphabet or PAD.\n     * <p>\n     * Intended for use in checking line-ending arrays\n     *\n     * @param arrayOctet byte array to test\n     * @return {@code true} if any byte is a valid character in the alphabet or PAD; {@code false} otherwise\n     */\n    protected boolean containsAlphabetOrPad(final byte[] arrayOctet) {\n        if (arrayOctet == null) {\n            return false;\n        }\n        for (final byte element : arrayOctet) {\n            if (pad == element || isInAlphabet(element)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    static int length(byte[] bytes) {\n        return bytes != null ? bytes.length : 0;\n    }\n\n    static boolean isEmpty(byte[] bytes) {\n        return length(bytes) == 0;\n    }\n\n    /**\n     * Decodes a byte[] containing characters in the Base-N alphabet.\n     *\n     * @param pArray A byte array containing Base-N character data\n     * @return a byte array containing binary data\n     */\n    public byte[] decode(final byte[] pArray) {\n        if (isEmpty(pArray)) {\n            return pArray;\n        }\n        final Context context = new Context();\n        decode(pArray, 0, pArray.length, context);\n        decode(pArray, 0, EOF, context); // Notify decoder of EOF.\n        final byte[] result = new byte[context.pos];\n        readResults(result, 0, result.length, context);\n        return result;\n    }\n\n    // package protected for access from I/O streams\n    abstract void decode(byte[] pArray, int i, int length, Context context);\n\n    /**\n     * Decodes a String containing characters in the Base-N alphabet.\n     *\n     * @param pArray A String containing Base-N character data\n     * @return a byte array containing binary data\n     */\n    public byte[] decode(final String pArray) {\n        return decode(Strings.utf8(pArray));\n    }\n\n    /**\n     * Encodes a byte[] containing binary data, into a byte[] containing characters in the alphabet.\n     *\n     * @param pArray a byte array containing binary data\n     * @return A byte array containing only the base N alphabetic character data\n     */\n    public byte[] encode(final byte[] pArray) {\n        if (isEmpty(pArray)) {\n            return pArray;\n        }\n        return encode(pArray, 0, pArray.length);\n    }\n\n    /**\n     * Encodes a byte[] containing binary data, into a byte[] containing\n     * characters in the alphabet.\n     *\n     * @param pArray a byte array containing binary data\n     * @param offset initial offset of the subarray.\n     * @param length length of the subarray.\n     * @return A byte array containing only the base N alphabetic character data\n     */\n    public byte[] encode(final byte[] pArray, final int offset, final int length) {\n        if (isEmpty(pArray)) {\n            return pArray;\n        }\n        final Context context = new Context();\n        encode(pArray, offset, length, context);\n        encode(pArray, offset, EOF, context); // Notify encoder of EOF.\n        final byte[] buf = new byte[context.pos - context.readPos];\n        readResults(buf, 0, buf.length, context);\n        return buf;\n    }\n\n    // package protected for access from I/O streams\n    abstract void encode(byte[] pArray, int i, int length, Context context);\n\n    /**\n     * Encodes a byte[] containing binary data, into a String containing characters in the appropriate alphabet.\n     * Uses UTF8 encoding.\n     *\n     * @param pArray a byte array containing binary data\n     * @return String containing only character data in the appropriate alphabet.\n     * @since 1.5\n     * This is a duplicate of {@link #encodeToString(byte[])}; it was merged during refactoring.\n     */\n    public String encodeAsString(final byte[] pArray) {\n        return Strings.utf8(encode(pArray));\n    }\n\n    /**\n     * Encodes a byte[] containing binary data, into a String containing characters in the Base-N alphabet.\n     * Uses UTF8 encoding.\n     *\n     * @param pArray a byte array containing binary data\n     * @return A String containing only Base-N character data\n     */\n    public String encodeToString(final byte[] pArray) {\n        return Strings.utf8(encode(pArray));\n    }\n\n    /**\n     * Ensure that the buffer has room for {@code size} bytes\n     *\n     * @param size    minimum spare space required\n     * @param context the context to be used\n     * @return the buffer\n     */\n    protected byte[] ensureBufferSize(final int size, final Context context) {\n        if (context.buffer == null) {\n            context.buffer = new byte[Math.max(size, getDefaultBufferSize())];\n            context.pos = 0;\n            context.readPos = 0;\n\n            // Overflow-conscious:\n            // x + y > z  ==  x + y - z > 0\n        } else if (context.pos + size - context.buffer.length > 0) {\n            return resizeBuffer(context, context.pos + size);\n        }\n        return context.buffer;\n    }\n\n//    /**\n//     * Returns the decoding behavior policy.\n//     *\n//     * <p>\n//     * The default is lenient. If the decoding policy is strict, then decoding will raise an\n//     * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding. Decoding will compose\n//     * trailing bits into 8-bit bytes and discard the remainder.\n//     * </p>\n//     *\n//     * @return true if using strict decoding\n//     * @since 1.15\n//     */\n//    public CodecPolicy getCodecPolicy() {\n//        return decodingPolicy;\n//    }\n\n    /**\n     * Get the default buffer size. Can be overridden.\n     *\n     * @return the default buffer size.\n     */\n    protected int getDefaultBufferSize() {\n        return DEFAULT_BUFFER_SIZE;\n    }\n\n    /**\n     * Calculates the amount of space needed to encode the supplied array.\n     *\n     * @param pArray byte[] array which will later be encoded\n     * @return amount of space needed to encode the supplied array.\n     * Returns a long since a max-len array will require &gt; Integer.MAX_VALUE\n     */\n    public long getEncodedLength(final byte[] pArray) {\n        // Calculate non-chunked size - rounded up to allow for padding\n        // cast to long is needed to avoid possibility of overflow\n        long len = (pArray.length + unencodedBlockSize - 1) / unencodedBlockSize * (long) encodedBlockSize;\n        if (lineLength > 0) { // We're using chunking\n            // Round up to nearest multiple\n            len += (len + lineLength - 1) / lineLength * chunkSeparatorLength;\n        }\n        return len;\n    }\n\n    /**\n     * Returns true if this object has buffered data for reading.\n     *\n     * @param context the context to be used\n     * @return true if there is data still available for reading.\n     */\n    boolean hasData(final Context context) {  // package protected for access from I/O streams\n        return context.pos > context.readPos;\n    }\n\n    /**\n     * Returns whether or not the {@code octet} is in the current alphabet.\n     * Does not allow whitespace or pad.\n     *\n     * @param value The value to test\n     * @return {@code true} if the value is defined in the current alphabet, {@code false} otherwise.\n     */\n    protected abstract boolean isInAlphabet(byte value);\n\n    /**\n     * Tests a given byte array to see if it contains only valid characters within the alphabet.\n     * The method optionally treats whitespace and pad as valid.\n     *\n     * @param arrayOctet byte array to test\n     * @param allowWSPad if {@code true}, then whitespace and PAD are also allowed\n     * @return {@code true} if all bytes are valid characters in the alphabet or if the byte array is empty;\n     * {@code false}, otherwise\n     */\n    public boolean isInAlphabet(final byte[] arrayOctet, final boolean allowWSPad) {\n        for (final byte octet : arrayOctet) {\n            if (!isInAlphabet(octet) &&\n                    (!allowWSPad || octet != pad && !Character.isWhitespace(octet))) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    /**\n     * Tests a given String to see if it contains only valid characters within the alphabet.\n     * The method treats whitespace and PAD as valid.\n     *\n     * @param basen String to test\n     * @return {@code true} if all characters in the String are valid characters in the alphabet or if\n     * the String is empty; {@code false}, otherwise\n     * @see #isInAlphabet(byte[], boolean)\n     */\n    public boolean isInAlphabet(final String basen) {\n        return isInAlphabet(Strings.utf8(basen), true);\n    }\n\n    /**\n     * Returns true if decoding behavior is strict. Decoding will raise an {@link IllegalArgumentException} if trailing\n     * bits are not part of a valid encoding.\n     *\n     * <p>\n     * The default is false for lenient decoding. Decoding will compose trailing bits into 8-bit bytes and discard the\n     * remainder.\n     * </p>\n     *\n     * @return true if using strict decoding\n     * @since 1.15\n     */\n    public boolean isStrictDecoding() {\n        return decodingPolicy == CodecPolicy.STRICT;\n    }\n\n    /**\n     * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail\n     * bytes. Returns how many bytes were actually extracted.\n     * <p>\n     * Package private for access from I/O streams.\n     * </p>\n     *\n     * @param b       byte[] array to extract the buffered data into.\n     * @param bPos    position in byte[] array to start extraction at.\n     * @param bAvail  amount of bytes we're allowed to extract. We may extract fewer (if fewer are available).\n     * @param context the context to be used\n     * @return The number of bytes successfully extracted into the provided byte[] array.\n     */\n    int readResults(final byte[] b, final int bPos, final int bAvail, final Context context) {\n        if (hasData(context)) {\n            final int len = Math.min(available(context), bAvail);\n            System.arraycopy(context.buffer, context.readPos, b, bPos, len);\n            context.readPos += len;\n            if (!hasData(context)) {\n                // All data read.\n                // Reset position markers but do not set buffer to null to allow its reuse.\n                // hasData(context) will still return false, and this method will return 0 until\n                // more data is available, or -1 if EOF.\n                context.pos = context.readPos = 0;\n            }\n            return len;\n        }\n        return context.eof ? EOF : 0;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/BaseNCodecInputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.Objects;\n\n/**\n * Abstract superclass for Base-N input streams.\n *\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-codec/tree/585497f09b026f6602daf986723a554e051bdfe6\">commons-codec\n * 585497f09b026f6602daf986723a554e051bdfe6</a>\n */\nclass BaseNCodecInputStream extends FilterInputStream {\n\n    private final BaseNCodec baseNCodec;\n\n    private final boolean doEncode;\n\n    private final byte[] singleByte = new byte[1];\n\n    private final byte[] buf;\n\n    private final BaseNCodec.Context context = new BaseNCodec.Context();\n\n    /**\n     * Create an instance.\n     *\n     * @param inputStream the input stream\n     * @param baseNCodec  the codec\n     * @param doEncode    set to true to perform encoding, else decoding\n     */\n    protected BaseNCodecInputStream(final InputStream inputStream, final BaseNCodec baseNCodec, final boolean doEncode) {\n        super(inputStream);\n        this.doEncode = doEncode;\n        this.baseNCodec = baseNCodec;\n        this.buf = new byte[doEncode ? 4096 : 8192];\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @return {@code 0} if the {@link InputStream} has reached {@code EOF},\n     * {@code 1} otherwise\n     * @since 1.7\n     */\n    @Override\n    public int available() throws IOException {\n        // Note: the logic is similar to the InflaterInputStream:\n        //       as long as we have not reached EOF, indicate that there is more\n        //       data available. As we do not know for sure how much data is left,\n        //       just return 1 as a safe guess.\n\n        return context.eof ? 0 : 1;\n    }\n\n    /**\n     * Returns true if decoding behavior is strict. Decoding will raise an\n     * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.\n     *\n     * <p>\n     * The default is false for lenient encoding. Decoding will compose trailing bits\n     * into 8-bit bytes and discard the remainder.\n     * </p>\n     *\n     * @return true if using strict decoding\n     * @since 1.15\n     */\n    public boolean isStrictDecoding() {\n        return baseNCodec.isStrictDecoding();\n    }\n\n    /**\n     * Marks the current position in this input stream.\n     * <p>\n     * The {@link #mark} method of {@link BaseNCodecInputStream} does nothing.\n     * </p>\n     *\n     * @param readLimit the maximum limit of bytes that can be read before the mark position becomes invalid.\n     * @see #markSupported()\n     * @since 1.7\n     */\n    @Override\n    public synchronized void mark(final int readLimit) {\n        // noop\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @return Always returns {@code false}\n     */\n    @Override\n    public boolean markSupported() {\n        return false; // not an easy job to support marks\n    }\n\n    /**\n     * Reads one {@code byte} from this input stream.\n     *\n     * @return the byte as an integer in the range 0 to 255. Returns -1 if EOF has been reached.\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public int read() throws IOException {\n        int r = read(singleByte, 0, 1);\n        while (r == 0) {\n            r = read(singleByte, 0, 1);\n        }\n        if (r > 0) {\n            final byte b = singleByte[0];\n            return b < 0 ? 256 + b : b;\n        }\n        return BaseNCodec.EOF;\n    }\n\n    /**\n     * Attempts to read {@code len} bytes into the specified {@code b} array starting at {@code offset}\n     * from this InputStream.\n     *\n     * @param array  destination byte array\n     * @param offset where to start writing the bytes\n     * @param len    maximum number of bytes to read\n     * @return number of bytes read\n     * @throws IOException               if an I/O error occurs.\n     * @throws NullPointerException      if the byte array parameter is null\n     * @throws IndexOutOfBoundsException if offset, len or buffer size are invalid\n     */\n    @Override\n    public int read(final byte[] array, final int offset, final int len) throws IOException {\n        Objects.requireNonNull(array, \"array\");\n        if (offset < 0 || len < 0) {\n            throw new IndexOutOfBoundsException();\n        }\n        if (offset > array.length || offset + len > array.length) {\n            throw new IndexOutOfBoundsException();\n        }\n        if (len == 0) {\n            return 0;\n        }\n        int readLen = 0;\n        /*\n         Rationale for while-loop on (readLen == 0):\n         -----\n         Base32.readResults() usually returns > 0 or EOF (-1).  In the\n         rare case where it returns 0, we just keep trying.\n\n         This is essentially an undocumented contract for InputStream\n         implementors that want their code to work properly with\n         java.io.InputStreamReader, since the latter hates it when\n         InputStream.read(byte[]) returns a zero.  Unfortunately our\n         readResults() call must return 0 if a large amount of the data\n         being decoded was non-base32, so this while-loop enables proper\n         interop with InputStreamReader for that scenario.\n         -----\n         This is a fix for CODEC-101\n        */\n        // Attempt to read the request length\n        while (readLen < len) {\n            if (!baseNCodec.hasData(context)) {\n                // Obtain more data.\n                // buf is reused across calls to read to avoid repeated allocations\n                final int c = in.read(buf);\n                if (doEncode) {\n                    baseNCodec.encode(buf, 0, c, context);\n                } else {\n                    baseNCodec.decode(buf, 0, c, context);\n                }\n            }\n            final int read = baseNCodec.readResults(array, offset + readLen, len - readLen, context);\n            if (read < 0) {\n                // Return the amount read or EOF\n                return readLen != 0 ? readLen : -1;\n            }\n            readLen += read;\n        }\n        return readLen;\n    }\n\n    /**\n     * Repositions this stream to the position at the time the mark method was last called on this input stream.\n     * <p>\n     * The {@link #reset} method of {@link BaseNCodecInputStream} does nothing except throw an {@link IOException}.\n     *\n     * @throws IOException if this method is invoked\n     * @since 1.7\n     */\n    @Override\n    public synchronized void reset() throws IOException {\n        throw new IOException(\"mark/reset not supported\");\n    }\n\n    /**\n     * {@inheritDoc}\n     *\n     * @throws IllegalArgumentException if the provided skip length is negative\n     * @since 1.7\n     */\n    @Override\n    public long skip(final long n) throws IOException {\n        if (n < 0) {\n            throw new IllegalArgumentException(\"Negative skip length: \" + n);\n        }\n\n        // skip in chunks of 512 bytes\n        final byte[] b = new byte[512];\n        long todo = n;\n\n        while (todo > 0) {\n            int len = (int) Math.min(b.length, todo);\n            len = this.read(b, 0, len);\n            if (len == BaseNCodec.EOF) {\n                break;\n            }\n            todo -= len;\n        }\n\n        return n - todo;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/BaseNCodecOutputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.FilterOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.Objects;\n\n/**\n * Abstract superclass for Base-N output streams.\n * <p>\n * To write the EOF marker without closing the stream, call {@link #eof()} or use an <a\n * href=\"https://commons.apache.org/proper/commons-io/\">Apache Commons IO</a> <a href=\n * \"https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html\"\n * >CloseShieldOutputStream</a>.\n * </p>\n *\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-codec/tree/585497f09b026f6602daf986723a554e051bdfe6\">commons-codec\n * 585497f09b026f6602daf986723a554e051bdfe6</a>\n */\nclass BaseNCodecOutputStream extends FilterOutputStream {\n\n    private final boolean doEncode;\n\n    private final BaseNCodec baseNCodec;\n\n    private final byte[] singleByte = new byte[1];\n\n    private final BaseNCodec.Context context = new BaseNCodec.Context();\n\n    /**\n     * TODO should this be protected?\n     *\n     * @param outputStream the underlying output or null.\n     * @param basedCodec   a BaseNCodec.\n     * @param doEncode     true to encode, false to decode, TODO should be an enum?\n     */\n    BaseNCodecOutputStream(final OutputStream outputStream, final BaseNCodec basedCodec, final boolean doEncode) {\n        super(outputStream);\n        this.baseNCodec = basedCodec;\n        this.doEncode = doEncode;\n    }\n\n    /**\n     * Closes this output stream and releases any system resources associated with the stream.\n     * <p>\n     * To write the EOF marker without closing the stream, call {@link #eof()} or use an\n     * <a href=\"https://commons.apache.org/proper/commons-io/\">Apache Commons IO</a> <a href=\n     * \"https://commons.apache.org/proper/commons-io/apidocs/org/apache/commons/io/output/CloseShieldOutputStream.html\"\n     * >CloseShieldOutputStream</a>.\n     * </p>\n     *\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void close() throws IOException {\n        eof();\n        flush();\n        out.close();\n    }\n\n    /**\n     * Writes EOF.\n     *\n     * @since 1.11\n     */\n    public void eof() {\n        // Notify encoder of EOF (-1).\n        if (doEncode) {\n            baseNCodec.encode(singleByte, 0, BaseNCodec.EOF, context);\n        } else {\n            baseNCodec.decode(singleByte, 0, BaseNCodec.EOF, context);\n        }\n    }\n\n    /**\n     * Flushes this output stream and forces any buffered output bytes to be written out to the stream.\n     *\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void flush() throws IOException {\n        flush(true);\n    }\n\n    /**\n     * Flushes this output stream and forces any buffered output bytes to be written out to the stream. If propagate is\n     * true, the wrapped stream will also be flushed.\n     *\n     * @param propagate boolean flag to indicate whether the wrapped OutputStream should also be flushed.\n     * @throws IOException if an I/O error occurs.\n     */\n    private void flush(final boolean propagate) throws IOException {\n        final int avail = baseNCodec.available(context);\n        if (avail > 0) {\n            final byte[] buf = new byte[avail];\n            final int c = baseNCodec.readResults(buf, 0, avail, context);\n            if (c > 0) {\n                out.write(buf, 0, c);\n            }\n        }\n        if (propagate) {\n            out.flush();\n        }\n    }\n\n    /**\n     * Returns true if decoding behavior is strict. Decoding will raise an\n     * {@link IllegalArgumentException} if trailing bits are not part of a valid encoding.\n     *\n     * <p>\n     * The default is false for lenient encoding. Decoding will compose trailing bits\n     * into 8-bit bytes and discard the remainder.\n     * </p>\n     *\n     * @return true if using strict decoding\n     * @since 1.15\n     */\n    public boolean isStrictDecoding() {\n        return baseNCodec.isStrictDecoding();\n    }\n\n    /**\n     * Writes {@code len} bytes from the specified {@code b} array starting at {@code offset} to this\n     * output stream.\n     *\n     * @param array  source byte array\n     * @param offset where to start reading the bytes\n     * @param len    maximum number of bytes to write\n     * @throws IOException               if an I/O error occurs.\n     * @throws NullPointerException      if the byte array parameter is null\n     * @throws IndexOutOfBoundsException if offset, len or buffer size are invalid\n     */\n    @Override\n    public void write(final byte[] array, final int offset, final int len) throws IOException {\n        Objects.requireNonNull(array, \"array\");\n        if (offset < 0 || len < 0) {\n            throw new IndexOutOfBoundsException();\n        }\n        if (offset > array.length || offset + len > array.length) {\n            throw new IndexOutOfBoundsException();\n        }\n        if (len > 0) {\n            if (doEncode) {\n                baseNCodec.encode(array, offset, len, context);\n            } else {\n                baseNCodec.decode(array, offset, len, context);\n            }\n            flush(false);\n        }\n    }\n\n    /**\n     * Writes the specified {@code byte} to this output stream.\n     *\n     * @param i source byte\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void write(final int i) throws IOException {\n        singleByte[0] = (byte) i;\n        write(singleByte, 0, 1);\n    }\n\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/ByteBase64UrlStreamEncoder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.io.Encoder;\nimport io.jsonwebtoken.io.EncodingException;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\npublic class ByteBase64UrlStreamEncoder implements Encoder<OutputStream, OutputStream> {\n\n    private final Encoder<byte[], String> delegate;\n\n    public ByteBase64UrlStreamEncoder(Encoder<byte[], String> delegate) {\n        this.delegate = Assert.notNull(delegate, \"delegate cannot be null.\");\n    }\n\n    @Override\n    public OutputStream encode(OutputStream outputStream) throws EncodingException {\n        Assert.notNull(outputStream, \"outputStream cannot be null.\");\n        return new TranslatingOutputStream(outputStream, delegate);\n    }\n\n    private static class TranslatingOutputStream extends FilteredOutputStream {\n\n        private final OutputStream dst;\n        private final Encoder<byte[], String> delegate;\n\n        public TranslatingOutputStream(OutputStream dst, Encoder<byte[], String> delegate) {\n            super(new ByteArrayOutputStream());\n            this.dst = dst;\n            this.delegate = delegate;\n        }\n\n        @Override\n        public void close() throws IOException {\n            byte[] data = ((ByteArrayOutputStream) out).toByteArray();\n            String s = delegate.encode(data);\n            dst.write(Strings.utf8(s));\n            dst.flush();\n            dst.close();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/BytesInputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\n\n/**\n * Allows read access to the internal byte array, avoiding the need copy/extract to a\n * {@link java.io.ByteArrayOutputStream}.\n *\n * @since 0.12.2\n */\npublic final class BytesInputStream extends ByteArrayInputStream {\n\n    BytesInputStream(byte[] buf) {\n        super(Bytes.isEmpty(buf) ? Bytes.EMPTY : buf);\n    }\n\n    public byte[] getBytes() {\n        return this.buf;\n    }\n\n    @Override\n    public void close() throws IOException {\n        reset();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/CharSequenceReader.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.Reader;\nimport java.io.Serializable;\nimport java.util.Objects;\n\n/**\n * {@link Reader} implementation that can read from String, StringBuffer, StringBuilder or CharBuffer.\n *\n * <p>\n * <strong>Note:</strong> Supports {@link #mark(int)} and {@link #reset()}.\n * </p>\n *\n * @since 0.12.0, copied from commons-io\n * <a href=\"https://github.com/apache/commons-io/blob/e67946c81a55069dcd32dd588faa57dd1532455f/src/main/java/org/apache/commons/io/input/CharSequenceInputStream.java\">2.14.0</a>\n */\npublic class CharSequenceReader extends Reader implements Serializable {\n\n    private static final long serialVersionUID = 3724187752191401220L;\n    private final CharSequence charSequence;\n    private int idx;\n    private int mark;\n\n    /**\n     * The start index in the character sequence, inclusive.\n     * <p>\n     * When de-serializing a CharSequenceReader that was serialized before\n     * this fields was added, this field will be initialized to 0, which\n     * gives the same behavior as before: start reading from the start.\n     * </p>\n     *\n     * @see #start()\n     * @since 2.7\n     */\n    private final int start;\n\n    /**\n     * The end index in the character sequence, exclusive.\n     * <p>\n     * When de-serializing a CharSequenceReader that was serialized before\n     * this fields was added, this field will be initialized to {@code null},\n     * which gives the same behavior as before: stop reading at the\n     * CharSequence's length.\n     * If this field was an int instead, it would be initialized to 0 when the\n     * CharSequenceReader is de-serialized, causing it to not return any\n     * characters at all.\n     * </p>\n     *\n     * @see #end()\n     * @since 2.7\n     */\n    private final Integer end;\n\n    /**\n     * Constructs a new instance with the specified character sequence.\n     *\n     * @param charSequence The character sequence, may be {@code null}\n     */\n    public CharSequenceReader(final CharSequence charSequence) {\n        this(charSequence, 0);\n    }\n\n    /**\n     * Constructs a new instance with a portion of the specified character sequence.\n     * <p>\n     * The start index is not strictly enforced to be within the bounds of the\n     * character sequence. This allows the character sequence to grow or shrink\n     * in size without risking any {@link IndexOutOfBoundsException} to be thrown.\n     * Instead, if the character sequence grows smaller than the start index, this\n     * instance will act as if all characters have been read.\n     * </p>\n     *\n     * @param charSequence The character sequence, may be {@code null}\n     * @param start        The start index in the character sequence, inclusive\n     * @throws IllegalArgumentException if the start index is negative\n     */\n    public CharSequenceReader(final CharSequence charSequence, final int start) {\n        this(charSequence, start, Integer.MAX_VALUE);\n    }\n\n    /**\n     * Constructs a new instance with a portion of the specified character sequence.\n     * <p>\n     * The start and end indexes are not strictly enforced to be within the bounds\n     * of the character sequence. This allows the character sequence to grow or shrink\n     * in size without risking any {@link IndexOutOfBoundsException} to be thrown.\n     * Instead, if the character sequence grows smaller than the start index, this\n     * instance will act as if all characters have been read; if the character sequence\n     * grows smaller than the end, this instance will use the actual character sequence\n     * length.\n     * </p>\n     *\n     * @param charSequence The character sequence, may be {@code null}\n     * @param start        The start index in the character sequence, inclusive\n     * @param end          The end index in the character sequence, exclusive\n     * @throws IllegalArgumentException if the start index is negative, or if the end index is smaller than the start index\n     */\n    public CharSequenceReader(final CharSequence charSequence, final int start, final int end) {\n        if (start < 0) {\n            throw new IllegalArgumentException(\"Start index is less than zero: \" + start);\n        }\n        if (end < start) {\n            throw new IllegalArgumentException(\"End index is less than start \" + start + \": \" + end);\n        }\n        // Don't check the start and end indexes against the CharSequence,\n        // to let it grow and shrink without breaking existing behavior.\n\n        this.charSequence = charSequence != null ? charSequence : \"\";\n        this.start = start;\n        this.end = end;\n\n        this.idx = start;\n        this.mark = start;\n    }\n\n    /**\n     * Close resets the file back to the start and removes any marked position.\n     */\n    @Override\n    public void close() {\n        idx = start;\n        mark = start;\n    }\n\n    /**\n     * Returns the index in the character sequence to end reading at, taking into account its length.\n     *\n     * @return The end index in the character sequence (exclusive).\n     */\n    private int end() {\n        /*\n         * end == null for de-serialized instances that were serialized before start and end were added.\n         * Use Integer.MAX_VALUE to get the same behavior as before - use the entire CharSequence.\n         */\n        return Math.min(charSequence.length(), end == null ? Integer.MAX_VALUE : end);\n    }\n\n    /**\n     * Mark the current position.\n     *\n     * @param readAheadLimit ignored\n     */\n    @Override\n    public void mark(final int readAheadLimit) {\n        mark = idx;\n    }\n\n    /**\n     * Mark is supported (returns true).\n     *\n     * @return {@code true}\n     */\n    @Override\n    public boolean markSupported() {\n        return true;\n    }\n\n    /**\n     * Read a single character.\n     *\n     * @return the next character from the character sequence\n     * or -1 if the end has been reached.\n     */\n    @Override\n    public int read() {\n        if (idx >= end()) {\n            return Streams.EOF;\n        }\n        return charSequence.charAt(idx++);\n    }\n\n    /**\n     * Read the specified number of characters into the array.\n     *\n     * @param array  The array to store the characters in\n     * @param offset The starting position in the array to store\n     * @param length The maximum number of characters to read\n     * @return The number of characters read or -1 if there are\n     * no more\n     */\n    @Override\n    public int read(final char[] array, final int offset, final int length) {\n        if (idx >= end()) {\n            return Streams.EOF;\n        }\n        Objects.requireNonNull(array, \"array\");\n        if (length < 0 || offset < 0 || offset + length > array.length) {\n            throw new IndexOutOfBoundsException(\"Array Size=\" + array.length +\n                    \", offset=\" + offset + \", length=\" + length);\n        }\n\n        if (charSequence instanceof String) {\n            final int count = Math.min(length, end() - idx);\n            ((String) charSequence).getChars(idx, idx + count, array, offset);\n            idx += count;\n            return count;\n        }\n        if (charSequence instanceof StringBuilder) {\n            final int count = Math.min(length, end() - idx);\n            ((StringBuilder) charSequence).getChars(idx, idx + count, array, offset);\n            idx += count;\n            return count;\n        }\n        if (charSequence instanceof StringBuffer) {\n            final int count = Math.min(length, end() - idx);\n            ((StringBuffer) charSequence).getChars(idx, idx + count, array, offset);\n            idx += count;\n            return count;\n        }\n\n        int count = 0;\n        for (int i = 0; i < length; i++) {\n            final int c = read();\n            if (c == Streams.EOF) {\n                return count;\n            }\n            array[offset + i] = (char) c;\n            count++;\n        }\n        return count;\n    }\n\n    /**\n     * Tells whether this stream is ready to be read.\n     *\n     * @return {@code true} if more characters from the character sequence are available, or {@code false} otherwise.\n     */\n    @Override\n    public boolean ready() {\n        return idx < end();\n    }\n\n    /**\n     * Reset the reader to the last marked position (or the beginning if\n     * mark has not been called).\n     */\n    @Override\n    public void reset() {\n        idx = mark;\n    }\n\n    /**\n     * Skip the specified number of characters.\n     *\n     * @param n The number of characters to skip\n     * @return The actual number of characters skipped\n     */\n    @Override\n    public long skip(final long n) {\n        if (n < 0) {\n            throw new IllegalArgumentException(\"Number of characters to skip is less than zero: \" + n);\n        }\n        if (idx >= end()) {\n            return 0;\n        }\n        final int dest = (int) Math.min(end(), idx + n);\n        final int count = dest - idx;\n        idx = dest;\n        return count;\n    }\n\n    /**\n     * Returns the index in the character sequence to start reading from, taking into account its length.\n     *\n     * @return The start index in the character sequence (inclusive).\n     */\n    private int start() {\n        return Math.min(charSequence.length(), start);\n    }\n\n    /**\n     * Return a String representation of the underlying\n     * character sequence.\n     *\n     * @return The contents of the character sequence\n     */\n    @Override\n    public String toString() {\n        final CharSequence subSequence = charSequence.subSequence(start(), end());\n        return subSequence.toString();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/ClosedInputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-io/blob/3a17f5259b105e734c8adce1d06d68f29884d1bb/src/main/java/org/apache/commons/io/input/ClosedInputStream.java\">\n * commons-io 3a17f5259b105e734c8adce1d06d68f29884d1bb</a>\n */\npublic final class ClosedInputStream extends InputStream {\n\n    public static final ClosedInputStream INSTANCE = new ClosedInputStream();\n\n    private ClosedInputStream() {\n    }\n\n    @Override\n    public int read() throws IOException {\n        return Streams.EOF;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/Codec.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.DecodingException;\nimport io.jsonwebtoken.io.Encoder;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\n\npublic class Codec implements Converter<byte[], CharSequence> {\n\n    public static final Codec BASE64 = new Codec(Encoders.BASE64, Decoders.BASE64);\n    public static final Codec BASE64URL = new Codec(Encoders.BASE64URL, Decoders.BASE64URL);\n\n    private final Encoder<byte[], String> encoder;\n    private final Decoder<CharSequence, byte[]> decoder;\n\n    public Codec(Encoder<byte[], String> encoder, Decoder<CharSequence, byte[]> decoder) {\n        this.encoder = Assert.notNull(encoder, \"Encoder cannot be null.\");\n        this.decoder = Assert.notNull(decoder, \"Decoder cannot be null.\");\n    }\n\n    @Override\n    public String applyTo(byte[] a) {\n        return this.encoder.encode(a);\n    }\n\n    @Override\n    public byte[] applyFrom(CharSequence b) {\n        try {\n            return this.decoder.decode(b);\n        } catch (DecodingException e) {\n            String msg = \"Cannot decode input String. Cause: \" + e.getMessage();\n            throw new IllegalArgumentException(msg, e);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/CodecPolicy.java",
    "content": "/*\n * Licensed to the Apache Software Foundation (ASF) under one or more\n * contributor license agreements.  See the NOTICE file distributed with\n * this work for additional information regarding copyright ownership.\n * The ASF licenses this file to You under the Apache License, Version 2.0\n * (the \"License\"); you may not use this file except in compliance with\n * the License.  You may obtain a copy of the License at\n *\n *      http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.jsonwebtoken.impl.io;\n\n/**\n * Defines encoding and decoding policies.\n *\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-codec/tree/585497f09b026f6602daf986723a554e051bdfe6\">commons-codec\n * 585497f09b026f6602daf986723a554e051bdfe6</a>\n */\nenum CodecPolicy {\n\n    /**\n     * The strict policy. Data that causes a codec to fail should throw an exception.\n     */\n    STRICT,\n\n    /**\n     * The lenient policy. Data that causes a codec to fail should not throw an exception.\n     */\n    LENIENT\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/ConvertingParser.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.Reader;\nimport java.util.Map;\n\npublic class ConvertingParser<T> extends AbstractParser<T> {\n\n    private final Function<Reader, Map<String, ?>> deserializer;\n    private final Converter<T, Object> converter;\n\n    public ConvertingParser(Function<Reader, Map<String, ?>> deserializer, Converter<T, Object> converter) {\n        this.deserializer = Assert.notNull(deserializer, \"Deserializer function cannot be null.\");\n        this.converter = Assert.notNull(converter, \"Converter cannot be null.\");\n    }\n\n    @Override\n    public final T parse(Reader reader) {\n        Assert.notNull(reader, \"Reader cannot be null.\");\n        Map<String, ?> m = this.deserializer.apply(reader);\n        return this.converter.applyFrom(m);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/CountingInputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.util.concurrent.atomic.AtomicLong;\n\npublic class CountingInputStream extends FilterInputStream {\n\n    private final AtomicLong count = new AtomicLong(0);\n\n    public CountingInputStream(InputStream in) {\n        super(in);\n    }\n\n    public long getCount() {\n        return count.get();\n    }\n\n    private void add(long n) {\n        // n can be -1 for EOF, and 0 for no bytes read, so we only add if we actually read 1 or more bytes:\n        if (n > 0) count.addAndGet(n);\n    }\n\n    @Override\n    public int read() throws IOException {\n        int next = super.read();\n        add(next == Streams.EOF ? Streams.EOF : 1);\n        return next;\n    }\n\n    @Override\n    public int read(byte[] b) throws IOException {\n        int n = super.read(b);\n        add(n);\n        return n;\n    }\n\n    @Override\n    public int read(byte[] b, int off, int len) throws IOException {\n        int n = super.read(b, off, len);\n        add(n);\n        return n;\n    }\n\n    @Override\n    public long skip(long n) throws IOException {\n        final long skipped = super.skip(n);\n        add(skipped);\n        return skipped;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/DecodingInputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.io.DecodingException;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.InputStream;\n\npublic class DecodingInputStream extends FilteredInputStream {\n\n    private final String codecName;\n    private final String name;\n\n    public DecodingInputStream(InputStream in, String codecName, String name) {\n        super(in);\n        this.codecName = Assert.hasText(codecName, \"codecName cannot be null or empty.\");\n        this.name = Assert.hasText(name, \"Name cannot be null or empty.\");\n    }\n\n    @Override\n    protected void onThrowable(Throwable t) {\n        String msg = \"Unable to \" + this.codecName + \"-decode \" + this.name + \": \" + t.getMessage();\n        throw new DecodingException(msg, t);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/DelegateStringDecoder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.io.Decoder;\nimport io.jsonwebtoken.io.DecodingException;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.io.InputStream;\n\n@SuppressWarnings(\"DeprecatedIsStillUsed\")\n@Deprecated //TODO: delete when deleting JwtParserBuilder#base64UrlDecodeWith\npublic class DelegateStringDecoder implements Decoder<InputStream, InputStream> {\n\n    private final Decoder<CharSequence, byte[]> delegate;\n\n    public DelegateStringDecoder(Decoder<CharSequence, byte[]> delegate) {\n        this.delegate = Assert.notNull(delegate, \"delegate cannot be null.\");\n    }\n\n    @Override\n    public InputStream decode(InputStream in) throws DecodingException {\n        try {\n            byte[] data = Streams.bytes(in, \"Unable to Base64URL-decode input.\");\n            data = delegate.decode(Strings.utf8(data));\n            return Streams.of(data);\n        } catch (Throwable t) {\n            String msg = \"Unable to Base64Url-decode InputStream: \" + t.getMessage();\n            throw new DecodingException(msg, t);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/EncodingOutputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.io.EncodingException;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.OutputStream;\n\npublic class EncodingOutputStream extends FilteredOutputStream {\n\n    private final String codecName;\n    private final String name;\n\n    public EncodingOutputStream(OutputStream out, String codecName, String name) {\n        super(out);\n        this.codecName = Assert.hasText(codecName, \"codecName cannot be null or empty.\");\n        this.name = Assert.hasText(name, \"name cannot be null or empty.\");\n    }\n\n    @Override\n    protected void onThrowable(Throwable t) {\n        String msg = \"Unable to \" + this.codecName + \"-encode \" + this.name + \": \" + t.getMessage();\n        throw new EncodingException(msg, t);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/FilteredInputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\n\nimport java.io.FilterInputStream;\nimport java.io.IOException;\nimport java.io.InputStream;\n\n/**\n * A filter stream that delegates its calls to an internal delegate stream without changing behavior, but also providing\n * pre/post/error handling hooks.  It is useful as a base for extending and adding custom functionality.\n *\n * <p>It is an alternative base class to FilterInputStream to increase re-usability, because FilterInputStream changes\n * the methods being called, such as read(byte[]) to read(byte[], int, int).</p>\n *\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-io/blob/3a17f5259b105e734c8adce1d06d68f29884d1bb/src/main/java/org/apache/commons/io/input/ProxyInputStream.java\">\n * commons-io 3a17f5259b105e734c8adce1d06d68f29884d1bb</a>\n */\npublic abstract class FilteredInputStream extends FilterInputStream {\n\n    /**\n     * Constructs a new FilteredInputStream that delegates to the specified {@link InputStream}.\n     *\n     * @param in the InputStream to delegate to\n     */\n    public FilteredInputStream(final InputStream in) {\n        super(in); // the delegate is stored in a protected superclass variable named 'in'\n    }\n\n    /**\n     * Invoked by the read methods after the proxied call has returned\n     * successfully. The number of bytes returned to the caller (or -1 if\n     * the end of stream was reached) is given as an argument.\n     * <p>\n     * Subclasses can override this method to add common post-processing\n     * functionality without having to override all the read methods.\n     * The default implementation does nothing.\n     * </p>\n     * <p>\n     * Note this method is <em>not</em> called from {@link #skip(long)} or\n     * {@link #reset()}. You need to explicitly override those methods if\n     * you want to add post-processing steps also to them.\n     * </p>\n     *\n     * @param n number of bytes read, or -1 if the end of stream was reached\n     * @throws IOException if the post-processing fails\n     * @since 2.0\n     */\n    @SuppressWarnings({\"unused\", \"RedundantThrows\"}) // Possibly thrown from subclasses.\n    protected void afterRead(final int n) throws IOException {\n        // no-op\n    }\n\n    /**\n     * Invokes the delegate's {@code available()} method.\n     *\n     * @return the number of available bytes\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public int available() throws IOException {\n        try {\n            return super.available();\n        } catch (final Throwable t) {\n            onThrowable(t);\n            return 0;\n        }\n    }\n\n    /**\n     * Invoked by the read methods before the call is proxied. The number\n     * of bytes that the caller wanted to read (1 for the {@link #read()}\n     * method, buffer length for {@link #read(byte[])}, etc.) is given as\n     * an argument.\n     * <p>\n     * Subclasses can override this method to add common pre-processing\n     * functionality without having to override all the read methods.\n     * The default implementation does nothing.\n     * </p>\n     * <p>\n     * Note this method is <em>not</em> called from {@link #skip(long)} or\n     * {@link #reset()}. You need to explicitly override those methods if\n     * you want to add pre-processing steps also to them.\n     * </p>\n     *\n     * @param n number of bytes that the caller asked to be read\n     * @throws IOException if the pre-processing fails\n     * @since 2.0\n     */\n    @SuppressWarnings({\"unused\", \"RedundantThrows\"}) // Possibly thrown from subclasses.\n    protected void beforeRead(final int n) throws IOException {\n        // no-op\n    }\n\n    /**\n     * Invokes the delegate's {@code close()} method.\n     *\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void close() throws IOException {\n        try {\n            super.close();\n        } catch (Throwable t) {\n            onThrowable(t);\n        }\n    }\n\n    /**\n     * Handle any Throwable thrown; by default, throws the given exception.\n     * <p>\n     * This method provides a point to implement custom exception\n     * handling. The default behavior is to re-throw the exception.\n     * </p>\n     *\n     * @param t The IOException thrown\n     * @throws IOException if an I/O error occurs.\n     */\n    protected void onThrowable(final Throwable t) throws IOException {\n        if (t instanceof IOException) throw (IOException) t;\n        throw new IOException(\"IO Exception: \" + t.getMessage(), t);\n    }\n\n    /**\n     * Invokes the delegate's {@code mark(int)} method.\n     *\n     * @param readlimit read ahead limit\n     */\n    @Override\n    public synchronized void mark(final int readlimit) {\n        in.mark(readlimit);\n    }\n\n    /**\n     * Invokes the delegate's {@code markSupported()} method.\n     *\n     * @return true if mark is supported, otherwise false\n     */\n    @Override\n    public boolean markSupported() {\n        return in.markSupported();\n    }\n\n    /**\n     * Invokes the delegate's {@code read()} method.\n     *\n     * @return the byte read or -1 if the end of stream\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public int read() throws IOException {\n        try {\n            beforeRead(1);\n            final int b = in.read();\n            afterRead(b != Streams.EOF ? 1 : Streams.EOF);\n            return b;\n        } catch (final Throwable t) {\n            onThrowable(t);\n            return Streams.EOF;\n        }\n    }\n\n    /**\n     * Invokes the delegate's {@code read(byte[])} method.\n     *\n     * @param bts the buffer to read the bytes into\n     * @return the number of bytes read or EOF if the end of stream\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public int read(final byte[] bts) throws IOException {\n        try {\n            beforeRead(Bytes.length(bts));\n            final int n = in.read(bts);\n            afterRead(n);\n            return n;\n        } catch (final Throwable t) {\n            onThrowable(t);\n            return Streams.EOF;\n        }\n    }\n\n    /**\n     * Invokes the delegate's {@code read(byte[], int, int)} method.\n     *\n     * @param bts the buffer to read the bytes into\n     * @param off The start offset\n     * @param len The number of bytes to read\n     * @return the number of bytes read or -1 if the end of stream\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public int read(final byte[] bts, final int off, final int len) throws IOException {\n        try {\n            beforeRead(len);\n            final int n = in.read(bts, off, len);\n            afterRead(n);\n            return n;\n        } catch (final Throwable t) {\n            onThrowable(t);\n            return Streams.EOF;\n        }\n    }\n\n    /**\n     * Invokes the delegate's {@code reset()} method.\n     *\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public synchronized void reset() throws IOException {\n        try {\n            in.reset();\n        } catch (final Throwable t) {\n            onThrowable(t);\n        }\n    }\n\n    /**\n     * Invokes the delegate's {@code skip(long)} method.\n     *\n     * @param ln the number of bytes to skip\n     * @return the actual number of bytes skipped\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public long skip(final long ln) throws IOException {\n        try {\n            return in.skip(ln);\n        } catch (final Throwable t) {\n            onThrowable(t);\n            return 0;\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/FilteredOutputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\n\nimport java.io.FilterOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\n/**\n * A Proxy stream which acts as expected, that is it passes the method\n * calls on to the proxied stream and doesn't change which methods are\n * being called. It is an alternative base class to FilterOutputStream\n * to increase reusability.\n * <p>\n * See the protected methods for ways in which a subclass can easily decorate\n * a stream with custom pre-, post- or error processing functionality.\n * </p>\n *\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-io/blob/3a17f5259b105e734c8adce1d06d68f29884d1bb/src/main/java/org/apache/commons/io/output/ProxyOutputStream.java\">\n * commons-io 3a17f5259b105e734c8adce1d06d68f29884d1bb</a>\n */\npublic class FilteredOutputStream extends FilterOutputStream {\n\n    /**\n     * Constructs a new ProxyOutputStream.\n     *\n     * @param out the OutputStream to delegate to\n     */\n    public FilteredOutputStream(final OutputStream out) {\n        super(out); // the proxy is stored in a protected superclass variable named 'out'\n    }\n\n    /**\n     * Invoked by the write methods after the proxied call has returned\n     * successfully. The number of bytes written (1 for the\n     * {@link #write(int)} method, buffer length for {@link #write(byte[])},\n     * etc.) is given as an argument.\n     * <p>\n     * Subclasses can override this method to add common post-processing\n     * functionality without having to override all the write methods.\n     * The default implementation does nothing.\n     *\n     * @param n number of bytes written\n     * @throws IOException if the post-processing fails\n     * @since 2.0\n     */\n    @SuppressWarnings({\"unused\", \"RedundantThrows\"}) // Possibly thrown from subclasses.\n    protected void afterWrite(final int n) throws IOException {\n        // noop\n    }\n\n    /**\n     * Invoked by the write methods before the call is proxied. The number\n     * of bytes to be written (1 for the {@link #write(int)} method, buffer\n     * length for {@link #write(byte[])}, etc.) is given as an argument.\n     * <p>\n     * Subclasses can override this method to add common pre-processing\n     * functionality without having to override all the write methods.\n     * The default implementation does nothing.\n     *\n     * @param n number of bytes to be written\n     * @throws IOException if the pre-processing fails\n     */\n    @SuppressWarnings({\"unused\", \"RedundantThrows\"}) // Possibly thrown from subclasses.\n    protected void beforeWrite(final int n) throws IOException {\n        // noop\n    }\n\n    /**\n     * Invokes the delegate's {@code close()} method.\n     *\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void close() throws IOException {\n        try {\n            super.close();\n        } catch (Throwable t) {\n            onThrowable(t);\n        }\n    }\n\n    /**\n     * Invokes the delegate's {@code flush()} method.\n     *\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void flush() throws IOException {\n        try {\n            out.flush();\n        } catch (final Throwable t) {\n            onThrowable(t);\n        }\n    }\n\n    /**\n     * Handle any IOExceptions thrown.\n     * <p>\n     * This method provides a point to implement custom exception\n     * handling. The default behavior is to re-throw the exception.\n     *\n     * @param t The Throwable thrown\n     * @throws IOException if an I/O error occurs.\n     */\n    protected void onThrowable(final Throwable t) throws IOException {\n        if (t instanceof IOException) throw (IOException) t;\n        throw new IOException(\"IO Exception \" + t.getMessage(), t);\n    }\n\n    /**\n     * Invokes the delegate's {@code write(byte[])} method.\n     *\n     * @param bts the bytes to write\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void write(final byte[] bts) throws IOException {\n        try {\n            final int len = Bytes.length(bts);\n            beforeWrite(len);\n            out.write(bts);\n            afterWrite(len);\n        } catch (final Throwable t) {\n            onThrowable(t);\n        }\n    }\n\n    /**\n     * Invokes the delegate's {@code write(byte[])} method.\n     *\n     * @param bts the bytes to write\n     * @param st  The start offset\n     * @param end The number of bytes to write\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void write(final byte[] bts, final int st, final int end) throws IOException {\n        try {\n            beforeWrite(end);\n            out.write(bts, st, end);\n            afterWrite(end);\n        } catch (final Throwable t) {\n            onThrowable(t);\n        }\n    }\n\n    /**\n     * Invokes the delegate's {@code write(int)} method.\n     *\n     * @param idx the byte to write\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void write(final int idx) throws IOException {\n        try {\n            beforeWrite(1);\n            out.write(idx);\n            afterWrite(1);\n        } catch (final Throwable t) {\n            onThrowable(t);\n        }\n    }\n\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/JsonObjectDeserializer.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.MalformedJwtException;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.io.DeserializationException;\nimport io.jsonwebtoken.io.Deserializer;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.Reader;\nimport java.util.Map;\n\n/**\n * Function that wraps a {@link Deserializer} to add JWT-related error handling.\n *\n * @since 0.11.3 (renamed from JwtDeserializer)\n */\npublic class JsonObjectDeserializer implements Function<Reader, Map<String, ?>> {\n\n    private static final String MALFORMED_ERROR = \"Malformed %s JSON: %s\";\n    private static final String MALFORMED_COMPLEX_ERROR = \"Malformed or excessively complex %s JSON. \" +\n            \"If experienced in a production environment, this could reflect a potential malicious %s, please \" +\n            \"investigate the source further. Cause: %s\";\n\n    private final Deserializer<?> deserializer;\n    private final String name;\n\n    public JsonObjectDeserializer(Deserializer<?> deserializer, String name) {\n        this.deserializer = Assert.notNull(deserializer, \"JSON Deserializer cannot be null.\");\n        this.name = Assert.hasText(name, \"name cannot be null or empty.\");\n    }\n\n    @Override\n    public Map<String, ?> apply(Reader in) {\n        Assert.notNull(in, \"InputStream argument cannot be null.\");\n        Object value;\n        try {\n            value = this.deserializer.deserialize(in);\n            if (value == null) {\n                String msg = \"Deserialized data resulted in a null value; cannot create Map<String,?>\";\n                throw new DeserializationException(msg);\n            }\n            if (!(value instanceof Map)) {\n                String msg = \"Deserialized data is not a JSON Object; cannot create Map<String,?>\";\n                throw new DeserializationException(msg);\n            }\n            // JSON Specification requires all JSON Objects to have string-only keys.  So instead of\n            // checking that the val.keySet() has all Strings, we blindly cast to a Map<String,?>\n            // since input would rarely, if ever, have non-string keys.\n            //noinspection unchecked\n            return (Map<String, ?>) value;\n        } catch (StackOverflowError e) {\n            String msg = String.format(MALFORMED_COMPLEX_ERROR, this.name, this.name, e.getMessage());\n            throw new DeserializationException(msg, e);\n        } catch (Throwable t) {\n            throw malformed(t);\n        }\n    }\n\n    protected RuntimeException malformed(Throwable t) {\n        String msg = String.format(MALFORMED_ERROR, this.name, t.getMessage());\n        throw new MalformedJwtException(msg, t);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/NamedSerializer.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.io.AbstractSerializer;\nimport io.jsonwebtoken.io.SerializationException;\nimport io.jsonwebtoken.io.Serializer;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.OutputStream;\nimport java.util.Map;\n\npublic class NamedSerializer extends AbstractSerializer<Map<String, ?>> {\n\n    private final String name;\n    private final Serializer<Map<String, ?>> DELEGATE;\n\n    public NamedSerializer(String name, Serializer<Map<String, ?>> serializer) {\n        this.DELEGATE = Assert.notNull(serializer, \"JSON Serializer cannot be null.\");\n        this.name = Assert.hasText(name, \"Name cannot be null or empty.\");\n    }\n\n    @Override\n    protected void doSerialize(Map<String, ?> m, OutputStream out) throws SerializationException {\n        try {\n            this.DELEGATE.serialize(m, out);\n        } catch (Throwable t) {\n            String msg = String.format(\"Cannot serialize %s to JSON. Cause: %s\", this.name, t.getMessage());\n            throw new SerializationException(msg, t);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/StandardCompressionAlgorithms.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.compression.DeflateCompressionAlgorithm;\nimport io.jsonwebtoken.impl.compression.GzipCompressionAlgorithm;\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.io.CompressionAlgorithm;\nimport io.jsonwebtoken.lang.Collections;\n\n@SuppressWarnings(\"unused\") // used via reflection in io.jsonwebtoken.Jwts.ZIP\npublic final class StandardCompressionAlgorithms extends IdRegistry<CompressionAlgorithm> {\n\n    public static final String NAME = \"Compression Algorithm\";\n\n    public StandardCompressionAlgorithms() {\n        super(NAME, Collections.<CompressionAlgorithm>of(\n                new DeflateCompressionAlgorithm(),\n                new GzipCompressionAlgorithm()\n        ));\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/Streams.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.io.BufferedInputStream;\nimport java.io.ByteArrayOutputStream;\nimport java.io.Flushable;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.io.InputStreamReader;\nimport java.io.OutputStream;\nimport java.io.Reader;\nimport java.util.concurrent.Callable;\n\n/**\n * @since 0.12.0\n */\npublic class Streams {\n\n    /**\n     * Represents the end-of-file (or stream).\n     */\n    public static final int EOF = -1;\n\n    public static byte[] bytes(final InputStream in, String exmsg) {\n        if (in instanceof BytesInputStream) {\n            return ((BytesInputStream) in).getBytes();\n        }\n        // otherwise we have to copy over:\n        ByteArrayOutputStream out = new ByteArrayOutputStream(8192);\n        copy(in, out, new byte[8192], exmsg);\n        return out.toByteArray();\n    }\n\n    public static InputStream of(byte[] bytes) {\n        return new BytesInputStream(bytes);\n    }\n\n    public static InputStream of(CharSequence seq) {\n        return of(Strings.utf8(seq));\n    }\n\n    public static Reader reader(byte[] bytes) {\n        return reader(Streams.of(bytes));\n    }\n\n    public static Reader reader(InputStream in) {\n        return new InputStreamReader(in, Strings.UTF_8);\n    }\n\n    public static Reader reader(CharSequence seq) {\n        return new CharSequenceReader(seq);\n    }\n\n    public static void flush(Flushable... flushables) {\n        Objects.nullSafeFlush(flushables);\n    }\n\n    /**\n     * Copies bytes from a {@link InputStream} to an {@link OutputStream} using the specified {@code buffer}, avoiding\n     * the need for a {@link BufferedInputStream}.\n     *\n     * @param inputStream  the {@link InputStream} to read.\n     * @param outputStream the {@link OutputStream} to write.\n     * @param buffer       the buffer to use for the copy\n     * @return the number of bytes copied.\n     * @throws IllegalArgumentException if the InputStream is {@code null}.\n     * @throws IllegalArgumentException if the OutputStream is {@code null}.\n     * @throws IOException              if an I/O error occurs.\n     */\n    public static long copy(final InputStream inputStream, final OutputStream outputStream, final byte[] buffer)\n            throws IOException {\n        Assert.notNull(inputStream, \"inputStream cannot be null.\");\n        Assert.notNull(outputStream, \"outputStream cannot be null.\");\n        Assert.notEmpty(buffer, \"buffer cannot be null or empty.\");\n        long count = 0;\n        int n = 0;\n        while (n != EOF) {\n            n = inputStream.read(buffer);\n            if (n > 0) outputStream.write(buffer, 0, n);\n            count += n;\n        }\n        return count;\n    }\n\n    public static long copy(final InputStream in, final OutputStream out, final byte[] buffer, final String exmsg) {\n        return run(new Callable<Long>() {\n            @Override\n            public Long call() throws IOException {\n                try {\n                    reset(in);\n                    return copy(in, out, buffer);\n                } finally {\n                    Objects.nullSafeFlush(out);\n                    reset(in);\n                }\n            }\n        }, exmsg);\n    }\n\n    public static void reset(final InputStream in) {\n        if (in == null) return;\n        Callable<Object> callable = new Callable<Object>() {\n            @Override\n            public Object call() {\n                try {\n                    in.reset();\n                } catch (Throwable ignored) {\n                }\n                return null;\n            }\n        };\n        try {\n            callable.call();\n        } catch (Throwable ignored) {\n        }\n    }\n\n    public static void write(final OutputStream out, final byte[] bytes, String exMsg) {\n        write(out, bytes, 0, Bytes.length(bytes), exMsg);\n    }\n\n    public static void write(final OutputStream out, final byte[] data, final int offset, final int len, String exMsg) {\n        if (out == null || Bytes.isEmpty(data) || len <= 0) return;\n        run(new Callable<Object>() {\n            @Override\n            public Object call() throws Exception {\n                out.write(data, offset, len);\n                return null;\n            }\n        }, exMsg);\n    }\n\n    public static void writeAndClose(final OutputStream out, final byte[] data, String exMsg) {\n        try {\n            write(out, data, exMsg);\n        } finally {\n            Objects.nullSafeClose(out);\n        }\n    }\n\n    public static <V> V run(Callable<V> c, String ioExMsg) {\n        Assert.hasText(ioExMsg, \"IO Exception Message cannot be null or empty.\");\n        try {\n            return c.call();\n        } catch (Throwable t) {\n            String msg = \"IO failure: \" + ioExMsg;\n            if (!msg.endsWith(\".\")) {\n                msg += \".\";\n            }\n            msg += \" Cause: \" + t.getMessage();\n            throw new io.jsonwebtoken.io.IOException(msg, t);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/TeeOutputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.io.IOException;\nimport java.io.OutputStream;\n\n/**\n * @since 0.12.0\n */\npublic class TeeOutputStream extends FilteredOutputStream {\n\n    private final OutputStream other;\n\n    public TeeOutputStream(OutputStream one, OutputStream two) {\n        super(one);\n        this.other = Assert.notNull(two, \"Second OutputStream cannot be null.\");\n    }\n\n    @Override\n    public void close() throws IOException {\n        try {\n            super.close();\n        } finally {\n            this.other.close();\n        }\n    }\n\n    @Override\n    public void flush() throws IOException {\n        super.flush();\n        this.other.flush();\n    }\n\n    @Override\n    public void write(byte[] bts) throws IOException {\n        super.write(bts);\n        this.other.write(bts);\n    }\n\n    @Override\n    public void write(byte[] bts, int st, int end) throws IOException {\n        super.write(bts, st, end);\n        this.other.write(bts, st, end);\n    }\n\n    @Override\n    public void write(int idx) throws IOException {\n        super.write(idx);\n        this.other.write(idx);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/io/UncloseableInputStream.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io;\n\nimport java.io.FilterInputStream;\nimport java.io.InputStream;\n\n/**\n * @since 0.12.0, copied from\n * <a href=\"https://github.com/apache/commons-io/blob/3a17f5259b105e734c8adce1d06d68f29884d1bb/src/main/java/org/apache/commons/io/input/CloseShieldInputStream.java\">\n * commons-io 3a17f5259b105e734c8adce1d06d68f29884d1bb</a>\n */\npublic final class UncloseableInputStream extends FilterInputStream {\n\n    public UncloseableInputStream(InputStream in) {\n        super(in);\n    }\n\n    @Override\n    public void close() {\n        in = ClosedInputStream.INSTANCE;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/BiConsumer.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\npublic interface BiConsumer<T, U> {\n\n    void accept(T t, U u);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/BigIntegerUBytesConverter.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.math.BigInteger;\n\npublic class BigIntegerUBytesConverter implements Converter<BigInteger, byte[]> {\n\n    private static final String NEGATIVE_MSG =\n        \"JWA Base64urlUInt values MUST be >= 0 (non-negative) per the 'Base64urlUInt' definition in \" +\n            \"[JWA RFC 7518, Section 2](https://www.rfc-editor.org/rfc/rfc7518.html#section-2)\";\n\n    @Override\n    public byte[] applyTo(BigInteger bigInt) {\n        Assert.notNull(bigInt, \"BigInteger argument cannot be null.\");\n        if (BigInteger.ZERO.compareTo(bigInt) > 0) {\n            throw new IllegalArgumentException(NEGATIVE_MSG);\n        }\n\n        final int bitLen = bigInt.bitLength();\n        final byte[] bytes = bigInt.toByteArray();\n        // Determine minimal number of bytes necessary to represent an unsigned byte array.\n        // It must be 1 or more because zero still requires one byte\n        final int unsignedByteLen = Math.max(1, Bytes.length(bitLen)); // always need at least one byte\n\n        if (bytes.length == unsignedByteLen) { // already in the form we need\n            return bytes;\n        }\n        //otherwise, we need to strip the sign byte (start copying at index 1 instead of 0):\n        byte[] ubytes = new byte[unsignedByteLen];\n        System.arraycopy(bytes, 1, ubytes, 0, unsignedByteLen);\n        return ubytes;\n    }\n\n    @Override\n    public BigInteger applyFrom(byte[] bytes) {\n        Assert.notEmpty(bytes, \"Byte array cannot be null or empty.\");\n        return new BigInteger(1, bytes);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Bytes.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.impl.security.Randoms;\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Assert;\n\npublic final class Bytes {\n\n    public static final byte[] EMPTY = new byte[0];\n\n    private static final int LONG_BYTE_LENGTH = Long.SIZE / Byte.SIZE;\n    private static final int INT_BYTE_LENGTH = Integer.SIZE / Byte.SIZE;\n    public static final String LONG_REQD_MSG = \"Long byte arrays must be \" + LONG_BYTE_LENGTH + \" bytes in length.\";\n    public static final String INT_REQD_MSG = \"Integer byte arrays must be \" + INT_BYTE_LENGTH + \" bytes in length.\";\n\n    //prevent instantiation\n    private Bytes() {\n    }\n\n    public static byte[] nullSafe(byte[] bytes) {\n        return bytes != null ? bytes : Bytes.EMPTY;\n    }\n\n    public static byte[] randomBits(int numBits) {\n        return random(numBits / Byte.SIZE);\n    }\n\n    public static byte[] random(int numBytes) {\n        if (numBytes <= 0) {\n            throw new IllegalArgumentException(\"numBytes argument must be >= 0\");\n        }\n        byte[] bytes = new byte[numBytes];\n        Randoms.secureRandom().nextBytes(bytes);\n        return bytes;\n    }\n\n    public static byte[] toBytes(int i) {\n        return new byte[]{\n                (byte) (i >>> 24),\n                (byte) (i >>> 16),\n                (byte) (i >>> 8),\n                (byte) i\n        };\n    }\n\n    public static byte[] toBytes(long l) {\n        return new byte[]{\n                (byte) (l >>> 56),\n                (byte) (l >>> 48),\n                (byte) (l >>> 40),\n                (byte) (l >>> 32),\n                (byte) (l >>> 24),\n                (byte) (l >>> 16),\n                (byte) (l >>> 8),\n                (byte) l\n        };\n    }\n\n    public static long toLong(byte[] bytes) {\n        Assert.isTrue(Arrays.length(bytes) == LONG_BYTE_LENGTH, LONG_REQD_MSG);\n        return ((bytes[0] & 0xFFL) << 56) |\n                ((bytes[1] & 0xFFL) << 48) |\n                ((bytes[2] & 0xFFL) << 40) |\n                ((bytes[3] & 0xFFL) << 32) |\n                ((bytes[4] & 0xFFL) << 24) |\n                ((bytes[5] & 0xFFL) << 16) |\n                ((bytes[6] & 0xFFL) << 8) |\n                (bytes[7] & 0xFFL);\n    }\n\n    public static int toInt(byte[] bytes) {\n        Assert.isTrue(Arrays.length(bytes) == INT_BYTE_LENGTH, INT_REQD_MSG);\n        return ((bytes[0] & 0xFF) << 24) |\n                ((bytes[1] & 0xFF) << 16) |\n                ((bytes[2] & 0xFF) << 8) |\n                (bytes[3] & 0xFF);\n    }\n\n    public static int indexOf(byte[] source, byte[] target) {\n        return indexOf(source, target, 0);\n    }\n\n    public static int indexOf(byte[] source, byte[] target, int fromIndex) {\n        return indexOf(source, 0, length(source), target, 0, length(target), fromIndex);\n    }\n\n\n    static int indexOf(byte[] source, int srcOffset, int srcLen,\n                       byte[] target, int targetOffset, int targetLen,\n                       int fromIndex) {\n\n        if (fromIndex >= srcLen) {\n            return (targetLen == 0 ? srcLen : -1);\n        }\n        if (fromIndex < 0) {\n            fromIndex = 0;\n        }\n        if (targetLen == 0) {\n            return fromIndex;\n        }\n\n        byte first = target[targetOffset];\n        int max = srcOffset + (srcLen - targetLen);\n\n        for (int i = srcOffset + fromIndex; i <= max; i++) { //\n\n            if (source[i] != first) { // continue on to find the first matching byte\n                //noinspection StatementWithEmptyBody\n                while (++i <= max && source[i] != first) ;\n            }\n\n            if (i <= max) { // found first byte in target, now try to find the rest:\n                int j = i + 1;\n                int end = j + targetLen - 1;\n                //noinspection StatementWithEmptyBody\n                for (int k = targetOffset + 1; j < end && source[j] == target[k]; j++, k++) ;\n                if (j == end) {\n                    return i - srcOffset; // found entire target byte array\n                }\n            }\n        }\n        return -1;\n    }\n\n    public static boolean startsWith(byte[] src, byte[] prefix) {\n        return startsWith(src, prefix, 0);\n    }\n\n    public static boolean startsWith(byte[] src, byte[] prefix, int offset) {\n        int to = offset;\n        int po = 0;\n        int pc = length(prefix);\n        if ((offset < 0) || (offset > length(src) - pc)) {\n            return false;\n        }\n        while (--pc >= 0) {\n            if (src[to++] != prefix[po++]) {\n                return false;\n            }\n        }\n        return true;\n    }\n\n    public static boolean endsWith(byte[] src, byte[] suffix) {\n        return startsWith(src, suffix, length(src) - length(suffix));\n    }\n\n    public static byte[] concat(byte[]... arrays) {\n        int len = 0;\n        int numArrays = Arrays.length(arrays);\n        for (int i = 0; i < numArrays; i++) {\n            len += length(arrays[i]);\n        }\n        byte[] output = new byte[len];\n        int position = 0;\n        if (len > 0) {\n            for (byte[] array : arrays) {\n                int alen = length(array);\n                if (alen > 0) {\n                    System.arraycopy(array, 0, output, position, alen);\n                    position += alen;\n                }\n            }\n        }\n        return output;\n    }\n\n    /**\n     * Clears the array by filling it with all zeros. Does nothing with a null or empty argument.\n     *\n     * @param bytes the (possibly null or empty) byte array to clear\n     */\n    public static void clear(byte[] bytes) {\n        if (isEmpty(bytes)) return;\n        java.util.Arrays.fill(bytes, (byte) 0);\n    }\n\n    public static boolean isEmpty(byte[] bytes) {\n        return length(bytes) == 0;\n    }\n\n    public static int length(byte[] bytes) {\n        return bytes == null ? 0 : bytes.length;\n    }\n\n    public static long bitLength(byte[] bytes) {\n        return length(bytes) * (long) Byte.SIZE;\n    }\n\n    /**\n     * Returns the minimum number of bytes required to represent the specified number of bits.\n     *\n     * <p>This is defined/used by many specifications, such as:</p>\n     * <ul>\n     *     <li><a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-2\">JWA RFC 7518, Section 2</a>'s\n     *     <code>Base64urlUInt</code> definition</li>\n     *     <li>Elliptic Curve <code>Integer-to-OctetString</code> conversion defined by Section 2.3.7 of the\n     *     <a href=\"http://www.secg.org/sec1-v2.pdf\">Standards for Efficient Cryptography Group,\n     *     &qupt;SEC 1: Elliptic Curve Cryptography&quot; Version 2.0, May 2009</a> (as required by\n     *     <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4\">RFC 7518, Section 3.4</a>)</li>\n     *     <li>and others.</li>\n     * </ul>\n     *\n     * @param bitLength the number of bits to represent as a byte array, must be >= 0\n     * @return the minimum number of bytes required to represent the specified number of bits.\n     * @throws IllegalArgumentException if {@code bitLength} is less than zero.\n     */\n    public static int length(int bitLength) {\n        if (bitLength < 0) throw new IllegalArgumentException(\"bitLength argument must be >= 0\");\n        return (bitLength + 7) / Byte.SIZE;\n    }\n\n    public static String bitsMsg(long bitLength) {\n        return bitLength + \" bits (\" + bitLength / Byte.SIZE + \" bytes)\";\n    }\n\n    public static String bytesMsg(int byteArrayLength) {\n        return bitsMsg((long) byteArrayLength * Byte.SIZE);\n    }\n\n    public static void increment(byte[] a) {\n        for (int i = a.length - 1; i >= 0; --i) {\n            if (++a[i] != 0) {\n                break;\n            }\n        }\n    }\n\n    /**\n     * Pads the front of the specified byte array with zeros if necessary, returning a new padded result, or the\n     * original array unmodified if padding isn't necessary.  Padding is only performed if {@code length} is greater\n     * than {@code bytes.length}.\n     *\n     * @param bytes  the byte array to pre-pad with zeros if necessary\n     * @param length the length of the required output array\n     * @return the potentially pre-padded byte array, or the existing {@code bytes} array if padding wasn't necessary.\n     * @since 0.12.4\n     */\n    public static byte[] prepad(byte[] bytes, int length) {\n        Assert.notNull(bytes, \"byte array cannot be null.\");\n        Assert.gt(length, 0, \"length must be positive (> 0).\");\n        if (bytes.length < length) { // need to pad with leading zero(es):\n            byte[] padded = new byte[length];\n            System.arraycopy(bytes, 0, padded, length - bytes.length, bytes.length);\n            bytes = padded;\n        }\n        return bytes;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/CheckedFunction.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\npublic interface CheckedFunction<T, R> {\n    R apply(T t) throws Exception;\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/CheckedSupplier.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\n/**\n * @since 0.12.0\n */\npublic interface CheckedSupplier<T> {\n\n    T get() throws Exception;\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/CollectionConverter.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\n\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Set;\n\nclass CollectionConverter<T, C extends Collection<T>> implements Converter<C, Object> {\n\n    private final Converter<T, Object> elementConverter;\n    private final Function<Integer, C> fn;\n\n    public static <T> CollectionConverter<T, List<T>> forList(Converter<T,Object> elementConverter) {\n        return new CollectionConverter<>(elementConverter, new CreateListFunction<T>());\n    }\n\n    public static <T> CollectionConverter<T, Set<T>> forSet(Converter<T, Object> elementConverter) {\n        return new CollectionConverter<>(elementConverter, new CreateSetFunction<T>());\n    }\n\n    public CollectionConverter(Converter<T, Object> elementConverter, Function<Integer, C> fn) {\n        this.elementConverter = Assert.notNull(elementConverter, \"Element converter cannot be null.\");\n        this.fn = Assert.notNull(fn, \"Collection function cannot be null.\");\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    @Override\n    public Object applyTo(C ts) {\n        if (Collections.isEmpty(ts)) {\n            return ts;\n        }\n        Collection c = fn.apply(ts.size());\n        for (T element : ts) {\n            Object encoded = elementConverter.applyTo(element);\n            c.add(encoded);\n        }\n        return c;\n    }\n\n    private C toElementList(Collection<?> c) {\n        Assert.notEmpty(c, \"Collection cannot be null or empty.\");\n        C result = fn.apply(c.size());\n        for (Object o : c) {\n            T element = elementConverter.applyFrom(o);\n            result.add(element);\n        }\n        return result;\n    }\n\n    @Override\n    public C applyFrom(Object value) {\n        if (value == null) {\n            return null;\n        }\n        Collection<?> c;\n        if (value.getClass().isArray() && !value.getClass().getComponentType().isPrimitive()) {\n            c = Collections.arrayToList(value);\n        } else if (value instanceof Collection) {\n            c = (Collection<?>) value;\n        } else {\n            c = java.util.Collections.singletonList(value);\n        }\n        C result;\n        if (Collections.isEmpty(c)) {\n            result = fn.apply(0);\n        } else {\n            result = toElementList(c);\n        }\n        return result;\n    }\n\n    private static class CreateListFunction<A> implements Function<Integer, List<A>> {\n        @Override\n        public List<A> apply(Integer size) {\n            return size > 0 ? new ArrayList<A>(size) : new ArrayList<A>();\n        }\n    }\n\n    private static class CreateSetFunction<T> implements Function<Integer, Set<T>> {\n        @Override\n        public Set<T> apply(Integer size) {\n            return size > 0 ? new LinkedHashSet<T>(size) : new LinkedHashSet<T>();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/CompactMediaTypeIdConverter.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\n\npublic final class CompactMediaTypeIdConverter implements Converter<String, Object> {\n\n    public static final Converter<String, Object> INSTANCE = new CompactMediaTypeIdConverter();\n\n    private static final char FORWARD_SLASH = '/';\n\n    private static final String APP_MEDIA_TYPE_PREFIX = \"application\" + FORWARD_SLASH;\n\n    static String compactIfPossible(String cty) {\n        Assert.hasText(cty, \"Value cannot be null or empty.\");\n        if (Strings.startsWithIgnoreCase(cty, APP_MEDIA_TYPE_PREFIX)) {\n            // per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10\n            // we can only use the compact form if no other '/' exists in the string\n            for (int i = cty.length() - 1; i >= APP_MEDIA_TYPE_PREFIX.length(); i--) {\n                char c = cty.charAt(i);\n                if (c == FORWARD_SLASH) {\n                    return cty; // found another '/', can't compact, so just return unmodified\n                }\n            }\n            // no additional '/' found, we can strip the prefix:\n            return cty.substring(APP_MEDIA_TYPE_PREFIX.length());\n        }\n        return cty; // didn't start with 'application/', so we can't trim it - just return unmodified\n    }\n\n    @Override\n    public Object applyTo(String s) {\n        return compactIfPossible(s);\n    }\n\n    @Override\n    public String applyFrom(Object o) {\n        Assert.notNull(o, \"Value cannot be null.\");\n        String s = Assert.isInstanceOf(String.class, o, \"Value must be a string.\");\n\n        // https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10:\n        //\n        //     A recipient using the media type value MUST treat it as if\n        //     \"application/\" were prepended to any \"cty\" value not containing a\n        //     '/'.\n        //\n        if (s.indexOf(FORWARD_SLASH) < 0) {\n            s = APP_MEDIA_TYPE_PREFIX + s;\n        }\n\n        return s;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/CompoundConverter.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\npublic class CompoundConverter<A, B, C> implements Converter<A, C> {\n\n    private final Converter<A, B> first;\n    private final Converter<B, C> second;\n\n    public CompoundConverter(Converter<A, B> first, Converter<B, C> second) {\n        this.first = Assert.notNull(first, \"First converter cannot be null.\");\n        this.second = Assert.notNull(second, \"Second converter cannot be null.\");\n    }\n\n    @Override\n    public C applyTo(A a) {\n        B b = first.applyTo(a);\n        return second.applyTo(b);\n    }\n\n    @Override\n    public A applyFrom(C c) {\n        B b = second.applyFrom(c);\n        return first.applyFrom(b);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/ConstantFunction.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\n\n/**\n * Function that always returns the same value\n *\n * @param <T> Input type\n * @param <R> Return value type\n */\npublic final class ConstantFunction<T, R> implements Function<T, R> {\n\n    private final R value;\n\n    public ConstantFunction(R value) {\n        this.value = value;\n    }\n\n    @Override\n    public R apply(T t) {\n        return this.value;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Converter.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\npublic interface Converter<A, B> {\n\n    /**\n     * Converts the specified (Java idiomatic type) value to the canonical RFC-required data type.\n     *\n     * @param a the preferred idiomatic value\n     * @return the canonical RFC-required data type value.\n     */\n    B applyTo(A a);\n\n    /**\n     * Converts the specified canonical (RFC-compliant data type) value to the preferred Java idiomatic type.\n     *\n     * @param b the canonical value to convert\n     * @return the preferred Java idiomatic type value.\n     */\n    A applyFrom(B b);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Converters.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.impl.io.Codec;\nimport io.jsonwebtoken.impl.security.JwtX509StringConverter;\n\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\nimport java.util.Set;\n\npublic final class Converters {\n\n    public static final Converter<URI, Object> URI = Converters.forEncoded(URI.class, new UriStringConverter());\n\n    public static final Converter<byte[], Object> BASE64URL_BYTES = Converters.forEncoded(byte[].class, Codec.BASE64URL);\n\n    public static final Converter<X509Certificate, Object> X509_CERTIFICATE =\n            Converters.forEncoded(X509Certificate.class, JwtX509StringConverter.INSTANCE);\n\n    public static final Converter<BigInteger, byte[]> BIGINT_UBYTES = new BigIntegerUBytesConverter();\n    public static final Converter<BigInteger, Object> BIGINT = Converters.forEncoded(BigInteger.class,\n            compound(BIGINT_UBYTES, Codec.BASE64URL));\n\n    //prevent instantiation\n    private Converters() {\n    }\n\n    public static <T> Converter<T, Object> forType(Class<T> clazz) {\n        return new RequiredTypeConverter<>(clazz);\n    }\n\n    public static <T> Converter<Set<T>, Object> forSet(Converter<T, Object> elementConverter) {\n        return CollectionConverter.forSet(elementConverter);\n    }\n\n    public static <T> Converter<List<T>, Object> forList(Converter<T, Object> elementConverter) {\n        return CollectionConverter.forList(elementConverter);\n    }\n\n    public static <T> Converter<T, Object> forEncoded(Class<T> elementType, Converter<T, CharSequence> elementConverter) {\n        return new EncodedObjectConverter<>(elementType, elementConverter);\n    }\n\n    public static <A, B, C> Converter<A, C> compound(final Converter<A, B> aConv, final Converter<B, C> bConv) {\n        return new CompoundConverter<>(aConv, bConv);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DefaultCollectionMutator.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.CollectionMutator;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Objects;\n\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\n\npublic class DefaultCollectionMutator<E, M extends CollectionMutator<E, M>> implements CollectionMutator<E, M> {\n\n    private final Collection<E> collection;\n\n    public DefaultCollectionMutator(Collection<? extends E> seed) {\n        this.collection = new LinkedHashSet<>(Collections.nullSafe(seed));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected final M self() {\n        return (M) this;\n    }\n\n    private boolean doAdd(E e) {\n        if (Objects.isEmpty(e)) return false;\n        return this.collection.add(e);\n    }\n\n    @Override\n    public M add(E e) {\n        if (doAdd(e)) changed();\n        return self();\n    }\n\n    @Override\n    public M remove(E e) {\n        if (this.collection.remove(e)) changed();\n        return self();\n    }\n\n    @Override\n    public M add(Collection<? extends E> c) {\n        boolean changed = false;\n        for (E element : Collections.nullSafe(c)) {\n            changed = doAdd(element) || changed;\n        }\n        if (changed) changed();\n        return self();\n    }\n\n    @Override\n    public M clear() {\n        boolean changed = !Collections.isEmpty(this.collection);\n        this.collection.clear();\n        if (changed) changed();\n        return self();\n    }\n\n    /**\n     * Callback for subclasses that wish to be notified if the internal collection has changed via builder mutation\n     * methods.\n     */\n    protected void changed() {\n    }\n\n    protected Collection<E> getCollection() {\n        return Collections.immutable(this.collection);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DefaultNestedCollection.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.NestedCollection;\n\nimport java.util.Collection;\n\npublic class DefaultNestedCollection<E, P> extends DefaultCollectionMutator<E, NestedCollection<E, P>>\n        implements NestedCollection<E, P> {\n\n    private final P parent;\n\n    public DefaultNestedCollection(P parent, Collection<? extends E> seed) {\n        super(seed);\n        this.parent = Assert.notNull(parent, \"Parent cannot be null.\");\n    }\n\n    @Override\n    public P and() {\n        return this.parent;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DefaultParameter.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Collection;\n\npublic class DefaultParameter<T> implements Parameter<T> {\n\n    private final String ID;\n    private final String NAME;\n    private final boolean SECRET;\n    private final Class<T> IDIOMATIC_TYPE; // data type, or if collection, element type\n    private final Class<? extends Collection<T>> COLLECTION_TYPE; // null if param doesn't represent collection\n    private final Converter<T, Object> CONVERTER;\n\n    public DefaultParameter(String id, String name, boolean secret,\n                            Class<T> idiomaticType,\n                            Class<? extends Collection<T>> collectionType,\n                            Converter<T, Object> converter) {\n        this.ID = Strings.clean(Assert.hasText(id, \"ID argument cannot be null or empty.\"));\n        this.NAME = Strings.clean(Assert.hasText(name, \"Name argument cannot be null or empty.\"));\n        this.IDIOMATIC_TYPE = Assert.notNull(idiomaticType, \"idiomaticType argument cannot be null.\");\n        this.CONVERTER = Assert.notNull(converter, \"Converter argument cannot be null.\");\n        this.SECRET = secret;\n        this.COLLECTION_TYPE = collectionType; // can be null if parameter isn't a collection\n    }\n\n    @Override\n    public String getId() {\n        return this.ID;\n    }\n\n    @Override\n    public String getName() {\n        return this.NAME;\n    }\n\n    @Override\n    public boolean supports(Object value) {\n        if (value == null) {\n            return true;\n        }\n        if (COLLECTION_TYPE != null && COLLECTION_TYPE.isInstance(value)) {\n            Collection<? extends T> c = COLLECTION_TYPE.cast(value);\n            return c.isEmpty() || IDIOMATIC_TYPE.isInstance(c.iterator().next());\n        }\n        return IDIOMATIC_TYPE.isInstance(value);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public T cast(Object value) {\n        if (value != null) {\n            if (COLLECTION_TYPE != null) { // parameter represents a collection, ensure it and its elements are the expected type:\n                if (!COLLECTION_TYPE.isInstance(value)) {\n                    String msg = \"Cannot cast \" + value.getClass().getName() + \" to \" +\n                            COLLECTION_TYPE.getName() + \"<\" + IDIOMATIC_TYPE.getName() + \">\";\n                    throw new ClassCastException(msg);\n                }\n                Collection<?> c = COLLECTION_TYPE.cast(value);\n                if (!c.isEmpty()) {\n                    Object element = c.iterator().next();\n                    if (!IDIOMATIC_TYPE.isInstance(element)) {\n                        String msg = \"Cannot cast \" + value.getClass().getName() + \" to \" +\n                                COLLECTION_TYPE.getName() + \"<\" + IDIOMATIC_TYPE.getName() + \">: At least one \" +\n                                \"element is not an instance of \" + IDIOMATIC_TYPE.getName();\n                        throw new ClassCastException(msg);\n                    }\n                }\n            } else if (!IDIOMATIC_TYPE.isInstance(value)) {\n                String msg = \"Cannot cast \" + value.getClass().getName() + \" to \" + IDIOMATIC_TYPE.getName();\n                throw new ClassCastException(msg);\n            }\n        }\n        return (T) value;\n    }\n\n    @Override\n    public boolean isSecret() {\n        return SECRET;\n    }\n\n    @Override\n    public int hashCode() {\n        return this.ID.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj instanceof Parameter) {\n            return this.ID.equals(((Parameter<?>) obj).getId());\n        }\n        return false;\n    }\n\n    @Override\n    public String toString() {\n        return \"'\" + this.ID + \"' (\" + this.NAME + \")\";\n    }\n\n    @Override\n    public Object applyTo(T t) {\n        return CONVERTER.applyTo(t);\n    }\n\n    @Override\n    public T applyFrom(Object o) {\n        return CONVERTER.applyFrom(o);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DefaultParameterBuilder.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.util.Collection;\nimport java.util.List;\nimport java.util.Set;\n\npublic class DefaultParameterBuilder<T> implements ParameterBuilder<T> {\n\n    private String id;\n    private String name;\n    private boolean secret;\n    private final Class<T> type;\n    private Converter<T, ?> converter;\n    private Class<? extends Collection<T>> collectionType; // will be null if parameter doesn't represent a collection (list or set)\n\n    public DefaultParameterBuilder(Class<T> type) {\n        this.type = Assert.notNull(type, \"Type cannot be null.\");\n    }\n\n    @Override\n    public ParameterBuilder<T> setId(String id) {\n        this.id = id;\n        return this;\n    }\n\n    @Override\n    public ParameterBuilder<T> setName(String name) {\n        this.name = name;\n        return this;\n    }\n\n    @Override\n    public ParameterBuilder<T> setSecret(boolean secret) {\n        this.secret = secret;\n        return this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public ParameterBuilder<List<T>> list() {\n        Class<?> clazz = List.class;\n        this.collectionType = (Class<? extends Collection<T>>) clazz;\n        return (ParameterBuilder<List<T>>) this;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public ParameterBuilder<Set<T>> set() {\n        Class<?> clazz = Set.class;\n        this.collectionType = (Class<? extends Collection<T>>) clazz;\n        return (ParameterBuilder<Set<T>>) this;\n    }\n\n    @Override\n    public ParameterBuilder<T> setConverter(Converter<T, ?> converter) {\n        this.converter = converter;\n        return this;\n    }\n\n    @SuppressWarnings({\"rawtypes\", \"unchecked\"})\n    @Override\n    public Parameter<T> build() {\n        Assert.notNull(this.type, \"Type must be set.\");\n        Converter conv = this.converter;\n        if (conv == null) {\n            conv = Converters.forType(this.type);\n        }\n        if (this.collectionType != null) {\n            conv = List.class.isAssignableFrom(collectionType) ? Converters.forList(conv) : Converters.forSet(conv);\n        }\n        if (this.secret) {\n            conv = new RedactedValueConverter(conv);\n        }\n        return new DefaultParameter<>(this.id, this.name, this.secret, this.type, this.collectionType, conv);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DefaultRegistry.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\npublic class DefaultRegistry<K, V> extends DelegatingMap<K, V, Map<K, V>> implements Registry<K, V>, Function<K, V> {\n\n    private final String qualifiedKeyName;\n\n    private static <K, V> Map<K, V> toMap(Collection<? extends V> values, Function<V, K> keyFn) {\n        Assert.notNull(values, \"Collection of values may not be null.\");\n        Assert.notNull(keyFn, \"Key function cannot be null.\");\n        Map<K, V> m = new LinkedHashMap<>(Collections.size(values));\n        for (V value : values) {\n            K key = Assert.notNull(keyFn.apply(value), \"Key function cannot return a null value.\");\n            m.put(key, value);\n        }\n        return Collections.immutable(m);\n    }\n\n    public DefaultRegistry(String name, String keyName, Collection<? extends V> values, Function<V, K> keyFn) {\n        super(toMap(values, keyFn));\n        name = Assert.hasText(Strings.clean(name), \"name cannot be null or empty.\");\n        keyName = Assert.hasText(Strings.clean(keyName), \"keyName cannot be null or empty.\");\n        this.qualifiedKeyName = name + \" \" + keyName;\n    }\n\n    @Override\n    public V apply(K k) {\n        return get(k);\n    }\n\n    @Override\n    public V forKey(K key) {\n        V value = get(key);\n        if (value == null) {\n            String msg = \"Unrecognized \" + this.qualifiedKeyName + \": \" + key;\n            throw new IllegalArgumentException(msg);\n        }\n        return value;\n    }\n\n    static <T> T immutable() {\n        throw new UnsupportedOperationException(\"Registries are immutable and cannot be modified.\");\n    }\n\n    @Override\n    public V put(K key, V value) {\n        return immutable();\n    }\n\n    @Override\n    public V remove(Object key) {\n        return immutable();\n    }\n\n    @Override\n    public void putAll(Map<? extends K, ? extends V> m) {\n        immutable();\n    }\n\n    @Override\n    public void clear() {\n        immutable();\n    }\n\n    @Override\n    public int hashCode() {\n        return DELEGATE.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) return true;\n        if (obj instanceof DefaultRegistry) {\n            DefaultRegistry<?, ?> other = (DefaultRegistry<?, ?>) obj;\n            return this.qualifiedKeyName.equals(other.qualifiedKeyName) &&\n                    this.DELEGATE.equals(other.DELEGATE);\n        }\n        return false;\n    }\n\n    @Override\n    public String toString() {\n        return DELEGATE.toString();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DelegatingCheckedFunction.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\npublic class DelegatingCheckedFunction<T, R> implements CheckedFunction<T, R> {\n\n    final Function<T, R> delegate;\n\n    public DelegatingCheckedFunction(Function<T, R> delegate) {\n        this.delegate = delegate;\n    }\n\n    @Override\n    public R apply(T t) throws Exception {\n        return delegate.apply(t);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DelegatingMap.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * A {@code Map} implementation that delegates all calls to an internal Map instance.\n *\n * @param <K> Map key type\n * @param <V> Map value type\n * @since 0.12.0\n */\npublic class DelegatingMap<K, V, T extends Map<K, V>> implements Map<K, V> {\n\n    protected T DELEGATE;\n\n    /**\n     * Initializes the instance with specified non-null backing delegate Map.\n     *\n     * @param delegate non-null delegate map to use for all map method implementations\n     * @throws IllegalArgumentException if {@code delegate} is null.\n     */\n    protected DelegatingMap(T delegate) {\n        setDelegate(delegate);\n    }\n\n    protected void setDelegate(T delegate) {\n        this.DELEGATE = Assert.notNull(delegate, \"Delegate cannot be null.\");\n    }\n\n    @Override\n    public int size() {\n        return DELEGATE.size();\n    }\n\n    @Override\n    public Collection<V> values() {\n        return DELEGATE.values();\n    }\n\n    @Override\n    public V get(Object id) {\n        return DELEGATE.get(id);\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return DELEGATE.isEmpty();\n    }\n\n    @Override\n    public boolean containsKey(Object key) {\n        return DELEGATE.containsKey(key);\n    }\n\n    @Override\n    public boolean containsValue(Object value) {\n        return DELEGATE.containsValue(value);\n    }\n\n    @Override\n    public V put(K key, V value) {\n        return DELEGATE.put(key, value);\n    }\n\n    @Override\n    public V remove(Object key) {\n        return DELEGATE.remove(key);\n    }\n\n    @Override\n    public void putAll(Map<? extends K, ? extends V> m) {\n        DELEGATE.putAll(m);\n    }\n\n    @Override\n    public void clear() {\n        DELEGATE.clear();\n    }\n\n    @Override\n    public Set<K> keySet() {\n        return DELEGATE.keySet();\n    }\n\n    @Override\n    public Set<Entry<K, V>> entrySet() {\n        return DELEGATE.entrySet();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/DelegatingMapMutator.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.MapMutator;\n\nimport java.util.Map;\n\n/**\n * @since 0.12.0\n */\npublic class DelegatingMapMutator<K, V, D extends Map<K, V>, T extends MapMutator<K, V, T>>\n        extends DelegatingMap<K, V, D> implements MapMutator<K, V, T> {\n\n    protected DelegatingMapMutator(D delegate) {\n        super(delegate);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected final T self() {\n        return (T) this;\n    }\n\n    @Override\n    public T empty() {\n        clear();\n        return self();\n    }\n\n    @Override\n    public T add(K key, V value) {\n        put(key, value);\n        return self();\n    }\n\n    @Override\n    public T add(Map<? extends K, ? extends V> m) {\n        putAll(m);\n        return self();\n    }\n\n    @Override\n    public T delete(K key) {\n        remove(key);\n        return self();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/EncodedObjectConverter.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\npublic class EncodedObjectConverter<T> implements Converter<T, Object> {\n\n    private final Class<T> type;\n    private final Converter<T, ? super CharSequence> converter;\n\n    public EncodedObjectConverter(Class<T> type, Converter<T, ? super CharSequence> converter) {\n        this.type = Assert.notNull(type, \"Value type cannot be null.\");\n        this.converter = Assert.notNull(converter, \"Value converter cannot be null.\");\n    }\n\n    @Override\n    public Object applyTo(T t) {\n        Assert.notNull(t, \"Value argument cannot be null.\");\n        return converter.applyTo(t);\n    }\n\n    @Override\n    public T applyFrom(Object value) {\n        Assert.notNull(value, \"Value argument cannot be null.\");\n        if (type.isInstance(value)) {\n            return type.cast(value);\n        } else if (value instanceof CharSequence) {\n            return converter.applyFrom((CharSequence) value);\n        } else {\n            String msg = \"Values must be either String or \" + type.getName() +\n                    \" instances. Value type found: \" + value.getClass().getName() + \".\";\n            throw new IllegalArgumentException(msg);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/FormattedStringFunction.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\npublic class FormattedStringFunction<T> implements Function<T, String> {\n\n    private final String msg;\n\n    public FormattedStringFunction(String msg) {\n        this.msg = Assert.hasText(msg, \"msg argument cannot be null or empty.\");\n    }\n\n    @Override\n    public String apply(T arg) {\n        return String.format(msg, arg);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/FormattedStringSupplier.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Supplier;\n\npublic class FormattedStringSupplier implements Supplier<String> {\n\n    private final String msg;\n\n    private final Object[] args;\n\n    public FormattedStringSupplier(String msg, Object[] args) {\n        this.msg = Assert.hasText(msg, \"Message cannot be null or empty.\");\n        this.args = Assert.notEmpty(args, \"Arguments cannot be null or empty.\");\n    }\n\n    @Override\n    public String get() {\n        return String.format(this.msg, this.args);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Function.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\npublic interface Function<T, R> {\n\n    R apply(T t);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Functions.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\npublic final class Functions {\n\n    private Functions() {\n    }\n\n    public static <T> Function<T, T> identity() {\n        return new Function<T, T>() {\n            @Override\n            public T apply(T t) {\n                return t;\n            }\n        };\n    }\n\n    /**\n     * Wraps the specified function to ensure that if any exception occurs, it is of the specified type and/or with\n     * the specified message.  If no exception occurs, the function's return value is returned as expected.\n     *\n     * <p>If {@code fn} throws an exception, its type is checked.  If it is already of type {@code exClass}, that\n     * exception is immediately thrown.  If it is not the expected exception type, a message is created with the\n     * specified {@code msg} template, and a new exception of the specified type is thrown with the formatted message,\n     * using the original exception as its cause.</p>\n     *\n     * @param fn      the function to execute\n     * @param exClass the exception type expected, if any\n     * @param msg     the formatted message to use if throwing a new exception, used as the first argument to {@link String#format(String, Object...) String.format}.\n     * @param <T>     the function argument type\n     * @param <R>     the function's return type\n     * @param <E>     type of exception to ensure\n     * @return the wrapping function instance.\n     */\n    public static <T, R, E extends RuntimeException> Function<T, R> wrapFmt(CheckedFunction<T, R> fn, Class<E> exClass, String msg) {\n        return new PropagatingExceptionFunction<>(fn, exClass, new FormattedStringFunction<T>(msg));\n    }\n\n    public static <T, R, E extends RuntimeException> Function<T, R> wrap(Function<T, R> fn, Class<E> exClass, String fmt, Object... args) {\n        return new PropagatingExceptionFunction<>(new DelegatingCheckedFunction<>(fn), exClass, new FormattedStringSupplier(fmt, args));\n    }\n\n    /**\n     * Returns a composed function that first applies the {@code before} function to its input, and then applies\n     * the {@code after} function to the result. If evaluation of either function throws an exception, it is relayed to\n     * the caller of the composed function.\n     *\n     * @param <T>    type of input to the {@code before} function and the resulting composed function.\n     * @param <V>    the type of output of the {@code before} function, and of the input to the {@code after} function.\n     * @param <R>    return type of the {@code after} function and the resulting composed function.\n     * @param before the function to invoke first\n     * @param after  the function to invoke second with the output from the first\n     * @return a composed function that first applies the {@code before} function and then\n     * applies the {@code after} function.\n     * @throws IllegalArgumentException if either {@code before} or {@code after} are null.\n     */\n    public static <T, V, R> Function<T, R> andThen(final Function<T, ? extends V> before, final Function<V, R> after) {\n        Assert.notNull(before, \"Before function cannot be null.\");\n        Assert.notNull(after, \"After function cannot be null.\");\n        return new Function<T, R>() {\n            @Override\n            public R apply(T t) {\n                V result = before.apply(t);\n                return after.apply(result);\n            }\n        };\n    }\n\n    /**\n     * Returns a composed function that invokes the specified functions in iteration order, and returns the first\n     * non-null result.  Once a non-null result is discovered, no further functions will be invoked, 'short-circuiting'\n     * any remaining functions. If evaluation of any function throws an exception, it is relayed to the caller of the\n     * composed function.\n     *\n     * @param <T> the type of input of the functions, and of the composed function\n     * @param <R> the type of output of the functions, and of the composed function\n     * @param fns the functions to iterate\n     * @return a composed function that invokes the specified functions in iteration order, returning the first non-null\n     * result.\n     * @throws NullPointerException if after is null\n     */\n    @SafeVarargs\n    public static <T, R> Function<T, R> firstResult(final Function<T, R>... fns) {\n        Assert.notEmpty(fns, \"Function list cannot be null or empty.\");\n        return new Function<T, R>() {\n            @Override\n            public R apply(T t) {\n                for (Function<T, R> fn : fns) {\n                    Assert.notNull(fn, \"Function cannot be null.\");\n                    R result = fn.apply(t);\n                    if (result != null) {\n                        return result;\n                    }\n                }\n                return null;\n            }\n        };\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/IdRegistry.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Collection;\n\npublic class IdRegistry<T extends Identifiable> extends StringRegistry<T> {\n\n    public static final Function<Identifiable, String> FN = new Function<Identifiable, String>() {\n        @Override\n        public String apply(Identifiable identifiable) {\n            Assert.notNull(identifiable, \"Identifiable argument cannot be null.\");\n            return Assert.notNull(Strings.clean(identifiable.getId()), \"Identifier cannot be null or empty.\");\n        }\n    };\n\n    @SuppressWarnings(\"unchecked\")\n    public static <T extends Identifiable> Function<T, String> fn() {\n        return (Function<T, String>) FN;\n    }\n\n    public IdRegistry(String name, Collection<T> instances) {\n        // Each registry requires CaSe-SeNsItIvE keys by default purpose - all JWA standard algorithm identifiers\n        // (JWS 'alg', JWE 'enc', JWK 'kty', etc) are all case-sensitive per via the following RFC language:\n        //\n        //     This name is a case-sensitive ASCII string.  Names may not match other registered names in a\n        //     case-insensitive manner unless the Designated Experts state that there is a compelling reason to\n        //     allow an exception.\n        //\n        // References:\n        // - JWS/JWE alg and JWE enc 'Algorithm Name': https://www.rfc-editor.org/rfc/rfc7518.html#section-7.1.1\n        // - JWE zip 'Compression Algorithm Value': https://www.rfc-editor.org/rfc/rfc7518.html#section-7.3.1\n        // - JWK '\"kty\" Parameter Value': https://www.rfc-editor.org/rfc/rfc7518.html#section-7.4.1\n        this(name, instances, true); // <---\n    }\n\n    public IdRegistry(String name, Collection<T> instances, boolean caseSensitive) {\n        super(name, \"id\",\n                Assert.notNull(instances, \"Collection of Identifiable instances may not be null.\"),\n                IdRegistry.<T>fn(),\n                caseSensitive);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/JwtDateConverter.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.DateFormats;\n\nimport java.text.ParseException;\nimport java.util.Calendar;\nimport java.util.Date;\n\npublic class JwtDateConverter implements Converter<Date, Object> {\n\n    public static final JwtDateConverter INSTANCE = new JwtDateConverter();\n\n    @Override\n    public Object applyTo(Date date) {\n        if (date == null) {\n            return null;\n        }\n        // https://www.rfc-editor.org/rfc/rfc7519.html#section-2, 'Numeric Date' definition:\n        return date.getTime() / 1000L;\n    }\n\n    @Override\n    public Date applyFrom(Object o) {\n        return toSpecDate(o);\n    }\n\n    /**\n     * Returns an RFC-compatible {@link Date} equivalent of the specified object value using heuristics.\n     *\n     * @param value object to convert to a {@code Date} using heuristics.\n     * @return an RFC-compatible {@link Date} equivalent of the specified object value using heuristics.\n     * @since 0.10.0\n     */\n    public static Date toSpecDate(Object value) {\n        if (value == null) {\n            return null;\n        }\n        if (value instanceof String) {\n            try {\n                value = Long.parseLong((String) value);\n            } catch (NumberFormatException ignored) { // will try in the fallback toDate method call below\n            }\n        }\n        if (value instanceof Number) {\n            // https://github.com/jwtk/jjwt/issues/122:\n            // The JWT RFC *mandates* NumericDate values are represented as seconds.\n            // Because java.util.Date requires milliseconds, we need to multiply by 1000:\n            long seconds = ((Number) value).longValue();\n            value = seconds * 1000;\n        }\n        //v would have been normalized to milliseconds if it was a number value, so perform normal date conversion:\n        return toDate(value);\n    }\n\n    /**\n     * Returns a {@link Date} equivalent of the specified object value using heuristics.\n     *\n     * @param v the object value to represent as a Date.\n     * @return a {@link Date} equivalent of the specified object value using heuristics.\n     */\n    public static Date toDate(Object v) {\n        if (v == null) {\n            return null;\n        } else if (v instanceof Date) {\n            return (Date) v;\n        } else if (v instanceof Calendar) { //since 0.10.0\n            return ((Calendar) v).getTime();\n        } else if (v instanceof Number) {\n            //assume millis:\n            long millis = ((Number) v).longValue();\n            return new Date(millis);\n        } else if (v instanceof String) {\n            return parseIso8601Date((String) v); //ISO-8601 parsing since 0.10.0\n        } else {\n            String msg = \"Cannot create Date from object of type \" + v.getClass().getName() + \".\";\n            throw new IllegalArgumentException(msg);\n        }\n    }\n\n    /**\n     * Parses the specified ISO-8601-formatted string and returns the corresponding {@link Date} instance.\n     *\n     * @param value an ISO-8601-formatted string.\n     * @return a {@link Date} instance reflecting the specified ISO-8601-formatted string.\n     * @since 0.10.0\n     */\n    private static Date parseIso8601Date(String value) throws IllegalArgumentException {\n        try {\n            return DateFormats.parseIso8601Date(value);\n        } catch (ParseException e) {\n            String msg = \"String value is not a JWT NumericDate, nor is it ISO-8601-formatted. \" +\n                    \"All heuristics exhausted. Cause: \" + e.getMessage();\n            throw new IllegalArgumentException(msg, e);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/LocatorFunction.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.Locator;\nimport io.jsonwebtoken.lang.Assert;\n\npublic class LocatorFunction<T> implements Function<Header, T> {\n\n    private final Locator<T> locator;\n\n    public LocatorFunction(Locator<T> locator) {\n        this.locator = Assert.notNull(locator, \"Locator instance cannot be null.\");\n    }\n\n    @Override\n    public T apply(Header h) {\n        return this.locator.locate(h);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Nameable.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\npublic interface Nameable {\n\n    String getName();\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/NestedIdentifiableCollection.java",
    "content": "/*\n * Copyright © 2025 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.NestedCollection;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\n/**\n * @param <E> the type of Identifiable elements in the collection\n * @param <P> the parent to return\n * @since 0.12.7\n */\npublic class NestedIdentifiableCollection<E extends Identifiable, P> implements NestedCollection<E, P> {\n\n    private final P PARENT;\n    private final Map<String, E> VALUES;\n\n    private static <K, V> Map<K, V> nullSafe(Map<K, V> m) {\n        return m == null ? Collections.<K, V>emptyMap() : m;\n    }\n\n    public NestedIdentifiableCollection(P parent, Map<String, ? extends E> seed) {\n        super();\n        this.PARENT = Assert.notNull(parent, \"parent cannot be null.\");\n        this.VALUES = new LinkedHashMap<>(nullSafe(seed));\n    }\n\n    protected final String assertId(E i) {\n        Assert.notNull(i, \"Identifiable instance cannot be null.\");\n        String id = i.getId();\n        if (!Strings.hasText(id)) {\n            String msg = i.getClass() + \" getId() cannot be null or empty.\";\n            throw new IllegalArgumentException(msg);\n        }\n        return id;\n    }\n\n    private boolean doAdd(E e) {\n        String id = assertId(e);\n        this.VALUES.put(id, e);\n        return true;\n    }\n\n    @Override\n    public NestedCollection<E, P> add(E e) {\n        if (e != null) {\n            doAdd(e);\n            changed();\n        }\n        return this;\n    }\n\n    @Override\n    public NestedCollection<E, P> remove(E e) {\n        if (e != null) {\n            String id = assertId(e);\n            E previous = this.VALUES.remove(id);\n            if (previous != null) changed();\n        }\n        return this;\n    }\n\n    @Override\n    public NestedCollection<E, P> clear() {\n        if (!Collections.isEmpty(this.VALUES)) {\n            this.VALUES.clear();\n            changed();\n        }\n        return this;\n    }\n\n    @Override\n    public NestedCollection<E, P> add(Collection<? extends E> c) {\n        boolean changed = false;\n        for (E element : Collections.nullSafe(c)) {\n            changed = doAdd(element) || changed;\n        }\n        if (changed) changed();\n        return this;\n    }\n\n    @Override\n    public P and() {\n        return this.PARENT;\n    }\n\n    protected void changed() {\n    }\n\n    protected final Map<String, E> getValues() {\n        return Collections.immutable(this.VALUES);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/NullSafeConverter.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\npublic class NullSafeConverter<A, B> implements Converter<A, B> {\n\n    private final Converter<A, B> converter;\n\n    public NullSafeConverter(Converter<A, B> converter) {\n        this.converter = Assert.notNull(converter, \"Delegate converter cannot be null.\");\n    }\n\n    @Override\n    public B applyTo(A a) {\n        return a == null ? null : converter.applyTo(a);\n    }\n\n    @Override\n    public A applyFrom(B b) {\n        return b == null ? null : converter.applyFrom(b);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/OptionalMethodInvoker.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Classes;\n\nimport java.lang.reflect.InvocationTargetException;\nimport java.lang.reflect.Method;\n\npublic class OptionalMethodInvoker<T, R> extends ReflectionFunction<T, R> {\n\n    private final Class<?> CLASS;\n    private final Method METHOD;\n\n    private final Class<?>[] PARAM_TYPES;\n    private final boolean STATIC;\n\n    public OptionalMethodInvoker(String fqcn, String methodName) {\n        this(fqcn, methodName, null, false);\n    }\n\n    public OptionalMethodInvoker(String fqcn, String methodName, Class<?> paramType, boolean isStatic) {\n        Class<?> clazz = null;\n        Method method = null;\n        Class<?>[] paramTypes = paramType != null ? new Class<?>[]{paramType} : null;\n        try {\n            clazz = Classes.forName(fqcn);\n            method = clazz.getMethod(methodName, paramTypes);\n        } catch (Throwable ignored) {\n        }\n        this.CLASS = clazz;\n        this.METHOD = method;\n        this.PARAM_TYPES = paramTypes;\n        this.STATIC = isStatic;\n    }\n\n    @Override\n    protected boolean supports(T input) {\n        Class<?> clazz = null;\n        if (CLASS != null && METHOD != null) {\n            clazz = STATIC && PARAM_TYPES != null ? PARAM_TYPES[0] : CLASS;\n        }\n        return clazz != null && clazz.isInstance(input);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    protected R invoke(T input) throws InvocationTargetException, IllegalAccessException {\n        return (STATIC) ? (R) METHOD.invoke(null, input) : (R) METHOD.invoke(input);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Parameter.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.Identifiable;\n\npublic interface Parameter<T> extends Identifiable, Converter<T, Object> {\n\n    String getName();\n\n    boolean supports(Object value);\n\n    T cast(Object value);\n\n    boolean isSecret();\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/ParameterBuilder.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Builder;\n\nimport java.util.List;\nimport java.util.Set;\n\n/**\n * @since 0.12.0\n */\npublic interface ParameterBuilder<T> extends Builder<Parameter<T>> {\n\n    ParameterBuilder<T> setId(String id);\n\n    ParameterBuilder<T> setName(String name);\n\n    ParameterBuilder<T> setSecret(boolean secret);\n\n    ParameterBuilder<List<T>> list();\n\n    ParameterBuilder<Set<T>> set();\n\n    ParameterBuilder<T> setConverter(Converter<T, ?> converter);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/ParameterReadable.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\npublic interface ParameterReadable {\n\n    <T> T get(Parameter<T> param);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Parameters.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Registry;\n\nimport java.math.BigInteger;\nimport java.net.URI;\nimport java.security.MessageDigest;\nimport java.security.cert.X509Certificate;\nimport java.util.Collection;\nimport java.util.Date;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic final class Parameters {\n\n    private Parameters() { // prevent instantiation\n    }\n\n    public static Parameter<String> string(String id, String name) {\n        return builder(String.class).setId(id).setName(name).build();\n    }\n\n    public static Parameter<Date> rfcDate(String id, String name) {\n        return builder(Date.class).setConverter(JwtDateConverter.INSTANCE).setId(id).setName(name).build();\n    }\n\n    public static Parameter<List<X509Certificate>> x509Chain(String id, String name) {\n        return builder(X509Certificate.class)\n                .setConverter(Converters.X509_CERTIFICATE).list()\n                .setId(id).setName(name).build();\n    }\n\n    public static <T> ParameterBuilder<T> builder(Class<T> type) {\n        return new DefaultParameterBuilder<>(type);\n    }\n\n    public static Parameter<Set<String>> stringSet(String id, String name) {\n        return builder(String.class).set().setId(id).setName(name).build();\n    }\n\n    public static Parameter<URI> uri(String id, String name) {\n        return builder(URI.class).setConverter(Converters.URI).setId(id).setName(name).build();\n    }\n\n    public static ParameterBuilder<byte[]> bytes(String id, String name) {\n        return builder(byte[].class).setConverter(Converters.BASE64URL_BYTES).setId(id).setName(name);\n    }\n\n    public static ParameterBuilder<BigInteger> bigInt(String id, String name) {\n        return builder(BigInteger.class).setConverter(Converters.BIGINT).setId(id).setName(name);\n    }\n\n    public static Parameter<BigInteger> secretBigInt(String id, String name) {\n        return bigInt(id, name).setSecret(true).build();\n    }\n\n    public static Registry<String, Parameter<?>> registry(Parameter<?>... params) {\n        return registry(Arrays.asList(params));\n    }\n\n    public static Registry<String, Parameter<?>> registry(Collection<Parameter<?>> params) {\n        return new IdRegistry<>(\"Parameter\", params, true);\n    }\n\n    public static Registry<String, Parameter<?>> registry(Registry<String, Parameter<?>> parent, Parameter<?>... params) {\n        Set<Parameter<?>> set = new LinkedHashSet<>(parent.size() + params.length);\n        set.addAll(parent.values());\n        set.addAll(Arrays.asList(params));\n        return new IdRegistry<>(\"Parameter\", set, true);\n    }\n\n    public static Registry<String, ? extends Parameter<?>> replace(Registry<String, ? extends Parameter<?>> registry, Parameter<?> param) {\n        Assert.notEmpty(registry, \"Registry cannot be null or empty.\");\n        Assert.notNull(param, \"Parameter cannot be null.\");\n        String id = Assert.hasText(param.getId(), \"Parameter id cannot be null or empty.\");\n        Map<String, Parameter<?>> newParams = new LinkedHashMap<>(registry);\n        newParams.remove(id); // remove old/default\n        newParams.put(id, param); // add new one\n        return registry(newParams.values());\n    }\n\n    private static byte[] bytes(BigInteger i) {\n        return i != null ? i.toByteArray() : null;\n    }\n\n    public static boolean bytesEquals(BigInteger a, BigInteger b) {\n        //noinspection NumberEquality\n        if (a == b) return true;\n        if (a == null || b == null) return false;\n        byte[] aBytes = bytes(a);\n        byte[] bBytes = bytes(b);\n        try {\n            return MessageDigest.isEqual(aBytes, bBytes);\n        } finally {\n            Bytes.clear(aBytes);\n            Bytes.clear(bBytes);\n        }\n    }\n\n    private static <T> boolean equals(T a, T b, Parameter<T> param) {\n        if (a == b) return true;\n        if (a == null || b == null) return false;\n        if (param.isSecret()) {\n            // byte[] and BigInteger are the only types of secret Parameters in the JJWT codebase\n            // (i.e. Parameter.isSecret() == true). If a Parameter is ever marked as secret, and it's not one of these two\n            // data types, we need to know about it.  So we use the 'assertSecret' helper above to ensure we do:\n            if (a instanceof byte[]) {\n                return b instanceof byte[] && MessageDigest.isEqual((byte[]) a, (byte[]) b);\n            } else if (a instanceof BigInteger) {\n                return b instanceof BigInteger && bytesEquals((BigInteger) a, (BigInteger) b);\n            }\n        }\n        // default to a standard null-safe comparison:\n        return Objects.nullSafeEquals(a, b);\n    }\n\n    public static <T> boolean equals(ParameterReadable a, Object o, Parameter<T> param) {\n        if (a == o) return true;\n        if (a == null || !(o instanceof ParameterReadable)) return false;\n        ParameterReadable b = (ParameterReadable) o;\n        return equals(a.get(param), b.get(param), param);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/PositiveIntegerConverter.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.util.concurrent.atomic.AtomicInteger;\n\npublic class PositiveIntegerConverter implements Converter<Integer, Object> {\n\n    public static final PositiveIntegerConverter INSTANCE = new PositiveIntegerConverter();\n\n    @Override\n    public Object applyTo(Integer integer) {\n        return integer;\n    }\n\n    @Override\n    public Integer applyFrom(Object o) {\n        Assert.notNull(o, \"Argument cannot be null.\");\n        int i;\n        if (o instanceof Byte || o instanceof Short || o instanceof Integer || o instanceof AtomicInteger) {\n            i = ((Number) o).intValue();\n        } else {  // could be Long, AtomicLong, Float, Decimal, BigInteger, BigDecimal, String, etc., all of which\n            // may not be accurately converted into an Integer, either due to overflow or fractional values.  The\n            // easiest way to account for all of them is to parse the string value as an int instead of testing all\n            // the types:\n            String sval = String.valueOf(o);\n            try {\n                i = Integer.parseInt(sval);\n            } catch (NumberFormatException e) {\n                String msg = \"Value cannot be represented as a java.lang.Integer.\";\n                throw new IllegalArgumentException(msg, e);\n            }\n        }\n        if (i <= 0) {\n            String msg = \"Value must be a positive integer.\";\n            throw new IllegalArgumentException(msg);\n        }\n        return i;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/PropagatingExceptionFunction.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Classes;\nimport io.jsonwebtoken.lang.Supplier;\n\nimport java.lang.reflect.Constructor;\n\npublic class PropagatingExceptionFunction<T, R, E extends RuntimeException> implements Function<T, R> {\n\n    private final CheckedFunction<T, R> function;\n\n    private final Function<T, String> msgFunction;\n    private final Class<E> clazz;\n\n    public PropagatingExceptionFunction(Function<T, R> f, Class<E> exceptionClass, String msg) {\n        this(new DelegatingCheckedFunction<>(f), exceptionClass, new ConstantFunction<T, String>(msg));\n    }\n\n    public PropagatingExceptionFunction(CheckedFunction<T, R> f, Class<E> exceptionClass, final String msg) {\n        this(f, exceptionClass, new ConstantFunction<T, String>(msg));\n    }\n\n    public PropagatingExceptionFunction(CheckedFunction<T, R> fn, Class<E> exceptionClass, final Supplier<String> msgSupplier) {\n        this(fn, exceptionClass, new Function<T, String>() {\n            @Override\n            public String apply(T t) {\n                return msgSupplier.get();\n            }\n        });\n    }\n\n    public PropagatingExceptionFunction(CheckedFunction<T, R> f, Class<E> exceptionClass, Function<T, String> msgFunction) {\n        this.clazz = Assert.notNull(exceptionClass, \"Exception class cannot be null.\");\n        this.msgFunction = Assert.notNull(msgFunction, \"msgFunction cannot be null.\");\n        this.function = Assert.notNull(f, \"Function cannot be null\");\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public R apply(T t) {\n        try {\n            return function.apply(t);\n        } catch (Exception e) {\n            if (clazz.isAssignableFrom(e.getClass())) {\n                throw clazz.cast(e);\n            }\n            String msg = this.msgFunction.apply(t);\n            if (!msg.endsWith(\".\")) {\n                msg += \".\";\n            }\n            msg += \" Cause: \" + e.getMessage();\n            Class<RuntimeException> clazzz = (Class<RuntimeException>) clazz;\n            Constructor<RuntimeException> ctor = Classes.getConstructor(clazzz, String.class, Throwable.class);\n            throw Classes.instantiate(ctor, msg, e);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/RedactedSupplier.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Supplier;\n\npublic class RedactedSupplier<T> implements Supplier<T> {\n\n    public static final String REDACTED_VALUE = \"<redacted>\";\n\n    private final T value;\n\n    public RedactedSupplier(T value) {\n        this.value = Assert.notNull(value, \"value cannot be null.\");\n    }\n\n    @Override\n    public T get() {\n        return value;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.nullSafeHashCode(value);\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) {\n            return true;\n        }\n        if (obj instanceof RedactedSupplier) {\n            obj = ((RedactedSupplier<?>) obj).value; // get the wrapped value\n        }\n        return Objects.nullSafeEquals(this.value, obj);\n    }\n\n    @Override\n    public String toString() {\n        return REDACTED_VALUE;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/RedactedValueConverter.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Supplier;\n\npublic class RedactedValueConverter<T> implements Converter<T, Object> {\n\n    private final Converter<T, Object> delegate;\n\n    public RedactedValueConverter(Converter<T, Object> delegate) {\n        this.delegate = Assert.notNull(delegate, \"Delegate cannot be null.\");\n    }\n\n    @Override\n    public Object applyTo(T t) {\n        Object value = this.delegate.applyTo(t);\n        if (value != null && !(value instanceof RedactedSupplier)) {\n            value = new RedactedSupplier<>(value);\n        }\n        return value;\n    }\n\n    @Override\n    public T applyFrom(Object o) {\n        if (o instanceof RedactedSupplier) {\n            o = ((Supplier<?>) o).get();\n        }\n        return this.delegate.applyFrom(o);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/ReflectionFunction.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nabstract class ReflectionFunction<T, R> implements Function<T, R> {\n\n    public static final String ERR_MSG = \"Reflection operation failed. This is likely due to an internal \" +\n            \"implementation programming error.  Please report this to the JJWT development team.  Cause: \";\n\n    protected abstract boolean supports(T input);\n\n    protected abstract R invoke(T input) throws Throwable;\n\n    @Override\n    public final R apply(T input) {\n        if (supports(input)) {\n            try {\n                return invoke(input);\n            } catch (Throwable throwable) {\n                // should never happen if supportsInput is true since that would mean we're using the API incorrectly\n                String msg = ERR_MSG + throwable.getMessage();\n                throw new IllegalStateException(msg, throwable);\n            }\n        }\n        return null;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/RequiredBitLengthConverter.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\npublic class RequiredBitLengthConverter implements Converter<byte[], Object> {\n\n    private final Converter<byte[], Object> converter;\n\n    private final int bitLength;\n    private final boolean exact;\n\n    public RequiredBitLengthConverter(Converter<byte[], Object> converter, int bitLength) {\n        this(converter, bitLength, true);\n    }\n\n    public RequiredBitLengthConverter(Converter<byte[], Object> converter, int bitLength, boolean exact) {\n        this.converter = Assert.notNull(converter, \"Converter cannot be null.\");\n        this.bitLength = Assert.gt(bitLength, 0, \"bitLength must be greater than 0\");\n        this.exact = exact;\n    }\n\n    private byte[] assertLength(byte[] bytes) {\n        long len = Bytes.bitLength(bytes);\n        if (exact && len != this.bitLength) {\n            String msg = \"Byte array must be exactly \" + Bytes.bitsMsg(this.bitLength) + \". Found \" + Bytes.bitsMsg(len);\n            throw new IllegalArgumentException(msg);\n        } else if (len < this.bitLength) {\n            String msg = \"Byte array must be at least \" + Bytes.bitsMsg(this.bitLength) + \". Found \" + Bytes.bitsMsg(len);\n            throw new IllegalArgumentException(msg);\n        }\n        return bytes;\n    }\n\n    @Override\n    public Object applyTo(byte[] bytes) {\n        assertLength(bytes);\n        return this.converter.applyTo(bytes);\n    }\n\n    @Override\n    public byte[] applyFrom(Object o) {\n        byte[] result = this.converter.applyFrom(o);\n        return assertLength(result);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/RequiredParameterReader.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.JwtException;\nimport io.jsonwebtoken.MalformedJwtException;\nimport io.jsonwebtoken.impl.security.JwkContext;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.MalformedKeyException;\n\npublic class RequiredParameterReader implements ParameterReadable {\n\n    private final ParameterReadable src;\n\n    public RequiredParameterReader(Header header) {\n        this(Assert.isInstanceOf(ParameterReadable.class, header, \"Header implementations must implement ParameterReadable: \"));\n    }\n\n    public RequiredParameterReader(ParameterReadable src) {\n        this.src = Assert.notNull(src, \"Source ParameterReadable cannot be null.\");\n        Assert.isInstanceOf(Nameable.class, src, \"ParameterReadable implementations must implement Nameable.\");\n    }\n\n    private String name() {\n        return ((Nameable) this.src).getName();\n    }\n\n    private JwtException malformed(String msg) {\n        if (this.src instanceof JwkContext || this.src instanceof Jwk) {\n            return new MalformedKeyException(msg);\n        } else {\n            return new MalformedJwtException(msg);\n        }\n    }\n\n    @Override\n    public <T> T get(Parameter<T> param) {\n        T value = this.src.get(param);\n        if (value == null) {\n            String msg = name() + \" is missing required \" + param + \" value.\";\n            throw malformed(msg);\n        }\n        return value;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/RequiredTypeConverter.java",
    "content": "/*\n * Copyright © 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\n/**\n * @since 0.12.0\n */\npublic class RequiredTypeConverter<T> implements Converter<T, Object> {\n\n    private final Class<T> type;\n\n    public RequiredTypeConverter(Class<T> type) {\n        this.type = Assert.notNull(type, \"type argument cannot be null.\");\n    }\n\n    @Override\n    public Object applyTo(T t) {\n        return t;\n    }\n\n    @Override\n    public T applyFrom(Object o) {\n        if (o == null) {\n            return null;\n        }\n        Class<?> clazz = o.getClass();\n        if (!type.isAssignableFrom(clazz)) {\n            String msg = \"Unsupported value type. Expected: \" + type.getName() + \", found: \" + clazz.getName();\n            throw new IllegalArgumentException(msg);\n        }\n        return type.cast(o);\n    }\n}"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/Services.java",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.ServiceLoader;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\n/**\n * Helper class for loading services from the classpath, using a {@link ServiceLoader}. Decouples loading logic for\n * better separation of concerns and testability.\n */\npublic final class Services {\n\n    private static final ConcurrentMap<Class<?>, Object> SERVICES = new ConcurrentHashMap<>();\n\n    private static final List<ClassLoaderAccessor> CLASS_LOADER_ACCESSORS = Arrays.asList(new ClassLoaderAccessor[]{\n            new ClassLoaderAccessor() {\n                @Override\n                public ClassLoader getClassLoader() {\n                    return Thread.currentThread().getContextClassLoader();\n                }\n            },\n            new ClassLoaderAccessor() {\n                @Override\n                public ClassLoader getClassLoader() {\n                    return Services.class.getClassLoader();\n                }\n            },\n            new ClassLoaderAccessor() {\n                @Override\n                public ClassLoader getClassLoader() {\n                    return ClassLoader.getSystemClassLoader();\n                }\n            }\n    });\n\n    private Services() {\n    }\n\n    /**\n     * Returns the first available implementation for the given SPI class, checking an internal thread-safe cache first,\n     * and, if not found, using a {@link ServiceLoader} to find implementations. When multiple implementations are\n     * available it will return the first one that it encounters. There is no guarantee with regard to ordering.\n     *\n     * @param spi The class of the Service Provider Interface\n     * @param <T> The type of the SPI\n     * @return The first available instance of the service.\n     * @throws UnavailableImplementationException When no implementation of the SPI class can be found.\n     * @since 0.12.4\n     */\n    public static <T> T get(Class<T> spi) {\n        // TODO: JDK8, replace this find/putIfAbsent logic with ConcurrentMap.computeIfAbsent\n        T instance = findCached(spi);\n        if (instance == null) {\n            instance = loadFirst(spi); // throws UnavailableImplementationException if not found, which is what we want\n            SERVICES.putIfAbsent(spi, instance); // cache if not already cached\n        }\n        return instance;\n    }\n\n    private static <T> T findCached(Class<T> spi) {\n        Assert.notNull(spi, \"Service interface cannot be null.\");\n        Object obj = SERVICES.get(spi);\n        if (obj != null) {\n            return Assert.isInstanceOf(spi, obj, \"Unexpected cached service implementation type.\");\n        }\n        return null;\n    }\n\n    private static <T> T loadFirst(Class<T> spi) {\n        for (ClassLoaderAccessor accessor : CLASS_LOADER_ACCESSORS) {\n            ServiceLoader<T> loader = ServiceLoader.load(spi, accessor.getClassLoader());\n            Assert.stateNotNull(loader, \"JDK ServiceLoader#load should never return null.\");\n            Iterator<T> i = loader.iterator();\n            Assert.stateNotNull(i, \"JDK ServiceLoader#iterator() should never return null.\");\n            if (i.hasNext()) {\n                return i.next();\n            }\n        }\n        throw new UnavailableImplementationException(spi);\n    }\n\n    /**\n     * Clears internal cache of service singletons. This is useful when testing, or for applications that dynamically\n     * change classloaders.\n     */\n    public static void reload() {\n        SERVICES.clear();\n    }\n\n    private interface ClassLoaderAccessor {\n        ClassLoader getClassLoader();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/StringRegistry.java",
    "content": "/*\n * Copyright © 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Locale;\nimport java.util.Map;\n\npublic class StringRegistry<V> extends DefaultRegistry<String, V> {\n\n    private final Function<String, String> CASE_FN;\n\n    private final Map<String, V> CI_VALUES;\n\n    public StringRegistry(String name, String keyName, Collection<V> values, Function<V, String> keyFn, boolean caseSensitive) {\n        this(name, keyName, values, keyFn, caseSensitive ? Functions.<String>identity() : CaseInsensitiveFunction.ENGLISH);\n    }\n\n    public StringRegistry(String name, String keyName, Collection<V> values, Function<V, String> keyFn, Function<String, String> caseFn) {\n        super(name, keyName, values, keyFn);\n        this.CASE_FN = Assert.notNull(caseFn, \"Case function cannot be null.\");\n        Map<String, V> m = new LinkedHashMap<>(Collections.size(values));\n        for (V value : values) {\n            String key = keyFn.apply(value);\n            key = this.CASE_FN.apply(key);\n            m.put(key, value);\n        }\n        this.CI_VALUES = Collections.immutable(m);\n    }\n\n    @Override\n    public V get(Object key) {\n        String id = (String) key; // could throw ClassCastException as allowed per Map 'get' contract\n        Assert.hasText(id, \"id argument cannot be null or empty.\");\n        V instance = super.get(id); //try standard ID lookup first.  This will satisfy 99% of invocations\n        if (instance == null) { // fall back to case-insensitive ID lookup:\n            id = CASE_FN.apply(id);\n            instance = CI_VALUES.get(id);\n        }\n        return instance;\n    }\n\n    private static final class CaseInsensitiveFunction implements Function<String, String> {\n\n        private static final CaseInsensitiveFunction ENGLISH = new CaseInsensitiveFunction(Locale.ENGLISH);\n\n        private final Locale LOCALE;\n\n        private CaseInsensitiveFunction(Locale locale) {\n            this.LOCALE = Assert.notNull(locale, \"Case insensitive Locale argument cannot be null.\");\n        }\n\n        @Override\n        public String apply(String s) {\n            s = Assert.notNull(Strings.clean(s), \"String identifier cannot be null or empty.\");\n            return s.toUpperCase(LOCALE);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/UnavailableImplementationException.java",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\n/**\n * Exception indicating that no implementation of an jjwt-api SPI was found on the classpath.\n *\n * @since 0.11.0\n */\npublic final class UnavailableImplementationException extends RuntimeException {\n\n    private static final String DEFAULT_NOT_FOUND_MESSAGE = \"Unable to find an implementation for %s using \" +\n            \"java.util.ServiceLoader. Ensure you include a backing implementation .jar in the classpath, \" +\n            \"for example jjwt-jackson.jar, jjwt-gson.jar or jjwt-orgjson.jar, or your own .jar for \" +\n            \"custom implementations.\";\n\n    UnavailableImplementationException(final Class<?> klass) {\n        super(String.format(DEFAULT_NOT_FOUND_MESSAGE, klass));\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/lang/UriStringConverter.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang;\n\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.net.URI;\n\npublic class UriStringConverter implements Converter<URI, CharSequence> {\n\n    @Override\n    public String applyTo(URI uri) {\n        Assert.notNull(uri, \"URI cannot be null.\");\n        return uri.toString();\n    }\n\n    @Override\n    public URI applyFrom(CharSequence s) {\n        Assert.hasText(s, \"URI string cannot be null or empty.\");\n        try {\n            return URI.create(s.toString());\n        } catch (Exception e) {\n            String msg = \"Unable to convert String value '\" + s + \"' to URI instance: \" + e.getMessage();\n            throw new IllegalArgumentException(msg, e);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractAsymmetricJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.AsymmetricJwk;\n\nimport java.net.URI;\nimport java.security.Key;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\nimport java.util.Set;\n\npublic abstract class AbstractAsymmetricJwk<K extends Key> extends AbstractJwk<K> implements AsymmetricJwk<K> {\n\n    static final Parameter<String> USE = Parameters.string(\"use\", \"Public Key Use\");\n    public static final Parameter<List<X509Certificate>> X5C = Parameters.x509Chain(\"x5c\", \"X.509 Certificate Chain\");\n    public static final Parameter<byte[]> X5T = Parameters.bytes(\"x5t\", \"X.509 Certificate SHA-1 Thumbprint\").build();\n    public static final Parameter<byte[]> X5T_S256 = Parameters.bytes(\"x5t#S256\", \"X.509 Certificate SHA-256 Thumbprint\").build();\n    public static final Parameter<URI> X5U = Parameters.uri(\"x5u\", \"X.509 URL\");\n    static final Set<Parameter<?>> PARAMS = Collections.concat(AbstractJwk.PARAMS, USE, X5C, X5T, X5T_S256, X5U);\n\n    AbstractAsymmetricJwk(JwkContext<K> ctx, List<Parameter<?>> thumbprintParams) {\n        super(ctx, thumbprintParams);\n    }\n\n    @Override\n    public String getPublicKeyUse() {\n        return this.context.getPublicKeyUse();\n    }\n\n    @Override\n    public URI getX509Url() {\n        return this.context.getX509Url();\n    }\n\n    @Override\n    public List<X509Certificate> getX509Chain() {\n        return Collections.immutable(this.context.getX509Chain());\n    }\n\n    @Override\n    public byte[] getX509Sha1Thumbprint() {\n        return (byte[])Arrays.copy(this.context.getX509Sha1Thumbprint());\n    }\n\n    @Override\n    public byte[] getX509Sha256Thumbprint() {\n        return (byte[])Arrays.copy(this.context.getX509Sha256Thumbprint());\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractAsymmetricJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.ParameterMap;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AsymmetricJwk;\nimport io.jsonwebtoken.security.AsymmetricJwkBuilder;\nimport io.jsonwebtoken.security.EcPrivateJwk;\nimport io.jsonwebtoken.security.EcPrivateJwkBuilder;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.EcPublicJwkBuilder;\nimport io.jsonwebtoken.security.MalformedKeyException;\nimport io.jsonwebtoken.security.OctetPrivateJwk;\nimport io.jsonwebtoken.security.OctetPrivateJwkBuilder;\nimport io.jsonwebtoken.security.OctetPublicJwk;\nimport io.jsonwebtoken.security.OctetPublicJwkBuilder;\nimport io.jsonwebtoken.security.PrivateJwk;\nimport io.jsonwebtoken.security.PrivateJwkBuilder;\nimport io.jsonwebtoken.security.PublicJwk;\nimport io.jsonwebtoken.security.PublicJwkBuilder;\nimport io.jsonwebtoken.security.RsaPrivateJwk;\nimport io.jsonwebtoken.security.RsaPrivateJwkBuilder;\nimport io.jsonwebtoken.security.RsaPublicJwk;\nimport io.jsonwebtoken.security.RsaPublicJwkBuilder;\n\nimport java.net.URI;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.cert.X509Certificate;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.List;\n\nabstract class AbstractAsymmetricJwkBuilder<K extends Key, J extends AsymmetricJwk<K>, T extends AsymmetricJwkBuilder<K, J, T>>\n        extends AbstractJwkBuilder<K, J, T> implements AsymmetricJwkBuilder<K, J, T> {\n\n    protected Boolean applyX509KeyUse = null;\n    private KeyUseStrategy keyUseStrategy = DefaultKeyUseStrategy.INSTANCE;\n\n    private final X509BuilderSupport x509;\n\n    public AbstractAsymmetricJwkBuilder(JwkContext<K> ctx) {\n        super(ctx);\n        ParameterMap map = Assert.isInstanceOf(ParameterMap.class, this.DELEGATE);\n        this.x509 = new X509BuilderSupport(map, MalformedKeyException.class);\n    }\n\n    AbstractAsymmetricJwkBuilder(AbstractAsymmetricJwkBuilder<?, ?, ?> b, JwkContext<K> ctx) {\n        this(ctx);\n        this.applyX509KeyUse = b.applyX509KeyUse;\n        this.keyUseStrategy = b.keyUseStrategy;\n    }\n\n    @Override\n    public T publicKeyUse(String use) {\n        Assert.hasText(use, \"publicKeyUse cannot be null or empty.\");\n        this.DELEGATE.setPublicKeyUse(use);\n        return self();\n    }\n\n    /*\n    public T setKeyUseStrategy(KeyUseStrategy strategy) {\n        this.keyUseStrategy = Assert.notNull(strategy, \"KeyUseStrategy cannot be null.\");\n        return tthis();\n    }\n     */\n\n    @Override\n    public T x509Chain(List<X509Certificate> chain) {\n        Assert.notEmpty(chain, \"X509Certificate chain cannot be null or empty.\");\n        this.x509.x509Chain(chain);\n        return self();\n    }\n\n    @Override\n    public T x509Url(URI uri) {\n        Assert.notNull(uri, \"X509Url cannot be null.\");\n        this.x509.x509Url(uri);\n        return self();\n    }\n\n    /*\n    @Override\n    public T withX509KeyUse(boolean enable) {\n        this.applyX509KeyUse = enable;\n        return tthis();\n    }\n     */\n\n    @Override\n    public T x509Sha1Thumbprint(byte[] thumbprint) {\n        this.x509.x509Sha1Thumbprint(thumbprint);\n        return self();\n    }\n\n    @Override\n    public T x509Sha256Thumbprint(byte[] thumbprint) {\n        this.x509.x509Sha256Thumbprint(thumbprint);\n        return self();\n    }\n\n    @Override\n    public T x509Sha1Thumbprint(boolean enable) {\n        this.x509.x509Sha1Thumbprint(enable);\n        return self();\n    }\n\n    @Override\n    public T x509Sha256Thumbprint(boolean enable) {\n        this.x509.x509Sha256Thumbprint(enable);\n        return self();\n    }\n\n    @Override\n    public J build() {\n        this.x509.apply();\n        return super.build();\n    }\n\n    private abstract static class DefaultPublicJwkBuilder<K extends PublicKey, L extends PrivateKey,\n            J extends PublicJwk<K>, M extends PrivateJwk<L, K, J>, P extends PrivateJwkBuilder<L, K, J, M, P>,\n            T extends PublicJwkBuilder<K, L, J, M, P, T>>\n            extends AbstractAsymmetricJwkBuilder<K, J, T>\n            implements PublicJwkBuilder<K, L, J, M, P, T> {\n\n        DefaultPublicJwkBuilder(JwkContext<K> ctx) {\n            super(ctx);\n        }\n\n        @Override\n        public P privateKey(L privateKey) {\n            Assert.notNull(privateKey, \"PrivateKey argument cannot be null.\");\n            final K publicKey = Assert.notNull(DELEGATE.getKey(), \"PublicKey cannot be null.\");\n            return newPrivateBuilder(newContext(privateKey)).publicKey(publicKey);\n        }\n\n        protected abstract P newPrivateBuilder(JwkContext<L> ctx);\n    }\n\n    private abstract static class DefaultPrivateJwkBuilder<K extends PrivateKey, L extends PublicKey,\n            J extends PublicJwk<L>, M extends PrivateJwk<K, L, J>,\n            T extends PrivateJwkBuilder<K, L, J, M, T>>\n            extends AbstractAsymmetricJwkBuilder<K, M, T>\n            implements PrivateJwkBuilder<K, L, J, M, T> {\n\n        DefaultPrivateJwkBuilder(JwkContext<K> ctx) {\n            super(ctx);\n        }\n\n        DefaultPrivateJwkBuilder(DefaultPublicJwkBuilder<L, K, J, M, ?, ?> b, JwkContext<K> ctx) {\n            super(b, ctx);\n            this.DELEGATE.setPublicKey(b.DELEGATE.getKey());\n        }\n\n        @Override\n        public T publicKey(L publicKey) {\n            this.DELEGATE.setPublicKey(publicKey);\n            return self();\n        }\n    }\n\n    static class DefaultRsaPublicJwkBuilder\n            extends DefaultPublicJwkBuilder<RSAPublicKey, RSAPrivateKey, RsaPublicJwk, RsaPrivateJwk, RsaPrivateJwkBuilder, RsaPublicJwkBuilder>\n            implements RsaPublicJwkBuilder {\n\n        DefaultRsaPublicJwkBuilder(JwkContext<RSAPublicKey> ctx) {\n            super(ctx);\n        }\n\n        @Override\n        protected RsaPrivateJwkBuilder newPrivateBuilder(JwkContext<RSAPrivateKey> ctx) {\n            return new DefaultRsaPrivateJwkBuilder(this, ctx);\n        }\n    }\n\n    static class DefaultEcPublicJwkBuilder\n            extends DefaultPublicJwkBuilder<ECPublicKey, ECPrivateKey, EcPublicJwk, EcPrivateJwk, EcPrivateJwkBuilder, EcPublicJwkBuilder>\n            implements EcPublicJwkBuilder {\n        DefaultEcPublicJwkBuilder(JwkContext<ECPublicKey> src) {\n            super(src);\n        }\n\n        @Override\n        protected EcPrivateJwkBuilder newPrivateBuilder(JwkContext<ECPrivateKey> ctx) {\n            return new DefaultEcPrivateJwkBuilder(this, ctx);\n        }\n    }\n\n    static class DefaultOctetPublicJwkBuilder<A extends PublicKey, B extends PrivateKey>\n            extends DefaultPublicJwkBuilder<A, B, OctetPublicJwk<A>, OctetPrivateJwk<B, A>,\n            OctetPrivateJwkBuilder<B, A>, OctetPublicJwkBuilder<A, B>>\n            implements OctetPublicJwkBuilder<A, B> {\n        DefaultOctetPublicJwkBuilder(JwkContext<A> ctx) {\n            super(ctx);\n            EdwardsCurve.assertEdwards(ctx.getKey());\n        }\n\n        @Override\n        protected OctetPrivateJwkBuilder<B, A> newPrivateBuilder(JwkContext<B> ctx) {\n            return new DefaultOctetPrivateJwkBuilder<>(this, ctx);\n        }\n    }\n\n    static class DefaultRsaPrivateJwkBuilder\n            extends DefaultPrivateJwkBuilder<RSAPrivateKey, RSAPublicKey, RsaPublicJwk, RsaPrivateJwk, RsaPrivateJwkBuilder>\n            implements RsaPrivateJwkBuilder {\n        DefaultRsaPrivateJwkBuilder(JwkContext<RSAPrivateKey> src) {\n            super(src);\n        }\n\n        DefaultRsaPrivateJwkBuilder(DefaultRsaPublicJwkBuilder b, JwkContext<RSAPrivateKey> ctx) {\n            super(b, ctx);\n        }\n    }\n\n    static class DefaultEcPrivateJwkBuilder\n            extends DefaultPrivateJwkBuilder<ECPrivateKey, ECPublicKey, EcPublicJwk, EcPrivateJwk, EcPrivateJwkBuilder>\n            implements EcPrivateJwkBuilder {\n        DefaultEcPrivateJwkBuilder(JwkContext<ECPrivateKey> src) {\n            super(src);\n        }\n\n        DefaultEcPrivateJwkBuilder(DefaultEcPublicJwkBuilder b, JwkContext<ECPrivateKey> ctx) {\n            super(b, ctx);\n        }\n    }\n\n    static class DefaultOctetPrivateJwkBuilder<A extends PrivateKey, B extends PublicKey>\n            extends DefaultPrivateJwkBuilder<A, B, OctetPublicJwk<B>, OctetPrivateJwk<A, B>,\n            OctetPrivateJwkBuilder<A, B>> implements OctetPrivateJwkBuilder<A, B> {\n        DefaultOctetPrivateJwkBuilder(JwkContext<A> src) {\n            super(src);\n            EdwardsCurve.assertEdwards(src.getKey());\n        }\n\n        DefaultOctetPrivateJwkBuilder(DefaultOctetPublicJwkBuilder<B, A> b, JwkContext<A> ctx) {\n            super(b, ctx);\n            EdwardsCurve.assertEdwards(ctx.getKey());\n            EdwardsCurve.assertEdwards(ctx.getPublicKey());\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractCurve.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.Curve;\nimport io.jsonwebtoken.security.KeyPairBuilder;\n\nimport java.security.Key;\n\nabstract class AbstractCurve implements Curve {\n\n    private final String ID;\n\n    private final String JCA_NAME;\n\n    AbstractCurve(String id, String jcaName) {\n        this.ID = Assert.notNull(Strings.clean(id), \"Curve ID cannot be null or empty.\");\n        this.JCA_NAME = Assert.notNull(Strings.clean(jcaName), \"Curve jcaName cannot be null or empty.\");\n    }\n\n    @Override\n    public String getId() {\n        return this.ID;\n    }\n\n    public String getJcaName() {\n        return this.JCA_NAME;\n    }\n\n    @Override\n    public int hashCode() {\n        return ID.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj instanceof Curve) {\n            Curve curve = (Curve) obj;\n            return ID.equals(curve.getId());\n        }\n        return false;\n    }\n\n    @Override\n    public String toString() {\n        return ID;\n    }\n\n    public KeyPairBuilder keyPair() {\n        return new DefaultKeyPairBuilder(this.JCA_NAME);\n    }\n\n    /**\n     * Returns {@code true} if the specified key is known to represent a point on the curve, {@code false} otherwise.\n     *\n     * @param key the key to test\n     * @return {@code true} if the specified key is known to represent a point on the curve, {@code false} otherwise.\n     */\n    abstract boolean contains(Key key);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractEcJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.Converters;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport java.math.BigInteger;\nimport java.security.Key;\nimport java.security.interfaces.ECKey;\nimport java.security.spec.EllipticCurve;\nimport java.util.Set;\n\nabstract class AbstractEcJwkFactory<K extends Key & ECKey, J extends Jwk<K>> extends AbstractFamilyJwkFactory<K, J> {\n\n    protected static ECCurve getCurveByJwaId(String jwaCurveId) {\n        ECCurve curve = ECCurve.findById(jwaCurveId);\n        if (curve == null) {\n            String msg = \"Unrecognized JWA EC curve id '\" + jwaCurveId + \"'\";\n            throw new UnsupportedKeyException(msg);\n        }\n        return curve;\n    }\n\n    /**\n     * https://tools.ietf.org/html/rfc7518#section-6.2.1.2 indicates that this algorithm logic is defined in\n     * http://www.secg.org/sec1-v2.pdf Section 2.3.5.\n     *\n     * @param curve      EllipticCurve\n     * @param coordinate EC point coordinate (e.g. x or y) on the {@code curve}\n     * @return A base64Url-encoded String representing the EC field element per the RFC format\n     */\n    // Algorithm defined in http://www.secg.org/sec1-v2.pdf Section 2.3.5\n    static String toOctetString(EllipticCurve curve, BigInteger coordinate) {\n        byte[] bytes = Converters.BIGINT_UBYTES.applyTo(coordinate);\n        int fieldSizeInBits = curve.getField().getFieldSize();\n        int mlen = Bytes.length(fieldSizeInBits);\n        bytes = Bytes.prepad(bytes, mlen);\n        return Encoders.BASE64URL.encode(bytes);\n    }\n\n    AbstractEcJwkFactory(Class<K> keyType, Set<Parameter<?>> params) {\n        super(DefaultEcPublicJwk.TYPE_VALUE, keyType, params);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractFamilyJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.KeyException;\n\nimport java.security.Key;\nimport java.security.KeyFactory;\nimport java.util.Set;\n\nabstract class AbstractFamilyJwkFactory<K extends Key, J extends Jwk<K>> implements FamilyJwkFactory<K, J> {\n\n    protected static <T> void put(JwkContext<?> ctx, Parameter<T> param, T value) {\n        ctx.put(param.getId(), param.applyTo(value));\n    }\n\n    private final String ktyValue;\n    private final Class<K> keyType;\n    private final Set<Parameter<?>> params;\n\n    AbstractFamilyJwkFactory(String ktyValue, Class<K> keyType, Set<Parameter<?>> params) {\n        this.ktyValue = Assert.hasText(ktyValue, \"keyType argument cannot be null or empty.\");\n        this.keyType = Assert.notNull(keyType, \"keyType class cannot be null.\");\n        this.params = Assert.notEmpty(params, \"Parameters collection cannot be null or empty.\");\n    }\n\n    @Override\n    public String getId() {\n        return this.ktyValue;\n    }\n\n    @Override\n    public boolean supports(Key key) {\n        return this.keyType.isInstance(key);\n    }\n\n    @Override\n    public JwkContext<K> newContext(JwkContext<?> src, K key) {\n        Assert.notNull(src, \"Source JwkContext cannot be null.\");\n        return key != null ?\n                new DefaultJwkContext<>(this.params, src, key) :\n                new DefaultJwkContext<K>(this.params, src, false);\n    }\n\n    @Override\n    public boolean supports(JwkContext<?> ctx) {\n        return supports(ctx.getKey()) || supportsKeyValues(ctx);\n    }\n\n    protected boolean supportsKeyValues(JwkContext<?> ctx) {\n        return this.ktyValue.equals(ctx.getType());\n    }\n\n    protected K generateKey(final JwkContext<K> ctx, final CheckedFunction<KeyFactory, K> fn) {\n        return generateKey(ctx, this.keyType, fn);\n    }\n\n    protected String getKeyFactoryJcaName(final JwkContext<?> ctx) {\n        String jcaName = KeysBridge.findAlgorithm(ctx.getKey());\n        return Strings.hasText(jcaName) ? jcaName : getId();\n    }\n\n    protected <T extends Key> T generateKey(final JwkContext<?> ctx, final Class<T> type, final CheckedFunction<KeyFactory, T> fn) {\n        String jcaName = getKeyFactoryJcaName(ctx);\n        JcaTemplate template = new JcaTemplate(jcaName, ctx.getProvider(), ctx.getRandom());\n        return template.withKeyFactory(new CheckedFunction<KeyFactory, T>() {\n            @Override\n            public T apply(KeyFactory instance) {\n                try {\n                    return fn.apply(instance);\n                } catch (KeyException keyException) {\n                    throw keyException; // propagate\n                } catch (Exception e) {\n                    String msg = \"Unable to create \" + type.getSimpleName() + \" from JWK \" + ctx + \": \" + e.getMessage();\n                    throw new InvalidKeyException(msg, e);\n                }\n            }\n        });\n    }\n\n    @Override\n    public final J createJwk(JwkContext<K> ctx) {\n        Assert.notNull(ctx, \"JwkContext argument cannot be null.\");\n        if (!supports(ctx)) { //should be asserted by caller, but assert just in case:\n            String msg = \"Unsupported JwkContext.\";\n            throw new IllegalArgumentException(msg);\n        }\n        K key = ctx.getKey();\n        if (key != null) {\n            ctx.setType(this.ktyValue);\n            return createJwkFromKey(ctx);\n        } else {\n            return createJwkFromValues(ctx);\n        }\n    }\n\n    //when called, ctx.getKey() is guaranteed to be non-null\n    protected abstract J createJwkFromKey(JwkContext<K> ctx);\n\n    //when called ctx.getType() is guaranteed to equal this.ktyValue\n    protected abstract J createJwkFromValues(JwkContext<K> ctx);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.lang.Nameable;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.lang.Supplier;\nimport io.jsonwebtoken.security.HashAlgorithm;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.JwkThumbprint;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyOperation;\n\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.Iterator;\nimport java.util.List;\nimport java.util.Map;\nimport java.util.Set;\n\npublic abstract class AbstractJwk<K extends Key> implements Jwk<K>, ParameterReadable, Nameable {\n\n    static final Parameter<String> ALG = Parameters.string(\"alg\", \"Algorithm\");\n    public static final Parameter<String> KID = Parameters.string(\"kid\", \"Key ID\");\n    static final Parameter<Set<KeyOperation>> KEY_OPS =\n            Parameters.builder(KeyOperation.class).setConverter(KeyOperationConverter.DEFAULT)\n                    .set().setId(\"key_ops\").setName(\"Key Operations\").build();\n    static final Parameter<String> KTY = Parameters.string(\"kty\", \"Key Type\");\n    static final Set<Parameter<?>> PARAMS = Collections.setOf(ALG, KID, KEY_OPS, KTY);\n    public static final String IMMUTABLE_MSG = \"JWKs are immutable and may not be modified.\";\n\n    protected final JwkContext<K> context;\n    private final List<Parameter<?>> THUMBPRINT_PARAMS;\n    private final int hashCode;\n\n    /**\n     * @param ctx              the backing JwkContext containing the JWK parameter values.\n     * @param thumbprintParams the required parameters to include in the JWK Thumbprint canonical JSON representation,\n     *                         sorted in lexicographic order as defined by\n     *                         <a href=\"https://www.rfc-editor.org/rfc/rfc7638#section-3.2\">RFC 7638, Section 3.2</a>.\n     */\n    AbstractJwk(JwkContext<K> ctx, List<Parameter<?>> thumbprintParams) {\n        this.context = Assert.notNull(ctx, \"JwkContext cannot be null.\");\n        Assert.isTrue(!ctx.isEmpty(), \"JwkContext cannot be empty.\");\n        Assert.hasText(ctx.getType(), \"JwkContext type cannot be null or empty.\");\n        Assert.notNull(ctx.getKey(), \"JwkContext key cannot be null.\");\n        this.THUMBPRINT_PARAMS = Assert.notEmpty(thumbprintParams, \"JWK Thumbprint parameters cannot be null or empty.\");\n        HashAlgorithm idThumbprintAlg = ctx.getIdThumbprintAlgorithm();\n        if (!Strings.hasText(getId()) && idThumbprintAlg != null) {\n            JwkThumbprint thumbprint = thumbprint(idThumbprintAlg);\n            String kid = thumbprint.toString();\n            ctx.setId(kid);\n        }\n        this.hashCode = computeHashCode();\n    }\n\n    /**\n     * Compute and return the JWK hashCode.  As JWKs are immutable, this value will be cached as a final constant\n     * upon JWK instantiation. This uses the JWK's thumbprint parameters during computation, but differs from\n     * JwkThumbprint calculation in two ways:\n     * <ol>\n     *     <li>JwkThumbprints use a MessageDigest calculation, which is unnecessary overhead for a hashcode</li>\n     *     <li>The hashCode calculation uses each parameter's idiomatic (Java) object value instead of the\n     *     JwkThumbprint-required canonical (String) value.</li>\n     * </ol>\n     *\n     * @return the JWK hashcode\n     */\n    private int computeHashCode() {\n        List<Object> list = new ArrayList<>(this.THUMBPRINT_PARAMS.size() + 1 /* possible discriminator */);\n        // So we don't leak information about the private key value, we need a discriminator to ensure that\n        // public and private key hashCodes are not identical (in case both JWKs need to be in the same hash set).\n        // So we add a discriminator String to the list of values that are used during hashCode calculation\n        Key key = Assert.notNull(toKey(), \"JWK toKey() value cannot be null.\");\n        if (key instanceof PublicKey) {\n            list.add(\"Public\");\n        } else if (key instanceof PrivateKey) {\n            list.add(\"Private\");\n        }\n        for (Parameter<?> param : this.THUMBPRINT_PARAMS) {\n            // Unlike thumbprint calculation, we get the idiomatic (Java) value, not canonical (String) value\n            // (We could have used either actually, but the idiomatic value hashCode calculation is probably\n            // faster).\n            Object val = Assert.notNull(get(param), \"computeHashCode: Parameter idiomatic value cannot be null.\");\n            list.add(val);\n        }\n        return Objects.nullSafeHashCode(list.toArray());\n    }\n\n    private String getRequiredThumbprintValue(Parameter<?> param) {\n        Object value = get(param.getId());\n        if (value instanceof Supplier) {\n            value = ((Supplier<?>) value).get();\n        }\n        return Assert.isInstanceOf(String.class, value, \"Parameter canonical value is not a String.\");\n    }\n\n    /**\n     * Returns the JWK's canonically ordered JSON for JWK thumbprint computation as defined by\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7638#section-3.2\">RFC 7638, Section 3.2</a>.\n     *\n     * @return the JWK's canonically ordered JSON for JWK thumbprint computation.\n     */\n    private String toThumbprintJson() {\n        StringBuilder sb = new StringBuilder().append('{');\n        Iterator<Parameter<?>> i = this.THUMBPRINT_PARAMS.iterator();\n        while (i.hasNext()) {\n            Parameter<?> param = i.next();\n            String value = getRequiredThumbprintValue(param);\n            sb.append('\"').append(param.getId()).append(\"\\\":\\\"\").append(value).append('\"');\n            if (i.hasNext()) {\n                sb.append(\",\");\n            }\n        }\n        sb.append('}');\n        return sb.toString();\n    }\n\n    @Override\n    public JwkThumbprint thumbprint() {\n        return thumbprint(Jwks.HASH.SHA256);\n    }\n\n    @Override\n    public JwkThumbprint thumbprint(final HashAlgorithm alg) {\n        String json = toThumbprintJson();\n        Assert.hasText(json, \"Canonical JWK Thumbprint JSON cannot be null or empty.\");\n        byte[] bytes = json.getBytes(StandardCharsets.UTF_8); // https://www.rfc-editor.org/rfc/rfc7638#section-3 #2\n        InputStream in = Streams.of(bytes);\n        byte[] digest = alg.digest(new DefaultRequest<>(in, this.context.getProvider(), this.context.getRandom()));\n        return new DefaultJwkThumbprint(digest, alg);\n    }\n\n    @Override\n    public String getType() {\n        return this.context.getType();\n    }\n\n    @Override\n    public String getName() {\n        return this.context.getName();\n    }\n\n    @Override\n    public Set<KeyOperation> getOperations() {\n        return Collections.immutable(this.context.getOperations());\n    }\n\n    @Override\n    public String getAlgorithm() {\n        return this.context.getAlgorithm();\n    }\n\n    @Override\n    public String getId() {\n        return this.context.getId();\n    }\n\n    @Override\n    public K toKey() {\n        return this.context.getKey();\n    }\n\n    @Override\n    public int size() {\n        return this.context.size();\n    }\n\n    @Override\n    public boolean isEmpty() {\n        return this.context.isEmpty();\n    }\n\n    @Override\n    public boolean containsKey(Object key) {\n        return this.context.containsKey(key);\n    }\n\n    @Override\n    public boolean containsValue(Object value) {\n        return this.context.containsValue(value);\n    }\n\n    @Override\n    public Object get(Object key) {\n        Object val = this.context.get(key);\n        if (val instanceof Map) {\n            return Collections.immutable((Map<?, ?>) val);\n        } else if (val instanceof Collection) {\n            return Collections.immutable((Collection<?>) val);\n        } else if (Objects.isArray(val)) {\n            return Arrays.copy(val);\n        } else {\n            return val;\n        }\n    }\n\n    @Override\n    public <T> T get(Parameter<T> param) {\n        return this.context.get(param);\n    }\n\n    @Override\n    public Set<String> keySet() {\n        return Collections.immutable(this.context.keySet());\n    }\n\n    @Override\n    public Collection<Object> values() {\n        return Collections.immutable(this.context.values());\n    }\n\n    @Override\n    public Set<Entry<String, Object>> entrySet() {\n        return Collections.immutable(this.context.entrySet());\n    }\n\n    private static Object immutable() {\n        throw new UnsupportedOperationException(IMMUTABLE_MSG);\n    }\n\n    @Override\n    public Object put(String s, Object o) {\n        return immutable();\n    }\n\n    @Override\n    public Object remove(Object o) {\n        return immutable();\n    }\n\n    @Override\n    public void putAll(Map<? extends String, ?> m) {\n        immutable();\n    }\n\n    @Override\n    public void clear() {\n        immutable();\n    }\n\n    @Override\n    public String toString() {\n        return this.context.toString();\n    }\n\n    @Override\n    public final int hashCode() {\n        return this.hashCode;\n    }\n\n    @Override\n    public final boolean equals(Object obj) {\n        if (obj == this) return true;\n        if (obj instanceof Jwk<?>) {\n            Jwk<?> other = (Jwk<?>) obj;\n            // this.getType() guaranteed non-null in constructor:\n            return getType().equals(other.getType()) && equals(other);\n        }\n        return false;\n    }\n\n    protected abstract boolean equals(Jwk<?> jwk);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.DefaultNestedCollection;\nimport io.jsonwebtoken.impl.lang.DelegatingMapMutator;\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.NestedCollection;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.security.HashAlgorithm;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.JwkBuilder;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyOperation;\nimport io.jsonwebtoken.security.KeyOperationPolicy;\nimport io.jsonwebtoken.security.MalformedKeyException;\nimport io.jsonwebtoken.security.SecretJwk;\nimport io.jsonwebtoken.security.SecretJwkBuilder;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.Provider;\nimport java.security.SecureRandom;\nimport java.util.Collection;\nimport java.util.Set;\n\nabstract class AbstractJwkBuilder<K extends Key, J extends Jwk<K>, T extends JwkBuilder<K, J, T>>\n        extends DelegatingMapMutator<String, Object, JwkContext<K>, T>\n        implements JwkBuilder<K, J, T> {\n\n    protected final JwkFactory<K, J> jwkFactory;\n\n    static final KeyOperationPolicy DEFAULT_OPERATION_POLICY = Jwks.OP.policy().build();\n\n    protected KeyOperationPolicy opsPolicy = DEFAULT_OPERATION_POLICY; // default\n\n    @SuppressWarnings(\"unchecked\")\n    protected AbstractJwkBuilder(JwkContext<K> jwkContext) {\n        this(jwkContext, (JwkFactory<K, J>) DispatchingJwkFactory.DEFAULT_INSTANCE);\n    }\n\n    // visible for testing\n    protected AbstractJwkBuilder(JwkContext<K> context, JwkFactory<K, J> factory) {\n        super(context);\n        this.jwkFactory = Assert.notNull(factory, \"JwkFactory cannot be null.\");\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    protected <A extends Key> JwkContext<A> newContext(A key) {\n        return (JwkContext<A>) this.jwkFactory.newContext(this.DELEGATE, (K) key);\n    }\n\n    @Override\n    public T provider(Provider provider) {\n        this.DELEGATE.setProvider(provider);\n        return self();\n    }\n\n    @Override\n    public T random(SecureRandom random) {\n        this.DELEGATE.setRandom(random);\n        return self();\n    }\n\n    @Override\n    public T algorithm(String alg) {\n        Assert.hasText(alg, \"Algorithm cannot be null or empty.\");\n        this.DELEGATE.setAlgorithm(alg);\n        return self();\n    }\n\n    @Override\n    public T id(String id) {\n        Assert.hasText(id, \"Id cannot be null or empty.\");\n        this.DELEGATE.setIdThumbprintAlgorithm(null); //clear out any previously set value\n        this.DELEGATE.setId(id);\n        return self();\n    }\n\n    @Override\n    public T idFromThumbprint() {\n        return idFromThumbprint(Jwks.HASH.SHA256);\n    }\n\n    @Override\n    public T idFromThumbprint(HashAlgorithm alg) {\n        Assert.notNull(alg, \"Thumbprint HashAlgorithm cannot be null.\");\n        Assert.notNull(alg.getId(), \"Thumbprint HashAlgorithm ID cannot be null.\");\n        this.DELEGATE.setId(null); // clear out any previous value\n        this.DELEGATE.setIdThumbprintAlgorithm(alg);\n        return self();\n    }\n\n    @Override\n    public NestedCollection<KeyOperation, T> operations() {\n        return new DefaultNestedCollection<KeyOperation, T>(self(), this.DELEGATE.getOperations()) {\n            @Override\n            protected void changed() {\n                Collection<? extends KeyOperation> c = getCollection();\n                opsPolicy.validate(c);\n                DELEGATE.setOperations(c);\n            }\n        };\n    }\n\n    @Override\n    public T operationPolicy(KeyOperationPolicy policy) throws IllegalArgumentException {\n        Assert.notNull(policy, \"Policy cannot be null.\");\n        Collection<KeyOperation> ops = policy.getOperations();\n        Assert.notEmpty(ops, \"Policy operations cannot be null or empty.\");\n        this.opsPolicy = policy;\n\n        // update the JWK internal param to enable the policy's values:\n        Registry<String, KeyOperation> registry = new IdRegistry<>(\"JSON Web Key Operation\", ops);\n        Parameter<Set<KeyOperation>> param = Parameters.builder(KeyOperation.class)\n                .setConverter(new KeyOperationConverter(registry)).set()\n                .setId(AbstractJwk.KEY_OPS.getId())\n                .setName(AbstractJwk.KEY_OPS.getName())\n                .build();\n        setDelegate(this.DELEGATE.parameter(param));\n        return self();\n    }\n\n    @Override\n    public J build() {\n\n        //should always exist as there isn't a way to set it outside the constructor:\n        Assert.stateNotNull(this.DELEGATE, \"JwkContext should always be non-null\");\n\n        K key = this.DELEGATE.getKey();\n        if (key == null && isEmpty()) {\n            String msg = \"A \" + Key.class.getName() + \" or one or more name/value pairs must be provided to create a JWK.\";\n            throw new IllegalStateException(msg);\n        }\n\n        try {\n            this.opsPolicy.validate(this.DELEGATE.get(AbstractJwk.KEY_OPS));\n            return jwkFactory.createJwk(this.DELEGATE);\n        } catch (IllegalArgumentException iae) {\n            //if we get an IAE, it means the builder state wasn't configured enough in order to create\n            String msg = \"Unable to create JWK: \" + iae.getMessage();\n            throw new MalformedKeyException(msg, iae);\n        }\n    }\n\n    static class DefaultSecretJwkBuilder extends AbstractJwkBuilder<SecretKey, SecretJwk, SecretJwkBuilder>\n            implements SecretJwkBuilder {\n        public DefaultSecretJwkBuilder(JwkContext<SecretKey> ctx) {\n            super(ctx);\n            // assign a standard algorithm if possible:\n            Key key = Assert.notNull(ctx.getKey(), \"SecretKey cannot be null.\");\n            DefaultMacAlgorithm mac = DefaultMacAlgorithm.findByKey(key);\n            if (mac != null) {\n                algorithm(mac.getId());\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractJwkParserBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.AbstractParserBuilder;\nimport io.jsonwebtoken.io.ParserBuilder;\nimport io.jsonwebtoken.security.KeyOperationPolicied;\nimport io.jsonwebtoken.security.KeyOperationPolicy;\n\nabstract class AbstractJwkParserBuilder<T, B extends ParserBuilder<T, B> & KeyOperationPolicied<B>>\n        extends AbstractParserBuilder<T, B> implements KeyOperationPolicied<B> {\n\n    protected KeyOperationPolicy operationPolicy = AbstractJwkBuilder.DEFAULT_OPERATION_POLICY;\n\n    @Override\n    public B operationPolicy(KeyOperationPolicy policy) throws IllegalArgumentException {\n        this.operationPolicy = policy;\n        return self();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractPrivateJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.KeyPair;\nimport io.jsonwebtoken.security.PrivateJwk;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.util.List;\n\nabstract class AbstractPrivateJwk<K extends PrivateKey, L extends PublicKey, M extends PublicJwk<L>> extends AbstractAsymmetricJwk<K> implements PrivateJwk<K, L, M> {\n\n    private final M publicJwk;\n    private final KeyPair<L, K> keyPair;\n\n    AbstractPrivateJwk(JwkContext<K> ctx, List<Parameter<?>> thumbprintParams, M pubJwk) {\n        super(ctx, thumbprintParams);\n        this.publicJwk = Assert.notNull(pubJwk, \"PublicJwk instance cannot be null.\");\n        L publicKey = Assert.notNull(pubJwk.toKey(), \"PublicJwk key instance cannot be null.\");\n        this.context.setPublicKey(publicKey);\n        this.keyPair = new DefaultKeyPair<>(publicKey, toKey());\n    }\n\n    @Override\n    public M toPublicJwk() {\n        return this.publicJwk;\n    }\n\n    @Override\n    public KeyPair<L, K> toKeyPair() {\n        return this.keyPair;\n    }\n\n    @Override\n    protected final boolean equals(Jwk<?> jwk) {\n        return jwk instanceof PrivateJwk && equals((PrivateJwk<?, ?, ?>) jwk);\n    }\n\n    protected abstract boolean equals(PrivateJwk<?, ?, ?> jwk);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractPublicJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.security.PublicKey;\nimport java.util.List;\n\nabstract class AbstractPublicJwk<K extends PublicKey> extends AbstractAsymmetricJwk<K> implements PublicJwk<K> {\n    AbstractPublicJwk(JwkContext<K> ctx, List<Parameter<?>> thumbprintParams) {\n        super(ctx, thumbprintParams);\n    }\n\n    @Override\n    protected final boolean equals(Jwk<?> jwk) {\n        return jwk instanceof PublicJwk && equals((PublicJwk<?>) jwk);\n    }\n\n    protected abstract boolean equals(PublicJwk<?> jwk);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractSecureDigestAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.KeyException;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SecurityException;\nimport io.jsonwebtoken.security.SignatureException;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\n\nimport java.io.InputStream;\nimport java.security.Key;\n\nabstract class AbstractSecureDigestAlgorithm<S extends Key, V extends Key> extends CryptoAlgorithm implements SecureDigestAlgorithm<S, V> {\n\n    AbstractSecureDigestAlgorithm(String id, String jcaName) {\n        super(id, jcaName);\n    }\n\n    protected static String keyType(boolean signing) {\n        return signing ? \"signing\" : \"verification\";\n    }\n\n    protected abstract void validateKey(Key key, boolean signing);\n\n    @Override\n    public final byte[] digest(SecureRequest<InputStream, S> request) throws SecurityException {\n        Assert.notNull(request, \"Request cannot be null.\");\n        final S key = Assert.notNull(request.getKey(), \"Signing key cannot be null.\");\n        Assert.notNull(request.getPayload(), \"Request content cannot be null.\");\n        try {\n            validateKey(key, true);\n            return doDigest(request);\n        } catch (SignatureException | KeyException e) {\n            throw e; //propagate\n        } catch (Exception e) {\n            String msg = \"Unable to compute \" + getId() + \" signature with JCA algorithm '\" + getJcaName() + \"' \" +\n                    \"using key {\" + KeysBridge.toString(key) + \"}: \" + e.getMessage();\n            throw new SignatureException(msg, e);\n        }\n    }\n\n    protected abstract byte[] doDigest(SecureRequest<InputStream, S> request) throws Exception;\n\n    @Override\n    public final boolean verify(VerifySecureDigestRequest<V> request) throws SecurityException {\n        Assert.notNull(request, \"Request cannot be null.\");\n        final V key = Assert.notNull(request.getKey(), \"Verification key cannot be null.\");\n        Assert.notNull(request.getPayload(), \"Request content cannot be null or empty.\");\n        Assert.notEmpty(request.getDigest(), \"Request signature byte array cannot be null or empty.\");\n        try {\n            validateKey(key, false);\n            return doVerify(request);\n        } catch (SignatureException | KeyException e) {\n            throw e; //propagate\n        } catch (Exception e) {\n            String msg = \"Unable to verify \" + getId() + \" signature with JCA algorithm '\" + getJcaName() + \"' \" +\n                    \"using key {\" + KeysBridge.toString(key) + \"}: \" + e.getMessage();\n            throw new SignatureException(msg, e);\n        }\n    }\n\n    protected abstract boolean doVerify(VerifySecureDigestRequest<V> request);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractSecurityBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.security.SecurityBuilder;\n\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\nabstract class AbstractSecurityBuilder<T, B extends SecurityBuilder<T, B>> implements SecurityBuilder<T, B> {\n\n    protected Provider provider;\n    protected SecureRandom random;\n\n    @SuppressWarnings(\"unchecked\")\n    protected final B self() {\n        return (B) this;\n    }\n\n    @Override\n    public B provider(Provider provider) {\n        this.provider = provider;\n        return self();\n    }\n\n    @Override\n    public B random(SecureRandom random) {\n        this.random = random != null ? random : Randoms.secureRandom();\n        return self();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AbstractSignatureAlgorithm.java",
    "content": "/*\n * Copyright © 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SignatureAlgorithm;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\n\nimport java.io.InputStream;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.Signature;\nimport java.text.MessageFormat;\n\nabstract class AbstractSignatureAlgorithm extends AbstractSecureDigestAlgorithm<PrivateKey, PublicKey>\n        implements SignatureAlgorithm {\n\n    private static final String KEY_TYPE_MSG_PATTERN =\n            \"{0} {1} keys must be {2}s (implement {3}). Provided key type: {4}.\";\n\n    AbstractSignatureAlgorithm(String id, String jcaName) {\n        super(id, jcaName);\n    }\n\n    @Override\n    protected void validateKey(Key key, boolean signing) {\n        // https://github.com/jwtk/jjwt/issues/68:\n        Class<?> type = signing ? PrivateKey.class : PublicKey.class;\n        if (!type.isInstance(key)) {\n            String msg = MessageFormat.format(KEY_TYPE_MSG_PATTERN, getId(),\n                    keyType(signing), type.getSimpleName(), type.getName(), key.getClass().getName());\n            throw new InvalidKeyException(msg);\n        }\n    }\n\n    protected final byte[] sign(Signature sig, InputStream payload) throws Exception {\n        byte[] buf = new byte[2048];\n        int len = 0;\n        while (len != -1) {\n            len = payload.read(buf);\n            if (len > 0) sig.update(buf, 0, len);\n        }\n        return sig.sign();\n    }\n\n    @Override\n    protected byte[] doDigest(final SecureRequest<InputStream, PrivateKey> request) {\n        return jca(request).withSignature(new CheckedFunction<Signature, byte[]>() {\n            @Override\n            public byte[] apply(Signature sig) throws Exception {\n                sig.initSign(request.getKey());\n                return sign(sig, request.getPayload());\n            }\n        });\n    }\n\n    protected boolean verify(Signature sig, InputStream payload, byte[] digest) throws Exception {\n        byte[] buf = new byte[1024];\n        int len = 0;\n        while (len != -1) {\n            len = payload.read(buf);\n            if (len > 0) sig.update(buf, 0, len);\n        }\n        return sig.verify(digest);\n    }\n\n    @Override\n    protected boolean doVerify(final VerifySecureDigestRequest<PublicKey> request) {\n        return jca(request).withSignature(new CheckedFunction<Signature, Boolean>() {\n            @Override\n            public Boolean apply(Signature sig) throws Exception {\n                sig.initVerify(request.getKey());\n                return verify(sig, request.getPayload(), request.getDigest());\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AesAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.IvSupplier;\nimport io.jsonwebtoken.security.KeyBuilderSupplier;\nimport io.jsonwebtoken.security.KeyLengthSupplier;\nimport io.jsonwebtoken.security.Request;\nimport io.jsonwebtoken.security.SecretKeyBuilder;\nimport io.jsonwebtoken.security.WeakKeyException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.GCMParameterSpec;\nimport javax.crypto.spec.IvParameterSpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.security.SecureRandom;\nimport java.security.spec.AlgorithmParameterSpec;\n\n/**\n * @since 0.12.0\n */\nabstract class AesAlgorithm extends CryptoAlgorithm implements KeyBuilderSupplier<SecretKey, SecretKeyBuilder>, KeyLengthSupplier {\n\n    protected static final String KEY_ALG_NAME = \"AES\";\n    protected static final int BLOCK_SIZE = 128;\n    protected static final int BLOCK_BYTE_SIZE = BLOCK_SIZE / Byte.SIZE;\n    protected static final int GCM_IV_SIZE = 96; // https://tools.ietf.org/html/rfc7518#section-5.3\n    //protected static final int GCM_IV_BYTE_SIZE = GCM_IV_SIZE / Byte.SIZE;\n    protected static final String DECRYPT_NO_IV = \"This algorithm implementation rejects decryption \" +\n            \"requests that do not include initialization vectors. AES ciphertext without an IV is weak and \" +\n            \"susceptible to attack.\";\n\n    protected final int keyBitLength;\n    protected final int ivBitLength;\n    protected final int tagBitLength;\n    protected final boolean gcm;\n\n    /**\n     * Ensures {@code keyBitLength is a valid AES key length}\n     * @param keyBitLength the key length (in bits) to check\n     * @since 0.12.4\n     */\n    static void assertKeyBitLength(int keyBitLength) {\n        if (keyBitLength == 128 || keyBitLength == 192 || keyBitLength == 256) return; // valid\n        String msg = \"Invalid AES key length: \" + Bytes.bitsMsg(keyBitLength) + \". AES only supports \" +\n                \"128, 192, or 256 bit keys.\";\n        throw new IllegalArgumentException(msg);\n    }\n\n    static SecretKey keyFor(byte[] bytes) {\n        int bitlen = (int) Bytes.bitLength(bytes);\n        assertKeyBitLength(bitlen);\n        return new SecretKeySpec(bytes, KEY_ALG_NAME);\n    }\n\n    AesAlgorithm(String id, final String jcaTransformation, int keyBitLength) {\n        super(id, jcaTransformation);\n        assertKeyBitLength(keyBitLength);\n        this.keyBitLength = keyBitLength;\n        this.gcm = jcaTransformation.startsWith(\"AES/GCM\");\n        this.ivBitLength = jcaTransformation.equals(\"AESWrap\") ? 0 : (this.gcm ? GCM_IV_SIZE : BLOCK_SIZE);\n        // https://tools.ietf.org/html/rfc7518#section-5.2.3 through https://tools.ietf.org/html/rfc7518#section-5.3 :\n        this.tagBitLength = this.gcm ? BLOCK_SIZE : this.keyBitLength;\n    }\n\n    @Override\n    public int getKeyBitLength() {\n        return this.keyBitLength;\n    }\n\n    @Override\n    public SecretKeyBuilder key() {\n        return new DefaultSecretKeyBuilder(KEY_ALG_NAME, getKeyBitLength());\n    }\n\n    protected SecretKey assertKey(SecretKey key) {\n        Assert.notNull(key, \"Request key cannot be null.\");\n        validateLengthIfPossible(key);\n        return key;\n    }\n\n    private void validateLengthIfPossible(SecretKey key) {\n        validateLength(key, this.keyBitLength, false);\n    }\n\n    protected static String lengthMsg(String id, String type, int requiredLengthInBits, long actualLengthInBits) {\n        return \"The '\" + id + \"' algorithm requires \" + type + \" with a length of \" +\n                Bytes.bitsMsg(requiredLengthInBits) + \".  The provided key has a length of \" +\n                Bytes.bitsMsg(actualLengthInBits) + \".\";\n    }\n\n    protected byte[] validateLength(SecretKey key, int requiredBitLength, boolean propagate) {\n        byte[] keyBytes;\n\n        try {\n            keyBytes = key.getEncoded();\n        } catch (RuntimeException re) {\n            if (propagate) {\n                throw re;\n            }\n            //can't get the bytes to validate, e.g. hardware security module or later Android, so just return:\n            return null;\n        }\n        long keyBitLength = Bytes.bitLength(keyBytes);\n        if (keyBitLength < requiredBitLength) {\n            throw new WeakKeyException(lengthMsg(getId(), \"keys\", requiredBitLength, keyBitLength));\n        }\n\n        return keyBytes;\n    }\n\n    protected byte[] assertBytes(byte[] bytes, String type, int requiredBitLen) {\n        long bitLen = Bytes.bitLength(bytes);\n        if (requiredBitLen != bitLen) {\n            String msg = lengthMsg(getId(), type, requiredBitLen, bitLen);\n            throw new IllegalArgumentException(msg);\n        }\n        return bytes;\n    }\n\n    byte[] assertIvLength(final byte[] iv) {\n        return assertBytes(iv, \"initialization vectors\", this.ivBitLength);\n    }\n\n    byte[] assertTag(byte[] tag) {\n        return assertBytes(tag, \"authentication tags\", this.tagBitLength);\n    }\n\n    byte[] assertDecryptionIv(IvSupplier src) throws IllegalArgumentException {\n        byte[] iv = src.getIv();\n        Assert.notEmpty(iv, DECRYPT_NO_IV);\n        return assertIvLength(iv);\n    }\n\n    protected byte[] ensureInitializationVector(Request<?> request) {\n        byte[] iv = null;\n        if (request instanceof IvSupplier) {\n            iv = Arrays.clean(((IvSupplier) request).getIv());\n        }\n        int ivByteLength = this.ivBitLength / Byte.SIZE;\n        if (iv == null || iv.length == 0) {\n            iv = new byte[ivByteLength];\n            SecureRandom random = ensureSecureRandom(request);\n            random.nextBytes(iv);\n        } else {\n            assertIvLength(iv);\n        }\n        return iv;\n    }\n\n    protected AlgorithmParameterSpec getIvSpec(byte[] iv) {\n        Assert.notEmpty(iv, \"Initialization Vector byte array cannot be null or empty.\");\n        return this.gcm ? new GCMParameterSpec(BLOCK_SIZE, iv) : new IvParameterSpec(iv);\n    }\n\n    protected void withCipher(Cipher cipher, InputStream in, OutputStream out) throws Exception {\n        byte[] last = withCipher(cipher, in, null, out);\n        out.write(last); // no AAD, so no tag, so we can just append\n    }\n\n    private void updateAAD(Cipher cipher, InputStream aad) throws Exception {\n        if (aad == null) return;\n        byte[] buf = new byte[2048];\n        int len = 0;\n        while (len != -1) {\n            len = aad.read(buf);\n            if (len > 0) {\n                cipher.updateAAD(buf, 0, len);\n            }\n        }\n    }\n\n    protected byte[] withCipher(Cipher cipher, InputStream in, InputStream aad, OutputStream out) throws Exception {\n        updateAAD(cipher, aad); // no-op if aad is null\n        byte[] buf = new byte[2048];\n        try {\n            int len = 0;\n            while (len != -1) {\n                len = in.read(buf);\n                if (len > 0) {\n                    byte[] enc = cipher.update(buf, 0, len);\n                    Streams.write(out, enc, \"Unable to write Cipher output to OutputStream\");\n                }\n            }\n            return cipher.doFinal();\n        } finally {\n            Bytes.clear(buf);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.impl.DefaultJweHeader;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.KeyResult;\nimport io.jsonwebtoken.security.SecretKeyAlgorithm;\nimport io.jsonwebtoken.security.SecurityException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.spec.AlgorithmParameterSpec;\n\n/**\n * @since 0.12.0\n */\npublic class AesGcmKeyAlgorithm extends AesAlgorithm implements SecretKeyAlgorithm {\n\n    public static final String TRANSFORMATION = \"AES/GCM/NoPadding\";\n\n    public AesGcmKeyAlgorithm(int keyLen) {\n        super(\"A\" + keyLen + \"GCMKW\", TRANSFORMATION, keyLen);\n    }\n\n    @Override\n    public KeyResult getEncryptionKey(final KeyRequest<SecretKey> request) throws SecurityException {\n\n        Assert.notNull(request, \"request cannot be null.\");\n        final JweHeader header = Assert.notNull(request.getHeader(), \"Request JweHeader cannot be null.\");\n        final SecretKey kek = assertKey(request.getPayload());\n        final SecretKey cek = generateCek(request);\n        final byte[] iv = ensureInitializationVector(request);\n        final AlgorithmParameterSpec ivSpec = getIvSpec(iv);\n\n        byte[] taggedCiphertext = jca(request).withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            public byte[] apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.WRAP_MODE, kek, ivSpec);\n                return cipher.wrap(cek);\n            }\n        });\n\n        int tagByteLength = this.tagBitLength / Byte.SIZE;\n        // When using GCM mode, the JDK appends the authentication tag to the ciphertext, so let's extract it:\n        int ciphertextLength = taggedCiphertext.length - tagByteLength;\n        byte[] ciphertext = new byte[ciphertextLength];\n        System.arraycopy(taggedCiphertext, 0, ciphertext, 0, ciphertextLength);\n        byte[] tag = new byte[tagByteLength];\n        System.arraycopy(taggedCiphertext, ciphertextLength, tag, 0, tagByteLength);\n\n        String encodedIv = Encoders.BASE64URL.encode(iv);\n        String encodedTag = Encoders.BASE64URL.encode(tag);\n        header.put(DefaultJweHeader.IV.getId(), encodedIv);\n        header.put(DefaultJweHeader.TAG.getId(), encodedTag);\n\n        return new DefaultKeyResult(cek, ciphertext);\n    }\n\n    @Override\n    public SecretKey getDecryptionKey(DecryptionKeyRequest<SecretKey> request) throws SecurityException {\n        Assert.notNull(request, \"request cannot be null.\");\n        final SecretKey kek = assertKey(request.getKey());\n        final byte[] cekBytes = Assert.notEmpty(request.getPayload(), \"Decryption request content (ciphertext) cannot be null or empty.\");\n        final JweHeader header = Assert.notNull(request.getHeader(), \"Request JweHeader cannot be null.\");\n        ParameterReadable frHeader = Assert.isInstanceOf(ParameterReadable.class, header, \"Header must implement ParameterReadable.\");\n        final ParameterReadable reader = new RequiredParameterReader(frHeader);\n        final byte[] tag = reader.get(DefaultJweHeader.TAG);\n        final byte[] iv = reader.get(DefaultJweHeader.IV);\n        final AlgorithmParameterSpec ivSpec = getIvSpec(iv);\n\n        //for tagged GCM, the JCA spec requires that the tag be appended to the end of the ciphertext byte array:\n        final byte[] taggedCiphertext = Bytes.concat(cekBytes, tag);\n\n        return jca(request).withCipher(new CheckedFunction<Cipher, SecretKey>() {\n            @Override\n            public SecretKey apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.UNWRAP_MODE, kek, ivSpec);\n                Key key = cipher.unwrap(taggedCiphertext, KEY_ALG_NAME, Cipher.SECRET_KEY);\n                Assert.state(key instanceof SecretKey, \"cipher.unwrap must produce a SecretKey instance.\");\n                return (SecretKey) key;\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AesWrapKeyAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.KeyResult;\nimport io.jsonwebtoken.security.SecretKeyAlgorithm;\nimport io.jsonwebtoken.security.SecurityException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport java.security.Key;\n\n/**\n * @since 0.12.0\n */\npublic class AesWrapKeyAlgorithm extends AesAlgorithm implements SecretKeyAlgorithm {\n\n    private static final String TRANSFORMATION = \"AESWrap\";\n\n    public AesWrapKeyAlgorithm(int keyLen) {\n        super(\"A\" + keyLen + \"KW\", TRANSFORMATION, keyLen);\n    }\n\n    @Override\n    public KeyResult getEncryptionKey(final KeyRequest<SecretKey> request) throws SecurityException {\n        Assert.notNull(request, \"request cannot be null.\");\n        final SecretKey kek = assertKey(request.getPayload());\n        final SecretKey cek = generateCek(request);\n\n        byte[] ciphertext = jca(request).withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            public byte[] apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.WRAP_MODE, kek);\n                return cipher.wrap(cek);\n            }\n        });\n\n        return new DefaultKeyResult(cek, ciphertext);\n    }\n\n    @Override\n    public SecretKey getDecryptionKey(DecryptionKeyRequest<SecretKey> request) throws SecurityException {\n        Assert.notNull(request, \"request cannot be null.\");\n        final SecretKey kek = assertKey(request.getKey());\n        final byte[] cekBytes = Assert.notEmpty(request.getPayload(), \"Request content (encrypted key) cannot be null or empty.\");\n\n        return jca(request).withCipher(new CheckedFunction<Cipher, SecretKey>() {\n            @Override\n            public SecretKey apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.UNWRAP_MODE, kek);\n                Key key = cipher.unwrap(cekBytes, KEY_ALG_NAME, Cipher.SECRET_KEY);\n                Assert.state(key instanceof SecretKey, \"Cipher unwrap must return a SecretKey instance.\");\n                return (SecretKey) key;\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/AsymmetricJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.Jwk;\n\nimport java.security.Key;\n\nclass AsymmetricJwkFactory implements FamilyJwkFactory<Key, Jwk<Key>> {\n\n    private final String id;\n    private final FamilyJwkFactory<Key, Jwk<Key>> publicFactory;\n    private final FamilyJwkFactory<Key, Jwk<Key>> privateFactory;\n\n    @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n    AsymmetricJwkFactory(FamilyJwkFactory publicFactory, FamilyJwkFactory privateFactory) {\n        this.publicFactory = (FamilyJwkFactory<Key, Jwk<Key>>) Assert.notNull(publicFactory, \"publicFactory cannot be null.\");\n        this.privateFactory = (FamilyJwkFactory<Key, Jwk<Key>>) Assert.notNull(privateFactory, \"privateFactory cannot be null.\");\n        this.id = Assert.notNull(publicFactory.getId(), \"publicFactory id cannot be null or empty.\");\n        Assert.isTrue(this.id.equals(privateFactory.getId()), \"privateFactory id must equal publicFactory id\");\n    }\n\n    @Override\n    public String getId() {\n        return this.id;\n    }\n\n    @Override\n    public boolean supports(JwkContext<?> ctx) {\n        return ctx != null &&\n                (this.id.equals(ctx.getType()) || privateFactory.supports(ctx) || publicFactory.supports(ctx));\n    }\n\n    @Override\n    public boolean supports(Key key) {\n        return key != null && (privateFactory.supports(key) || publicFactory.supports(key));\n    }\n\n    @Override\n    public JwkContext<Key> newContext(JwkContext<?> src, Key key) {\n        return (privateFactory.supports(key) || privateFactory.supports(src)) ?\n                privateFactory.newContext(src, key) :\n                publicFactory.newContext(src, key);\n    }\n\n    @Override\n    public Jwk<Key> createJwk(JwkContext<Key> ctx) {\n        if (privateFactory.supports(ctx)) {\n            return this.privateFactory.createJwk(ctx);\n        }\n        return this.publicFactory.createJwk(ctx);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ConcatKDF.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.SecurityException;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.io.ByteArrayOutputStream;\nimport java.security.Key;\nimport java.security.MessageDigest;\n\nimport static io.jsonwebtoken.impl.lang.Bytes.*;\n\n/**\n * 'Clean room' implementation of the Concat KDF algorithm based solely on\n * <a href=\"https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf\">NIST.800-56A</a>,\n * Section <code>5.8.1.1</code>.  Call the {@link #deriveKey(byte[], long, byte[]) deriveKey} method.\n */\nfinal class ConcatKDF extends CryptoAlgorithm {\n\n    private static final long MAX_REP_COUNT = 0xFFFFFFFFL;\n    private static final long MAX_HASH_INPUT_BYTE_LENGTH = Integer.MAX_VALUE; //no Java byte arrays bigger than this\n    private static final long MAX_HASH_INPUT_BIT_LENGTH = MAX_HASH_INPUT_BYTE_LENGTH * Byte.SIZE;\n\n    private final int hashBitLength;\n\n    /**\n     * NIST.SP.800-56Ar2.pdf, Section 5.8.1.1, Input requirement #2 says that the maximum bit length of the\n     * derived key cannot be more than this:\n     * <code><pre>\n     *     hashBitLength * (2^32 - 1)\n     * </pre></code>\n     * However, this number is always greater than Integer.MAX_VALUE * Byte.SIZE, which is the maximum number of\n     * bits that can be represented in a Java byte array.  So our implementation must be limited to that size\n     * regardless of what the spec allows:\n     */\n    private static final long MAX_DERIVED_KEY_BIT_LENGTH = (long) Integer.MAX_VALUE * (long) Byte.SIZE;\n\n    ConcatKDF(String jcaName) {\n        super(\"ConcatKDF\", jcaName);\n        int hashByteLength = jca().withMessageDigest(new CheckedFunction<MessageDigest, Integer>() {\n            @Override\n            public Integer apply(MessageDigest instance) {\n                return instance.getDigestLength();\n            }\n        });\n        this.hashBitLength = hashByteLength * Byte.SIZE;\n        Assert.state(this.hashBitLength > 0, \"MessageDigest length must be a positive value.\");\n    }\n\n    /**\n     * 'Clean room' implementation of the Concat KDF algorithm based solely on\n     * <a href=\"https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf\">NIST.800-56A</a>,\n     * Section <code>5.8.1.1</code>.\n     *\n     * @param Z                   shared secret key to use to seed the derived secret. Cannot be null or empty.\n     * @param derivedKeyBitLength the total number of <b>bits</b> <em>(not bytes)</em> required in the returned derived\n     *                            key.\n     * @param otherInfo           any additional party info to be associated with the derived key. May be null/empty.\n     * @return the derived key\n     * @throws UnsupportedKeyException if unable to obtain {@code sharedSecretKey}'s\n     *                                 {@link Key#getEncoded() encoded byte array}.\n     * @throws SecurityException       if unable to perform the necessary {@link MessageDigest} computations to\n     *                                 generate the derived key.\n     */\n    public SecretKey deriveKey(final byte[] Z, final long derivedKeyBitLength, final byte[] otherInfo)\n            throws UnsupportedKeyException, SecurityException {\n\n        // sharedSecretKey argument assertions:\n        Assert.notEmpty(Z, \"Z cannot be null or empty.\");\n\n        // derivedKeyBitLength argument assertions:\n        Assert.isTrue(derivedKeyBitLength > 0, \"derivedKeyBitLength must be a positive integer.\");\n        if (derivedKeyBitLength > MAX_DERIVED_KEY_BIT_LENGTH) {\n            String msg = \"derivedKeyBitLength may not exceed \" + bitsMsg(MAX_DERIVED_KEY_BIT_LENGTH) +\n                    \". Specified size: \" + bitsMsg(derivedKeyBitLength) + \".\";\n            throw new IllegalArgumentException(msg);\n        }\n        final long derivedKeyByteLength = derivedKeyBitLength / Byte.SIZE;\n\n        final byte[] OtherInfo = otherInfo == null ? EMPTY : otherInfo;\n\n        // Section 5.8.1.1, Process step #1:\n        final double repsd = derivedKeyBitLength / (double) this.hashBitLength;\n        final long reps = (long) Math.ceil(repsd);\n        // If repsd didn't result in a whole number, the last derived key byte will be partially filled per\n        // Section 5.8.1.1, Process step #6:\n        final boolean kLastPartial = repsd != (double) reps;\n\n        // Section 5.8.1.1, Process step #2:\n        Assert.state(reps <= MAX_REP_COUNT, \"derivedKeyBitLength is too large.\");\n\n        // Section 5.8.1.1, Process step #3:\n        final byte[] counter = new byte[]{0, 0, 0, 1}; // same as 0x0001L, but no extra step to convert to byte[]\n\n        // Section 5.8.1.1, Process step #4:\n        long inputBitLength = bitLength(counter) + bitLength(Z) + bitLength(OtherInfo);\n        Assert.state(inputBitLength <= MAX_HASH_INPUT_BIT_LENGTH, \"Hash input is too large.\");\n\n        final ClearableByteArrayOutputStream stream = new ClearableByteArrayOutputStream((int) derivedKeyByteLength);\n        byte[] derivedKeyBytes = EMPTY;\n\n        try {\n            derivedKeyBytes = jca().withMessageDigest(new CheckedFunction<MessageDigest, byte[]>() {\n                @Override\n                public byte[] apply(MessageDigest md) throws Exception {\n\n                    // Section 5.8.1.1, Process step #5.  We depart from Java idioms here by starting iteration index at 1\n                    // (instead of 0) and continue to <= reps (instead of < reps) to match the NIST publication algorithm\n                    // notation convention (so variables like Ki and kLast below match the NIST definitions).\n                    for (long i = 1; i <= reps; i++) {\n\n                        // Section 5.8.1.1, Process step #5.1:\n                        md.update(counter);\n                        md.update(Z);\n                        md.update(OtherInfo);\n                        byte[] Ki = md.digest();\n\n                        // Section 5.8.1.1, Process step #5.2:\n                        increment(counter);\n\n                        // Section 5.8.1.1, Process step #6:\n                        if (i == reps && kLastPartial) {\n                            long leftmostBitLength = derivedKeyBitLength % hashBitLength;\n                            int leftmostByteLength = (int) (leftmostBitLength / Byte.SIZE);\n                            byte[] kLast = new byte[leftmostByteLength];\n                            System.arraycopy(Ki, 0, kLast, 0, kLast.length);\n                            Ki = kLast;\n                        }\n\n                        stream.write(Ki);\n                    }\n\n                    // Section 5.8.1.1, Process step #7:\n                    return stream.toByteArray();\n                }\n            });\n            return new SecretKeySpec(derivedKeyBytes, AesAlgorithm.KEY_ALG_NAME);\n        } finally {\n            // key cleanup\n            Bytes.clear(derivedKeyBytes); // SecretKeySpec clones this, so we can clear it out safely\n            Bytes.clear(counter);\n            stream.reset();\n            // we don't clear out 'Z', since that is the responsibility of the caller\n        }\n    }\n\n    /**\n     * Calling ByteArrayOutputStream.toByteArray returns a copy of the bytes, so this class allows us to completely\n     * zero-out the buffer upon reset (whereas BAOS just resets the position marker, leaving the bytes in tact)\n     */\n    private static class ClearableByteArrayOutputStream extends ByteArrayOutputStream {\n\n        public ClearableByteArrayOutputStream(int size) {\n            super(size);\n        }\n\n        @Override\n        public synchronized void reset() {\n            super.reset();\n            Bytes.clear(buf); // zero out internal buffer\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ConstantKeyLocator.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.Header;\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.JwsHeader;\nimport io.jsonwebtoken.LocatorAdapter;\nimport io.jsonwebtoken.impl.lang.Function;\n\nimport java.security.Key;\n\npublic class ConstantKeyLocator extends LocatorAdapter<Key> implements Function<Header, Key> {\n\n    private final Key jwsKey;\n    private final Key jweKey;\n\n    public ConstantKeyLocator(Key jwsKey, Key jweKey) {\n        this.jwsKey = jwsKey;\n        this.jweKey = jweKey;\n    }\n\n    @Override\n    protected Key locate(JwsHeader header) {\n        return this.jwsKey;\n    }\n\n    @Override\n    protected Key locate(JweHeader header) {\n        return this.jweKey;\n    }\n\n    @Override\n    public Key apply(Header header) {\n        return locate(header);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/CryptoAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.Request;\nimport io.jsonwebtoken.security.SecretKeyBuilder;\n\nimport javax.crypto.SecretKey;\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\n/**\n * @since 0.12.0\n */\nabstract class CryptoAlgorithm implements Identifiable {\n\n    private final String ID;\n\n    private final String jcaName;\n\n    CryptoAlgorithm(String id, String jcaName) {\n        Assert.hasText(id, \"id cannot be null or empty.\");\n        this.ID = id;\n        Assert.hasText(jcaName, \"jcaName cannot be null or empty.\");\n        this.jcaName = jcaName;\n    }\n\n    @Override\n    public String getId() {\n        return this.ID;\n    }\n\n    String getJcaName() {\n        return this.jcaName;\n    }\n\n    static SecureRandom ensureSecureRandom(Request<?> request) {\n        SecureRandom random = request != null ? request.getSecureRandom() : null;\n        return random != null ? random : Randoms.secureRandom();\n    }\n\n    protected JcaTemplate jca() {\n        return new JcaTemplate(getJcaName());\n    }\n\n    protected JcaTemplate jca(Request<?> request) {\n        Assert.notNull(request, \"request cannot be null.\");\n        String jcaName = Assert.hasText(getJcaName(request), \"Request jcaName cannot be null or empty.\");\n        Provider provider = request.getProvider();\n        SecureRandom random = ensureSecureRandom(request);\n        return new JcaTemplate(jcaName, provider, random);\n    }\n\n    protected String getJcaName(Request<?> request) {\n        return getJcaName();\n    }\n\n    protected SecretKey generateCek(KeyRequest<?> request) {\n        AeadAlgorithm enc = Assert.notNull(request.getEncryptionAlgorithm(), \"Request encryptionAlgorithm cannot be null.\");\n        SecretKeyBuilder builder = Assert.notNull(enc.key(), \"Request encryptionAlgorithm KeyBuilder cannot be null.\");\n        SecretKey key = builder.random(request.getSecureRandom()).build();\n        return Assert.notNull(key, \"Request encryptionAlgorithm SecretKeyBuilder cannot produce null keys.\");\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (this == obj) {\n            return true;\n        }\n        if (obj instanceof CryptoAlgorithm) {\n            CryptoAlgorithm other = (CryptoAlgorithm) obj;\n            return this.ID.equals(other.getId()) && this.jcaName.equals(other.getJcaName());\n        }\n        return false;\n    }\n\n    @Override\n    public int hashCode() {\n        int hash = 7;\n        hash = 31 * hash + ID.hashCode();\n        hash = 31 * hash + jcaName.hashCode();\n        return hash;\n    }\n\n    @Override\n    public String toString() {\n        return ID;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.security.AeadRequest;\nimport io.jsonwebtoken.security.IvSupplier;\n\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\n/**\n * @since 0.12.0\n */\npublic class DefaultAeadRequest extends DefaultSecureRequest<InputStream, SecretKey>\n        implements AeadRequest, IvSupplier {\n\n    private final byte[] IV;\n\n    private final InputStream AAD;\n\n    DefaultAeadRequest(InputStream payload, Provider provider, SecureRandom secureRandom,\n                       SecretKey key, InputStream aad, byte[] iv) {\n        super(payload, provider, secureRandom, key);\n        this.AAD = aad;\n        this.IV = iv;\n    }\n\n    public DefaultAeadRequest(InputStream payload, Provider provider, SecureRandom secureRandom,\n                              SecretKey key, InputStream aad) {\n        this(payload, provider, secureRandom, key, aad, null);\n    }\n\n    @Override\n    public InputStream getAssociatedData() {\n        return this.AAD;\n    }\n\n    @Override\n    public byte[] getIv() {\n        return this.IV;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultAeadResult.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AeadResult;\nimport io.jsonwebtoken.security.DigestSupplier;\nimport io.jsonwebtoken.security.IvSupplier;\n\nimport java.io.OutputStream;\n\npublic class DefaultAeadResult implements AeadResult, DigestSupplier, IvSupplier {\n\n    private final OutputStream out;\n    private byte[] tag;\n    private byte[] iv;\n\n    public DefaultAeadResult(OutputStream out) {\n        this.out = Assert.notNull(out, \"OutputStream cannot be null.\");\n    }\n\n    @Override\n    public OutputStream getOutputStream() {\n        return this.out;\n    }\n\n    @Override\n    public byte[] getDigest() {\n        return this.tag;\n    }\n\n    @Override\n    public AeadResult setTag(byte[] tag) {\n        this.tag = Assert.notEmpty(tag, \"Authentication Tag cannot be null or empty.\");\n        return this;\n    }\n\n    @Override\n    public AeadResult setIv(byte[] iv) {\n        this.iv = Assert.notEmpty(iv, \"Initialization Vector cannot be null or empty.\");\n        return this;\n    }\n\n    @Override\n    public byte[] getIv() {\n        return this.iv;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptAeadRequest.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.DecryptAeadRequest;\n\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\n\n/**\n * @since 0.12.0\n */\npublic class DefaultDecryptAeadRequest extends DefaultAeadRequest implements DecryptAeadRequest {\n\n    private final byte[] TAG;\n\n    public DefaultDecryptAeadRequest(InputStream payload, SecretKey key, InputStream aad, byte[] iv, byte[] tag) {\n        super(payload, null, null, key, aad,\n                Assert.notEmpty(iv, \"Initialization Vector cannot be null or empty.\"));\n        this.TAG = Assert.notEmpty(tag, \"AAD Authentication Tag cannot be null or empty.\");\n    }\n\n    @Override\n    public byte[] getDigest() {\n        return this.TAG;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDecryptionKeyRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\n\nimport java.security.Key;\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\npublic class DefaultDecryptionKeyRequest<K extends Key> extends DefaultKeyRequest<byte[]> implements DecryptionKeyRequest<K> {\n\n    private final K decryptionKey;\n\n    public DefaultDecryptionKeyRequest(byte[] encryptedCek, Provider provider, SecureRandom secureRandom,\n                                       JweHeader header, AeadAlgorithm encryptionAlgorithm, K decryptionKey) {\n        super(encryptedCek, provider, secureRandom, header, encryptionAlgorithm);\n        this.decryptionKey = Assert.notNull(decryptionKey, \"decryption key cannot be null.\");\n    }\n\n    @Override\n    protected void assertBytePayload(byte[] payload) {\n        Assert.notNull(payload, \"encrypted key bytes cannot be null (but may be empty.\");\n    }\n\n    @Override\n    public K getKey() {\n        return this.decryptionKey;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultDynamicJwkBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.DynamicJwkBuilder;\nimport io.jsonwebtoken.security.EcPrivateJwkBuilder;\nimport io.jsonwebtoken.security.EcPublicJwkBuilder;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.OctetPrivateJwkBuilder;\nimport io.jsonwebtoken.security.OctetPublicJwkBuilder;\nimport io.jsonwebtoken.security.PrivateJwkBuilder;\nimport io.jsonwebtoken.security.PublicJwkBuilder;\nimport io.jsonwebtoken.security.RsaPrivateJwkBuilder;\nimport io.jsonwebtoken.security.RsaPublicJwkBuilder;\nimport io.jsonwebtoken.security.SecretJwkBuilder;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.cert.X509Certificate;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.List;\n\n@SuppressWarnings(\"unused\") //used via reflection by io.jsonwebtoken.security.Jwks\npublic class DefaultDynamicJwkBuilder<K extends Key, J extends Jwk<K>>\n        extends AbstractJwkBuilder<K, J, DynamicJwkBuilder<K, J>> implements DynamicJwkBuilder<K, J> {\n\n    public DefaultDynamicJwkBuilder() {\n        this(new DefaultJwkContext<K>());\n    }\n\n    public DefaultDynamicJwkBuilder(JwkContext<K> ctx) {\n        super(ctx);\n    }\n\n    @Override\n    public SecretJwkBuilder key(SecretKey key) {\n        return new AbstractJwkBuilder.DefaultSecretJwkBuilder(newContext(key));\n    }\n\n    @Override\n    public RsaPublicJwkBuilder key(RSAPublicKey key) {\n        return new AbstractAsymmetricJwkBuilder.DefaultRsaPublicJwkBuilder(newContext(key));\n    }\n\n    @Override\n    public RsaPrivateJwkBuilder key(RSAPrivateKey key) {\n        return new AbstractAsymmetricJwkBuilder.DefaultRsaPrivateJwkBuilder(newContext(key));\n    }\n\n    @Override\n    public EcPublicJwkBuilder key(ECPublicKey key) {\n        return new AbstractAsymmetricJwkBuilder.DefaultEcPublicJwkBuilder(newContext(key));\n    }\n\n    @Override\n    public EcPrivateJwkBuilder key(ECPrivateKey key) {\n        return new AbstractAsymmetricJwkBuilder.DefaultEcPrivateJwkBuilder(newContext(key));\n    }\n\n    private static UnsupportedKeyException unsupportedKey(Key key, Exception e) {\n        String msg = \"There is no builder that supports specified key [\" + KeysBridge.toString(key) + \"].\";\n        return new UnsupportedKeyException(msg, e);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <A extends PublicKey, B extends PrivateKey> PublicJwkBuilder<A, B, ?, ?, ?, ?> key(A key) {\n        if (key instanceof RSAPublicKey) {\n            return (PublicJwkBuilder<A, B, ?, ?, ?, ?>) key((RSAPublicKey) key);\n        } else if (key instanceof ECPublicKey) {\n            return (PublicJwkBuilder<A, B, ?, ?, ?, ?>) key((ECPublicKey) key);\n        } else {\n            try {\n                return octetKey(key);\n            } catch (Exception e) {\n                throw unsupportedKey(key, e);\n            }\n        }\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <A extends PublicKey, B extends PrivateKey> PrivateJwkBuilder<B, A, ?, ?, ?> key(B key) {\n        Assert.notNull(key, \"Key cannot be null.\");\n        if (key instanceof RSAPrivateKey) {\n            return (PrivateJwkBuilder<B, A, ?, ?, ?>) key((RSAPrivateKey) key);\n        } else if (key instanceof ECPrivateKey) {\n            return (PrivateJwkBuilder<B, A, ?, ?, ?>) key((ECPrivateKey) key);\n        } else {\n            try {\n                return octetKey(key);\n            } catch (Exception e) {\n                throw unsupportedKey(key, e);\n            }\n        }\n    }\n\n    @Override\n    public <A extends PublicKey, B extends PrivateKey> OctetPublicJwkBuilder<A, B> octetKey(A key) {\n        return new AbstractAsymmetricJwkBuilder.DefaultOctetPublicJwkBuilder<>(newContext(key));\n    }\n\n    @Override\n    public <A extends PrivateKey, B extends PublicKey> OctetPrivateJwkBuilder<A, B> octetKey(A key) {\n        return new AbstractAsymmetricJwkBuilder.DefaultOctetPrivateJwkBuilder<>(newContext(key));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <A extends PublicKey, B extends PrivateKey> PublicJwkBuilder<A, B, ?, ?, ?, ?> chain(List<X509Certificate> chain)\n            throws UnsupportedKeyException {\n        Assert.notEmpty(chain, \"chain cannot be null or empty.\");\n        X509Certificate cert = Assert.notNull(chain.get(0), \"The first X509Certificate cannot be null.\");\n        PublicKey key = Assert.notNull(cert.getPublicKey(), \"The first X509Certificate's PublicKey cannot be null.\");\n        return this.<A, B>key((A) key).x509Chain(chain);\n    }\n\n    @Override\n    public RsaPublicJwkBuilder rsaChain(List<X509Certificate> chain) {\n        Assert.notEmpty(chain, \"X509Certificate chain cannot be empty.\");\n        X509Certificate cert = chain.get(0);\n        PublicKey key = cert.getPublicKey();\n        RSAPublicKey pubKey = KeyPairs.assertKey(key, RSAPublicKey.class, \"The first X509Certificate's \");\n        return key(pubKey).x509Chain(chain);\n    }\n\n    @Override\n    public EcPublicJwkBuilder ecChain(List<X509Certificate> chain) {\n        Assert.notEmpty(chain, \"X509Certificate chain cannot be empty.\");\n        X509Certificate cert = chain.get(0);\n        PublicKey key = cert.getPublicKey();\n        ECPublicKey pubKey = KeyPairs.assertKey(key, ECPublicKey.class, \"The first X509Certificate's \");\n        return key(pubKey).x509Chain(chain);\n    }\n\n    @SuppressWarnings(\"unchecked\") // ok because of the EdwardsCurve.assertEdwards calls\n    @Override\n    public <A extends PrivateKey, B extends PublicKey> OctetPrivateJwkBuilder<A, B> octetKeyPair(KeyPair pair) {\n        PublicKey pub = KeyPairs.getKey(pair, PublicKey.class);\n        PrivateKey priv = KeyPairs.getKey(pair, PrivateKey.class);\n        EdwardsCurve.assertEdwards(pub);\n        EdwardsCurve.assertEdwards(priv);\n        return (OctetPrivateJwkBuilder<A, B>) octetKey(priv).publicKey(pub);\n    }\n\n    @SuppressWarnings(\"unchecked\") // ok because of the EdwardsCurve.assertEdwards calls\n    @Override\n    public <A extends PublicKey, B extends PrivateKey> OctetPublicJwkBuilder<A, B> octetChain(List<X509Certificate> chain) {\n        Assert.notEmpty(chain, \"X509Certificate chain cannot be empty.\");\n        X509Certificate cert = chain.get(0);\n        PublicKey key = cert.getPublicKey();\n        Assert.notNull(key, \"The first X509Certificate's PublicKey cannot be null.\");\n        EdwardsCurve.assertEdwards(key);\n        return this.<A, B>octetKey((A) key).x509Chain(chain);\n    }\n\n    @Override\n    public RsaPrivateJwkBuilder rsaKeyPair(KeyPair pair) {\n        RSAPublicKey pub = KeyPairs.getKey(pair, RSAPublicKey.class);\n        RSAPrivateKey priv = KeyPairs.getKey(pair, RSAPrivateKey.class);\n        return key(priv).publicKey(pub);\n    }\n\n    @Override\n    public EcPrivateJwkBuilder ecKeyPair(KeyPair pair) {\n        ECPublicKey pub = KeyPairs.getKey(pair, ECPublicKey.class);\n        ECPrivateKey priv = KeyPairs.getKey(pair, ECPrivateKey.class);\n        return key(priv).publicKey(pub);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    @Override\n    public <A extends PublicKey, B extends PrivateKey> PrivateJwkBuilder<B, A, ?, ?, ?> keyPair(KeyPair keyPair)\n            throws UnsupportedKeyException {\n        A pub = (A) KeyPairs.getKey(keyPair, PublicKey.class);\n        B priv = (B) KeyPairs.getKey(keyPair, PrivateKey.class);\n        return this.<A, B>key(priv).publicKey(pub);\n    }\n\n    @Override\n    public J build() {\n        if (Strings.hasText(this.DELEGATE.get(AbstractJwk.KTY))) {\n            // Ensure we have a context that represents the configured kty value.  Converting the existing context to\n            // the type-specific context will also perform any necessary parameter value type conversion / error checking\n            // this will also perform any necessary parameter value type conversions / error checking\n            setDelegate(this.jwkFactory.newContext(this.DELEGATE, this.DELEGATE.getKey()));\n        }\n        return super.build();\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwks class.\n    public static final class Supplier<K extends Key, J extends Jwk<K>> implements io.jsonwebtoken.lang.Supplier<DynamicJwkBuilder<K, J>> {\n        @Override\n        public DynamicJwkBuilder<K, J> get() {\n            return new DefaultDynamicJwkBuilder<>();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultEcPrivateJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.EcPrivateJwk;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.PrivateJwk;\n\nimport java.math.BigInteger;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.util.Set;\n\nimport static io.jsonwebtoken.impl.security.DefaultEcPublicJwk.equalsPublic;\n\nclass DefaultEcPrivateJwk extends AbstractPrivateJwk<ECPrivateKey, ECPublicKey, EcPublicJwk> implements EcPrivateJwk {\n\n    static final Parameter<BigInteger> D = Parameters.bigInt(\"d\", \"ECC Private Key\")\n            .setConverter(FieldElementConverter.B64URL_CONVERTER)\n            .setSecret(true) // important!\n            .build();\n    static final Set<Parameter<?>> PARAMS = Collections.concat(DefaultEcPublicJwk.PARAMS, D);\n\n    DefaultEcPrivateJwk(JwkContext<ECPrivateKey> ctx, EcPublicJwk pubJwk) {\n        super(ctx,\n                // only public members are included in Private JWK Thumbprints per\n                // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n                DefaultEcPublicJwk.THUMBPRINT_PARAMS,\n                pubJwk);\n    }\n\n    @Override\n    protected boolean equals(PrivateJwk<?, ?, ?> jwk) {\n        return jwk instanceof EcPrivateJwk && equalsPublic(this, jwk) && Parameters.equals(this, jwk, D);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultEcPublicJwk.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.math.BigInteger;\nimport java.security.interfaces.ECPublicKey;\nimport java.util.List;\nimport java.util.Set;\n\nclass DefaultEcPublicJwk extends AbstractPublicJwk<ECPublicKey> implements EcPublicJwk {\n\n    static final String TYPE_VALUE = \"EC\";\n\n    static final Parameter<String> CRV = Parameters.string(\"crv\", \"Curve\");\n    static final Parameter<BigInteger> X = Parameters.bigInt(\"x\", \"X Coordinate\")\n            .setConverter(FieldElementConverter.B64URL_CONVERTER).build();\n    static final Parameter<BigInteger> Y = Parameters.bigInt(\"y\", \"Y Coordinate\")\n            .setConverter(FieldElementConverter.B64URL_CONVERTER).build();\n    static final Set<Parameter<?>> PARAMS = Collections.concat(AbstractAsymmetricJwk.PARAMS, CRV, X, Y);\n\n    // https://www.rfc-editor.org/rfc/rfc7638#section-3.2\n    static final List<Parameter<?>> THUMBPRINT_PARAMS = Collections.<Parameter<?>>of(CRV, KTY, X, Y);\n\n    DefaultEcPublicJwk(JwkContext<ECPublicKey> ctx) {\n        super(ctx, THUMBPRINT_PARAMS);\n    }\n\n    static boolean equalsPublic(ParameterReadable self, Object candidate) {\n        return Parameters.equals(self, candidate, CRV) &&\n                Parameters.equals(self, candidate, X) &&\n                Parameters.equals(self, candidate, Y);\n    }\n\n    @Override\n    protected boolean equals(PublicJwk<?> jwk) {\n        return jwk instanceof EcPublicJwk && equalsPublic(this, jwk);\n    }\n\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultHashAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.HashAlgorithm;\nimport io.jsonwebtoken.security.Request;\nimport io.jsonwebtoken.security.VerifyDigestRequest;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.security.MessageDigest;\nimport java.util.Locale;\n\npublic final class DefaultHashAlgorithm extends CryptoAlgorithm implements HashAlgorithm {\n\n    public static final HashAlgorithm SHA1 = new DefaultHashAlgorithm(\"sha-1\");\n\n    DefaultHashAlgorithm(String id) {\n        super(id, id.toUpperCase(Locale.ENGLISH));\n    }\n\n    @Override\n    public byte[] digest(final Request<InputStream> request) {\n        Assert.notNull(request, \"Request cannot be null.\");\n        final InputStream payload = Assert.notNull(request.getPayload(), \"Request payload cannot be null.\");\n        return jca(request).withMessageDigest(new CheckedFunction<MessageDigest, byte[]>() {\n            @Override\n            public byte[] apply(MessageDigest md) throws IOException {\n                byte[] buf = new byte[1024];\n                int len = 0;\n                while (len != -1) {\n                    len = payload.read(buf);\n                    if (len > 0) md.update(buf, 0, len);\n                }\n                return md.digest();\n            }\n        });\n    }\n\n    @Override\n    public boolean verify(VerifyDigestRequest request) {\n        Assert.notNull(request, \"VerifyDigestRequest cannot be null.\");\n        byte[] digest = Assert.notNull(request.getDigest(), \"Digest cannot be null.\");\n        byte[] computed = digest(request);\n        return MessageDigest.isEqual(computed, digest); // time-constant comparison required, not standard equals\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultJwkContext.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.AbstractX509Context;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.security.HashAlgorithm;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyOperation;\n\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\nimport static io.jsonwebtoken.lang.Strings.nespace;\n\npublic class DefaultJwkContext<K extends Key> extends AbstractX509Context<JwkContext<K>> implements JwkContext<K> {\n\n    private static final Set<Parameter<?>> DEFAULT_PARAMS;\n\n    static { // assume all JWA params:\n        Set<Parameter<?>> set = new LinkedHashSet<>();\n        set.addAll(DefaultSecretJwk.PARAMS); // Private/Secret JWKs has both public and private params\n        set.addAll(DefaultEcPrivateJwk.PARAMS); // Private JWKs have both public and private params\n        set.addAll(DefaultRsaPrivateJwk.PARAMS); // Private JWKs have both public and private params\n        set.addAll(DefaultOctetPrivateJwk.PARAMS); // Private JWKs have both public and private params\n\n        // EC JWKs and Octet JWKs have two params that are named identically, but have different type requirements.  So\n        // we swap out those params with placeholders that allow either.  When the JwkContext is converted to its\n        // type-specific context by the ProtoBuilder, the values will be correctly converted to their required types\n        // at that time.  It is also important to retain toString security (via parameter.setSecret(true)) to ensure\n        // any printing of the builder or its internal context does not print secure data.\n        set.remove(DefaultEcPublicJwk.X);\n        set.remove(DefaultEcPrivateJwk.D);\n        set.add(Parameters.string(DefaultEcPublicJwk.X.getId(), \"Elliptic Curve public key X coordinate\"));\n        set.add(Parameters.builder(String.class).setSecret(true)\n                .setId(DefaultEcPrivateJwk.D.getId()).setName(\"Elliptic Curve private key\").build());\n\n        DEFAULT_PARAMS = Collections.immutable(set);\n    }\n\n    private K key;\n    private PublicKey publicKey;\n    private Provider provider;\n\n    private SecureRandom random;\n\n    private HashAlgorithm idThumbprintAlgorithm;\n\n    public DefaultJwkContext() {\n        // For the default constructor case, we don't know how it will be used or what values will be populated,\n        // so we can't know ahead of time what the sensitive data is.  As such, for security reasons, we assume all\n        // the known params for all supported keys/algorithms in case it is used for any of them:\n        this(DEFAULT_PARAMS);\n    }\n\n    public DefaultJwkContext(Set<Parameter<?>> params) {\n        super(params);\n    }\n\n    public DefaultJwkContext(Set<Parameter<?>> params, JwkContext<?> other) {\n        this(params, other, true);\n    }\n\n    public DefaultJwkContext(Set<Parameter<?>> params, JwkContext<?> other, K key) {\n        //if the key is null or a PublicKey, we don't want to redact - we want to fully remove the items that are\n        //private names (public JWKs should never contain any private key params, even if redacted):\n        this(params, other, (key == null || key instanceof PublicKey));\n        this.key = Assert.notNull(key, \"Key cannot be null.\");\n    }\n\n    public DefaultJwkContext(Set<Parameter<?>> params, JwkContext<?> other, boolean removePrivate) {\n        super(Assert.notEmpty(params, \"Parameters cannot be null or empty.\"));\n        Assert.notNull(other, \"JwkContext cannot be null.\");\n        Assert.isInstanceOf(DefaultJwkContext.class, other, \"JwkContext must be a DefaultJwkContext instance.\");\n        DefaultJwkContext<?> src = (DefaultJwkContext<?>) other;\n        this.provider = other.getProvider();\n        this.random = other.getRandom();\n        this.idThumbprintAlgorithm = other.getIdThumbprintAlgorithm();\n        this.values.putAll(src.values);\n        // Ensure the source's idiomatic values match the types expected by this object:\n        for (Map.Entry<String, Object> entry : src.idiomaticValues.entrySet()) {\n            String id = entry.getKey();\n            Object value = entry.getValue();\n            Parameter<?> param = this.PARAMS.get(id);\n            if (param != null && !param.supports(value)) { // src idiomatic value is not what is expected, so convert:\n                value = this.values.get(param.getId());\n                put(param, value); // perform idiomatic conversion with original/raw src value\n            } else {\n                this.idiomaticValues.put(id, value);\n            }\n        }\n        if (removePrivate) {\n            for (Parameter<?> param : src.PARAMS.values()) {\n                if (param.isSecret()) {\n                    remove(param.getId());\n                }\n            }\n        }\n    }\n\n    @Override\n    public JwkContext<K> parameter(Parameter<?> param) {\n        Registry<String, ? extends Parameter<?>> registry = Parameters.replace(this.PARAMS, param);\n        Set<Parameter<?>> params = new LinkedHashSet<>(registry.values());\n        return this.key != null ?\n                new DefaultJwkContext<>(params, this, key) :\n                new DefaultJwkContext<K>(params, this, false);\n    }\n\n    @Override\n    public String getName() {\n        String value = get(AbstractJwk.KTY);\n        if (DefaultSecretJwk.TYPE_VALUE.equals(value)) {\n            value = \"Secret\";\n        } else if (DefaultOctetPublicJwk.TYPE_VALUE.equals(value)) {\n            value = \"Octet\";\n        }\n        StringBuilder sb = value != null ? new StringBuilder(value) : new StringBuilder();\n        K key = getKey();\n        if (key instanceof PublicKey) {\n            nespace(sb).append(\"Public\");\n        } else if (key instanceof PrivateKey) {\n            nespace(sb).append(\"Private\");\n        }\n        nespace(sb).append(\"JWK\");\n        return sb.toString();\n    }\n\n    @Override\n    public void putAll(Map<? extends String, ?> m) {\n        Assert.notEmpty(m, \"JWK values cannot be null or empty.\");\n        super.putAll(m);\n    }\n\n    @Override\n    public String getAlgorithm() {\n        return get(AbstractJwk.ALG);\n    }\n\n    @Override\n    public JwkContext<K> setAlgorithm(String algorithm) {\n        put(AbstractJwk.ALG, algorithm);\n        return this;\n    }\n\n    @Override\n    public String getId() {\n        return get(AbstractJwk.KID);\n    }\n\n    @Override\n    public JwkContext<K> setId(String id) {\n        put(AbstractJwk.KID, id);\n        return this;\n    }\n\n    @Override\n    public JwkContext<K> setIdThumbprintAlgorithm(HashAlgorithm alg) {\n        this.idThumbprintAlgorithm = alg;\n        return this;\n    }\n\n    @Override\n    public HashAlgorithm getIdThumbprintAlgorithm() {\n        return this.idThumbprintAlgorithm;\n    }\n\n    @Override\n    public Set<KeyOperation> getOperations() {\n        return get(AbstractJwk.KEY_OPS);\n    }\n\n    @Override\n    public JwkContext<K> setOperations(Collection<? extends KeyOperation> ops) {\n        put(AbstractJwk.KEY_OPS, ops);\n        return this;\n    }\n\n    @Override\n    public String getType() {\n        return get(AbstractJwk.KTY);\n    }\n\n    @Override\n    public JwkContext<K> setType(String type) {\n        put(AbstractJwk.KTY, type);\n        return this;\n    }\n\n    @Override\n    public String getPublicKeyUse() {\n        return get(AbstractAsymmetricJwk.USE);\n    }\n\n    @Override\n    public JwkContext<K> setPublicKeyUse(String use) {\n        put(AbstractAsymmetricJwk.USE, use);\n        return this;\n    }\n\n    @Override\n    public boolean isSigUse() {\n        // Even though 'use' is for PUBLIC KEY use (as defined in RFC 7515), RFC 7520 shows secret keys with\n        // 'use' values, so we'll account for that as well:\n        if (\"sig\".equals(getPublicKeyUse())) {\n            return true;\n        }\n        Set<KeyOperation> ops = getOperations();\n        if (Collections.isEmpty(ops)) {\n            return false;\n        }\n        return ops.contains(Jwks.OP.SIGN) || ops.contains(Jwks.OP.VERIFY);\n    }\n\n    @Override\n    public K getKey() {\n        return this.key;\n    }\n\n    @Override\n    public JwkContext<K> setKey(K key) {\n        this.key = key;\n        return this;\n    }\n\n    @Override\n    public PublicKey getPublicKey() {\n        return this.publicKey;\n    }\n\n    @Override\n    public JwkContext<K> setPublicKey(PublicKey publicKey) {\n        this.publicKey = publicKey;\n        return this;\n    }\n\n    @Override\n    public Provider getProvider() {\n        return this.provider;\n    }\n\n    @Override\n    public JwkContext<K> setProvider(Provider provider) {\n        this.provider = provider;\n        return this;\n    }\n\n    @Override\n    public SecureRandom getRandom() {\n        return this.random;\n    }\n\n    @Override\n    public JwkContext<K> setRandom(SecureRandom random) {\n        this.random = random;\n        return this;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultJwkParserBuilder.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.ConvertingParser;\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.JwkParserBuilder;\n\npublic class DefaultJwkParserBuilder extends AbstractJwkParserBuilder<Jwk<?>, JwkParserBuilder>\n        implements JwkParserBuilder {\n    @Override\n    public Parser<Jwk<?>> doBuild() {\n        JwkDeserializer deserializer = new JwkDeserializer(this.deserializer);\n        JwkBuilderSupplier supplier = new JwkBuilderSupplier(this.provider, this.operationPolicy);\n        JwkConverter<Jwk<?>> converter = new JwkConverter<>(supplier);\n        return new ConvertingParser<>(deserializer, converter);\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwks class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<JwkParserBuilder> {\n        @Override\n        public JwkParserBuilder get() {\n            return new DefaultJwkParserBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultJwkSet.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.ParameterMap;\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.JwkSet;\n\nimport java.util.Iterator;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class DefaultJwkSet extends ParameterMap implements JwkSet {\n\n    private static final String NAME = \"JWK Set\";\n\n    static Parameter<Set<Jwk<?>>> param(Converter<Jwk<?>, ?> converter) {\n        return Parameters.builder(JwkConverter.JWK_CLASS)\n                .setConverter(converter).set()\n                .setId(\"keys\").setName(\"JSON Web Keys\")\n                .build();\n    }\n\n    static final Parameter<Set<Jwk<?>>> KEYS = param(JwkConverter.ANY);\n\n    public DefaultJwkSet(Parameter<Set<Jwk<?>>> param, Map<String, ?> src) {\n        super(Parameters.registry(param), src);\n    }\n\n    @Override\n    public String getName() {\n        return NAME;\n    }\n\n    @Override\n    public Set<Jwk<?>> getKeys() {\n        Set<Jwk<?>> jwks = get(KEYS);\n        if (Collections.isEmpty(jwks)) {\n            return Collections.emptySet();\n        }\n        return Collections.immutable(jwks);\n    }\n\n    @Override\n    public Iterator<Jwk<?>> iterator() {\n        return getKeys().iterator(); // immutable because of getKeys() return value\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultJwkSetBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.ParameterMap;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.JwkSet;\nimport io.jsonwebtoken.security.JwkSetBuilder;\nimport io.jsonwebtoken.security.KeyOperationPolicy;\n\nimport java.security.Provider;\nimport java.util.Collection;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class DefaultJwkSetBuilder extends AbstractSecurityBuilder<JwkSet, JwkSetBuilder>\n        implements JwkSetBuilder {\n\n    private KeyOperationPolicy operationPolicy;\n    private JwkSetConverter converter;\n    private ParameterMap map;\n\n    public DefaultJwkSetBuilder() {\n        this.operationPolicy = AbstractJwkBuilder.DEFAULT_OPERATION_POLICY;\n        this.converter = new JwkSetConverter();\n        this.map = new ParameterMap(Parameters.registry(DefaultJwkSet.KEYS));\n    }\n\n    @Override\n    public JwkSetBuilder delete(String key) {\n        map.remove(key);\n        return this;\n    }\n\n    @Override\n    public JwkSetBuilder empty() {\n        map.clear();\n        return this;\n    }\n\n    @Override\n    public JwkSetBuilder add(String key, Object value) {\n        map.put(key, value);\n        return this;\n    }\n\n    @Override\n    public JwkSetBuilder add(Map<? extends String, ?> m) {\n        map.putAll(m);\n        return this;\n    }\n\n    private JwkSetBuilder refresh() {\n        JwkConverter<Jwk<?>> jwkConverter = new JwkConverter<>(new JwkBuilderSupplier(this.provider, this.operationPolicy));\n        this.converter = new JwkSetConverter(jwkConverter, this.converter.isIgnoreUnsupported());\n        Parameter<Set<Jwk<?>>> param = DefaultJwkSet.param(jwkConverter);\n        this.map = new ParameterMap(Parameters.registry(param), this.map, true);\n        // a new policy could have been applied, ensure that any existing keys match that policy:\n        Set<Jwk<?>> jwks = this.map.get(param);\n        if (!Collections.isEmpty(jwks)) {\n            for (Jwk<?> jwk : jwks) {\n                this.operationPolicy.validate(jwk.getOperations());\n            }\n        }\n        return this;\n    }\n\n    @Override\n    public JwkSetBuilder provider(Provider provider) {\n        super.provider(provider);\n        return refresh();\n    }\n\n    @Override\n    public JwkSetBuilder operationPolicy(final KeyOperationPolicy policy) throws IllegalArgumentException {\n        this.operationPolicy = policy != null ? policy : AbstractJwkBuilder.DEFAULT_OPERATION_POLICY;\n        return refresh();\n    }\n\n    Collection<Jwk<?>> ensureKeys() {\n        Collection<Jwk<?>> keys = map.get(DefaultJwkSet.KEYS);\n        return Collections.isEmpty(keys) ? new LinkedHashSet<Jwk<?>>() : keys;\n    }\n\n    @Override\n    public JwkSetBuilder add(Jwk<?> jwk) {\n        if (jwk != null) {\n            this.operationPolicy.validate(jwk.getOperations());\n            Collection<Jwk<?>> keys = ensureKeys();\n            keys.add(jwk);\n            keys(keys);\n        }\n        return this;\n    }\n\n    @Override\n    public JwkSetBuilder add(Collection<Jwk<?>> c) {\n        if (!Collections.isEmpty(c)) {\n            for (Jwk<?> jwk : c) {\n                add(jwk);\n            }\n        }\n        return this;\n    }\n\n    @Override\n    public JwkSetBuilder keys(Collection<Jwk<?>> c) {\n        return add(DefaultJwkSet.KEYS.getId(), c);\n    }\n\n    @Override\n    public JwkSet build() {\n        return converter.applyFrom(this.map);\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwks class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<JwkSetBuilder> {\n        @Override\n        public JwkSetBuilder get() {\n            return new DefaultJwkSetBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultJwkSetParserBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.ConvertingParser;\nimport io.jsonwebtoken.io.Parser;\nimport io.jsonwebtoken.security.JwkSet;\nimport io.jsonwebtoken.security.JwkSetParserBuilder;\n\npublic class DefaultJwkSetParserBuilder extends AbstractJwkParserBuilder<JwkSet, JwkSetParserBuilder>\n        implements JwkSetParserBuilder {\n\n    private boolean ignoreUnsupported = true;\n\n    @Override\n    public JwkSetParserBuilder ignoreUnsupported(boolean ignore) {\n        this.ignoreUnsupported = ignore;\n        return this;\n    }\n\n    @Override\n    public Parser<JwkSet> doBuild() {\n        JwkSetDeserializer deserializer = new JwkSetDeserializer(this.deserializer);\n        JwkBuilderSupplier supplier = new JwkBuilderSupplier(this.provider, this.operationPolicy);\n        JwkSetConverter converter = new JwkSetConverter(supplier, this.ignoreUnsupported);\n        return new ConvertingParser<>(deserializer, converter);\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwks class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<JwkSetParserBuilder> {\n        @Override\n        public JwkSetParserBuilder get() {\n            return new DefaultJwkSetParserBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultJwkThumbprint.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.HashAlgorithm;\nimport io.jsonwebtoken.security.JwkThumbprint;\n\nimport java.net.URI;\nimport java.security.MessageDigest;\n\nclass DefaultJwkThumbprint implements JwkThumbprint {\n\n    private static final String URI_PREFIX = \"urn:ietf:params:oauth:jwk-thumbprint:\";\n\n    private final byte[] digest;\n    private final HashAlgorithm alg;\n    private final URI uri;\n    private final int hashcode;\n    private final String sval;\n\n    DefaultJwkThumbprint(byte[] digest, HashAlgorithm alg) {\n        this.digest = Assert.notEmpty(digest, \"Thumbprint digest byte array cannot be null or empty.\");\n        this.alg = Assert.notNull(alg, \"Thumbprint HashAlgorithm cannot be null.\");\n        String id = Assert.hasText(Strings.clean(alg.getId()), \"Thumbprint HashAlgorithm id cannot be null or empty.\");\n        String base64Url = Encoders.BASE64URL.encode(digest);\n        String s = URI_PREFIX + id + \":\" + base64Url;\n        this.uri = URI.create(s);\n        this.hashcode = Objects.nullSafeHashCode(this.digest, this.alg);\n        this.sval = Encoders.BASE64URL.encode(digest);\n    }\n\n    @Override\n    public HashAlgorithm getHashAlgorithm() {\n        return this.alg;\n    }\n\n    @Override\n    public byte[] toByteArray() {\n        return this.digest.clone();\n    }\n\n    @Override\n    public URI toURI() {\n        return this.uri;\n    }\n\n    @Override\n    public String toString() {\n        return sval;\n    }\n\n    @Override\n    public int hashCode() {\n        return this.hashcode;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) {\n            return true;\n        }\n        if (obj instanceof DefaultJwkThumbprint) {\n            DefaultJwkThumbprint other = (DefaultJwkThumbprint) obj;\n            return this.alg.equals(other.alg) &&\n                    MessageDigest.isEqual(this.digest, other.digest);\n        }\n\n        return false;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyOperation.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.KeyOperation;\n\nimport java.util.Set;\n\nfinal class DefaultKeyOperation implements KeyOperation {\n\n    private static final String CUSTOM_DESCRIPTION = \"Custom key operation\";\n\n    static final KeyOperation SIGN = of(\"sign\", \"Compute digital signature or MAC\", \"verify\");\n    static final KeyOperation VERIFY = of(\"verify\", \"Verify digital signature or MAC\", \"sign\");\n    static final KeyOperation ENCRYPT = of(\"encrypt\", \"Encrypt content\", \"decrypt\");\n    static final KeyOperation DECRYPT =\n            of(\"decrypt\", \"Decrypt content and validate decryption, if applicable\", \"encrypt\");\n    static final KeyOperation WRAP = of(\"wrapKey\", \"Encrypt key\", \"unwrapKey\");\n    static final KeyOperation UNWRAP =\n            of(\"unwrapKey\", \"Decrypt key and validate decryption, if applicable\", \"wrapKey\");\n    static final KeyOperation DERIVE_KEY = of(\"deriveKey\", \"Derive key\", null);\n    static final KeyOperation DERIVE_BITS =\n            of(\"deriveBits\", \"Derive bits not to be used as a key\", null);\n\n    final String id;\n    final String description;\n    final Set<String> related;\n\n    static KeyOperation of(String id, String description, String related) {\n        return new DefaultKeyOperation(id, description, Collections.setOf(related));\n    }\n\n    DefaultKeyOperation(String id) {\n        this(id, null, null);\n    }\n\n    DefaultKeyOperation(String id, String description, Set<String> related) {\n        this.id = Assert.hasText(id, \"id cannot be null or empty.\");\n        this.description = Strings.hasText(description) ? description : CUSTOM_DESCRIPTION;\n        this.related = related != null ? Collections.immutable(related) : Collections.<String>emptySet();\n    }\n\n    @Override\n    public String getId() {\n        return this.id;\n    }\n\n    @Override\n    public String getDescription() {\n        return this.description;\n    }\n\n    @Override\n    public boolean isRelated(KeyOperation operation) {\n        return equals(operation) || (operation != null && this.related.contains(operation.getId()));\n    }\n\n    @Override\n    public int hashCode() {\n        return id.hashCode();\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        return obj == this ||\n                (obj instanceof KeyOperation && this.id.equals(((KeyOperation) obj).getId()));\n    }\n\n    @Override\n    public String toString() {\n        return \"'\" + this.id + \"' (\" + this.description + \")\";\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyOperationBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.KeyOperation;\nimport io.jsonwebtoken.security.KeyOperationBuilder;\n\nimport java.util.LinkedHashSet;\nimport java.util.Set;\n\npublic class DefaultKeyOperationBuilder implements KeyOperationBuilder {\n\n    private String id;\n    private String description;\n    private final Set<String> related = new LinkedHashSet<>();\n\n    @Override\n    public KeyOperationBuilder id(String id) {\n        this.id = id;\n        return this;\n    }\n\n    @Override\n    public KeyOperationBuilder description(String description) {\n        this.description = description;\n        return this;\n    }\n\n    @Override\n    public KeyOperationBuilder related(String related) {\n        if (Strings.hasText(related)) {\n            this.related.add(related);\n        }\n        return this;\n    }\n\n    @Override\n    public KeyOperation build() {\n        return new DefaultKeyOperation(this.id, this.description, this.related);\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwks class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<KeyOperationBuilder> {\n        @Override\n        public KeyOperationBuilder get() {\n            return new DefaultKeyOperationBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyOperationPolicy.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.security.KeyOperation;\nimport io.jsonwebtoken.security.KeyOperationPolicy;\n\nimport java.util.Collection;\n\nfinal class DefaultKeyOperationPolicy implements KeyOperationPolicy {\n\n    private final Collection<KeyOperation> ops;\n\n    private final boolean allowUnrelated;\n\n    DefaultKeyOperationPolicy(Collection<KeyOperation> ops, boolean allowUnrelated) {\n        Assert.notEmpty(ops, \"KeyOperation collection cannot be null or empty.\");\n        this.ops = Collections.immutable(ops);\n        this.allowUnrelated = allowUnrelated;\n    }\n\n    @Override\n    public Collection<KeyOperation> getOperations() {\n        return this.ops;\n    }\n\n    @Override\n    public void validate(Collection<? extends KeyOperation> ops) {\n        if (allowUnrelated || Collections.isEmpty(ops)) return;\n        for (KeyOperation operation : ops) {\n            for (KeyOperation inner : ops) {\n                if (!operation.isRelated(inner)) {\n                    String msg = \"Unrelated key operations are not allowed. KeyOperation [\" + inner +\n                            \"] is unrelated to [\" + operation + \"].\";\n                    throw new IllegalArgumentException(msg);\n                }\n            }\n        }\n    }\n\n    @Override\n    public int hashCode() {\n        int hash = Boolean.valueOf(this.allowUnrelated).hashCode();\n        KeyOperation[] ops = this.ops.toArray(new KeyOperation[0]);\n        hash = 31 * hash + Objects.nullSafeHashCode((Object[]) ops);\n        return hash;\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) return true;\n        if (!(obj instanceof DefaultKeyOperationPolicy)) {\n            return false;\n        }\n        DefaultKeyOperationPolicy other = (DefaultKeyOperationPolicy) obj;\n        return this.allowUnrelated == other.allowUnrelated &&\n                Collections.size(this.ops) == Collections.size(other.ops) &&\n                this.ops.containsAll(other.ops);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyOperationPolicyBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.DefaultCollectionMutator;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyOperation;\nimport io.jsonwebtoken.security.KeyOperationPolicy;\nimport io.jsonwebtoken.security.KeyOperationPolicyBuilder;\n\npublic class DefaultKeyOperationPolicyBuilder extends DefaultCollectionMutator<KeyOperation, KeyOperationPolicyBuilder>\n        implements KeyOperationPolicyBuilder {\n\n    private boolean unrelated = false;\n\n    public DefaultKeyOperationPolicyBuilder() {\n        super(Jwks.OP.get().values());\n    }\n\n    @Override\n    public KeyOperationPolicyBuilder unrelated() {\n        this.unrelated = true;\n        return this;\n    }\n\n    @Override\n    public KeyOperationPolicy build() {\n        return new DefaultKeyOperationPolicy(Collections.immutable(getCollection()), this.unrelated);\n    }\n\n    // @since 0.12.7 per https://github.com/jwtk/jjwt/issues/988\n    @SuppressWarnings(\"unused\") // used via reflection in the api module's Jwks class.\n    public static final class Supplier implements io.jsonwebtoken.lang.Supplier<KeyOperationPolicyBuilder> {\n        @Override\n        public KeyOperationPolicyBuilder get() {\n            return new DefaultKeyOperationPolicyBuilder();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyPair.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.KeyPair;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\npublic class DefaultKeyPair<A extends PublicKey, B extends PrivateKey> implements KeyPair<A, B> {\n\n    private final A publicKey;\n    private final B privateKey;\n\n    private final java.security.KeyPair jdkPair;\n\n    public DefaultKeyPair(A publicKey, B privateKey) {\n        this.publicKey = Assert.notNull(publicKey, \"PublicKey argument cannot be null.\");\n        this.privateKey = Assert.notNull(privateKey, \"PrivateKey argument cannot be null.\");\n        this.jdkPair = new java.security.KeyPair(this.publicKey, this.privateKey);\n    }\n\n    @Override\n    public A getPublic() {\n        return this.publicKey;\n    }\n\n    @Override\n    public B getPrivate() {\n        return this.privateKey;\n    }\n\n    @Override\n    public java.security.KeyPair toJavaKeyPair() {\n        return this.jdkPair;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyPairBuilder.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.KeyPairBuilder;\n\nimport java.security.KeyPair;\nimport java.security.spec.AlgorithmParameterSpec;\n\npublic class DefaultKeyPairBuilder extends AbstractSecurityBuilder<KeyPair, KeyPairBuilder> implements KeyPairBuilder {\n\n    private final String jcaName;\n    private final int bitLength;\n    private final AlgorithmParameterSpec params;\n\n    public DefaultKeyPairBuilder(String jcaName) {\n        this.jcaName = Assert.hasText(jcaName, \"jcaName cannot be null or empty.\");\n        this.bitLength = 0;\n        this.params = null;\n    }\n\n    public DefaultKeyPairBuilder(String jcaName, int bitLength) {\n        this.jcaName = Assert.hasText(jcaName, \"jcaName cannot be null or empty.\");\n        this.bitLength = Assert.gt(bitLength, 0, \"bitLength must be a positive integer greater than 0\");\n        this.params = null;\n    }\n\n    public DefaultKeyPairBuilder(String jcaName, AlgorithmParameterSpec params) {\n        this.jcaName = Assert.hasText(jcaName, \"jcaName cannot be null or empty.\");\n        this.params = Assert.notNull(params, \"AlgorithmParameterSpec params cannot be null.\");\n        this.bitLength = 0;\n    }\n\n    @Override\n    public KeyPair build() {\n        JcaTemplate template = new JcaTemplate(this.jcaName, this.provider, this.random);\n        if (this.params != null) {\n            return template.generateKeyPair(this.params);\n        } else if (this.bitLength > 0) {\n            return template.generateKeyPair(this.bitLength);\n        } else {\n            return template.generateKeyPair();\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyRequest.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.KeyRequest;\n\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\npublic class DefaultKeyRequest<T> extends DefaultRequest<T> implements KeyRequest<T> {\n\n    private final JweHeader header;\n    private final AeadAlgorithm encryptionAlgorithm;\n\n    public DefaultKeyRequest(T payload, Provider provider, SecureRandom secureRandom, JweHeader header, AeadAlgorithm encryptionAlgorithm) {\n        super(payload, provider, secureRandom);\n        this.header = Assert.notNull(header, \"JweHeader/Builder cannot be null.\");\n        this.encryptionAlgorithm = Assert.notNull(encryptionAlgorithm, \"AeadAlgorithm argument cannot be null.\");\n    }\n\n    @Override\n    public JweHeader getHeader() {\n        return this.header;\n    }\n\n    @Override\n    public AeadAlgorithm getEncryptionAlgorithm() {\n        return this.encryptionAlgorithm;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyResult.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.KeyResult;\n\nimport javax.crypto.SecretKey;\n\npublic class DefaultKeyResult extends DefaultMessage<byte[]> implements KeyResult {\n\n    private final SecretKey key;\n\n    public DefaultKeyResult(SecretKey key) {\n        this(key, Bytes.EMPTY);\n    }\n\n    public DefaultKeyResult(SecretKey key, byte[] encryptedKey) {\n        super(encryptedKey);\n        this.key = Assert.notNull(key, \"Content Encryption Key cannot be null.\");\n    }\n\n    @Override\n    protected void assertBytePayload(byte[] payload) {\n        Assert.notNull(payload, \"encrypted key bytes cannot be null (but may be empty.\");\n    }\n\n    @Override\n    public SecretKey getKey() {\n        return this.key;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultKeyUseStrategy.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\npublic class DefaultKeyUseStrategy implements KeyUseStrategy {\n\n    static final KeyUseStrategy INSTANCE = new DefaultKeyUseStrategy();\n\n    // values from https://www.rfc-editor.org/rfc/rfc7517.html#section-4.2\n    private static final String SIGNATURE = \"sig\";\n    private static final String ENCRYPTION = \"enc\";\n\n    @Override\n    public String toJwkValue(KeyUsage usage) {\n\n        // states 2, 3, 4\n        if (usage.isKeyEncipherment() || usage.isDataEncipherment() || usage.isKeyAgreement()) {\n            return ENCRYPTION;\n        }\n\n        // states 0, 1, 5, 6\n        if (usage.isDigitalSignature() || usage.isNonRepudiation() || usage.isKeyCertSign() || usage.isCRLSign()) {\n            return SIGNATURE;\n        }\n\n        // We don't need to check for encipherOnly (7) and decipherOnly (8) because per\n        // [RFC 5280, Section 4.2.1.3](https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3),\n        // those two states are only relevant when keyAgreement (4) is true, and that is covered in the first\n        // conditional above\n\n        return null; //can't infer\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultMacAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.MacAlgorithm;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.SecretKeyBuilder;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\nimport io.jsonwebtoken.security.WeakKeyException;\n\nimport javax.crypto.Mac;\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\nimport java.security.Key;\nimport java.security.MessageDigest;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @since 0.12.0\n */\nfinal class DefaultMacAlgorithm extends AbstractSecureDigestAlgorithm<SecretKey, SecretKey> implements MacAlgorithm {\n\n    private static final String HS256_OID = \"1.2.840.113549.2.9\";\n    private static final String HS384_OID = \"1.2.840.113549.2.10\";\n    private static final String HS512_OID = \"1.2.840.113549.2.11\";\n\n    private static final Set<String> JWA_STANDARD_IDS = new LinkedHashSet<>(Collections.of(\"HS256\", \"HS384\", \"HS512\"));\n\n    static final DefaultMacAlgorithm HS256 = new DefaultMacAlgorithm(256);\n    static final DefaultMacAlgorithm HS384 = new DefaultMacAlgorithm(384);\n    static final DefaultMacAlgorithm HS512 = new DefaultMacAlgorithm(512);\n\n    private static final Map<String, DefaultMacAlgorithm> JCA_NAME_MAP;\n\n    static {\n        JCA_NAME_MAP = new LinkedHashMap<>(6);\n\n        // In addition to JCA names, PKCS12 OIDs are added to these per\n        // https://bugs.openjdk.java.net/browse/JDK-8243551 as well:\n        JCA_NAME_MAP.put(HS256.getJcaName().toUpperCase(Locale.ENGLISH), HS256); // for case-insensitive lookup\n        JCA_NAME_MAP.put(HS256_OID, HS256);\n\n        JCA_NAME_MAP.put(HS384.getJcaName().toUpperCase(Locale.ENGLISH), HS384);\n        JCA_NAME_MAP.put(HS384_OID, HS384);\n\n        JCA_NAME_MAP.put(HS512.getJcaName().toUpperCase(Locale.ENGLISH), HS512);\n        JCA_NAME_MAP.put(HS512_OID, HS512);\n    }\n\n    private final int minKeyBitLength; //in bits\n\n    private DefaultMacAlgorithm(int digestBitLength) {\n        this(\"HS\" + digestBitLength, \"HmacSHA\" + digestBitLength, digestBitLength);\n    }\n\n    DefaultMacAlgorithm(String id, String jcaName, int minKeyBitLength) {\n        super(id, jcaName);\n        Assert.isTrue(minKeyBitLength > 0, \"minKeyLength must be greater than zero.\");\n        this.minKeyBitLength = minKeyBitLength;\n    }\n\n    @Override\n    public int getKeyBitLength() {\n        return this.minKeyBitLength;\n    }\n\n    private boolean isJwaStandard() {\n        return JWA_STANDARD_IDS.contains(getId());\n    }\n\n    private static boolean isJwaStandardJcaName(String jcaName) {\n        String key = jcaName.toUpperCase(Locale.ENGLISH);\n        return JCA_NAME_MAP.containsKey(key);\n    }\n\n    static DefaultMacAlgorithm findByKey(Key key) {\n\n        String alg = KeysBridge.findAlgorithm(key);\n        if (!Strings.hasText(alg)) {\n            return null;\n        }\n\n        String upper = alg.toUpperCase(Locale.ENGLISH);\n        DefaultMacAlgorithm mac = JCA_NAME_MAP.get(upper);\n        if (mac == null) {\n            return null;\n        }\n\n        // even though we found a standard alg based on the JCA name, we need to confirm that the key length is\n        // sufficient if the encoded key bytes are available:\n        byte[] encoded = KeysBridge.findEncoded(key);\n        long size = Bytes.bitLength(encoded);\n        if (size >= mac.getKeyBitLength()) {\n            return mac;\n        }\n\n        return null; // couldn't find a suitable match\n    }\n\n\n    @Override\n    public SecretKeyBuilder key() {\n        return new DefaultSecretKeyBuilder(getJcaName(), getKeyBitLength());\n    }\n\n    private void assertAlgorithmName(SecretKey key, boolean signing) {\n\n        String name = key.getAlgorithm();\n        if (!Strings.hasText(name)) {\n            String msg = \"The \" + keyType(signing) + \" key's algorithm cannot be null or empty.\";\n            throw new InvalidKeyException(msg);\n        }\n\n        // We can ignore key name assertions for generic secrets, because HSM module key algorithm names\n        // don't always align with JCA standard algorithm names\n        boolean generic = KeysBridge.isGenericSecret(key);\n\n        //assert key's jca name is valid if it's a JWA standard algorithm:\n        if (!generic && isJwaStandard() && !isJwaStandardJcaName(name)) {\n            throw new InvalidKeyException(\"The \" + keyType(signing) + \" key's algorithm '\" + name +\n                    \"' does not equal a valid HmacSHA* algorithm name or PKCS12 OID and cannot be used with \" +\n                    getId() + \".\");\n        }\n    }\n\n    @Override\n    protected void validateKey(Key k, boolean signing) {\n\n        final String keyType = keyType(signing);\n        if (k == null) {\n            throw new IllegalArgumentException(\"MAC \" + keyType + \" key cannot be null.\");\n        }\n\n        if (!(k instanceof SecretKey)) {\n            String msg = \"MAC \" + keyType + \" keys must be SecretKey instances.  Specified key is of type \" +\n                    k.getClass().getName();\n            throw new InvalidKeyException(msg);\n        }\n\n        if (k instanceof Password) {\n            String msg = \"Passwords are intended for use with key derivation algorithms only.\";\n            throw new InvalidKeyException(msg);\n        }\n\n        final SecretKey key = (SecretKey) k;\n\n        final String id = getId();\n\n        assertAlgorithmName(key, signing);\n\n        int size = KeysBridge.findBitLength(key);\n\n        // We can only perform length validation if key bit length is available\n        // per https://github.com/jwtk/jjwt/issues/478 and https://github.com/jwtk/jjwt/issues/619\n        // so return early if we can't:\n        if (size < 0) return;\n\n        if (size < this.minKeyBitLength) {\n            String msg = \"The \" + keyType + \" key's size is \" + size + \" bits which \" +\n                    \"is not secure enough for the \" + id + \" algorithm.\";\n\n            if (isJwaStandard() && isJwaStandardJcaName(getJcaName())) { //JWA standard algorithm name - reference the spec:\n                msg += \" The JWT \" +\n                        \"JWA Specification (RFC 7518, Section 3.2) states that keys used with \" + id + \" MUST have a \" +\n                        \"size >= \" + minKeyBitLength + \" bits (the key size must be greater than or equal to the hash \" +\n                        \"output size). Consider using the Jwts.SIG.\" + id + \".key() \" +\n                        \"builder to create a key guaranteed to be secure enough for \" + id + \".  See \" +\n                        \"https://tools.ietf.org/html/rfc7518#section-3.2 for more information.\";\n            } else { //custom algorithm - just indicate required key length:\n                msg += \" The \" + id + \" algorithm requires keys to have a size >= \" + minKeyBitLength + \" bits.\";\n            }\n\n            throw new WeakKeyException(msg);\n        }\n    }\n\n    @Override\n    public byte[] doDigest(final SecureRequest<InputStream, SecretKey> request) {\n        return jca(request).withMac(new CheckedFunction<Mac, byte[]>() {\n            @Override\n            public byte[] apply(Mac mac) throws Exception {\n                mac.init(request.getKey());\n                InputStream payload = request.getPayload();\n                byte[] buf = new byte[1024];\n                int len = 0;\n                while (len != -1) {\n                    len = payload.read(buf);\n                    if (len > 0) mac.update(buf, 0, len);\n                }\n                return mac.doFinal();\n            }\n        });\n    }\n\n    protected boolean doVerify(VerifySecureDigestRequest<SecretKey> request) {\n        byte[] providedSignature = request.getDigest();\n        Assert.notEmpty(providedSignature, \"Request signature byte array cannot be null or empty.\");\n        byte[] computedSignature = digest(request);\n        return MessageDigest.isEqual(providedSignature, computedSignature);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultMessage.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.Message;\n\nclass DefaultMessage<T> implements Message<T> {\n\n    private final T payload;\n\n    DefaultMessage(T payload) {\n        this.payload = Assert.notNull(payload, \"payload cannot be null.\");\n        if (payload instanceof byte[]) {\n            assertBytePayload((byte[])payload);\n        }\n    }\n    protected void assertBytePayload(byte[] payload) {\n        Assert.notEmpty(payload, \"payload byte array cannot be null or empty.\");\n    }\n\n    @Override\n    public T getPayload() {\n        return payload;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultOctetPrivateJwk.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.OctetPrivateJwk;\nimport io.jsonwebtoken.security.OctetPublicJwk;\nimport io.jsonwebtoken.security.PrivateJwk;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.util.Set;\n\nimport static io.jsonwebtoken.impl.security.DefaultOctetPublicJwk.equalsPublic;\n\npublic class DefaultOctetPrivateJwk<T extends PrivateKey, P extends PublicKey>\n        extends AbstractPrivateJwk<T, P, OctetPublicJwk<P>> implements OctetPrivateJwk<T, P> {\n\n    static final Parameter<byte[]> D = Parameters.bytes(\"d\", \"The private key\").setSecret(true).build();\n\n    static final Set<Parameter<?>> PARAMS = Collections.concat(DefaultOctetPublicJwk.PARAMS, D);\n\n    DefaultOctetPrivateJwk(JwkContext<T> ctx, OctetPublicJwk<P> pubJwk) {\n        super(ctx,\n                // only public members are included in Private JWK Thumbprints per\n                // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n                DefaultOctetPublicJwk.THUMBPRINT_PARAMS, pubJwk);\n    }\n\n    @Override\n    protected boolean equals(PrivateJwk<?, ?, ?> jwk) {\n        return jwk instanceof OctetPrivateJwk && equalsPublic(this, jwk) && Parameters.equals(this, jwk, D);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultOctetPublicJwk.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.OctetPublicJwk;\nimport io.jsonwebtoken.security.PublicJwk;\n\nimport java.security.PublicKey;\nimport java.util.List;\nimport java.util.Set;\n\npublic class DefaultOctetPublicJwk<T extends PublicKey> extends AbstractPublicJwk<T> implements OctetPublicJwk<T> {\n\n    static final String TYPE_VALUE = \"OKP\";\n    static final Parameter<String> CRV = DefaultEcPublicJwk.CRV;\n    static final Parameter<byte[]> X = Parameters.bytes(\"x\", \"The public key\").build();\n    static final Set<Parameter<?>> PARAMS = Collections.concat(AbstractAsymmetricJwk.PARAMS, CRV, X);\n\n    // https://www.rfc-editor.org/rfc/rfc8037#section-2 (last paragraph):\n    static final List<Parameter<?>> THUMBPRINT_PARAMS = Collections.<Parameter<?>>of(CRV, KTY, X);\n\n    DefaultOctetPublicJwk(JwkContext<T> ctx) {\n        super(ctx, THUMBPRINT_PARAMS);\n    }\n\n    static boolean equalsPublic(ParameterReadable self, Object candidate) {\n        return Parameters.equals(self, candidate, CRV) && Parameters.equals(self, candidate, X);\n    }\n\n    @Override\n    protected boolean equals(PublicJwk<?> jwk) {\n        return jwk instanceof OctetPublicJwk && equalsPublic(this, jwk);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRequest.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.security.Request;\n\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\npublic class DefaultRequest<T> extends DefaultMessage<T> implements Request<T> {\n\n    private final Provider provider;\n    private final SecureRandom secureRandom;\n\n    public DefaultRequest(T payload, Provider provider, SecureRandom secureRandom) {\n        super(payload);\n        this.provider = provider;\n        this.secureRandom = secureRandom;\n    }\n\n    @Override\n    public Provider getProvider() {\n        return this.provider;\n    }\n\n    @Override\n    public SecureRandom getSecureRandom() {\n        return this.secureRandom;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRsaKeyAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.KeyResult;\nimport io.jsonwebtoken.security.SecurityException;\nimport io.jsonwebtoken.security.WeakKeyException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.spec.AlgorithmParameterSpec;\n\n/**\n * @since 0.12.0\n */\npublic class DefaultRsaKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey, PrivateKey> {\n\n    private final AlgorithmParameterSpec SPEC; //can be null\n\n    private static final int MIN_KEY_BIT_LENGTH = 2048;\n\n    public DefaultRsaKeyAlgorithm(String id, String jcaTransformationString) {\n        this(id, jcaTransformationString, null);\n    }\n\n    public DefaultRsaKeyAlgorithm(String id, String jcaTransformationString, AlgorithmParameterSpec spec) {\n        super(id, jcaTransformationString);\n        this.SPEC = spec; //can be null\n    }\n\n    private static String keyType(boolean encryption) {\n        return encryption ? \"encryption\" : \"decryption\";\n    }\n\n    protected void validate(Key key, boolean encryption) { // true = encryption, false = decryption\n\n        if (!RsaSignatureAlgorithm.isRsaAlgorithmName(key)) {\n            throw new InvalidKeyException(\"Invalid RSA key algorithm name.\");\n        }\n\n        if (RsaSignatureAlgorithm.isPss(key)) {\n            String msg = \"RSASSA-PSS keys may not be used for \" + keyType(encryption) +\n                    \", only digital signature algorithms.\";\n            throw new InvalidKeyException(msg);\n        }\n\n        int size = KeysBridge.findBitLength(key);\n        if (size < 0) return; // can't validate size: material or length not available (e.g. PKCS11 or HSM)\n        if (size < MIN_KEY_BIT_LENGTH) {\n            String id = getId();\n            String section = id.startsWith(\"RSA1\") ? \"4.2\" : \"4.3\";\n            String msg = \"The RSA \" + keyType(encryption) + \" key size (aka modulus bit length) is \" + size +\n                    \" bits which is not secure enough for the \" + id + \" algorithm. \" +\n                    \"The JWT JWA Specification (RFC 7518, Section \" + section + \") states that RSA keys MUST \" +\n                    \"have a size >= \" + MIN_KEY_BIT_LENGTH + \" bits. See \" +\n                    \"https://www.rfc-editor.org/rfc/rfc7518.html#section-\" + section + \" for more information.\";\n            throw new WeakKeyException(msg);\n        }\n    }\n\n    @Override\n    public KeyResult getEncryptionKey(final KeyRequest<PublicKey> request) throws SecurityException {\n\n        Assert.notNull(request, \"Request cannot be null.\");\n        final PublicKey kek = Assert.notNull(request.getPayload(), \"RSA PublicKey encryption key cannot be null.\");\n        validate(kek, true);\n        final SecretKey cek = generateCek(request);\n\n        byte[] ciphertext = jca(request).withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            public byte[] apply(Cipher cipher) throws Exception {\n                if (SPEC == null) {\n                    cipher.init(Cipher.WRAP_MODE, kek, ensureSecureRandom(request));\n                } else {\n                    cipher.init(Cipher.WRAP_MODE, kek, SPEC, ensureSecureRandom(request));\n                }\n                return cipher.wrap(cek);\n            }\n        });\n\n        return new DefaultKeyResult(cek, ciphertext);\n    }\n\n    @Override\n    public SecretKey getDecryptionKey(DecryptionKeyRequest<PrivateKey> request) throws SecurityException {\n        Assert.notNull(request, \"request cannot be null.\");\n        final PrivateKey kek = Assert.notNull(request.getKey(), \"RSA PrivateKey decryption key cannot be null.\");\n        validate(kek, false);\n        final byte[] cekBytes = Assert.notEmpty(request.getPayload(), \"Request content (encrypted key) cannot be null or empty.\");\n\n        return jca(request).withCipher(new CheckedFunction<Cipher, SecretKey>() {\n            @Override\n            public SecretKey apply(Cipher cipher) throws Exception {\n                if (SPEC == null) {\n                    cipher.init(Cipher.UNWRAP_MODE, kek);\n                } else {\n                    cipher.init(Cipher.UNWRAP_MODE, kek, SPEC);\n                }\n                Key key = cipher.unwrap(cekBytes, AesAlgorithm.KEY_ALG_NAME, Cipher.SECRET_KEY);\n                return Assert.isInstanceOf(SecretKey.class, key, \"Cipher unwrap must return a SecretKey instance.\");\n            }\n        });\n    }\n}"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRsaPrivateJwk.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.PrivateJwk;\nimport io.jsonwebtoken.security.RsaPrivateJwk;\nimport io.jsonwebtoken.security.RsaPublicJwk;\n\nimport java.math.BigInteger;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.RSAOtherPrimeInfo;\nimport java.util.List;\nimport java.util.Set;\n\nimport static io.jsonwebtoken.impl.security.DefaultRsaPublicJwk.equalsPublic;\n\nclass DefaultRsaPrivateJwk extends AbstractPrivateJwk<RSAPrivateKey, RSAPublicKey, RsaPublicJwk> implements RsaPrivateJwk {\n\n    static final Parameter<BigInteger> PRIVATE_EXPONENT = Parameters.secretBigInt(\"d\", \"Private Exponent\");\n    static final Parameter<BigInteger> FIRST_PRIME = Parameters.secretBigInt(\"p\", \"First Prime Factor\");\n    static final Parameter<BigInteger> SECOND_PRIME = Parameters.secretBigInt(\"q\", \"Second Prime Factor\");\n    static final Parameter<BigInteger> FIRST_CRT_EXPONENT = Parameters.secretBigInt(\"dp\", \"First Factor CRT Exponent\");\n    static final Parameter<BigInteger> SECOND_CRT_EXPONENT = Parameters.secretBigInt(\"dq\", \"Second Factor CRT Exponent\");\n    static final Parameter<BigInteger> FIRST_CRT_COEFFICIENT = Parameters.secretBigInt(\"qi\", \"First CRT Coefficient\");\n    static final Parameter<List<RSAOtherPrimeInfo>> OTHER_PRIMES_INFO =\n            Parameters.builder(RSAOtherPrimeInfo.class)\n                    .setId(\"oth\").setName(\"Other Primes Info\")\n                    .setConverter(RSAOtherPrimeInfoConverter.INSTANCE).list()\n                    .build();\n\n    static final Set<Parameter<?>> PARAMS = Collections.concat(DefaultRsaPublicJwk.PARAMS,\n            PRIVATE_EXPONENT, FIRST_PRIME, SECOND_PRIME, FIRST_CRT_EXPONENT,\n            SECOND_CRT_EXPONENT, FIRST_CRT_COEFFICIENT, OTHER_PRIMES_INFO\n    );\n\n    DefaultRsaPrivateJwk(JwkContext<RSAPrivateKey> ctx, RsaPublicJwk pubJwk) {\n        super(ctx,\n                // only public members are included in Private JWK Thumbprints per\n                // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n                DefaultRsaPublicJwk.THUMBPRINT_PARAMS,\n                pubJwk);\n    }\n\n    private static boolean equals(RSAOtherPrimeInfo a, RSAOtherPrimeInfo b) {\n        if (a == b) return true;\n        if (a == null || b == null) return false;\n        return Parameters.bytesEquals(a.getPrime(), b.getPrime()) &&\n                Parameters.bytesEquals(a.getExponent(), b.getExponent()) &&\n                Parameters.bytesEquals(a.getCrtCoefficient(), b.getCrtCoefficient());\n    }\n\n    private static boolean equalsOtherPrimes(ParameterReadable a, ParameterReadable b) {\n        List<RSAOtherPrimeInfo> aOthers = a.get(OTHER_PRIMES_INFO);\n        List<RSAOtherPrimeInfo> bOthers = b.get(OTHER_PRIMES_INFO);\n        int aSize = Collections.size(aOthers);\n        int bSize = Collections.size(bOthers);\n        if (aSize != bSize) return false;\n        if (aSize == 0) return true;\n        RSAOtherPrimeInfo[] aInfos = aOthers.toArray(new RSAOtherPrimeInfo[0]);\n        RSAOtherPrimeInfo[] bInfos = bOthers.toArray(new RSAOtherPrimeInfo[0]);\n        for (int i = 0; i < aSize; i++) {\n            if (!equals(aInfos[i], bInfos[i])) return false;\n        }\n        return true;\n    }\n\n    @Override\n    protected boolean equals(PrivateJwk<?, ?, ?> jwk) {\n        return jwk instanceof RsaPrivateJwk && equalsPublic(this, jwk) &&\n                Parameters.equals(this, jwk, PRIVATE_EXPONENT) &&\n                Parameters.equals(this, jwk, FIRST_PRIME) &&\n                Parameters.equals(this, jwk, SECOND_PRIME) &&\n                Parameters.equals(this, jwk, FIRST_CRT_EXPONENT) &&\n                Parameters.equals(this, jwk, SECOND_CRT_EXPONENT) &&\n                Parameters.equals(this, jwk, FIRST_CRT_COEFFICIENT) &&\n                equalsOtherPrimes(this, (ParameterReadable) jwk);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultRsaPublicJwk.java",
    "content": "/*\n * Copyright © 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.PublicJwk;\nimport io.jsonwebtoken.security.RsaPublicJwk;\n\nimport java.math.BigInteger;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.List;\nimport java.util.Set;\n\nclass DefaultRsaPublicJwk extends AbstractPublicJwk<RSAPublicKey> implements RsaPublicJwk {\n\n    static final String TYPE_VALUE = \"RSA\";\n    static final Parameter<BigInteger> MODULUS = Parameters.bigInt(\"n\", \"Modulus\").build();\n    static final Parameter<BigInteger> PUBLIC_EXPONENT = Parameters.bigInt(\"e\", \"Public Exponent\").build();\n    static final Set<Parameter<?>> PARAMS = Collections.concat(AbstractAsymmetricJwk.PARAMS, MODULUS, PUBLIC_EXPONENT);\n\n    // https://www.rfc-editor.org/rfc/rfc7638#section-3.2\n    static final List<Parameter<?>> THUMBPRINT_PARAMS = Collections.<Parameter<?>>of(PUBLIC_EXPONENT, KTY, MODULUS);\n\n    DefaultRsaPublicJwk(JwkContext<RSAPublicKey> ctx) {\n        super(ctx, THUMBPRINT_PARAMS);\n    }\n\n    static boolean equalsPublic(ParameterReadable self, Object candidate) {\n        return Parameters.equals(self, candidate, MODULUS) && Parameters.equals(self, candidate, PUBLIC_EXPONENT);\n    }\n\n    @Override\n    protected boolean equals(PublicJwk<?> jwk) {\n        return jwk instanceof RsaPublicJwk && equalsPublic(this, jwk);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultSecretJwk.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.SecretJwk;\n\nimport javax.crypto.SecretKey;\nimport java.util.List;\nimport java.util.Set;\n\nclass DefaultSecretJwk extends AbstractJwk<SecretKey> implements SecretJwk {\n\n    static final String TYPE_VALUE = \"oct\";\n    static final Parameter<byte[]> K = Parameters.bytes(\"k\", \"Key Value\").setSecret(true).build();\n    static final Set<Parameter<?>> PARAMS = Collections.concat(AbstractJwk.PARAMS, K);\n\n    // https://www.rfc-editor.org/rfc/rfc7638#section-3.2\n    static final List<Parameter<?>> THUMBPRINT_PARAMS = Collections.<Parameter<?>>of(K, KTY);\n\n    DefaultSecretJwk(JwkContext<SecretKey> ctx) {\n        super(ctx, THUMBPRINT_PARAMS);\n    }\n\n    @Override\n    protected boolean equals(Jwk<?> jwk) {\n        return jwk instanceof SecretJwk && Parameters.equals(this, jwk, K);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultSecretKeyBuilder.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.SecretKeyBuilder;\n\nimport javax.crypto.SecretKey;\n\n/**\n * @since 0.12.0\n */\npublic class DefaultSecretKeyBuilder extends AbstractSecurityBuilder<SecretKey, SecretKeyBuilder>\n        implements SecretKeyBuilder {\n\n    protected final String JCA_NAME;\n    protected final int BIT_LENGTH;\n\n    public DefaultSecretKeyBuilder(String jcaName, int bitLength) {\n        this.JCA_NAME = Assert.hasText(jcaName, \"jcaName cannot be null or empty.\");\n        if (bitLength % Byte.SIZE != 0) {\n            String msg = \"bitLength must be an even multiple of 8\";\n            throw new IllegalArgumentException(msg);\n        }\n        this.BIT_LENGTH = Assert.gt(bitLength, 0, \"bitLength must be > 0\");\n        random(Randoms.secureRandom());\n    }\n\n    @Override\n    public SecretKey build() {\n        JcaTemplate template = new JcaTemplate(JCA_NAME, this.provider, this.random);\n        return template.generateSecretKey(this.BIT_LENGTH);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultSecureRequest.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.SecureRequest;\n\nimport java.security.Key;\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\npublic class DefaultSecureRequest<T, K extends Key> extends DefaultRequest<T> implements SecureRequest<T, K> {\n\n    private final K KEY;\n\n    public DefaultSecureRequest(T payload, Provider provider, SecureRandom secureRandom, K key) {\n        super(payload, provider, secureRandom);\n        this.KEY = Assert.notNull(key, \"key cannot be null.\");\n    }\n\n    @Override\n    public K getKey() {\n        return this.KEY;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifyDigestRequest.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.VerifyDigestRequest;\n\nimport java.io.InputStream;\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\npublic class DefaultVerifyDigestRequest extends DefaultRequest<InputStream> implements VerifyDigestRequest {\n\n    private final byte[] digest;\n\n    public DefaultVerifyDigestRequest(InputStream payload, Provider provider, SecureRandom secureRandom, byte[] digest) {\n        super(payload, provider, secureRandom);\n        this.digest = Assert.notEmpty(digest, \"Digest byte array cannot be null or empty.\");\n    }\n\n    @Override\n    public byte[] getDigest() {\n        return this.digest;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DefaultVerifySecureDigestRequest.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\n\nimport java.io.InputStream;\nimport java.security.Key;\nimport java.security.Provider;\nimport java.security.SecureRandom;\n\npublic class DefaultVerifySecureDigestRequest<K extends Key> extends DefaultSecureRequest<InputStream, K> implements VerifySecureDigestRequest<K> {\n\n    private final byte[] digest;\n\n    public DefaultVerifySecureDigestRequest(InputStream payload, Provider provider, SecureRandom secureRandom, K key, byte[] digest) {\n        super(payload, provider, secureRandom, key);\n        this.digest = Assert.notEmpty(digest, \"Digest byte array cannot be null or empty.\");\n    }\n\n    @Override\n    public byte[] getDigest() {\n        return this.digest;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DirectKeyAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.KeyResult;\nimport io.jsonwebtoken.security.SecurityException;\n\nimport javax.crypto.SecretKey;\n\n/**\n * @since 0.12.0\n */\npublic class DirectKeyAlgorithm implements KeyAlgorithm<SecretKey, SecretKey> {\n\n    static final String ID = \"dir\";\n\n    @Override\n    public String getId() {\n        return ID;\n    }\n\n    @Override\n    public KeyResult getEncryptionKey(final KeyRequest<SecretKey> request) throws SecurityException {\n        Assert.notNull(request, \"request cannot be null.\");\n        SecretKey key = Assert.notNull(request.getPayload(), \"Encryption key cannot be null.\");\n        return new DefaultKeyResult(key);\n    }\n\n    @Override\n    public SecretKey getDecryptionKey(DecryptionKeyRequest<SecretKey> request) throws SecurityException {\n        Assert.notNull(request, \"request cannot be null.\");\n        return Assert.notNull(request.getKey(), \"Decryption key cannot be null.\");\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/DispatchingJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport java.security.Key;\nimport java.util.ArrayList;\nimport java.util.Collection;\nimport java.util.List;\n\nclass DispatchingJwkFactory implements JwkFactory<Key, Jwk<Key>> {\n\n    @SuppressWarnings({\"unchecked\", \"rawtypes\"})\n    private static Collection<FamilyJwkFactory<Key, ?>> createDefaultFactories() {\n        List families = new ArrayList<>(3);\n        families.add(new SecretJwkFactory());\n        families.add(new AsymmetricJwkFactory(EcPublicJwkFactory.INSTANCE, new EcPrivateJwkFactory()));\n        families.add(new AsymmetricJwkFactory(RsaPublicJwkFactory.INSTANCE, new RsaPrivateJwkFactory()));\n        families.add(new AsymmetricJwkFactory(OctetPublicJwkFactory.INSTANCE, new OctetPrivateJwkFactory()));\n        return families;\n    }\n\n    private static final Collection<FamilyJwkFactory<Key, ?>> DEFAULT_FACTORIES = createDefaultFactories();\n    static final JwkFactory<Key, Jwk<Key>> DEFAULT_INSTANCE = new DispatchingJwkFactory();\n\n    private final Collection<FamilyJwkFactory<Key, ?>> factories;\n\n    DispatchingJwkFactory() {\n        this(DEFAULT_FACTORIES);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    DispatchingJwkFactory(Collection<? extends FamilyJwkFactory<?, ?>> factories) {\n        Assert.notEmpty(factories, \"FamilyJwkFactory collection cannot be null or empty.\");\n        this.factories = new ArrayList<>(factories.size());\n        for (FamilyJwkFactory<?, ?> factory : factories) {\n            Assert.hasText(factory.getId(), \"FamilyJwkFactory.getFactoryId() cannot return null or empty.\");\n            this.factories.add((FamilyJwkFactory<Key, ?>) factory);\n        }\n    }\n\n    @Override\n    public JwkContext<Key> newContext(JwkContext<?> src, Key key) {\n        Assert.notNull(src, \"JwkContext cannot be null.\");\n        String kty = src.getType();\n        assertKeyOrKeyType(key, kty);\n        for (FamilyJwkFactory<Key, ?> factory : this.factories) {\n            if (factory.supports(key) || factory.supports(src)) {\n                JwkContext<Key> ctx = factory.newContext(src, key);\n                return Assert.notNull(ctx, \"FamilyJwkFactory implementation cannot return null JwkContexts.\");\n            }\n        }\n        throw noFamily(key, kty);\n    }\n\n    private static void assertKeyOrKeyType(Key key, String kty) {\n        if (key == null && !Strings.hasText(kty)) {\n            String msg = \"Either a Key instance or a \" + AbstractJwk.KTY + \" value is required to create a JWK.\";\n            throw new InvalidKeyException(msg);\n        }\n    }\n\n    @Override\n    public Jwk<Key> createJwk(JwkContext<Key> ctx) {\n\n        Assert.notNull(ctx, \"JwkContext cannot be null.\");\n\n        final Key key = ctx.getKey();\n        final String kty = Strings.clean(ctx.getType());\n        assertKeyOrKeyType(key, kty);\n\n        for (FamilyJwkFactory<Key, ?> factory : this.factories) {\n            if (factory.supports(ctx)) {\n                String algFamilyId = Assert.hasText(factory.getId(), \"factory id cannot be null or empty.\");\n                if (kty == null) {\n                    ctx.setType(algFamilyId); //ensure the kty is available for the rest of the creation process\n                }\n                return factory.createJwk(ctx);\n            }\n        }\n\n        // if nothing has been returned at this point, no factory supported the JwkContext, so that's an error:\n        throw noFamily(key, kty);\n    }\n\n    private static UnsupportedKeyException noFamily(Key key, String kty) {\n        String reason = key != null ?\n                \"key of type \" + key.getClass().getName() :\n                \"kty value '\" + kty + \"'\";\n        String msg = \"Unable to create JWK for unrecognized \" + reason +\n                \": there is no known JWK Factory capable of creating JWKs for this key type.\";\n        return new UnsupportedKeyException(msg);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ECCurve.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.KeyPairBuilder;\n\nimport java.math.BigInteger;\nimport java.security.AlgorithmParameters;\nimport java.security.Key;\nimport java.security.interfaces.ECKey;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.spec.ECFieldFp;\nimport java.security.spec.ECGenParameterSpec;\nimport java.security.spec.ECParameterSpec;\nimport java.security.spec.ECPoint;\nimport java.security.spec.ECPublicKeySpec;\nimport java.security.spec.EllipticCurve;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\npublic class ECCurve extends AbstractCurve {\n\n    private static final BigInteger TWO = BigInteger.valueOf(2);\n    private static final BigInteger THREE = BigInteger.valueOf(3);\n\n    static final String KEY_PAIR_GENERATOR_JCA_NAME = \"EC\";\n\n    public static final ECCurve P256 = new ECCurve(\"P-256\", \"secp256r1\"); // JDK standard\n    public static final ECCurve P384 = new ECCurve(\"P-384\", \"secp384r1\"); // JDK standard\n    public static final ECCurve P521 = new ECCurve(\"P-521\", \"secp521r1\"); // JDK standard\n\n    public static final Collection<ECCurve> VALUES = Collections.setOf(P256, P384, P521);\n    private static final Map<String, ECCurve> BY_ID = new LinkedHashMap<>(3);\n    private static final Map<EllipticCurve, ECCurve> BY_JCA_CURVE = new LinkedHashMap<>(3);\n\n    static {\n        for (ECCurve curve : VALUES) {\n            BY_ID.put(curve.getId(), curve);\n        }\n        for (ECCurve curve : VALUES) {\n            BY_JCA_CURVE.put(curve.spec.getCurve(), curve);\n        }\n    }\n\n    static EllipticCurve assertJcaCurve(ECKey key) {\n        Assert.notNull(key, \"ECKey cannot be null.\");\n        ECParameterSpec spec = Assert.notNull(key.getParams(), \"ECKey params() cannot be null.\");\n        return Assert.notNull(spec.getCurve(), \"ECKey params().getCurve() cannot be null.\");\n    }\n\n    static ECCurve findById(String id) {\n        return BY_ID.get(id);\n    }\n\n    static ECCurve findByJcaCurve(EllipticCurve curve) {\n        return BY_JCA_CURVE.get(curve);\n    }\n\n    static ECCurve findByKey(Key key) {\n        if (!(key instanceof ECKey)) {\n            return null;\n        }\n        ECKey ecKey = (ECKey) key;\n        ECParameterSpec spec = ecKey.getParams();\n        if (spec == null) {\n            return null;\n        }\n        EllipticCurve jcaCurve = spec.getCurve();\n        ECCurve curve = BY_JCA_CURVE.get(jcaCurve);\n        if (curve != null && key instanceof ECPublicKey) {\n            ECPublicKey pub = (ECPublicKey) key;\n            ECPoint w = pub.getW();\n            if (w == null || !curve.contains(w)) { // don't support keys with a point not on its indicated curve\n                curve = null;\n            }\n        }\n        return curve;\n    }\n\n    static ECPublicKeySpec publicKeySpec(ECPrivateKey key) throws IllegalArgumentException {\n        EllipticCurve jcaCurve = assertJcaCurve(key);\n        ECCurve curve = BY_JCA_CURVE.get(jcaCurve);\n        Assert.notNull(curve, \"There is no JWA-standard Elliptic Curve for specified ECPrivateKey.\");\n        final ECPoint w = curve.multiply(key.getS());\n        return new ECPublicKeySpec(w, curve.spec);\n    }\n\n    private final ECParameterSpec spec;\n\n    public ECCurve(String id, String jcaName) {\n        super(id, jcaName);\n        JcaTemplate template = new JcaTemplate(KEY_PAIR_GENERATOR_JCA_NAME);\n        this.spec = template.withAlgorithmParameters(new CheckedFunction<AlgorithmParameters, ECParameterSpec>() {\n            @Override\n            public ECParameterSpec apply(AlgorithmParameters params) throws Exception {\n                params.init(new ECGenParameterSpec(getJcaName()));\n                return params.getParameterSpec(ECParameterSpec.class);\n            }\n        });\n    }\n\n    public ECParameterSpec toParameterSpec() {\n        return this.spec;\n    }\n\n    @Override\n    public KeyPairBuilder keyPair() {\n        return new DefaultKeyPairBuilder(KEY_PAIR_GENERATOR_JCA_NAME, toParameterSpec());\n    }\n\n    @Override\n    public boolean contains(Key key) {\n        if (key instanceof ECPublicKey) {\n            ECPublicKey pub = (ECPublicKey) key;\n            ECParameterSpec pubSpec = pub.getParams();\n            return pubSpec != null &&\n                    this.spec.getCurve().equals(pubSpec.getCurve()) &&\n                    contains(pub.getW());\n\n        }\n        return false;\n    }\n\n    boolean contains(ECPoint point) {\n        return contains(this.spec.getCurve(), point);\n    }\n\n    /**\n     * Returns {@code true} if the specified curve contains the specified {@code point}, {@code false} otherwise.\n     * Assumes elliptic curves over finite fields adhering to the reduced (a.k.a short or narrow)\n     * Weierstrass form:\n     * <p>\n     * <code>y<sup>2</sup> = x<sup>3</sup> + ax + b</code>\n     * </p>\n     *\n     * @param curve the EllipticCurve to check\n     * @param point a point that may or may not be defined on this elliptic curve\n     * @return {@code true} if this curve contains the specified {@code point}, {@code false} otherwise.\n     */\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    static boolean contains(EllipticCurve curve, ECPoint point) {\n\n        if (point == null || ECPoint.POINT_INFINITY.equals(point)) {\n            return false;\n        }\n\n        final BigInteger a = curve.getA();\n        final BigInteger b = curve.getB();\n        final BigInteger x = point.getAffineX();\n        final BigInteger y = point.getAffineY();\n\n        // The reduced Weierstrass form y^2 = x^3 + ax + b reflects an elliptic curve E over any field K (e.g. all real\n        // numbers or all complex numbers, etc). For computational simplicity, cryptographic (e.g. NIST) elliptic curves\n        // restrict K to be a field of integers modulo a prime number 'p'.  As such, we apply modulo p (the field prime)\n        // to the equation to account for the restricted field.  For a nice overview of the math behind EC curves and\n        // their application in cryptography, see\n        // https://web.northeastern.edu/dummit/docs/cryptography_5_elliptic_curves_in_cryptography.pdf\n\n        final BigInteger p = ((ECFieldFp) curve.getField()).getP();\n\n        // Verify the point coordinates are in field range:\n        if (x.compareTo(BigInteger.ZERO) < 0 || x.compareTo(p) >= 0 ||\n                y.compareTo(BigInteger.ZERO) < 0 || y.compareTo(p) >= 0) {\n            return false;\n        }\n\n        // Finally, assert Weierstrass form equality:\n        final BigInteger lhs = y.modPow(TWO, p); //mod p to account for field prime\n        final BigInteger rhs = x.modPow(THREE, p).add(a.multiply(x)).add(b).mod(p); //mod p to account for field prime\n        return lhs.equals(rhs);\n    }\n\n    /**\n     * Multiply this curve's generator (aka 'base point') by scalar {@code s} on the curve.\n     *\n     * @param s the scalar value to multiply\n     */\n    private ECPoint multiply(BigInteger s) {\n        return multiply(this.spec.getGenerator(), s);\n    }\n\n    /**\n     * Multiply a point {@code p} by scalar {@code s} on the curve.\n     *\n     * @param p the Elliptic Curve point to multiply\n     * @param s the scalar value to multiply\n     */\n    private ECPoint multiply(ECPoint p, BigInteger s) {\n\n        if (ECPoint.POINT_INFINITY.equals(p)) {\n            return p;\n        }\n\n        final BigInteger n = this.spec.getOrder();\n        final BigInteger k = s.mod(n);\n\n        ECPoint r0 = ECPoint.POINT_INFINITY;\n        ECPoint r1 = p;\n\n        // Montgomery Ladder implementation to mitigate side-channel attacks (i.e. an 'add' operation and a 'double'\n        // operation is calculated for every loop iteration, regardless if the 'add'' is needed or not)\n        // See: https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#Montgomery_ladder\n//        while (k.compareTo(BigInteger.ZERO) > 0) {\n//            ECPoint temp = add(r0, r1, curve);\n//            r0 = k.testBit(0) ? temp : r0;\n//            r1 = doublePoint(r1, curve);\n//            k = k.shiftRight(1);\n//        }\n        // above implementation (k.compareTo/k.shiftRight) works correctly , but this is a little faster:\n        for (int i = k.bitLength() - 1; i >= 0; i--) {\n            if (k.testBit(i)) { // bit == 1\n                r0 = add(r0, r1);\n                r1 = doublePoint(r1);\n            } else { // bit == 0\n                r1 = add(r0, r1);\n                r0 = doublePoint(r0);\n            }\n        }\n\n        return r0;\n    }\n\n    private ECPoint add(ECPoint P, ECPoint Q) {\n\n        if (ECPoint.POINT_INFINITY.equals(P)) {\n            return Q;\n        } else if (ECPoint.POINT_INFINITY.equals(Q)) {\n            return P;\n        } else if (P.equals(Q)) {\n            return doublePoint(P);\n        }\n\n        final EllipticCurve curve = this.spec.getCurve();\n\n        final BigInteger Px = P.getAffineX();\n        final BigInteger Py = P.getAffineY();\n        final BigInteger Qx = Q.getAffineX();\n        final BigInteger Qy = Q.getAffineY();\n        final BigInteger prime = ((ECFieldFp) curve.getField()).getP();\n        final BigInteger slope = Qy.subtract(Py).multiply(Qx.subtract(Px).modInverse(prime)).mod(prime);\n        final BigInteger Rx = slope.pow(2).subtract(Px).subtract(Qx).mod(prime);\n        final BigInteger Ry = slope.multiply(Px.subtract(Rx)).subtract(Py).mod(prime);\n\n        return new ECPoint(Rx, Ry);\n    }\n\n    private ECPoint doublePoint(ECPoint P) {\n\n        if (ECPoint.POINT_INFINITY.equals(P)) {\n            return P;\n        }\n\n        final EllipticCurve curve = this.spec.getCurve();\n        final BigInteger Px = P.getAffineX();\n        final BigInteger Py = P.getAffineY();\n        final BigInteger p = ((ECFieldFp) curve.getField()).getP();\n        final BigInteger a = curve.getA();\n        final BigInteger s = THREE.multiply(Px.pow(2)).add(a).mod(p).multiply(TWO.multiply(Py).modInverse(p)).mod(p);\n        final BigInteger x = s.pow(2).subtract(TWO.multiply(Px)).mod(p);\n        final BigInteger y = s.multiply(Px.subtract(x)).subtract(Py).mod(p);\n\n        return new ECPoint(x, y);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/EcPrivateJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.EcPrivateJwk;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.InvalidKeyException;\n\nimport java.math.BigInteger;\nimport java.security.KeyFactory;\nimport java.security.PublicKey;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.spec.ECPrivateKeySpec;\nimport java.security.spec.ECPublicKeySpec;\nimport java.security.spec.InvalidKeySpecException;\n\nclass EcPrivateJwkFactory extends AbstractEcJwkFactory<ECPrivateKey, EcPrivateJwk> {\n\n    private static final String ECPUBKEY_ERR_MSG = \"JwkContext publicKey must be an \" + ECPublicKey.class.getName() +\n            \" instance.\";\n\n    private static final EcPublicJwkFactory PUB_FACTORY = EcPublicJwkFactory.INSTANCE;\n\n    EcPrivateJwkFactory() {\n        super(ECPrivateKey.class, DefaultEcPrivateJwk.PARAMS);\n    }\n\n    @Override\n    protected boolean supportsKeyValues(JwkContext<?> ctx) {\n        return super.supportsKeyValues(ctx) && ctx.containsKey(DefaultEcPrivateJwk.D.getId());\n    }\n\n    // visible for testing\n    protected ECPublicKey derivePublic(KeyFactory keyFactory, ECPublicKeySpec spec) throws InvalidKeySpecException {\n        return (ECPublicKey) keyFactory.generatePublic(spec);\n    }\n\n    protected ECPublicKey derivePublic(final JwkContext<ECPrivateKey> ctx) {\n        final ECPrivateKey key = ctx.getKey();\n        return generateKey(ctx, ECPublicKey.class, new CheckedFunction<KeyFactory, ECPublicKey>() {\n            @Override\n            public ECPublicKey apply(KeyFactory kf) {\n                try {\n                    ECPublicKeySpec spec = ECCurve.publicKeySpec(key);\n                    return derivePublic(kf, spec);\n                } catch (Exception e) {\n                    String msg = \"Unable to derive ECPublicKey from ECPrivateKey: \" + e.getMessage();\n                    throw new InvalidKeyException(msg, e);\n                }\n            }\n        });\n    }\n\n    @Override\n    protected EcPrivateJwk createJwkFromKey(JwkContext<ECPrivateKey> ctx) {\n\n        ECPrivateKey key = ctx.getKey();\n        ECPublicKey ecPublicKey;\n\n        PublicKey publicKey = ctx.getPublicKey();\n        if (publicKey != null) {\n            ecPublicKey = Assert.isInstanceOf(ECPublicKey.class, publicKey, ECPUBKEY_ERR_MSG);\n        } else {\n            ecPublicKey = derivePublic(ctx);\n        }\n\n        // [JWA spec](https://tools.ietf.org/html/rfc7518#section-6.2.2)\n        // requires public values to be present in private JWKs, so add them:\n\n        // If a JWK fingerprint has been requested to be the JWK id, ensure we copy over the one computed for the\n        // public key per https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        boolean copyId = !Strings.hasText(ctx.getId()) && ctx.getIdThumbprintAlgorithm() != null;\n\n        JwkContext<ECPublicKey> pubCtx = PUB_FACTORY.newContext(ctx, ecPublicKey);\n        EcPublicJwk pubJwk = PUB_FACTORY.createJwk(pubCtx);\n        ctx.putAll(pubJwk); // add public values to private key context\n        if (copyId) {\n            ctx.setId(pubJwk.getId());\n        }\n\n        String d = toOctetString(key.getParams().getCurve(), key.getS());\n        ctx.put(DefaultEcPrivateJwk.D.getId(), d);\n\n        return new DefaultEcPrivateJwk(ctx, pubJwk);\n    }\n\n    @Override\n    protected EcPrivateJwk createJwkFromValues(final JwkContext<ECPrivateKey> ctx) {\n\n        ParameterReadable reader = new RequiredParameterReader(ctx);\n        String curveId = reader.get(DefaultEcPublicJwk.CRV);\n        BigInteger d = reader.get(DefaultEcPrivateJwk.D);\n\n        // We don't actually need the public x,y point coordinates for JVM lookup, but the\n        // [JWA spec](https://tools.ietf.org/html/rfc7518#section-6.2.2)\n        // requires them to be present and valid for the private key as well, so we assert that here:\n        JwkContext<ECPublicKey> pubCtx = new DefaultJwkContext<>(DefaultEcPublicJwk.PARAMS, ctx);\n        EcPublicJwk pubJwk = EcPublicJwkFactory.INSTANCE.createJwk(pubCtx);\n\n        ECCurve curve = getCurveByJwaId(curveId);\n        final ECPrivateKeySpec privateSpec = new ECPrivateKeySpec(d, curve.toParameterSpec());\n        ECPrivateKey key = generateKey(ctx, new CheckedFunction<KeyFactory, ECPrivateKey>() {\n            @Override\n            public ECPrivateKey apply(KeyFactory kf) throws Exception {\n                return (ECPrivateKey) kf.generatePrivate(privateSpec);\n            }\n        });\n\n        ctx.setKey(key);\n\n        return new DefaultEcPrivateJwk(ctx, pubJwk);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/EcPublicJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.InvalidKeyException;\n\nimport java.math.BigInteger;\nimport java.security.KeyFactory;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.spec.ECParameterSpec;\nimport java.security.spec.ECPoint;\nimport java.security.spec.ECPublicKeySpec;\nimport java.security.spec.EllipticCurve;\nimport java.util.Map;\n\nclass EcPublicJwkFactory extends AbstractEcJwkFactory<ECPublicKey, EcPublicJwk> {\n\n    private static final String UNSUPPORTED_CURVE_MSG = \"The specified ECKey curve does not match a JWA standard curve id.\";\n\n    static final EcPublicJwkFactory INSTANCE = new EcPublicJwkFactory();\n\n    EcPublicJwkFactory() {\n        super(ECPublicKey.class, DefaultEcPublicJwk.PARAMS);\n    }\n\n    protected static String keyContainsErrorMessage(String curveId) {\n        Assert.hasText(curveId, \"curveId cannot be null or empty.\");\n        String fmt = \"ECPublicKey's ECPoint does not exist on elliptic curve '%s' \" +\n                \"and may not be used to create '%s' JWKs.\";\n        return String.format(fmt, curveId, curveId);\n    }\n\n    protected static String jwkContainsErrorMessage(String curveId, Map<String, ?> jwk) {\n        Assert.hasText(curveId, \"curveId cannot be null or empty.\");\n        String fmt = \"EC JWK x,y coordinates do not exist on elliptic curve '%s'. This \" +\n                \"could be due simply to an incorrectly-created JWK or possibly an attempted Invalid Curve Attack \" +\n                \"(see https://safecurves.cr.yp.to/twist.html for more information).\";\n        return String.format(fmt, curveId, jwk);\n    }\n\n    protected static String getJwaIdByCurve(EllipticCurve curve) {\n        ECCurve c = ECCurve.findByJcaCurve(curve);\n        if (c == null) {\n            throw new InvalidKeyException(UNSUPPORTED_CURVE_MSG);\n        }\n        return c.getId();\n    }\n\n    @Override\n    protected EcPublicJwk createJwkFromKey(JwkContext<ECPublicKey> ctx) {\n\n        ECPublicKey key = ctx.getKey();\n\n        ECParameterSpec spec = key.getParams();\n        EllipticCurve curve = spec.getCurve();\n        ECPoint point = key.getW();\n\n        String curveId = getJwaIdByCurve(curve);\n        if (!ECCurve.contains(curve, point)) {\n            String msg = keyContainsErrorMessage(curveId);\n            throw new InvalidKeyException(msg);\n        }\n\n        ctx.put(DefaultEcPublicJwk.CRV.getId(), curveId);\n\n        String x = toOctetString(curve, point.getAffineX());\n        ctx.put(DefaultEcPublicJwk.X.getId(), x);\n\n        String y = toOctetString(curve, point.getAffineY());\n        ctx.put(DefaultEcPublicJwk.Y.getId(), y);\n\n        return new DefaultEcPublicJwk(ctx);\n    }\n\n    @Override\n    protected EcPublicJwk createJwkFromValues(final JwkContext<ECPublicKey> ctx) {\n\n        ParameterReadable reader = new RequiredParameterReader(ctx);\n        String curveId = reader.get(DefaultEcPublicJwk.CRV);\n        BigInteger x = reader.get(DefaultEcPublicJwk.X);\n        BigInteger y = reader.get(DefaultEcPublicJwk.Y);\n\n        ECCurve curve = getCurveByJwaId(curveId);\n        ECPoint point = new ECPoint(x, y);\n\n        if (!curve.contains(point)) {\n            String msg = jwkContainsErrorMessage(curveId, ctx);\n            throw new InvalidKeyException(msg);\n        }\n\n        final ECPublicKeySpec pubSpec = new ECPublicKeySpec(point, curve.toParameterSpec());\n        ECPublicKey key = generateKey(ctx, new CheckedFunction<KeyFactory, ECPublicKey>() {\n            @Override\n            public ECPublicKey apply(KeyFactory kf) throws Exception {\n                return (ECPublicKey) kf.generatePublic(pubSpec);\n            }\n        });\n\n        ctx.setKey(key);\n\n        return new DefaultEcPublicJwk(ctx);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/EcSignatureAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.JwtException;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyPairBuilder;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SignatureAlgorithm;\nimport io.jsonwebtoken.security.SignatureException;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\n\nimport java.io.InputStream;\nimport java.math.BigInteger;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.Signature;\nimport java.security.interfaces.ECKey;\nimport java.security.spec.ECGenParameterSpec;\nimport java.util.Arrays;\nimport java.util.LinkedHashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\n\n// @since 0.12.0\nfinal class EcSignatureAlgorithm extends AbstractSignatureAlgorithm {\n\n    private static final String REQD_ORDER_BIT_LENGTH_MSG = \"orderBitLength must equal 256, 384, or 521.\";\n\n    private static final String DER_ENCODING_SYS_PROPERTY_NAME = \"io.jsonwebtoken.impl.crypto.EllipticCurveSignatureValidator.derEncodingSupported\";\n\n    private static final String ES256_OID = \"1.2.840.10045.4.3.2\";\n    private static final String ES384_OID = \"1.2.840.10045.4.3.3\";\n    private static final String ES512_OID = \"1.2.840.10045.4.3.4\";\n\n    private static final Set<String> KEY_ALG_NAMES = Collections.setOf(\"EC\", \"ECDSA\", ES256_OID, ES384_OID, ES512_OID);\n\n    private final ECGenParameterSpec KEY_PAIR_GEN_PARAMS;\n\n    private final int orderBitLength;\n\n    private final String OID;\n\n    /**\n     * JWA EC (concat formatted) length in bytes for this instance's {@link #orderBitLength}.\n     */\n    private final int signatureByteLength;\n    private final int sigFieldByteLength;\n\n    private static int shaSize(int orderBitLength) {\n        return orderBitLength == 521 ? 512 : orderBitLength;\n    }\n\n    /**\n     * Returns {@code true} for Order bit lengths defined in the JWA specification, {@code false} otherwise.\n     * Specifically, returns {@code true} <em>only</em> for values of {@code 256}, {@code 384} and {@code 521}.  See\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4\">RFC 7518, Section 3.4</a> for more.\n     *\n     * @param orderBitLength the EC key Order bit length to check\n     * @return {@code true} for Order bit lengths defined in the JWA specification, {@code false} otherwise.\n     */\n    private static boolean isSupportedOrderBitLength(int orderBitLength) {\n        // This implementation supports only those defined in the JWA specification.\n        return orderBitLength == 256 || orderBitLength == 384 || orderBitLength == 521;\n    }\n\n    static final EcSignatureAlgorithm ES256 = new EcSignatureAlgorithm(256, ES256_OID);\n    static final EcSignatureAlgorithm ES384 = new EcSignatureAlgorithm(384, ES384_OID);\n    static final EcSignatureAlgorithm ES512 = new EcSignatureAlgorithm(521, ES512_OID);\n\n    private static final Map<String, SignatureAlgorithm> BY_OID = new LinkedHashMap<>(3);\n\n    static {\n        for (EcSignatureAlgorithm alg : Collections.of(ES256, ES384, ES512)) {\n            BY_OID.put(alg.OID, alg);\n        }\n    }\n\n    static SignatureAlgorithm findByKey(Key key) {\n\n        String algName = KeysBridge.findAlgorithm(key);\n        if (!Strings.hasText(algName)) {\n            return null;\n        }\n        algName = algName.toUpperCase(Locale.ENGLISH);\n\n        SignatureAlgorithm alg = BY_OID.get(algName);\n        if (alg != null) {\n            return alg;\n        }\n\n        if (\"EC\".equalsIgnoreCase(algName) || \"ECDSA\".equalsIgnoreCase(algName)) {\n            // some PKCS11 keystores and HSMs won't expose the RSAKey interface, so we can't assume it:\n            final int bitLength = KeysBridge.findBitLength(key); // returns -1 if we're unable to find out\n            if (bitLength == ES512.orderBitLength) {\n                return ES512;\n            } else if (bitLength == ES384.orderBitLength) {\n                return ES384;\n            } else if (bitLength == ES256.orderBitLength) {\n                return ES256;\n            }\n        }\n\n        return null;\n    }\n\n    private EcSignatureAlgorithm(int orderBitLength, String oid) {\n        super(\"ES\" + shaSize(orderBitLength), \"SHA\" + shaSize(orderBitLength) + \"withECDSA\");\n        Assert.isTrue(isSupportedOrderBitLength(orderBitLength), REQD_ORDER_BIT_LENGTH_MSG);\n        this.OID = Assert.hasText(oid, \"Invalid OID.\");\n        String curveName = \"secp\" + orderBitLength + \"r1\";\n        this.KEY_PAIR_GEN_PARAMS = new ECGenParameterSpec(curveName);\n        this.orderBitLength = orderBitLength;\n        this.sigFieldByteLength = Bytes.length(this.orderBitLength);\n        this.signatureByteLength = this.sigFieldByteLength * 2; // R bytes + S bytes = concat signature bytes\n    }\n\n    @Override\n    public KeyPairBuilder keyPair() {\n        return new DefaultKeyPairBuilder(ECCurve.KEY_PAIR_GENERATOR_JCA_NAME, this.KEY_PAIR_GEN_PARAMS)\n                .random(Randoms.secureRandom());\n    }\n\n    @Override\n    protected void validateKey(Key key, boolean signing) {\n        super.validateKey(key, signing);\n        if (!KEY_ALG_NAMES.contains(KeysBridge.findAlgorithm(key))) {\n            throw new InvalidKeyException(\"Unrecognized EC key algorithm name.\");\n        }\n        int size = KeysBridge.findBitLength(key);\n        if (size < 0) return; // likely PKCS11 or HSM key, can't get the data we need\n        int sigFieldByteLength = Bytes.length(size);\n        int concatByteLength = sigFieldByteLength * 2;\n        if (concatByteLength != this.signatureByteLength) {\n            String msg = \"The provided Elliptic Curve \" + keyType(signing) +\n                    \" key size (aka order bit length) is \" + Bytes.bitsMsg(size) + \", but the '\" +\n                    getId() + \"' algorithm requires EC Keys with \" + Bytes.bitsMsg(this.orderBitLength) +\n                    \" per [RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).\";\n            throw new InvalidKeyException(msg);\n        }\n    }\n\n    @Override\n    protected byte[] doDigest(final SecureRequest<InputStream, PrivateKey> request) {\n        return jca(request).withSignature(new CheckedFunction<Signature, byte[]>() {\n            @Override\n            public byte[] apply(Signature sig) throws Exception {\n                sig.initSign(KeysBridge.root(request));\n                byte[] signature = sign(sig, request.getPayload());\n                return transcodeDERToConcat(signature, signatureByteLength);\n            }\n        });\n    }\n\n    boolean isValidRAndS(PublicKey key, byte[] concatSignature) {\n        if (key instanceof ECKey) { //Some PKCS11 providers and HSMs won't expose the ECKey interface, so we have to check first\n            ECKey ecKey = (ECKey) key;\n            BigInteger order = ecKey.getParams().getOrder();\n            BigInteger r = new BigInteger(1, Arrays.copyOfRange(concatSignature, 0, sigFieldByteLength));\n            BigInteger s = new BigInteger(1, Arrays.copyOfRange(concatSignature, sigFieldByteLength, concatSignature.length));\n            return r.signum() >= 1 && s.signum() >= 1 && r.compareTo(order) < 0 && s.compareTo(order) < 0;\n        }\n        return true;\n    }\n\n    @Override\n    protected boolean doVerify(final VerifySecureDigestRequest<PublicKey> request) {\n\n        final PublicKey key = request.getKey();\n\n        return jca(request).withSignature(new CheckedFunction<Signature, Boolean>() {\n            @Override\n            public Boolean apply(Signature sig) {\n                byte[] concatSignature = request.getDigest();\n                byte[] derSignature;\n                try {\n                    // mandated per https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4 :\n                    if (signatureByteLength != concatSignature.length) {\n                        /*\n                         * If the expected size is not valid for JOSE, fall back to ASN.1 DER signature IFF the application\n                         * is configured to do so.  This fallback is for backwards compatibility ONLY (to support tokens\n                         * generated by early versions of jjwt) and backwards compatibility will be removed in a future\n                         * version of this library.  This fallback is only enabled if the system property is set to 'true' due to\n                         * the risk of CVE-2022-21449 attacks on early JVM versions 15, 17 and 18.\n                         */\n                        // TODO: remove for 1.0 (DER-encoding support is not in the JWT RFCs)\n                        if (concatSignature[0] == 0x30 &&\n                                \"true\".equalsIgnoreCase(System.getProperty(DER_ENCODING_SYS_PROPERTY_NAME))) {\n                            derSignature = concatSignature;\n                        } else {\n                            String msg = \"Provided signature is \" + Bytes.bytesMsg(concatSignature.length) + \" but \" +\n                                    getId() + \" signatures must be exactly \" + Bytes.bytesMsg(signatureByteLength) +\n                                    \" per [RFC 7518, Section 3.4 (validation)]\" +\n                                    \"(https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).\";\n                            throw new SignatureException(msg);\n                        }\n                    } else {\n                        //guard for JVM security bug CVE-2022-21449:\n                        if (!isValidRAndS(key, concatSignature)) {\n                            return false;\n                        }\n\n                        // Convert from concat to DER encoding since\n                        // 1) SHAXXXWithECDSAInP1363Format algorithms are only available on >= JDK 9 and\n                        // 2) the SignatureAlgorithm enum JCA alg names are all SHAXXXwithECDSA (which expects DER formatting)\n                        derSignature = transcodeConcatToDER(concatSignature);\n                    }\n\n                    sig.initVerify(key);\n                    return verify(sig, request.getPayload(), derSignature);\n\n                } catch (Exception e) {\n                    String msg = \"Unable to verify Elliptic Curve signature using provided ECPublicKey: \" + e.getMessage();\n                    throw new SignatureException(msg, e);\n                }\n            }\n        });\n    }\n\n    /**\n     * Transcodes the JCA ASN.1/DER-encoded signature into the concatenated\n     * R + S format expected by ECDSA JWS.\n     *\n     * @param derSignature The ASN1./DER-encoded. Must not be {@code null}.\n     * @param outputLength The expected length of the ECDSA JWS signature.\n     * @return The ECDSA JWS encoded signature.\n     * @throws JwtException If the ASN.1/DER signature format is invalid.\n     * @author Martin Treurnicht via <a href=\"https://github.com/jwtk/jjwt/commit/61510dfca58dd40b4b32c708935126785dcff48c\">61510dfca58dd40b4b32c708935126785dcff48c</a>\n     */\n    public static byte[] transcodeDERToConcat(final byte[] derSignature, int outputLength) throws JwtException {\n\n        if (derSignature.length < 8 || derSignature[0] != 48) {\n            throw new JwtException(\"Invalid ECDSA signature format\");\n        }\n\n        int offset;\n        if (derSignature[1] > 0) {\n            offset = 2;\n        } else if (derSignature[1] == (byte) 0x81) {\n            offset = 3;\n        } else {\n            throw new JwtException(\"Invalid ECDSA signature format\");\n        }\n\n        byte rLength = derSignature[offset + 1];\n\n        int i = rLength;\n        while ((i > 0) && (derSignature[(offset + 2 + rLength) - i] == 0)) {\n            i--;\n        }\n\n        byte sLength = derSignature[offset + 2 + rLength + 1];\n\n        int j = sLength;\n        while ((j > 0) && (derSignature[(offset + 2 + rLength + 2 + sLength) - j] == 0)) {\n            j--;\n        }\n\n        int rawLen = Math.max(i, j);\n        rawLen = Math.max(rawLen, outputLength / 2);\n\n        if ((derSignature[offset - 1] & 0xff) != derSignature.length - offset ||\n                (derSignature[offset - 1] & 0xff) != 2 + rLength + 2 + sLength ||\n                derSignature[offset] != 2 || derSignature[offset + 2 + rLength] != 2) {\n            throw new JwtException(\"Invalid ECDSA signature format\");\n        }\n\n        final byte[] concatSignature = new byte[2 * rawLen];\n\n        System.arraycopy(derSignature, (offset + 2 + rLength) - i, concatSignature, rawLen - i, i);\n        System.arraycopy(derSignature, (offset + 2 + rLength + 2 + sLength) - j, concatSignature, 2 * rawLen - j, j);\n\n        return concatSignature;\n    }\n\n    /**\n     * Transcodes the ECDSA JWS signature into ASN.1/DER format for use by the JCA verifier.\n     *\n     * @param jwsSignature The JWS signature, consisting of the concatenated R and S values. Must not be {@code null}.\n     * @return The ASN.1/DER encoded signature.\n     * @throws JwtException If the ECDSA JWS signature format is invalid.\n     */\n    public static byte[] transcodeConcatToDER(byte[] jwsSignature) throws JwtException {\n        try {\n            return concatToDER(jwsSignature);\n        } catch (Exception e) { // CVE-2022-21449 guard\n            String msg = \"Invalid ECDSA signature format.\";\n            throw new SignatureException(msg, e);\n        }\n    }\n\n    /**\n     * Converts the specified concat-encoded signature to a DER-encoded signature.\n     *\n     * @param jwsSignature concat-encoded signature\n     * @return correpsonding DER-encoded signature\n     * @throws ArrayIndexOutOfBoundsException if the signature cannot be converted\n     * @author Martin Treurnicht via <a href=\"https://github.com/jwtk/jjwt/commit/61510dfca58dd40b4b32c708935126785dcff48c\">61510dfca58dd40b4b32c708935126785dcff48c</a>\n     */\n    private static byte[] concatToDER(byte[] jwsSignature) throws ArrayIndexOutOfBoundsException {\n\n        int rawLen = jwsSignature.length / 2;\n\n        int i = rawLen;\n\n        while ((i > 0) && (jwsSignature[rawLen - i] == 0)) {\n            i--;\n        }\n\n        int j = i;\n\n        if (jwsSignature[rawLen - i] < 0) {\n            j += 1;\n        }\n\n        int k = rawLen;\n\n        while ((k > 0) && (jwsSignature[2 * rawLen - k] == 0)) {\n            k--;\n        }\n\n        int l = k;\n\n        if (jwsSignature[2 * rawLen - k] < 0) {\n            l += 1;\n        }\n\n        int len = 2 + j + 2 + l;\n\n        if (len > 255) {\n            throw new JwtException(\"Invalid ECDSA signature format\");\n        }\n\n        int offset;\n\n        final byte[] derSignature;\n\n        if (len < 128) {\n            derSignature = new byte[2 + 2 + j + 2 + l];\n            offset = 1;\n        } else {\n            derSignature = new byte[3 + 2 + j + 2 + l];\n            derSignature[1] = (byte) 0x81;\n            offset = 2;\n        }\n\n        derSignature[0] = 48;\n        derSignature[offset++] = (byte) len;\n        derSignature[offset++] = 2;\n        derSignature[offset++] = (byte) j;\n\n        System.arraycopy(jwsSignature, rawLen - i, derSignature, (offset + j) - i, i);\n\n        offset += j;\n\n        derSignature[offset++] = 2;\n        derSignature[offset++] = (byte) l;\n\n        System.arraycopy(jwsSignature, 2 * rawLen - k, derSignature, (offset + l) - k, k);\n\n        return derSignature;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/EcdhKeyAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.impl.DefaultJweHeader;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.Curve;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\nimport io.jsonwebtoken.security.DynamicJwkBuilder;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.KeyLengthSupplier;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.KeyResult;\nimport io.jsonwebtoken.security.OctetPublicJwk;\nimport io.jsonwebtoken.security.PublicJwk;\nimport io.jsonwebtoken.security.Request;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SecurityException;\n\nimport javax.crypto.KeyAgreement;\nimport javax.crypto.SecretKey;\nimport java.nio.charset.StandardCharsets;\nimport java.security.Key;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.interfaces.ECKey;\n\n/**\n * @since 0.12.0\n */\nclass EcdhKeyAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<PublicKey, PrivateKey> {\n\n    protected static final String JCA_NAME = \"ECDH\";\n    protected static final String XDH_JCA_NAME = \"XDH\";\n    protected static final String DEFAULT_ID = JCA_NAME + \"-ES\";\n\n    // Per https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2, 2nd paragraph:\n    //    Key derivation is performed using the Concat KDF, as defined in\n    //    Section 5.8.1 of [NIST.800-56A](https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf),\n    //    where the Digest Method is SHA-256.\n    private static final String CONCAT_KDF_HASH_ALG_NAME = \"SHA-256\";\n    private static final ConcatKDF CONCAT_KDF = new ConcatKDF(CONCAT_KDF_HASH_ALG_NAME);\n\n    private final KeyAlgorithm<SecretKey, SecretKey> WRAP_ALG;\n\n    private static String idFor(KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {\n        return wrapAlg instanceof DirectKeyAlgorithm ? DEFAULT_ID : DEFAULT_ID + \"+\" + wrapAlg.getId();\n    }\n\n    EcdhKeyAlgorithm() {\n        // default ECDH-ES doesn't do a wrap, so we use DirectKeyAlgorithm which is a no-op.  That is, we're using\n        // the Null Object Design Pattern so we don't have to check for null depending on if key wrapping is used or not\n        this(new DirectKeyAlgorithm());\n    }\n\n    EcdhKeyAlgorithm(KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {\n        super(idFor(wrapAlg), JCA_NAME);\n        this.WRAP_ALG = Assert.notNull(wrapAlg, \"Wrap algorithm cannot be null.\");\n    }\n\n    //visible for testing, for Edwards elliptic curves\n    protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {\n        return curve.keyPair().provider(provider).random(random).build();\n    }\n\n    protected byte[] generateZ(final KeyRequest<?> request, final PublicKey pub, final PrivateKey priv) {\n        return jca(request).withKeyAgreement(new CheckedFunction<KeyAgreement, byte[]>() {\n            @Override\n            public byte[] apply(KeyAgreement keyAgreement) throws Exception {\n                keyAgreement.init(KeysBridge.root(priv), ensureSecureRandom(request));\n                keyAgreement.doPhase(pub, true);\n                return keyAgreement.generateSecret();\n            }\n        });\n    }\n\n    protected String getConcatKDFAlgorithmId(AeadAlgorithm enc) {\n        return this.WRAP_ALG instanceof DirectKeyAlgorithm ? Assert.hasText(enc.getId(),\n                \"AeadAlgorithm id cannot be null or empty.\") : getId();\n    }\n\n    private byte[] createOtherInfo(int keydatalen, String AlgorithmID, byte[] PartyUInfo, byte[] PartyVInfo) {\n\n        // https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2 \"AlgorithmID\":\n        Assert.hasText(AlgorithmID, \"AlgorithmId cannot be null or empty.\");\n        byte[] algIdBytes = AlgorithmID.getBytes(StandardCharsets.US_ASCII);\n\n        PartyUInfo = Arrays.length(PartyUInfo) == 0 ? Bytes.EMPTY : PartyUInfo; // ensure not null\n        PartyVInfo = Arrays.length(PartyVInfo) == 0 ? Bytes.EMPTY : PartyVInfo; // ensure not null\n\n        // Values and order defined in https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2 and\n        // https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf section 5.8.1.2 :\n        return Bytes.concat(Bytes.toBytes(algIdBytes.length), algIdBytes, // AlgorithmID\n                Bytes.toBytes(PartyUInfo.length), PartyUInfo, // PartyUInfo\n                Bytes.toBytes(PartyVInfo.length), PartyVInfo, // PartyVInfo\n                Bytes.toBytes(keydatalen),                    // SuppPubInfo per https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\n                Bytes.EMPTY                                   // SuppPrivInfo empty per https://www.rfc-editor.org/rfc/rfc7518.html#section-4.6.2\n        );\n    }\n\n    private int getKeyBitLength(AeadAlgorithm enc) {\n        int bitLength = this.WRAP_ALG instanceof KeyLengthSupplier ?\n                ((KeyLengthSupplier) this.WRAP_ALG).getKeyBitLength() : enc.getKeyBitLength();\n        return Assert.gt(bitLength, 0, \"Algorithm keyBitLength must be > 0\");\n    }\n\n    private SecretKey deriveKey(KeyRequest<?> request, PublicKey publicKey, PrivateKey privateKey) {\n        AeadAlgorithm enc = Assert.notNull(request.getEncryptionAlgorithm(),\n                \"Request encryptionAlgorithm cannot be null.\");\n        int requiredCekBitLen = getKeyBitLength(enc);\n        final String AlgorithmID = getConcatKDFAlgorithmId(enc);\n        byte[] apu = request.getHeader().getAgreementPartyUInfo();\n        byte[] apv = request.getHeader().getAgreementPartyVInfo();\n        byte[] OtherInfo = createOtherInfo(requiredCekBitLen, AlgorithmID, apu, apv);\n        byte[] Z = generateZ(request, publicKey, privateKey);\n        try {\n            return CONCAT_KDF.deriveKey(Z, requiredCekBitLen, OtherInfo);\n        } finally {\n            Bytes.clear(Z);\n        }\n    }\n\n    @Override\n    protected String getJcaName(Request<?> request) {\n        if (request instanceof SecureRequest) {\n            return ((SecureRequest<?, ?>) request).getKey() instanceof ECKey ? super.getJcaName(request) : XDH_JCA_NAME;\n        } else {\n            return request.getPayload() instanceof ECKey ? super.getJcaName(request) : XDH_JCA_NAME;\n        }\n    }\n\n    private static AbstractCurve assertCurve(Key key) {\n        Curve curve = StandardCurves.findByKey(key);\n        if (curve == null) {\n            String type = key instanceof PublicKey ? \"encryption \" : \"decryption \";\n            String msg = \"Unable to determine JWA-standard Elliptic Curve for \" + type + \"key [\" +\n                    KeysBridge.toString(key) + \"]\";\n            throw new InvalidKeyException(msg);\n        }\n        if (curve instanceof EdwardsCurve && ((EdwardsCurve) curve).isSignatureCurve()) {\n            String msg = curve.getId() + \" keys may not be used with ECDH-ES key agreement algorithms per \" +\n                    \"https://www.rfc-editor.org/rfc/rfc8037#section-3.1.\";\n            throw new InvalidKeyException(msg);\n        }\n        return Assert.isInstanceOf(AbstractCurve.class, curve, \"AbstractCurve instance expected.\");\n    }\n\n    @Override\n    public KeyResult getEncryptionKey(KeyRequest<PublicKey> request) throws SecurityException {\n        Assert.notNull(request, \"Request cannot be null.\");\n        JweHeader header = Assert.notNull(request.getHeader(), \"Request JweHeader cannot be null.\");\n        PublicKey publicKey = Assert.notNull(request.getPayload(), \"Encryption PublicKey cannot be null.\");\n\n        Curve curve = assertCurve(publicKey);\n        // note: we don't need to validate if specified key's point is on a supported curve here\n        // because that will automatically be asserted when using Jwks.builder().... below\n        Assert.stateNotNull(curve, \"Internal implementation state: Curve cannot be null.\");\n\n        // Generate our ephemeral key pair:\n        final SecureRandom random = ensureSecureRandom(request);\n        DynamicJwkBuilder<?, ?> jwkBuilder = Jwks.builder().random(random);\n        KeyPair pair = generateKeyPair(curve, null, random);\n\n        Assert.stateNotNull(pair, \"Internal implementation state: KeyPair cannot be null.\");\n\n        // This asserts that the generated public key (and therefore the request key) is on a JWK-supported curve:\n        PublicJwk<?> jwk = jwkBuilder.key(pair.getPublic()).build();\n\n        final SecretKey derived = deriveKey(request, publicKey, pair.getPrivate());\n\n        KeyRequest<SecretKey> wrapReq = new DefaultKeyRequest<>(derived, request.getProvider(),\n                request.getSecureRandom(), request.getHeader(), request.getEncryptionAlgorithm());\n        KeyResult result = WRAP_ALG.getEncryptionKey(wrapReq);\n\n        header.put(DefaultJweHeader.EPK.getId(), jwk);\n\n        return result;\n    }\n\n    @Override\n    public SecretKey getDecryptionKey(DecryptionKeyRequest<PrivateKey> request) throws SecurityException {\n\n        Assert.notNull(request, \"Request cannot be null.\");\n        JweHeader header = Assert.notNull(request.getHeader(), \"Request JweHeader cannot be null.\");\n        PrivateKey privateKey = Assert.notNull(request.getKey(), \"Decryption PrivateKey cannot be null.\");\n        ParameterReadable reader = new RequiredParameterReader(header);\n        PublicJwk<?> epk = reader.get(DefaultJweHeader.EPK);\n\n        AbstractCurve curve = assertCurve(privateKey);\n        Assert.stateNotNull(curve, \"Internal implementation state: Curve cannot be null.\");\n        Class<?> epkClass = curve instanceof ECCurve ? EcPublicJwk.class : OctetPublicJwk.class;\n        if (!epkClass.isInstance(epk)) {\n            String msg = \"JWE Header \" + DefaultJweHeader.EPK + \" value is not an Elliptic Curve \" +\n                    \"Public JWK. Value: \" + epk;\n            throw new InvalidKeyException(msg);\n        }\n        if (!curve.contains(epk.toKey())) {\n            String msg = \"JWE Header \" + DefaultJweHeader.EPK + \" value does not represent \" +\n                    \"a point on the expected curve. Value: \" + epk;\n            throw new InvalidKeyException(msg);\n        }\n\n        final SecretKey derived = deriveKey(request, epk.toKey(), privateKey);\n\n        DecryptionKeyRequest<SecretKey> unwrapReq = new DefaultDecryptionKeyRequest<>(request.getPayload(),\n                null, request.getSecureRandom(), header, request.getEncryptionAlgorithm(), derived);\n\n        return WRAP_ALG.getDecryptionKey(unwrapReq);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/EdSignatureAlgorithm.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyPairBuilder;\nimport io.jsonwebtoken.security.Request;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.VerifyDigestRequest;\n\nimport java.security.Key;\nimport java.security.PrivateKey;\n\nfinal class EdSignatureAlgorithm extends AbstractSignatureAlgorithm {\n\n    private static final String ID = \"EdDSA\";\n\n    private final EdwardsCurve preferredCurve;\n\n    static final EdSignatureAlgorithm INSTANCE = new EdSignatureAlgorithm();\n\n    static boolean isSigningKey(PrivateKey key) {\n        EdwardsCurve curve = EdwardsCurve.findByKey(key);\n        return curve != null && curve.isSignatureCurve();\n    }\n\n    private EdSignatureAlgorithm() {\n        super(ID, ID);\n        this.preferredCurve = EdwardsCurve.Ed448;\n        Assert.isTrue(this.preferredCurve.isSignatureCurve(), \"Must be signature curve, not key agreement curve.\");\n    }\n\n    @Override\n    protected String getJcaName(Request<?> request) {\n        SecureRequest<?, ?> req = Assert.isInstanceOf(SecureRequest.class, request, \"SecureRequests are required.\");\n        Key key = Assert.notNull(req.getKey(), \"Request key cannot be null.\");\n        // If we're signing, and this instance's algorithm name is the default/generic 'EdDSA', then prefer the\n        // signing key's curve algorithm ID.  This ensures the most specific JCA algorithm is used for signing,\n        // (while generic 'EdDSA' is fine for validation)\n        String jcaName = getJcaName(); //default for JCA interaction\n        if (!(request instanceof VerifyDigestRequest)) { // then we're signing, not verifying\n            jcaName = EdwardsCurve.forKey(key).getJcaName();\n        }\n        return jcaName;\n    }\n\n    @Override\n    public KeyPairBuilder keyPair() {\n        return this.preferredCurve.keyPair();\n    }\n\n    @Override\n    protected void validateKey(Key key, boolean signing) {\n        super.validateKey(key, signing);\n        // should always be non-null due to algorithm name lookup, even without encoded key bytes:\n        EdwardsCurve curve = EdwardsCurve.forKey(key);\n        if (!curve.isSignatureCurve()) {\n            String msg = curve.getId() + \" keys may not be used with \" + getId() + \" digital signatures per \" +\n                    \"https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2\";\n            throw new InvalidKeyException(msg);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/EdwardsCurve.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyException;\nimport io.jsonwebtoken.security.KeyLengthSupplier;\nimport io.jsonwebtoken.security.KeyPairBuilder;\n\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.spec.KeySpec;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.security.spec.X509EncodedKeySpec;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\n\npublic class EdwardsCurve extends AbstractCurve implements KeyLengthSupplier {\n\n    private static final String OID_PREFIX = \"1.3.101.\";\n\n    // ASN.1-encoded edwards keys have this exact sequence identifying the type of key that follows.  The trailing\n    // byte is the exact edwards curve subsection OID terminal node id.\n    private static final byte[] ASN1_OID_PREFIX = new byte[]{0x06, 0x03, 0x2B, 0x65};\n\n    private static final Function<Key, String> CURVE_NAME_FINDER = new NamedParameterSpecValueFinder();\n\n    public static final EdwardsCurve X25519 = new EdwardsCurve(\"X25519\", 110); // Requires JDK >= 11 or BC\n    public static final EdwardsCurve X448 = new EdwardsCurve(\"X448\", 111); // Requires JDK >= 11 or BC\n    public static final EdwardsCurve Ed25519 = new EdwardsCurve(\"Ed25519\", 112); // Requires JDK >= 15 or BC\n    public static final EdwardsCurve Ed448 = new EdwardsCurve(\"Ed448\", 113); // Requires JDK >= 15 or BC\n\n    public static final Collection<EdwardsCurve> VALUES = Collections.of(X25519, X448, Ed25519, Ed448);\n\n    private static final Map<String, EdwardsCurve> REGISTRY;\n\n    private static final Map<Integer, EdwardsCurve> BY_OID_TERMINAL_NODE;\n\n    static {\n        REGISTRY = new LinkedHashMap<>(8);\n        BY_OID_TERMINAL_NODE = new LinkedHashMap<>(4);\n        for (EdwardsCurve curve : VALUES) {\n            int subcategoryId = curve.ASN1_OID[curve.ASN1_OID.length - 1];\n            BY_OID_TERMINAL_NODE.put(subcategoryId, curve);\n            REGISTRY.put(curve.getId(), curve);\n            REGISTRY.put(curve.OID, curve); // add OID as an alias for alg/id lookups\n        }\n    }\n\n    private static byte[] publicKeyAsn1Prefix(int byteLength, byte[] ASN1_OID) {\n        return Bytes.concat(\n                new byte[]{\n                        0x30, (byte) (byteLength + 10),\n                        0x30, 0x05}, // ASN.1 SEQUENCE of 5 bytes to follow (i.e. the OID)\n                ASN1_OID,\n                new byte[]{\n                        0x03,\n                        (byte) (byteLength + 1),\n                        0x00}\n        );\n    }\n\n    private static byte[] privateKeyPkcs8Prefix(int byteLength, byte[] ASN1_OID, boolean ber) {\n\n        byte[] keyPrefix = ber ?\n                new byte[]{0x04, (byte) (byteLength + 2), 0x04, (byte) byteLength} : // correct\n                new byte[]{0x04, (byte) byteLength}; // https://bugs.openjdk.org/browse/JDK-8213363\n\n        return Bytes.concat(\n                new byte[]{\n                        0x30,\n                        (byte) (5 + ASN1_OID.length + keyPrefix.length + byteLength),\n                        0x02, 0x01, 0x00, // encoding version 1 (integer, 1 byte, value 0)\n                        0x30, 0x05}, // ASN.1 SEQUENCE of 5 bytes to follow (i.e. the OID)\n                ASN1_OID,\n                keyPrefix\n        );\n    }\n\n    private final String OID;\n\n    /**\n     * The byte sequence within an ASN.1-encoded key that indicates an Edwards curve encoded key follows. ASN.1 (hex)\n     * notation:\n     * <pre>\n     * 06 03       ;   OBJECT IDENTIFIER (3 bytes long)\n     * |  2B 65 $I ;     \"1.3.101.$I\" for Edwards alg OID, where $I = 6E, 6F, 70, or 71 (decimal 110, 111, 112, or 113)\n     * </pre>\n     */\n    final byte[] ASN1_OID;\n\n    private final int keyBitLength;\n\n    private final int encodedKeyByteLength;\n\n    /**\n     * X.509 (ASN.1) encoding of a public key associated with this curve as a prefix (that is, <em>without</em> the\n     * actual encoded key material at the end). Appending the public key material directly to the end of this value\n     * results in a complete X.509 (ASN.1) encoded public key.  ASN.1 (hex) notation:\n     * <pre>\n     * 30 $M               ; ASN.1 SEQUENCE ($M bytes long), where $M = encodedKeyByteLength + 10\n     *    30 05            ;   ASN.1 SEQUENCE (5 bytes long)\n     *       06 03         ;     OBJECT IDENTIFIER (3 bytes long)\n     *          2B 65 $I   ;       \"1.3.101.$I\" for Edwards alg OID, where $I = 6E, 6F, 70, or 71 (110, 111, 112, or 113 decimal)\n     *    03 $S            ;   ASN.1 BIT STRING ($S bytes long), where $S = encodedKeyByteLength + 1\n     *       00            ;     ASN.1 bit string marker indicating zero unused bits at the end of the bit string\n     *       XX XX XX ...  ;     encoded key material (not included in this PREFIX byte array variable)\n     * </pre>\n     */\n    private final byte[] PUBLIC_KEY_ASN1_PREFIX; // https://www.rfc-editor.org/rfc/rfc5280#section-4.1.2.7\n\n    /**\n     * PKCS8 (ASN.1) Version 1 encoding of a private key associated with this curve, as a prefix (that is,\n     * <em>without</em> actual encoded key material at the end). Appending the private key material directly to the\n     * end of this value results in a complete PKCS8 (ASN.1) V1 encoded private key.  ASN.1 (hex) notation:\n     * <pre>\n     * 30 $M                  ; ASN.1 SEQUENCE ($M bytes long), where $M = encodedKeyByteLength + 14\n     *    02 01               ;   ASN.1 INTEGER (1 byte long)\n     *       00               ;     zero (private key encoding version V1)\n     *    30 05               ;   ASN.1 SEQUENCE (5 bytes long)\n     *       06 03            ;     OBJECT IDENTIFIER (3 bytes long). This is the edwards algorithm ID.\n     *          2B 65 $I      ;       \"1.3.101.$I\" for Edwards alg OID, where $I = 6E, 6F, 70, or 71 (110, 111, 112, or 113 decimal)\n     *    04 $B               ;   ASN.1 SEQUENCE ($B bytes long, where $B = encodedKeyByteLength + 2\n     *       04 $K            ;     ASN.1 SEQUENCE ($K bytes long), where $K = encodedKeyByteLength\n     *          XX XX XX ...  ;       encoded key material (not included in this PREFIX byte array variable)\n     * </pre>\n     */\n    private final byte[] PRIVATE_KEY_ASN1_PREFIX;\n    private final byte[] PRIVATE_KEY_JDK11_PREFIX; // https://bugs.openjdk.org/browse/JDK-8213363\n\n    /**\n     * {@code true} IFF the curve is used for digital signatures, {@code false} if used for key agreement\n     */\n    private final boolean signatureCurve;\n\n    EdwardsCurve(final String id, int oidTerminalNode) {\n        super(id, id);\n\n        if (oidTerminalNode < 110 || oidTerminalNode > 113) {\n            String msg = \"Invalid Edwards Curve ASN.1 OID terminal node value\";\n            throw new IllegalArgumentException(msg);\n        }\n\n        // OIDs (with terminal node IDs) defined here: https://www.rfc-editor.org/rfc/rfc8410#section-3\n        // X25519 (oid 1.3.101.110) has 255 bytes per https://www.rfc-editor.org/rfc/rfc7748.html#section-5 \"Here, the \"bits\" parameter should be set to 255 for X25519 and 448 for X448\"\n        // X448 (oid 1.3.101.111) have 448 bits per https://www.rfc-editor.org/rfc/rfc7748.html#section-5\n        // Ed25519 (oid 1.3.101.112) has 255 bits per https://www.rfc-editor.org/rfc/rfc8032#section-5.1\n        // Ed448 (oid 1.3.101.113) has 456 (448 + 8) bits per https://www.rfc-editor.org/rfc/rfc8032#section-5.2\n        this.keyBitLength = oidTerminalNode % 2 == 0 ? 255 : 448;\n        int encodingBitLen = oidTerminalNode == 113 ?\n                this.keyBitLength + Byte.SIZE : // https://www.rfc-editor.org/rfc/rfc8032#section-5.2.2\n                this.keyBitLength;\n        this.encodedKeyByteLength = Bytes.length(encodingBitLen);\n\n        this.OID = OID_PREFIX + oidTerminalNode;\n        this.signatureCurve = (oidTerminalNode == 112 || oidTerminalNode == 113);\n        byte[] suffix = new byte[]{(byte) oidTerminalNode};\n        this.ASN1_OID = Bytes.concat(ASN1_OID_PREFIX, suffix);\n\n        this.PUBLIC_KEY_ASN1_PREFIX = publicKeyAsn1Prefix(this.encodedKeyByteLength, this.ASN1_OID);\n        this.PRIVATE_KEY_ASN1_PREFIX = privateKeyPkcs8Prefix(this.encodedKeyByteLength, this.ASN1_OID, true);\n        this.PRIVATE_KEY_JDK11_PREFIX = privateKeyPkcs8Prefix(this.encodedKeyByteLength, this.ASN1_OID, false);\n    }\n\n    @Override\n    public int getKeyBitLength() {\n        return this.keyBitLength;\n    }\n\n    public byte[] getKeyMaterial(Key key) {\n        try {\n            return doGetKeyMaterial(key); // can throw assertion and ArrayIndexOutOfBound exception on invalid input\n        } catch (Throwable t) {\n            if (t instanceof KeyException) { //propagate\n                throw (KeyException) t;\n            }\n            String msg = \"Invalid \" + getId() + \" ASN.1 encoding: \" + t.getMessage();\n            throw new InvalidKeyException(msg, t);\n        }\n    }\n\n    /**\n     * Parses the ASN.1-encoding of the specified key\n     *\n     * @param key the Edwards curve key\n     * @return the key value, encoded according to <a href=\"https://www.rfc-editor.org/rfc/rfc8032\">RFC 8032</a>\n     * @throws RuntimeException if the key's encoded bytes do not reflect a validly ASN.1-encoded edwards key\n     */\n    protected byte[] doGetKeyMaterial(Key key) {\n        byte[] encoded = KeysBridge.getEncoded(key);\n        int i = Bytes.indexOf(encoded, ASN1_OID);\n        Assert.gt(i, -1, \"Missing or incorrect algorithm OID.\");\n        i = i + ASN1_OID.length;\n        int keyLen = 0;\n        if (encoded[i] == 0x05) { // NULL terminator, next should be zero byte indicator\n            int unusedBytes = encoded[++i];\n            Assert.eq(unusedBytes, 0, \"OID NULL terminator should indicate zero unused bytes.\");\n            i++;\n        }\n        if (encoded[i] == 0x03) { // ASN.1 bit stream, Public Key\n            i++;\n            keyLen = encoded[i++];\n            int unusedBytes = encoded[i++];\n            Assert.eq(unusedBytes, 0, \"BIT STREAM should not indicate unused bytes.\");\n            keyLen--;\n        } else if (encoded[i] == 0x04) { // ASN.1 octet sequence, Private Key.  Key length follows as next byte.\n            i++;\n            keyLen = encoded[i++];\n            if (encoded[i] == 0x04) { // ASN.1 octet sequence, key length follows as next byte.\n                i++; // skip sequence marker\n                keyLen = encoded[i++]; // next byte is length\n            }\n        }\n        Assert.eq(keyLen, this.encodedKeyByteLength, \"Invalid key length.\");\n        byte[] result = Arrays.copyOfRange(encoded, i, i + keyLen);\n        keyLen = Bytes.length(result);\n        Assert.eq(keyLen, this.encodedKeyByteLength, \"Invalid key length.\");\n        return result;\n    }\n\n    private void assertLength(byte[] raw, boolean isPublic) {\n        int len = Bytes.length(raw);\n        if (len != this.encodedKeyByteLength) {\n            String msg = \"Invalid \" + getId() + \" encoded \" + (isPublic ? \"PublicKey\" : \"PrivateKey\") +\n                    \" length. Should be \" + Bytes.bytesMsg(this.encodedKeyByteLength) + \", found \" +\n                    Bytes.bytesMsg(len) + \".\";\n            throw new InvalidKeyException(msg);\n        }\n    }\n\n    public PublicKey toPublicKey(byte[] x, Provider provider) {\n        assertLength(x, true);\n        final byte[] encoded = Bytes.concat(this.PUBLIC_KEY_ASN1_PREFIX, x);\n        final X509EncodedKeySpec spec = new X509EncodedKeySpec(encoded);\n        JcaTemplate template = new JcaTemplate(getJcaName(), provider);\n        return template.generatePublic(spec);\n    }\n\n    KeySpec privateKeySpec(byte[] d, boolean standard) {\n        byte[] prefix = standard ? this.PRIVATE_KEY_ASN1_PREFIX : this.PRIVATE_KEY_JDK11_PREFIX;\n        byte[] encoded = Bytes.concat(prefix, d);\n        return new PKCS8EncodedKeySpec(encoded);\n    }\n\n    public PrivateKey toPrivateKey(final byte[] d, Provider provider) {\n        assertLength(d, false);\n        KeySpec spec = privateKeySpec(d, true);\n        JcaTemplate template = new JcaTemplate(getJcaName(), provider);\n        return template.generatePrivate(spec);\n    }\n\n    /**\n     * Returns {@code true} if this curve is used to compute signatures, {@code false} if used for key agreement.\n     *\n     * @return {@code true} if this curve is used to compute signatures, {@code false} if used for key agreement.\n     */\n    public boolean isSignatureCurve() {\n        return this.signatureCurve;\n    }\n\n    @Override\n    public KeyPairBuilder keyPair() {\n        return new DefaultKeyPairBuilder(getJcaName(), this.keyBitLength);\n    }\n\n    public static boolean isEdwards(Key key) {\n        if (key == null) {\n            return false;\n        }\n        String alg = Strings.clean(key.getAlgorithm());\n        return \"EdDSA\".equals(alg) || \"XDH\".equals(alg) || findByKey(key) != null;\n    }\n\n    /**\n     * Computes the PublicKey associated with the specified Edwards-curve PrivateKey.\n     *\n     * @param pk the Edwards-curve {@code PrivateKey} to inspect.\n     * @return the PublicKey associated with the specified Edwards-curve PrivateKey.\n     * @throws KeyException if the PrivateKey is not an Edwards-curve key or unable to access the PrivateKey's\n     *                      material.\n     */\n    public static PublicKey derivePublic(PrivateKey pk) throws KeyException {\n        return EdwardsPublicKeyDeriver.INSTANCE.apply(pk);\n    }\n\n    public static EdwardsCurve findById(String id) {\n        return REGISTRY.get(id);\n    }\n\n    public static EdwardsCurve findByKey(Key key) {\n        if (key == null) {\n            return null;\n        }\n\n        String alg = key.getAlgorithm();\n        EdwardsCurve curve = findById(alg); // try constant time lookup first\n        if (curve == null) { // Fall back to JDK 11+ NamedParameterSpec access if possible\n            alg = CURVE_NAME_FINDER.apply(key);\n            curve = findById(alg);\n        }\n\n        // try to perform oid and/or length checks:\n        byte[] encoded = KeysBridge.findEncoded(key);\n\n        if (curve == null && !Bytes.isEmpty(encoded)) { // Try to find the Key ASN.1 algorithm OID:\n            int oidTerminalNode = findOidTerminalNode(encoded);\n            curve = BY_OID_TERMINAL_NODE.get(oidTerminalNode);\n        }\n        if (curve != null && !Bytes.isEmpty(encoded)) {\n            // found a curve, and we have encoded bytes, let's make sure that the encoding represents\n            // the correct key length:\n            try {\n                curve.getKeyMaterial(key);\n            } catch (Throwable ignored) {\n                curve = null; // key length is invalid for its indicated curve, not a match\n            }\n        }\n\n        //TODO: check if key exists on discovered curve via equation\n\n        return curve;\n    }\n\n    @Override\n    public boolean contains(Key key) {\n        EdwardsCurve curve = findByKey(key);\n        return curve.equals(this);\n    }\n\n    private static int findOidTerminalNode(byte[] encoded) {\n        int index = Bytes.indexOf(encoded, ASN1_OID_PREFIX);\n        if (index > -1) {\n            index = index + ASN1_OID_PREFIX.length;\n            if (index < encoded.length) {\n                return encoded[index];\n            }\n        }\n        return -1;\n    }\n\n    public static EdwardsCurve forKey(Key key) {\n        Assert.notNull(key, \"Key cannot be null.\");\n        EdwardsCurve curve = findByKey(key);\n        if (curve == null) {\n            String msg = \"Unrecognized Edwards Curve key: [\" + KeysBridge.toString(key) + \"]\";\n            throw new InvalidKeyException(msg);\n        }\n        //TODO: assert key exists on discovered curve via equation\n        return curve;\n    }\n\n    @SuppressWarnings(\"UnusedReturnValue\")\n    static <K extends Key> K assertEdwards(K key) {\n        forKey(key); // will throw UnsupportedKeyException if the key is not an Edwards key\n        return key;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/EdwardsPublicKeyDeriver.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.InvalidKeyException;\n\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\n\n/**\n * Derives a PublicKey from an Edwards-curve PrivateKey instance.\n */\nfinal class EdwardsPublicKeyDeriver implements Function<PrivateKey, PublicKey> {\n\n    public static final Function<PrivateKey, PublicKey> INSTANCE = new EdwardsPublicKeyDeriver();\n\n    private EdwardsPublicKeyDeriver() {\n        // prevent public instantiation.\n    }\n\n    @Override\n    public PublicKey apply(PrivateKey privateKey) {\n\n        EdwardsCurve curve = EdwardsCurve.findByKey(privateKey);\n        if (curve == null) {\n            String msg = \"Unable to derive Edwards-curve PublicKey for specified PrivateKey: \" + KeysBridge.toString(privateKey);\n            throw new InvalidKeyException(msg);\n        }\n\n        byte[] pkBytes = curve.getKeyMaterial(privateKey);\n\n        // This is a hack that utilizes the JCE implementations' behavior of using an RNG to generate a new private\n        // key, and from that, the implementation computes a public key from the private key bytes.\n        // Since we already have a private key, we provide a RNG that 'generates' the existing private key\n        // instead of a random one, and the corresponding public key will be computed for us automatically.\n        SecureRandom random = new ConstantRandom(pkBytes);\n        KeyPair pair = curve.keyPair().random(random).build();\n        Assert.stateNotNull(pair, \"Edwards curve generated keypair cannot be null.\");\n        return Assert.stateNotNull(pair.getPublic(), \"Edwards curve KeyPair must have a PublicKey\");\n    }\n\n    private static final class ConstantRandom extends SecureRandom {\n        private final byte[] value;\n\n        public ConstantRandom(byte[] value) {\n            this.value = value.clone();\n        }\n\n        @Override\n        public void nextBytes(byte[] bytes) {\n            System.arraycopy(value, 0, bytes, 0, value.length);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/FamilyJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.security.Jwk;\n\nimport java.security.Key;\n\npublic interface FamilyJwkFactory<K extends Key, J extends Jwk<K>> extends JwkFactory<K, J>, Identifiable {\n\n    boolean supports(Key key);\n\n    boolean supports(JwkContext<?> context);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/FieldElementConverter.java",
    "content": "/*\n * Copyright © 2024 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.Codec;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.impl.lang.Converters;\n\nimport java.math.BigInteger;\n\n/**\n * Hotfix for <a href=\"https://github.com/jwtk/jjwt/issues/901\">JJWT Issue 901</a>. This is currently hard-coded\n * expecting field elements for NIST P-256, P-384, or P-521 curves.  Ideally this should be refactored to work for\n * <em>any</em> curve based on its field size, not just for these NIST curves.  However, the\n * {@link EcPublicJwkFactory} and {@link EcPrivateJwkFactory} implementations only work with JWA NIST curves,\n * so this implementation is acceptable until (and if) different Weierstrass elliptic curves (ever) need to be\n * supported.\n *\n * @since 0.12.4\n */\nfinal class FieldElementConverter implements Converter<BigInteger, byte[]> {\n\n    static final FieldElementConverter INSTANCE = new FieldElementConverter();\n\n    static final Converter<BigInteger, Object> B64URL_CONVERTER = Converters.forEncoded(BigInteger.class,\n            Converters.compound(INSTANCE, Codec.BASE64URL));\n\n    private static int bytelen(ECCurve curve) {\n        return Bytes.length(curve.toParameterSpec().getCurve().getField().getFieldSize());\n    }\n\n    private static final int P256_BYTE_LEN = bytelen(ECCurve.P256);\n    private static final int P384_BYTE_LEN = bytelen(ECCurve.P384);\n    private static final int P521_BYTE_LEN = bytelen(ECCurve.P521);\n\n    @Override\n    public byte[] applyTo(BigInteger bigInteger) {\n        byte[] bytes = Converters.BIGINT_UBYTES.applyTo(bigInteger);\n        int len = bytes.length;\n        if (len == P256_BYTE_LEN || len == P384_BYTE_LEN || len == P521_BYTE_LEN) return bytes;\n        if (len < P256_BYTE_LEN) {\n            bytes = Bytes.prepad(bytes, P256_BYTE_LEN);\n        } else if (len < P384_BYTE_LEN) {\n            bytes = Bytes.prepad(bytes, P384_BYTE_LEN);\n        } else { // > P-384, so must be P-521:\n            bytes = Bytes.prepad(bytes, P521_BYTE_LEN);\n        }\n        return bytes;\n    }\n\n    @Override\n    public BigInteger applyFrom(byte[] bytes) {\n        return Converters.BIGINT_UBYTES.applyFrom(bytes);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/GcmAesAeadAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.AeadRequest;\nimport io.jsonwebtoken.security.AeadResult;\nimport io.jsonwebtoken.security.DecryptAeadRequest;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.SequenceInputStream;\nimport java.security.spec.AlgorithmParameterSpec;\n\n/**\n * @since 0.12.0\n */\npublic class GcmAesAeadAlgorithm extends AesAlgorithm implements AeadAlgorithm {\n\n    private static final String TRANSFORMATION_STRING = \"AES/GCM/NoPadding\";\n\n    public GcmAesAeadAlgorithm(int keyLength) {\n        super(\"A\" + keyLength + \"GCM\", TRANSFORMATION_STRING, keyLength);\n    }\n\n    @Override\n    public void encrypt(final AeadRequest req, AeadResult res) throws SecurityException {\n\n        Assert.notNull(req, \"Request cannot be null.\");\n        Assert.notNull(res, \"Result cannot be null.\");\n\n        final SecretKey key = assertKey(req.getKey());\n        final InputStream plaintext = Assert.notNull(req.getPayload(),\n                \"Request content (plaintext) InputStream cannot be null.\");\n        final OutputStream out = Assert.notNull(res.getOutputStream(),\n                \"Result ciphertext OutputStream cannot be null.\");\n        final InputStream aad = req.getAssociatedData();\n        final byte[] iv = ensureInitializationVector(req);\n        final AlgorithmParameterSpec ivSpec = getIvSpec(iv);\n\n        final byte[] tag = jca(req).withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            public byte[] apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec);\n                byte[] taggedCiphertext = withCipher(cipher, plaintext, aad, out);\n                // When using GCM mode, the JDK appends the authentication tag to the ciphertext, so let's extract it:\n                // (tag has a length of BLOCK_BYTE_SIZE):\n                int ciphertextLength = Bytes.length(taggedCiphertext) - BLOCK_BYTE_SIZE;\n                Streams.write(out, taggedCiphertext, 0, ciphertextLength, \"Ciphertext write failure.\");\n                byte[] tag = new byte[BLOCK_BYTE_SIZE];\n                System.arraycopy(taggedCiphertext, ciphertextLength, tag, 0, BLOCK_BYTE_SIZE);\n                return tag;\n            }\n        });\n        Streams.flush(out);\n        Streams.reset(plaintext);\n\n        res.setTag(tag).setIv(iv);\n    }\n\n    @Override\n    public void decrypt(final DecryptAeadRequest req, final OutputStream out) throws SecurityException {\n\n        Assert.notNull(req, \"Request cannot be null.\");\n        Assert.notNull(out, \"Plaintext OutputStream cannot be null.\");\n        final SecretKey key = assertKey(req.getKey());\n        final InputStream ciphertext = Assert.notNull(req.getPayload(),\n                \"Decryption request content (ciphertext) InputStream cannot be null.\");\n        final InputStream aad = req.getAssociatedData();\n        final byte[] tag = Assert.notEmpty(req.getDigest(),\n                \"Decryption request authentication tag cannot be null or empty.\");\n        final byte[] iv = assertDecryptionIv(req);\n        final AlgorithmParameterSpec ivSpec = getIvSpec(iv);\n\n        //for tagged GCM, the JCA spec requires that the tag be appended to the end of the ciphertext byte array:\n        final InputStream taggedCiphertext = new SequenceInputStream(ciphertext, Streams.of(tag));\n\n        jca(req).withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            public byte[] apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.DECRYPT_MODE, key, ivSpec);\n                byte[] last = withCipher(cipher, taggedCiphertext, aad, out);\n                Streams.write(out, last, \"GcmAesAeadAlgorithm#decrypt plaintext write failure.\");\n                return Bytes.EMPTY;\n            }\n        });\n        Streams.flush(out);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.io.TeeOutputStream;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.AeadRequest;\nimport io.jsonwebtoken.security.AeadResult;\nimport io.jsonwebtoken.security.DecryptAeadRequest;\nimport io.jsonwebtoken.security.SecretKeyBuilder;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SignatureException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.io.ByteArrayOutputStream;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.SequenceInputStream;\nimport java.security.MessageDigest;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.util.ArrayList;\nimport java.util.Arrays;\nimport java.util.Collection;\nimport java.util.Collections;\n\n/**\n * @since 0.12.0\n */\npublic class HmacAesAeadAlgorithm extends AesAlgorithm implements AeadAlgorithm {\n\n    private static final String TRANSFORMATION_STRING = \"AES/CBC/PKCS5Padding\";\n\n    private final DefaultMacAlgorithm SIGALG;\n\n    private static int digestLength(int keyLength) {\n        return keyLength * 2;\n    }\n\n    private static String id(int keyLength) {\n        return \"A\" + keyLength + \"CBC-HS\" + digestLength(keyLength);\n    }\n\n    public HmacAesAeadAlgorithm(String id, DefaultMacAlgorithm sigAlg) {\n        super(id, TRANSFORMATION_STRING, sigAlg.getKeyBitLength());\n        this.SIGALG = sigAlg;\n    }\n\n    public HmacAesAeadAlgorithm(int keyBitLength) {\n        this(id(keyBitLength), new DefaultMacAlgorithm(id(keyBitLength), \"HmacSHA\" + digestLength(keyBitLength), keyBitLength));\n    }\n\n    @Override\n    public int getKeyBitLength() {\n        return super.getKeyBitLength() * 2;\n    }\n\n    @Override\n    public SecretKeyBuilder key() {\n        // The Sun JCE KeyGenerator throws an exception if bitLengths are not standard AES 128, 192 or 256 values.\n        // Since the JWA HmacAes algorithms require double that, we use secure-random keys instead:\n        return new RandomSecretKeyBuilder(KEY_ALG_NAME, getKeyBitLength());\n    }\n\n    byte[] assertKeyBytes(SecureRequest<?, SecretKey> request) {\n        SecretKey key = Assert.notNull(request.getKey(), \"Request key cannot be null.\");\n        return validateLength(key, this.keyBitLength * 2, true);\n    }\n\n    @Override\n    public void encrypt(final AeadRequest req, final AeadResult res) {\n\n        Assert.notNull(req, \"Request cannot be null.\");\n        Assert.notNull(res, \"Result cannot be null.\");\n\n        byte[] compositeKeyBytes = assertKeyBytes(req);\n        int halfCount = compositeKeyBytes.length / 2; // https://tools.ietf.org/html/rfc7518#section-5.2\n        byte[] macKeyBytes = Arrays.copyOfRange(compositeKeyBytes, 0, halfCount);\n        byte[] encKeyBytes = Arrays.copyOfRange(compositeKeyBytes, halfCount, compositeKeyBytes.length);\n        final SecretKey encryptionKey;\n        try {\n            encryptionKey = new SecretKeySpec(encKeyBytes, KEY_ALG_NAME);\n        } finally {\n            Bytes.clear(encKeyBytes);\n            Bytes.clear(compositeKeyBytes);\n        }\n\n        final InputStream plaintext = Assert.notNull(req.getPayload(),\n                \"Request content (plaintext) InputStream cannot be null.\");\n        final OutputStream out = Assert.notNull(res.getOutputStream(), \"Result ciphertext OutputStream cannot be null.\");\n        final InputStream aad = req.getAssociatedData(); //can be null if there's no associated data\n        final byte[] iv = ensureInitializationVector(req);\n        final AlgorithmParameterSpec ivSpec = getIvSpec(iv);\n\n        // we need the ciphertext bytes for message digest calculation, so we'll use a TeeOutputStream to\n        // aggregate those results while they're being written to the result output stream\n        final ByteArrayOutputStream copy = new ByteArrayOutputStream(8192);\n        final OutputStream tee = new TeeOutputStream(out, copy);\n\n        jca(req).withCipher(new CheckedFunction<Cipher, Object>() {\n            @Override\n            public Object apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, ivSpec);\n                withCipher(cipher, plaintext, tee);\n                return null; // don't need to return anything\n            }\n        });\n\n        byte[] aadBytes = aad == null ? Bytes.EMPTY : Streams.bytes(aad, \"Unable to read AAD bytes.\");\n\n        byte[] tag;\n        try {\n            tag = sign(aadBytes, iv, Streams.of(copy.toByteArray()), macKeyBytes);\n            res.setTag(tag).setIv(iv);\n        } finally {\n            Bytes.clear(macKeyBytes);\n        }\n    }\n\n    private byte[] sign(byte[] aad, byte[] iv, InputStream ciphertext, byte[] macKeyBytes) {\n\n        long aadLength = io.jsonwebtoken.lang.Arrays.length(aad);\n        long aadLengthInBits = aadLength * Byte.SIZE;\n        long aadLengthInBitsAsUnsignedInt = aadLengthInBits & 0xffffffffL;\n        byte[] AL = Bytes.toBytes(aadLengthInBitsAsUnsignedInt);\n\n        Collection<InputStream> streams = new ArrayList<>(4);\n        if (!Bytes.isEmpty(aad)) { // must come first if it exists\n            streams.add(Streams.of(aad));\n        }\n        streams.add(Streams.of(iv));\n        streams.add(ciphertext);\n        streams.add(Streams.of(AL));\n        InputStream in = new SequenceInputStream(Collections.enumeration(streams));\n\n        SecretKey key = new SecretKeySpec(macKeyBytes, SIGALG.getJcaName());\n        SecureRequest<InputStream, SecretKey> request =\n                new DefaultSecureRequest<>(in, null, null, key);\n        byte[] digest = SIGALG.digest(request);\n\n        // https://tools.ietf.org/html/rfc7518#section-5.2.2.1 #5 requires truncating the signature\n        // to be the same length as the macKey/encKey:\n        return assertTag(Arrays.copyOfRange(digest, 0, macKeyBytes.length));\n    }\n\n    @Override\n    public void decrypt(final DecryptAeadRequest req, final OutputStream plaintext) {\n\n        Assert.notNull(req, \"Request cannot be null.\");\n        Assert.notNull(plaintext, \"Plaintext OutputStream cannot be null.\");\n\n        byte[] compositeKeyBytes = assertKeyBytes(req);\n        int halfCount = compositeKeyBytes.length / 2; // https://tools.ietf.org/html/rfc7518#section-5.2\n        byte[] macKeyBytes = Arrays.copyOfRange(compositeKeyBytes, 0, halfCount);\n        byte[] encKeyBytes = Arrays.copyOfRange(compositeKeyBytes, halfCount, compositeKeyBytes.length);\n        final SecretKey decryptionKey;\n        try {\n            decryptionKey = new SecretKeySpec(encKeyBytes, KEY_ALG_NAME);\n        } finally {\n            Bytes.clear(encKeyBytes);\n            Bytes.clear(compositeKeyBytes);\n        }\n\n        InputStream in = Assert.notNull(req.getPayload(),\n                \"Decryption request content (ciphertext) InputStream cannot be null.\");\n        final InputStream aad = req.getAssociatedData(); // can be null if there's no associated data\n        final byte[] tag = assertTag(req.getDigest());\n        final byte[] iv = assertDecryptionIv(req);\n        final AlgorithmParameterSpec ivSpec = getIvSpec(iv);\n\n        // Assert that the aad + iv + ciphertext provided, when signed, equals the tag provided,\n        // thereby verifying none of it has been tampered with:\n        byte[] aadBytes = aad == null ? Bytes.EMPTY : Streams.bytes(aad, \"Unable to read AAD bytes.\");\n        byte[] digest;\n        try {\n            digest = sign(aadBytes, iv, in, macKeyBytes);\n        } finally {\n            Bytes.clear(macKeyBytes);\n        }\n        if (!MessageDigest.isEqual(digest, tag)) { //constant time comparison to avoid side-channel attacks\n            String msg = \"Ciphertext decryption failed: Authentication tag verification failed.\";\n            throw new SignatureException(msg);\n        }\n        Streams.reset(in); // rewind for decryption\n\n        final InputStream ciphertext = in;\n        jca(req).withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            public byte[] apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.DECRYPT_MODE, decryptionKey, ivSpec);\n                withCipher(cipher, ciphertext, plaintext);\n                return Bytes.EMPTY;\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JcaTemplate.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.CheckedSupplier;\nimport io.jsonwebtoken.impl.lang.DefaultRegistry;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.SecurityException;\nimport io.jsonwebtoken.security.SignatureException;\n\nimport javax.crypto.Cipher;\nimport javax.crypto.KeyAgreement;\nimport javax.crypto.KeyGenerator;\nimport javax.crypto.Mac;\nimport javax.crypto.NoSuchPaddingException;\nimport javax.crypto.SecretKey;\nimport javax.crypto.SecretKeyFactory;\nimport java.io.InputStream;\nimport java.security.AlgorithmParameters;\nimport java.security.InvalidAlgorithmParameterException;\nimport java.security.InvalidKeyException;\nimport java.security.KeyFactory;\nimport java.security.KeyPair;\nimport java.security.KeyPairGenerator;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.PrivateKey;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.security.Signature;\nimport java.security.cert.CertificateException;\nimport java.security.cert.CertificateFactory;\nimport java.security.cert.X509Certificate;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.security.spec.InvalidKeySpecException;\nimport java.security.spec.KeySpec;\nimport java.security.spec.PKCS8EncodedKeySpec;\nimport java.util.List;\nimport java.util.concurrent.ConcurrentHashMap;\nimport java.util.concurrent.ConcurrentMap;\n\npublic class JcaTemplate {\n\n    private static final List<InstanceFactory<?>> FACTORIES = Collections.<InstanceFactory<?>>of(\n            new CipherFactory(),\n            new KeyFactoryFactory(),\n            new SecretKeyFactoryFactory(),\n            new KeyGeneratorFactory(),\n            new KeyPairGeneratorFactory(),\n            new KeyAgreementFactory(),\n            new MessageDigestFactory(),\n            new SignatureFactory(),\n            new MacFactory(),\n            new AlgorithmParametersFactory(),\n            new CertificateFactoryFactory()\n    );\n\n    private static final Registry<Class<?>, InstanceFactory<?>> REGISTRY = new DefaultRegistry<>(\n            \"JCA Instance Factory\", \"instance class\", FACTORIES,\n            new Function<InstanceFactory<?>, Class<?>>() {\n                @Override\n                public Class<?> apply(InstanceFactory<?> factory) {\n                    return factory.getInstanceClass();\n                }\n            });\n\n    // visible for testing\n    protected Provider findBouncyCastle() {\n        return Providers.findBouncyCastle();\n    }\n\n    private final String jcaName;\n    private final Provider provider;\n    private final SecureRandom secureRandom;\n\n    JcaTemplate(String jcaName) {\n        this(jcaName, null);\n    }\n\n    JcaTemplate(String jcaName, Provider provider) {\n        this(jcaName, provider, null);\n    }\n\n    JcaTemplate(String jcaName, Provider provider, SecureRandom secureRandom) {\n        this.jcaName = Assert.hasText(jcaName, \"jcaName string cannot be null or empty.\");\n        this.secureRandom = secureRandom != null ? secureRandom : Randoms.secureRandom();\n        this.provider = provider; //may be null, meaning to use the JCA subsystem default provider\n    }\n\n    private <T, R> R execute(Class<T> clazz, CheckedFunction<T, R> callback, Provider provider) throws Exception {\n        InstanceFactory<?> factory = REGISTRY.get(clazz);\n        Assert.notNull(factory, \"Unsupported JCA instance class.\");\n\n        Object object = factory.get(this.jcaName, provider);\n        T instance = Assert.isInstanceOf(clazz, object, \"Factory instance does not match expected type.\");\n\n        return callback.apply(instance);\n    }\n\n    private <T> T execute(Class<?> clazz, CheckedSupplier<T> fn) throws SecurityException {\n        try {\n            return fn.get();\n        } catch (SecurityException se) {\n            throw se; //propagate\n        } catch (Throwable t) {\n            String msg = clazz.getSimpleName() + \" callback execution failed: \" + t.getMessage();\n            throw new SecurityException(msg, t);\n        }\n    }\n\n    private <T, R> R execute(final Class<T> clazz, final CheckedFunction<T, R> fn) throws SecurityException {\n        return execute(clazz, new CheckedSupplier<R>() {\n            @Override\n            public R get() throws Exception {\n                return execute(clazz, fn, JcaTemplate.this.provider);\n            }\n        });\n    }\n\n    protected <T, R> R fallback(final Class<T> clazz, final CheckedFunction<T, R> callback) throws SecurityException {\n        return execute(clazz, new CheckedSupplier<R>() {\n            @Override\n            public R get() throws Exception {\n                try {\n                    return execute(clazz, callback, JcaTemplate.this.provider);\n                } catch (Exception e) {\n                    try { // fallback\n                        Provider bc = findBouncyCastle();\n                        if (bc != null) {\n                            return execute(clazz, callback, bc);\n                        }\n                    } catch (Throwable ignored) { // report original exception instead\n                    }\n                    throw e;\n                }\n            }\n        });\n    }\n\n    public <R> R withCipher(CheckedFunction<Cipher, R> fn) throws SecurityException {\n        return execute(Cipher.class, fn);\n    }\n\n    public <R> R withKeyFactory(CheckedFunction<KeyFactory, R> fn) throws SecurityException {\n        return execute(KeyFactory.class, fn);\n    }\n\n    public <R> R withSecretKeyFactory(CheckedFunction<SecretKeyFactory, R> fn) throws SecurityException {\n        return execute(SecretKeyFactory.class, fn);\n    }\n\n    public <R> R withKeyGenerator(CheckedFunction<KeyGenerator, R> fn) throws SecurityException {\n        return execute(KeyGenerator.class, fn);\n    }\n\n    public <R> R withKeyAgreement(CheckedFunction<KeyAgreement, R> fn) throws SecurityException {\n        return execute(KeyAgreement.class, fn);\n    }\n\n    public <R> R withKeyPairGenerator(CheckedFunction<KeyPairGenerator, R> fn) throws SecurityException {\n        return execute(KeyPairGenerator.class, fn);\n    }\n\n    public <R> R withMessageDigest(CheckedFunction<MessageDigest, R> fn) throws SecurityException {\n        return execute(MessageDigest.class, fn);\n    }\n\n    public <R> R withSignature(CheckedFunction<Signature, R> fn) throws SecurityException {\n        return execute(Signature.class, fn);\n    }\n\n    public <R> R withMac(CheckedFunction<Mac, R> fn) throws SecurityException {\n        return execute(Mac.class, fn);\n    }\n\n    public <R> R withAlgorithmParameters(CheckedFunction<AlgorithmParameters, R> fn) throws SecurityException {\n        return execute(AlgorithmParameters.class, fn);\n    }\n\n    public <R> R withCertificateFactory(CheckedFunction<CertificateFactory, R> fn) throws SecurityException {\n        return execute(CertificateFactory.class, fn);\n    }\n\n    public SecretKey generateSecretKey(final int keyBitLength) {\n        return withKeyGenerator(new CheckedFunction<KeyGenerator, SecretKey>() {\n            @Override\n            public SecretKey apply(KeyGenerator generator) {\n                generator.init(keyBitLength, secureRandom);\n                return generator.generateKey();\n            }\n        });\n    }\n\n    public KeyPair generateKeyPair() {\n        return withKeyPairGenerator(new CheckedFunction<KeyPairGenerator, KeyPair>() {\n            @Override\n            public KeyPair apply(KeyPairGenerator gen) {\n                return gen.generateKeyPair();\n            }\n        });\n    }\n\n    public KeyPair generateKeyPair(final int keyBitLength) {\n        return withKeyPairGenerator(new CheckedFunction<KeyPairGenerator, KeyPair>() {\n            @Override\n            public KeyPair apply(KeyPairGenerator generator) {\n                generator.initialize(keyBitLength, secureRandom);\n                return generator.generateKeyPair();\n            }\n        });\n    }\n\n    public KeyPair generateKeyPair(final AlgorithmParameterSpec params) {\n        return withKeyPairGenerator(new CheckedFunction<KeyPairGenerator, KeyPair>() {\n            @Override\n            public KeyPair apply(KeyPairGenerator generator) throws InvalidAlgorithmParameterException {\n                generator.initialize(params, secureRandom);\n                return generator.generateKeyPair();\n            }\n        });\n    }\n\n    public PublicKey generatePublic(final KeySpec spec) {\n        return fallback(KeyFactory.class, new CheckedFunction<KeyFactory, PublicKey>() {\n            @Override\n            public PublicKey apply(KeyFactory keyFactory) throws Exception {\n                return keyFactory.generatePublic(spec);\n            }\n        });\n    }\n\n    protected boolean isJdk11() {\n        return System.getProperty(\"java.version\").startsWith(\"11\");\n    }\n\n    private boolean isJdk8213363Bug(InvalidKeySpecException e) {\n        return isJdk11() &&\n                (\"XDH\".equals(this.jcaName) || \"X25519\".equals(this.jcaName) || \"X448\".equals(this.jcaName)) &&\n                e.getCause() instanceof InvalidKeyException &&\n                !Objects.isEmpty(e.getStackTrace()) &&\n                \"sun.security.ec.XDHKeyFactory\".equals(e.getStackTrace()[0].getClassName()) &&\n                \"engineGeneratePrivate\".equals(e.getStackTrace()[0].getMethodName());\n    }\n\n    // visible for testing\n    private int getJdk8213363BugExpectedSize(InvalidKeyException e) {\n        String msg = e.getMessage();\n        String prefix = \"key length must be \";\n        if (Strings.hasText(msg) && msg.startsWith(prefix)) {\n            String expectedSizeString = msg.substring(prefix.length());\n            try {\n                return Integer.parseInt(expectedSizeString);\n            } catch (NumberFormatException ignored) { // return -1 below\n            }\n        }\n        return -1;\n    }\n\n    private KeySpec respecIfNecessary(InvalidKeySpecException e, KeySpec spec) {\n        if (!(spec instanceof PKCS8EncodedKeySpec)) {\n            return null;\n        }\n        PKCS8EncodedKeySpec pkcs8Spec = (PKCS8EncodedKeySpec) spec;\n        byte[] encoded = pkcs8Spec.getEncoded();\n\n        // Address the [JDK 11 SunCE provider bug](https://bugs.openjdk.org/browse/JDK-8213363) for X25519\n        // and X448 encoded keys: Even though the key material might be encoded properly, JDK 11's\n        // SunCE provider incorrectly expects an ASN.1 OCTET STRING (without the DER tag/length prefix)\n        // when it should actually be a BER-encoded OCTET STRING (with the tag/length prefix).\n        // So we get the raw key bytes and use our key factory method:\n        if (isJdk8213363Bug(e)) {\n            InvalidKeyException cause = // asserted in isJdk8213363Bug method\n                    Assert.isInstanceOf(InvalidKeyException.class, e.getCause(), \"Unexpected argument.\");\n            int size = getJdk8213363BugExpectedSize(cause);\n            if ((size == 32 || size == 56) && Bytes.length(encoded) >= size) {\n                byte[] adjusted = new byte[size];\n                System.arraycopy(encoded, encoded.length - size, adjusted, 0, size);\n                EdwardsCurve curve = size == 32 ? EdwardsCurve.X25519 : EdwardsCurve.X448;\n                return curve.privateKeySpec(adjusted, false);\n            }\n        }\n\n        return null;\n    }\n\n    // visible for testing\n    protected PrivateKey generatePrivate(KeyFactory factory, KeySpec spec) throws InvalidKeySpecException {\n        return factory.generatePrivate(spec);\n    }\n\n    public PrivateKey generatePrivate(final KeySpec spec) {\n        return fallback(KeyFactory.class, new CheckedFunction<KeyFactory, PrivateKey>() {\n            @Override\n            public PrivateKey apply(KeyFactory keyFactory) throws Exception {\n                try {\n                    return generatePrivate(keyFactory, spec);\n                } catch (InvalidKeySpecException e) {\n                    KeySpec respec = respecIfNecessary(e, spec);\n                    if (respec != null) {\n                        return generatePrivate(keyFactory, respec);\n                    }\n                    throw e; // could not respec, propagate\n                }\n            }\n        });\n    }\n\n    public X509Certificate generateX509Certificate(final byte[] x509DerBytes) {\n        return fallback(CertificateFactory.class, new CheckedFunction<CertificateFactory, X509Certificate>() {\n            @Override\n            public X509Certificate apply(CertificateFactory cf) throws CertificateException {\n                InputStream is = Streams.of(x509DerBytes);\n                return (X509Certificate) cf.generateCertificate(is);\n            }\n        });\n    }\n\n    private interface InstanceFactory<T> extends Identifiable {\n\n        Class<T> getInstanceClass();\n\n        T get(String jcaName, Provider provider) throws Exception;\n    }\n\n    private static abstract class JcaInstanceFactory<T> implements InstanceFactory<T> {\n\n        private final Class<T> clazz;\n\n        // Boolean value: missing/null = haven't attempted, true = attempted and succeeded, false = attempted and failed\n        private final ConcurrentMap<String, Boolean> FALLBACK_ATTEMPTS = new ConcurrentHashMap<>();\n\n        JcaInstanceFactory(Class<T> clazz) {\n            this.clazz = Assert.notNull(clazz, \"Class argument cannot be null.\");\n        }\n\n        @Override\n        public Class<T> getInstanceClass() {\n            return this.clazz;\n        }\n\n        @Override\n        public String getId() {\n            return clazz.getSimpleName();\n        }\n\n        // visible for testing\n        protected Provider findBouncyCastle() {\n            return Providers.findBouncyCastle();\n        }\n\n        @SuppressWarnings(\"GrazieInspection\")\n        @Override\n        public final T get(String jcaName, final Provider specifiedProvider) throws Exception {\n            Assert.hasText(jcaName, \"jcaName cannot be null or empty.\");\n            Provider provider = specifiedProvider;\n            final Boolean attempted = FALLBACK_ATTEMPTS.get(jcaName);\n            if (provider == null && attempted != null && attempted) {\n                // We tried with the default provider previously, and needed to fallback, so just\n                // preemptively load the fallback to avoid the fallback/retry again:\n                provider = findBouncyCastle();\n            }\n            try {\n                return doGet(jcaName, provider);\n            } catch (NoSuchAlgorithmException nsa) { // try to fallback if possible\n\n                if (specifiedProvider == null && attempted == null) { // default provider doesn't support the alg name,\n                    // and we haven't tried BC yet, so try that now:\n                    Provider fallback = findBouncyCastle();\n                    if (fallback != null) { // BC found, try again:\n                        try {\n                            T value = doGet(jcaName, fallback);\n                            // record the successful attempt so we don't have to do this again:\n                            FALLBACK_ATTEMPTS.putIfAbsent(jcaName, Boolean.TRUE);\n                            return value;\n                        } catch (Throwable ignored) {\n                            // record the failed attempt so we don't keep trying and propagate original exception:\n                            FALLBACK_ATTEMPTS.putIfAbsent(jcaName, Boolean.FALSE);\n                        }\n                    }\n                }\n                // otherwise, we tried the fallback, or there isn't a fallback, so no need to try again, so\n                // propagate the exception:\n                throw wrap(nsa, jcaName, specifiedProvider, null);\n            } catch (Exception e) {\n                throw wrap(e, jcaName, specifiedProvider, null);\n            }\n        }\n\n        protected abstract T doGet(String jcaName, Provider provider) throws Exception;\n\n        // visible for testing:\n        protected Exception wrap(Exception e, String jcaName, Provider specifiedProvider, Provider fallbackProvider) {\n            String msg = \"Unable to obtain '\" + jcaName + \"' \" + getId() + \" instance from \";\n            if (specifiedProvider != null) {\n                msg += \"specified '\" + specifiedProvider + \"' Provider\";\n            } else {\n                msg += \"default JCA Provider\";\n            }\n            if (fallbackProvider != null) {\n                msg += \" or fallback '\" + fallbackProvider + \"' Provider\";\n            }\n            msg += \": \" + e.getMessage();\n            return wrap(msg, e);\n        }\n\n        protected Exception wrap(String msg, Exception cause) {\n            if (Signature.class.isAssignableFrom(clazz) || Mac.class.isAssignableFrom(clazz)) {\n                return new SignatureException(msg, cause);\n            }\n            return new SecurityException(msg, cause);\n        }\n    }\n\n    private static class CipherFactory extends JcaInstanceFactory<Cipher> {\n        CipherFactory() {\n            super(Cipher.class);\n        }\n\n        @Override\n        public Cipher doGet(String jcaName, Provider provider) throws NoSuchPaddingException, NoSuchAlgorithmException {\n            return provider != null ? Cipher.getInstance(jcaName, provider) : Cipher.getInstance(jcaName);\n        }\n    }\n\n    private static class KeyFactoryFactory extends JcaInstanceFactory<KeyFactory> {\n        KeyFactoryFactory() {\n            super(KeyFactory.class);\n        }\n\n        @Override\n        public KeyFactory doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? KeyFactory.getInstance(jcaName, provider) : KeyFactory.getInstance(jcaName);\n        }\n    }\n\n    private static class SecretKeyFactoryFactory extends JcaInstanceFactory<SecretKeyFactory> {\n        SecretKeyFactoryFactory() {\n            super(SecretKeyFactory.class);\n        }\n\n        @Override\n        public SecretKeyFactory doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? SecretKeyFactory.getInstance(jcaName, provider) : SecretKeyFactory.getInstance(jcaName);\n        }\n    }\n\n    private static class KeyGeneratorFactory extends JcaInstanceFactory<KeyGenerator> {\n        KeyGeneratorFactory() {\n            super(KeyGenerator.class);\n        }\n\n        @Override\n        public KeyGenerator doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? KeyGenerator.getInstance(jcaName, provider) : KeyGenerator.getInstance(jcaName);\n        }\n    }\n\n    private static class KeyPairGeneratorFactory extends JcaInstanceFactory<KeyPairGenerator> {\n        KeyPairGeneratorFactory() {\n            super(KeyPairGenerator.class);\n        }\n\n        @Override\n        public KeyPairGenerator doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? KeyPairGenerator.getInstance(jcaName, provider) : KeyPairGenerator.getInstance(jcaName);\n        }\n    }\n\n    private static class KeyAgreementFactory extends JcaInstanceFactory<KeyAgreement> {\n        KeyAgreementFactory() {\n            super(KeyAgreement.class);\n        }\n\n        @Override\n        public KeyAgreement doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? KeyAgreement.getInstance(jcaName, provider) : KeyAgreement.getInstance(jcaName);\n        }\n    }\n\n    private static class MessageDigestFactory extends JcaInstanceFactory<MessageDigest> {\n        MessageDigestFactory() {\n            super(MessageDigest.class);\n        }\n\n        @Override\n        public MessageDigest doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? MessageDigest.getInstance(jcaName, provider) : MessageDigest.getInstance(jcaName);\n        }\n    }\n\n    private static class SignatureFactory extends JcaInstanceFactory<Signature> {\n        SignatureFactory() {\n            super(Signature.class);\n        }\n\n        @Override\n        public Signature doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? Signature.getInstance(jcaName, provider) : Signature.getInstance(jcaName);\n        }\n    }\n\n    private static class MacFactory extends JcaInstanceFactory<Mac> {\n        MacFactory() {\n            super(Mac.class);\n        }\n\n        @Override\n        public Mac doGet(String jcaName, Provider provider) throws NoSuchAlgorithmException {\n            return provider != null ? Mac.getInstance(jcaName, provider) : Mac.getInstance(jcaName);\n        }\n    }\n\n    private static class AlgorithmParametersFactory extends JcaInstanceFactory<AlgorithmParameters> {\n        AlgorithmParametersFactory() {\n            super(AlgorithmParameters.class);\n        }\n\n        @Override\n        protected AlgorithmParameters doGet(String jcaName, Provider provider) throws Exception {\n            return provider != null ?\n                    AlgorithmParameters.getInstance(jcaName, provider) :\n                    AlgorithmParameters.getInstance(jcaName);\n        }\n    }\n\n    private static class CertificateFactoryFactory extends JcaInstanceFactory<CertificateFactory> {\n        CertificateFactoryFactory() {\n            super(CertificateFactory.class);\n        }\n\n        @Override\n        protected CertificateFactory doGet(String jcaName, Provider provider) throws Exception {\n            return provider != null ?\n                    CertificateFactory.getInstance(jcaName, provider) :\n                    CertificateFactory.getInstance(jcaName);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwkBuilderSupplier.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Supplier;\nimport io.jsonwebtoken.security.DynamicJwkBuilder;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyOperationPolicy;\n\nimport java.security.Provider;\n\npublic class JwkBuilderSupplier implements Supplier<DynamicJwkBuilder<?, ?>> {\n\n    public static final JwkBuilderSupplier DEFAULT = new JwkBuilderSupplier(null, null);\n\n    private final Provider provider;\n    private final KeyOperationPolicy operationPolicy;\n\n    public JwkBuilderSupplier(Provider provider, KeyOperationPolicy operationPolicy) {\n        this.provider = provider;\n        this.operationPolicy = operationPolicy;\n    }\n\n    @Override\n    public DynamicJwkBuilder<?, ?> get() {\n        DynamicJwkBuilder<?, ?> builder = Jwks.builder().provider(this.provider);\n        if (this.operationPolicy != null) {\n            builder.operationPolicy(operationPolicy);\n        }\n        return builder;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwkContext.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.impl.X509Context;\nimport io.jsonwebtoken.impl.lang.Nameable;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.security.HashAlgorithm;\nimport io.jsonwebtoken.security.KeyOperation;\n\nimport java.security.Key;\nimport java.security.Provider;\nimport java.security.PublicKey;\nimport java.security.SecureRandom;\nimport java.util.Collection;\nimport java.util.Map;\nimport java.util.Set;\n\npublic interface JwkContext<K extends Key> extends Identifiable, Map<String, Object>, ParameterReadable, Nameable,\n        X509Context<JwkContext<K>> {\n\n    JwkContext<K> parameter(Parameter<?> param);\n\n    JwkContext<K> setId(String id);\n\n    JwkContext<K> setIdThumbprintAlgorithm(HashAlgorithm alg);\n\n    HashAlgorithm getIdThumbprintAlgorithm();\n\n    String getType();\n\n    JwkContext<K> setType(String type);\n\n    Set<KeyOperation> getOperations();\n\n    JwkContext<K> setOperations(Collection<? extends KeyOperation> operations);\n\n    String getAlgorithm();\n\n    JwkContext<K> setAlgorithm(String algorithm);\n\n    String getPublicKeyUse();\n\n    JwkContext<K> setPublicKeyUse(String use);\n\n    /**\n     * Returns {@code true} if relevant context values indicate JWK use with MAC or digital signature algorithms,\n     * {@code false} otherwise.  Specifically {@code true} is only returned if either:\n     * <ul>\n     *     <li>&quot;sig&quot;.equals({@link #getPublicKeyUse()}), OR</li>\n     *     <li>{@link #getOperations()} is not empty and contains either &quot;sign&quot; or &quot;verify&quot;</li>\n     * </ul>\n     * <p>otherwise {@code false}.</p>\n     *\n     * @return {@code true} if relevant context values indicate JWK use with MAC or digital signature algorithms,\n     * {@code false} otherwise.\n     */\n    boolean isSigUse();\n\n    K getKey();\n\n    JwkContext<K> setKey(K key);\n\n    PublicKey getPublicKey();\n\n    JwkContext<K> setPublicKey(PublicKey publicKey);\n\n    Provider getProvider();\n\n    JwkContext<K> setProvider(Provider provider);\n\n    SecureRandom getRandom();\n\n    JwkContext<K> setRandom(SecureRandom random);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwkConverter.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.impl.lang.Nameable;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.lang.Supplier;\nimport io.jsonwebtoken.security.DynamicJwkBuilder;\nimport io.jsonwebtoken.security.EcPrivateJwk;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.MalformedKeyException;\nimport io.jsonwebtoken.security.OctetPrivateJwk;\nimport io.jsonwebtoken.security.OctetPublicJwk;\nimport io.jsonwebtoken.security.PrivateJwk;\nimport io.jsonwebtoken.security.PublicJwk;\nimport io.jsonwebtoken.security.RsaPrivateJwk;\nimport io.jsonwebtoken.security.RsaPublicJwk;\nimport io.jsonwebtoken.security.SecretJwk;\n\nimport java.util.Map;\n\nimport static io.jsonwebtoken.lang.Strings.nespace;\n\npublic final class JwkConverter<T extends Jwk<?>> implements Converter<T, Object> {\n\n    @SuppressWarnings(\"unchecked\")\n    public static final Class<Jwk<?>> JWK_CLASS = (Class<Jwk<?>>) (Class<?>) Jwk.class;\n\n    @SuppressWarnings(\"unchecked\")\n    public static final Class<PublicJwk<?>> PUBLIC_JWK_CLASS = (Class<PublicJwk<?>>) (Class<?>) PublicJwk.class;\n\n    public static final JwkConverter<Jwk<?>> ANY = new JwkConverter<>(JWK_CLASS);\n\n    public static final JwkConverter<PublicJwk<?>> PUBLIC_JWK = new JwkConverter<>(PUBLIC_JWK_CLASS);\n\n    private final Class<T> desiredType;\n\n    private final Supplier<DynamicJwkBuilder<?, ?>> supplier;\n\n    public JwkConverter(Class<T> desiredType) {\n        this(desiredType, JwkBuilderSupplier.DEFAULT);\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public JwkConverter(Supplier<DynamicJwkBuilder<?, ?>> supplier) {\n        this((Class<T>) JWK_CLASS, supplier);\n    }\n\n    public JwkConverter(Class<T> desiredType, Supplier<DynamicJwkBuilder<?, ?>> supplier) {\n        this.desiredType = Assert.notNull(desiredType, \"desiredType cannot be null.\");\n        this.supplier = Assert.notNull(supplier, \"supplier cannot be null.\");\n    }\n\n    @Override\n    public Object applyTo(T jwk) {\n        return desiredType.cast(jwk);\n    }\n\n    private static String articleFor(String s) {\n        switch (s.charAt(0)) {\n            case 'E': // for Elliptic/Edwards Curve\n            case 'R': // for RSA\n                return \"an\";\n            default:\n                return \"a\";\n        }\n    }\n\n    private static String typeString(Jwk<?> jwk) {\n        Assert.isInstanceOf(Nameable.class, jwk, \"All JWK implementations must implement Nameable.\");\n        return ((Nameable) jwk).getName();\n    }\n\n    private static String typeString(Class<?> clazz) {\n        StringBuilder sb = new StringBuilder();\n        if (SecretJwk.class.isAssignableFrom(clazz)) {\n            sb.append(\"Secret\");\n        } else if (RsaPublicJwk.class.isAssignableFrom(clazz) || RsaPrivateJwk.class.isAssignableFrom(clazz)) {\n            sb.append(\"RSA\");\n        } else if (EcPublicJwk.class.isAssignableFrom(clazz) || EcPrivateJwk.class.isAssignableFrom(clazz)) {\n            sb.append(\"EC\");\n        } else if (OctetPublicJwk.class.isAssignableFrom(clazz) || OctetPrivateJwk.class.isAssignableFrom(clazz)) {\n            sb.append(\"Edwards Curve\");\n        }\n        return typeString(sb, clazz);\n    }\n\n    private static String typeString(StringBuilder sb, Class<?> clazz) {\n        if (PublicJwk.class.isAssignableFrom(clazz)) {\n            nespace(sb).append(\"Public\");\n        } else if (PrivateJwk.class.isAssignableFrom(clazz)) {\n            nespace(sb).append(\"Private\");\n        }\n        nespace(sb).append(\"JWK\");\n        return sb.toString();\n    }\n\n    private IllegalArgumentException unexpectedIAE(Jwk<?> jwk) {\n        String desired = typeString(this.desiredType);\n        String jwkType = typeString(jwk);\n        String msg = \"Value must be \" + articleFor(desired) + \" \" + desired + \", not \" +\n                articleFor(jwkType) + \" \" + jwkType + \".\";\n        return new IllegalArgumentException(msg);\n    }\n\n    @Override\n    public T applyFrom(Object o) {\n        Assert.notNull(o, \"JWK cannot be null.\");\n        if (desiredType.isInstance(o)) {\n            return desiredType.cast(o);\n        } else if (o instanceof Jwk<?>) {\n            throw unexpectedIAE((Jwk<?>) o);\n        }\n        if (!(o instanceof Map)) {\n            String msg = \"JWK must be a Map<String,?> (JSON Object). Type found: \" + o.getClass().getName() + \".\";\n            throw new IllegalArgumentException(msg);\n        }\n        final Map<?, ?> map = Collections.immutable((Map<?, ?>) o);\n\n        Parameter<String> param = AbstractJwk.KTY;\n        // mandatory for all JWKs: https://datatracker.ietf.org/doc/html/rfc7517#section-4.1\n        // no need for builder param type conversion overhead if this isn't present:\n        if (Collections.isEmpty(map) || !map.containsKey(param.getId())) {\n            String msg = \"JWK is missing required \" + param + \" parameter.\";\n            throw new MalformedKeyException(msg);\n        }\n        Object val = map.get(param.getId());\n        if (val == null) {\n            String msg = \"JWK \" + param + \" value cannot be null.\";\n            throw new MalformedKeyException(msg);\n        }\n        if (!(val instanceof String)) {\n            String msg = \"JWK \" + param + \" value must be a String. Type found: \" + val.getClass().getName();\n            throw new MalformedKeyException(msg);\n        }\n        String kty = (String) val;\n        if (!Strings.hasText(kty)) {\n            String msg = \"JWK \" + param + \" value cannot be empty.\";\n            throw new MalformedKeyException(msg);\n        }\n\n        DynamicJwkBuilder<?, ?> builder = this.supplier.get();\n        for (Map.Entry<?, ?> entry : map.entrySet()) {\n            Object key = entry.getKey();\n            Assert.notNull(key, \"JWK map key cannot be null.\");\n            if (!(key instanceof String)) {\n                String msg = \"JWK map keys must be Strings. Encountered key '\" + key + \"' of type \" +\n                        key.getClass().getName() + \".\";\n                throw new IllegalArgumentException(msg);\n            }\n            String skey = (String) key;\n            builder.add(skey, entry.getValue());\n        }\n        Jwk<?> jwk = builder.build();\n\n        if (desiredType.isInstance(jwk)) {\n            return desiredType.cast(jwk);\n        }\n        throw unexpectedIAE(jwk);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwkDeserializer.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.JsonObjectDeserializer;\nimport io.jsonwebtoken.io.Deserializer;\nimport io.jsonwebtoken.security.MalformedKeyException;\n\npublic class JwkDeserializer extends JsonObjectDeserializer {\n\n    public JwkDeserializer(Deserializer<?> deserializer) {\n        super(deserializer, \"JWK\");\n    }\n\n    @Override\n    protected RuntimeException malformed(Throwable t) {\n        String msg = \"Malformed JWK JSON: \" + t.getMessage();\n        return new MalformedKeyException(msg);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.security.Jwk;\n\nimport java.security.Key;\n\npublic interface JwkFactory<K extends Key, J extends Jwk<K>> {\n\n    JwkContext<K> newContext(JwkContext<?> src,  K key);\n\n    J createJwk(JwkContext<K> ctx);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwkSetConverter.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Supplier;\nimport io.jsonwebtoken.security.DynamicJwkBuilder;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.JwkSet;\nimport io.jsonwebtoken.security.KeyException;\nimport io.jsonwebtoken.security.MalformedKeySetException;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport java.util.Collection;\nimport java.util.LinkedHashMap;\nimport java.util.LinkedHashSet;\nimport java.util.Map;\nimport java.util.Set;\n\npublic class JwkSetConverter implements Converter<JwkSet, Object> {\n\n    private final Converter<Jwk<?>, Object> JWK_CONVERTER;\n    private final Parameter<Set<Jwk<?>>> PARAM;\n\n    private final boolean ignoreUnsupported;\n\n    public JwkSetConverter() {\n        // ignore is true by default per https://www.rfc-editor.org/rfc/rfc7517.html#section-5:\n        this(JwkBuilderSupplier.DEFAULT, true);\n    }\n\n    public JwkSetConverter(boolean ignoreUnsupported) {\n        this(JwkBuilderSupplier.DEFAULT, ignoreUnsupported);\n    }\n\n    public JwkSetConverter(Supplier<DynamicJwkBuilder<?, ?>> supplier, boolean ignoreUnsupported) {\n        this(new JwkConverter<>(supplier), ignoreUnsupported);\n    }\n\n    public JwkSetConverter(Converter<Jwk<?>, Object> jwkConverter, boolean ignoreUnsupported) {\n        this.JWK_CONVERTER = Assert.notNull(jwkConverter, \"JWK converter cannot be null.\");\n        this.PARAM = DefaultJwkSet.param(jwkConverter);\n        this.ignoreUnsupported = ignoreUnsupported;\n    }\n\n    public boolean isIgnoreUnsupported() {\n        return ignoreUnsupported;\n    }\n\n    @Override\n    public Object applyTo(JwkSet jwkSet) {\n        return jwkSet;\n    }\n\n    @Override\n    public JwkSet applyFrom(Object o) {\n        Assert.notNull(o, \"Value cannot be null.\");\n        if (o instanceof JwkSet) {\n            return (JwkSet) o;\n        }\n        if (!(o instanceof Map)) {\n            String msg = \"Value must be a Map<String,?> (JSON Object). Type found: \" + o.getClass().getName() + \".\";\n            throw new IllegalArgumentException(msg);\n        }\n        final Map<?, ?> m = Collections.immutable((Map<?, ?>) o);\n\n        // mandatory for all JWK Sets: https://datatracker.ietf.org/doc/html/rfc7517#section-5\n        // no need for builder parameter type conversion overhead if this isn't present:\n        if (Collections.isEmpty(m) || !m.containsKey(PARAM.getId())) {\n            String msg = \"Missing required \" + PARAM + \" parameter.\";\n            throw new MalformedKeySetException(msg);\n        }\n        Object val = m.get(PARAM.getId());\n        if (val == null) {\n            String msg = \"JWK Set \" + PARAM + \" value cannot be null.\";\n            throw new MalformedKeySetException(msg);\n        }\n        if (!(val instanceof Collection)) {\n            String msg = \"JWK Set \" + PARAM + \" value must be a Collection (JSON Array). Type found: \" +\n                    val.getClass().getName();\n            throw new MalformedKeySetException(msg);\n        }\n        int size = Collections.size((Collection<?>) val);\n        if (size == 0) {\n            String msg = \"JWK Set \" + PARAM + \" collection cannot be empty.\";\n            throw new MalformedKeySetException(msg);\n        }\n\n        // Copy values so we don't mutate the original input\n        Map<String, Object> src = new LinkedHashMap<>(Collections.size((Map<?, ?>) o));\n        for (Map.Entry<?, ?> entry : ((Map<?, ?>) o).entrySet()) {\n            Object key = Assert.notNull(entry.getKey(), \"JWK Set map key cannot be null.\");\n            if (!(key instanceof String)) {\n                String msg = \"JWK Set map keys must be Strings. Encountered key '\" + key + \"' of type \" +\n                        key.getClass().getName();\n                throw new IllegalArgumentException(msg);\n            }\n            String skey = (String) key;\n            src.put(skey, entry.getValue());\n        }\n\n        Set<Jwk<?>> jwks = new LinkedHashSet<>(size);\n        int i = 0; // keep track of which element fails (if any)\n        for (Object candidate : ((Collection<?>) val)) {\n            try {\n                Jwk<?> jwk = JWK_CONVERTER.applyFrom(candidate);\n                jwks.add(jwk);\n            } catch (UnsupportedKeyException e) {\n                if (!ignoreUnsupported) {\n                    String msg = \"JWK Set keys[\" + i + \"]: \" + e.getMessage();\n                    throw new UnsupportedKeyException(msg, e);\n                }\n            } catch (IllegalArgumentException | KeyException e) {\n                if (!ignoreUnsupported) {\n                    String msg = \"JWK Set keys[\" + i + \"]: \" + e.getMessage();\n                    throw new MalformedKeySetException(msg, e);\n                }\n            }\n            i++;\n        }\n\n        // Replace the `keys` value with validated entries:\n        src.remove(PARAM.getId());\n        src.put(PARAM.getId(), jwks);\n        return new DefaultJwkSet(PARAM, src);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwkSetDeserializer.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.JsonObjectDeserializer;\nimport io.jsonwebtoken.io.Deserializer;\nimport io.jsonwebtoken.security.MalformedKeySetException;\n\npublic class JwkSetDeserializer extends JsonObjectDeserializer {\n\n    public JwkSetDeserializer(Deserializer<?> deserializer) {\n        super(deserializer, \"JWK Set\");\n    }\n\n    @Override\n    protected RuntimeException malformed(Throwable t) {\n        String msg = \"Malformed JWK Set JSON: \" + t.getMessage();\n        throw new MalformedKeySetException(msg, t);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwksBridge.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.io.NamedSerializer;\nimport io.jsonwebtoken.impl.lang.Services;\nimport io.jsonwebtoken.io.Serializer;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.Jwk;\n\nimport java.io.ByteArrayOutputStream;\nimport java.util.Map;\n\npublic final class JwksBridge {\n\n    private JwksBridge() {\n    }\n\n    @SuppressWarnings({\"unchecked\", \"unused\"}) // used via reflection by io.jsonwebtoken.security.Jwks\n    public static String UNSAFE_JSON(Jwk<?> jwk) {\n        Serializer<Map<String, ?>> serializer = Services.get(Serializer.class);\n        Assert.stateNotNull(serializer, \"Serializer lookup failed. Ensure JSON impl .jar is in the runtime classpath.\");\n        NamedSerializer ser = new NamedSerializer(\"JWK\", serializer);\n        ByteArrayOutputStream out = new ByteArrayOutputStream(512);\n        ser.serialize(jwk, out);\n        return Strings.utf8(out.toByteArray());\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/JwtX509StringConverter.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.io.Decoders;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.SecurityException;\n\nimport java.security.cert.CertificateEncodingException;\nimport java.security.cert.X509Certificate;\n\npublic class JwtX509StringConverter implements Converter<X509Certificate, CharSequence> {\n\n    public static final JwtX509StringConverter INSTANCE = new JwtX509StringConverter();\n\n    // Returns a Base64 encoded (NOT Base64Url encoded) string of the cert's encoded byte array per\n    // https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.6\n    // https://www.rfc-editor.org/rfc/rfc7516.html#section-4.1.8\n    // https://www.rfc-editor.org/rfc/rfc7517.html#section-4.7\n    @Override\n    public String applyTo(X509Certificate cert) {\n        Assert.notNull(cert, \"X509Certificate cannot be null.\");\n        byte[] der;\n        try {\n            der = cert.getEncoded();\n        } catch (CertificateEncodingException e) {\n            String msg = \"Unable to access X509Certificate encoded bytes necessary to perform DER \" +\n                    \"Base64-encoding. Certificate: {\" + cert + \"}. Cause: \" + e.getMessage();\n            throw new IllegalArgumentException(msg, e);\n        }\n        if (Bytes.isEmpty(der)) {\n            String msg = \"X509Certificate encoded bytes cannot be null or empty.  Certificate: {\" + cert + \"}.\";\n            throw new IllegalArgumentException(msg);\n        }\n        return Encoders.BASE64.encode(der);\n    }\n\n    // visible for testing\n    protected X509Certificate toCert(final byte[] der) throws SecurityException {\n        return new JcaTemplate(\"X.509\").generateX509Certificate(der);\n    }\n\n    @Override\n    public X509Certificate applyFrom(CharSequence s) {\n        Assert.hasText(s, \"X.509 Certificate encoded string cannot be null or empty.\");\n        try {\n            byte[] der = Decoders.BASE64.decode(s); //RFC requires Base64, not Base64Url\n            return toCert(der);\n        } catch (Exception e) {\n            String msg = \"Unable to convert Base64 String '\" + s + \"' to X509Certificate instance. Cause: \" + e.getMessage();\n            throw new IllegalArgumentException(msg, e);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/KeyOperationConverter.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Registry;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyOperation;\n\nfinal class KeyOperationConverter implements Converter<KeyOperation, Object> {\n\n    static final Converter<KeyOperation, Object> DEFAULT = new KeyOperationConverter(Jwks.OP.get());\n\n    private final Registry<String, KeyOperation> registry;\n\n    KeyOperationConverter(Registry<String, KeyOperation> registry) {\n        this.registry = Assert.notEmpty(registry, \"KeyOperation registry cannot be null or empty.\");\n    }\n\n    @Override\n    public String applyTo(KeyOperation operation) {\n        Assert.notNull(operation, \"KeyOperation cannot be null.\");\n        return operation.getId();\n    }\n\n    @Override\n    public KeyOperation applyFrom(Object o) {\n        if (o instanceof KeyOperation) {\n            return (KeyOperation) o;\n        }\n        String id = Assert.isInstanceOf(String.class, o, \"Argument must be a KeyOperation or String.\");\n        Assert.hasText(id, \"KeyOperation string value cannot be null or empty.\");\n        KeyOperation keyOp = this.registry.get(id);\n        return keyOp != null ? keyOp : Jwks.OP.builder().id(id).build(); // custom operations are allowed\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/KeyPairs.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\n\nimport java.security.Key;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.interfaces.ECKey;\nimport java.security.interfaces.RSAKey;\n\npublic final class KeyPairs {\n\n    private KeyPairs() {\n    }\n\n    private static String familyPrefix(Class<?> clazz) {\n        if (RSAKey.class.isAssignableFrom(clazz)) {\n            return \"RSA \";\n        } else if (ECKey.class.isAssignableFrom(clazz)) {\n            return \"EC \";\n        } else {\n            return Strings.EMPTY;\n        }\n    }\n\n    public static <K> K getKey(KeyPair pair, Class<K> clazz) {\n        Assert.notNull(pair, \"KeyPair cannot be null.\");\n        String prefix = familyPrefix(clazz) + \"KeyPair \";\n        boolean isPrivate = PrivateKey.class.isAssignableFrom(clazz);\n        Key key = isPrivate ? pair.getPrivate() : pair.getPublic();\n        return assertKey(key, clazz, prefix);\n    }\n\n    public static <K> K assertKey(Key key, Class<K> clazz, String msgPrefix) {\n        Assert.notNull(key, \"Key argument cannot be null.\");\n        Assert.notNull(clazz, \"Class argument cannot be null.\");\n        String type = key instanceof PrivateKey ? \"private\" : \"public\";\n        if (!clazz.isInstance(key)) {\n            String msg = msgPrefix + type + \" key must be an instance of \" + clazz.getName() +\n                \". Type found: \" + key.getClass().getName();\n            throw new IllegalArgumentException(msg);\n        }\n        return clazz.cast(key);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/KeyUsage.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport java.security.cert.X509Certificate;\n\npublic final class KeyUsage {\n\n    private static final boolean[] NO_FLAGS = new boolean[9];\n\n    // Direct from X509Certificate#getKeyUsage() JavaDoc.  For an understand of when/how to use these\n    // flags, read https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.3\n    private static final int\n        digitalSignature = 0,\n        nonRepudiation = 1,\n        keyEncipherment = 2,\n        dataEncipherment = 3,\n        keyAgreement = 4,\n        keyCertSign = 5,\n        cRLSign = 6,\n        encipherOnly = 7, //if keyAgreement, then only encipher data during key agreement\n        decipherOnly = 8; //if keyAgreement, then only decipher data during key agreement\n\n    private final boolean[] is; //for readability: i.e. is[nonRepudiation] simulates isNonRepudiation, etc.\n\n    public KeyUsage(X509Certificate cert) {\n        boolean[] arr = cert != null ? cert.getKeyUsage() : NO_FLAGS;\n        this.is = arr != null ? arr : NO_FLAGS;\n    }\n\n    public boolean isDigitalSignature() {\n        return is[digitalSignature];\n    }\n\n    public boolean isNonRepudiation() {\n        return is[nonRepudiation];\n    }\n\n    public boolean isKeyEncipherment() {\n        return is[keyEncipherment];\n    }\n\n    public boolean isDataEncipherment() {\n        return is[dataEncipherment];\n    }\n\n    public boolean isKeyAgreement() {\n        return is[keyAgreement];\n    }\n\n    public boolean isKeyCertSign() {\n        return is[keyCertSign];\n    }\n\n    public boolean isCRLSign() {\n        return is[cRLSign];\n    }\n\n    public boolean isEncipherOnly() {\n        return is[encipherOnly];\n    }\n\n    public boolean isDecipherOnly() {\n        return is[decipherOnly];\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/KeyUseStrategy.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\n//TODO: Make a non-impl concept?\npublic interface KeyUseStrategy {\n\n    //TODO: change argument to have more information?\n    String toJwkValue(KeyUsage keyUses);\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/KeysBridge.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeySupplier;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.PrivateKeyBuilder;\nimport io.jsonwebtoken.security.SecretKeyBuilder;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.interfaces.ECKey;\nimport java.security.interfaces.RSAKey;\n\n@SuppressWarnings({\"unused\"}) // reflection bridge class for the io.jsonwebtoken.security.Keys implementation\npublic final class KeysBridge {\n\n     // Some HSMs use generic secrets. This prefix matches the generic secret algorithm name\n     // used by SUN PKCS#11 provider, AWS CloudHSM JCE provider and possibly other HSMs\n    private static final String GENERIC_SECRET_ALG_PREFIX = \"Generic\";\n\n    // prevent instantiation\n    private KeysBridge() {\n    }\n\n    public static Password password(char[] password) {\n        return new PasswordSpec(password);\n    }\n\n    public static SecretKeyBuilder builder(SecretKey key) {\n        return new ProvidedSecretKeyBuilder(key);\n    }\n\n    public static PrivateKeyBuilder builder(PrivateKey key) {\n        return new ProvidedPrivateKeyBuilder(key);\n    }\n\n    /**\n     * If the specified {@code key} is a {@link KeySupplier}, the 'root' (lowest level) key that may exist in\n     * a {@code KeySupplier} chain is returned, otherwise the {@code key} is returned.\n     *\n     * @param key the key to check if it is a {@code KeySupplier}\n     * @param <K> the key type\n     * @return the lowest-level/root key available.\n     */\n    @SuppressWarnings(\"unchecked\")\n    public static <K extends Key> K root(K key) {\n        return (key instanceof KeySupplier<?>) ? (K) root((KeySupplier<?>) key) : key;\n    }\n\n    public static <K extends Key> K root(KeySupplier<K> supplier) {\n        Assert.notNull(supplier, \"KeySupplier canot be null.\");\n        return Assert.notNull(root(supplier.getKey()), \"KeySupplier key cannot be null.\");\n    }\n\n    public static String findAlgorithm(Key key) {\n        return key != null ? Strings.clean(key.getAlgorithm()) : null;\n    }\n\n    /**\n     * Returns the specified key's available encoded bytes, or {@code null} if not available.\n     *\n     * <p>Some KeyStore implementations - like Hardware Security Modules, PKCS11 key stores, and later versions\n     * of Android - will not allow applications or libraries to obtain a key's encoded bytes.  In these cases,\n     * this method will return null.</p>\n     *\n     * @param key the key to inspect\n     * @return the specified key's available encoded bytes, or {@code null} if not available.\n     */\n    public static byte[] findEncoded(Key key) {\n        Assert.notNull(key, \"Key cannot be null.\");\n        byte[] encoded = null;\n        try {\n            encoded = key.getEncoded();\n        } catch (Throwable ignored) {\n        }\n        return encoded;\n    }\n\n    public static boolean isGenericSecret(Key key) {\n        if (!(key instanceof SecretKey)) {\n            return false;\n        }\n\n        String algName = Assert.hasText(key.getAlgorithm(), \"Key algorithm cannot be null or empty.\");\n        return algName.startsWith(GENERIC_SECRET_ALG_PREFIX);\n    }\n\n    /**\n     * Returns the specified key's key length (in bits) if possible, or {@code -1} if unable to determine the length.\n     *\n     * @param key the key to inspect\n     * @return the specified key's key length in bits, or {@code -1} if unable to determine length.\n     */\n    public static int findBitLength(Key key) {\n\n        int bitlen = -1;\n\n        // try to parse the length from key specification\n        if (key instanceof SecretKey) {\n            SecretKey secretKey = (SecretKey) key;\n            if (\"RAW\".equals(secretKey.getFormat())) {\n                byte[] encoded = findEncoded(secretKey);\n                if (!Bytes.isEmpty(encoded)) {\n                    bitlen = (int) Bytes.bitLength(encoded);\n                    Bytes.clear(encoded);\n                }\n            }\n        } else if (key instanceof RSAKey) {\n            RSAKey rsaKey = (RSAKey) key;\n            bitlen = rsaKey.getModulus().bitLength();\n        } else if (key instanceof ECKey) {\n            ECKey ecKey = (ECKey) key;\n            bitlen = ecKey.getParams().getOrder().bitLength();\n        } else {\n            // We can check additional logic for EdwardsCurve even if the current JDK version doesn't support it:\n            EdwardsCurve curve = EdwardsCurve.findByKey(key);\n            if (curve != null) bitlen = curve.getKeyBitLength();\n        }\n\n        return bitlen;\n    }\n\n    public static byte[] getEncoded(Key key) {\n        Assert.notNull(key, \"Key cannot be null.\");\n        byte[] encoded;\n        try {\n            encoded = key.getEncoded();\n        } catch (Throwable t) {\n            String msg = \"Cannot obtain required encoded bytes from key [\" + KeysBridge.toString(key) + \"]: \" +\n                    t.getMessage();\n            throw new InvalidKeyException(msg, t);\n        }\n        if (Bytes.isEmpty(encoded)) {\n            String msg = \"Missing required encoded bytes for key [\" + toString(key) + \"].\";\n            throw new InvalidKeyException(msg);\n        }\n        return encoded;\n    }\n\n    public static String toString(Key key) {\n        if (key == null) {\n            return \"null\";\n        }\n        if (key instanceof PublicKey) {\n            return key.toString(); // safe to show internal key state as it's a public key\n        }\n        // else secret or private key, don't show internal key state, just public attributes\n        return \"class: \" + key.getClass().getName() +\n                \", algorithm: \" + key.getAlgorithm() +\n                \", format: \" + key.getFormat();\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/LocatingKeyResolver.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.JwsHeader;\nimport io.jsonwebtoken.Locator;\nimport io.jsonwebtoken.SigningKeyResolver;\nimport io.jsonwebtoken.lang.Assert;\n\nimport java.security.Key;\n\n@SuppressWarnings(\"deprecation\") // TODO: delete this class for 1.0\npublic class LocatingKeyResolver implements SigningKeyResolver {\n\n    private final Locator<? extends Key> locator;\n\n    public LocatingKeyResolver(Locator<? extends Key> locator) {\n        this.locator = Assert.notNull(locator, \"Locator cannot be null.\");\n    }\n\n    @Override\n    public Key resolveSigningKey(JwsHeader header, Claims claims) {\n        return this.locator.locate(header);\n    }\n\n    @Override\n    public Key resolveSigningKey(JwsHeader header, byte[] content) {\n        return this.locator.locate(header);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/NamedParameterSpecValueFinder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.impl.lang.Functions;\nimport io.jsonwebtoken.impl.lang.OptionalMethodInvoker;\n\nimport java.security.Key;\nimport java.security.spec.AlgorithmParameterSpec;\n\npublic class NamedParameterSpecValueFinder implements Function<Key, String> {\n\n    private static final Function<Key, AlgorithmParameterSpec> EDEC_KEY_GET_PARAMS =\n            new OptionalMethodInvoker<>(\"java.security.interfaces.EdECKey\", \"getParams\"); // >= JDK 15\n    private static final Function<Key, AlgorithmParameterSpec> XEC_KEY_GET_PARAMS =\n            new OptionalMethodInvoker<>(\"java.security.interfaces.XECKey\", \"getParams\"); // >= JDK 11\n    private static final Function<Object, String> GET_NAME =\n            new OptionalMethodInvoker<>(\"java.security.spec.NamedParameterSpec\", \"getName\"); // >= JDK 11\n\n    private static final Function<Key, String> COMPOSED = Functions.andThen(Functions.firstResult(EDEC_KEY_GET_PARAMS, XEC_KEY_GET_PARAMS), GET_NAME);\n\n    @Override\n    public String apply(final Key key) {\n        return COMPOSED.apply(key);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/NoneSignatureAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SecurityException;\nimport io.jsonwebtoken.security.SignatureException;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\n\nimport java.io.InputStream;\nimport java.security.Key;\n\nfinal class NoneSignatureAlgorithm implements SecureDigestAlgorithm<Key, Key> {\n\n    private static final String ID = \"none\";\n\n    static final SecureDigestAlgorithm<Key, Key> INSTANCE = new NoneSignatureAlgorithm();\n\n    private NoneSignatureAlgorithm() {\n    }\n\n    @Override\n    public String getId() {\n        return ID;\n    }\n\n    @Override\n    public byte[] digest(SecureRequest<InputStream, Key> request) throws SecurityException {\n        throw new SignatureException(\"The 'none' algorithm cannot be used to create signatures.\");\n    }\n\n    @Override\n    public boolean verify(VerifySecureDigestRequest<Key> request) throws SignatureException {\n        throw new SignatureException(\"The 'none' algorithm cannot be used to verify signatures.\");\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        return this == obj ||\n                (obj instanceof SecureDigestAlgorithm &&\n                        ID.equalsIgnoreCase(((SecureDigestAlgorithm<?, ?>) obj).getId()));\n    }\n\n    @Override\n    public int hashCode() {\n        return getId().hashCode();\n    }\n\n    @Override\n    public String toString() {\n        return ID;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/OctetJwkFactory.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport java.security.Key;\nimport java.util.Set;\n\npublic abstract class OctetJwkFactory<K extends Key, J extends Jwk<K>> extends AbstractFamilyJwkFactory<K, J> {\n\n    OctetJwkFactory(Class<K> keyType, Set<Parameter<?>> params) {\n        super(DefaultOctetPublicJwk.TYPE_VALUE, keyType, params);\n    }\n\n    @Override\n    public boolean supports(Key key) {\n        return super.supports(key) && EdwardsCurve.isEdwards(key);\n    }\n\n    protected static EdwardsCurve getCurve(final ParameterReadable reader) throws UnsupportedKeyException {\n        Parameter<String> param = DefaultOctetPublicJwk.CRV;\n        String crvId = reader.get(param);\n        EdwardsCurve curve = EdwardsCurve.findById(crvId);\n        if (curve == null) {\n            String msg = \"Unrecognized OKP JWK \" + param + \" value '\" + crvId + \"'\";\n            throw new UnsupportedKeyException(msg);\n        }\n        return curve;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/OctetPrivateJwkFactory.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.OctetPrivateJwk;\nimport io.jsonwebtoken.security.OctetPublicJwk;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\n\npublic class OctetPrivateJwkFactory extends OctetJwkFactory<PrivateKey, OctetPrivateJwk<PrivateKey, PublicKey>> {\n\n    public OctetPrivateJwkFactory() {\n        super(PrivateKey.class, DefaultOctetPrivateJwk.PARAMS);\n    }\n\n    @Override\n    protected boolean supportsKeyValues(JwkContext<?> ctx) {\n        return super.supportsKeyValues(ctx) && ctx.containsKey(DefaultOctetPrivateJwk.D.getId());\n    }\n\n    @Override\n    protected OctetPrivateJwk<PrivateKey, PublicKey> createJwkFromKey(JwkContext<PrivateKey> ctx) {\n        PrivateKey key = Assert.notNull(ctx.getKey(), \"PrivateKey cannot be null.\");\n        EdwardsCurve crv = EdwardsCurve.forKey(key);\n\n        PublicKey pub = ctx.getPublicKey();\n        if (pub != null) {\n            if (!crv.equals(EdwardsCurve.forKey(pub))) {\n                String msg = \"Specified Edwards Curve PublicKey does not match the specified PrivateKey's curve.\";\n                throw new InvalidKeyException(msg);\n            }\n        } else { // not supplied - try to generate it:\n            pub = EdwardsCurve.derivePublic(key);\n        }\n\n        // If a JWK fingerprint has been requested to be the JWK id, ensure we copy over the one computed for the\n        // public key per https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        boolean copyId = !Strings.hasText(ctx.getId()) && ctx.getIdThumbprintAlgorithm() != null;\n        JwkContext<PublicKey> pubCtx = OctetPublicJwkFactory.INSTANCE.newContext(ctx, pub);\n        OctetPublicJwk<PublicKey> pubJwk = OctetPublicJwkFactory.INSTANCE.createJwk(pubCtx);\n        ctx.putAll(pubJwk);\n        if (copyId) {\n            ctx.setId(pubJwk.getId());\n        }\n\n        //now add the d value\n        byte[] d = crv.getKeyMaterial(key);\n        Assert.notEmpty(d, \"Edwards PrivateKey 'd' value cannot be null or empty.\");\n        //TODO: assert that the curve contains the specified key\n        put(ctx, DefaultOctetPrivateJwk.D, d);\n\n        return new DefaultOctetPrivateJwk<>(ctx, pubJwk);\n    }\n\n    @Override\n    protected OctetPrivateJwk<PrivateKey, PublicKey> createJwkFromValues(JwkContext<PrivateKey> ctx) {\n        ParameterReadable reader = new RequiredParameterReader(ctx);\n        EdwardsCurve curve = getCurve(reader);\n        //TODO: assert that the curve contains the specified key\n\n        // public values are required, so assert them:\n        JwkContext<PublicKey> pubCtx = new DefaultJwkContext<>(DefaultOctetPublicJwk.PARAMS, ctx);\n        OctetPublicJwk<PublicKey> pubJwk = OctetPublicJwkFactory.INSTANCE.createJwkFromValues(pubCtx);\n\n        byte[] d = reader.get(DefaultOctetPrivateJwk.D);\n        PrivateKey key = curve.toPrivateKey(d, ctx.getProvider());\n        ctx.setKey(key);\n\n        return new DefaultOctetPrivateJwk<>(ctx, pubJwk);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/OctetPublicJwkFactory.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.OctetPublicJwk;\n\nimport java.security.PublicKey;\n\npublic class OctetPublicJwkFactory extends OctetJwkFactory<PublicKey, OctetPublicJwk<PublicKey>> {\n\n    static final OctetPublicJwkFactory INSTANCE = new OctetPublicJwkFactory();\n\n    OctetPublicJwkFactory() {\n        super(PublicKey.class, DefaultOctetPublicJwk.PARAMS);\n    }\n\n    @Override\n    protected OctetPublicJwk<PublicKey> createJwkFromKey(JwkContext<PublicKey> ctx) {\n        PublicKey key = Assert.notNull(ctx.getKey(), \"PublicKey cannot be null.\");\n        EdwardsCurve crv = EdwardsCurve.forKey(key);\n        byte[] x = crv.getKeyMaterial(key);\n        Assert.notEmpty(x, \"Edwards PublicKey 'x' value cannot be null or empty.\");\n        //TODO: assert that the curve contains the specified key\n        put(ctx, DefaultOctetPublicJwk.CRV, crv.getId());\n        put(ctx, DefaultOctetPublicJwk.X, x);\n        return new DefaultOctetPublicJwk<>(ctx);\n    }\n\n    @Override\n    protected OctetPublicJwk<PublicKey> createJwkFromValues(JwkContext<PublicKey> ctx) {\n        ParameterReadable reader = new RequiredParameterReader(ctx);\n        EdwardsCurve curve = getCurve(reader);\n        byte[] x = reader.get(DefaultOctetPublicJwk.X);\n        //TODO: assert that the curve contains the specified key\n        PublicKey key = curve.toPublicKey(x, ctx.getProvider());\n        ctx.setKey(key);\n        return new DefaultOctetPublicJwk<>(ctx);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/PasswordSpec.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.security.Password;\n\nimport java.security.spec.KeySpec;\n\npublic class PasswordSpec implements Password, KeySpec {\n\n    private static final String NONE_ALGORITHM = \"NONE\";\n    private static final String DESTROYED_MSG = \"Password has been destroyed. Password character array may not be obtained.\";\n    private static final String ENCODED_DISABLED_MSG =\n            \"getEncoded() is disabled for Password instances as they are intended to be used \" +\n                    \"with key derivation algorithms only. Because passwords rarely have the length or entropy \" +\n                    \"necessary for secure cryptographic operations such as authenticated hashing or encryption, \" +\n                    \"they are disabled as direct inputs for these operations to help avoid accidental misuse; if \" +\n                    \"you see this exception message, it is likely that the associated Password instance is \" +\n                    \"being used incorrectly.\";\n\n    private volatile boolean destroyed;\n    private final char[] password;\n\n    public PasswordSpec(char[] password) {\n        this.password = Assert.notEmpty(password, \"Password character array cannot be null or empty.\");\n    }\n\n    private void assertActive() {\n        if (destroyed) {\n            throw new IllegalStateException(DESTROYED_MSG);\n        }\n    }\n\n    @Override\n    public char[] toCharArray() {\n        assertActive();\n        return this.password.clone();\n    }\n\n    @Override\n    public String getAlgorithm() {\n        return NONE_ALGORITHM;\n    }\n\n    @Override\n    public String getFormat() {\n        return null; // encoding isn't supported, so we return null per the Key#getFormat() JavaDoc\n    }\n\n    @Override\n    public byte[] getEncoded() {\n        throw new UnsupportedOperationException(ENCODED_DISABLED_MSG);\n    }\n\n    public void destroy() {\n        this.destroyed = true;\n        java.util.Arrays.fill(password, '\\u0000');\n    }\n\n    public boolean isDestroyed() {\n        return this.destroyed;\n    }\n\n    @Override\n    public int hashCode() {\n        return Objects.nullSafeHashCode(this.password);\n    }\n\n    @Override\n    public boolean equals(Object obj) {\n        if (obj == this) {\n            return true;\n        }\n        if (obj instanceof PasswordSpec) {\n            PasswordSpec other = (PasswordSpec) obj;\n            return Objects.nullSafeEquals(this.password, other.password);\n        }\n        return false;\n    }\n\n    @Override\n    public final String toString() {\n        return \"<redacted>\";\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithm.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.JweHeader;\nimport io.jsonwebtoken.UnsupportedJwtException;\nimport io.jsonwebtoken.impl.DefaultJweHeader;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.DecryptionKeyRequest;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.KeyRequest;\nimport io.jsonwebtoken.security.KeyResult;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.SecurityException;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.SecretKeyFactory;\nimport javax.crypto.spec.PBEKeySpec;\nimport javax.crypto.spec.SecretKeySpec;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * @since 0.12.0\n */\npublic class Pbes2HsAkwAlgorithm extends CryptoAlgorithm implements KeyAlgorithm<Password, Password> {\n\n    // See https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2 :\n    private static final int DEFAULT_SHA256_ITERATIONS = 310000;\n    private static final int DEFAULT_SHA384_ITERATIONS = 210000;\n    private static final int DEFAULT_SHA512_ITERATIONS = 120000;\n\n    private static final int MIN_RECOMMENDED_ITERATIONS = 1000; // https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2\n    private static final String MIN_ITERATIONS_MSG_PREFIX =\n            \"[JWA RFC 7518, Section 4.8.1.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.2) \" +\n                    \"recommends password-based-encryption iterations be greater than or equal to \" +\n                    MIN_RECOMMENDED_ITERATIONS + \". Provided: \";\n    private static final double MAX_ITERATIONS_FACTOR = 2.5;\n\n    private final int HASH_BYTE_LENGTH;\n    private final int DERIVED_KEY_BIT_LENGTH;\n    private final byte[] SALT_PREFIX;\n    private final int DEFAULT_ITERATIONS;\n    private final int MAX_ITERATIONS;\n    private final KeyAlgorithm<SecretKey, SecretKey> wrapAlg;\n\n    private static byte[] toRfcSaltPrefix(byte[] bytes) {\n        // last byte must always be zero as it is a delimiter per\n        // https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8.1.1\n        // We ensure this by creating a byte array that is one element larger than bytes.length since Java defaults all\n        // new byte array indices to 0x00, meaning the last one will be our zero delimiter:\n        byte[] output = new byte[bytes.length + 1];\n        System.arraycopy(bytes, 0, output, 0, bytes.length);\n        return output;\n    }\n\n    private static int hashBitLength(int keyBitLength) {\n        return keyBitLength * 2;\n    }\n\n    private static String idFor(int hashBitLength, KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {\n        Assert.notNull(wrapAlg, \"wrapAlg argument cannot be null.\");\n        return \"PBES2-HS\" + hashBitLength + \"+\" + wrapAlg.getId();\n    }\n\n    public static int assertIterations(int iterations) {\n        if (iterations < MIN_RECOMMENDED_ITERATIONS) {\n            String msg = MIN_ITERATIONS_MSG_PREFIX + iterations;\n            throw new IllegalArgumentException(msg);\n        }\n        return iterations;\n    }\n\n    public Pbes2HsAkwAlgorithm(int keyBitLength) {\n        this(hashBitLength(keyBitLength), new AesWrapKeyAlgorithm(keyBitLength));\n    }\n\n    protected Pbes2HsAkwAlgorithm(int hashBitLength, KeyAlgorithm<SecretKey, SecretKey> wrapAlg) {\n        super(idFor(hashBitLength, wrapAlg), \"PBKDF2WithHmacSHA\" + hashBitLength);\n        this.wrapAlg = wrapAlg; // no need to assert non-null due to 'idFor' implementation above\n\n        // There's some white box knowledge here: there is no need to assert the value of hashBitLength\n        // because that is done implicitly in the constructor when instantiating AesWrapKeyAlgorithm. See that class's\n        // implementation to see the assertion:\n        this.HASH_BYTE_LENGTH = hashBitLength / Byte.SIZE;\n\n        // If the JwtBuilder caller doesn't specify an iteration count, fall back to OWASP best-practice recommendations\n        // per https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\n        if (hashBitLength >= 512) {\n            DEFAULT_ITERATIONS = DEFAULT_SHA512_ITERATIONS;\n        } else if (hashBitLength >= 384) {\n            DEFAULT_ITERATIONS = DEFAULT_SHA384_ITERATIONS;\n        } else {\n            DEFAULT_ITERATIONS = DEFAULT_SHA256_ITERATIONS;\n        }\n        MAX_ITERATIONS = (int) (DEFAULT_ITERATIONS * MAX_ITERATIONS_FACTOR); // upper bound to help mitigate DoS attacks\n\n        // https://www.rfc-editor.org/rfc/rfc7518.html#section-4.8, 2nd paragraph, last sentence:\n        // \"Their derived-key lengths respectively are 16, 24, and 32 octets.\" :\n        this.DERIVED_KEY_BIT_LENGTH = hashBitLength / 2; // results in 128, 192, or 256\n\n        this.SALT_PREFIX = toRfcSaltPrefix(getId().getBytes(StandardCharsets.UTF_8));\n    }\n\n    // protected visibility for testing\n    protected SecretKey deriveKey(SecretKeyFactory factory, final char[] password, final byte[] rfcSalt, int iterations) throws Exception {\n        PBEKeySpec spec = new PBEKeySpec(password, rfcSalt, iterations, DERIVED_KEY_BIT_LENGTH);\n        try {\n            SecretKey derived = factory.generateSecret(spec);\n            return new SecretKeySpec(derived.getEncoded(), AesAlgorithm.KEY_ALG_NAME); // needed to keep the Sun Provider happy\n        } finally {\n            spec.clearPassword();\n        }\n    }\n\n    private SecretKey deriveKey(final KeyRequest<?> request, final char[] password, final byte[] salt, final int iterations) {\n        try {\n            Assert.notEmpty(password, \"Key password character array cannot be null or empty.\");\n            return jca(request).withSecretKeyFactory(new CheckedFunction<SecretKeyFactory, SecretKey>() {\n                @Override\n                public SecretKey apply(SecretKeyFactory factory) throws Exception {\n                    return deriveKey(factory, password, salt, iterations);\n                }\n            });\n        } finally {\n            java.util.Arrays.fill(password, '\\u0000');\n        }\n    }\n\n    protected byte[] generateInputSalt(KeyRequest<?> request) {\n        byte[] inputSalt = new byte[this.HASH_BYTE_LENGTH];\n        ensureSecureRandom(request).nextBytes(inputSalt);\n        return inputSalt;\n    }\n\n    // protected visibility for testing\n    protected byte[] toRfcSalt(byte[] inputSalt) {\n        return Bytes.concat(this.SALT_PREFIX, inputSalt);\n    }\n\n    @Override\n    public KeyResult getEncryptionKey(final KeyRequest<Password> request) throws SecurityException {\n\n        Assert.notNull(request, \"request cannot be null.\");\n        final Password key = Assert.notNull(request.getPayload(), \"Encryption Password cannot be null.\");\n        final JweHeader header = Assert.notNull(request.getHeader(), \"JweHeader cannot be null.\");\n        Integer p2c = header.getPbes2Count();\n        if (p2c == null) { // set a default, and ensure it's available in the header for later decryption:\n            p2c = DEFAULT_ITERATIONS;\n            header.put(DefaultJweHeader.P2C.getId(), p2c);\n        }\n        final int iterations = assertIterations(p2c);\n        byte[] inputSalt = generateInputSalt(request);\n        final byte[] rfcSalt = toRfcSalt(inputSalt);\n        char[] password = key.toCharArray(); // password will be safely cleaned/zeroed in deriveKey next:\n        final SecretKey derivedKek = deriveKey(request, password, rfcSalt, iterations);\n\n        // now get a new CEK that is encrypted ('wrapped') with the PBE-derived key:\n        KeyRequest<SecretKey> wrapReq = new DefaultKeyRequest<>(derivedKek, request.getProvider(),\n                request.getSecureRandom(), request.getHeader(), request.getEncryptionAlgorithm());\n        KeyResult result = wrapAlg.getEncryptionKey(wrapReq);\n\n        request.getHeader().put(DefaultJweHeader.P2S.getId(), inputSalt); //retain for recipients\n\n        return result;\n    }\n\n    @Override\n    public SecretKey getDecryptionKey(DecryptionKeyRequest<Password> request) throws SecurityException {\n\n        JweHeader header = Assert.notNull(request.getHeader(), \"Request JweHeader cannot be null.\");\n        final Password key = Assert.notNull(request.getKey(), \"Decryption Password cannot be null.\");\n        ParameterReadable reader = new RequiredParameterReader(header);\n        final byte[] inputSalt = reader.get(DefaultJweHeader.P2S);\n\n        Parameter<Integer> param = DefaultJweHeader.P2C;\n        final int iterations = reader.get(param);\n        if (iterations > MAX_ITERATIONS) {\n            String msg = \"JWE Header \" + param + \" value \" + iterations + \" exceeds \" + getId() + \" maximum \" +\n                    \"allowed value \" + MAX_ITERATIONS + \". The larger value is rejected to help mitigate \" +\n                    \"potential Denial of Service attacks.\";\n            throw new UnsupportedJwtException(msg);\n        }\n\n        final byte[] rfcSalt = Bytes.concat(SALT_PREFIX, inputSalt);\n        final char[] password = key.toCharArray(); // password will be safely cleaned/zeroed in deriveKey next:\n        final SecretKey derivedKek = deriveKey(request, password, rfcSalt, iterations);\n\n        DecryptionKeyRequest<SecretKey> unwrapReq =\n                new DefaultDecryptionKeyRequest<>(request.getPayload(), request.getProvider(),\n                        request.getSecureRandom(), header, request.getEncryptionAlgorithm(), derivedKek);\n\n        return wrapAlg.getDecryptionKey(unwrapReq);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/PrivateECKey.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.KeySupplier;\n\nimport java.security.PrivateKey;\nimport java.security.interfaces.ECKey;\nimport java.security.spec.ECParameterSpec;\n\n/**\n * @since 0.12.0\n */\npublic class PrivateECKey implements PrivateKey, ECKey, KeySupplier<PrivateKey> {\n\n    private final PrivateKey privateKey;\n    private final ECParameterSpec params;\n\n    public PrivateECKey(PrivateKey privateKey, ECParameterSpec params) {\n        this.privateKey = Assert.notNull(privateKey, \"PrivateKey cannot be null.\");\n        this.params = Assert.notNull(params, \"ECParameterSpec cannot be null.\");\n    }\n\n    @Override\n    public String getAlgorithm() {\n        return this.privateKey.getAlgorithm();\n    }\n\n    @Override\n    public String getFormat() {\n        return this.privateKey.getFormat();\n    }\n\n    @Override\n    public byte[] getEncoded() {\n        return this.privateKey.getEncoded();\n    }\n\n    @Override\n    public ECParameterSpec getParams() {\n        return this.params;\n    }\n\n    @Override\n    public PrivateKey getKey() {\n        return this.privateKey;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ProvidedKeyBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.KeyBuilder;\n\nimport java.security.Key;\n\nabstract class ProvidedKeyBuilder<K extends Key, B extends KeyBuilder<K, B>> extends AbstractSecurityBuilder<K, B>\n        implements KeyBuilder<K, B> {\n\n    protected final K key;\n\n    ProvidedKeyBuilder(K key) {\n        this.key = Assert.notNull(key, \"Key cannot be null.\");\n    }\n\n    @Override\n    public final K build() {\n        if (this.key instanceof ProviderKey) { // already wrapped, don't wrap again:\n            return this.key;\n        }\n        return doBuild();\n    }\n\n    abstract K doBuild();\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ProvidedPrivateKeyBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.PrivateKeyBuilder;\n\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.interfaces.ECKey;\n\npublic class ProvidedPrivateKeyBuilder extends ProvidedKeyBuilder<PrivateKey, PrivateKeyBuilder>\n        implements PrivateKeyBuilder {\n\n    private PublicKey publicKey;\n\n    ProvidedPrivateKeyBuilder(PrivateKey key) {\n        super(key);\n    }\n\n    @Override\n    public PrivateKeyBuilder publicKey(PublicKey publicKey) {\n        this.publicKey = publicKey;\n        return this;\n    }\n\n    @Override\n    public PrivateKey doBuild() {\n\n        PrivateKey key = this.key;\n\n        // We only need to wrap as an ECKey if:\n        // 1. The private key is not already an ECKey. If it is, we can validate normally\n        // 2. The private key indicates via its algorithm that it is intended to be used as an EC key.\n        // 3. The public key is an ECKey - this must be true to represent EC params for the private key\n        String privAlg = Strings.clean(this.key.getAlgorithm());\n        if (!(key instanceof ECKey) && (\"EC\".equalsIgnoreCase(privAlg) || \"ECDSA\".equalsIgnoreCase(privAlg)) &&\n                this.publicKey instanceof ECKey) {\n            key = new PrivateECKey(key, ((ECKey) this.publicKey).getParams());\n        }\n\n        return this.provider != null ? new ProviderPrivateKey(this.provider, key) : key;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ProvidedSecretKeyBuilder.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.SecretKeyBuilder;\n\nimport javax.crypto.SecretKey;\n\nclass ProvidedSecretKeyBuilder extends ProvidedKeyBuilder<SecretKey, SecretKeyBuilder> implements SecretKeyBuilder {\n\n    ProvidedSecretKeyBuilder(SecretKey key) {\n        super(key);\n    }\n\n    @Override\n    public SecretKey doBuild() {\n        if (this.key instanceof Password) {\n            return this.key; // provider never needed for Password instances.\n        }\n        return provider != null ? new ProviderSecretKey(this.provider, this.key) : this.key;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ProviderKey.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.security.KeySupplier;\n\nimport java.security.Key;\nimport java.security.Provider;\n\npublic class ProviderKey<T extends Key> implements Key, KeySupplier<T> {\n\n    private final T key;\n\n    private final Provider provider;\n\n    public static Provider getProvider(Key key, Provider backup) {\n        if (key instanceof ProviderKey<?>) {\n            ProviderKey<?> pkey = (ProviderKey<?>) key;\n            return Assert.stateNotNull(pkey.getProvider(), \"ProviderKey provider can never be null.\");\n        }\n        return backup;\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <K extends Key> K getKey(K key) {\n        return key instanceof ProviderKey ? ((ProviderKey<K>) key).getKey() : key;\n    }\n\n    ProviderKey(Provider provider, T key) {\n        this.provider = Assert.notNull(provider, \"Provider cannot be null.\");\n        this.key = Assert.notNull(key, \"Key argument cannot be null.\");\n        if (key instanceof ProviderKey<?>) {\n            String msg = \"Nesting not permitted.\";\n            throw new IllegalArgumentException(msg);\n        }\n    }\n\n    @Override\n    public T getKey() {\n        return this.key;\n    }\n\n    @Override\n    public String getAlgorithm() {\n        return this.key.getAlgorithm();\n    }\n\n    @Override\n    public String getFormat() {\n        return this.key.getFormat();\n    }\n\n    @Override\n    public byte[] getEncoded() {\n        return this.key.getEncoded();\n    }\n\n    public final Provider getProvider() {\n        return this.provider;\n    }\n\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ProviderPrivateKey.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport java.security.PrivateKey;\nimport java.security.Provider;\n\npublic final class ProviderPrivateKey extends ProviderKey<PrivateKey> implements PrivateKey {\n\n    ProviderPrivateKey(Provider provider, PrivateKey key) {\n        super(provider, key);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/ProviderSecretKey.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport javax.crypto.SecretKey;\nimport java.security.Provider;\n\npublic final class ProviderSecretKey extends ProviderKey<SecretKey> implements SecretKey {\n\n    ProviderSecretKey(Provider provider, SecretKey key) {\n        super(provider, key);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/Providers.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.lang.Classes;\n\nimport java.security.Provider;\nimport java.security.Security;\nimport java.util.concurrent.atomic.AtomicReference;\n\n/**\n * @since 0.12.0\n */\nfinal class Providers {\n\n    private static final String BC_PROVIDER_CLASS_NAME = \"org.bouncycastle.jce.provider.BouncyCastleProvider\";\n    static final boolean BOUNCY_CASTLE_AVAILABLE = Classes.isAvailable(BC_PROVIDER_CLASS_NAME);\n    private static final AtomicReference<Provider> BC_PROVIDER = new AtomicReference<>();\n\n    private Providers() {\n    }\n\n    /**\n     * Returns the BouncyCastle provider if and only if BouncyCastle is available, or {@code null} otherwise.\n     *\n     * <p>If the JVM runtime already has BouncyCastle registered\n     * (e.g. {@code Security.addProvider(bcProvider)}, that Provider instance will be found and returned.\n     * If an existing BC provider is not found, a new BC instance will be created, cached for future reference,\n     * and returned.</p>\n     *\n     * <p>If a new BC provider is created and returned, it is <em>not</em> registered in the JVM via\n     * {@code Security.addProvider} to ensure JJWT doesn't interfere with the application security provider\n     * configuration and/or expectations.</p>\n     *\n     * @return any available BouncyCastle Provider, or {@code null} if BouncyCastle is not available.\n     */\n    public static Provider findBouncyCastle() {\n        if (!BOUNCY_CASTLE_AVAILABLE) {\n            return null;\n        }\n        Provider provider = BC_PROVIDER.get();\n        if (provider == null) {\n\n            Class<Provider> clazz = Classes.forName(BC_PROVIDER_CLASS_NAME);\n\n            //check to see if the user has already registered the BC provider:\n            Provider[] providers = Security.getProviders();\n            for (Provider aProvider : providers) {\n                if (clazz.isInstance(aProvider)) {\n                    BC_PROVIDER.set(aProvider);\n                    return aProvider;\n                }\n            }\n\n            //user hasn't created the BC provider, so we'll create one just for JJWT's needs:\n            provider = Classes.newInstance(clazz);\n            BC_PROVIDER.set(provider);\n        }\n        return provider;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/RSAOtherPrimeInfoConverter.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.Converter;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.Parameters;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.MalformedKeyException;\n\nimport java.math.BigInteger;\nimport java.security.spec.RSAOtherPrimeInfo;\nimport java.util.LinkedHashMap;\nimport java.util.Map;\nimport java.util.Set;\n\nclass RSAOtherPrimeInfoConverter implements Converter<RSAOtherPrimeInfo, Object> {\n\n    static final RSAOtherPrimeInfoConverter INSTANCE = new RSAOtherPrimeInfoConverter();\n\n    static final Parameter<BigInteger> PRIME_FACTOR = Parameters.secretBigInt(\"r\", \"Prime Factor\");\n    static final Parameter<BigInteger> FACTOR_CRT_EXPONENT = Parameters.secretBigInt(\"d\", \"Factor CRT Exponent\");\n    static final Parameter<BigInteger> FACTOR_CRT_COEFFICIENT = Parameters.secretBigInt(\"t\", \"Factor CRT Coefficient\");\n    static final Set<Parameter<?>> PARAMS = Collections.<Parameter<?>>setOf(PRIME_FACTOR, FACTOR_CRT_EXPONENT, FACTOR_CRT_COEFFICIENT);\n\n    @Override\n    public Object applyTo(RSAOtherPrimeInfo info) {\n        Map<String, Object> m = new LinkedHashMap<>(3);\n        m.put(PRIME_FACTOR.getId(), PRIME_FACTOR.applyTo(info.getPrime()));\n        m.put(FACTOR_CRT_EXPONENT.getId(), FACTOR_CRT_EXPONENT.applyTo(info.getExponent()));\n        m.put(FACTOR_CRT_COEFFICIENT.getId(), FACTOR_CRT_COEFFICIENT.applyTo(info.getCrtCoefficient()));\n        return m;\n    }\n\n    @Override\n    public RSAOtherPrimeInfo applyFrom(Object o) {\n        if (o == null) {\n            throw new MalformedKeyException(\"RSA JWK 'oth' (Other Prime Info) element cannot be null.\");\n        }\n        if (!(o instanceof Map)) {\n            String msg = \"RSA JWK 'oth' (Other Prime Info) must contain map elements of name/value pairs. \" +\n                    \"Element type found: \" + o.getClass().getName();\n            throw new MalformedKeyException(msg);\n        }\n        Map<?, ?> m = (Map<?, ?>) o;\n        if (Collections.isEmpty(m)) {\n            throw new MalformedKeyException(\"RSA JWK 'oth' (Other Prime Info) element map cannot be empty.\");\n        }\n\n        // Need a Context instance to satisfy the API contract of the reader.get* methods below.\n        JwkContext<?> ctx = new DefaultJwkContext<>(PARAMS);\n        try {\n            for (Map.Entry<?, ?> entry : m.entrySet()) {\n                String name = String.valueOf(entry.getKey());\n                ctx.put(name, entry.getValue());\n            }\n        } catch (Exception e) {\n            throw new MalformedKeyException(e.getMessage(), e);\n        }\n\n        ParameterReadable reader = new RequiredParameterReader(ctx);\n        BigInteger prime = reader.get(PRIME_FACTOR);\n        BigInteger primeExponent = reader.get(FACTOR_CRT_EXPONENT);\n        BigInteger crtCoefficient = reader.get(FACTOR_CRT_COEFFICIENT);\n\n        return new RSAOtherPrimeInfo(prime, primeExponent, crtCoefficient);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/RandomSecretKeyBuilder.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * @since 0.12.0\n */\npublic class RandomSecretKeyBuilder extends DefaultSecretKeyBuilder {\n\n    public RandomSecretKeyBuilder(String jcaName, int bitLength) {\n        super(jcaName, bitLength);\n    }\n\n    @Override\n    public SecretKey build() {\n        byte[] bytes = new byte[this.BIT_LENGTH / Byte.SIZE];\n        this.random.nextBytes(bytes);\n        return new SecretKeySpec(bytes, this.JCA_NAME);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/Randoms.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport java.security.SecureRandom;\n\n/**\n * @since 0.12.0\n */\npublic final class Randoms {\n\n    private static final SecureRandom DEFAULT_SECURE_RANDOM;\n\n    static {\n        DEFAULT_SECURE_RANDOM = new SecureRandom();\n        DEFAULT_SECURE_RANDOM.nextBytes(new byte[64]);\n    }\n\n    private Randoms() {\n    }\n\n    /**\n     * Returns JJWT's default SecureRandom number generator - a static singleton which may be cached if desired.\n     * The RNG is initialized using the JVM default as follows:\n     *\n     * <pre><code>\n     * static {\n     *     DEFAULT_SECURE_RANDOM = new SecureRandom();\n     *     DEFAULT_SECURE_RANDOM.nextBytes(new byte[64]);\n     * }\n     * </code></pre>\n     *\n     * <p><code>nextBytes</code> is called to force the RNG to initialize itself if not already initialized.  The\n     * byte array is not used and discarded immediately for garbage collection.</p>\n     *\n     * @return JJWT's default SecureRandom number generator.\n     */\n    public static SecureRandom secureRandom() {\n        return DEFAULT_SECURE_RANDOM;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/RsaPrivateJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.Parameter;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.lang.Arrays;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.RsaPrivateJwk;\nimport io.jsonwebtoken.security.RsaPublicJwk;\nimport io.jsonwebtoken.security.UnsupportedKeyException;\n\nimport java.math.BigInteger;\nimport java.security.KeyFactory;\nimport java.security.PublicKey;\nimport java.security.interfaces.RSAMultiPrimePrivateCrtKey;\nimport java.security.interfaces.RSAPrivateCrtKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.KeySpec;\nimport java.security.spec.RSAMultiPrimePrivateCrtKeySpec;\nimport java.security.spec.RSAOtherPrimeInfo;\nimport java.security.spec.RSAPrivateCrtKeySpec;\nimport java.security.spec.RSAPrivateKeySpec;\nimport java.security.spec.RSAPublicKeySpec;\nimport java.util.List;\nimport java.util.Set;\n\nclass RsaPrivateJwkFactory extends AbstractFamilyJwkFactory<RSAPrivateKey, RsaPrivateJwk> {\n\n    //All RSA Private params _except_ for PRIVATE_EXPONENT.  That is always required:\n    private static final Set<Parameter<BigInteger>> OPTIONAL_PRIVATE_PARAMS = Collections.setOf(\n            DefaultRsaPrivateJwk.FIRST_PRIME, DefaultRsaPrivateJwk.SECOND_PRIME,\n            DefaultRsaPrivateJwk.FIRST_CRT_EXPONENT, DefaultRsaPrivateJwk.SECOND_CRT_EXPONENT,\n            DefaultRsaPrivateJwk.FIRST_CRT_COEFFICIENT\n    );\n\n    private static final String PUBKEY_ERR_MSG = \"JwkContext publicKey must be an \" + RSAPublicKey.class.getName() + \" instance.\";\n    private static final String PUB_EXPONENT_EX_MSG =\n            \"Unable to derive RSAPublicKey from RSAPrivateKey [%s]. Supported keys implement the \" +\n                    RSAPrivateCrtKey.class.getName() + \" or \" + RSAMultiPrimePrivateCrtKey.class.getName() +\n                    \" interfaces.  If the specified RSAPrivateKey cannot be one of these two, you must explicitly \" +\n                    \"provide an RSAPublicKey in addition to the RSAPrivateKey, as the \" +\n                    \"[JWA RFC, Section 6.3.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.2) \" +\n                    \"requires public values to be present in private RSA JWKs.\";\n\n    RsaPrivateJwkFactory() {\n        super(DefaultRsaPublicJwk.TYPE_VALUE, RSAPrivateKey.class, DefaultRsaPrivateJwk.PARAMS);\n    }\n\n    @Override\n    protected boolean supportsKeyValues(JwkContext<?> ctx) {\n        return super.supportsKeyValues(ctx) && ctx.containsKey(DefaultRsaPrivateJwk.PRIVATE_EXPONENT.getId());\n    }\n\n    private static BigInteger getPublicExponent(RSAPrivateKey key) {\n        if (key instanceof RSAPrivateCrtKey) {\n            return ((RSAPrivateCrtKey) key).getPublicExponent();\n        } else if (key instanceof RSAMultiPrimePrivateCrtKey) {\n            return ((RSAMultiPrimePrivateCrtKey) key).getPublicExponent();\n        }\n\n        String msg = String.format(PUB_EXPONENT_EX_MSG, KeysBridge.toString(key));\n        throw new UnsupportedKeyException(msg);\n    }\n\n    private RSAPublicKey derivePublic(final JwkContext<RSAPrivateKey> ctx) {\n        RSAPrivateKey key = ctx.getKey();\n        BigInteger modulus = key.getModulus();\n        BigInteger publicExponent = getPublicExponent(key);\n        final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);\n        return generateKey(ctx, RSAPublicKey.class, new CheckedFunction<KeyFactory, RSAPublicKey>() {\n            @Override\n            public RSAPublicKey apply(KeyFactory kf) {\n                try {\n                    return (RSAPublicKey) kf.generatePublic(spec);\n                } catch (Exception e) {\n                    String msg = \"Unable to derive RSAPublicKey from RSAPrivateKey \" + ctx + \". Cause: \" + e.getMessage();\n                    throw new InvalidKeyException(msg);\n                }\n            }\n        });\n    }\n\n    @Override\n    protected RsaPrivateJwk createJwkFromKey(JwkContext<RSAPrivateKey> ctx) {\n\n        RSAPrivateKey key = ctx.getKey();\n        RSAPublicKey rsaPublicKey;\n\n        PublicKey publicKey = ctx.getPublicKey();\n        if (publicKey != null) {\n            rsaPublicKey = Assert.isInstanceOf(RSAPublicKey.class, publicKey, PUBKEY_ERR_MSG);\n        } else {\n            rsaPublicKey = derivePublic(ctx);\n        }\n\n        // The [JWA Spec](https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.1)\n        // requires public values to be present in private JWKs, so add them:\n\n        // If a JWK fingerprint has been requested to be the JWK id, ensure we copy over the one computed for the\n        // public key per https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        boolean copyId = !Strings.hasText(ctx.getId()) && ctx.getIdThumbprintAlgorithm() != null;\n\n        JwkContext<RSAPublicKey> pubCtx = RsaPublicJwkFactory.INSTANCE.newContext(ctx, rsaPublicKey);\n        RsaPublicJwk pubJwk = RsaPublicJwkFactory.INSTANCE.createJwk(pubCtx);\n        ctx.putAll(pubJwk); // add public values to private key context\n        if (copyId) {\n            ctx.setId(pubJwk.getId());\n        }\n\n        put(ctx, DefaultRsaPrivateJwk.PRIVATE_EXPONENT, key.getPrivateExponent());\n\n        if (key instanceof RSAPrivateCrtKey) {\n            RSAPrivateCrtKey ckey = (RSAPrivateCrtKey) key;\n            //noinspection DuplicatedCode\n            put(ctx, DefaultRsaPrivateJwk.FIRST_PRIME, ckey.getPrimeP());\n            put(ctx, DefaultRsaPrivateJwk.SECOND_PRIME, ckey.getPrimeQ());\n            put(ctx, DefaultRsaPrivateJwk.FIRST_CRT_EXPONENT, ckey.getPrimeExponentP());\n            put(ctx, DefaultRsaPrivateJwk.SECOND_CRT_EXPONENT, ckey.getPrimeExponentQ());\n            put(ctx, DefaultRsaPrivateJwk.FIRST_CRT_COEFFICIENT, ckey.getCrtCoefficient());\n        } else if (key instanceof RSAMultiPrimePrivateCrtKey) {\n            RSAMultiPrimePrivateCrtKey ckey = (RSAMultiPrimePrivateCrtKey) key;\n            //noinspection DuplicatedCode\n            put(ctx, DefaultRsaPrivateJwk.FIRST_PRIME, ckey.getPrimeP());\n            put(ctx, DefaultRsaPrivateJwk.SECOND_PRIME, ckey.getPrimeQ());\n            put(ctx, DefaultRsaPrivateJwk.FIRST_CRT_EXPONENT, ckey.getPrimeExponentP());\n            put(ctx, DefaultRsaPrivateJwk.SECOND_CRT_EXPONENT, ckey.getPrimeExponentQ());\n            put(ctx, DefaultRsaPrivateJwk.FIRST_CRT_COEFFICIENT, ckey.getCrtCoefficient());\n            List<RSAOtherPrimeInfo> infos = Arrays.asList(ckey.getOtherPrimeInfo());\n            if (!Collections.isEmpty(infos)) {\n                put(ctx, DefaultRsaPrivateJwk.OTHER_PRIMES_INFO, infos);\n            }\n        }\n\n        return new DefaultRsaPrivateJwk(ctx, pubJwk);\n    }\n\n    @Override\n    protected RsaPrivateJwk createJwkFromValues(JwkContext<RSAPrivateKey> ctx) {\n\n        final ParameterReadable reader = new RequiredParameterReader(ctx);\n\n        final BigInteger privateExponent = reader.get(DefaultRsaPrivateJwk.PRIVATE_EXPONENT);\n\n        //The [JWA Spec, Section 6.3.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-6.3.2) requires\n        //RSA Private Keys to also encode the public key values, so we assert that we can acquire it successfully:\n        JwkContext<RSAPublicKey> pubCtx = new DefaultJwkContext<>(DefaultRsaPublicJwk.PARAMS, ctx);\n        RsaPublicJwk pubJwk = RsaPublicJwkFactory.INSTANCE.createJwkFromValues(pubCtx);\n        RSAPublicKey pubKey = pubJwk.toKey();\n        final BigInteger modulus = pubKey.getModulus();\n        final BigInteger publicExponent = pubKey.getPublicExponent();\n\n        // JWA Section 6.3.2 also indicates that if any of the optional private names are present, then *all* of those\n        // optional values must be present (except 'oth', which is handled separately next).  Quote:\n        //\n        //     If the producer includes any of the other private key parameters, then all of the others MUST\n        //     be present, with the exception of \"oth\", which MUST only be present when more than two prime\n        //     factors were used\n        //\n        boolean containsOptional = false;\n        for (Parameter<?> param : OPTIONAL_PRIVATE_PARAMS) {\n            if (ctx.containsKey(param.getId())) {\n                containsOptional = true;\n                break;\n            }\n        }\n\n        KeySpec spec;\n\n        if (containsOptional) { //if any one optional parameter exists, they are all required per JWA Section 6.3.2:\n            BigInteger firstPrime = reader.get(DefaultRsaPrivateJwk.FIRST_PRIME);\n            BigInteger secondPrime = reader.get(DefaultRsaPrivateJwk.SECOND_PRIME);\n            BigInteger firstCrtExponent = reader.get(DefaultRsaPrivateJwk.FIRST_CRT_EXPONENT);\n            BigInteger secondCrtExponent = reader.get(DefaultRsaPrivateJwk.SECOND_CRT_EXPONENT);\n            BigInteger firstCrtCoefficient = reader.get(DefaultRsaPrivateJwk.FIRST_CRT_COEFFICIENT);\n\n            // Other Primes Info is actually optional even if the above ones are required:\n            if (ctx.containsKey(DefaultRsaPrivateJwk.OTHER_PRIMES_INFO.getId())) {\n                List<RSAOtherPrimeInfo> otherPrimes = reader.get(DefaultRsaPrivateJwk.OTHER_PRIMES_INFO);\n                RSAOtherPrimeInfo[] arr = new RSAOtherPrimeInfo[Collections.size(otherPrimes)];\n                arr = otherPrimes.toArray(arr);\n                spec = new RSAMultiPrimePrivateCrtKeySpec(modulus, publicExponent, privateExponent, firstPrime,\n                        secondPrime, firstCrtExponent, secondCrtExponent, firstCrtCoefficient, arr);\n            } else {\n                spec = new RSAPrivateCrtKeySpec(modulus, publicExponent, privateExponent, firstPrime, secondPrime,\n                        firstCrtExponent, secondCrtExponent, firstCrtCoefficient);\n            }\n        } else {\n            spec = new RSAPrivateKeySpec(modulus, privateExponent);\n        }\n\n        RSAPrivateKey key = generateFromSpec(ctx, spec);\n        ctx.setKey(key);\n\n        return new DefaultRsaPrivateJwk(ctx, pubJwk);\n    }\n\n    protected RSAPrivateKey generateFromSpec(JwkContext<RSAPrivateKey> ctx, final KeySpec keySpec) {\n        return generateKey(ctx, new CheckedFunction<KeyFactory, RSAPrivateKey>() {\n            @Override\n            public RSAPrivateKey apply(KeyFactory kf) throws Exception {\n                return (RSAPrivateKey) kf.generatePrivate(keySpec);\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/RsaPublicJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.security.RsaPublicJwk;\n\nimport java.math.BigInteger;\nimport java.security.KeyFactory;\nimport java.security.interfaces.RSAPublicKey;\nimport java.security.spec.RSAPublicKeySpec;\n\nclass RsaPublicJwkFactory extends AbstractFamilyJwkFactory<RSAPublicKey, RsaPublicJwk> {\n\n    static final RsaPublicJwkFactory INSTANCE = new RsaPublicJwkFactory();\n\n    RsaPublicJwkFactory() {\n        super(DefaultRsaPublicJwk.TYPE_VALUE, RSAPublicKey.class, DefaultRsaPublicJwk.PARAMS);\n    }\n\n    @Override\n    protected RsaPublicJwk createJwkFromKey(JwkContext<RSAPublicKey> ctx) {\n        RSAPublicKey key = ctx.getKey();\n        ctx.put(DefaultRsaPublicJwk.MODULUS.getId(), DefaultRsaPublicJwk.MODULUS.applyTo(key.getModulus()));\n        ctx.put(DefaultRsaPublicJwk.PUBLIC_EXPONENT.getId(), DefaultRsaPublicJwk.PUBLIC_EXPONENT.applyTo(key.getPublicExponent()));\n        return new DefaultRsaPublicJwk(ctx);\n    }\n\n    @Override\n    protected RsaPublicJwk createJwkFromValues(JwkContext<RSAPublicKey> ctx) {\n        ParameterReadable reader = new RequiredParameterReader(ctx);\n        BigInteger modulus = reader.get(DefaultRsaPublicJwk.MODULUS);\n        BigInteger publicExponent = reader.get(DefaultRsaPublicJwk.PUBLIC_EXPONENT);\n        final RSAPublicKeySpec spec = new RSAPublicKeySpec(modulus, publicExponent);\n\n        RSAPublicKey key = generateKey(ctx, new CheckedFunction<KeyFactory, RSAPublicKey>() {\n            @Override\n            public RSAPublicKey apply(KeyFactory keyFactory) throws Exception {\n                return (RSAPublicKey) keyFactory.generatePublic(spec);\n            }\n        });\n\n        ctx.setKey(key);\n\n        return new DefaultRsaPublicJwk(ctx);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/RsaSignatureAlgorithm.java",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.KeyPairBuilder;\nimport io.jsonwebtoken.security.SecureRequest;\nimport io.jsonwebtoken.security.SignatureAlgorithm;\nimport io.jsonwebtoken.security.VerifySecureDigestRequest;\nimport io.jsonwebtoken.security.WeakKeyException;\n\nimport java.io.InputStream;\nimport java.security.Key;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.Signature;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.security.spec.MGF1ParameterSpec;\nimport java.security.spec.PSSParameterSpec;\nimport java.util.LinkedHashMap;\nimport java.util.Locale;\nimport java.util.Map;\nimport java.util.Set;\n\n/**\n * @since 0.12.0\n */\nfinal class RsaSignatureAlgorithm extends AbstractSignatureAlgorithm {\n\n    // Defined in https://www.rfc-editor.org/rfc/rfc8017#appendix-A.1:\n    //private static final String RSA_ENC_OID = \"1.2.840.113549.1.1.1\"; // RFC 8017's \"rsaEncryption\"\n\n    // Defined in https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.3:\n    static final String PSS_JCA_NAME = \"RSASSA-PSS\";\n    static final String PSS_OID = \"1.2.840.113549.1.1.10\"; // RFC 8017's \"id-RSASSA-PSS\"\n\n    // Defined in https://www.rfc-editor.org/rfc/rfc8017#appendix-A.2.4:\n    private static final String RS256_OID = \"1.2.840.113549.1.1.11\"; // RFC 8017's \"sha256WithRSAEncryption\"\n    private static final String RS384_OID = \"1.2.840.113549.1.1.12\"; // RFC 8017's \"sha384WithRSAEncryption\"\n    private static final String RS512_OID = \"1.2.840.113549.1.1.13\"; // RFC 8017's \"sha512WithRSAEncryption\"\n\n    private static final Set<String> PSS_ALG_NAMES = Collections.setOf(PSS_JCA_NAME, PSS_OID);\n\n    private static final Set<String> KEY_ALG_NAMES =\n            Collections.setOf(\"RSA\", PSS_JCA_NAME, PSS_OID, RS256_OID, RS384_OID, RS512_OID);\n\n    private static final int MIN_KEY_BIT_LENGTH = 2048;\n\n    private static AlgorithmParameterSpec pssParamSpec(int digestBitLength) {\n        MGF1ParameterSpec ps = new MGF1ParameterSpec(\"SHA-\" + digestBitLength);\n        int saltByteLength = digestBitLength / Byte.SIZE;\n        return new PSSParameterSpec(ps.getDigestAlgorithm(), \"MGF1\", ps, saltByteLength, 1);\n    }\n\n    private static SignatureAlgorithm rsaSsaPss(int digestBitLength) {\n        return new RsaSignatureAlgorithm(digestBitLength, pssParamSpec(digestBitLength));\n    }\n\n    static final SignatureAlgorithm RS256 = new RsaSignatureAlgorithm(256);\n    static final SignatureAlgorithm RS384 = new RsaSignatureAlgorithm(384);\n    static final SignatureAlgorithm RS512 = new RsaSignatureAlgorithm(512);\n    static final SignatureAlgorithm PS256 = rsaSsaPss(256);\n    static final SignatureAlgorithm PS384 = rsaSsaPss(384);\n    static final SignatureAlgorithm PS512 = rsaSsaPss(512);\n\n    private static final Map<String, SignatureAlgorithm> PKCSv15_ALGS;\n\n    static {\n        PKCSv15_ALGS = new LinkedHashMap<>();\n        PKCSv15_ALGS.put(RS256_OID, RS256);\n        PKCSv15_ALGS.put(RS384_OID, RS384);\n        PKCSv15_ALGS.put(RS512_OID, RS512);\n    }\n\n    private final int preferredKeyBitLength;\n\n    private final AlgorithmParameterSpec algorithmParameterSpec;\n\n    private RsaSignatureAlgorithm(String name, String jcaName, int digestBitLength, AlgorithmParameterSpec paramSpec) {\n        super(name, jcaName);\n        this.preferredKeyBitLength = digestBitLength * Byte.SIZE; // RSA invariant\n        // invariant since this is a protected constructor:\n        Assert.state(this.preferredKeyBitLength >= MIN_KEY_BIT_LENGTH);\n        this.algorithmParameterSpec = paramSpec;\n    }\n\n    private RsaSignatureAlgorithm(int digestBitLength) {\n        this(\"RS\" + digestBitLength, \"SHA\" + digestBitLength + \"withRSA\", digestBitLength, null);\n    }\n\n    // RSASSA-PSS constructor\n    private RsaSignatureAlgorithm(int digestBitLength, AlgorithmParameterSpec paramSpec) {\n        this(\"PS\" + digestBitLength, PSS_JCA_NAME, digestBitLength, paramSpec);\n    }\n\n    static SignatureAlgorithm findByKey(Key key) {\n\n        String algName = KeysBridge.findAlgorithm(key);\n        if (!Strings.hasText(algName)) {\n            return null;\n        }\n        algName = algName.toUpperCase(Locale.ENGLISH); // for checking against name Sets\n\n        // some PKCS11 keystores and HSMs won't expose the RSAKey interface, so we can't assume it:\n        final int bitLength = KeysBridge.findBitLength(key); // returns -1 if we're unable to find out\n\n        if (PSS_ALG_NAMES.contains(algName)) { // generic RSASSA-PSS names, check for key lengths:\n            // even though we found an RSASSA-PSS key, we need to confirm that the key length is\n            // sufficient if the encoded key bytes are available:\n            if (bitLength >= 4096) {\n                return PS512;\n            } else if (bitLength >= 3072) {\n                return PS384;\n            } else if (bitLength >= MIN_KEY_BIT_LENGTH) {\n                return PS256;\n            }\n        }\n\n        // unable to resolve/recommend an RSASSA-PSS alg, so try PKCS v 1.5 algs by OID:\n        SignatureAlgorithm alg = PKCSv15_ALGS.get(algName);\n        if (alg != null) {\n            return alg;\n        }\n\n        if (\"RSA\".equals(algName)) {\n            if (bitLength >= 4096) {\n                return RS512;\n            } else if (bitLength >= 3072) {\n                return RS384;\n            } else if (bitLength >= MIN_KEY_BIT_LENGTH) {\n                return RS256;\n            }\n        }\n\n        return null;\n    }\n\n    static boolean isPss(Key key) {\n        String alg = KeysBridge.findAlgorithm(key);\n        return PSS_ALG_NAMES.contains(alg);\n    }\n\n    @SuppressWarnings(\"BooleanMethodIsAlwaysInverted\")\n    static boolean isRsaAlgorithmName(Key key) {\n        String alg = KeysBridge.findAlgorithm(key);\n        return KEY_ALG_NAMES.contains(alg);\n    }\n\n    @Override\n    public KeyPairBuilder keyPair() {\n        final String jcaName = this.algorithmParameterSpec != null ? PSS_JCA_NAME : \"RSA\";\n\n        //TODO: JDK 8 or later, for RSASSA-PSS, use the following instead of what is below:\n        //\n        // AlgorithmParameterSpec keyGenSpec = new RSAKeyGenParameterSpec(this.preferredKeyBitLength,\n        //     RSAKeyGenParameterSpec.F4, this.algorithmParameterSpec);\n        // return new DefaultKeyPairBuilder(jcaName, keyGenSpec).provider(getProvider()).random(Randoms.secureRandom());\n        //\n\n        return new DefaultKeyPairBuilder(jcaName, this.preferredKeyBitLength).random(Randoms.secureRandom());\n    }\n\n    @Override\n    protected void validateKey(Key key, boolean signing) {\n        super.validateKey(key, signing);\n        if (!isRsaAlgorithmName(key)) {\n            throw new InvalidKeyException(\"Unrecognized RSA or RSASSA-PSS key algorithm name.\");\n        }\n        int size = KeysBridge.findBitLength(key);\n        if (size < 0) return; // https://github.com/jwtk/jjwt/issues/68\n        if (size < MIN_KEY_BIT_LENGTH) {\n            String id = getId();\n            String section = id.startsWith(\"PS\") ? \"3.5\" : \"3.3\";\n            String msg = \"The RSA \" + keyType(signing) + \" key size (aka modulus bit length) is \" + size + \" bits \" +\n                    \"which is not secure enough for the \" + id + \" algorithm.  The JWT JWA Specification \" +\n                    \"(RFC 7518, Section \" + section + \") states that RSA keys MUST have a size >= \" +\n                    MIN_KEY_BIT_LENGTH + \" bits.  Consider using the Jwts.SIG.\" + id +\n                    \".keyPair() builder to create a KeyPair guaranteed to be secure enough for \" + id + \".  See \" +\n                    \"https://tools.ietf.org/html/rfc7518#section-\" + section + \" for more information.\";\n            throw new WeakKeyException(msg);\n        }\n    }\n\n    @Override\n    protected byte[] doDigest(final SecureRequest<InputStream, PrivateKey> request) {\n        return jca(request).withSignature(new CheckedFunction<Signature, byte[]>() {\n            @Override\n            public byte[] apply(Signature sig) throws Exception {\n                if (algorithmParameterSpec != null) {\n                    sig.setParameter(algorithmParameterSpec);\n                }\n                sig.initSign(request.getKey());\n                return sign(sig, request.getPayload());\n            }\n        });\n    }\n\n    @Override\n    protected boolean doVerify(final VerifySecureDigestRequest<PublicKey> request) {\n        return jca(request).withSignature(new CheckedFunction<Signature, Boolean>() {\n            @Override\n            public Boolean apply(Signature sig) throws Exception {\n                if (algorithmParameterSpec != null) {\n                    sig.setParameter(algorithmParameterSpec);\n                }\n                sig.initVerify(request.getKey());\n                return verify(sig, request.getPayload(), request.getDigest());\n            }\n        });\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/SecretJwkFactory.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.Identifiable;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.impl.lang.Bytes;\nimport io.jsonwebtoken.impl.lang.ParameterReadable;\nimport io.jsonwebtoken.impl.lang.RequiredParameterReader;\nimport io.jsonwebtoken.io.Encoders;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Strings;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.InvalidKeyException;\nimport io.jsonwebtoken.security.Keys;\nimport io.jsonwebtoken.security.MacAlgorithm;\nimport io.jsonwebtoken.security.MalformedKeyException;\nimport io.jsonwebtoken.security.SecretJwk;\nimport io.jsonwebtoken.security.SecretKeyAlgorithm;\nimport io.jsonwebtoken.security.WeakKeyException;\n\nimport javax.crypto.SecretKey;\nimport javax.crypto.spec.SecretKeySpec;\n\n/**\n * @since 0.12.0\n */\nclass SecretJwkFactory extends AbstractFamilyJwkFactory<SecretKey, SecretJwk> {\n\n    SecretJwkFactory() {\n        super(DefaultSecretJwk.TYPE_VALUE, SecretKey.class, DefaultSecretJwk.PARAMS);\n    }\n\n    @Override\n    protected SecretJwk createJwkFromKey(JwkContext<SecretKey> ctx) {\n        SecretKey key = Assert.notNull(ctx.getKey(), \"JwkContext key cannot be null.\");\n        String k;\n        byte[] encoded = null;\n        try {\n            encoded = KeysBridge.getEncoded(key);\n            k = Encoders.BASE64URL.encode(encoded);\n            Assert.hasText(k, \"k value cannot be null or empty.\");\n        } catch (Throwable t) {\n            String msg = \"Unable to encode SecretKey to JWK: \" + t.getMessage();\n            throw new InvalidKeyException(msg, t);\n        } finally {\n            Bytes.clear(encoded);\n        }\n\n        MacAlgorithm mac = DefaultMacAlgorithm.findByKey(key);\n        if (mac != null) {\n            ctx.put(AbstractJwk.ALG.getId(), mac.getId());\n        }\n\n        ctx.put(DefaultSecretJwk.K.getId(), k);\n\n        return createJwkFromValues(ctx);\n    }\n\n    private static void assertKeyBitLength(byte[] bytes, MacAlgorithm alg) {\n        long bitLen = Bytes.bitLength(bytes);\n        long requiredBitLen = alg.getKeyBitLength();\n        if (bitLen < requiredBitLen) {\n            // Implementors note:  Don't print out any information about the `bytes` value itself - size,\n            // content, etc., as it is considered secret material:\n            String msg = \"Secret JWK \" + AbstractJwk.ALG + \" value is '\" + alg.getId() +\n                    \"', but the \" + DefaultSecretJwk.K + \" length is smaller than the \" + alg.getId() +\n                    \" minimum length of \" + Bytes.bitsMsg(requiredBitLen) +\n                    \" required by \" +\n                    \"[JWA RFC 7518, Section 3.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2), \" +\n                    \"2nd paragraph: 'A key of the same size as the hash output or larger MUST be used with this \" +\n                    \"algorithm.'\";\n            throw new WeakKeyException(msg);\n        }\n    }\n\n    private static void assertSymmetric(Identifiable alg) {\n        if (alg instanceof MacAlgorithm || alg instanceof SecretKeyAlgorithm || alg instanceof AeadAlgorithm)\n            return; // valid\n        String msg = \"Invalid Secret JWK \" + AbstractJwk.ALG + \" value '\" + alg.getId() + \"'. Secret JWKs \" +\n                \"may only be used with symmetric (secret) key algorithms.\";\n        throw new MalformedKeyException(msg);\n    }\n\n    @Override\n    protected SecretJwk createJwkFromValues(JwkContext<SecretKey> ctx) {\n        ParameterReadable reader = new RequiredParameterReader(ctx);\n        final byte[] bytes = reader.get(DefaultSecretJwk.K);\n        SecretKey key;\n\n        String algId = ctx.getAlgorithm();\n        if (!Strings.hasText(algId)) { // optional per https://www.rfc-editor.org/rfc/rfc7517.html#section-4.4\n\n            // Here we try to infer the best type of key to create based on siguse and/or key length.\n            //\n            // AES requires 128, 192, or 256 bits, so anything larger than 256 cannot be AES, so we'll need to assume\n            // HMAC.\n            //\n            // Also, 256 bits works for either HMAC or AES, so we just have to choose one as there is no other\n            // RFC-based criteria for determining.  Historically, we've chosen AES due to the larger number of\n            // KeyAlgorithm and AeadAlgorithm use cases, so that's our default.\n            int kBitLen = (int) Bytes.bitLength(bytes);\n\n            if (ctx.isSigUse() || kBitLen > Jwts.SIG.HS256.getKeyBitLength()) {\n                // The only JWA SecretKey signature algorithms are HS256, HS384, HS512, so choose based on bit length:\n                key = Keys.hmacShaKeyFor(bytes);\n            } else {\n                key = AesAlgorithm.keyFor(bytes);\n            }\n            ctx.setKey(key);\n            return new DefaultSecretJwk(ctx);\n        }\n\n        //otherwise 'alg' was specified, ensure it's valid for secret key use:\n        Identifiable alg = Jwts.SIG.get().get(algId);\n        if (alg == null) alg = Jwts.KEY.get().get(algId);\n        if (alg == null) alg = Jwts.ENC.get().get(algId);\n        if (alg != null) assertSymmetric(alg); // if we found a standard alg, it must be a symmetric key algorithm\n\n        if (alg instanceof MacAlgorithm) {\n            assertKeyBitLength(bytes, ((MacAlgorithm) alg));\n            String jcaName = ((CryptoAlgorithm) alg).getJcaName();\n            Assert.hasText(jcaName, \"Algorithm jcaName cannot be null or empty.\");\n            key = new SecretKeySpec(bytes, jcaName);\n        } else {\n            // all other remaining JWA-standard symmetric algs use AES:\n            key = AesAlgorithm.keyFor(bytes);\n        }\n        ctx.setKey(key);\n        return new DefaultSecretJwk(ctx);\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/StandardCurves.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.Curve;\n\nimport java.security.Key;\n\npublic final class StandardCurves extends IdRegistry<Curve> {\n\n    public StandardCurves() {\n        super(\"Elliptic Curve\", Collections.<Curve>of(\n                ECCurve.P256,\n                ECCurve.P384,\n                ECCurve.P521,\n                EdwardsCurve.X25519,\n                EdwardsCurve.X448,\n                EdwardsCurve.Ed25519,\n                EdwardsCurve.Ed448\n        ));\n    }\n\n    public static Curve findByKey(Key key) {\n        if (key == null) {\n            return null;\n        }\n        Curve curve = ECCurve.findByKey(key);\n        if (curve == null) {\n            curve = EdwardsCurve.findByKey(key);\n        }\n        return curve;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/StandardEncryptionAlgorithms.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.AeadAlgorithm;\n\n@SuppressWarnings(\"unused\") // used via reflection in io.jsonwebtoken.Jwts.ENC\npublic final class StandardEncryptionAlgorithms extends IdRegistry<AeadAlgorithm> {\n\n    public static final String NAME = \"JWE Encryption Algorithm\";\n\n    public StandardEncryptionAlgorithms() {\n        super(NAME, Collections.of(\n                (AeadAlgorithm) new HmacAesAeadAlgorithm(128),\n                new HmacAesAeadAlgorithm(192),\n                new HmacAesAeadAlgorithm(256),\n                new GcmAesAeadAlgorithm(128),\n                new GcmAesAeadAlgorithm(192),\n                new GcmAesAeadAlgorithm(256)));\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/StandardHashAlgorithms.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.HashAlgorithm;\n\n/**\n * Backing implementation for the {@link io.jsonwebtoken.security.Jwks.HASH} implementation.\n *\n * @since 0.12.0\n */\n@SuppressWarnings(\"unused\") // used via reflection in io.jsonwebtoken.security.Jwks.HASH\npublic final class StandardHashAlgorithms extends IdRegistry<HashAlgorithm> {\n\n    public StandardHashAlgorithms() {\n        super(\"IANA Hash Algorithm\", Collections.<HashAlgorithm>of(\n                // We don't include DefaultHashAlgorithm.SHA1 here on purpose because 1) it's not in the JWK IANA\n                // registry so we don't need to expose it anyway, and 2) we don't want to expose a less-safe algorithm.\n                // The SHA1 instance only exists in JJWT's codebase to support RFC-required `x5t`\n                // (X.509 SHA-1 Thumbprint) computation - we don't use it anywhere else.\n                new DefaultHashAlgorithm(\"sha-256\"),\n                new DefaultHashAlgorithm(\"sha-384\"),\n                new DefaultHashAlgorithm(\"sha-512\"),\n                new DefaultHashAlgorithm(\"sha3-256\"),\n                new DefaultHashAlgorithm(\"sha3-384\"),\n                new DefaultHashAlgorithm(\"sha3-512\")\n        ));\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/StandardKeyAlgorithms.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.KeyAlgorithm;\n\nimport javax.crypto.spec.OAEPParameterSpec;\nimport javax.crypto.spec.PSource;\nimport java.security.spec.AlgorithmParameterSpec;\nimport java.security.spec.MGF1ParameterSpec;\n\n/**\n * Static class definitions for standard {@link KeyAlgorithm} instances.\n *\n * @since 0.12.0\n */\npublic final class StandardKeyAlgorithms extends IdRegistry<KeyAlgorithm<?, ?>> {\n\n    public static final String NAME = \"JWE Key Management Algorithm\";\n\n    private static final String RSA1_5_ID = \"RSA1_5\";\n    private static final String RSA1_5_TRANSFORMATION = \"RSA/ECB/PKCS1Padding\";\n    private static final String RSA_OAEP_ID = \"RSA-OAEP\";\n    private static final String RSA_OAEP_TRANSFORMATION = \"RSA/ECB/OAEPWithSHA-1AndMGF1Padding\";\n    private static final String RSA_OAEP_256_ID = \"RSA-OAEP-256\";\n    private static final String RSA_OAEP_256_TRANSFORMATION = \"RSA/ECB/OAEPWithSHA-256AndMGF1Padding\";\n    private static final AlgorithmParameterSpec RSA_OAEP_256_SPEC =\n            new OAEPParameterSpec(\"SHA-256\", \"MGF1\", MGF1ParameterSpec.SHA256, PSource.PSpecified.DEFAULT);\n\n    public StandardKeyAlgorithms() {\n        super(NAME, Collections.<KeyAlgorithm<?, ?>>of(\n                new DirectKeyAlgorithm(),\n                new AesWrapKeyAlgorithm(128),\n                new AesWrapKeyAlgorithm(192),\n                new AesWrapKeyAlgorithm(256),\n                new AesGcmKeyAlgorithm(128),\n                new AesGcmKeyAlgorithm(192),\n                new AesGcmKeyAlgorithm(256),\n                new Pbes2HsAkwAlgorithm(128),\n                new Pbes2HsAkwAlgorithm(192),\n                new Pbes2HsAkwAlgorithm(256),\n                new EcdhKeyAlgorithm(),\n                new EcdhKeyAlgorithm(new AesWrapKeyAlgorithm(128)),\n                new EcdhKeyAlgorithm(new AesWrapKeyAlgorithm(192)),\n                new EcdhKeyAlgorithm(new AesWrapKeyAlgorithm(256)),\n                new DefaultRsaKeyAlgorithm(RSA1_5_ID, RSA1_5_TRANSFORMATION),\n                new DefaultRsaKeyAlgorithm(RSA_OAEP_ID, RSA_OAEP_TRANSFORMATION),\n                new DefaultRsaKeyAlgorithm(RSA_OAEP_256_ID, RSA_OAEP_256_TRANSFORMATION, RSA_OAEP_256_SPEC)\n        ));\n    }\n\n    /*\n    private static KeyAlgorithm<Password, Password> lean(final Pbes2HsAkwAlgorithm alg) {\n\n        // ensure we use the same key factory over and over so that time spent acquiring one is not repeated:\n        JcaTemplate template = new JcaTemplate(alg.getJcaName(), null, Randoms.secureRandom());\n        final SecretKeyFactory factory = template.execute(SecretKeyFactory.class, new CheckedFunction<SecretKeyFactory, SecretKeyFactory>() {\n            @Override\n            public SecretKeyFactory apply(SecretKeyFactory secretKeyFactory) {\n                return secretKeyFactory;\n            }\n        });\n\n        // pre-compute the salt so we don't spend time doing that on each iteration.  Doesn't need to be random for a\n        // computation-only test:\n        final byte[] rfcSalt = alg.toRfcSalt(alg.generateInputSalt(null));\n\n        // ensure that the bare minimum steps are performed to hash, ensuring our time sampling pertains only to\n        // hashing and not ancillary steps needed to setup the hashing/derivation\n        return new KeyAlgorithm<Password, Password>() {\n            @Override\n            public KeyResult getEncryptionKey(KeyRequest<Password> request) throws SecurityException {\n                int iterations = request.getHeader().getPbes2Count();\n                char[] password = request.getKey().getPassword();\n                try {\n                    alg.deriveKey(factory, password, rfcSalt, iterations);\n                } catch (Exception e) {\n                    throw new SecurityException(\"Unable to derive key\", e);\n                }\n                return null;\n            }\n\n            @Override\n            public SecretKey getDecryptionKey(DecryptionKeyRequest<Password> request) throws SecurityException {\n                throw new UnsupportedOperationException(\"Not intended to be called.\");\n            }\n\n            @Override\n            public String getId() {\n                return alg.getId();\n            }\n        };\n    }\n\n    private static char randomChar() {\n        return (char) Randoms.secureRandom().nextInt(Character.MAX_VALUE);\n    }\n\n    private static char[] randomChars(@SuppressWarnings(\"SameParameterValue\") int length) {\n        char[] chars = new char[length];\n        for (int i = 0; i < length; i++) {\n            chars[i] = randomChar();\n        }\n        return chars;\n    }\n\n    public static int estimateIterations(KeyAlgorithm<Password, Password> alg, long desiredMillis) {\n\n        // The number of computational samples that land in our 'sweet spot' timing range matching desiredMillis.\n        // These samples will be averaged and the final average will be the return value of this method\n        // representing the number of iterations that should be taken for any given PBE hashing attempt to get\n        // reasonably close to desiredMillis:\n        final int NUM_SAMPLES = 30;\n        final int SKIP = 3;\n        // More important than the actual password (or characters) is the password length.\n        // 8 characters is a commonly-found minimum required length in many systems circa 2021.\n        final int PASSWORD_LENGTH = 8;\n\n        final JweHeader HEADER = new DefaultJweHeader();\n        final AeadAlgorithm ENC_ALG = Jwts.ENC.A128GCM; // not used, needed to satisfy API\n\n        if (alg instanceof Pbes2HsAkwAlgorithm) {\n            // Strip away all things that cause time during computation except for the actual hashing algorithm:\n            alg = lean((Pbes2HsAkwAlgorithm) alg);\n        }\n\n        int workFactor = 1000; // same as iterations for PBKDF2.  Different concept for Bcrypt/Scrypt\n        int minWorkFactor = workFactor;\n        List<Point> points = new ArrayList<>(NUM_SAMPLES);\n        for (int i = 0; points.size() < NUM_SAMPLES; i++) {\n\n            char[] password = randomChars(PASSWORD_LENGTH);\n            Password key = Keys.password(password);\n            HEADER.pbes2Count(workFactor);\n            KeyRequest<Password> request = new DefaultKeyRequest<>(null, null, key, HEADER, ENC_ALG);\n\n            long start = System.currentTimeMillis();\n            alg.getEncryptionKey(request); // <-- Computation occurs here.  Don't need the result, just need to exec\n            long end = System.currentTimeMillis();\n            long duration = end - start;\n\n            // Exclude the first SKIP number of attempts from the average due to initial JIT optimization/slowness.\n            // After a few attempts, the JVM should be relatively optimized and the subsequent\n            // PBE hashing times are the ones we want to include in our analysis\n            boolean warmedUp = i >= SKIP;\n\n            // how close we were on this hashing attempt to reach our desiredMillis target:\n            // A number under 1 means we weren't slow enough, a number greater than 1 means we were too slow:\n            double durationPercentAchieved = (double) duration / (double) desiredMillis;\n\n            // we only want to collect timing samples if :\n            // 1. we're warmed up (to account for JIT optimization)\n            // 2. The attempt time at least met (>=) the desiredMillis target\n            boolean collectSample = warmedUp && duration >= desiredMillis;\n            if (collectSample) {\n                // For each attempt, the x axis is the workFactor, and the y axis is how long it took to compute:\n                points.add(new Point(workFactor, duration));\n                //System.out.println(\"Collected point: workFactor=\" + workFactor + \", duration=\" + duration + \" ms, %achieved=\" + durationPercentAchieved);\n            } else {\n                minWorkFactor = Math.max(minWorkFactor, workFactor);\n                //System.out.println(\"      Excluding sample: workFactor=\" + workFactor + \", duration=\" + duration + \" ms, %achieved=\" + durationPercentAchieved);\n            }\n\n            // amount to increase or decrease the workFactor for the next hashing iteration.  We increase if\n            // we haven't met the desired millisecond time, and decrease if we're over it a little too much, always\n            // trying to stay in that desired timing sweet spot\n            double percentAdjust = workFactor * 0.0075; // 3/4ths of a percent\n            if (durationPercentAchieved < 1d) {\n                // Under target.  Let's increase by the amount that should get right at (or near) 100%:\n                double ratio = desiredMillis / (double) duration;\n                if (ratio > 1) {\n                    double result = workFactor * ratio;\n                    workFactor = (int) result;\n                } else {\n                    double difference = workFactor * (1 - durationPercentAchieved);\n                    workFactor += Math.max(percentAdjust, difference);\n                }\n            } else if (durationPercentAchieved > 1.01d) {\n                // Over target. Let's decrease gently to get closer.\n                double difference = workFactor * (durationPercentAchieved - 1.01);\n                difference = Math.min(percentAdjust, difference);\n                // math.max here because the min allowed is 1000 per the JWA RFC, so we never want to go below that.\n                workFactor = (int) Math.max(1000, workFactor - difference);\n            } else {\n                // we're at our target (desiredMillis); let's increase by a teeny bit to see where we get\n                // (and the JVM might optimize with the same inputs, so we want to prevent that here)\n                workFactor += 100;\n            }\n        }\n\n        // We've collected all of our samples, now let's find the workFactor average number\n        // That average is the best estimate for ensuring PBE hashes for the specified algorithm meet the\n        // desiredMillis target on the current JVM/CPU platform:\n        double sumX = 0;\n        for (Point p : points) {\n            sumX += p.x;\n        }\n        double average = sumX / points.size();\n        //ensure our average is at least as much as the smallest work factor that got us closest to desiredMillis:\n        return (int) Math.max(average, minWorkFactor);\n    }\n\n    private static class Point {\n        long x;\n        long y;\n        double lnY;\n\n        public Point(long x, long y) {\n            this.x = x;\n            this.y = y;\n            this.lnY = Math.log((double) y);\n        }\n    }\n     */\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/StandardKeyOperations.java",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.KeyOperation;\n\npublic final class StandardKeyOperations extends IdRegistry<KeyOperation> {\n\n    public StandardKeyOperations() {\n        super(\"JSON Web Key Operation\", Collections.of(\n                DefaultKeyOperation.SIGN,\n                DefaultKeyOperation.VERIFY,\n                DefaultKeyOperation.ENCRYPT,\n                DefaultKeyOperation.DECRYPT,\n                DefaultKeyOperation.WRAP,\n                DefaultKeyOperation.UNWRAP,\n                DefaultKeyOperation.DERIVE_KEY,\n                DefaultKeyOperation.DERIVE_BITS\n        ));\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/StandardSecureDigestAlgorithms.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.lang.IdRegistry;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.SecureDigestAlgorithm;\n\nimport javax.crypto.SecretKey;\nimport java.security.Key;\nimport java.security.PrivateKey;\n\n@SuppressWarnings(\"unused\") // used via reflection in io.jsonwebtoken.Jwts.SIG\npublic final class StandardSecureDigestAlgorithms extends IdRegistry<SecureDigestAlgorithm<?, ?>> {\n\n    public static final String NAME = \"JWS Digital Signature or MAC\";\n\n    public StandardSecureDigestAlgorithms() {\n        super(NAME, Collections.of(\n                NoneSignatureAlgorithm.INSTANCE,\n                DefaultMacAlgorithm.HS256,\n                DefaultMacAlgorithm.HS384,\n                DefaultMacAlgorithm.HS512,\n                RsaSignatureAlgorithm.RS256,\n                RsaSignatureAlgorithm.RS384,\n                RsaSignatureAlgorithm.RS512,\n                RsaSignatureAlgorithm.PS256,\n                RsaSignatureAlgorithm.PS384,\n                RsaSignatureAlgorithm.PS512,\n                EcSignatureAlgorithm.ES256,\n                EcSignatureAlgorithm.ES384,\n                EcSignatureAlgorithm.ES512,\n                EdSignatureAlgorithm.INSTANCE\n        ));\n    }\n\n    @SuppressWarnings(\"unchecked\")\n    public static <K extends Key> SecureDigestAlgorithm<K, ?> findBySigningKey(K key) {\n\n        SecureDigestAlgorithm<?, ?> alg = null; // null value means no suitable match\n\n        if (key instanceof SecretKey && !(key instanceof Password)) {\n\n            alg = DefaultMacAlgorithm.findByKey(key);\n\n        } else if (key instanceof PrivateKey) {\n\n            PrivateKey pk = (PrivateKey) key;\n\n            alg = RsaSignatureAlgorithm.findByKey(pk);\n            if (alg == null) {\n                alg = EcSignatureAlgorithm.findByKey(pk);\n            }\n            if (alg == null && EdSignatureAlgorithm.isSigningKey(pk)) {\n                alg = EdSignatureAlgorithm.INSTANCE;\n            }\n        }\n\n        return (SecureDigestAlgorithm<K, ?>) alg;\n    }\n}\n"
  },
  {
    "path": "impl/src/main/java/io/jsonwebtoken/impl/security/X509BuilderSupport.java",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security;\n\nimport io.jsonwebtoken.impl.ParameterMap;\nimport io.jsonwebtoken.impl.io.Streams;\nimport io.jsonwebtoken.impl.lang.CheckedFunction;\nimport io.jsonwebtoken.impl.lang.Function;\nimport io.jsonwebtoken.impl.lang.Functions;\nimport io.jsonwebtoken.lang.Assert;\nimport io.jsonwebtoken.lang.Collections;\nimport io.jsonwebtoken.lang.Objects;\nimport io.jsonwebtoken.security.HashAlgorithm;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.Request;\nimport io.jsonwebtoken.security.X509Builder;\n\nimport java.io.InputStream;\nimport java.net.URI;\nimport java.security.cert.X509Certificate;\nimport java.util.List;\n\n//Consolidates logic between DefaultJwtHeaderBuilder and AbstractAsymmetricJwkBuilder\npublic class X509BuilderSupport implements X509Builder<X509BuilderSupport> {\n\n    private final ParameterMap map;\n\n    protected boolean computeX509Sha1Thumbprint;\n\n    /**\n     * Boolean object indicates 3 states: 1) not configured 2) configured as true, 3) configured as false\n     */\n    protected Boolean computeX509Sha256Thumbprint = null;\n\n    private static Function<X509Certificate, byte[]> createGetBytesFunction(Class<? extends RuntimeException> clazz) {\n        return Functions.wrapFmt(new CheckedFunction<X509Certificate, byte[]>() {\n            @Override\n            public byte[] apply(X509Certificate cert) throws Exception {\n                return cert.getEncoded();\n            }\n        }, clazz, \"Unable to access X509Certificate encoded bytes necessary to compute thumbprint. Certificate: %s\");\n    }\n\n    private final Function<X509Certificate, byte[]> GET_X509_BYTES;\n\n    public X509BuilderSupport(ParameterMap map, Class<? extends RuntimeException> getBytesFailedException) {\n        this.map = Assert.notNull(map, \"ParameterMap cannot be null.\");\n        this.GET_X509_BYTES = createGetBytesFunction(getBytesFailedException);\n    }\n\n    @Override\n    public X509BuilderSupport x509Url(URI uri) {\n        this.map.put(AbstractAsymmetricJwk.X5U.getId(), uri);\n        return this;\n    }\n\n    @Override\n    public X509BuilderSupport x509Chain(List<X509Certificate> chain) {\n        this.map.put(AbstractAsymmetricJwk.X5C.getId(), chain);\n        return this;\n    }\n\n    @Override\n    public X509BuilderSupport x509Sha1Thumbprint(byte[] thumbprint) {\n        this.map.put(AbstractAsymmetricJwk.X5T.getId(), thumbprint);\n        return this;\n    }\n\n    @Override\n    public X509BuilderSupport x509Sha1Thumbprint(boolean enable) {\n        this.computeX509Sha1Thumbprint = enable;\n        return this;\n    }\n\n    @Override\n    public X509BuilderSupport x509Sha256Thumbprint(byte[] thumbprint) {\n        this.map.put(AbstractAsymmetricJwk.X5T_S256.getId(), thumbprint);\n        return this;\n    }\n\n    @Override\n    public X509BuilderSupport x509Sha256Thumbprint(boolean enable) {\n        this.computeX509Sha256Thumbprint = enable;\n        return this;\n    }\n\n    private byte[] computeThumbprint(final X509Certificate cert, HashAlgorithm alg) {\n        byte[] encoded = GET_X509_BYTES.apply(cert);\n        InputStream in = Streams.of(encoded);\n        Request<InputStream> request = new DefaultRequest<>(in, null, null);\n        return alg.digest(request);\n    }\n\n    public void apply() {\n        List<X509Certificate> chain = this.map.get(AbstractAsymmetricJwk.X5C);\n        X509Certificate firstCert = null;\n        if (!Collections.isEmpty(chain)) {\n            firstCert = chain.get(0);\n        }\n\n        Boolean computeX509Sha256 = this.computeX509Sha256Thumbprint;\n        if (computeX509Sha256 == null) { //if not specified, enable by default if possible:\n            computeX509Sha256 = firstCert != null &&\n                    !computeX509Sha1Thumbprint && // no need if at least one thumbprint will be set\n                    Objects.isEmpty(this.map.get(AbstractAsymmetricJwk.X5T_S256)); // no need if already set\n        }\n\n        if (firstCert != null) {\n            if (computeX509Sha1Thumbprint) {\n                byte[] thumbprint = computeThumbprint(firstCert, DefaultHashAlgorithm.SHA1);\n                x509Sha1Thumbprint(thumbprint);\n            }\n            if (computeX509Sha256) {\n                byte[] thumbprint = computeThumbprint(firstCert, Jwks.HASH.SHA256);\n                x509Sha256Thumbprint(thumbprint);\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/main/resources/META-INF/services/io.jsonwebtoken.CompressionCodec",
    "content": "io.jsonwebtoken.impl.compression.DeflateCompressionAlgorithm\nio.jsonwebtoken.impl.compression.GzipCompressionAlgorithm"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/CompressionCodecsTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.impl.compression.DeflateCompressionAlgorithm\nimport io.jsonwebtoken.impl.compression.GzipCompressionAlgorithm\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.assertArrayEquals\nimport static org.junit.Assert.assertTrue\n\nclass CompressionCodecsTest {\n\n    static final String PLAINTEXT =\n            '''Bacon ipsum dolor amet venison beef pork chop, doner jowl pastrami ground round alcatra.\n               Beef leberkas filet mignon ball tip pork spare ribs kevin short loin ribeye ground round\n               biltong jerky short ribs corned beef. Strip steak turducken meatball porchetta beef ribs\n               shoulder pork belly doner salami corned beef kielbasa cow filet mignon drumstick. Bacon\n               tenderloin pancetta flank frankfurter ham kevin leberkas meatball turducken beef ribs.\n               Cupim short loin short ribs shankle tenderloin. Ham ribeye hamburger flank tenderloin\n               cupim t-bone, shank tri-tip venison salami sausage pancetta. Pork belly chuck salami\n               alcatra sirloin.\n               \n               以ケ ホゥ婧詃 橎ちゅぬ蛣埣 禧ざしゃ蟨廩 椥䤥グ曣わ 基覧 滯っ䶧きょメ Ủ䧞以ケ妣 择禤槜谣お 姨のドゥ,\n               らボみょば䪩 苯礊觊ツュ婃 䩦ディふげセ げセりょ 禤槜 Ủ䧞以ケ妣 せがみゅちょ䰯 择禤槜谣お 難ゞ滧 蝥ちゃ,\n               滯っ䶧きょメ らボみょば䪩 礯みゃ楦と饥 椥䤥グ ウァ槚 訤をりゃしゑ びゃ驨も氩簥 栨キョ奎婨榞 ヌに楃 以ケ,\n               姚奊べ 椥䤥グ曣わ 栨キョ奎婨榞 ちょ䰯 Ủ䧞以ケ妣 誧姨のドゥろ よ苯礊 く涥, りゅぽ槞 馣ぢゃ尦䦎ぎ\n               大た䏩䰥ぐ 郎きや楺橯 䧎キェ, 難ゞ滧 栧择 谯䧟簨訧ぎょ 椥䤥グ曣わ'''\n\n    @Test\n    void testCtor() {\n        //test coverage for private constructor:\n        new CompressionCodecs()\n    }\n\n    @Test\n    void testStatics() {\n        assertTrue Jwts.ZIP.DEF instanceof DeflateCompressionAlgorithm\n        assertTrue Jwts.ZIP.GZIP instanceof GzipCompressionAlgorithm\n    }\n\n    @Test\n    void testRoundTrip() {\n        byte[] data = Strings.utf8(PLAINTEXT)\n        for (CompressionCodec codec : [CompressionCodecs.GZIP, CompressionCodecs.DEFLATE]) {\n            byte[] compressed = codec.compress(data)\n            assertArrayEquals data, codec.decompress(compressed)\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/CustomObjectDeserializationTest.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.jackson.io.JacksonDeserializer\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNotNull\n\nclass CustomObjectDeserializationTest {\n\n    /**\n     * Test parsing without and then with a custom deserializer. Ensures custom type is parsed from claims\n     */\n    @Test\n    void testCustomObjectDeserialization() {\n\n        CustomBean customBean = new CustomBean()\n        customBean.key1 = \"value1\"\n        customBean.key2 = 42\n\n        String jwtString = Jwts.builder().claim(\"cust\", customBean).compact()\n\n        // no custom deserialization, object is a map\n        Jwt<Header, Claims> jwt = Jwts.parser().unsecured().build().parseUnsecuredClaims(jwtString)\n        assertNotNull jwt\n        assertEquals jwt.getPayload().get('cust'), [key1: 'value1', key2: 42]\n\n        // custom type for 'cust' claim\n        def des = new JacksonDeserializer([cust: CustomBean])\n        jwt = Jwts.parser().unsecured().json(des).build().parseUnsecuredClaims(jwtString)\n        assertNotNull jwt\n        CustomBean result = jwt.getPayload().get(\"cust\", CustomBean)\n        assertEquals customBean, result\n    }\n\n    static class CustomBean {\n        private String key1\n        private Integer key2\n\n        String getKey1() {\n            return key1\n        }\n\n        void setKey1(String key1) {\n            this.key1 = key1\n        }\n\n        Integer getKey2() {\n            return key2\n        }\n\n        void setKey2(Integer key2) {\n            this.key2 = key2\n        }\n\n        boolean equals(o) {\n            if (this.is(o)) return true\n            if (getClass() != o.class) return false\n\n            CustomBean that = (CustomBean) o\n\n            if (key1 != that.key1) return false\n            if (key2 != that.key2) return false\n\n            return true\n        }\n\n        int hashCode() {\n            int result\n            result = (key1 != null ? key1.hashCode() : 0)\n            result = 31 * result + (key2 != null ? key2.hashCode() : 0)\n            return result\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/DateTestUtils.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nfinal class DateTestUtils {\n\n    /**\n     * Date util method for lopping truncate the millis from a date.\n     * @param date input date\n     * @return The date time in millis with the precision of seconds\n     */\n    static long truncateMillis(Date date) {\n        Calendar cal = Calendar.getInstance()\n        cal.setTime(date)\n        cal.set(Calendar.MILLISECOND, 0)\n        return cal.getTimeInMillis()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/JwtParserTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.impl.DefaultJwtParser\nimport io.jsonwebtoken.impl.FixedClock\nimport io.jsonwebtoken.impl.JwtTokenizer\nimport io.jsonwebtoken.impl.lang.JwtDateConverter\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.DateFormats\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.Keys\nimport io.jsonwebtoken.security.SignatureException\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.nio.charset.StandardCharsets\nimport java.security.SecureRandom\n\nimport static io.jsonwebtoken.DateTestUtils.truncateMillis\nimport static io.jsonwebtoken.impl.DefaultJwtParser.INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE\nimport static io.jsonwebtoken.impl.DefaultJwtParser.MISSING_EXPECTED_CLAIM_VALUE_MESSAGE_TEMPLATE\nimport static org.junit.Assert.*\n\n@SuppressWarnings('GrDeprecatedAPIUsage')\nclass JwtParserTest {\n\n    private static final SecureRandom random = new SecureRandom() //doesn't need to be seeded - just testing\n\n    protected static byte[] randomKey() {\n        //create random signing key for testing:\n        byte[] key = new byte[64]\n        random.nextBytes(key)\n        return key\n    }\n\n    protected static String base64Url(String s) {\n        byte[] bytes = s.getBytes(Strings.UTF_8)\n        return Encoders.BASE64URL.encode(bytes)\n    }\n\n    @Test\n    void testIsSignedWithNullArgument() {\n        assertFalse Jwts.parser().build().isSigned(null)\n    }\n\n    @Test\n    void testIsSignedWithJunkArgument() {\n        assertFalse Jwts.parser().build().isSigned('hello')\n    }\n\n    @Test\n    void testParseWithJunkArgument() {\n\n        String junkPayload = '{;aklsjd;fkajsd;fkjasd;lfkj}'\n        byte[] bytes = Strings.utf8(junkPayload)\n\n        String bad = base64Url('{\"alg\":\"none\"}') + '.' + base64Url(junkPayload) + '.'\n\n        // Can't be treated as claims, so payload must be treated as a byte array:\n        assertArrayEquals bytes, Jwts.parser().unsecured().build().parse(bad).getPayload() as byte[]\n    }\n\n    @Test\n    void testParseClaimsWithJunkArgument() {\n\n        String junkPayload = '{;aklsjd;fkajsd;fkjasd;lfkj}'\n\n        String bad = base64Url('{\"alg\":\"none\"}') + '.' + base64Url(junkPayload) + '.'\n\n        try {\n            Jwts.parser().unsecured().build().parseUnsecuredClaims(bad)\n            fail()\n        } catch (UnsupportedJwtException expected) {\n            String msg = 'Unexpected unsecured content JWT.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testParseJwsWithBadAlgHeader() {\n\n        String badAlgorithmName = 'whatever'\n\n        String header = \"{\\\"alg\\\":\\\"$badAlgorithmName\\\"}\"\n\n        String payload = '{\"subject\":\"Joe\"}'\n\n        String badSig = \";aklsjdf;kajsd;fkjas;dklfj\"\n\n        String bad = base64Url(header) + '.' + base64Url(payload) + '.' + base64Url(badSig)\n\n        try {\n            Jwts.parser().setSigningKey(randomKey()).build().parse(bad)\n            fail()\n        } catch (SignatureException se) {\n            String msg = \"Unsupported signature algorithm '$badAlgorithmName': \" +\n                    \"Unsupported JWS header 'alg' (Algorithm) value '$badAlgorithmName'.\"\n            assertEquals msg, se.getMessage()\n        }\n    }\n\n    @Test\n    void testParseWithInvalidSignature() {\n\n        String header = '{\"alg\":\"HS256\"}'\n\n        String payload = '{\"subject\":\"Joe\"}'\n\n        String badSig = \";aklsjdf;kajsd;fkjas;dklfj\"\n\n        String bad = base64Url(header) + '.' + base64Url(payload) + '.' + base64Url(badSig)\n\n        try {\n            Jwts.parser().setSigningKey(randomKey()).build().parse(bad)\n            fail()\n        } catch (SignatureException se) {\n            assertEquals se.getMessage(), 'JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.'\n        }\n\n    }\n\n    @Test\n    void testParseSignedContentWithIncorrectAlg() {\n\n        def header = '{\"alg\":\"none\"}'\n\n        def payload = '{\"subject\":\"Joe\"}'\n\n        def badSig = \";aklsjdf;kajsd;fkjas;dklfj\"\n\n        String bad = base64Url(header) + '.' + base64Url(payload) + '.' + base64Url(badSig)\n\n        try {\n            Jwts.parser().unsecured().setSigningKey(randomKey()).build().parse(bad)\n            fail()\n        } catch (MalformedJwtException se) {\n            assertEquals 'The JWS header references signature algorithm \\'none\\' yet the compact JWS string contains a signature. This is not permitted per https://tools.ietf.org/html/rfc7518#section-3.6.', se.getMessage()\n        }\n\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseUnsecuredJwsDefault() {\n        // not signed - unsecured by default.  Parsing should be disabled automatically\n        def header = '{\"alg\":\"none\"}'\n        def payload = '{\"subject\":\"Joe\"}'\n        String unsecured = base64Url(header) + '.' + base64Url(payload) + '.'\n        try {\n            Jwts.parser().build().parse(unsecured)\n            fail()\n        } catch (UnsupportedJwtException expected) {\n            String msg = DefaultJwtParser.UNSECURED_DISABLED_MSG_PREFIX + '{alg=none}'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testParseWithBase64EncodedSigningKey() {\n\n        byte[] key = randomKey()\n\n        String base64Encodedkey = Encoders.BASE64.encode(key)\n\n        String payload = 'Hello world!'\n\n        //noinspection GrDeprecatedAPIUsage\n        String compact = Jwts.builder().setPayload(payload).signWith(SignatureAlgorithm.HS256, base64Encodedkey).compact()\n\n        assertTrue Jwts.parser().build().isSigned(compact)\n\n        def jwt = Jwts.parser().setSigningKey(base64Encodedkey).build().parse(compact)\n\n        assertEquals payload, new String(jwt.payload as byte[], StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseEmptyPayload() {\n        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)\n\n        String payload = ''\n\n        String compact = Jwts.builder().setPayload(payload).signWith(key).compact()\n\n        assertTrue Jwts.parser().build().isSigned(compact)\n\n        def jwt = Jwts.parser().setSigningKey(key).build().parse(compact)\n\n        assertEquals payload, new String(jwt.payload as byte[], StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseNullPayload() {\n        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)\n        String compact = Jwts.builder().signWith(key).compact()\n        assertTrue Jwts.parser().build().isSigned(compact)\n\n        def jwt = Jwts.parser().setSigningKey(key).build().parse(compact)\n        assertEquals '', new String(jwt.payload as byte[], StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseNullPayloadWithoutKey() {\n        String compact = Jwts.builder().compact()\n        def jwt = Jwts.parser().unsecured().build().parse(compact)\n        assertEquals 'none', jwt.header.alg\n        assertEquals '', new String(jwt.payload as byte[], StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseWithExpiredJwt() {\n\n        // Test with a fixed clock to assert full exception message\n        long testTime = 1657552537573L\n        Clock fixedClock = new FixedClock(testTime)\n\n        Date exp = new Date(testTime - 1000)\n\n        String compact = Jwts.builder().setSubject('Joe').setExpiration(exp).compact()\n\n        try {\n            Jwts.parser().unsecured().setClock(fixedClock).build().parse(compact)\n            fail()\n        } catch (ExpiredJwtException e) {\n            // https://github.com/jwtk/jjwt/issues/107 (the Z designator at the end of the timestamp):\n            // https://github.com/jwtk/jjwt/issues/660 (show differences as now - expired)\n            String msg = \"JWT expired 1573 milliseconds ago at 2022-07-11T15:15:36.000Z. \" +\n                    \"Current time: 2022-07-11T15:15:37.573Z. Allowed clock skew: 0 milliseconds.\"\n            assertEquals msg, e.message\n        }\n    }\n\n    @Test\n    void testParseWithPrematureJwt() {\n\n        long differenceMillis = 100000 // arbitrary, anything > 0 is fine\n        def nbf = JwtDateConverter.INSTANCE.applyFrom(System.currentTimeMillis() / 1000L)\n        def earlier = new Date(nbf.getTime() - differenceMillis)\n\n        String compact = Jwts.builder().subject('Joe').notBefore(nbf).compact()\n\n        try {\n            Jwts.parser().unsecured().clock(new FixedClock(earlier)).build().parse(compact)\n            fail()\n        } catch (PrematureJwtException e) {\n            def nbf8601 = DateFormats.formatIso8601(nbf, true)\n            def earlier8601 = DateFormats.formatIso8601(earlier, true)\n            String msg = \"JWT early by ${differenceMillis} milliseconds before ${nbf8601}. \" +\n                    \"Current time: ${earlier8601}. Allowed clock skew: 0 milliseconds.\";\n            assertEquals msg, e.message\n\n            //https://github.com/jwtk/jjwt/issues/107 (the Z designator at the end of the timestamp):\n            assertTrue nbf8601.endsWith('Z')\n            assertTrue earlier8601.endsWith('Z')\n        }\n    }\n\n    @Test\n    void testParseWithExpiredJwtWithinAllowedClockSkew() {\n\n        long differenceMillis = 3000 // arbitrary, anything > 0 is fine\n        long millis = System.currentTimeMillis()\n        // RFC requires time in seconds, so we need to base our assertions based on second-normalized dates,\n        // otherwise we'll get nondeterministic tests:\n        long seconds = (millis / 1000L).longValue()\n        millis = seconds * 1000L\n        def exp = new Date(millis)\n        def later = new Date(exp.getTime() + differenceMillis)\n        def s = Jwts.builder().expiration(exp).compact()\n\n        String subject = 'Joe'\n        String compact = Jwts.builder().subject(subject).expiration(exp).compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().unsecured().setAllowedClockSkewSeconds(10)\n                .clock(new FixedClock(later)).build().parse(compact)\n\n        assertEquals jwt.getPayload().getSubject(), subject\n    }\n\n    @Test\n    void testParseWithExpiredJwtNotWithinAllowedClockSkew() {\n\n        long differenceMillis = 3000 // arbitrary, anything > 0 is fine\n        def exp = JwtDateConverter.INSTANCE.applyFrom(System.currentTimeMillis() / 1000L)\n        def later = new Date(exp.getTime() + differenceMillis)\n\n        def s = Jwts.builder().expiration(exp).compact()\n\n        def skewSeconds = 1\n\n        try {\n            Jwts.parser().unsecured().setAllowedClockSkewSeconds(skewSeconds)\n                    .clock(new FixedClock(later)).build().parse(s)\n            fail()\n        } catch (ExpiredJwtException e) {\n            def exp8601 = DateFormats.formatIso8601(exp, true)\n            def later8601 = DateFormats.formatIso8601(later, true)\n            String msg = \"JWT expired ${differenceMillis} milliseconds ago at ${exp8601}. \" +\n                    \"Current time: ${later8601}. Allowed clock skew: ${skewSeconds * 1000} milliseconds.\";\n            assertEquals msg, e.message\n        }\n    }\n\n    @Test\n    void testParseWithPrematureJwtWithinAllowedClockSkew() {\n        Date exp = new Date(System.currentTimeMillis() + 3000)\n\n        String subject = 'Joe'\n        String compact = Jwts.builder().setSubject(subject).setNotBefore(exp).compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().unsecured().setAllowedClockSkewSeconds(10).build().parse(compact)\n\n        assertEquals jwt.getPayload().getSubject(), subject\n    }\n\n    @Test\n    void testParseWithPrematureJwtNotWithinAllowedClockSkew() {\n\n        long differenceMillis = 3000 // arbitrary, anything > 0 is fine\n        def nbf = JwtDateConverter.INSTANCE.applyFrom(System.currentTimeMillis() / 1000L)\n        def earlier = new Date(nbf.getTime() - differenceMillis)\n\n        String compact = Jwts.builder().subject('Joe').notBefore(nbf).compact()\n\n        def skewSeconds = 1\n\n        try {\n            Jwts.parser().unsecured()\n                    .setAllowedClockSkewSeconds(skewSeconds).clock(new FixedClock(earlier))\n                    .build().parse(compact)\n            fail()\n        } catch (PrematureJwtException e) {\n            def nbf8601 = DateFormats.formatIso8601(nbf, true)\n            def earlier8601 = DateFormats.formatIso8601(earlier, true)\n            String msg = \"JWT early by ${differenceMillis} milliseconds before ${nbf8601}. \" +\n                    \"Current time: ${earlier8601}. Allowed clock skew: ${skewSeconds * 1000} milliseconds.\";\n            assertEquals msg, e.message\n        }\n    }\n\n    // ========================================================================\n    // parseUnsecuredContent tests\n    // ========================================================================\n\n    @Test\n    void testParseUnsecuredContent() {\n\n        String payload = 'Hello world!'\n\n        String compact = Jwts.builder().setPayload(payload).compact()\n\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n\n        assertEquals payload, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseUnprotectedContentWithClaimsJwt() {\n\n        String compact = Jwts.builder().setSubject('Joe').compact()\n\n        try {\n            Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured Claims JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseUnprotectedContentWithContentJws() {\n\n        String payload = 'Hello world!'\n\n        String compact = Jwts.builder().setPayload(payload).signWith(SignatureAlgorithm.HS256, randomKey()).compact()\n\n        try {\n            Jwts.parser().build().parseUnsecuredContent(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Cannot verify JWS signature: unable to locate signature verification key for JWS with header: {alg=HS256}', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseUnsecuredContentWithClaimsJws() {\n\n        def key = randomKey()\n        String compact = Jwts.builder().setSubject('Joe').signWith(SignatureAlgorithm.HS256, key).compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).build().parseSignedContent(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected Claims JWS.', e.getMessage()\n        }\n    }\n\n    // ========================================================================\n    // parseUnsecuredClaims tests\n    // ========================================================================\n\n    @Test\n    void testParseUnsecuredClaims() {\n\n        String subject = 'Joe'\n\n        String compact = Jwts.builder().setSubject(subject).compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().unsecured().build().parseUnsecuredClaims(compact)\n\n        assertEquals jwt.getPayload().getSubject(), subject\n    }\n\n    @Test\n    void testParseUnsecuredClaimsWithContentJwt() {\n\n        String payload = 'Hello world!'\n\n        String compact = Jwts.builder().setPayload(payload).compact()\n\n        try {\n            Jwts.parser().unsecured().build().parseUnsecuredClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured content JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseUnsecuredClaimsWithContentJws() {\n\n        String payload = 'Hello world!'\n\n        String compact = Jwts.builder().setPayload(payload).signWith(SignatureAlgorithm.HS256, randomKey()).compact()\n\n        try {\n            Jwts.parser().build().parseUnsecuredClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Cannot verify JWS signature: unable to locate signature verification key for JWS with header: {alg=HS256}', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseUnsecuredClaimsWithClaimsJws() {\n\n        def key = randomKey()\n        String compact = Jwts.builder().setSubject('Joe').signWith(SignatureAlgorithm.HS256, key).compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).build().parseUnsecuredClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected Claims JWS.', e.getMessage()\n        }\n    }\n\n    // ========================================================================\n    // parseSignedContent tests\n    // ========================================================================\n\n    @Test\n    void testParseSignedContent() {\n\n        String payload = 'Hello world!'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setPayload(payload).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        def jwt = Jwts.parser().\n                setSigningKey(key).\n                build().\n                parseSignedContent(compact)\n\n        assertEquals payload, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseSignedContentWithContentJwt() {\n\n        String payload = 'Hello world!'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setPayload(payload).compact()\n\n        try {\n            Jwts.parser().unsecured().setSigningKey(key).build().parseSignedContent(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured content JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseSignedContentWithClaimsJwt() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).compact()\n\n        try {\n            Jwts.parser().unsecured().setSigningKey(key).build().parseSignedContent(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured Claims JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseSignedContentWithClaimsJws() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).build().parseSignedContent(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected Claims JWS.', e.getMessage()\n        }\n    }\n\n    // ========================================================================\n    // parseSignedClaims tests\n    // ========================================================================\n\n    @Test\n    void testParseSignedClaims() {\n\n        String sub = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(sub).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).build().parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().getSubject(), sub\n    }\n\n    @Test\n    void testParseSignedClaimsWithExpiredJws() {\n\n        long differenceMillis = 843 // arbitrary, anything > 0 is fine\n        def exp = JwtDateConverter.INSTANCE.applyFrom(System.currentTimeMillis() / 1000L)\n        def later = new Date(exp.getTime() + differenceMillis)\n\n        String sub = 'Joe'\n        byte[] key = randomKey()\n        String compact = Jwts.builder().subject(sub).expiration(exp).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).clock(new FixedClock(later)).build().parseUnsecuredClaims(compact)\n            fail()\n        } catch (ExpiredJwtException e) {\n            def exp8601 = DateFormats.formatIso8601(exp, true)\n            def later8601 = DateFormats.formatIso8601(later, true)\n            String msg = \"JWT expired ${differenceMillis} milliseconds ago at ${exp8601}. \" +\n                    \"Current time: ${later8601}. Allowed clock skew: 0 milliseconds.\";\n            assertEquals msg, e.message\n            assertEquals e.getClaims().getSubject(), sub\n            assertEquals e.getHeader().getAlgorithm(), \"HS256\"\n        }\n    }\n\n    @Test\n    void testParseSignedClaimsWithPrematureJws() {\n\n        long differenceMillis = 3842 // arbitrary, anything > 0 is fine\n        def nbf = JwtDateConverter.INSTANCE.applyFrom(System.currentTimeMillis() / 1000L)\n        def earlier = new Date(nbf.getTime() - differenceMillis)\n\n        String sub = 'Joe'\n        byte[] key = randomKey()\n        String compact = Jwts.builder().subject(sub).notBefore(nbf).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).clock(new FixedClock(earlier)).build().parseSignedClaims(compact)\n            fail()\n        } catch (PrematureJwtException e) {\n            def nbf8601 = DateFormats.formatIso8601(nbf, true)\n            def earlier8601 = DateFormats.formatIso8601(earlier, true)\n            String msg = \"JWT early by ${differenceMillis} milliseconds before ${nbf8601}. \" +\n                    \"Current time: ${earlier8601}. Allowed clock skew: 0 milliseconds.\";\n            assertEquals msg, e.message\n\n            assertEquals e.getClaims().getSubject(), sub\n            assertEquals e.getHeader().getAlgorithm(), \"HS256\"\n        }\n    }\n\n    @Test\n    void testParseSignedClaimsWithContentJwt() {\n\n        String payload = 'Hello world!'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setPayload(payload).compact()\n\n        try {\n            Jwts.parser().unsecured().setSigningKey(key).build().parseSignedClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured content JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseSignedClaimsWithClaimsJwt() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).compact()\n\n        try {\n            Jwts.parser().unsecured().setSigningKey(key).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected unsecured Claims JWT.', e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseSignedClaimsWithContentJws() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).build().parseSignedContent(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            assertEquals 'Unexpected Claims JWS.', e.getMessage()\n        }\n    }\n\n    // ========================================================================\n    // parseSignedClaims with signingKey resolver.\n    // ========================================================================\n\n    @Test\n    void testParseClaimsWithSigningKeyResolver() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        def signingKeyResolver = new SigningKeyResolverAdapter() {\n            @Override\n            byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n                return key\n            }\n        }\n\n        Jws jws = Jwts.parser().setSigningKeyResolver(signingKeyResolver).build().parseSignedClaims(compact)\n\n        assertEquals jws.getPayload().getSubject(), subject\n    }\n\n    @Test\n    void testParseClaimsWithSigningKeyResolverInvalidKey() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        def signingKeyResolver = new SigningKeyResolverAdapter() {\n            @Override\n            byte[] resolveSigningKeyBytes(JwsHeader header, Claims claims) {\n                return randomKey()\n            }\n        }\n\n        try {\n            Jwts.parser().setSigningKeyResolver(signingKeyResolver).build().parseSignedClaims(compact)\n            fail()\n        } catch (SignatureException se) {\n            assertEquals 'JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.', se.getMessage()\n        }\n    }\n\n    @Test\n    void testParseClaimsWithNullSigningKeyResolver() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        try {\n            Jwts.parser().setSigningKeyResolver(null).build().parseSignedClaims(compact)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals 'SigningKeyResolver cannot be null.', iae.getMessage()\n        }\n    }\n\n    @Test\n    void testParseClaimsWithInvalidSigningKeyResolverAdapter() {\n\n        String subject = 'Joe'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setSubject(subject).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        def signingKeyResolver = new SigningKeyResolverAdapter()\n\n        try {\n            Jwts.parser().setSigningKeyResolver(signingKeyResolver).build().parseSignedClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException ex) {\n            assertEquals 'The specified SigningKeyResolver implementation does not support ' +\n                    'Claims JWS signing key resolution.  Consider overriding either the resolveSigningKey(JwsHeader, Claims) method ' +\n                    'or, for HMAC algorithms, the resolveSigningKeyBytes(JwsHeader, Claims) method.', ex.getMessage()\n        }\n    }\n\n    @Test\n    void testParseSignedClaimsWithNumericTypes() {\n        byte[] key = randomKey()\n\n        def b = (byte) 42\n        def s = (short) 42\n        def i = 42\n\n        def smallLong = (long) 42\n        def bigLong = ((long) Integer.MAX_VALUE) + 42\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                claim(\"byte\", b).\n                claim(\"short\", s).\n                claim(\"int\", i).\n                claim(\"long_small\", smallLong).\n                claim(\"long_big\", bigLong).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).build().parseSignedClaims(compact)\n\n        Claims claims = jwt.getPayload()\n\n        assertEquals(b, claims.get(\"byte\", Byte.class))\n        assertEquals(s, claims.get(\"short\", Short.class))\n        assertEquals(i, claims.get(\"int\", Integer.class))\n        assertEquals(smallLong, claims.get(\"long_small\", Long.class))\n        assertEquals(bigLong, claims.get(\"long_big\", Long.class))\n    }\n\n    // ========================================================================\n    // parseSignedContent with signingKey resolver.\n    // ========================================================================\n\n    @Test\n    void testParseSignedContentWithSigningKeyResolverAdapter() {\n\n        String inputPayload = 'Hello world!'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setPayload(inputPayload).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        def signingKeyResolver = new SigningKeyResolverAdapter() {\n            @Override\n            byte[] resolveSigningKeyBytes(JwsHeader header, byte[] payload) {\n                return key\n            }\n        }\n\n        def jws = Jwts.parser().setSigningKeyResolver(signingKeyResolver).build().parseSignedContent(compact)\n\n        assertEquals inputPayload, new String(jws.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseSignedContentWithSigningKeyResolverInvalidKey() {\n\n        String inputPayload = 'Hello world!'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setPayload(inputPayload).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        def signingKeyResolver = new SigningKeyResolverAdapter() {\n            @Override\n            byte[] resolveSigningKeyBytes(JwsHeader header, byte[] payload) {\n                return randomKey()\n            }\n        }\n\n        try {\n            Jwts.parser().setSigningKeyResolver(signingKeyResolver).build().parseSignedContent(compact)\n            fail()\n        } catch (SignatureException se) {\n            assertEquals 'JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.', se.getMessage()\n        }\n    }\n\n    @Test\n    void testParseSignedContentWithInvalidSigningKeyResolverAdapter() {\n\n        String payload = 'Hello world!'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().setPayload(payload).signWith(SignatureAlgorithm.HS256, key).compact()\n\n        def signingKeyResolver = new SigningKeyResolverAdapter()\n\n        try {\n            Jwts.parser().setSigningKeyResolver(signingKeyResolver).build().parseSignedContent(compact)\n            fail()\n        } catch (UnsupportedJwtException ex) {\n            assertEquals ex.getMessage(), 'The specified SigningKeyResolver implementation does not support content ' +\n                    'JWS signing key resolution.  Consider overriding either the resolveSigningKey(JwsHeader, byte[]) ' +\n                    'method or, for HMAC algorithms, the resolveSigningKeyBytes(JwsHeader, byte[]) method.'\n        }\n    }\n\n    @Test\n    void testParseRequireDontAllowNullClaimName() {\n        def expectedClaimValue = 'A Most Awesome Claim Value'\n\n        byte[] key = randomKey()\n\n        // not setting expected claim name in JWT\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).setIssuer('Dummy').compact()\n\n        try {\n            // expecting null claim name, but with value\n            Jwts.parser().setSigningKey(key).require(null, expectedClaimValue).build().parseSignedClaims(compact)\n            fail()\n        } catch (IllegalArgumentException e) {\n            assertEquals(\n                    \"claim name cannot be null or empty.\",\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireDontAllowEmptyClaimName() {\n        def expectedClaimValue = 'A Most Awesome Claim Value'\n\n        byte[] key = randomKey()\n\n        // not setting expected claim name in JWT\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setIssuer('Dummy').\n                compact()\n\n        try {\n            // expecting null claim name, but with value\n            Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                    require(\"\", expectedClaimValue).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IllegalArgumentException e) {\n            assertEquals(\n                    \"claim name cannot be null or empty.\",\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireDontAllowNullClaimValue() {\n        def expectedClaimName = 'A Most Awesome Claim Name'\n\n        byte[] key = randomKey()\n\n        // not setting expected claim name in JWT\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).setIssuer('Dummy').compact()\n\n        try {\n            // expecting claim name, but with null value\n            Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                    require(expectedClaimName, null).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IllegalArgumentException e) {\n            assertEquals(\n                    \"The value cannot be null for claim name: \" + expectedClaimName,\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireGeneric_Success() {\n        def expectedClaimName = 'A Most Awesome Claim Name'\n        def expectedClaimValue = 'A Most Awesome Claim Value'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                claim(expectedClaimName, expectedClaimValue).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                require(expectedClaimName, expectedClaimValue).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().get(expectedClaimName), expectedClaimValue\n    }\n\n    @Test\n    void testParseRequireGeneric_Incorrect_Fail() {\n        def goodClaimName = 'A Most Awesome Claim Name'\n        def goodClaimValue = 'A Most Awesome Claim Value'\n\n        def badClaimValue = 'A Most Bogus Claim Value'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                claim(goodClaimName, badClaimValue).\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    require(goodClaimName, goodClaimValue).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IncorrectClaimException e) {\n            assertEquals(\n                    String.format(INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE, goodClaimName, goodClaimValue, badClaimValue),\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireedGeneric_Missing_Fail() {\n        def claimName = 'A Most Awesome Claim Name'\n        def claimValue = 'A Most Awesome Claim Value'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setIssuer('Dummy').\n                compact()\n\n        try {\n            Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                    require(claimName, claimValue).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (MissingClaimException e) {\n            String msg = \"Missing '$claimName' claim. Expected value: $claimValue\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseRequireIssuedAt_Success() {\n\n        def issuedAt = new Date(System.currentTimeMillis())\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setIssuedAt(issuedAt).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                requireIssuedAt(issuedAt).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().getIssuedAt().getTime(), truncateMillis(issuedAt), 0\n    }\n\n    @Test(expected = IncorrectClaimException)\n    void testParseRequireIssuedAt_Incorrect_Fail() {\n        def goodIssuedAt = new Date(System.currentTimeMillis())\n        def badIssuedAt = new Date(System.currentTimeMillis() - 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setIssuedAt(badIssuedAt).\n                compact()\n\n        Jwts.parser().setSigningKey(key).\n                requireIssuedAt(goodIssuedAt).\n                build().\n                parseSignedClaims(compact)\n    }\n\n    @Test(expected = MissingClaimException)\n    void testParseRequireIssuedAt_Missing_Fail() {\n        def issuedAt = new Date(System.currentTimeMillis() - 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setSubject(\"Dummy\").\n                compact()\n\n        Jwts.parser().setSigningKey(key).\n                requireIssuedAt(issuedAt).\n                build().\n                parseSignedClaims(compact)\n    }\n\n    @Test\n    void testParseRequireIssuer_Success() {\n        def issuer = 'A Most Awesome Issuer'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setIssuer(issuer).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                requireIssuer(issuer).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().getIssuer(), issuer\n    }\n\n    @Test\n    void testParseRequireIssuer_Incorrect_Fail() {\n        def goodIssuer = 'A Most Awesome Issuer'\n        def badIssuer = 'A Most Bogus Issuer'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setIssuer(badIssuer).\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireIssuer(goodIssuer).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IncorrectClaimException e) {\n            assertEquals(\n                    String.format(INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE, Claims.ISSUER, goodIssuer, badIssuer),\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireIssuer_Missing_Fail() {\n        def issuer = 'A Most Awesome Issuer'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setId('id').\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireIssuer(issuer).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (MissingClaimException e) {\n            String msg = \"Missing 'iss' claim. Expected value: $issuer\"\n            assertEquals msg, e.message\n        }\n    }\n\n    @Test\n    void testParseRequireAudience_Success() {\n        def audience = 'A Most Awesome Audience'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setAudience(audience).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                requireAudience(audience).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals audience, jwt.getPayload().getAudience().iterator().next()\n    }\n\n    @Test\n    void testParseExpectedEqualAudiences() {\n        def one = 'one'\n        def two = 'two'\n        def expected = [one, two]\n        String jwt = Jwts.builder().audience().add(one).add(two).and().compact()\n        def aud = Jwts.parser().unsecured().requireAudience(one).requireAudience(two).build()\n                .parseUnsecuredClaims(jwt).getPayload().getAudience()\n        assertEquals expected.size(), aud.size()\n        assertTrue aud.containsAll(expected)\n    }\n\n    @Test\n    void testParseAtLeastOneAudiences() {\n        def one = 'one'\n\n        String jwt = Jwts.builder().audience().add(one).add('two').and().compact() // more audiences than required\n\n        def aud = Jwts.parser().unsecured().requireAudience(one) // require only one\n                .build().parseUnsecuredClaims(jwt).getPayload().getAudience()\n\n        assertNotNull aud\n        assertTrue aud.contains(one)\n    }\n\n    @Test\n    void testParseMissingAudiences() {\n        def one = 'one'\n        def two = 'two'\n        String jwt = Jwts.builder().id('foo').compact()\n        try {\n            Jwts.parser().unsecured().requireAudience(one).requireAudience(two).build().parseUnsecuredClaims(jwt)\n            fail()\n        } catch (MissingClaimException expected) {\n            String msg = \"Missing 'aud' claim. Expected values: [$one, $two]\"\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testParseSingleValueClaimExpectingMultipleValues() {\n        def one = 'one'\n        def two = 'two'\n        def expected = [one, two]\n        String jwt = Jwts.builder().claim('custom', one).compact()\n        try {\n            Jwts.parser().unsecured().require('custom', expected).build().parseUnsecuredClaims(jwt)\n        } catch (IncorrectClaimException e) {\n            String msg = \"Missing expected '$two' value in 'custom' claim [$one].\"\n            assertEquals msg, e.message\n        }\n    }\n\n    @Test\n    void testParseRequireAudience_Incorrect_Fail() {\n        def goodAudience = 'A Most Awesome Audience'\n        def badAudience = 'A Most Bogus Audience'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setAudience(badAudience).\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireAudience(goodAudience).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IncorrectClaimException e) {\n            String msg = String.format(MISSING_EXPECTED_CLAIM_VALUE_MESSAGE_TEMPLATE, goodAudience,\n                    Claims.AUDIENCE, [badAudience])\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseRequireAudience_Missing_Fail() {\n        def audience = 'A Most Awesome audience'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setId('id').\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireAudience(audience).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (MissingClaimException e) {\n            String msg = \"Missing 'aud' claim. Expected values: [$audience]\"\n            assertEquals msg, e.message\n        }\n    }\n\n    @Test\n    void testParseRequireSubject_Success() {\n        def subject = 'A Most Awesome Subject'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setSubject(subject).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                requireSubject(subject).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().getSubject(), subject\n    }\n\n    @Test\n    void testParseRequireSubject_Incorrect_Fail() {\n        def goodSubject = 'A Most Awesome Subject'\n        def badSubject = 'A Most Bogus Subject'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setSubject(badSubject).\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireSubject(goodSubject).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IncorrectClaimException e) {\n            assertEquals(\n                    String.format(INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE, Claims.SUBJECT, goodSubject, badSubject),\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireSubject_Missing_Fail() {\n        def subject = 'A Most Awesome Subject'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setId('id').\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireSubject(subject).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (MissingClaimException e) {\n            String msg = \"Missing 'sub' claim. Expected value: $subject\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseRequireId_Success() {\n        def id = 'A Most Awesome id'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setId(id).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                requireId(id).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().getId(), id\n    }\n\n    @Test\n    void testParseRequireId_Incorrect_Fail() {\n        def goodId = 'A Most Awesome Id'\n        def badId = 'A Most Bogus Id'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setId(badId).\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireId(goodId).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IncorrectClaimException e) {\n            assertEquals(\n                    String.format(INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE, Claims.ID, goodId, badId),\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireId_Missing_Fail() {\n        def id = 'A Most Awesome Id'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setIssuer('me').\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    requireId(id).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (MissingClaimException e) {\n            String msg = \"Missing 'jti' claim. Expected value: $id\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseRequireExpiration_Success() {\n        // expire in the future\n        def expiration = new Date(System.currentTimeMillis() + 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setExpiration(expiration).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                requireExpiration(expiration).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().getExpiration().getTime(), truncateMillis(expiration)\n    }\n\n    @Test(expected = IncorrectClaimException)\n    void testParseRequireExpirationAt_Incorrect_Fail() {\n        def goodExpiration = new Date(System.currentTimeMillis() + 20000)\n        def badExpiration = new Date(System.currentTimeMillis() + 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setExpiration(badExpiration).\n                compact()\n\n        Jwts.parser().setSigningKey(key).\n                requireExpiration(goodExpiration).\n                build().\n                parseSignedClaims(compact)\n    }\n\n    @Test(expected = MissingClaimException)\n    void testParseRequireExpiration_Missing_Fail() {\n        def expiration = new Date(System.currentTimeMillis() + 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setSubject(\"Dummy\").\n                compact()\n\n        Jwts.parser().setSigningKey(key).\n                requireExpiration(expiration).\n                build().\n                parseSignedClaims(compact)\n    }\n\n    @Test\n    void testParseRequireNotBefore_Success() {\n        // expire in the future\n        def notBefore = new Date(System.currentTimeMillis() - 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setNotBefore(notBefore).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                requireNotBefore(notBefore).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().getNotBefore().getTime(), truncateMillis(notBefore)\n    }\n\n    @Test(expected = IncorrectClaimException)\n    void testParseRequireNotBefore_Incorrect_Fail() {\n        def goodNotBefore = new Date(System.currentTimeMillis() - 20000)\n        def badNotBefore = new Date(System.currentTimeMillis() - 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setNotBefore(badNotBefore).\n                compact()\n\n        Jwts.parser().setSigningKey(key).\n                requireNotBefore(goodNotBefore).\n                build().\n                parseSignedClaims(compact)\n    }\n\n    @Test(expected = MissingClaimException)\n    void testParseRequireNotBefore_Missing_Fail() {\n        def notBefore = new Date(System.currentTimeMillis() - 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setSubject(\"Dummy\").\n                compact()\n\n        Jwts.parser().setSigningKey(key).\n                requireNotBefore(notBefore).\n                build().\n                parseSignedClaims(compact)\n    }\n\n    @Test\n    void testParseRequireCustomDate_Success() {\n\n        def aDate = new Date(System.currentTimeMillis())\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                claim(\"aDate\", aDate).\n                compact()\n\n        Jwt<Header, Claims> jwt = Jwts.parser().setSigningKey(key).\n                require(\"aDate\", aDate).\n                build().\n                parseSignedClaims(compact)\n\n        assertEquals jwt.getPayload().get(\"aDate\", Date.class), aDate\n    }\n\n    @Test\n    //since 0.10.0\n    void testParseRequireCustomDateWhenClaimIsNotADate() {\n\n        def goodDate = new Date(System.currentTimeMillis())\n        def badDate = 'hello'\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                claim(\"aDate\", badDate).\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    require(\"aDate\", goodDate).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IncorrectClaimException e) {\n            String expected = 'JWT Claim \\'aDate\\' was expected to be a Date, but its value cannot be converted to a ' +\n                    'Date using current heuristics.  Value: hello'\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseRequireCustomDate_Incorrect_Fail() {\n\n        def goodDate = new Date(System.currentTimeMillis())\n        def badDate = new Date(System.currentTimeMillis() - 10000)\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                claim(\"aDate\", badDate).\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    require(\"aDate\", goodDate).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (IncorrectClaimException e) {\n            assertEquals(\n                    String.format(INCORRECT_EXPECTED_CLAIM_MESSAGE_TEMPLATE, \"aDate\", goodDate, badDate),\n                    e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testParseRequireCustomDate_Missing_Fail() {\n        def aDate = new Date(System.currentTimeMillis())\n\n        byte[] key = randomKey()\n\n        String compact = Jwts.builder().signWith(SignatureAlgorithm.HS256, key).\n                setSubject(\"Dummy\").\n                compact()\n\n        try {\n            Jwts.parser().setSigningKey(key).\n                    require(\"aDate\", aDate).\n                    build().\n                    parseSignedClaims(compact)\n            fail()\n        } catch (MissingClaimException e) {\n            String msg = \"Missing 'aDate' claim. Expected value: $aDate\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testParseClockManipulationWithFixedClock() {\n        def then = System.currentTimeMillis() - 1000\n        Date expiry = new Date(then)\n        Date beforeExpiry = new Date(then - 1000)\n\n        String compact = Jwts.builder().setSubject('Joe').setExpiration(expiry).compact()\n\n        Jwts.parser().unsecured().setClock(new FixedClock(beforeExpiry)).build().parse(compact)\n    }\n\n    @Test\n    void testParseClockManipulationWithNullClock() {\n        JwtParserBuilder parser = Jwts.parser();\n        try {\n            parser.setClock(null)\n            fail()\n        } catch (IllegalArgumentException expected) {\n        }\n    }\n\n    @Test\n    void testParseMalformedJwt() {\n\n        String header = '{\"alg\":\"none\"}'\n\n        String payload = '{\"subject\":\"Joe\"}'\n\n        String badSig = \";aklsjdf;kajsd;fkjas;dklfj\"\n\n        String bogus = 'bogus'\n\n        String bad = base64Url(header) + '.' + base64Url(payload) + '.' + base64Url(badSig) + '.' + base64Url(bogus)\n\n        try {\n            Jwts.parser().setSigningKey(randomKey()).build().parse(bad)\n            fail()\n        } catch (MalformedJwtException se) {\n            String expected = JwtTokenizer.DELIM_ERR_MSG_PREFIX + '3'\n            assertEquals expected, se.message\n        }\n    }\n\n    @Test\n    void testNoHeaderNoSig() {\n\n        String payload = '{\"subject\":\"Joe\"}'\n\n        String jwtStr = '.' + base64Url(payload) + '.'\n\n        try {\n            Jwts.parser().build().parse(jwtStr)\n            fail()\n        } catch (MalformedJwtException e) {\n            assertEquals 'Compact JWT strings MUST always have a Base64Url protected header per https://tools.ietf.org/html/rfc7519#section-7.2 (steps 2-4).', e.getMessage()\n        }\n    }\n\n    @Test\n    void testNoHeaderSig() {\n\n        String payload = '{\"subject\":\"Joe\"}'\n\n        String sig = \";aklsjdf;kajsd;fkjas;dklfj\"\n\n        String jwtStr = '.' + base64Url(payload) + '.' + base64Url(sig)\n\n        try {\n            Jwts.parser().build().parse(jwtStr)\n            fail()\n        } catch (MalformedJwtException se) {\n            assertEquals 'Compact JWT strings MUST always have a Base64Url protected header per https://tools.ietf.org/html/rfc7519#section-7.2 (steps 2-4).', se.message\n        }\n    }\n\n    @Test\n    void testBadHeaderSig() {\n\n        String header = '{\"alg\":\"none\"}'\n\n        String payload = '{\"subject\":\"Joe\"}'\n\n        String sig = \";aklsjdf;kajsd;fkjas;dklfj\"\n\n        String jwtStr = base64Url(header) + '.' + base64Url(payload) + '.' + base64Url(sig)\n\n        try {\n            Jwts.parser().unsecured().build().parse(jwtStr)\n            fail()\n        } catch (MalformedJwtException se) {\n            assertEquals 'The JWS header references signature algorithm \\'none\\' yet the compact JWS string contains a signature. This is not permitted per https://tools.ietf.org/html/rfc7518#section-3.6.', se.message\n        }\n    }\n\n    /**\n     * Ensures that compression algorithms can be removed completely, thereby disabling compression entirely\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/996\">Issue 996</a>\n     * @since 0.12.7\n     */\n    @Test\n    void testEmptyZipAlgCollection() {\n\n        // create a compressed JWE first:\n        def key = TestKeys.A256GCM\n        def jwe = Jwts.builder().claim(\"hello\", \"world\")\n                .compressWith(Jwts.ZIP.DEF)\n                .encryptWith(key, Jwts.ENC.A256GCM)\n                .compact()\n\n        //now build a parser with no decompression algs (which should disable decompression)\n        def parser = Jwts.parser().zip().clear().and().decryptWith(key).build()\n\n        //parsing should fail since (de)compression is disabled:\n        try {\n            parser.parseEncryptedClaims(jwe)\n        } catch (UnsupportedJwtException e) {\n            String expected = \"Unsupported JWE header 'zip' (Compression Algorithm) value 'DEF': \" +\n                    \"decompression is disabled (no compression algorithms have been configured).\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * Ensures that mac/signature algorithms can be removed completely, thereby disabling JWSs entirely\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/996\">Issue 996</a>\n     * @since 0.12.7\n     */\n    @Test\n    void testEmptySigAlgCollection() {\n\n        // create a compressed JWE first:\n        def key = TestKeys.HS256\n        def jws = Jwts.builder().claim(\"hello\", \"world\")\n                .signWith(key, Jwts.SIG.HS256)\n                .compact()\n\n        //now build a parser with no signature algs, which should completely disable signature verification\n        def parser = Jwts.parser().sig().clear().and().verifyWith(key).build()\n\n        //parsing should fail since signature verification is disabled:\n        try {\n            parser.parseSignedClaims(jws)\n        } catch (SignatureException e) {\n            String expected = \"Unsupported signature algorithm 'HS256': Unsupported JWS header 'alg' (Algorithm) \" +\n                    \"value 'HS256': signature verification is disabled (no mac or signature algorithms have been \" +\n                    \"configured).\"\n            assertTrue e.getCause() instanceof UnsupportedJwtException\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * Ensures that encryption algorithms can be removed completely, thereby disabling JWEs entirely\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/996\">Issue 996</a>\n     * @since 0.12.7\n     */\n    @Test\n    void testEmptyEncAlgCollection() {\n\n        // create a compressed JWE first:\n        def key = TestKeys.A256GCM\n        def jwe = Jwts.builder().claim(\"hello\", \"world\")\n                .encryptWith(key, Jwts.ENC.A256GCM)\n                .compact()\n\n        //now build a parser with no encryption algs, which should completely disable decryption\n        def parser = Jwts.parser().enc().clear().and().decryptWith(key).build()\n\n        //parsing should fail since decryption is disabled:\n        try {\n            parser.parseEncryptedClaims(jwe)\n        } catch (UnsupportedJwtException e) {\n            String expected = \"Unsupported JWE header 'enc' (Encryption Algorithm) value 'A256GCM': \" +\n                    \"decryption is disabled (no encryption algorithms have been configured).\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * Ensures that key management algorithms can be removed completely, thereby disabling JWEs entirely\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/996\">Issue 996</a>\n     * @since 0.12.7\n     */\n    @Test\n    void testEmptyKeyAlgCollection() {\n\n        // create a compressed JWE first:\n        def key = TestKeys.A256GCM\n        def jwe = Jwts.builder().claim(\"hello\", \"world\")\n                .encryptWith(key, Jwts.ENC.A256GCM)\n                .compact()\n\n        //now build a parser with no key management algs, which should completely disable decryption\n        def parser = Jwts.parser().key().clear().and().decryptWith(key).build()\n\n        //parsing should fail since key management is disabled:\n        try {\n            parser.parseEncryptedClaims(jwe)\n        } catch (UnsupportedJwtException e) {\n            String expected = \"Unsupported JWE header 'alg' (Algorithm) value 'dir': decryption is disabled \" +\n                    \"(no key management algorithms have been configured).\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/JwtsTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.SignatureAlgorithm\nimport io.jsonwebtoken.impl.*\nimport io.jsonwebtoken.impl.compression.GzipCompressionAlgorithm\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.impl.security.*\nimport io.jsonwebtoken.io.*\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.Mac\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.Charset\nimport java.nio.charset.StandardCharsets\nimport java.security.Key\nimport java.security.KeyPair\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\nclass JwtsTest {\n\n    private static Date dateWithOnlySecondPrecision(long millis) {\n        long seconds = (millis / 1000) as long\n        long secondOnlyPrecisionMillis = seconds * 1000\n        return new Date(secondOnlyPrecisionMillis)\n    }\n\n    private static Date now() {\n        Date date = dateWithOnlySecondPrecision(System.currentTimeMillis())\n        return date\n    }\n\n    private static int later() {\n        def date = laterDate(10000)\n        def seconds = date.getTime() / 1000\n        return seconds as int\n    }\n\n    private static Date laterDate(int seconds) {\n        def millis = seconds * 1000L\n        def time = System.currentTimeMillis() + millis\n        return dateWithOnlySecondPrecision(time)\n    }\n\n    protected static String base64Url(String s) {\n        byte[] bytes = s.getBytes(Strings.UTF_8)\n        return Encoders.BASE64URL.encode(bytes)\n    }\n\n    static def toJson(def o) {\n        def serializer = Services.get(Serializer)\n        def out = new ByteArrayOutputStream()\n        serializer.serialize(o, out)\n        return Strings.utf8(out.toByteArray())\n    }\n\n    @Test\n    void testPrivateCtor() { // for code coverage only\n        //noinspection GroovyAccessibility\n        new Jwts()\n    }\n\n    @Test\n    void testHeaderWithNoArgs() {\n        def header = Jwts.header().build()\n        assertTrue header instanceof DefaultHeader\n    }\n\n    @Test\n    void testHeaderWithMapArg() {\n        def header = Jwts.header().add([alg: \"HS256\"]).build()\n        assertTrue header instanceof DefaultJwsHeader\n        assertEquals 'HS256', header.getAlgorithm()\n        assertEquals 'HS256', header.alg\n    }\n\n    @Test\n    void testClaims() {\n        Claims claims = Jwts.claims().build()\n        assertNotNull claims\n    }\n\n    @Test\n    void testClaimsWithMapArg() {\n        Claims claims = Jwts.claims([sub: 'Joe'])\n        assertNotNull claims\n        assertEquals 'Joe', claims.getSubject()\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseMalformedHeader() {\n        def headerString = '{\"jku\":42}' // cannot be parsed as a URI --> malformed header\n        def claimsString = '{\"sub\":\"joe\"}'\n        def encodedHeader = base64Url(headerString)\n        def encodedClaims = base64Url(claimsString)\n        def compact = encodedHeader + '.' + encodedClaims + '.AAD='\n        try {\n            Jwts.parser().build().parseSignedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            String expected = 'Invalid protected header: Invalid JWS header \\'jku\\' (JWK Set URL) value: 42. ' +\n                    'Values must be either String or java.net.URI instances. Value type found: java.lang.Integer.'\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseMalformedClaims() {\n        def key = TestKeys.HS256\n        def h = base64Url('{\"alg\":\"HS256\"}')\n        def c = base64Url('{\"sub\":\"joe\",\"exp\":\"-42-\"}')\n        def data = Strings.utf8((\"$h.$c\" as String))\n        def payload = Streams.of(data)\n        def request = new DefaultSecureRequest<>(payload, null, null, key)\n        def result = Jwts.SIG.HS256.digest(request)\n        def sig = Encoders.BASE64URL.encode(result)\n        def compact = \"$h.$c.$sig\" as String\n        try {\n            Jwts.parser().setSigningKey(key).build().parseSignedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            String expected = 'Invalid claims: Invalid JWT Claims \\'exp\\' (Expiration Time) value: -42-. ' +\n                    'String value is not a JWT NumericDate, nor is it ISO-8601-formatted. All heuristics exhausted. ' +\n                    'Cause: Unparseable date: \"-42-\"'\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    @Test\n    void testContentJwtString() {\n        // Assert exact output per example at https://www.rfc-editor.org/rfc/rfc7519.html#section-6.1\n        String encodedBody = 'eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ'\n        String payload = new String(Decoders.BASE64URL.decode(encodedBody), StandardCharsets.UTF_8)\n        String val = Jwts.builder().setPayload(payload).compact()\n        String RFC_VALUE = 'eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.'\n        assertEquals RFC_VALUE, val\n    }\n\n    @Test\n    void testContentWithContentType() {\n        String s = 'Hello JJWT'\n        String cty = 'text/plain'\n        String compact = Jwts.builder().content(s, cty).compact()\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        assertEquals cty, jwt.header.getContentType()\n        assertEquals s, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testContentBytesWithContentType() {\n        String s = 'Hello JJWT'\n        byte[] content = Strings.utf8(s)\n        String cty = 'text/plain'\n        String compact = Jwts.builder().content(content, cty).compact()\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        assertEquals cty, jwt.header.getContentType()\n        assertEquals s, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testContentStreamWithContentType() {\n        String s = 'Hello JJWT'\n        InputStream content = Streams.of(Strings.utf8(s))\n        String cty = 'text/plain'\n        String compact = Jwts.builder().content(content, cty).compact()\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        assertEquals cty, jwt.header.getContentType()\n        assertEquals s, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testContentStreamWithoutContentType() {\n        String s = 'Hello JJWT'\n        InputStream content = Streams.of(Strings.utf8(s))\n        String compact = Jwts.builder().content(content).compact()\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        assertNull jwt.header.getContentType()\n        assertEquals s, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testContentStreamNull() {\n        String compact = Jwts.builder().content((InputStream) null).compact()\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        assertEquals 'none', jwt.header.getAlgorithm()\n        assertTrue Bytes.isEmpty(jwt.getPayload())\n    }\n\n    @Test\n    void testContentWithApplicationContentType() {\n        String s = 'Hello JJWT'\n        String subtype = 'foo'\n        String cty = \"application/$subtype\"\n        String compact = Jwts.builder().content(s, cty).compact()\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        // assert raw value is compact form:\n        assertEquals subtype, jwt.header.get('cty')\n        // assert getter reflects normalized form per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10:\n        assertEquals cty, jwt.header.getContentType()\n        assertEquals s, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testContentWithNonCompactApplicationContentType() {\n        String s = 'Hello JJWT'\n        String subtype = 'foo'\n        String cty = \"application/$subtype;part=1/2\"\n        String compact = Jwts.builder().content(s, cty).compact()\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        assertEquals cty, jwt.header.getContentType() // two slashes, can't compact\n        assertEquals s, new String(jwt.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testParseContentToken() {\n\n        def claims = [iss: 'joe', exp: later(), 'https://example.com/is_root': true]\n\n        String jwt = Jwts.builder().claims().add(claims).and().compact()\n\n        def token = Jwts.parser().unsecured().build().parse(jwt)\n\n        //noinspection GrEqualsBetweenInconvertibleTypes\n        assert token.payload == claims\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testParseNull() {\n        Jwts.parser().build().parse(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testParseEmptyString() {\n        Jwts.parser().build().parse('')\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testParseWhitespaceString() {\n        Jwts.parser().build().parse('   ')\n    }\n\n    @Test\n    void testParseClaimsWithLeadingAndTrailingWhitespace() {\n        String whitespaceChars = ' \\t \\n \\r '\n        String claimsJson = whitespaceChars + '{\"sub\":\"joe\"}' + whitespaceChars\n\n        String header = Encoders.BASE64URL.encode('{\"alg\":\"none\"}'.getBytes(StandardCharsets.UTF_8))\n        String claims = Encoders.BASE64URL.encode(claimsJson.getBytes(StandardCharsets.UTF_8))\n\n        String compact = header + '.' + claims + '.'\n        def jwt = Jwts.parser().unsecured().build().parseUnsecuredClaims(compact)\n        assertEquals 'none', jwt.header.getAlgorithm()\n        assertEquals 'joe', jwt.payload.getSubject()\n    }\n\n    @Test\n    void testParseWithNoPeriods() {\n        try {\n            Jwts.parser().build().parse('foo')\n            fail()\n        } catch (MalformedJwtException e) {\n            //noinspection GroovyAccessibility\n            String expected = JwtTokenizer.DELIM_ERR_MSG_PREFIX + '0'\n            assertEquals expected, e.message\n        }\n    }\n\n    @Test\n    void testParseWithOnePeriodOnly() {\n        try {\n            Jwts.parser().build().parse('.')\n            fail()\n        } catch (MalformedJwtException e) {\n            //noinspection GroovyAccessibility\n            String expected = JwtTokenizer.DELIM_ERR_MSG_PREFIX + '1'\n            assertEquals expected, e.message\n        }\n    }\n\n    @Test\n    void testParseWithTwoPeriodsOnly() {\n        try {\n            Jwts.parser().build().parse('..')\n            fail()\n        } catch (MalformedJwtException e) {\n            String msg = 'Compact JWT strings MUST always have a Base64Url protected header per ' +\n                    'https://tools.ietf.org/html/rfc7519#section-7.2 (steps 2-4).'\n            assertEquals msg, e.message\n        }\n    }\n\n    @Test\n    void testParseWithHeaderOnly() {\n        String unsecuredJwt = base64Url(\"{\\\"alg\\\":\\\"none\\\"}\") + \"..\"\n        Jwt jwt = Jwts.parser().unsecured().build().parse(unsecuredJwt)\n        assertEquals \"none\", jwt.getHeader().get(\"alg\")\n    }\n\n    @Test\n    void testParseWithSignatureOnly() {\n        try {\n            Jwts.parser().build().parse('..bar')\n            fail()\n        } catch (MalformedJwtException e) {\n            assertEquals 'Compact JWT strings MUST always have a Base64Url protected header per https://tools.ietf.org/html/rfc7519#section-7.2 (steps 2-4).', e.message\n        }\n    }\n\n    @Test\n    void testParseWithMissingRequiredSignature() {\n        Key key = Jwts.SIG.HS256.key().build()\n        String compact = Jwts.builder().setSubject('foo').signWith(key).compact()\n        int i = compact.lastIndexOf('.')\n        String missingSig = compact.substring(0, i + 1)\n        try {\n            Jwts.parser().unsecured().setSigningKey(key).build().parseSignedClaims(missingSig)\n            fail()\n        } catch (MalformedJwtException expected) {\n            String s = String.format(DefaultJwtParser.MISSING_JWS_DIGEST_MSG_FMT, 'HS256')\n            assertEquals s, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testWithInvalidCompressionAlgorithm() {\n        try {\n            Jwts.builder().header().add('zip', 'CUSTOM').and().id(\"andId\").compact()\n        } catch (CompressionException e) {\n            assertEquals \"Unsupported compression algorithm 'CUSTOM'\", e.getMessage()\n        }\n    }\n\n    @Test\n    void testConvenienceIssuer() {\n        String compact = Jwts.builder().setIssuer(\"Me\").compact()\n        Claims claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertEquals 'Me', claims.getIssuer()\n\n        compact = Jwts.builder().setSubject(\"Joe\")\n                .setIssuer(\"Me\") //set it\n                .setIssuer(null) //null should remove it\n                .compact()\n\n        claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertNull claims.getIssuer()\n    }\n\n    @Test\n    void testConvenienceSubject() {\n        String compact = Jwts.builder().setSubject(\"Joe\").compact()\n        Claims claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertEquals 'Joe', claims.getSubject()\n\n        compact = Jwts.builder().setIssuer(\"Me\")\n                .setSubject(\"Joe\") //set it\n                .setSubject(null) //null should remove it\n                .compact()\n\n        claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertNull claims.getSubject()\n    }\n\n    @Test\n    void testConvenienceAudience() {\n        String compact = Jwts.builder().setAudience(\"You\").compact()\n        Claims claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertEquals 'You', claims.getAudience().iterator().next()\n\n        compact = Jwts.builder().setIssuer(\"Me\")\n                .setAudience(\"You\") //set it\n                .setAudience(null) //null should remove it\n                .compact()\n\n        claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertNull claims.getAudience()\n    }\n\n    @Test\n    void testConvenienceExpiration() {\n        Date then = laterDate(10000)\n        String compact = Jwts.builder().setExpiration(then).compact()\n        Claims claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        def claimedDate = claims.getExpiration()\n        assertEquals then, claimedDate\n\n        compact = Jwts.builder().setIssuer(\"Me\")\n                .setExpiration(then) //set it\n                .setExpiration(null) //null should remove it\n                .compact()\n\n        claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertNull claims.getExpiration()\n    }\n\n    @Test\n    void testConvenienceNotBefore() {\n        Date now = now() //jwt exp only supports *seconds* since epoch:\n        String compact = Jwts.builder().setNotBefore(now).compact()\n        Claims claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        def claimedDate = claims.getNotBefore()\n        assertEquals now, claimedDate\n\n        compact = Jwts.builder().setIssuer(\"Me\")\n                .setNotBefore(now) //set it\n                .setNotBefore(null) //null should remove it\n                .compact()\n\n        claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertNull claims.getNotBefore()\n    }\n\n    @Test\n    void testConvenienceIssuedAt() {\n        Date now = now() //jwt exp only supports *seconds* since epoch:\n        String compact = Jwts.builder().setIssuedAt(now).compact()\n        Claims claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        def claimedDate = claims.getIssuedAt()\n        assertEquals now, claimedDate\n\n        compact = Jwts.builder().setIssuer(\"Me\")\n                .setIssuedAt(now) //set it\n                .setIssuedAt(null) //null should remove it\n                .compact()\n\n        claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertNull claims.getIssuedAt()\n    }\n\n    @Test\n    void testConvenienceId() {\n        String id = UUID.randomUUID().toString()\n        String compact = Jwts.builder().setId(id).compact()\n        Claims claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertEquals id, claims.getId()\n\n        compact = Jwts.builder().setIssuer(\"Me\")\n                .setId(id) //set it\n                .setId(null) //null should remove it\n                .compact()\n\n        claims = Jwts.parser().unsecured().build().parse(compact).payload as Claims\n        assertNull claims.getId()\n    }\n\n    @Test\n    void testUncompressedJwt() {\n\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        String id = UUID.randomUUID().toString()\n\n        String compact = Jwts.builder().id(id).issuer(\"an issuer\").signWith(key, alg)\n                .claim(\"state\", \"hello this is an amazing jwt\").compact()\n\n        def jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(compact)\n\n        Claims claims = jws.payload\n\n        assertNull jws.header.getCompressionAlgorithm()\n\n        assertEquals id, claims.getId()\n        assertEquals \"an issuer\", claims.getIssuer()\n        assertEquals \"hello this is an amazing jwt\", claims.state\n    }\n\n    @Test\n    void testCompressedJwtWithDeflate() {\n\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        String id = UUID.randomUUID().toString()\n\n        String compact = Jwts.builder().id(id).issuer(\"an issuer\").signWith(key, alg)\n                .claim(\"state\", \"hello this is an amazing jwt\").compressWith(Jwts.ZIP.DEF).compact()\n\n        def jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(compact)\n\n        Claims claims = jws.payload\n\n        assertEquals \"DEF\", jws.header.getCompressionAlgorithm()\n\n        assertEquals id, claims.getId()\n        assertEquals \"an issuer\", claims.getIssuer()\n        assertEquals \"hello this is an amazing jwt\", claims.state\n    }\n\n    @Test\n    void testCompressedJwtWithGZIP() {\n\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        String id = UUID.randomUUID().toString()\n\n        String compact = Jwts.builder().id(id).issuer(\"an issuer\").signWith(key, alg)\n                .claim(\"state\", \"hello this is an amazing jwt\").compressWith(Jwts.ZIP.GZIP).compact()\n\n        def jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(compact)\n\n        Claims claims = jws.payload\n\n        assertEquals \"GZIP\", jws.header.getCompressionAlgorithm()\n\n        assertEquals id, claims.getId()\n        assertEquals \"an issuer\", claims.getIssuer()\n        assertEquals \"hello this is an amazing jwt\", claims.state\n    }\n\n    @Test\n    void testCompressedWithCustomResolver() {\n\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        String id = UUID.randomUUID().toString()\n\n        String compact = Jwts.builder().id(id).issuer(\"an issuer\").signWith(key, alg)\n                .claim(\"state\", \"hello this is an amazing jwt\").compressWith(new GzipCompressionAlgorithm() {\n            @Override\n            String getId() {\n                return \"CUSTOM\"\n            }\n        }).compact()\n\n        def jws = Jwts.parser().verifyWith(key).setCompressionCodecResolver(new CompressionCodecResolver() {\n            @Override\n            CompressionCodec resolveCompressionCodec(Header header) throws CompressionException {\n                String algorithm = header.getCompressionAlgorithm()\n                //noinspection ChangeToOperator\n                if (\"CUSTOM\".equals(algorithm)) {\n                    return Jwts.ZIP.GZIP as CompressionCodec\n                } else {\n                    return null\n                }\n            }\n        }).build().parseSignedClaims(compact)\n\n        Claims claims = jws.payload\n\n        assertEquals \"CUSTOM\", jws.header.getCompressionAlgorithm()\n\n        assertEquals id, claims.getId()\n        assertEquals \"an issuer\", claims.getIssuer()\n        assertEquals \"hello this is an amazing jwt\", claims.state\n\n    }\n\n    @Test(expected = UnsupportedJwtException.class)\n    void testCompressedJwtWithUnrecognizedHeader() {\n\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        String id = UUID.randomUUID().toString()\n\n        String compact = Jwts.builder().setId(id).setAudience(\"an audience\").signWith(key, alg)\n                .claim(\"state\", \"hello this is an amazing jwt\").compressWith(new GzipCompressionAlgorithm() {\n            @Override\n            String getId() {\n                return \"CUSTOM\"\n            }\n        }).compact()\n\n        Jwts.parser().setSigningKey(key).build().parseSignedClaims(compact)\n    }\n\n    @Test\n    void testCompressStringPayloadWithDeflate() {\n\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        String payload = \"this is my test for a payload\"\n\n        String compact = Jwts.builder().setPayload(payload).signWith(key, alg)\n                .compressWith(Jwts.ZIP.DEF).compact()\n\n        def jws = Jwts.parser().setSigningKey(key).build().parseSignedContent(compact)\n\n        assertEquals \"DEF\", jws.header.getCompressionAlgorithm()\n\n        assertEquals \"this is my test for a payload\", new String(jws.payload, StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testHS256() {\n        testHmac(Jwts.SIG.HS256)\n    }\n\n    @Test\n    void testHS384() {\n        testHmac(Jwts.SIG.HS384)\n    }\n\n    @Test\n    void testHS512() {\n        testHmac(Jwts.SIG.HS512)\n    }\n\n    @Test\n    void testRS256() {\n        testRsa(Jwts.SIG.RS256)\n    }\n\n    @Test\n    void testRS384() {\n        testRsa(Jwts.SIG.RS384)\n    }\n\n    @Test\n    void testRS512() {\n        testRsa(Jwts.SIG.RS512)\n    }\n\n    @Test\n    void testPS256() {\n        testRsa(Jwts.SIG.PS256)\n    }\n\n    @Test\n    void testPS384() {\n        testRsa(Jwts.SIG.PS384)\n    }\n\n    @Test\n    void testPS512() {\n        testRsa(Jwts.SIG.PS512)\n    }\n\n    @Test\n    void testES256() {\n        testEC(Jwts.SIG.ES256)\n    }\n\n    @Test\n    void testES384() {\n        testEC(Jwts.SIG.ES384)\n    }\n\n    @Test\n    void testES512() {\n        testEC(Jwts.SIG.ES512)\n    }\n\n    @Test\n    void testEdDSA() {\n        testEC(Jwts.SIG.EdDSA)\n    }\n\n    @Test\n    void testEd25519() {\n        testEC(Jwts.SIG.EdDSA, TestKeys.forAlgorithm(Jwks.CRV.Ed25519).pair)\n    }\n\n    @Test\n    void testEd448() {\n        testEC(Jwts.SIG.EdDSA, TestKeys.forAlgorithm(Jwks.CRV.Ed448).pair)\n    }\n\n    @Test\n    void testES256WithPrivateKeyValidation() {\n        def alg = Jwts.SIG.ES256\n        try {\n            testEC(alg, true)\n            fail(\"EC private keys cannot be used to validate EC signatures.\")\n        } catch (IllegalArgumentException e) {\n            assertEquals DefaultJwtParser.PRIV_KEY_VERIFY_MSG, e.getMessage()\n        }\n    }\n\n    @Test(expected = WeakKeyException)\n    void testparseSignedClaimsWithWeakHmacKey() {\n\n        def alg = Jwts.SIG.HS384\n        def key = alg.key().build()\n        def weakKey = Jwts.SIG.HS256.key().build()\n\n        String jws = Jwts.builder().setSubject(\"Foo\").signWith(key, alg).compact()\n\n        Jwts.parser().setSigningKey(weakKey).build().parseSignedClaims(jws)\n        fail('parseSignedClaims must fail for weak keys')\n    }\n\n    /**\n     * @since 0.11.5\n     */\n    @Test\n    void testBuilderWithEcdsaPublicKey() {\n        def builder = Jwts.builder().setSubject('foo')\n        def pair = TestKeys.ES256.pair\n        try {\n            builder.signWith(pair.public, SignatureAlgorithm.ES256) //public keys can't be used to create signatures\n        } catch (InvalidKeyException expected) {\n            String msg = \"ECDSA signing keys must be PrivateKey instances.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.11.5 as part of testing guards against JVM CVE-2022-21449\n     */\n    @Test\n    void testBuilderWithMismatchedEllipticCurveKeyAndAlgorithm() {\n        def builder = Jwts.builder().setSubject('foo')\n        def pair = TestKeys.ES384.pair\n        try {\n            builder.signWith(pair.private, SignatureAlgorithm.ES256)\n            //ES384 keys can't be used to create ES256 signatures\n        } catch (InvalidKeyException expected) {\n            String msg = \"EllipticCurve key has a field size of 48 bytes (384 bits), but ES256 requires a \" +\n                    \"field size of 32 bytes (256 bits) per [RFC 7518, Section 3.4 (validation)]\" +\n                    \"(https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.11.5 as part of testing guards against JVM CVE-2022-21449\n     */\n    @Test\n    void testParserWithMismatchedEllipticCurveKeyAndAlgorithm() {\n        def pair = TestKeys.ES256.pair\n        def jws = Jwts.builder().setSubject('foo').signWith(pair.private).compact()\n        def parser = Jwts.parser().setSigningKey(TestKeys.ES384.pair.public).build()\n        try {\n            parser.parseSignedClaims(jws)\n        } catch (UnsupportedJwtException expected) {\n            String msg = 'The parsed JWT indicates it was signed with the \\'ES256\\' signature algorithm, but ' +\n                    'the provided sun.security.ec.ECPublicKeyImpl key may not be used to verify ES256 signatures.  ' +\n                    'Because the specified key reflects a specific and expected algorithm, and the JWT does not ' +\n                    'reflect this algorithm, it is likely that the JWT was not expected and therefore should not ' +\n                    'be trusted.  Another possibility is that the parser was provided the incorrect signature ' +\n                    'verification key, but this cannot be assumed for security reasons.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.11.5 as part of testing guards against JVM CVE-2022-21449\n     */\n    @Test(expected = io.jsonwebtoken.security.SignatureException)\n    void testEcdsaInvalidSignatureValue() {\n        def withoutSignature = \"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n        def invalidEncodedSignature = \"_____wAAAAD__________7zm-q2nF56E87nKwvxjJVH_____AAAAAP__________vOb6racXnoTzucrC_GMlUQ\"\n        String jws = withoutSignature + '.' + invalidEncodedSignature\n        def keypair = Jwts.SIG.ES256.keyPair().build()\n        Jwts.parser().setSigningKey(keypair.public).build().parseSignedClaims(jws)\n    }\n\n    //Asserts correct/expected behavior discussed in https://github.com/jwtk/jjwt/issues/20\n    @Test\n    void testparseSignedClaimsWithUnsignedJwt() {\n\n        //create random signing key for testing:\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        String notSigned = Jwts.builder().setSubject(\"Foo\").compact()\n\n        try {\n            Jwts.parser().unsecured().setSigningKey(key).build().parseSignedClaims(notSigned)\n            fail('parseSignedClaims must fail for unsigned JWTs')\n        } catch (UnsupportedJwtException expected) {\n            assertEquals 'Unexpected unsecured Claims JWT.', expected.message\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweMissingAlg() {\n        def h = base64Url('{\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def compact = h + '.ecek.iv.' + c + '.tag'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            assertEquals DefaultJwtParser.MISSING_JWE_ALG_MSG, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweEmptyAlg() {\n        def h = base64Url('{\"alg\":\"\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def compact = h + '.ecek.iv.' + c + '.tag'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            assertEquals DefaultJwtParser.MISSING_JWE_ALG_MSG, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWhitespaceAlg() {\n        def h = base64Url('{\"alg\":\"  \",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def compact = h + '.ecek.iv.' + c + '.tag'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            assertEquals DefaultJwtParser.MISSING_JWE_ALG_MSG, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithNoneAlg() {\n        def h = base64Url('{\"alg\":\"none\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def compact = h + '.ecek.iv.' + c + '.tag'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            assertEquals DefaultJwtParser.JWE_NONE_MSG, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithMissingAadTag() {\n        def h = base64Url('{\"alg\":\"dir\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def compact = h + '.ecek.iv.' + c + '.'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            String expected = String.format(DefaultJwtParser.MISSING_JWE_DIGEST_MSG_FMT, 'dir')\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithEmptyAadTag() {\n        def h = base64Url('{\"alg\":\"dir\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        // our decoder skips invalid Base64Url characters, so this decodes to empty which is not allowed:\n        def tag = '&'\n        def compact = h + '.IA==.IA==.' + c + '.' + tag\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            String expected = 'Compact JWE strings must always contain an AAD Authentication Tag.'\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithMissingRequiredBody() {\n        def h = base64Url('{\"alg\":\"dir\",\"enc\":\"A128GCM\"}')\n        def compact = h + '.ecek.iv..tag'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            String expected = 'Compact JWE strings MUST always contain a payload (ciphertext).'\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithEmptyEncryptedKey() {\n        def h = base64Url('{\"alg\":\"dir\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        // our decoder skips invalid Base64Url characters, so this decodes to empty which is not allowed:\n        def encodedKey = '&'\n        def compact = h + '.' + encodedKey + '.iv.' + c + '.tag'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            String expected = 'Compact JWE string represents an encrypted key, but the key is empty.'\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithMissingInitializationVector() {\n        def h = base64Url('{\"alg\":\"dir\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def compact = h + '.IA==..' + c + '.tag'\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            String expected = 'Compact JWE strings must always contain an Initialization Vector.'\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithMissingEncHeader() {\n        def h = base64Url('{\"alg\":\"dir\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def ekey = 'IA=='\n        def iv = 'IA=='\n        def tag = 'IA=='\n        def compact = \"$h.$ekey.$iv.$c.$tag\" as String\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (MalformedJwtException e) {\n            assertEquals DefaultJwtParser.MISSING_ENC_MSG, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithUnrecognizedEncValue() {\n        def h = base64Url('{\"alg\":\"dir\",\"enc\":\"foo\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def ekey = 'IA=='\n        def iv = 'IA=='\n        def tag = 'IA=='\n        def compact = \"$h.$ekey.$iv.$c.$tag\" as String\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String expected = \"Unsupported JWE header 'enc' (Encryption Algorithm) value 'foo'.\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithUnrecognizedAlgValue() {\n        def h = base64Url('{\"alg\":\"bar\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def ekey = 'IA=='\n        def iv = 'IA=='\n        def tag = 'IA=='\n        def compact = \"$h.$ekey.$iv.$c.$tag\" as String\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String expected = \"Unsupported JWE header 'alg' (Algorithm) value 'bar'.\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJwsWithUnrecognizedAlgValue() {\n        def h = base64Url('{\"alg\":\"bar\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def sig = 'IA=='\n        def compact = \"$h.$c.$sig\" as String\n        try {\n            Jwts.parser().build().parseSignedClaims(compact)\n            fail()\n        } catch (io.jsonwebtoken.security.SignatureException e) {\n            String expected = \"Unsupported signature algorithm 'bar': \" +\n                    \"Unsupported JWS header 'alg' (Algorithm) value 'bar'.\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithUnlocatableKey() {\n        def h = base64Url('{\"alg\":\"dir\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def ekey = 'IA=='\n        def iv = 'IA=='\n        def tag = 'IA=='\n        def compact = \"$h.$ekey.$iv.$c.$tag\" as String\n        try {\n            Jwts.parser().build().parseEncryptedClaims(compact)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String expected = \"Cannot decrypt JWE payload: unable to locate key for JWE with header: {alg=dir, enc=A128GCM}\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJwsWithCustomSignatureAlgorithm() {\n        def realAlg = Jwts.SIG.HS256 // any alg will do, we're going to wrap it\n        def key = TestKeys.HS256\n        def id = realAlg.getId() + 'X' // custom id\n        def alg = new TestMacAlgorithm(id: id, delegate: realAlg)\n\n        def jws = Jwts.builder().setSubject(\"joe\").signWith(key, alg).compact()\n\n        assertEquals 'joe', Jwts.parser()\n                .sig().add(alg).and()\n                .setSigningKey(key)\n                .build()\n                .parseSignedClaims(jws).payload.getSubject()\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithCustomEncryptionAlgorithm() {\n        def realAlg = Jwts.ENC.A128GCM // any alg will do, we're going to wrap it\n        def key = realAlg.key().build()\n        def enc = realAlg.getId() + 'X' // custom id\n        def encAlg = new AeadAlgorithm() {\n            @Override\n            void encrypt(AeadRequest request, AeadResult result) throws SecurityException {\n                realAlg.encrypt(request, result)\n            }\n\n            @Override\n            void decrypt(DecryptAeadRequest request, OutputStream out) throws SecurityException {\n                realAlg.decrypt(request, out)\n            }\n\n            @Override\n            String getId() {\n                return enc\n            }\n\n            @Override\n            SecretKeyBuilder key() {\n                return realAlg.key()\n            }\n\n            @Override\n            int getKeyBitLength() {\n                return realAlg.getKeyBitLength()\n            }\n        }\n\n        def jwe = Jwts.builder().setSubject(\"joe\").encryptWith(key, encAlg).compact()\n\n        assertEquals 'joe', Jwts.parser()\n                .enc().add(encAlg).and()\n                .decryptWith(key)\n                .build()\n                .parseEncryptedClaims(jwe).payload.getSubject()\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseJweWithBadKeyAlg() {\n        def alg = 'foo'\n        def h = base64Url('{\"alg\":\"foo\",\"enc\":\"A128GCM\"}')\n        def c = base64Url('{\"sub\":\"joe\"}')\n        def ekey = 'IA=='\n        def iv = 'IA=='\n        def tag = 'IA=='\n        def compact = \"$h.$ekey.$iv.$c.$tag\" as String\n\n        def badKeyAlg = new KeyAlgorithm() {\n            @Override\n            KeyResult getEncryptionKey(KeyRequest request) throws SecurityException {\n                return null\n            }\n\n            @Override\n            SecretKey getDecryptionKey(DecryptionKeyRequest request) throws SecurityException {\n                return null // bad implementation here - returns null, and that's not good\n            }\n\n            @Override\n            String getId() {\n                return alg\n            }\n        }\n\n        try {\n            Jwts.parser()\n                    .keyLocator(new ConstantKeyLocator(TestKeys.HS256, TestKeys.A128GCM))\n                    .key().add(badKeyAlg).and() // <-- add bad alg here\n                    .build()\n                    .parseEncryptedClaims(compact)\n            fail()\n        } catch (IllegalStateException e) {\n            String expected = \"The 'foo' JWE key algorithm did not return a decryption key. \" +\n                    \"Unable to perform 'A128GCM' decryption.\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    /**\n     * @since 0.12.0\n     */\n    @Test\n    void testParseRequiredInt() {\n        def key = TestKeys.HS256\n        def jws = Jwts.builder().signWith(key).claim(\"foo\", 42).compact()\n        Jwts.parser().setSigningKey(key)\n                .require(\"foo\", 42L) //require a long, but jws contains int, should still work\n                .build().parseSignedClaims(jws)\n    }\n\n    /**\n     * Asserts that if a {@link Jwts#claims()} builder is used to set a single string Audience value, and the\n     * resulting constructed {@link Claims} instance is used on a {@link Jwts#builder()}, that the resulting JWT\n     * retains a single-string Audience value (and it is not automatically coerced to a {@code Set<String>}).\n     *\n     * @since 0.12.4\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/890\">JJWT Issue 890</a>\n     */\n    @Test\n    void testClaimsBuilderSingleStringAudienceThenJwtBuilder() {\n\n        def key = TestKeys.HS256\n        def aud = 'foo'\n        def claims = Jwts.claims().audience().single(aud).build()\n        def jws = Jwts.builder().claims(claims).signWith(key).compact()\n\n        // we can't use a JwtParser here because that will automatically normalize a single String value as a\n        // Set<String> for app developer convenience.  So we assert that the JWT looks as expected by simple\n        // json parsing and map inspection\n\n        int i = jws.indexOf('.')\n        int j = jws.lastIndexOf('.')\n        def b64 = jws.substring(i, j)\n        def json = Strings.utf8(Decoders.BASE64URL.decode(b64))\n        def deser = Services.get(Deserializer)\n        def m = deser.deserialize(new StringReader(json)) as Map<String,?>\n\n        assertEquals aud, m.get('aud') // single string value\n    }\n\n    //Asserts correct/expected behavior discussed in https://github.com/jwtk/jjwt/issues/20\n    @Test\n    void testForgedTokenWithSwappedHeaderUsingNoneAlgorithm() {\n\n        //create random signing key for testing:\n        def alg = Jwts.SIG.HS256\n        SecretKey key = alg.key().build()\n\n        //this is a 'real', valid JWT:\n        String compact = Jwts.builder().setSubject(\"Joe\").signWith(key, alg).compact()\n\n        //Now strip off the signature so we can add it back in later on a forged token:\n        int i = compact.lastIndexOf('.')\n        String signature = compact.substring(i + 1)\n\n        //now let's create a fake header and payload with whatever we want (without signing):\n        String forged = Jwts.builder().setSubject(\"Not Joe\").compact()\n\n        //assert that our forged header has a 'NONE' algorithm:\n        assertEquals 'none', Jwts.parser().unsecured().build().parseUnsecuredClaims(forged).getHeader().get('alg')\n\n        //now let's forge it by appending the signature the server expects:\n        forged += signature\n\n        //now assert that, when the server tries to parse the forged token, parsing fails:\n        try {\n            Jwts.parser().unsecured().setSigningKey(key).build().parse(forged)\n            fail(\"Parsing must fail for a forged token.\")\n        } catch (MalformedJwtException expected) {\n            assertEquals 'The JWS header references signature algorithm \\'none\\' yet the compact JWS string contains a signature. This is not permitted per https://tools.ietf.org/html/rfc7518#section-3.6.', expected.message\n        }\n    }\n\n    //Asserts correct/expected behavior discussed in https://github.com/jwtk/jjwt/issues/20 and https://github.com/jwtk/jjwt/issues/25\n    @Test\n    void testParseForgedRsaPublicKeyAsHmacTokenVerifiedWithTheRsaPrivateKey() {\n\n        //Create a legitimate RSA public and private key pair:\n        KeyPair kp = TestKeys.RS256.pair\n        PublicKey publicKey = kp.getPublic()\n        PrivateKey privateKey = kp.getPrivate()\n\n        String header = base64Url(toJson(['alg': 'HS256']))\n        String body = base64Url(toJson('foo'))\n        String compact = header + '.' + body + '.'\n\n        // Now for the forgery: simulate an attacker using the RSA public key to sign a token, but\n        // using it as an HMAC signing key instead of RSA:\n        Mac mac = Mac.getInstance('HmacSHA256')\n        byte[] raw = ((RSAPublicKey) publicKey).getModulus().toByteArray()\n        if (raw.length > 256) {\n            raw = Arrays.copyOfRange(raw, 1, raw.length)\n        }\n        mac.init(new SecretKeySpec(raw, 'HmacSHA256'))\n        byte[] signatureBytes = mac.doFinal(compact.getBytes(Charset.forName('US-ASCII')))\n        String encodedSignature = Encoders.BASE64URL.encode(signatureBytes)\n\n        //Finally, the forged token is the header + body + forged signature:\n        String forged = compact + encodedSignature\n\n        // Assert that the server does not recognized the forged token:\n        try {\n            Jwts.parser().verifyWith(publicKey).build().parse(forged)\n            fail(\"Forged token must not be successfully parsed.\")\n        } catch (UnsupportedJwtException expected) {\n            assertTrue expected.getMessage().startsWith('The parsed JWT indicates it was signed with the')\n        }\n    }\n\n    //Asserts correct behavior for https://github.com/jwtk/jjwt/issues/25\n    @Test\n    void testParseForgedRsaPublicKeyAsHmacTokenVerifiedWithTheRsaPublicKey() {\n\n        //Create a legitimate RSA public and private key pair:\n        KeyPair kp = TestKeys.RS256.pair\n        PublicKey publicKey = kp.getPublic()\n        //PrivateKey privateKey = kp.getPrivate();\n\n        String header = base64Url(toJson(['alg': 'HS256']))\n        String body = base64Url(toJson('foo'))\n        String compact = header + '.' + body + '.'\n\n        // Now for the forgery: simulate an attacker using the RSA public key to sign a token, but\n        // using it as an HMAC signing key instead of RSA:\n        Mac mac = Mac.getInstance('HmacSHA256')\n        byte[] raw = ((RSAPublicKey) publicKey).getModulus().toByteArray()\n        if (raw.length > 256) {\n            raw = Arrays.copyOfRange(raw, 1, raw.length)\n        }\n        mac.init(new SecretKeySpec(raw, 'HmacSHA256'))\n        byte[] signatureBytes = mac.doFinal(compact.getBytes(Charset.forName('US-ASCII')))\n        String encodedSignature = Encoders.BASE64URL.encode(signatureBytes)\n\n        //Finally, the forged token is the header + body + forged signature:\n        String forged = compact + encodedSignature\n\n        // Assert that the parser does not recognized the forged token:\n        try {\n            Jwts.parser().setSigningKey(publicKey).build().parse(forged)\n            fail(\"Forged token must not be successfully parsed.\")\n        } catch (UnsupportedJwtException expected) {\n            assertTrue expected.getMessage().startsWith('The parsed JWT indicates it was signed with the')\n        }\n    }\n\n    //Asserts correct behavior for https://github.com/jwtk/jjwt/issues/25\n    @Test\n    void testParseForgedEllipticCurvePublicKeyAsHmacToken() {\n\n        //Create a legitimate EC public and private key pair:\n        KeyPair kp = TestKeys.ES256.pair\n        PublicKey publicKey = kp.getPublic()\n        //PrivateKey privateKey = kp.getPrivate();\n\n        String header = base64Url(toJson(['alg': 'HS256']))\n        String body = base64Url(toJson('foo'))\n        String compact = header + '.' + body + '.'\n\n        // Now for the forgery: simulate an attacker using the Elliptic Curve public key to sign a token, but\n        // using it as an HMAC signing key instead of Elliptic Curve:\n        Mac mac = Mac.getInstance('HmacSHA256')\n        byte[] raw = ((ECPublicKey) publicKey).getParams().getOrder().toByteArray()\n        if (raw.length > 32) {\n            raw = Arrays.copyOfRange(raw, 1, raw.length)\n        }\n        mac.init(new SecretKeySpec(raw, 'HmacSHA256'))\n        byte[] signatureBytes = mac.doFinal(compact.getBytes(Charset.forName('US-ASCII')))\n        String encodedSignature = Encoders.BASE64URL.encode(signatureBytes)\n\n        //Finally, the forged token is the header + body + forged signature:\n        String forged = compact + encodedSignature\n\n        // Assert that the parser does not recognized the forged token:\n        try {\n            Jwts.parser().setSigningKey(publicKey).build().parse(forged)\n            fail(\"Forged token must not be successfully parsed.\")\n        } catch (UnsupportedJwtException expected) {\n            assertTrue expected.getMessage().startsWith('The parsed JWT indicates it was signed with the')\n        }\n    }\n\n    @Test\n    void testSecretKeyJwes() {\n\n        def algs = Jwts.KEY.get().values().findAll({ it ->\n            it instanceof DirectKeyAlgorithm || it instanceof SecretKeyAlgorithm\n        })// as Collection<KeyAlgorithm<SecretKey, SecretKey>>\n\n        for (KeyAlgorithm alg : algs) {\n\n            for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                SecretKey key = alg instanceof SecretKeyAlgorithm ?\n                        ((SecretKeyAlgorithm) alg).key().build() :\n                        enc.key().build()\n\n                // encrypt:\n                String jwe = Jwts.builder()\n                        .claim('foo', 'bar')\n                        .encryptWith(key, alg, enc)\n                        .compact()\n\n                //decrypt:\n                def jwt = Jwts.parser()\n                        .decryptWith(key)\n                        .build()\n                        .parseEncryptedClaims(jwe)\n                assertEquals 'bar', jwt.getPayload().get('foo')\n            }\n        }\n    }\n\n    @Test\n    void testJweCompression() {\n\n        def codecs = [Jwts.ZIP.DEF, Jwts.ZIP.GZIP]\n\n        for (CompressionCodec codec : codecs) {\n\n            for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                SecretKey key = enc.key().build()\n\n                // encrypt and compress:\n                String jwe = Jwts.builder()\n                        .claim('foo', 'bar')\n                        .compressWith(codec)\n                        .encryptWith(key, enc)\n                        .compact()\n\n                //decompress and decrypt:\n                def jwt = Jwts.parser()\n                        .decryptWith(key)\n                        .build()\n                        .parseEncryptedClaims(jwe)\n                assertEquals 'bar', jwt.getPayload().get('foo')\n            }\n        }\n    }\n\n    @Test\n    void testJweCompressionWithArbitraryContentString() {\n        def codecs = [Jwts.ZIP.DEF, Jwts.ZIP.GZIP]\n\n        for (CompressionAlgorithm zip : codecs) {\n\n            for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                SecretKey key = enc.key().build()\n\n                String payload = 'hello, world!'\n\n                // encrypt and compress:\n                String jwe = Jwts.builder()\n                    .content(payload)\n                    .compressWith(zip)\n                    .encryptWith(key, enc)\n                    .compact()\n\n                //decompress and decrypt:\n                def jwt = Jwts.parser()\n                    .decryptWith(key)\n                    .build()\n                    .parseEncryptedContent(jwe)\n                assertEquals payload, new String(jwt.getPayload(), StandardCharsets.UTF_8)\n            }\n        }\n    }\n\n    @Test\n    void testJweCompressionWithArbitraryContentByteArray() {\n        def codecs = [Jwts.ZIP.DEF, Jwts.ZIP.GZIP]\n\n        for (CompressionAlgorithm zip : codecs) {\n\n            for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                SecretKey key = enc.key().build()\n\n                byte[] payload = new byte[14];\n                Randoms.secureRandom().nextBytes(payload)\n\n                // encrypt and compress:\n                String jwe = Jwts.builder()\n                        .content(payload)\n                        .compressWith(zip)\n                        .encryptWith(key, enc)\n                        .compact()\n\n                //decompress and decrypt:\n                def jwt = Jwts.parser()\n                        .decryptWith(key)\n                        .build()\n                        .parseEncryptedContent(jwe)\n                assertArrayEquals payload, jwt.getPayload()\n            }\n        }\n    }\n\n    @Test\n    void testJweCompressionWithArbitraryContentInputStream() {\n        def codecs = [Jwts.ZIP.DEF, Jwts.ZIP.GZIP]\n\n        for (CompressionAlgorithm zip : codecs) {\n\n            for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                SecretKey key = enc.key().build()\n\n                byte[] payloadBytes = new byte[14];\n                Randoms.secureRandom().nextBytes(payloadBytes)\n\n                ByteArrayInputStream payload = new ByteArrayInputStream(payloadBytes)\n\n                // encrypt and compress:\n                String jwe = Jwts.builder()\n                        .content(payload)\n                        .compressWith(zip)\n                        .encryptWith(key, enc)\n                        .compact()\n\n                //decompress and decrypt:\n                def jwt = Jwts.parser()\n                        .decryptWith(key)\n                        .build()\n                        .parseEncryptedContent(jwe)\n                assertArrayEquals payloadBytes, jwt.getPayload()\n            }\n        }\n    }\n\n    @Test\n    void testPasswordJwes() {\n\n        def algs = Jwts.KEY.get().values().findAll({ it ->\n            it instanceof Pbes2HsAkwAlgorithm\n        })// as Collection<KeyAlgorithm<SecretKey, SecretKey>>\n\n        Password key = Keys.password(\"12345678\".toCharArray())\n\n        for (KeyAlgorithm alg : algs) {\n\n            for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                // encrypt:\n                String jwe = Jwts.builder()\n                        .claim('foo', 'bar')\n                        .encryptWith(key, alg, enc)\n                        .compact()\n\n                //decrypt:\n                def jwt = Jwts.parser()\n                        .decryptWith(key)\n                        .build()\n                        .parseEncryptedClaims(jwe)\n                assertEquals 'bar', jwt.getPayload().get('foo')\n            }\n        }\n    }\n\n    @Test\n    void testPasswordJweWithoutSpecifyingAlg() {\n\n        Password key = Keys.password(\"12345678\".toCharArray())\n\n        // encrypt:\n        String jwe = Jwts.builder()\n                .claim('foo', 'bar')\n                .encryptWith(key, Jwts.ENC.A256GCM) // should auto choose KeyAlg PBES2_HS512_A256KW\n                .compact()\n\n        //decrypt:\n        def jwt = Jwts.parser()\n                .decryptWith(key)\n                .build()\n                .parseEncryptedClaims(jwe)\n        assertEquals 'bar', jwt.getPayload().get('foo')\n        assertEquals Jwts.KEY.PBES2_HS512_A256KW, Jwts.KEY.get().forKey(jwt.getHeader().getAlgorithm())\n    }\n\n    @Test\n    void testRsaJwes() {\n\n        def pairs = [TestKeys.RS256.pair, TestKeys.RS384.pair, TestKeys.RS512.pair]\n\n        def algs = Jwts.KEY.get().values().findAll({ it ->\n            it instanceof DefaultRsaKeyAlgorithm\n        })// as Collection<KeyAlgorithm<SecretKey, SecretKey>>\n\n        for (KeyPair pair : pairs) {\n\n            def pubKey = pair.getPublic()\n            def privKey = pair.getPrivate()\n\n            for (KeyAlgorithm alg : algs) {\n\n                for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                    // encrypt:\n                    String jwe = Jwts.builder()\n                            .claim('foo', 'bar')\n                            .encryptWith(pubKey, alg, enc)\n                            .compact()\n\n                    //decrypt:\n                    def jwt = Jwts.parser()\n                            .decryptWith(privKey)\n                            .build()\n                            .parseEncryptedClaims(jwe)\n                    assertEquals 'bar', jwt.getPayload().get('foo')\n                }\n            }\n        }\n    }\n\n    @Test\n    void testEcJwes() {\n\n        def pairs = [TestKeys.ES256.pair, TestKeys.ES384.pair, TestKeys.ES512.pair]\n\n        def algs = Jwts.KEY.get().values().findAll({ it ->\n            it.getId().startsWith(\"ECDH-ES\")\n        })\n\n        for (KeyPair pair : pairs) {\n\n            def pubKey = pair.getPublic()\n            def privKey = pair.getPrivate()\n\n            for (KeyAlgorithm alg : algs) {\n\n                for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n\n                    // encrypt:\n                    String jwe = Jwts.builder()\n                            .claim('foo', 'bar')\n                            .encryptWith(pubKey, alg, enc)\n                            .compact()\n\n                    //decrypt:\n                    def jwt = Jwts.parser()\n                            .decryptWith(privKey)\n                            .build()\n                            .parseEncryptedClaims(jwe)\n                    assertEquals 'bar', jwt.getPayload().get('foo')\n                }\n            }\n        }\n    }\n\n    @Test\n    void testEdwardsCurveJwes() { // ensures encryption works with Edwards Curve keys (X25519 and X448)\n\n        def pairs = [TestKeys.X25519.pair, TestKeys.X448.pair]\n\n        def algs = Jwts.KEY.get().values().findAll({ it ->\n            it.getId().startsWith(\"ECDH-ES\")\n        })\n\n        for (KeyPair pair : pairs) {\n\n            def pubKey = pair.getPublic()\n            def privKey = pair.getPrivate()\n\n            for (KeyAlgorithm alg : algs) {\n                for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n                    String jwe = encrypt(pubKey, alg, enc)\n                    def jwt = decrypt(jwe, privKey)\n                    assertEquals 'bar', jwt.getPayload().get('foo')\n                }\n            }\n        }\n    }\n\n    /**\n     * Asserts that Edwards Curve signing keys cannot be used for encryption (key agreement) per\n     * https://www.rfc-editor.org/rfc/rfc8037#section-3.1\n     */\n    @Test\n    void testEdwardsCurveEncryptionWithSigningKeys() {\n        def pairs = [TestKeys.Ed25519.pair, TestKeys.Ed448.pair] // signing keys, can't be used\n\n        def algs = Jwts.KEY.get().values().findAll({ it ->\n            it.getId().startsWith(\"ECDH-ES\")\n        })\n\n        for (KeyPair pair : pairs) {\n            def pubKey = pair.getPublic()\n            for (KeyAlgorithm alg : algs) {\n                for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n                    try {\n                        encrypt(pubKey, alg, enc)\n                        fail()\n                    } catch (InvalidKeyException expected) {\n                        String id = EdwardsCurve.forKey(pubKey).getId()\n                        String msg = id + \" keys may not be used with ECDH-ES key \" +\n                                \"agreement algorithms per https://www.rfc-editor.org/rfc/rfc8037#section-3.1.\"\n                        assertEquals msg, expected.getMessage()\n                    }\n                }\n            }\n        }\n    }\n\n    /**\n     * Asserts that Edwards Curve signing keys cannot be used for decryption (key agreement) per\n     * https://www.rfc-editor.org/rfc/rfc8037#section-3.1\n     */\n    @Test\n    void testEdwardsCurveDecryptionWithSigningKeys() {\n\n        def pairs = [ // private keys are invalid signing keys to test decryption:\n                      new KeyPair(TestKeys.X25519.pair.public, TestKeys.Ed25519.pair.private),\n                      new KeyPair(TestKeys.X448.pair.public, TestKeys.Ed448.pair.private)\n        ]\n\n        def algs = Jwts.KEY.get().values().findAll({ it ->\n            it.getId().startsWith(\"ECDH-ES\")\n        })\n\n        for (KeyPair pair : pairs) {\n            for (KeyAlgorithm alg : algs) {\n                for (AeadAlgorithm enc : Jwts.ENC.get().values()) {\n                    String jwe = encrypt(pair.getPublic(), alg, enc)\n                    PrivateKey key = pair.getPrivate()\n                    try {\n                        decrypt(jwe, key) // invalid signing key\n                        fail()\n                    } catch (InvalidKeyException expected) {\n                        String id = EdwardsCurve.forKey(key).getId()\n                        String msg = id + \" keys may not be used with ECDH-ES key \" +\n                                \"agreement algorithms per https://www.rfc-editor.org/rfc/rfc8037#section-3.1.\"\n                        assertEquals msg, expected.getMessage()\n                    }\n                }\n            }\n        }\n    }\n\n    static String encrypt(PublicKey key, KeyAlgorithm alg, AeadAlgorithm enc) {\n        return Jwts.builder().claim('foo', 'bar').encryptWith(key, alg, enc).compact()\n    }\n\n    static Jwe<Claims> decrypt(String jwe, PrivateKey key) {\n        return Jwts.parser().decryptWith(key).build().parseEncryptedClaims(jwe)\n    }\n\n    static void testRsa(io.jsonwebtoken.security.SignatureAlgorithm alg) {\n\n        KeyPair kp = TestKeys.forAlgorithm(alg).pair\n        PublicKey publicKey = kp.getPublic()\n        PrivateKey privateKey = kp.getPrivate()\n\n        def claims = new DefaultClaims([iss: 'joe', exp: later(), 'https://example.com/is_root': true])\n\n        String jwt = Jwts.builder().claims().add(claims).and().signWith(privateKey, alg).compact()\n\n        def token = Jwts.parser().verifyWith(publicKey).build().parse(jwt)\n\n        assertEquals([alg: alg.getId()], token.header)\n        assertEquals(claims, token.payload)\n    }\n\n    static void testHmac(MacAlgorithm alg) {\n\n        //create random signing key for testing:\n        SecretKey key = alg.key().build()\n\n        def claims = new DefaultClaims([iss: 'joe', exp: later(), 'https://example.com/is_root': true])\n\n        String jwt = Jwts.builder().claims().add(claims).and().signWith(key, alg).compact()\n\n        def token = Jwts.parser().verifyWith(key).build().parse(jwt)\n\n        assertEquals([alg: alg.getId()], token.header)\n        assertEquals(claims, token.payload)\n    }\n\n    static void testEC(io.jsonwebtoken.security.SignatureAlgorithm alg, boolean verifyWithPrivateKey = false) {\n        testEC(alg, TestKeys.forAlgorithm(alg).pair, verifyWithPrivateKey)\n    }\n\n    static void testEC(io.jsonwebtoken.security.SignatureAlgorithm alg, KeyPair pair, boolean verifyWithPrivateKey = false) {\n\n        PublicKey publicKey = pair.getPublic()\n        PrivateKey privateKey = pair.getPrivate()\n\n        def claims = new DefaultClaims([iss: 'joe', exp: later(), 'https://example.com/is_root': true])\n\n        String jwt = Jwts.builder().claims().add(claims).and().signWith(privateKey, alg).compact()\n\n        def key = publicKey\n        if (verifyWithPrivateKey) {\n            key = privateKey\n        }\n\n        def token = Jwts.parser().verifyWith(key).build().parse(jwt)\n\n        assertEquals([alg: alg.getId()], token.header)\n        assertEquals(claims, token.payload)\n\n    }\n}\n\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/LocatorAdapterTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\n\nimport io.jsonwebtoken.impl.DefaultJweHeader\nimport io.jsonwebtoken.impl.DefaultJwsHeader\nimport org.junit.Test\n\nimport static org.junit.Assert.assertNull\nimport static org.junit.Assert.assertSame\n\nclass LocatorAdapterTest {\n\n    @Test\n    void testJwtHeader() {\n        Header input = Jwts.header().build()\n        def locator = new LocatorAdapter() {\n            @Override\n            protected Object doLocate(Header header) {\n                return header\n            }\n        }\n        assertSame input, locator.locate(input as Header /* force Groovy to avoid signature erasure */)\n    }\n\n    @Test\n    void testJwtHeaderWithoutOverride() {\n        Header input = Jwts.header().build()\n        Locator locator = new LocatorAdapter() {}\n        assertNull locator.locate(input as Header /* force Groovy to avoid signature erasure */)\n    }\n\n    @Test\n    void testJwsHeader() {\n        Header input = new DefaultJwsHeader([:])\n        Locator locator = new LocatorAdapter() {\n            @Override\n            protected Object locate(JwsHeader header) {\n                return header\n            }\n        }\n        assertSame input, locator.locate(input as Header /* force Groovy to avoid signature erasure */)\n    }\n\n    @Test\n    void testJwsHeaderWithoutOverride() {\n        Header input = new DefaultJwsHeader([:])\n        Locator locator = new LocatorAdapter() {}\n        assertNull locator.locate(input as Header)\n    }\n\n    @Test\n    void testJweHeader() {\n        JweHeader input = new DefaultJweHeader([:])\n        def locator = new LocatorAdapter() {\n            @Override\n            protected Object locate(JweHeader header) {\n                return header\n            }\n        }\n        assertSame input, locator.locate(input as Header)\n    }\n\n    @Test\n    void testJweHeaderWithoutOverride() {\n        JweHeader input = new DefaultJweHeader([:])\n        def locator = new LocatorAdapter() {}\n        assertNull locator.locate(input as Header)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/RFC7515AppendixETest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.impl.DefaultJwtParser\nimport io.jsonwebtoken.impl.RfcTests\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.io.Serializer\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass RFC7515AppendixETest {\n\n    static final Serializer<Map<String, ?>> serializer = Services.get(Serializer)\n    static final Deserializer<Map<String, ?>> deserializer = Services.get(Deserializer)\n\n    static byte[] ser(def value) {\n        ByteArrayOutputStream baos = new ByteArrayOutputStream(512)\n        serializer.serialize(value, baos)\n        return baos.toByteArray()\n    }\n\n    static <T> T deser(String s) {\n        T t = deserializer.deserialize(Streams.reader(s)) as T\n        return t\n    }\n\n    @Test\n    void test() {\n\n        String headerString = RfcTests.stripws('''\n        {\"alg\":\"none\",\n         \"crit\":[\"http://example.invalid/UNDEFINED\"],\n         \"http://example.invalid/UNDEFINED\":true\n        }''')\n        Map<String, ?> header = deser(headerString)\n        String b64url = Encoders.BASE64URL.encode(ser(header))\n\n        String jws = b64url + '.RkFJTA.'\n\n        try {\n            Jwts.parser().unsecured().build().parse(jws)\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = String.format(DefaultJwtParser.CRIT_UNSECURED_MSG, header)\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n\n    @Test\n    void testProtected() {\n\n        // The RFC test case above shows an Unprotected header using the 'crit' header, but this isn't allowed per\n        // their own language in https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11.  To assert that\n        // the invalid crit values can't be used in Protected headers either, we amend the above test to represent\n        // that scenario as well:\n\n        // 'alg' here indicates a protected header, so we should get a different exception message compared to\n        // the test above\n        String critVal = 'http://example.invalid/UNDEFINED'\n        String headerString = RfcTests.stripws(\"\"\"\n        {\"alg\":\"HS256\",\n         \"crit\":[\"$critVal\"],\n         \"$critVal\":true\n        }\"\"\")\n        Map<String, ?> header = deser(headerString)\n        String b64url = Encoders.BASE64URL.encode(ser(header))\n\n        String jws = b64url + '.RkFJTA.fakesignature' // needed to parse a JWS properly\n\n        try {\n            Jwts.parser().unsecured().build().parse(jws)\n            fail()\n        } catch (UnsupportedJwtException expected) {\n            String msg = String.format(DefaultJwtParser.CRIT_UNSUPPORTED_MSG, critVal, critVal, header)\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/RFC7797Test.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.impl.DefaultJwsHeader\nimport io.jsonwebtoken.impl.DefaultJwtParser\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.Serializer\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\n/**\n * Test cases for https://datatracker.ietf.org/doc/html/rfc7797 functionality.\n */\nclass RFC7797Test {\n\n    @Test\n    void testJwe() {\n        try {\n            Jwts.builder().content('hello').encryptWith(TestKeys.A128GCM, Jwts.ENC.A128GCM)\n                    .encodePayload(false) // not allowed with JWE\n                    .compact()\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = 'Payload encoding may not be disabled for JWEs, only JWSs.'\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testUnprotectedJwt() {\n        try {\n            Jwts.builder().content('hello')\n                    .encodePayload(false) // not allowed with JWT\n                    .compact()\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = 'Payload encoding may not be disabled for unprotected JWTs, only JWSs.'\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void parseSignedContentBytes() {\n\n        def key = TestKeys.HS256\n\n        byte[] content = Strings.utf8('$.02') // https://datatracker.ietf.org/doc/html/rfc7797#section-4.2\n\n        String s = Jwts.builder().signWith(key).content(content).encodePayload(false).compact()\n\n        // But verify with 3 types of sources: string, byte array, and two different kinds of InputStreams:\n        InputStream asByteInputStream = Streams.of(content)\n        InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))\n\n        for (def payload : [content, asByteInputStream, asBufferedInputStream]) {\n            def parser = Jwts.parser().verifyWith(key).build()\n            def jws\n            if (payload instanceof byte[]) {\n                jws = parser.parseSignedContent(s, (byte[]) payload)\n            } else {\n                jws = parser.parseSignedContent(s, (InputStream) payload)\n            }\n            // When the supplied unencodedPayload is not a byte array or a ByteArrayInputStream, we can't know how\n            // big the payload stream might be, and we don't want to pull it all into memory, so the JWS payload\n            // body will be empty.  So we only assert the payload contents when we can get the bytes:\n            if (payload instanceof byte[] || payload instanceof ByteArrayInputStream) {\n                assertArrayEquals content, jws.getPayload()\n            } else {\n                assertArrayEquals Bytes.EMPTY, jws.getPayload()\n            }\n        }\n    }\n\n    @Test\n    void parseSignedClaimsBytes() {\n\n        def key = TestKeys.HS256\n\n        def claims = Jwts.claims().subject('me').build()\n\n        ByteArrayOutputStream out = new ByteArrayOutputStream()\n        Services.get(Serializer).serialize(claims, out)\n        byte[] content = out.toByteArray()\n\n        String s = Jwts.builder().signWith(key).content(content).encodePayload(false).compact()\n\n        // But verify with 3 types of sources: string, byte array, and two different kinds of InputStreams:\n        InputStream asByteInputStream = Streams.of(content)\n        InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))\n\n        for (def payload : [content, asByteInputStream, asBufferedInputStream]) {\n            def parser = Jwts.parser().verifyWith(key).build()\n            def jws\n            if (payload instanceof byte[]) {\n                jws = parser.parseSignedClaims(s, (byte[]) payload)\n            } else {\n                jws = parser.parseSignedClaims(s, (InputStream) payload)\n            }\n            assertEquals claims, jws.getPayload()\n        }\n    }\n\n    @Test\n    void parseSignedContentByteArrayInputStream() {\n\n        def key = TestKeys.HS256\n\n        byte[] content = Strings.utf8('$.02') // https://datatracker.ietf.org/doc/html/rfc7797#section-4.2\n        InputStream contentStream = Streams.of(content)\n\n        String s = Jwts.builder().signWith(key).content(contentStream).encodePayload(false).compact()\n\n        // But verify with 3 types of sources: byte array, and two different kinds of InputStreams:\n        InputStream asByteInputStream =Streams.of(content)\n        InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(content))\n\n        for (def payload : [content, asByteInputStream, asBufferedInputStream]) {\n            def parser = Jwts.parser().verifyWith(key).build()\n            def jws\n            if (payload instanceof byte[]) {\n                jws = parser.parseSignedContent(s, (byte[]) payload)\n            } else {\n                jws = parser.parseSignedContent(s, (InputStream) payload)\n            }\n            // When the supplied unencodedPayload is not a byte array or a ByteArrayInputStream, we can't know how\n            // big the payload stream might be, and we don't want to pull it all into memory, so the JWS payload\n            // body will be empty.  So we only assert the payload contents when we can get the bytes:\n            if (payload instanceof byte[] || payload instanceof ByteArrayInputStream) {\n                assertArrayEquals content, jws.getPayload()\n            } else {\n                assertArrayEquals Bytes.EMPTY, jws.getPayload()\n            }\n        }\n    }\n\n    @Test\n    void payloadStreamThatDoesNotSupportMark() {\n        def key = TestKeys.HS256\n        String s = 'Hello JJWT'\n        byte[] data = Strings.utf8(s)\n        InputStream stream = new ByteArrayInputStream(data) {\n            @Override\n            boolean markSupported() {\n                return false\n            }\n\n            @Override\n            void mark(int readAheadLimit) {\n                throw new UnsupportedOperationException(\"Not supported.\")\n            }\n        }\n\n        // compact/sign shouldn't fail, should still compute signature:\n        String compact = Jwts.builder().content(stream).signWith(key).encodePayload(false).compact()\n\n        // signature still verified:\n        def jwt = Jwts.parser().verifyWith(key).build().parseSignedContent(compact, data)\n        assertEquals 'HS256', jwt.header.getAlgorithm()\n        assertEquals s, Strings.utf8(jwt.getPayload())\n    }\n\n    @Test\n    void testClaimsPayload() {\n\n        def key = TestKeys.HS256\n\n        byte[] payload = Strings.utf8('{\"sub\":\"me\"}')\n\n        String s = Jwts.builder().signWith(key).content(payload).encodePayload(false).compact()\n\n        def jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(s, payload)\n\n        assertEquals 'me', jws.getPayload().getSubject()\n    }\n\n    /**\n     * Asserts that, even if the parser builder is not explicitly called with .critical(\"b64\") to indicate B64 payloads\n     * are supported, that a call to .parse*Jws(String, unencodedPayload) still works.  Just the fact that the\n     * overloaded method is called explicitly means they want B64 payload support, so it should still 'just work'\n     * even if .critical isn't configured.\n     */\n    @Test\n    void critUnspecifiedOnParserBuilder() {\n\n        def key = TestKeys.HS256\n\n        byte[] payload = Strings.utf8('{\"sub\":\"me\"}')\n\n        String s = Jwts.builder().signWith(key).content(payload).encodePayload(false).compact()\n\n        def jws = Jwts.parser().verifyWith(key) // .critical(\"b64\") is not called, should still work:\n                .build().parseSignedClaims(s, payload)\n\n        assertEquals 'me', jws.getPayload().getSubject()\n    }\n\n    @Test\n    void testEmptyBytesPayload() {\n        try {\n            Jwts.builder().content(Bytes.EMPTY).encodePayload(false).signWith(TestKeys.HS256).compact()\n            fail()\n        } catch (IllegalStateException expected) {\n            String msg = \"'b64' Unencoded payload option has been specified, but payload is empty.\"\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testParseContentWithEmptyBytesPayload() {\n        try {\n            Jwts.parser().verifyWith(TestKeys.HS256).build().parseSignedContent('whatever', Bytes.EMPTY) // <-- empty\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = 'unencodedPayload argument cannot be null or empty.'\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testParseClaimsWithEmptyBytesPayload() {\n        try {\n            Jwts.parser().verifyWith(TestKeys.HS256).build().parseSignedClaims('whatever', Bytes.EMPTY) // <-- empty\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = 'unencodedPayload argument cannot be null or empty.'\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testParseContentWithoutPayload() {\n\n        def key = TestKeys.HS256\n\n        byte[] payload = Strings.utf8('$.02') // https://datatracker.ietf.org/doc/html/rfc7797#section-4.2\n\n        // s is an unencoded-payload JWS\n        String s = Jwts.builder().signWith(key).content(payload).encodePayload(false).compact()\n        def expectedHeader = [alg: 'HS256', b64: false, crit: ['b64']]\n\n        // try to parse it as a 'normal' JWS (without supplying the payload):\n        try {\n            Jwts.parser().verifyWith(key).critical().add(DefaultJwsHeader.B64.id).and().build()\n                    .parseSignedContent(s) // <-- no payload supplied\n            fail()\n        } catch (io.jsonwebtoken.security.SignatureException expected) {\n            String msg = String.format(DefaultJwtParser.B64_MISSING_PAYLOAD, expectedHeader)\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testParseClaimsWithoutPayload() {\n\n        def key = TestKeys.HS256\n\n        byte[] payload = Strings.utf8('$.02') // https://datatracker.ietf.org/doc/html/rfc7797#section-4.2\n\n        // s is an unencoded-payload JWS\n        String s = Jwts.builder().signWith(key).content(payload).encodePayload(false).compact()\n\n        def expectedHeader = [alg: 'HS256', b64: false, crit: ['b64']]\n\n        // try to parse it as a 'normal' JWS (without supplying the payload):\n        try {\n            Jwts.parser().verifyWith(key).critical().add(DefaultJwsHeader.B64.id).and().build()\n                    .parseSignedClaims(s) // <-- no payload supplied\n            fail()\n        } catch (io.jsonwebtoken.security.SignatureException expected) {\n            String msg = String.format(DefaultJwtParser.B64_MISSING_PAYLOAD, expectedHeader)\n            assertEquals msg, expected.message\n        }\n    }\n\n    /**\n     * Asserts that, as long as a non-detached unencoded payload does not have period characters in it, it can\n     * be parsed 'normally' via normal JWS signature verification logic.  It does this by using the the payload's\n     * UTF-8 bytes instead of relying on a user-supplied unencodedPayload byte array.\n     */\n    @Test\n    void testNonDetachedContent() {\n\n        def key = TestKeys.HS256\n\n        String payload = 'foo'\n\n        // create a non-detached unencoded JWS:\n        String s = Jwts.builder().signWith(key).content(payload).encodePayload(false).compact()\n\n        def jws = Jwts.parser().verifyWith(key).critical().add(DefaultJwsHeader.B64.id).and().build()\n                .parseSignedContent(s) // <--- parse normally, without calling parseSignedContent(s, unencodedPayload)\n\n        assertArrayEquals Strings.utf8(payload), jws.getPayload()\n    }\n\n    @Test\n    void testNonDetachedClaims() {\n\n        def key = TestKeys.HS256\n\n        // create a non-detached unencoded JWS:\n        String s = Jwts.builder().signWith(key).subject('me').encodePayload(false).compact()\n\n        def jws = Jwts.parser().verifyWith(key).critical().add(DefaultJwsHeader.B64.id).and().build()\n                .parseSignedClaims(s) // <--- parse normally, without calling parseSignedClaims(s, unencodedPayload)\n\n        assertEquals 'me', jws.getPayload().getSubject()\n    }\n\n    @Test\n    void testDecompression() {\n\n        def key = TestKeys.HS256\n\n        byte[] content = Strings.utf8('hello world')\n\n        for (def zip : Jwts.ZIP.get().values()) {\n\n            ByteArrayOutputStream out = new ByteArrayOutputStream()\n            OutputStream cos = zip.compress(out); cos.write(content); cos.close()\n            def compressed = out.toByteArray()\n\n            // create a detached unencoded JWS that is compressed:\n            String s = Jwts.builder().signWith(key).content(content).encodePayload(false)\n                    .compressWith(zip)\n                    .compact()\n\n            // But verify with 3 types of sources: byte array, and two different kinds of InputStreams:\n            InputStream asByteInputStream = Streams.of(compressed)\n            InputStream asBufferedInputStream = new BufferedInputStream(Streams.of(compressed))\n\n            for (def payload : [compressed, asByteInputStream, asBufferedInputStream]) {\n                def parser = Jwts.parser().verifyWith(key).build()\n                def jws\n                if (payload instanceof byte[]) {\n                    jws = parser.parseSignedContent(s, (byte[]) payload)\n                } else {\n                    jws = parser.parseSignedContent(s, (InputStream) payload)\n                }\n                // When the supplied unencodedPayload is not a byte array or a ByteArrayInputStream, we can't know how\n                // big the payload stream might be, and we don't want to pull it all into memory, so the JWS payload\n                // body will be empty.  So we only assert the payload contents when we can get the bytes:\n                if (payload instanceof byte[] || payload instanceof ByteArrayInputStream) {\n                    assertArrayEquals content, jws.getPayload()\n                } else {\n                    assertArrayEquals Bytes.EMPTY, jws.getPayload()\n                }\n            }\n        }\n    }\n\n    /**\n     * Safe decompression of an unencoded payload using a SigningKeyResolver (SKR) is not possible because the SKR\n     * only verifies signatures after payloads are enabled, and signatures need to be verified before the payload\n     * is trusted.  And decompressing an unsecured payload is a security risk.\n     */\n    @SuppressWarnings('GrDeprecatedAPIUsage')\n    @Test\n    void testDecompressionWhenSigningKeyResolverIsUsed() {\n\n        def key = TestKeys.HS256\n\n        byte[] payload = Strings.utf8('hello world')\n\n        def zip = Jwts.ZIP.DEF\n\n        // create a detached unencoded JWS that is compressed:\n        String s = Jwts.builder().content(payload).encodePayload(false)\n                .compressWith(zip).signWith(key).compact()\n\n        // now try to parse a compressed unencoded using a signing key resolver:\n        try {\n            Jwts.parser()\n                    .setSigningKeyResolver(new SigningKeyResolverAdapter() {\n                        @Override\n                        Key resolveSigningKey(JwsHeader header, byte[] content) {\n                            return key\n                        }\n                    })\n                    .build()\n                    .parseSignedContent(s, payload)\n            fail()\n        } catch (UnsupportedJwtException expected) {\n            String msg = String.format(DefaultJwtParser.B64_DECOMPRESSION_MSG, zip.id)\n            assertEquals msg, expected.message\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/RsaSigningKeyResolverAdapterTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.security.Keys\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass RsaSigningKeyResolverAdapterTest {\n\n    @Test\n    void testResolveClaimsSigningKeyWithRsaKey() {\n\n        def alg = SignatureAlgorithm.RS256\n\n        def pair = Keys.keyPairFor(alg)\n\n        def compact = Jwts.builder().claim('foo', 'bar').signWith(pair.private, alg).compact()\n\n        Jws<Claims> jws = Jwts.parser().setSigningKey(pair.public).build().parseSignedClaims(compact)\n\n        try {\n            new SigningKeyResolverAdapter().resolveSigningKey(jws.header, jws.payload)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals \"The default resolveSigningKey(JwsHeader, Claims) implementation cannot be used for asymmetric key algorithms (RSA, Elliptic Curve).  Override the resolveSigningKey(JwsHeader, Claims) method instead and return a Key instance appropriate for the RS256 algorithm.\", iae.message\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/SignatureAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.Keys\nimport io.jsonwebtoken.security.SignatureException\nimport io.jsonwebtoken.security.WeakKeyException\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.security.Key\nimport java.security.PrivateKey\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\nimport java.security.spec.ECParameterSpec\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass SignatureAlgorithmTest {\n\n    @Test\n    void testNames() {\n        def algNames = ['HS256', 'HS384', 'HS512', 'RS256', 'RS384', 'RS512',\n                        'ES256', 'ES384', 'ES512', 'PS256', 'PS384', 'PS512', 'NONE']\n\n        for (String name : algNames) {\n            testName(name)\n        }\n    }\n\n    private static void testName(String name) {\n        def alg = SignatureAlgorithm.forName(name);\n        def namedAlg = name as SignatureAlgorithm //Groovy type coercion FTW!\n        assertTrue alg == namedAlg\n        assert alg.description != null && alg.description != \"\"\n    }\n\n    @Test(expected = SignatureException)\n    void testUnrecognizedAlgorithmName() {\n        SignatureAlgorithm.forName('whatever')\n    }\n\n    @Test\n    void testIsHmac() {\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg.name().startsWith(\"HS\")) {\n                assertTrue alg.isHmac()\n            } else {\n                assertFalse alg.isHmac()\n            }\n        }\n    }\n\n    @Test\n    void testHmacFamilyName() {\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg.name().startsWith(\"HS\")) {\n                assertEquals alg.getFamilyName(), \"HMAC\"\n            }\n        }\n    }\n\n    @Test\n    void testIsRsa() {\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg.getDescription().startsWith(\"RSASSA\")) {\n                assertTrue alg.isRsa()\n            } else {\n                assertFalse alg.isRsa()\n            }\n        }\n    }\n\n    @Test\n    void testRsaFamilyName() {\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg.name().startsWith(\"RS\") || alg.name().startsWith(\"PS\")) {\n                assertEquals alg.getFamilyName(), \"RSA\"\n            }\n        }\n    }\n\n    @Test\n    void testIsEllipticCurve() {\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg.name().startsWith(\"ES\")) {\n                assertTrue alg.isEllipticCurve()\n            } else {\n                assertFalse alg.isEllipticCurve()\n            }\n        }\n    }\n\n    @Test\n    void testEllipticCurveFamilyName() {\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg.name().startsWith(\"ES\")) {\n                assertEquals alg.getFamilyName(), \"ECDSA\"\n            }\n        }\n    }\n\n    @Test\n    void testIsJdkStandard() {\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg.name().startsWith(\"PS\") || alg == SignatureAlgorithm.NONE) {\n                assertFalse alg.isJdkStandard()\n            } else {\n                assertTrue alg.isJdkStandard()\n            }\n        }\n    }\n\n    @Test\n    void testGetMinKeyLength() {\n        for(SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n            if (alg == SignatureAlgorithm.NONE) {\n                assertEquals 0, alg.getMinKeyLength()\n            } else {\n                if (alg.isRsa()) {\n                    assertEquals 2048, alg.getMinKeyLength()\n                } else {\n                    int num = alg.name().substring(2, 5).toInteger()\n                    if (alg == SignatureAlgorithm.ES512) {\n                        num = 521\n                    }\n                    assertEquals num, alg.getMinKeyLength()\n                }\n            }\n        }\n    }\n\n    @Test\n    void testForSigningKeyNullArgument() {\n        try {\n            SignatureAlgorithm.forSigningKey(null)\n        } catch (InvalidKeyException expected) {\n            assertEquals 'Key argument cannot be null.', expected.message\n        }\n    }\n\n    @Test\n    void testForSigningKeyInvalidType() {\n        def key = new Key() {\n            @Override\n            String getAlgorithm() {\n                return null\n            }\n\n            @Override\n            String getFormat() {\n                return null\n            }\n\n            @Override\n            byte[] getEncoded() {\n                return new byte[0]\n            }\n        }\n\n        try {\n            SignatureAlgorithm.forSigningKey(key)\n            fail()\n        } catch (InvalidKeyException expected) {\n            assertTrue expected.getMessage().startsWith(\"JWT standard signing algorithms require either 1) a \" +\n                    \"SecretKey for HMAC-SHA algorithms or 2) a private RSAKey for RSA algorithms or 3) a private \" +\n                    \"ECKey for Elliptic Curve algorithms.  The specified key is of type \")\n        }\n    }\n\n    @Test\n    void testForSigningKeySecretKeyWeakKey() {\n        try {\n            SignatureAlgorithm.forSigningKey(new SecretKeySpec(new byte[1], 'HmacSHA256'))\n            fail()\n        } catch (WeakKeyException expected) {\n        }\n    }\n\n    @Test\n    void testForSigningKeySecretKeyHappyPath() {\n        for(SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n            int numBytes = alg.minKeyLength / 8 as int\n            assertEquals alg, SignatureAlgorithm.forSigningKey(Keys.hmacShaKeyFor(new byte[numBytes]))\n        }\n    }\n\n    @Test\n    void testForSigningKeyRSAWeakKey() {\n\n        RSAPrivateKey key = createMock(RSAPrivateKey)\n        BigInteger modulus = bigInteger(1024)\n        expect(key.getModulus()).andStubReturn(modulus)\n\n        replay key\n\n        try {\n            SignatureAlgorithm.forSigningKey(key)\n            fail()\n        } catch (WeakKeyException expected) {\n        }\n\n        verify key\n    }\n\n    @Test\n    void testForSigningKeyRSAHappyPath() {\n\n        for(SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.name().startsWith(\"RS\") }) {\n\n            int heuristicKeyLength = (alg == SignatureAlgorithm.RS512 ? 4096 : (alg == SignatureAlgorithm.RS384 ? 3072 : 2048))\n\n            RSAPrivateKey key = createMock(RSAPrivateKey)\n            BigInteger modulus = bigInteger(heuristicKeyLength)\n            expect(key.getModulus()).andStubReturn(modulus)\n\n            replay key\n\n            assertEquals alg, SignatureAlgorithm.forSigningKey(key)\n\n            verify key\n        }\n    }\n\n    @Test\n    void testForSigningKeyECWeakKey() {\n\n        ECPrivateKey key = createMock(ECPrivateKey)\n        ECParameterSpec spec = createMock(ECParameterSpec)\n        BigInteger order = bigInteger(128)\n        expect(key.getParams()).andStubReturn(spec)\n        expect(spec.getOrder()).andStubReturn(order)\n\n        replay key, spec\n\n        try {\n            SignatureAlgorithm.forSigningKey(key)\n            fail()\n        } catch (WeakKeyException expected) {\n        }\n\n        verify key, spec\n    }\n\n    @Test\n    void testForSigningKeyECHappyPath() {\n\n        for(SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            ECPrivateKey key = createMock(ECPrivateKey)\n            ECParameterSpec spec = createMock(ECParameterSpec)\n            BigInteger order = bigInteger(alg.minKeyLength)\n            expect(key.getParams()).andStubReturn(spec)\n            expect(spec.getOrder()).andStubReturn(order)\n\n            replay key, spec\n\n            assertEquals alg, SignatureAlgorithm.forSigningKey(key)\n\n            verify key, spec\n        }\n    }\n\n    @Test\n    void testAssertValidSigningKeyWithNoneAlgorithm() {\n        Key key = createMock(Key)\n        try {\n            SignatureAlgorithm.NONE.assertValidSigningKey(key)\n            fail()\n        } catch (InvalidKeyException expected) {\n            assertEquals \"The 'NONE' signature algorithm does not support cryptographic keys.\" as String, expected.message\n        }\n    }\n\n    @Test\n    void testAssertValidHmacSigningKeyHappyPath() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            int numBits = alg.minKeyLength\n            int numBytes = numBits / 8 as int\n            expect(key.getEncoded()).andReturn(new byte[numBytes])\n            expect(key.getAlgorithm()).andReturn(alg.jcaName)\n\n            replay key\n\n            alg.assertValidSigningKey(key)\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidHmacSigningKeyNotSecretKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            Key key = createMock(Key)\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'HMAC signing keys must be SecretKey instances.', expected.message\n            }\n        }\n    }\n\n    @Test\n    void testAssertValidHmacSigningKeyNullBytes() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            expect(key.getEncoded()).andReturn(null)\n\n            replay key\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The signing key's encoded bytes cannot be null.\", expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidHmacSigningKeyMissingAlgorithm() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])\n            expect(key.getAlgorithm()).andReturn(null)\n\n            replay key\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The signing key's algorithm cannot be null.\", expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test // https://github.com/jwtk/jjwt/issues/381\n    void testAssertValidHmacSigningKeyCaseInsensitiveJcaName() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            int numBits = alg.minKeyLength\n            int numBytes = numBits / 8 as int\n            expect(key.getEncoded()).andReturn(new byte[numBytes])\n            expect(key.getAlgorithm()).andReturn(alg.jcaName.toUpperCase()) // <-- upper case, non standard JCA name\n\n            replay key\n\n            alg.assertValidSigningKey(key)\n\n            verify key\n        }\n    }\n\n    @Test // https://github.com/jwtk/jjwt/issues/588\n    void assertAssertValidHmacSigningKeyCaseOidAlgorithmName() {\n        for (SignatureAlgorithm alg in EnumSet.complementOf(EnumSet.of(SignatureAlgorithm.NONE))) {\n            assertNotNull(alg.pkcs12Name)\n        }\n\n        for (SignatureAlgorithm alg in SignatureAlgorithm.values().findAll {it.isHmac()}) {\n\n            int numBits = alg.minKeyLength\n            int numBytes = numBits / 8 as int\n\n            SecretKey key = createMock(SecretKey)\n            expect(key.getEncoded()).andReturn(new byte[numBytes])\n            expect(key.getAlgorithm()).andReturn(alg.pkcs12Name)\n\n            replay key\n\n            alg.assertValidSigningKey(key)\n\n            verify key\n        }\n\n        for (SignatureAlgorithm alg in SignatureAlgorithm.values().findAll {!it.isHmac()}) {\n            assertEquals(\"For non HmacSHA-keys the name when loaded from pkcs12 key store is identical to the jcaName\",\n                    alg.jcaName, alg.pkcs12Name)\n        }\n    }\n\n    @Test\n    void testAssertValidHmacSigningKeyUnsupportedAlgorithm() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])\n            expect(key.getAlgorithm()).andReturn('AES')\n\n            replay key\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The signing key's algorithm 'AES' does not equal a valid HmacSHA* algorithm \" +\n                        \"name and cannot be used with ${alg.name()}.\" as String, expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidHmacSigningKeyInsufficientKeyLength() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            int numBits = alg.minKeyLength - 8 //8 bits shorter than expected\n            int numBytes = numBits / 8 as int\n            expect(key.getEncoded()).andReturn(new byte[numBytes])\n            expect(key.getAlgorithm()).andReturn(alg.jcaName)\n\n            replay key\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The signing key's size is $numBits bits which is not secure enough for the \" +\n                        \"${alg.name()} algorithm.  The JWT JWA Specification \" +\n                        \"(RFC 7518, Section 3.2) states that keys used with ${alg.name()} MUST have a size >= \" +\n                        \"${alg.minKeyLength} bits (the key size must be greater than or equal to the hash output \" +\n                        \"size).  Consider using the ${Keys.class.getName()} class's 'secretKeyFor(\" +\n                        \"SignatureAlgorithm.${alg.name()})' method to create a key guaranteed to be secure enough \" +\n                        \"for ${alg.name()}.  See https://tools.ietf.org/html/rfc7518#section-3.2 for \" +\n                        \"more information.\" as String, expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidECSigningKeyHappyPath() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            ECPrivateKey key = createMock(ECPrivateKey)\n            ECParameterSpec spec = createMock(ECParameterSpec)\n            BigInteger order = bigInteger(alg.minKeyLength)\n            expect(key.getParams()).andStubReturn(spec)\n            expect(spec.getOrder()).andStubReturn(order)\n\n            replay key, spec\n\n            alg.assertValidSigningKey(key)\n\n            verify key, spec\n        }\n    }\n\n    @Test\n    void testAssertValidECSigningNotPrivateKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            ECPublicKey key = createMock(ECPublicKey)\n\n            replay key\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'ECDSA signing keys must be PrivateKey instances.', expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidECSigningKeyNotECKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            PrivateKey key = createMock(PrivateKey)\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'ECDSA signing keys must be ECKey instances.', expected.message\n            }\n        }\n    }\n\n    @Test\n    void testAssertValidECSigningKeyInsufficientKeyLength() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            ECPrivateKey key = createMock(ECPrivateKey)\n            ECParameterSpec spec = createMock(ECParameterSpec)\n            BigInteger order = bigInteger(alg.minKeyLength - 8) //one less byte\n            expect(key.getParams()).andStubReturn(spec)\n            expect(spec.getOrder()).andStubReturn(order)\n\n            replay key, spec\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The signing key's size (ECParameterSpec order) is ${order.bitLength()} bits \" +\n                        \"which is not secure enough for the ${alg.name()} algorithm.  The JWT JWA Specification \" +\n                        \"(RFC 7518, Section 3.4) states that keys used with ${alg.name()} MUST have a size >= \" +\n                        \"${alg.minKeyLength} bits.  Consider using the ${Keys.class.getName()} class's \" +\n                        \"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed \" +\n                        \"to be secure enough for ${alg.name()}.  See \" +\n                        \"https://tools.ietf.org/html/rfc7518#section-3.4 for more information.\" as String, expected.message\n            }\n\n            verify key, spec\n        }\n    }\n\n    @Test\n    void testAssertValidRSASigningKeyHappyPath() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {\n\n            RSAPrivateKey key = createMock(RSAPrivateKey)\n            BigInteger modulus = bigInteger(alg.minKeyLength)\n            expect(key.getModulus()).andStubReturn(modulus)\n\n            replay key\n\n            alg.assertValidSigningKey(key)\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidRSASigningNotPrivateKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {\n\n            RSAPublicKey key = createMock(RSAPublicKey)\n\n            replay key\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'RSA signing keys must be PrivateKey instances.', expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidRSASigningKeyNotRSAKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {\n\n            PrivateKey key = createMock(PrivateKey)\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'RSA signing keys must be RSAKey instances.', expected.message\n            }\n        }\n    }\n\n    @Test\n    void testAssertValidRSASigningKeyInsufficientKeyLength() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {\n\n            String section = alg.name().startsWith(\"P\") ? \"3.5\" : \"3.3\"\n\n            RSAPrivateKey key = createMock(RSAPrivateKey)\n            BigInteger modulus = bigInteger(alg.minKeyLength - 8) // 1 less byte\n            expect(key.getModulus()).andStubReturn(modulus)\n\n            replay key\n\n            try {\n                alg.assertValidSigningKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The signing key's size is ${modulus.bitLength()} bits which is not secure \" +\n                        \"enough for the ${alg.name()} algorithm.  The JWT JWA Specification \" +\n                        \"(RFC 7518, Section ${section}) states that keys used with ${alg.name()} MUST have a size >= \" +\n                        \"${alg.minKeyLength} bits.  Consider using the ${Keys.class.getName()} class's \" +\n                        \"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed \" +\n                        \"to be secure enough for ${alg.name()}.  See \" +\n                        \"https://tools.ietf.org/html/rfc7518#section-${section} for more information.\" as String, expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidVerificationKeyWithNoneAlgorithm() {\n        Key key = createMock(Key)\n        try {\n            SignatureAlgorithm.NONE.assertValidVerificationKey(key)\n            fail()\n        } catch (InvalidKeyException expected) {\n            assertEquals \"The 'NONE' signature algorithm does not support cryptographic keys.\" as String, expected.message\n        }\n    }\n\n    @Test\n    void testAssertValidHmacVerificationKeyHappyPath() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            int numBits = alg.minKeyLength\n            int numBytes = numBits / 8 as int\n            expect(key.getEncoded()).andReturn(new byte[numBytes])\n            expect(key.getAlgorithm()).andReturn(alg.jcaName)\n\n            replay key\n\n            alg.assertValidVerificationKey(key)\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidHmacVerificationKeyNotSecretKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            Key key = createMock(Key)\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'HMAC verification keys must be SecretKey instances.', expected.message\n            }\n        }\n    }\n\n    @Test\n    void testAssertValidHmacVerificationKeyNullBytes() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            expect(key.getEncoded()).andReturn(null)\n\n            replay key\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The verification key's encoded bytes cannot be null.\", expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidHmacVerificationKeyMissingAlgorithm() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])\n            expect(key.getAlgorithm()).andReturn(null)\n\n            replay key\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The verification key's algorithm cannot be null.\", expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidHmacVerificationKeyUnsupportedAlgorithm() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            expect(key.getEncoded()).andReturn(new byte[alg.minKeyLength / 8 as int])\n            expect(key.getAlgorithm()).andReturn('AES')\n\n            replay key\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The verification key's algorithm 'AES' does not equal a valid HmacSHA* algorithm \" +\n                        \"name and cannot be used with ${alg.name()}.\" as String, expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidHmacVerificationKeyInsufficientKeyLength() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isHmac() }) {\n\n            SecretKey key = createMock(SecretKey)\n            int numBits = alg.minKeyLength - 8 // 8 bits (1 byte) less than required\n            int numBytes = numBits / 8 as int\n            expect(key.getEncoded()).andReturn(new byte[numBytes])\n            expect(key.getAlgorithm()).andReturn(alg.jcaName)\n\n            replay key\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The verification key's size is $numBits bits which is not secure enough for the \" +\n                        \"${alg.name()} algorithm.  The JWT JWA Specification \" +\n                        \"(RFC 7518, Section 3.2) states that keys used with ${alg.name()} MUST have a size >= \" +\n                        \"${alg.minKeyLength} bits (the key size must be greater than or equal to the hash output \" +\n                        \"size).  Consider using the ${Keys.class.getName()} class's 'secretKeyFor(\" +\n                        \"SignatureAlgorithm.${alg.name()})' method to create a key guaranteed to be secure enough \" +\n                        \"for ${alg.name()}.  See https://tools.ietf.org/html/rfc7518#section-3.2 for \" +\n                        \"more information.\" as String, expected.message\n            }\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidECVerificationKeyHappyPath() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            ECPrivateKey key = createMock(ECPrivateKey)\n            ECParameterSpec spec = createMock(ECParameterSpec)\n            BigInteger order = bigInteger(alg.minKeyLength)\n            expect(key.getParams()).andStubReturn(spec)\n            expect(spec.getOrder()).andStubReturn(order)\n\n            replay key, spec\n\n            alg.assertValidVerificationKey(key)\n\n            verify key, spec\n        }\n    }\n\n    @Test\n    void testAssertValidECVerificationKeyNotECKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            PrivateKey key = createMock(PrivateKey)\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'ECDSA verification keys must be ECKey instances.', expected.message\n            }\n        }\n    }\n\n    @Test\n    void testAssertValidECVerificationKeyInsufficientKeyLength() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isEllipticCurve() }) {\n\n            ECPrivateKey key = createMock(ECPrivateKey)\n            ECParameterSpec spec = createMock(ECParameterSpec)\n            BigInteger order = bigInteger(alg.minKeyLength - 8) // 1 less byte\n            expect(key.getParams()).andStubReturn(spec)\n            expect(spec.getOrder()).andStubReturn(order)\n\n            replay key, spec\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The verification key's size (ECParameterSpec order) is ${order.bitLength()} bits \" +\n                        \"which is not secure enough for the ${alg.name()} algorithm.  The JWT JWA Specification \" +\n                        \"(RFC 7518, Section 3.4) states that keys used with ${alg.name()} MUST have a size >= \" +\n                        \"${alg.minKeyLength} bits.  Consider using the ${Keys.class.getName()} class's \" +\n                        \"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed \" +\n                        \"to be secure enough for ${alg.name()}.  See \" +\n                        \"https://tools.ietf.org/html/rfc7518#section-3.4 for more information.\" as String, expected.message\n            }\n\n            verify key, spec\n        }\n    }\n\n    @Test\n    void testAssertValidRSAVerificationKeyHappyPath() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {\n\n            RSAPrivateKey key = createMock(RSAPrivateKey)\n            BigInteger modulus = bigInteger(alg.minKeyLength)\n            expect(key.getModulus()).andStubReturn(modulus)\n\n            replay key\n\n            alg.assertValidVerificationKey(key)\n\n            verify key\n        }\n    }\n\n    @Test\n    void testAssertValidRSAVerificationKeyNotRSAKey() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {\n\n            PrivateKey key = createMock(PrivateKey)\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals 'RSA verification keys must be RSAKey instances.', expected.message\n            }\n        }\n    }\n\n    @Test\n    void testAssertValidRSAVerificationKeyInsufficientKeyLength() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values().findAll { it.isRsa() }) {\n\n            String section = alg.name().startsWith(\"P\") ? \"3.5\" : \"3.3\"\n\n            RSAPrivateKey key = createMock(RSAPrivateKey)\n            BigInteger modulus = bigInteger(alg.minKeyLength - 8) //one less byte\n            expect(key.getModulus()).andStubReturn(modulus)\n\n            replay key\n\n            try {\n                alg.assertValidVerificationKey(key)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals \"The verification key's size is ${modulus.bitLength()} bits which is not secure enough \" +\n                        \"for the ${alg.name()} algorithm.  The JWT JWA Specification \" +\n                        \"(RFC 7518, Section ${section}) states that keys used with ${alg.name()} MUST have a size >= \" +\n                        \"${alg.minKeyLength} bits.  Consider using the ${Keys.class.getName()} class's \" +\n                        \"'keyPairFor(SignatureAlgorithm.${alg.name()})' method to create a key pair guaranteed \" +\n                        \"to be secure enough for ${alg.name()}.  See \" +\n                        \"https://tools.ietf.org/html/rfc7518#section-${section} for more information.\" as String, expected.message\n            }\n\n            verify key\n        }\n    }\n\n    private static BigInteger bigInteger(int bitLength) {\n        BigInteger result = null\n        // https://github.com/jwtk/jjwt/issues/552:\n        //\n        // This method just used to be simply:\n        //\n        //     return new BigInteger(bitLength, 0, Random.newInstance())\n        //\n        // However, this was unbearably slow due to the 2nd certainty argument of the BigInteger constructor. Since\n        // we don't need ideal randomness for this method (we're just using it as a mock value),\n        // the following will just loop until we get a mock value that equals the required length:\n        //\n        while (result == null || result.bitLength() != bitLength) {\n            result = new BigInteger(bitLength, Random.newInstance())\n        }\n        return result\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/StubService.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken\n\ninterface StubService {\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/AbstractProtectedHeaderTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.security.Randoms\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.security.EcPrivateJwk\nimport io.jsonwebtoken.security.EcPublicJwk\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.SecretJwk\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass AbstractProtectedHeaderTest {\n\n    private static DefaultProtectedHeader h(Map<String, ?> m) {\n        return new DefaultProtectedHeader(DefaultProtectedHeader.PARAMS, m)\n    }\n\n    @Test\n    void testKeyId() {\n        def kid = 'foo'\n        def header = h([kid: kid])\n        assertEquals kid, header.get('kid')\n        assertEquals kid, header.getKeyId()\n    }\n\n    @Test\n    void testKeyIdNonString() {\n        try {\n            h([kid: 42])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWT header 'kid' (Key ID) value: 42. Unsupported value type. \" +\n                    \"Expected: java.lang.String, found: java.lang.Integer\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testJku() {\n        URI uri = URI.create('https://github.com')\n        def header = Jwts.header().add('alg', 'foo').jwkSetUrl(uri).build() as DefaultProtectedHeader\n        assertEquals uri.toString(), header.get('jku')\n        assertEquals uri, header.getJwkSetUrl()\n    }\n\n    @Test\n    void testJkuString() {\n        String url = 'https://google.com'\n        URI uri = URI.create(url)\n        def header = h([jku: url])\n        assertEquals url, header.get('jku')\n        assertEquals uri, header.getJwkSetUrl()\n    }\n\n    @Test\n    void testJkuNonString() {\n        try {\n            h([jku: 42])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWT header 'jku' (JWK Set URL) value: 42. Values must be either String or \" +\n                    \"java.net.URI instances. Value type found: java.lang.Integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testJwkNull() {\n        def header = h([jwk: null])\n        assertNull header.getJwk()\n    }\n\n    @Test\n    void testJwkWithEmptyMap() {\n        def header = h([jwk: [:]])\n        assertNull header.getJwk()\n    }\n\n    @Test\n    void testJwkWithoutMap() {\n        try {\n            h([jwk: 42])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWT header ${DefaultProtectedHeader.JWK} value: 42. \" +\n                    \"JWK must be a Map<String,?> (JSON Object). Type found: java.lang.Integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testJwkWithJwk() {\n        EcPrivateJwk jwk = Jwks.builder().ecKeyPair(TestKeys.ES256.pair).build()\n        EcPublicJwk pubJwk = jwk.toPublicJwk()\n        def header = h([jwk: pubJwk])\n        assertEquals pubJwk, header.getJwk()\n    }\n\n    @Test\n    void testJwkWithMap() {\n        EcPrivateJwk jwk = Jwks.builder().ecKeyPair(TestKeys.ES256.pair).build()\n        EcPublicJwk pubJwk = jwk.toPublicJwk()\n        Map<String, ?> m = new LinkedHashMap<>(pubJwk)\n        def header = h([jwk: m])\n        assertEquals pubJwk, header.getJwk()\n    }\n\n    @Test\n    void testJwkWithBadMapKeys() {\n        def m = [kty: 'oct', 42: \"hello\"]\n        try {\n            h([jwk: m])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWT header 'jwk' (JSON Web Key) value: {kty=oct, 42=hello}. JWK map keys \" +\n                    \"must be Strings. Encountered key '42' of type java.lang.Integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testJwkWithSecretJwk() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.HS256).build()\n        try {\n            h([jwk: jwk])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWT header 'jwk' (JSON Web Key) value: {alg=HS256, kty=oct, k=<redacted>}. \" +\n                    \"Value must be a Public JWK, not a Secret JWK.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testJwkWithPrivateJwk() {\n        EcPrivateJwk jwk = Jwks.builder().ecKeyPair(TestKeys.ES256.pair).build()\n        try {\n            h([jwk: jwk])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWT header 'jwk' (JSON Web Key) value: {kty=EC, crv=P-256, \" +\n                    \"x=ZWF7HQuzPoW_HarfomiU-HCMELJ486IzskTXL5fwuy4, y=Hf3WL_YAGj1XCSa5HSIAFsItY-SQNjRb1TdKQFEb3oU, \" +\n                    \"d=<redacted>}. Value must be a Public JWK, not an EC Private JWK.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testX509Url() {\n        URI uri = URI.create('https://google.com')\n        def header = h([x5u: uri])\n        assertEquals uri.toString(), header.get('x5u')\n        assertEquals uri, header.getX509Url()\n    }\n\n    @Test\n    void testX509UrlString() { //test canonical/idiomatic conversion\n        String url = 'https://google.com'\n        URI uri = URI.create(url)\n        def header = h([x5u: url])\n        assertEquals url, header.get('x5u')\n        assertEquals uri, header.getX509Url()\n    }\n\n    @Test\n    void testX509CertChain() {\n        def bundle = TestKeys.RS256\n        List<String> encodedCerts = Collections.of(Encoders.BASE64.encode(bundle.cert.getEncoded()))\n        def header = h([x5c: bundle.chain])\n        assertEquals bundle.chain, header.getX509Chain()\n        assertEquals encodedCerts, header.get('x5c')\n    }\n\n    @Test\n    void testX509CertSha1Thumbprint() {\n        byte[] thumbprint = new byte[16] // simulate\n        Randoms.secureRandom().nextBytes(thumbprint)\n        String encoded = Encoders.BASE64URL.encode(thumbprint)\n        def header = h([x5t: thumbprint])\n        assertArrayEquals thumbprint, header.getX509Sha1Thumbprint()\n        assertEquals encoded, header.get('x5t')\n    }\n\n    @Test\n    void testX509CertSha256Thumbprint() {\n        byte[] thumbprint = new byte[32] // simulate\n        Randoms.secureRandom().nextBytes(thumbprint)\n        String encoded = Encoders.BASE64URL.encode(thumbprint)\n        def header = h(['x5t#S256': thumbprint])\n        assertArrayEquals thumbprint, header.getX509Sha256Thumbprint()\n        assertEquals encoded, header.get('x5t#S256')\n    }\n\n    @Test\n    void testCritical() {\n        Set<String> crits = Collections.setOf('foo', 'bar')\n        def header = Jwts.header().add('alg', 'HS256').add('foo', 'value1').add('bar', 'value2')\n                .critical().add(crits).and().build() as DefaultProtectedHeader\n        assertEquals crits, header.getCritical()\n    }\n\n    @Test\n    void testCritNull() {\n        def header = h([crit: null])\n        assertNull header.getCritical()\n    }\n\n    @Test\n    void testCritEmpty() {\n        def header = h([crit: []])\n        assertNull header.getCritical()\n    }\n\n    @Test\n    void testCritSingleValue() {\n        def header = h([crit: 'foo'])\n        assertEquals([\"foo\"] as Set, header.get('crit'))\n        assertEquals([\"foo\"] as Set, header.getCritical())\n    }\n\n    @Test\n    void testCritArray() {\n        String[] crit = [\"exp\"] as String[]\n        def header = h([crit: crit])\n        assertEquals([\"exp\"] as Set, header.get('crit'))\n        assertEquals([\"exp\"] as Set, header.getCritical())\n    }\n\n    @Test\n    void testCritList() {\n        List<String> crit = [\"exp\"] as List<String>\n        def header = h([crit: crit])\n        assertEquals([\"exp\"] as Set, header.get('crit'))\n        assertEquals([\"exp\"] as Set, header.getCritical())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/AndroidBase64CodecTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\n@Deprecated //remove just before 1.0.0 release\nclass AndroidBase64CodecTest {\n\n    @Test\n    void testEncode() {\n        String input = 'Hello 世界'\n        byte[] bytes = input.getBytes(Strings.UTF_8)\n        String encoded = new AndroidBase64Codec().encode(bytes)\n        assertEquals 'SGVsbG8g5LiW55WM', encoded\n    }\n\n    @Test\n    void testDecode() {\n        String encoded = 'SGVsbG8g5LiW55WM' // Hello 世界\n        byte[] bytes = new AndroidBase64Codec().decode(encoded)\n        String result = new String(bytes, Strings.UTF_8)\n        assertEquals 'Hello 世界', result\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/Base64CodecTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\n@Deprecated //remove just before 1.0.0 release\nclass Base64CodecTest {\n\n    @Test\n    void testEncodeDecode() {\n\n        String s = \"Hello 世界\"\n\n        def codec = new Base64Codec()\n\n        String encoded = codec.encode(s)\n\n        assertEquals s, codec.decodeToString(encoded)\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/Base64UrlCodecTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\nimport static org.junit.Assert.*\n\n@Deprecated //remove just before 1.0.0 release\nclass Base64UrlCodecTest {\n\n    @Test\n    void testEncodeDecode() {\n\n        String s = \"Hello 世界\"\n\n        def codec = new Base64UrlCodec()\n\n        String base64url = codec.encode(s.getBytes(Strings.UTF_8))\n\n        byte[] decoded = codec.decode(base64url)\n\n        String result = new String(decoded, Strings.UTF_8)\n\n        assertEquals s, result\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultClaimsBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertTrue\n\nclass DefaultClaimsBuilderTest {\n\n    DefaultClaimsBuilder builder\n\n    @Before\n    void setUp() {\n        this.builder = new DefaultClaimsBuilder()\n    }\n\n    @Test\n    void testPut() {\n        builder.put('foo', 'bar')\n        assertEquals 'bar', builder.build().foo\n    }\n\n    @Test\n    void testPutAll() {\n        def m = [foo: 'bar', hello: 'world']\n        builder.putAll(m)\n        assertEquals m, builder.build()\n    }\n\n    @Test\n    void testPutAllEmpty() {\n        builder.putAll([:])\n        assertTrue builder.build().isEmpty()\n    }\n\n    @Test\n    void testPutAllNull() {\n        builder.putAll((Map<String,Object>)null)\n        assertTrue builder.build().isEmpty()\n    }\n\n    @Test\n    void testRemove() {\n        builder.put('foo', 'bar')\n        assertEquals 'bar', builder.build().foo\n        builder.remove('foo')\n        assertTrue builder.build().isEmpty()\n    }\n\n    @Test\n    void testClear() {\n        def m = [foo: 'bar', hello: 'world']\n        builder.putAll(m)\n        assertEquals m, builder.build()\n\n        builder.clear()\n        assertTrue builder.build().isEmpty()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultClaimsTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.Claims\nimport io.jsonwebtoken.RequiredTypeException\nimport io.jsonwebtoken.impl.lang.Parameter\nimport io.jsonwebtoken.lang.DateFormats\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultClaimsTest {\n\n    Claims claims\n\n    @Before\n    void setup() {\n        claims = new DefaultClaims()\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_Null_Success() {\n        claims.put(\"aNull\", null)\n        Object result = claims.get(\"aNull\", Integer.class)\n        assertNull(result)\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_Exception() {\n        claims.put(\"anInteger\", new Integer(5))\n        try {\n            claims.get(\"anInteger\", String.class)\n            fail()\n        } catch (RequiredTypeException e) {\n            assertEquals(\n                String.format(DefaultClaims.CONVERSION_ERROR_MSG, 'class java.lang.Integer', 'class java.lang.String'),\n                e.getMessage()\n            )\n        }\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_Integer_Success() {\n        def expected = new Integer(5)\n        claims.put(\"anInteger\", expected)\n        Object result = claims.get(\"anInteger\", Integer.class)\n        assertEquals(expected, result)\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_Long_Success() {\n        def expected = new Long(123)\n        claims.put(\"aLong\", expected)\n        Object result = claims.get(\"aLong\", Long.class)\n        assertEquals(expected, result)\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_LongWithInteger_Success() {\n        // long value that fits inside an Integer\n        def expected = new Long(Integer.MAX_VALUE - 100)\n        // deserialized as an Integer from JSON\n        // (type information is not available during parsing)\n        claims.put(\"smallLong\", expected.intValue())\n        // should still be available as Long\n        Object result = claims.get(\"smallLong\", Long.class)\n        assertEquals(expected, result)\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_ShortWithInteger_Success() {\n        def expected = new Short((short) 42)\n        claims.put(\"short\", expected.intValue())\n        Object result = claims.get(\"short\", Short.class)\n        assertEquals(expected, result)\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_ShortWithBigInteger_Exception() {\n        claims.put(\"tooBigForShort\", ((int) Short.MAX_VALUE) + 42)\n        try {\n            claims.get(\"tooBigForShort\", Short.class)\n            fail(\"getClaim() shouldn't silently lose precision.\")\n        } catch (RequiredTypeException e) {\n            assertEquals(\n                    e.getMessage(),\n                    String.format(DefaultClaims.CONVERSION_ERROR_MSG, 'class java.lang.Integer', 'class java.lang.Short')\n            )\n        }\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_ShortWithSmallInteger_Exception() {\n        claims.put(\"tooSmallForShort\", ((int) Short.MIN_VALUE) - 42)\n        try {\n            claims.get(\"tooSmallForShort\", Short.class)\n            fail(\"getClaim() shouldn't silently lose precision.\")\n        } catch (RequiredTypeException e) {\n            assertEquals(\n                    e.getMessage(),\n                    String.format(DefaultClaims.CONVERSION_ERROR_MSG, 'class java.lang.Integer', 'class java.lang.Short')\n            )\n        }\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_ByteWithInteger_Success() {\n        def expected = new Byte((byte) 42)\n        claims.put(\"byte\", expected.intValue())\n        Object result = claims.get(\"byte\", Byte.class)\n        assertEquals(expected, result)\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_ByteWithBigInteger_Exception() {\n        claims.put(\"tooBigForByte\", ((int) Byte.MAX_VALUE) + 42)\n        try {\n            claims.get(\"tooBigForByte\", Byte.class)\n            fail(\"getClaim() shouldn't silently lose precision.\")\n        } catch (RequiredTypeException e) {\n            assertEquals(\n                    e.getMessage(),\n                    String.format(DefaultClaims.CONVERSION_ERROR_MSG, 'class java.lang.Integer', 'class java.lang.Byte')\n            )\n        }\n    }\n\n    @Test\n    void testGetClaimWithRequiredType_ByteWithSmallInteger_Exception() {\n        claims.put(\"tooSmallForByte\", ((int) Byte.MIN_VALUE) - 42)\n        try {\n            claims.get(\"tooSmallForByte\", Byte.class)\n            fail(\"getClaim() shouldn't silently lose precision.\")\n        } catch (RequiredTypeException e) {\n            assertEquals(\n                    e.getMessage(),\n                    String.format(DefaultClaims.CONVERSION_ERROR_MSG, 'class java.lang.Integer', 'class java.lang.Byte')\n            )\n        }\n    }\n\n    @Test\n    void testGetRequiredIntegerFromLong() {\n        claims.put('foo', Long.valueOf(Integer.MAX_VALUE))\n        assertEquals Integer.MAX_VALUE, claims.get('foo', Integer.class) as Integer\n    }\n\n    @Test\n    void testGetRequiredIntegerWouldCauseOverflow() {\n        claims.put('foo', Long.MAX_VALUE)\n        try {\n            claims.get('foo', Integer.class)\n        } catch (RequiredTypeException expected) {\n            String msg = \"Claim 'foo' value is too large or too small to be represented as a java.lang.Integer instance (would cause numeric overflow).\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testGetRequiredDateFromNull() {\n        Date date = claims.get(\"aDate\", Date.class)\n        assertNull date\n    }\n\n    @Test\n    void testGetRequiredDateFromDate() {\n        def expected = new Date()\n        claims.put(\"aDate\", expected)\n        Date result = claims.get(\"aDate\", Date.class)\n        assertEquals expected, result\n    }\n\n    @Test\n    void testGetRequiredDateFromCalendar() {\n        def c = Calendar.getInstance(TimeZone.getTimeZone(\"UTC\"))\n        def expected = c.getTime()\n        claims.put(\"aDate\", c)\n        Date result = claims.get('aDate', Date.class)\n        assertEquals expected, result\n    }\n\n    @Test\n    void testGetRequiredDateFromLong() {\n        def expected = new Date()\n        // note that Long is stored in claim\n        claims.put(\"aDate\", expected.getTime())\n        Date result = claims.get(\"aDate\", Date.class)\n        assertEquals expected, result\n    }\n\n    @Test\n    void testGetRequiredDateFromIso8601String() {\n        def expected = new Date()\n        claims.put(\"aDate\", DateFormats.formatIso8601(expected))\n        Date result = claims.get(\"aDate\", Date.class)\n        assertEquals expected, result\n    }\n\n    @Test\n    void testGetRequiredDateFromIso8601MillisString() {\n        def expected = new Date()\n        claims.put(\"aDate\", DateFormats.formatIso8601(expected, true))\n        Date result = claims.get(\"aDate\", Date.class)\n        assertEquals expected, result\n    }\n\n    @Test\n    void testGetRequiredDateFromInvalidIso8601String() {\n        Date d = new Date()\n        String s = d.toString()\n        claims.put('aDate', s)\n        try {\n            claims.get('aDate', Date.class)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String expectedMsg = \"Cannot create Date from 'aDate' value '$s'. Cause: \" +\n                    \"String value is not a JWT NumericDate, nor is it ISO-8601-formatted. All heuristics \" +\n                    \"exhausted. Cause: Unparseable date: \\\"$s\\\"\"\n            assertEquals expectedMsg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testToSpecDateWithNull() {\n        assertNull claims.get(Claims.EXPIRATION)\n        assertNull claims.getExpiration()\n        assertNull claims.get(Claims.ISSUED_AT)\n        assertNull claims.getIssuedAt()\n        assertNull claims.get(Claims.NOT_BEFORE)\n        assertNull claims.getNotBefore()\n    }\n\n    @Test\n    void testGetSpecDateWithLongString() {\n        Date orig = new Date()\n        long millis = orig.getTime()\n        long seconds = millis / 1000L as long\n        Date expected = new Date(seconds * 1000L)\n        String secondsString = '' + seconds\n        claims.put(Claims.EXPIRATION, secondsString)\n        claims.put(Claims.ISSUED_AT, secondsString)\n        claims.put(Claims.NOT_BEFORE, secondsString)\n        assertEquals expected, claims.getExpiration()\n        assertEquals expected, claims.getIssuedAt()\n        assertEquals expected, claims.getNotBefore()\n        assertEquals seconds, claims.get(Claims.EXPIRATION)\n        assertEquals seconds, claims.get(Claims.ISSUED_AT)\n        assertEquals seconds, claims.get(Claims.NOT_BEFORE)\n    }\n\n    @Test\n    void testGetSpecDateWithLong() {\n        Date orig = new Date()\n        long millis = orig.getTime()\n        long seconds = millis / 1000L as long\n        Date expected = new Date(seconds * 1000L)\n        claims.put(Claims.EXPIRATION, seconds)\n        claims.put(Claims.ISSUED_AT, seconds)\n        claims.put(Claims.NOT_BEFORE, seconds)\n        assertEquals expected, claims.getExpiration()\n        assertEquals expected, claims.getIssuedAt()\n        assertEquals expected, claims.getNotBefore()\n        assertEquals seconds, claims.get(Claims.EXPIRATION)\n        assertEquals seconds, claims.get(Claims.ISSUED_AT)\n        assertEquals seconds, claims.get(Claims.NOT_BEFORE)\n    }\n\n    @Test\n    void testGetSpecDateWithIso8601String() {\n        Date orig = new Date()\n        long millis = orig.getTime()\n        long seconds = millis / 1000L as long\n        String s = DateFormats.formatIso8601(orig)\n        claims.put(Claims.EXPIRATION, s)\n        claims.put(Claims.ISSUED_AT, s)\n        claims.put(Claims.NOT_BEFORE, s)\n        assertEquals orig, claims.getExpiration()\n        assertEquals orig, claims.getIssuedAt()\n        assertEquals orig, claims.getNotBefore()\n        assertEquals seconds, claims.get(Claims.EXPIRATION)\n        assertEquals seconds, claims.get(Claims.ISSUED_AT)\n        assertEquals seconds, claims.get(Claims.NOT_BEFORE)\n    }\n\n    @Test\n    void testGetSpecDateWithDate() {\n        Date orig = new Date()\n        long millis = orig.getTime()\n        long seconds = millis / 1000L as long\n        claims.put(Claims.EXPIRATION, orig)\n        claims.put(Claims.ISSUED_AT, orig)\n        claims.put(Claims.NOT_BEFORE, orig)\n        assertEquals orig, claims.getExpiration()\n        assertEquals orig, claims.getIssuedAt()\n        assertEquals orig, claims.getNotBefore()\n        assertEquals seconds, claims.get(Claims.EXPIRATION)\n        assertEquals seconds, claims.get(Claims.ISSUED_AT)\n        assertEquals seconds, claims.get(Claims.NOT_BEFORE)\n    }\n\n    @Test\n    void testGetSpecDateWithCalendar() {\n        Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(\"UTC\"))\n        Date date = cal.getTime()\n        long millis = date.getTime()\n        long seconds = millis / 1000L as long\n        claims.put(Claims.EXPIRATION, cal)\n        claims.put(Claims.ISSUED_AT, cal)\n        claims.put(Claims.NOT_BEFORE, cal)\n        assertEquals date, claims.getExpiration()\n        assertEquals date, claims.getIssuedAt()\n        assertEquals date, claims.getNotBefore()\n        assertEquals seconds, claims.get(Claims.EXPIRATION)\n        assertEquals seconds, claims.get(Claims.ISSUED_AT)\n        assertEquals seconds, claims.get(Claims.NOT_BEFORE)\n    }\n\n    @Test\n    void testToSpecDateWithDate() {\n        long millis = System.currentTimeMillis()\n        Date d = new Date(millis)\n        claims.put('exp', d)\n        assertEquals d, claims.getExpiration()\n    }\n\n    void trySpecDateNonDate(Parameter<?> param) {\n        def val = new Object() { @Override String toString() {return 'hi'} }\n        try {\n            claims.put(param.getId(), val)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            String msg = \"Invalid JWT Claims $param value: hi. Cannot create Date from object of type io.jsonwebtoken.impl.DefaultClaimsTest\\$1.\"\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testSpecDateFromNonDateObject() {\n        trySpecDateNonDate(DefaultClaims.EXPIRATION)\n        trySpecDateNonDate(DefaultClaims.ISSUED_AT)\n        trySpecDateNonDate(DefaultClaims.NOT_BEFORE)\n    }\n\n    @Test\n    void testGetClaimExpiration_Success() {\n        def now = new Date(System.currentTimeMillis())\n        claims.put('exp', now)\n        Date expected = claims.get(\"exp\", Date.class)\n        assertEquals(expected, claims.getExpiration())\n    }\n\n    @Test\n    void testGetClaimIssuedAt_Success() {\n        def now = new Date(System.currentTimeMillis())\n        claims.put('iat', now)\n        Date expected = claims.get(\"iat\", Date.class)\n        assertEquals(expected, claims.getIssuedAt())\n    }\n\n    @Test\n    void testGetClaimNotBefore_Success() {\n        def now = new Date(System.currentTimeMillis())\n        claims.put('nbf', now)\n        Date expected = claims.get(\"nbf\", Date.class)\n        assertEquals(expected, claims.getNotBefore())\n    }\n\n    @Test\n    void testPutWithIat() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        claims.put('iat', now) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('iat') //conversion should have happened\n    }\n\n    @Test\n    void testPutAllWithIat() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        claims.putAll([iat: now]) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('iat') //conversion should have happened\n    }\n\n    @Test\n    void testConstructorWithIat() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        this.claims = new DefaultClaims([iat: now]) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('iat') //conversion should have happened\n    }\n\n    @Test\n    void testPutWithNbf() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        claims.put('nbf', now) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('nbf') //conversion should have happened\n    }\n\n    @Test\n    void testPutAllWithNbf() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        claims.putAll([nbf: now]) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('nbf') //conversion should have happened\n    }\n\n    @Test\n    void testConstructorWithNbf() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        this.claims = new DefaultClaims([nbf: now]) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('nbf') //conversion should have happened\n    }\n\n    @Test\n    void testPutWithExp() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        claims.put('exp', now) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('exp') //conversion should have happened\n    }\n\n    @Test\n    void testPutAllWithExp() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        claims.putAll([exp: now]) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('exp') //conversion should have happened\n    }\n\n    @Test\n    void testConstructorWithExp() {\n        long millis = System.currentTimeMillis()\n        long seconds = millis / 1000 as long\n        Date now = new Date(millis)\n        this.claims = new DefaultClaims([exp: now]) //this should convert 'now' to seconds since epoch\n        assertEquals seconds, claims.get('exp') //conversion should have happened\n    }\n\n    @Test\n    void testPutWithNonSpecDate() {\n        long millis = System.currentTimeMillis()\n        Date now = new Date(millis)\n        claims.put('foo', now)\n        assertEquals now, claims.get('foo') //conversion should NOT have occurred\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultHeaderTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNull\n\nclass DefaultHeaderTest {\n    \n    private DefaultHeader header\n\n    private static DefaultHeader h(Map<String, ?> m) {\n        return new DefaultHeader(m)\n    }\n    \n    @Test\n    void testType() {\n        header = h([typ: 'foo'])\n        assertEquals 'foo', header.getType()\n        assertEquals 'foo', header.get('typ')\n    }\n\n    @Test\n    void testContentType() {\n        header = h([cty: 'bar'])\n        // Per per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10, the raw header should have a\n        // compact form, but application developers shouldn't have to check for that all the time, so our getter has\n        // the normalized form:\n        assertEquals 'bar', header.get('cty') // raw compact form\n        assertEquals 'application/bar', header.getContentType() // getter normalized form\n    }\n\n    @Test\n    void testAlgorithm() {\n        header = h([alg: 'foo'])\n        assertEquals 'foo', header.getAlgorithm()\n        assertEquals 'foo', header.get('alg')\n    }\n\n    @Test\n    void testSetCompressionAlgorithm() {\n        header = h([zip: 'DEF'])\n        assertEquals \"DEF\", header.getCompressionAlgorithm()\n        assertEquals 'DEF', header.get('zip')\n    }\n\n    @SuppressWarnings('GrDeprecatedAPIUsage')\n    @Test\n    void testBackwardsCompatibleCompressionHeader() {\n        header = h([calg: 'DEF'])\n        assertEquals \"DEF\", header.getCompressionAlgorithm()\n        assertEquals 'DEF', header.get('calg')\n        assertNull header.get('zip')\n    }\n\n    @Test\n    void testGetName() {\n        def header = new DefaultHeader([:])\n        assertEquals 'JWT header', header.getName()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJweHeaderTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.impl.security.Randoms\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPublicJwk\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\nimport java.security.PublicKey\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPublicKey\nimport java.util.concurrent.atomic.AtomicInteger\n\nimport static org.junit.Assert.*\n\n/**\n * @since 0.12.0\n */\nclass DefaultJweHeaderTest {\n\n    private DefaultJweHeader header\n\n    private static DefaultJweHeader h(Map<String, ?> m) {\n        return new DefaultJweHeader(m)\n    }\n\n    @Test\n    void testEncryptionAlgorithm() {\n        assertEquals 'foo', h([enc: 'foo']).getEncryptionAlgorithm()\n        assertEquals 'bar', h([enc: 'bar']).getEncryptionAlgorithm()\n    }\n\n    @Test\n    void testGetName() {\n        assertEquals 'JWE header', new DefaultJweHeader([:]).getName()\n    }\n\n    @Test\n    void testEpkWithSecretJwk() {\n        def jwk = Jwks.builder().key(TestKeys.HS256).build()\n        def values = new LinkedHashMap(jwk) //extract values to remove JWK type\n        try {\n            h([epk: values])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'epk' (Ephemeral Public Key) value: {alg=HS256, kty=oct, k=<redacted>}. \" +\n                    \"Value must be a Public JWK, not a Secret JWK.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEpkWithPrivateJwk() {\n        def jwk = Jwks.builder().key(TestKeys.ES256.pair.private as ECPrivateKey).build()\n        def values = new LinkedHashMap(jwk) //extract values to remove JWK type\n        try {\n            h([epk: values])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'epk' (Ephemeral Public Key) value: {kty=EC, crv=P-256, \" +\n                    \"x=ZWF7HQuzPoW_HarfomiU-HCMELJ486IzskTXL5fwuy4, y=Hf3WL_YAGj1XCSa5HSIAFsItY-SQNjRb1TdKQFEb3oU, \" +\n                    \"d=<redacted>}. Value must be a Public JWK, not an EC Private JWK.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEpkWithRsaPublicJwk() {\n        def jwk = Jwks.builder().key(TestKeys.RS256.pair.public as RSAPublicKey).build()\n        def values = new LinkedHashMap(jwk) //extract values to remove JWK type\n        def epk = h([epk: values]).getEphemeralPublicKey()\n        assertTrue epk instanceof RsaPublicJwk\n        assertEquals(jwk, epk)\n    }\n\n    @Test\n    void testEpkWithEcPublicJwkValues() {\n        def jwk = Jwks.builder().key(TestKeys.ES256.pair.public as ECPublicKey).build()\n        def values = new LinkedHashMap(jwk) //extract values to remove JWK type\n        assertEquals jwk, h([epk: values]).get('epk')\n    }\n\n    @Test\n    void testEpkWithInvalidEcPublicJwk() {\n        def jwk = Jwks.builder().key(TestKeys.ES256.pair.public as ECPublicKey).build()\n        def values = new LinkedHashMap(jwk) // copy params so we can mutate\n        // We have a public JWK for a point on the curve, now swap out the x coordinate for something invalid:\n        values.put('x', 'Kg')\n        try {\n            h([epk: values])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'epk' (Ephemeral Public Key) value: {kty=EC, crv=P-256, x=Kg, \" +\n                    \"y=Hf3WL_YAGj1XCSa5HSIAFsItY-SQNjRb1TdKQFEb3oU}. EC JWK x,y coordinates do not exist on \" +\n                    \"elliptic curve 'P-256'. This could be due simply to an incorrectly-created JWK or possibly an \" +\n                    \"attempted Invalid Curve Attack (see https://safecurves.cr.yp.to/twist.html for more \" +\n                    \"information).\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEpkWithEcPublicJwk() {\n        def jwk = Jwks.builder().key(TestKeys.ES256.pair.public as ECPublicKey).build()\n        header = h([epk: jwk])\n        assertEquals jwk, header.get('epk')\n        assertEquals jwk, header.getEphemeralPublicKey()\n    }\n\n    @Test\n    void testEpkWithEdPublicJwk() {\n        def keys = TestKeys.EdEC.collect({it -> it.pair.public as PublicKey})\n        for(PublicKey key : keys) {\n            def jwk = Jwks.builder().key((PublicKey)key as PublicKey).build()\n            header = h([epk: jwk])\n            assertEquals jwk, header.get('epk')\n            assertEquals jwk, header.getEphemeralPublicKey()\n        }\n    }\n\n    @Test\n    void testAgreementPartyUInfo() {\n        String val = \"Party UInfo\"\n        byte[] info = val.getBytes(StandardCharsets.UTF_8)\n        assertArrayEquals info, h([apu: info]).getAgreementPartyUInfo()\n    }\n\n    @Test\n    void testAgreementPartyUInfoString() {\n        String val = \"Party UInfo\"\n        byte[] info = val.getBytes(StandardCharsets.UTF_8)\n        assertArrayEquals info, h([apu: info]).getAgreementPartyUInfo()\n    }\n\n    @Test\n    void testEmptyAgreementPartyUInfo() {\n        byte[] info = new byte[0]\n        assertNull h([apu: info]).getAgreementPartyUInfo()\n    }\n\n    @Test\n    void testEmptyAgreementPartyUInfoString() {\n        def val = '    '\n        assertNull h([apu: val]).getAgreementPartyUInfo()\n    }\n\n    @Test\n    void testAgreementPartyVInfo() {\n        String val = \"Party VInfo\"\n        byte[] info = Strings.utf8(val)\n        assertArrayEquals info, h([apv: info]).getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testAgreementPartyVInfoString() {\n        String val = \"Party VInfo\"\n        byte[] info = Strings.utf8(val)\n        assertArrayEquals info, h(apv: info).getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testEmptyAgreementPartyVInfo() {\n        byte[] info = new byte[0]\n        assertNull h([apv: info]).getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testEmptyAgreementPartyVInfoString() {\n        String s = '  '\n        header = h([apv: s])\n        assertNull header.getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testIv() {\n        byte[] bytes = new byte[12]\n        Randoms.secureRandom().nextBytes(bytes)\n        header = h([iv: bytes])\n        assertEquals Encoders.BASE64URL.encode(bytes), header.get('iv')\n        assertTrue MessageDigest.isEqual(bytes, header.getInitializationVector())\n    }\n\n    @Test\n    void testIvWithIncorrectSize() {\n        byte[] bytes = new byte[7]\n        Randoms.secureRandom().nextBytes(bytes)\n        try {\n            h([iv: bytes])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'iv' (Initialization Vector) value. \" +\n                    \"Byte array must be exactly 96 bits (12 bytes). Found 56 bits (7 bytes)\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testTag() {\n        byte[] bytes = new byte[16]\n        Randoms.secureRandom().nextBytes(bytes)\n        header = h([tag: bytes])\n        assertEquals Encoders.BASE64URL.encode(bytes), header.get('tag')\n        assertTrue MessageDigest.isEqual(bytes, header.getAuthenticationTag())\n    }\n\n    @Test\n    void testTagWithIncorrectSize() {\n        byte[] bytes = new byte[15]\n        Randoms.secureRandom().nextBytes(bytes)\n        try {\n            h([tag: bytes])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'tag' (Authentication Tag) value. \" +\n                    \"Byte array must be exactly 128 bits (16 bytes). Found 120 bits (15 bytes)\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testP2cByte() {\n        header = h([p2c: Byte.MAX_VALUE])\n        assertEquals 127, header.getPbes2Count()\n    }\n\n    @Test\n    void testP2cShort() {\n        header = h([p2c: Short.MAX_VALUE])\n        assertEquals 32767, header.getPbes2Count()\n    }\n\n    @Test\n    void testP2cInt() {\n        header = h([p2c: Integer.MAX_VALUE])\n        assertEquals 0x7fffffff as Integer, header.getPbes2Count()\n    }\n\n    @Test\n    void testP2cAtomicInteger() {\n        header = h([p2c: new AtomicInteger(Integer.MAX_VALUE)])\n        assertEquals 0x7fffffff as Integer, header.getPbes2Count()\n    }\n\n    @Test\n    void testP2cString() {\n        header = h([p2c: '100'])\n        assertEquals 100, header.getPbes2Count()\n    }\n\n    @Test\n    void testP2cZero() {\n        try {\n            h([p2c: 0])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'p2c' (PBES2 Count) value: 0. Value must be a positive integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testP2cNegative() {\n        try {\n            h([p2c: -1])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'p2c' (PBES2 Count) value: -1. Value must be a positive integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testP2cTooLarge() {\n        try {\n            h([p2c: Long.MAX_VALUE])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'p2c' (PBES2 Count) value: 9223372036854775807. \" +\n                    \"Value cannot be represented as a java.lang.Integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testP2cDecimal() {\n        double d = 42.2348423d\n        try {\n            h([p2c: d])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'p2c' (PBES2 Count) value: $d. \" +\n                    \"Value cannot be represented as a java.lang.Integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testPbe2SaltBytes() {\n        byte[] salt = new byte[32]\n        Randoms.secureRandom().nextBytes(salt)\n        header = h([p2s: salt])\n        assertEquals Encoders.BASE64URL.encode(salt), header.get('p2s')\n        assertArrayEquals salt, header.getPbes2Salt()\n    }\n\n    @Test\n    void pbe2SaltStringTest() {\n        byte[] salt = new byte[32]\n        Randoms.secureRandom().nextBytes(salt)\n        String val = Encoders.BASE64URL.encode(salt)\n        header = h([p2s: val])\n        //ensure that even though a Base64Url string was set, we get back a byte[]:\n        assertArrayEquals salt, header.getPbes2Salt()\n    }\n\n    @Test\n    void testPbe2SaltInputTooSmall() {\n        byte[] salt = new byte[7] // RFC requires a minimum of 64 bits (8 bytes), so we go 1 byte less\n        Randoms.secureRandom().nextBytes(salt)\n        String val = Encoders.BASE64URL.encode(salt)\n        try {\n            h([p2s: val])\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Invalid JWE header 'p2s' (PBES2 Salt Input) value: $val. \" +\n                    \"Byte array must be at least 64 bits (8 bytes). Found 56 bits (7 bytes)\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJweTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.AeadAlgorithm\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNotEquals\n\nclass DefaultJweTest {\n\n    @Test\n    void testToString() {\n        def alg = Jwts.ENC.A128CBC_HS256 as AeadAlgorithm\n        def key = alg.key().build()\n        String compact = Jwts.builder().claim('foo', 'bar').encryptWith(key, alg).compact()\n        def jwe = Jwts.parser().decryptWith(key).build().parseEncryptedClaims(compact)\n        String encodedIv = Encoders.BASE64URL.encode(jwe.initializationVector)\n        String encodedTag = Encoders.BASE64URL.encode(jwe.digest)\n        String expected = \"header={alg=dir, enc=A128CBC-HS256},payload={foo=bar},tag=$encodedTag,iv=$encodedIv\"\n        assertEquals expected, jwe.toString()\n    }\n\n    @Test\n    void testEqualsAndHashCode() {\n        def alg = Jwts.ENC.A128CBC_HS256 as AeadAlgorithm\n        def key = alg.key().build()\n        String compact = Jwts.builder().claim('foo', 'bar').encryptWith(key, alg).compact()\n        def parser = Jwts.parser().decryptWith(key).build()\n        def jwe1 = parser.parseEncryptedClaims(compact)\n        def jwe2 = parser.parseEncryptedClaims(compact)\n        assertNotEquals jwe1, 'hello' as String\n        assertEquals jwe1, jwe1\n        assertEquals jwe2, jwe2\n        assertEquals jwe1, jwe2\n        assertEquals jwe1.hashCode(), jwe2.hashCode()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwsHeaderTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass DefaultJwsHeaderTest {\n\n    @Test\n    void testGetName() {\n        def header = new DefaultJwsHeader([:])\n        assertEquals 'JWS header', header.getName()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwsTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.JwsHeader\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.io.Encoders\nimport org.junit.Test\n\nimport java.security.MessageDigest\n\nimport static org.junit.Assert.*\n\nclass DefaultJwsTest {\n\n    @Test\n    void testConstructor() {\n        JwsHeader header = new DefaultJwsHeader([:])\n        byte[] sig = Bytes.random(32)\n        String b64u = Encoders.BASE64URL.encode(sig)\n        def jws = new DefaultJws<String>(header, 'foo', sig, b64u)\n        assertSame jws.getHeader(), header\n        assertEquals jws.getPayload(), 'foo'\n        assertTrue MessageDigest.isEqual(sig, jws.getDigest())\n        assertEquals b64u, jws.getSignature()\n    }\n\n    @Test\n    void testToString() {\n        //create random signing key for testing:\n        def alg = Jwts.SIG.HS256\n        def key = alg.key().build()\n        String compact = Jwts.builder().claim('foo', 'bar').signWith(key, alg).compact()\n        int i = compact.lastIndexOf('.')\n        String signature = compact.substring(i + 1)\n        def jws = Jwts.parser().verifyWith(key).build().parseSignedClaims(compact)\n        assertEquals 'header={alg=HS256},payload={foo=bar},signature=' + signature, jws.toString()\n    }\n\n    @Test\n    void testEqualsAndHashCode() {\n        def alg = Jwts.SIG.HS256\n        def key = alg.key().build()\n        String compact = Jwts.builder().claim('foo', 'bar').signWith(key, alg).compact()\n        def parser = Jwts.parser().verifyWith(key).build()\n        def jws1 = parser.parseSignedClaims(compact)\n        def jws2 = parser.parseSignedClaims(compact)\n        assertNotEquals jws1, 'hello' as String\n        assertEquals jws1, jws1\n        assertEquals jws2, jws2\n        assertEquals jws1, jws2\n        assertEquals jws1.hashCode(), jws2.hashCode()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport io.jsonwebtoken.Claims\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.SignatureAlgorithm\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.io.TestSerializer\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.impl.security.Randoms\nimport io.jsonwebtoken.impl.security.TestKey\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.*\nimport io.jsonwebtoken.security.*\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\nimport java.security.Provider\nimport java.security.SecureRandom\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass DefaultJwtBuilderTest {\n\n    private static ObjectMapper objectMapper = new ObjectMapper()\n\n    private DefaultJwtBuilder builder\n\n    private static byte[] serialize(Map<String, ?> map) {\n        def serializer = Services.get(Serializer)\n        ByteArrayOutputStream out = new ByteArrayOutputStream(512)\n        serializer.serialize(map, out)\n        return out.toByteArray()\n    }\n\n    private static Map<String, ?> deser(byte[] data) {\n        def reader = Streams.reader(data)\n        Map<String, ?> m = Services.get(Deserializer).deserialize(reader) as Map<String, ?>\n        return m\n    }\n\n    @Before\n    void setUp() {\n        this.builder = new DefaultJwtBuilder()\n    }\n\n    @Test\n    void testSetProvider() {\n\n        Provider provider = createMock(Provider)\n\n        final boolean[] called = new boolean[1]\n\n        io.jsonwebtoken.security.SignatureAlgorithm alg = new io.jsonwebtoken.security.SignatureAlgorithm() {\n            @Override\n            byte[] digest(SecureRequest request) throws SignatureException, KeyException {\n                assertSame provider, request.getProvider()\n                called[0] = true\n                //simulate a digest:\n                byte[] bytes = new byte[32]\n                Randoms.secureRandom().nextBytes(bytes)\n                return bytes\n            }\n\n            @Override\n            boolean verify(VerifySecureDigestRequest request) throws SignatureException, KeyException {\n                throw new IllegalStateException(\"should not be called during build\")\n            }\n\n            @Override\n            KeyPairBuilder keyPair() {\n                throw new IllegalStateException(\"should not be called during build\")\n            }\n\n            @Override\n            String getId() {\n                return \"test\"\n            }\n        }\n\n        replay provider\n        def b = new DefaultJwtBuilder().provider(provider)\n                .setSubject('me').signWith(Jwts.SIG.HS256.key().build(), alg)\n        assertSame provider, b.provider\n        b.compact()\n        verify provider\n        assertTrue called[0]\n    }\n\n    @Test\n    void testSetSecureRandom() {\n\n        final SecureRandom random = new SecureRandom()\n\n        final boolean[] called = new boolean[1]\n\n        io.jsonwebtoken.security.SignatureAlgorithm alg = new io.jsonwebtoken.security.SignatureAlgorithm() {\n            @Override\n            byte[] digest(SecureRequest request) throws SignatureException, KeyException {\n                assertSame random, request.getSecureRandom()\n                called[0] = true\n                //simulate a digest:\n                byte[] bytes = new byte[32]\n                Randoms.secureRandom().nextBytes(bytes)\n                return bytes\n            }\n\n            @Override\n            boolean verify(VerifySecureDigestRequest request) throws SignatureException, KeyException {\n                throw new IllegalStateException(\"should not be called during build\")\n            }\n\n            @Override\n            KeyPairBuilder keyPair() {\n                throw new IllegalStateException(\"should not be called during build\")\n            }\n\n            @Override\n            String getId() {\n                return \"test\"\n            }\n        }\n\n        def b = new DefaultJwtBuilder().random(random)\n                .setSubject('me').signWith(Jwts.SIG.HS256.key().build(), alg)\n        assertSame random, b.secureRandom\n        b.compact()\n        assertTrue called[0]\n    }\n\n    @Test\n    void testSetHeader() {\n        def h = Jwts.header().add('foo', 'bar').build()\n        builder.setHeader(h)\n        assertEquals h, builder.headerBuilder.build()\n    }\n\n    @Test\n    void testSetHeaderFromMap() {\n        def m = [foo: 'bar']\n        builder.setHeader(m)\n        assertEquals builder.headerBuilder.build().foo, 'bar'\n    }\n\n    @Test\n    void testSetHeaderParams() {\n        def m = [a: 'b', c: 'd']\n        builder.setHeaderParams(m)\n        assertEquals builder.headerBuilder.build().a, 'b'\n        assertEquals builder.headerBuilder.build().c, 'd'\n    }\n\n    @Test\n    void testSetHeaderParam() {\n        builder.setHeaderParam('foo', 'bar')\n        assertEquals builder.headerBuilder.build().foo, 'bar'\n    }\n\n    @Test\n    void testSetClaims() {\n        Claims c = Jwts.claims().add('foo', 'bar').build()\n        builder.setClaims(c)\n        assertEquals c, builder.claimsBuilder\n    }\n\n    @Test\n    void testSetClaimsMap() {\n        def m = [foo: 'bar']\n        builder.setClaims(m)\n        assertEquals 1, builder.claimsBuilder.size()\n        assertTrue builder.claimsBuilder.containsKey('foo')\n        assertTrue builder.claimsBuilder.containsValue('bar')\n    }\n\n    @Test\n    void testAddClaims() {\n        def b = new DefaultJwtBuilder()\n        def c = Jwts.claims([initial: 'initial'])\n        b.claims().add(c)\n        def c2 = [foo: 'bar', baz: 'buz']\n        b.addClaims(c2)\n        assertEquals 'initial', b.claimsBuilder.get('initial')\n        assertEquals 'bar', b.claimsBuilder.get('foo')\n    }\n\n    @Test\n    void testAddClaimsWithoutInitializing() {\n        def b = new DefaultJwtBuilder()\n        def c = [foo: 'bar', baz: 'buz']\n        b.addClaims(c)\n        assertNotNull b.claimsBuilder\n        assertEquals c, b.claimsBuilder\n    }\n\n    @Test\n    void testClaim() {\n        def b = new DefaultJwtBuilder()\n        b.claim('foo', 'bar')\n        assertNotNull b.claimsBuilder\n        assertEquals b.claimsBuilder.size(), 1\n        assertEquals b.claimsBuilder.foo, 'bar'\n    }\n\n    @Test\n    void testExistingClaimsAndSetClaim() {\n        Claims c = Jwts.claims().add('foo', 'bar').build()\n        builder.claims().add(c)\n        assertEquals c, builder.claimsBuilder\n        assertEquals builder.claimsBuilder.size(), 1\n        assertEquals c.size(), 1\n        assertEquals builder.claimsBuilder.foo, 'bar'\n        assertEquals c.foo, 'bar'\n    }\n\n    @Test\n    void testRemoveClaimBySettingNullValue() {\n        def b = new DefaultJwtBuilder()\n        b.claim('foo', 'bar')\n        assertNotNull b.claimsBuilder\n        assertEquals b.claimsBuilder.size(), 1\n        assertEquals b.claimsBuilder.foo, 'bar'\n\n        b.claim('foo', null)\n        assertNotNull b.claimsBuilder\n        assertNull b.claimsBuilder.foo\n    }\n\n    @Test\n    void testCompactWithoutPayloadOrClaims() {\n        def header = Encoders.BASE64URL.encode(serialize(['alg': 'none']))\n        assertEquals \"$header..\" as String, new DefaultJwtBuilder().compact()\n    }\n\n    @Test\n    void testNullPayloadString() {\n        String payload = null\n        def header = Encoders.BASE64URL.encode(serialize(['alg': 'none']))\n        assertEquals \"$header..\" as String, builder.setPayload((String) payload).compact()\n    }\n\n    @Test\n    void testCompactWithBothPayloadAndClaims() {\n        try {\n            builder.setPayload('foo').claim('a', 'b').compact()\n            fail()\n        } catch (IllegalStateException ise) {\n            assertEquals ise.message, \"Both 'content' and 'claims' cannot be specified. Choose either one.\"\n        }\n    }\n\n    @Test\n    void testCompactWithJwsHeader() {\n        def b = new DefaultJwtBuilder()\n        b.header().keyId('a')\n        b.setPayload('foo')\n        def alg = SignatureAlgorithm.HS256\n        def key = Keys.secretKeyFor(alg)\n        b.signWith(key, alg)\n        String s1 = b.compact()\n        //ensure deprecated with(alg, key) produces the same result:\n        b.signWith(alg, key)\n        String s2 = b.compact()\n        assertEquals s1, s2\n    }\n\n    @Test\n    void testHeaderSerializationErrorException() {\n        def ex = new IOException('foo')\n        def ser = new TestSerializer(ex: ex)\n        def b = new DefaultJwtBuilder().json(ser)\n        try {\n            b.content('bar').compact()\n            fail()\n        } catch (SerializationException expected) {\n            assertEquals 'Cannot serialize JWT Header to JSON. Cause: foo', expected.getMessage()\n        }\n    }\n\n    @Test\n    void testCompactCompressionCodecJsonProcessingException() {\n        def ex = new IOException('dummy text')\n        def ser = new TestSerializer(ex: ex)\n        def b = new DefaultJwtBuilder()\n                .setSubject(\"Joe\") // ensures claims instance\n                .compressWith(Jwts.ZIP.DEF)\n                .json(ser)\n        try {\n            b.compact()\n            fail()\n        } catch (SerializationException expected) {\n            assertEquals 'Cannot serialize JWT Header to JSON. Cause: dummy text', expected.message\n        }\n    }\n\n    @Test\n    void testSignWithKeyOnly() {\n\n        builder.subject(\"Joe\") // make Claims JWS\n\n        for (SecureDigestAlgorithm alg : Jwts.SIG.get().values()) {\n            if (alg.equals(Jwts.SIG.NONE)) { // skip\n                continue;\n            }\n            def key, vkey\n            if (alg instanceof KeyPairBuilderSupplier) {\n                def keyPair = alg.keyPair().build()\n                key = keyPair.private\n                vkey = keyPair.public\n            } else { // MAC\n                key = ((MacAlgorithm) alg).key().build()\n                vkey = key\n            }\n\n            def parser = Jwts.parser().verifyWith(vkey).build()\n\n            String s1 = builder.signWith(key).compact()\n            def jws = parser.parseSignedClaims(s1)\n\n            String s2 = builder.signWith(key, alg).compact()\n            def jws2 = parser.parseSignedClaims(s2)\n\n            // signatures differ across duplicate operations for some algorithms, so we can't do\n            // assertEquals jws, jws2 (since those .equals implementations use the signature)\n            // So we check for header and payload equality instead, and check the signature when we can:\n            assertEquals jws.getHeader(), jws2.getHeader()\n            assertEquals jws2.getPayload(), jws2.getPayload()\n            // ES* and PS* signatures are nondeterministic and differ on each sign operation, even for identical\n            // input, so we can't assert signature equality for them.  But we can with the others:\n            if (!alg.id.startsWith('ES') && !alg.id.startsWith('PS')) {\n                assertTrue MessageDigest.isEqual(jws.getDigest(), jws2.getDigest())\n            }\n        }\n    }\n\n    @Test\n    void testSignWithKeyOnlyUsingUnsupportedKey() {\n        try {\n            builder.signWith(new TestKey(algorithm: 'foo'))\n            fail()\n        } catch (UnsupportedKeyException expected) {\n            String msg = 'Unable to determine a suitable MAC or Signature algorithm for the specified key using ' +\n                    'available heuristics: either the key size is too weak be used with available algorithms, or ' +\n                    'the key size is unavailable (e.g. if using a PKCS11 or HSM (Hardware Security Module) key ' +\n                    'store). If you are using a PKCS11 or HSM keystore, consider using the ' +\n                    'JwtBuilder.signWith(Key, SecureDigestAlgorithm) method instead.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testSignWithBytesWithoutHmac() {\n        def bytes = new byte[16];\n        try {\n            new DefaultJwtBuilder().signWith(SignatureAlgorithm.ES256, bytes);\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals \"Key bytes may only be specified for HMAC signatures.  If using RSA or \" +\n                    \"Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.\", iae.message\n        }\n    }\n\n    @Test\n    void testSignWithBase64EncodedBytesWithoutHmac() {\n        try {\n            new DefaultJwtBuilder().signWith(SignatureAlgorithm.ES256, 'foo');\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals \"Base64-encoded key bytes may only be specified for HMAC signatures.  If using RSA or Elliptic Curve, use the signWith(SignatureAlgorithm, Key) method instead.\", iae.message\n        }\n\n    }\n\n    @Test\n    void testSetHeaderParamsWithNullMap() {\n        builder.setHeaderParams(null)\n        assertTrue builder.headerBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetHeaderParamsWithEmptyMap() {\n        builder.setHeaderParams([:])\n        assertTrue builder.headerBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetIssuerWithNull() {\n        def b = new DefaultJwtBuilder()\n        b.setIssuer(null)\n        assertTrue b.claimsBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetSubjectWithNull() {\n        builder.setSubject(null)\n        assertTrue builder.claimsBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetAudienceWithNull() {\n        builder.setAudience(null)\n        assertTrue builder.claimsBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetIdWithNull() {\n        builder.setId(null)\n        assertTrue builder.claimsBuilder.isEmpty()\n    }\n\n    @Test\n    void testClaimNullValue() {\n        builder.claim('foo', null)\n        assertTrue builder.claimsBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetNullExpirationWithNullClaims() {\n        builder.setExpiration(null)\n        assertTrue builder.claimsBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetNullNotBeforeWithNullClaims() {\n        builder.setNotBefore(null)\n        assertTrue builder.claimsBuilder.isEmpty()\n    }\n\n    @Test\n    void testSetNullIssuedAtWithNullClaims() {\n        builder.setIssuedAt(null)\n        assertTrue builder.claimsBuilder.isEmpty()\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testBase64UrlEncodeWithNullArgument() {\n        new DefaultJwtBuilder().base64UrlEncodeWith(null)\n    }\n\n    @Test\n    void testBase64UrlEncodeWithCustomEncoder() {\n        def encoder = new Encoder() {\n            @Override\n            Object encode(Object o) throws EncodingException {\n                return null\n            }\n        }\n        def b = new DefaultJwtBuilder().b64Url(encoder)\n        assertSame encoder, b.encoder\n    }\n\n    @Test\n    void base64UrlEncodeWith() {\n\n        boolean invoked = false\n\n        Encoder<byte[], String> encoder = new Encoder<byte[], String>() {\n            @Override\n            String encode(byte[] bytes) throws EncodingException {\n                invoked = true\n                return Encoders.BASE64URL.encode(bytes)\n            }\n        }\n\n        def key = TestKeys.HS256\n        def b = new DefaultJwtBuilder().signWith(key).subject('me')\n\n        def jws1 = b.compact()\n        def jws2 = b.base64UrlEncodeWith(encoder).compact()\n\n        assertEquals jws1, jws2\n        assertTrue invoked\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testSerializeToJsonWithNullArgument() {\n        new DefaultJwtBuilder().serializeToJsonWith(null)\n    }\n\n    @Test\n    void testSerializeToJsonWithCustomSerializer() {\n        boolean invoked = false\n        def serializer = new AbstractSerializer() {\n            @Override\n            protected void doSerialize(Object o, OutputStream out) throws Exception {\n                invoked = true\n                byte[] data = objectMapper.writeValueAsBytes(o)\n                out.write(data)\n            }\n        }\n\n        def b = new DefaultJwtBuilder().serializeToJsonWith(serializer)\n\n        def key = Keys.secretKeyFor(SignatureAlgorithm.HS256)\n\n        String jws = b.signWith(key, SignatureAlgorithm.HS256)\n                .claim('foo', 'bar')\n                .compact()\n\n        assertTrue invoked // ensure we call our custom one\n        assertEquals 'bar', Jwts.parser().setSigningKey(key).build().parseSignedClaims(jws).getPayload().get('foo')\n    }\n\n    @Test\n    void testSignWithNoneAlgorithm() {\n        def key = TestKeys.HS256\n        try {\n            builder.signWith(key, Jwts.SIG.NONE)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"The 'none' JWS algorithm cannot be used to sign JWTs.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testSignWithPublicKey() {\n        def key = TestKeys.RS256.pair.public\n        def alg = Jwts.SIG.RS256\n        try {\n            builder.signWith(key, alg)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals(DefaultJwtBuilder.PUB_KEY_SIGN_MSG, iae.getMessage())\n        }\n    }\n\n    @Test\n    void testCompactSimplestPayload() {\n        def enc = Jwts.ENC.A128GCM\n        def key = enc.key().build()\n        def jwe = builder.setPayload(\"me\").encryptWith(key, enc).compact()\n        def jwt = Jwts.parser().decryptWith(key).build().parseEncryptedContent(jwe)\n        assertEquals 'me', new String(jwt.getPayload(), StandardCharsets.UTF_8)\n    }\n\n    @Test\n    void testCompactSimplestClaims() {\n        def enc = Jwts.ENC.A128GCM\n        def key = enc.key().build()\n        def jwe = builder.setSubject('joe').encryptWith(key, enc).compact()\n        def jwt = Jwts.parser().decryptWith(key).build().parseEncryptedClaims(jwe)\n        assertEquals 'joe', jwt.getPayload().getSubject()\n    }\n\n    @Test\n    void testSignWithAndEncryptWith() {\n        def key = TestKeys.HS256\n        try {\n            builder.signWith(key).encryptWith(key, Jwts.ENC.A128GCM).compact()\n            fail()\n        } catch (IllegalStateException expected) {\n            String msg = \"Both 'signWith' and 'encryptWith' cannot be specified. Choose either one.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEmptyPayloadAndClaimsJwe() {\n        def key = TestKeys.HS256\n        try {\n            builder.encryptWith(key, Jwts.ENC.A128GCM).compact()\n            fail()\n        } catch (IllegalStateException expected) {\n            String msg = \"Encrypted JWTs must have either 'claims' or non-empty 'content'.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @SuppressWarnings('GrDeprecatedAPIUsage')\n    @Test\n    void testAudienceSingle() {\n        def key = TestKeys.HS256\n        String audienceSingleString = 'test'\n        def jwt = builder.audience().single(audienceSingleString).compact()\n        // can't use the parser here to validate because it coerces the string value into an array automatically,\n        // so we need to check the raw payload:\n        def encoded = new JwtTokenizer().tokenize(Streams.reader(jwt)).getPayload()\n        byte[] bytes = Decoders.BASE64URL.decode(encoded)\n        def claims = deser(bytes)\n\n        assertEquals audienceSingleString, claims.aud\n    }\n\n    /**\n     * Asserts that an additional call to audienceSingle is a full replacement operation and fully replaces the\n     * previous audienceSingle value\n     */\n    @Test\n    void testAudienceSingleMultiple() {\n        def first = 'first'\n        def second = 'second'\n        //noinspection GrDeprecatedAPIUsage\n        def jwt = builder.audience().single(first).audience().single(second).compact()\n        // can't use the parser here to validate because it coerces the string value into an array automatically,\n        // so we need to check the raw payload:\n        def encoded = new JwtTokenizer().tokenize(Streams.reader(jwt)).getPayload()\n        byte[] bytes = Decoders.BASE64URL.decode(encoded)\n        def claims = deser(bytes)\n\n        assertEquals second, claims.aud // second audienceSingle call replaces first value\n    }\n\n    /**\n     * Asserts that an additional call to audienceSingle is a full replacement operation and fully replaces the\n     * previous audienceSingle value\n     */\n    @Test\n    void testAudienceSingleThenNull() {\n        def jwt = builder.id('test')\n                .audience().single('single') // set one\n                .audience().single(null) // remove it entirely\n                .compact()\n\n        // shouldn't be an audience at all:\n        assertNull Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n    }\n\n    /**\n     * Asserts that, even if audienceSingle is called and then the value removed, a final call to audience(Collection)\n     * still represents a collection without any value errors\n     */\n    @Test\n    void testAudienceSingleThenNullThenCollection() {\n        def first = 'first'\n        def second = 'second'\n        def expected = [first, second] as Set<String>\n        def jwt = builder\n                .audience().single(first) // sets single value\n                .audience().single(null) // removes entirely\n                .audience().add([first, second]).and() // sets collection\n                .compact()\n\n        def aud = Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n        assertEquals expected, aud\n    }\n\n    /**\n     * Test to ensure that if we receive a JWT with a single string value, that the parser coerces it to a String array\n     * so we don't have to worry about different data types:\n     */\n    @SuppressWarnings('GrDeprecatedAPIUsage')\n    @Test\n    void testParseAudienceSingle() {\n        def key = TestKeys.HS256\n        String audienceSingleString = 'test'\n        def jwt = builder.audience().single(audienceSingleString).compact()\n\n        assertEquals audienceSingleString, Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload\n                .getAudience().iterator().next() // a collection, not a single string\n    }\n\n    @Test\n    void testAudience() {\n        def aud = 'fubar'\n        def jwt = Jwts.builder().audience().add(aud).and().compact()\n        assertEquals aud, Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience().iterator().next()\n    }\n\n    @Test\n    void testAudienceNullString() {\n        def jwt = Jwts.builder().subject('me').audience().add(null).and().compact()\n        assertNull Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n    }\n\n    @Test\n    void testAudienceEmptyString() {\n        def jwt = Jwts.builder().subject('me').audience().add('  ').and().compact()\n        assertNull Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n    }\n\n    @Test\n    void testAudienceMultipleTimes() {\n        def one = 'one'\n        def two = 'two'\n        def jwt = Jwts.builder().audience().add(one).add(two).and().compact()\n        def aud = Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n        assertTrue aud.contains(one)\n        assertTrue aud.contains(two)\n    }\n\n    @Test\n    void testAudienceNullCollection() {\n        Collection c = null\n        def jwt = Jwts.builder().subject('me').audience().add(c).and().compact()\n        assertNull Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n    }\n\n    @Test\n    void testAudienceEmptyCollection() {\n        Collection c = new ArrayList()\n        def jwt = Jwts.builder().subject('me').audience().add(c).and().compact()\n        assertNull Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n    }\n\n    @Test\n    void testAudienceCollectionWithNullElement() {\n        Collection c = new ArrayList()\n        c.add(null)\n        def jwt = Jwts.builder().subject('me').audience().add(c).and().compact()\n        assertNull Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n    }\n\n    /**\n     * Asserts that if someone calls builder.audienceSingle and then audience(String), that the audience value\n     * will automatically be coerced from a String to a Set<String> and contain both elements.\n     */\n    @Test\n    void testAudienceSingleThenAudience() {\n        def one = 'one'\n        def two = 'two'\n        //noinspection GrDeprecatedAPIUsage\n        def jwt = Jwts.builder().audience().single(one).audience().add(two).and().compact()\n        def aud = Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n        assertTrue aud.contains(one)\n        assertTrue aud.contains(two)\n    }\n\n    /**\n     * Asserts that if someone calls builder.audience and then audienceSingle, that the audience value\n     * will automatically be coerced to a single String contain only the single value since audienceSingle is a\n     * full-replacement operation.\n     */\n    @Test\n    void testAudienceThenAudienceSingle() {\n        def one = 'one'\n        def two = 'two'\n        //noinspection GrDeprecatedAPIUsage\n        def jwt = Jwts.builder().audience().add(one).and().audience().single(two).compact()\n\n        // can't use the parser here to validate because it coerces the string value into an array automatically,\n        // so we need to check the raw payload:\n        def encoded = new JwtTokenizer().tokenize(Streams.reader(jwt)).getPayload()\n        byte[] bytes = Decoders.BASE64URL.decode(encoded)\n        def claims = Services.get(Deserializer).deserialize(Streams.reader(bytes))\n\n        assertEquals two, claims.aud\n    }\n\n    /**\n     * Asserts that if someone calls builder.audienceSingle and then audience(Collection), the builder coerces the\n     * aud to a Set<String> and all elements will be applied since audience(Collection).\n     */\n    @Test\n    void testAudienceSingleThenAudienceCollection() {\n        def single = 'one'\n        def collection = ['two', 'three'] as Set<String>\n        def expected = ['one', 'two', 'three'] as Set<String>\n        //noinspection GrDeprecatedAPIUsage\n        def jwt = Jwts.builder().audience().single(single).audience().add(collection).and().compact()\n        def aud = Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).payload.getAudience()\n        assertEquals expected.size(), aud.size()\n        assertTrue aud.contains(single) && aud.containsAll(collection)\n    }\n\n    /**\n     * Asserts that if someone calls builder.audience(Collection) and then audienceSingle, that the audience value\n     * will automatically be coerced to a single String contain only the single value since audienceSingle is a\n     * full-replacement operation.\n     */\n    @Test\n    void testAudienceCollectionThenAudienceSingle() {\n        def one = 'one'\n        def two = 'two'\n        def three = 'three'\n        def jwt = Jwts.builder().audience().add([one, two]).and().audience().single(three).compact()\n\n        // can't use the parser here to validate because it coerces the string value into an array automatically,\n        // so we need to check the raw payload:\n        def encoded = new JwtTokenizer().tokenize(Streams.reader(jwt)).getPayload()\n        byte[] bytes = Decoders.BASE64URL.decode(encoded)\n        def claims = deser(bytes)\n\n        assertEquals three, claims.aud\n    }\n\n    /**\n     * Asserts that if a .audience() builder is used, and its .and() method is not called, the change to the\n     * audience is still applied when building the JWT.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testAudienceWithoutConjunction() {\n        def aud = 'my-web'\n        def builder = Jwts.builder()\n        builder.audience().add(aud) // no .and() call\n        def jwt = builder.compact()\n\n        // assert that the resulting claims has the audience array set as expected:\n        def parsed = Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt)\n        assertEquals aud, parsed.payload.getAudience()[0]\n    }\n\n    /**\n     * Asserts that if a .header().critical() builder is used, and its .and() method is not called, the change to the\n     * crit collection is still applied when building the header.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testCritWithoutConjunction() {\n        def crit = 'test'\n        def builder = Jwts.builder().issuer('me')\n        def headerBuilder = builder.header()\n        headerBuilder.critical().add(crit) // no .and() method\n        headerBuilder.add(crit, 'foo') // no .and() method\n        builder.signWith(TestKeys.HS256)\n        def jwt = builder.compact()\n\n        def headerBytes = Decoders.BASE64URL.decode(jwt.split('\\\\.')[0])\n        def headerMap = Services.get(Deserializer).deserialize(Streams.reader(headerBytes)) as Map<String, ?>\n\n        def expected = [crit] as Set<String>\n        def val = headerMap.get('crit') as Set<String>\n        assertNotNull val\n        assertEquals expected, val\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtHeaderBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.JweHeader\nimport io.jsonwebtoken.JwsHeader\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.ProtectedHeader\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.security.DefaultHashAlgorithm\nimport io.jsonwebtoken.impl.security.DefaultRequest\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\nclass DefaultJwtHeaderBuilderTest {\n\n    static DefaultJwtHeaderBuilder builder\n    static def header\n\n    static DefaultJwtHeaderBuilder jws() {\n        // assignment and return must be on different lines when testing on JDK 7:\n        builder = new DefaultJwtHeaderBuilder().add('alg', 'foo') as DefaultJwtHeaderBuilder\n        return builder\n    }\n\n    static DefaultJwtHeaderBuilder jwe() {\n        // assignment and return must be on different lines when testing on JDK 7 otherwise we get\n        // (class: io/jsonwebtoken/impl/DefaultJwtHeaderBuilderTest, method: jwe signature: ()Lio/jsonwebtoken/impl/DefaultJwtHeaderBuilder;) Illegal target of jump or branch\n        builder = jws().add('enc', 'bar') as DefaultJwtHeaderBuilder\n        return builder\n    }\n\n    @Before\n    void setUp() {\n        header = null\n        builder = new DefaultJwtHeaderBuilder()\n    }\n\n    @SuppressWarnings('GroovyAssignabilityCheck')\n    private static void assertSymmetry(String propName, def val) {\n        def name = Strings.capitalize(propName)\n        switch (propName) {\n            case 'algorithm': builder.add('alg', val); break // no setter\n            case 'compressionAlgorithm': builder.add('zip', val); break // no setter\n            default: builder.\"$propName\"(val)\n        }\n        header = builder.build()\n        if (val instanceof byte[]) {\n            assertArrayEquals val, header.\"get$name\"()\n        } else {\n            assertEquals val, header.\"get$name\"()\n        }\n    }\n\n    @Test\n    void testStaticFactoryMethod() {\n        assertTrue Jwts.header() instanceof DefaultJwtHeaderBuilder\n    }\n\n    @Test\n    void testDefault() { // no properties are set, so assert an unprotected header:\n        header = builder.build()\n        assertFalse header instanceof JwsHeader\n        assertFalse header instanceof JweHeader\n        assertTrue header instanceof DefaultHeader\n    }\n\n    // ====================== Map Methods =======================\n\n    @Test\n    void testSize() {\n        assertEquals 0, builder.size()\n        builder.put('foo', 'bar')\n        assertEquals 1, builder.build().size()\n    }\n\n    @Test\n    void testIsEmpty() {\n        assertTrue builder.build().isEmpty()\n        builder.put('foo', 'bar')\n        assertFalse builder.build().isEmpty()\n    }\n\n    @Test\n    void testContainsKey() {\n        def key = 'foo'\n        assertFalse builder.build().containsKey(key)\n        builder.put(key, 'bar')\n        assertTrue builder.build().containsKey(key)\n    }\n\n    @Test\n    void testContainsValue() {\n        def value = 'bar'\n        assertFalse builder.build().containsValue(value)\n        builder.put('foo', value)\n        assertTrue builder.build().containsValue(value)\n    }\n\n    @Test\n    void testGet() {\n        def key = 'foo'\n        def value = 'bar'\n        assertNull builder.build().get(key)\n        builder.put(key, value)\n        assertEquals value, builder.build().get(key)\n    }\n\n    @Test\n    void testKeySet() {\n        def key = 'foo'\n        def value = 'bar'\n        assertTrue builder.build().keySet().isEmpty()\n\n        builder.put(key, value)\n        def built = builder.build()\n        assertFalse built.keySet().isEmpty()\n        assertEquals 1, built.keySet().size()\n        assertEquals key, built.keySet().iterator().next()\n\n        def i = builder.build().keySet().iterator()\n        i.next()\n        //built headers are immutable:\n        try {\n            i.remove() // assert keyset modification modifies builder state:\n            fail()\n        } catch (UnsupportedOperationException expected) {\n        }\n    }\n\n    @Test\n    void testValues() {\n        def key = 'foo'\n        def value = 'bar'\n        assertTrue builder.build().values().isEmpty()\n\n        builder.put(key, value)\n        assertFalse builder.build().values().isEmpty()\n        assertEquals 1, builder.build().values().size()\n        assertEquals value, builder.build().values().iterator().next()\n\n        def i = builder.build().values().iterator()\n        i.next()\n        //built headers are immutable:\n        try {\n            i.remove()\n            fail()\n        } catch (UnsupportedOperationException expected) {\n        }\n    }\n\n    @Test\n    void testEntrySet() {\n        def key = 'foo'\n        def value = 'bar'\n        assertTrue builder.build().entrySet().isEmpty()\n\n        builder.put(key, value)\n        assertFalse builder.build().entrySet().isEmpty()\n        assertEquals 1, builder.build().entrySet().size()\n        def entry = builder.build().entrySet().iterator().next()\n        assertEquals key, entry.getKey()\n        assertEquals value, entry.getValue()\n\n        def i = builder.build().entrySet().iterator()\n        i.next()\n        //built headers are immutable:\n        try {\n            i.remove()\n            fail()\n        } catch (UnsupportedOperationException expected) {\n        }\n    }\n\n    @Test\n    void testPut() {\n        builder.put('foo', 'bar')\n        assertEquals 'bar', builder.build().get('foo')\n    }\n\n    @Test\n    void testPutAll() {\n        def m = ['foo': 'bar', 'baz': 'bat']\n        def header = builder.add(m).build()\n        assertEquals m, header\n    }\n\n    @Test\n    void testRemove() {\n        builder.put('foo', 'bar')\n        assertEquals 'bar', builder.build().foo\n\n        builder.remove('foo')\n        assertTrue builder.build().isEmpty()\n    }\n\n    @Test\n    void testClear() {\n        def m = ['foo': 'bar', 'baz': 'bat']\n        builder.add(m)\n        builder.clear()\n        def header = builder.build()\n        assertTrue header.isEmpty()\n    }\n\n    @Test\n    void testEmpty() {\n        def m = ['foo': 'bar', 'baz': 'bat']\n        def header = builder.add(m).empty().build()\n        assertTrue header.isEmpty()\n    }\n\n    @Test\n    void testToMap() {\n        def m = ['foo': 'bar', 'baz': 'bat']\n        builder.putAll(m)\n        assertEquals m, builder\n        assertEquals m, builder.build()\n    }\n\n    // ====================== Generic Header Methods =======================\n\n    @Test\n    void testType() {\n        assertSymmetry('type', 'foo')\n    }\n\n    @Test\n    void testContentType() {\n        assertSymmetry('contentType', 'text/plain')\n    }\n\n    /**\n     * Asserts that if the 'alg' member is set to any other value other than 'none', but no JWE-only members\n     * are set, a JwsHeader is created.  Although a JweHeader also has an 'alg' value, there must be at least\n     * one JWE-only member set as well to trigger JweHeader creation.\n     */\n    @Test\n    void testAlgNone() { // alg of 'none', so build an unprotected header:\n        assertSymmetry('algorithm', 'none')\n        assertFalse header instanceof JwsHeader\n        assertFalse header instanceof JweHeader\n        assertTrue header instanceof DefaultHeader\n    }\n\n    @Test\n    void testCompressionAlgorithm() {\n        assertSymmetry('compressionAlgorithm', 'DEF')\n    }\n\n    @Test\n    void testDeprecatedSetters() { // TODO: remove before 1.0\n        assertEquals 'foo', builder.setType('foo').build().getType()\n\n        assertEquals 'foo', builder.setContentType('foo').build().get('cty') // compact form\n        assertEquals 'application/foo', builder.build().getContentType()     // normalized form\n\n        assertEquals 'foo', builder.setCompressionAlgorithm('foo').build().getCompressionAlgorithm()\n        assertEquals 'foo', jws().setKeyId('foo').build().getKeyId()\n        assertEquals 'foo', jws().setAlgorithm('foo').build().getAlgorithm()\n    }\n\n    // ====================== Protected Header Methods =======================\n\n    /**\n     * Asserts that if the protected-header-only 'jku' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testJwkSetUrl() {\n        URI uri = URI.create('https://github.com/jwtk/jjwt')\n        header = jws().jwkSetUrl(uri).build() as JwsHeader\n        assertEquals uri, header.getJwkSetUrl()\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'jwk' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testJwk() {\n        def jwk = Jwks.builder().key(TestKeys.RS256.pair.public as RSAPublicKey).build()\n        header = jws().jwk(jwk).build() as JwsHeader\n        assertEquals jwk, header.getJwk()\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'kid' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testKeyId() {\n        def kid = 'whatever'\n        header = jws().keyId(kid).build() as JwsHeader\n        assertEquals kid, header.getKeyId()\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'crit' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testCritical() {\n        def crit = ['foo'] as Set<String>\n        header = jws().add('foo', 'bar').critical().add(crit).and().build() as JwsHeader\n        assertTrue header instanceof JwsHeader\n        assertFalse header instanceof JweHeader\n        assertEquals crit, header.getCritical()\n    }\n\n    // ====================== X.509 Methods =======================\n\n    /**\n     * Asserts that if the protected-header-only 'x5u' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX09Url() {\n        def uri = URI.create('https://github.com/jwtk/jjwt')\n        header = jws().x509Url(uri).build() as JwsHeader\n        assertEquals uri, header.getX509Url()\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'x5c' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX509CertificateChain() {\n        def chain = TestKeys.RS256.chain\n        header = jws().x509Chain(chain).build() as JwsHeader\n        assertEquals chain, header.getX509Chain()\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'x5t' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX509CertificateSha1Thumbprint() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        def request = new DefaultRequest(payload, null, null)\n        def x5t = DefaultHashAlgorithm.SHA1.digest(request)\n        String encoded = Encoders.BASE64URL.encode(x5t)\n\n        header = jws().x509Sha1Thumbprint(x5t).build() as JwsHeader\n        assertArrayEquals x5t, header.getX509Sha1Thumbprint()\n        assertEquals encoded, header.get('x5t')\n    }\n\n    @Test\n    void testX509CertificateSha1ThumbprintEnabled() {\n        def chain = TestKeys.RS256.chain\n        def payload = Streams.of(chain[0].getEncoded())\n        def request = new DefaultRequest(payload, null, null)\n        def x5t = DefaultHashAlgorithm.SHA1.digest(request)\n        String encoded = Encoders.BASE64URL.encode(x5t)\n        header = jws().x509Chain(chain).x509Sha1Thumbprint(true).build() as JwsHeader\n        assertArrayEquals x5t, header.getX509Sha1Thumbprint()\n        assertEquals encoded, header.get('x5t')\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'x5t#S256' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX509CertificateSha256Thumbprint() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        def request = new DefaultRequest(payload, null, null)\n        def x5tS256 = Jwks.HASH.@SHA256.digest(request)\n        String encoded = Encoders.BASE64URL.encode(x5tS256)\n        header = jws().x509Sha256Thumbprint(x5tS256).build() as JwsHeader\n        assertArrayEquals x5tS256, header.getX509Sha256Thumbprint()\n        assertEquals encoded, header.get('x5t#S256')\n    }\n\n    @Test\n    void testX509CertificateSha256ThumbprintEnabled() {\n        def chain = TestKeys.RS256.chain\n        def payload = Streams.of(chain[0].getEncoded())\n        def request = new DefaultRequest(payload, null, null)\n        def x5tS256 = Jwks.HASH.SHA256.digest(request)\n        String encoded = Encoders.BASE64URL.encode(x5tS256)\n        header = jws().x509Chain(chain).x509Sha256Thumbprint(true).build() as JwsHeader\n        assertArrayEquals x5tS256, header.getX509Sha256Thumbprint()\n        assertEquals encoded, header.get('x5t#S256')\n    }\n\n    // ====================== JWE Header Methods =======================\n\n    @Test\n    void testEncryptionAlgorithm() {\n        def enc = Jwts.ENC.A256GCM.getId()\n        header = builder.add('alg', Jwts.KEY.A192KW.getId()).add('enc', enc).build() as JweHeader\n        assertEquals enc, header.getEncryptionAlgorithm()\n    }\n\n    @Test\n    void testEphemeralPublicKey() {\n        def key = TestKeys.ES256.pair.public\n        def jwk = Jwks.builder().key(key).build()\n        header = jwe().add('epk', jwk).build() as JweHeader\n        assertEquals jwk, header.getEphemeralPublicKey()\n    }\n\n    @Test\n    void testAgreementPartyUInfo() {\n        def info = Strings.utf8(\"UInfo\")\n        def header = jwe().agreementPartyUInfo(info).build() as JweHeader\n        assertArrayEquals info, header.getAgreementPartyUInfo()\n    }\n\n    @Test\n    void testAgreementPartyUInfoString() {\n        def s = \"UInfo\"\n        def info = Strings.utf8(s)\n        def header = jwe().agreementPartyUInfo(s).build() as JweHeader\n        assertArrayEquals info, header.getAgreementPartyUInfo()\n    }\n\n    @Test\n    void testAgreementPartyVInfo() {\n        def info = Strings.utf8(\"VInfo\")\n        def header = jwe().agreementPartyVInfo(info).build() as JweHeader\n        assertArrayEquals info, header.getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testAgreementPartyVInfoString() {\n        def s = \"VInfo\"\n        def info = Strings.utf8(s)\n        def header = jwe().agreementPartyVInfo(s).build() as JweHeader\n        assertArrayEquals info, header.getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testPbes2Salt() {\n        byte[] salt = Bytes.randomBits(256)\n        def header = jwe().add('p2s', salt).build() as JweHeader\n        assertArrayEquals salt, header.getPbes2Salt()\n    }\n\n    @Test\n    void testPbes2Count() {\n        int count = 4096\n        def header = jwe().pbes2Count(count).build() as JweHeader\n        assertEquals count, header.getPbes2Count()\n    }\n\n    @Test\n    void testInitializationVector() {\n        byte[] iv = Bytes.randomBits(96)\n        def header = jwe().add('iv', iv).build() as JweHeader\n        assertArrayEquals iv, header.getInitializationVector()\n    }\n\n    @Test\n    void testAuthenticationTag() {\n        byte[] val = Bytes.randomBits(128)\n        def header = jwe().add('tag', val).build() as JweHeader\n        assertArrayEquals val, header.getAuthenticationTag()\n    }\n\n    @Test\n    void testUnprotectedHeaderChangedToProtectedHeaderChangedToJweHeader() {\n        builder.put('foo', 'bar')\n        assertEquals new DefaultHeader([foo: 'bar']), builder.build()\n\n        // add JWS-required property:\n        builder.put(DefaultHeader.ALGORITHM.getId(), 'HS256')\n        assertEquals new DefaultJwsHeader([foo: 'bar', alg: 'HS256']), builder.build()\n\n        // add JWE required property:\n        builder.put(DefaultJweHeader.ENCRYPTION_ALGORITHM.getId(), Jwts.ENC.A256GCM.getId())\n        assertEquals new DefaultJweHeader([foo: 'bar', alg: 'HS256', enc: 'A256GCM']), builder.build()\n    }\n\n    @Test\n    void testCritSingle() {\n        def crit = 'test'\n        def header = jws().add(crit, 'foo').critical().add(crit).and().build() as ProtectedHeader\n        def expected = [crit] as Set<String>\n        assertEquals expected, header.getCritical()\n    }\n\n    /**\n     * Asserts that if a .critical() builder is used, and its .and() method is not called, the change to the\n     * crit collection is still applied when building the header.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testCritWithoutConjunction() {\n        def crit = 'test'\n        def builder = jws()\n        builder.add(crit, 'foo').critical().add(crit) // no .and() method\n        def header = builder.build() as ProtectedHeader\n        def expected = [crit] as Set<String>\n        assertEquals expected, header.getCritical()\n    }\n\n    @Test\n    void testCritSingleNullIgnored() {\n        def crit = 'test'\n        def expected = [crit] as Set<String>\n        def header = jws().add(crit, 'foo').critical().add(crit).and().build() as ProtectedHeader\n        assertEquals expected, header.getCritical()\n        header = builder.critical().add((String) null).and().build() as ProtectedHeader // ignored\n        assertEquals expected, header.getCritical() // nothing changed\n    }\n\n    @Test\n    void testCritNullCollectionIgnored() {\n        def crit = ['test'] as Set<String>\n        def header = jws().add('test', 'foo').critical().add(crit).and().build() as ProtectedHeader\n        assertEquals crit, header.getCritical()\n        header = builder.critical().add((Collection) null).and().build() as ProtectedHeader\n        assertEquals crit, header.getCritical() // nothing changed\n    }\n\n    @Test\n    void testCritCollectionWithNullElement() {\n        def crit = [null] as Set<String>\n        def header = jws().add('test', 'foo').critical().add(crit).and().build() as ProtectedHeader\n        assertNull header.getCritical()\n    }\n\n    @Test\n    void testCritEmptyIgnored() {\n        def crit = ['test'] as Set<String>\n        ProtectedHeader header = jws().add('test', 'foo').critical().add(crit).and().build() as ProtectedHeader\n        assertEquals crit, header.getCritical()\n        header = builder.critical().add([] as Set<String>).and().build() as ProtectedHeader\n        assertEquals crit, header.getCritical() // ignored\n    }\n\n    /**\n     * Asserts that per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, a {@code crit} header is not\n     * allowed in non-protected headers.\n     */\n    @Test\n    void testCritRemovedForUnprotectedHeader() {\n        def crit = Collections.setOf('foo', 'bar')\n        // no JWS or JWE params specified:\n        def header = builder.add('test', 'value').critical().add(crit).and().build()\n        assertFalse header.containsKey(DefaultProtectedHeader.CRIT.getId())\n    }\n\n    /**\n     * Asserts that per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11, a value in the {@code crit} set\n     * is removed if the corresponding header parameter is missing.\n     */\n    @Test\n    void testCritNamesSanitizedWhenHeaderMissingCorrespondingParameter() {\n        def critGiven = ['foo', 'bar'] as Set<String>\n        def critExpected = ['foo'] as Set<String>\n        def header = jws().add('foo', 'fooVal').critical().add(critGiven).and().build() as ProtectedHeader\n        // header didn't set the 'bar' parameter, so 'bar' should not be in the crit values:\n        assertEquals critExpected, header.getCritical()\n    }\n\n    @Test\n    void testCritNamesRemovedWhenHeaderMissingCorrespondingParameter() {\n        def critGiven = ['foo'] as Set<String>\n        ProtectedHeader header = jws().critical().add(critGiven).and().build() as ProtectedHeader\n        // header didn't set the 'foo' parameter, so crit would have been empty, and then removed from the header:\n        assertNull header.getCritical()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtParserBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport io.jsonwebtoken.*\nimport io.jsonwebtoken.impl.security.*\nimport io.jsonwebtoken.io.*\nimport io.jsonwebtoken.security.InvalidKeyException\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.Provider\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\n// NOTE to the casual reader: even though this test class appears mostly empty, the DefaultJwtParserBuilder\n// implementation is tested to 100% coverage.  The vast majority of its tests are in the JwtsTest class.  This class\n// just fills in any remaining test gaps.\nclass DefaultJwtParserBuilderTest {\n\n    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper()\n\n    private DefaultJwtParserBuilder builder\n\n    @Before\n    void setUp() {\n        builder = new DefaultJwtParserBuilder()\n    }\n\n    @Test\n    void testCriticalEmtpy() {\n        builder.critical().add(' ').and() // shouldn't modify the set\n        assertTrue builder.@critical.isEmpty()\n    }\n\n    /**\n     * Asserts that if a .critical() builder is used, and its .and() method is not called, the change to the\n     * crit collection is still applied when building the parser.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testCriticalWithoutConjunction() {\n        builder.critical().add('foo') // no .and() call\n        assertFalse builder.@critical.isEmpty()\n        assertTrue builder.@critical.contains('foo')\n        def parser = builder.build()\n        assertFalse parser.@critical.isEmpty()\n        assertTrue parser.@critical.contains('foo')\n    }\n\n    @Test\n    void testSetProvider() {\n        Provider provider = createMock(Provider)\n        replay provider\n\n        def parser = builder.provider(provider).build()\n\n        assertSame provider, parser.provider\n        verify provider\n    }\n\n    @Test\n    void testKeyLocatorAndVerificationKeyConfigured() {\n        try {\n            builder\n                    .keyLocator(new ConstantKeyLocator(null, null))\n                    .verifyWith(TestKeys.HS256)\n                    .build()\n            fail()\n        } catch (IllegalStateException e) {\n            String msg = \"Both 'keyLocator' and a 'verifyWith' key cannot be configured. Prefer 'keyLocator' if possible.\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testKeyLocatorAndDecryptionKeyConfigured() {\n        try {\n            builder\n                    .keyLocator(new ConstantKeyLocator(null, null))\n                    .decryptWith(TestKeys.A128GCM)\n                    .build()\n            fail()\n        } catch (IllegalStateException e) {\n            String msg = \"Both 'keyLocator' and a 'decryptWith' key cannot be configured. Prefer 'keyLocator' if possible.\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testBase64UrlDecodeWithNullArgument() {\n        builder.base64UrlDecodeWith(null)\n    }\n\n    @Test\n    void testBase64UrlEncodeWithCustomDecoder() {\n\n        String jwt = Jwts.builder().claim('foo', 'bar').compact()\n\n        boolean invoked = false\n        Decoder<String, byte[]> decoder = new Decoder<String, byte[]>() {\n            @Override\n            byte[] decode(String s) throws DecodingException {\n                invoked = true\n                return Decoders.BASE64URL.decode(s)\n            }\n        }\n        def parser = builder.base64UrlDecodeWith(decoder).unsecured().build()\n        assertFalse invoked\n\n        assertEquals 'bar', parser.parseUnsecuredClaims(jwt).getPayload().get('foo')\n        assertTrue invoked\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testDeserializeJsonWithNullArgument() {\n        builder.deserializeJsonWith(null)\n    }\n\n    @Test\n    void testDeserializeJsonWithCustomSerializer() {\n        def deserializer = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                return OBJECT_MAPPER.readValue(reader, Map.class)\n            }\n        }\n        def p = builder.deserializeJsonWith(deserializer)\n        assertSame deserializer, p.@deserializer\n\n        def alg = Jwts.SIG.HS256\n        def key = alg.key().build()\n\n        String jws = Jwts.builder().claim('foo', 'bar').signWith(key, alg).compact()\n\n        assertEquals 'bar', p.verifyWith(key).build().parseSignedClaims(jws).getPayload().get('foo')\n    }\n\n    @Test\n    void testMaxAllowedClockSkewSeconds() {\n        long max = Long.MAX_VALUE / 1000 as long\n        builder.setAllowedClockSkewSeconds(max) // no exception should be thrown\n    }\n\n    @Test\n    void testExceededAllowedClockSkewSeconds() {\n        long value = Long.MAX_VALUE / 1000 as long\n        value = value + 1L\n        try {\n            builder.setAllowedClockSkewSeconds(value)\n        } catch (IllegalArgumentException expected) {\n            assertEquals DefaultJwtParserBuilder.MAX_CLOCK_SKEW_ILLEGAL_MSG, expected.message\n        }\n    }\n\n    @Test\n    void testCompressionCodecResolver() {\n        def resolver = new CompressionCodecResolver() {\n            @Override\n            CompressionCodec resolveCompressionCodec(Header header) throws CompressionException {\n                return null\n            }\n        }\n        def parser = builder.setCompressionCodecResolver(resolver).build()\n        assertSame resolver, parser.zipAlgs.resolver\n    }\n\n    @Test\n    void testAddCompressionAlgorithms() {\n        def codec = new TestCompressionCodec(id: 'test')\n        def parser = builder.zip().add(codec).and().build()\n        def header = Jwts.header().add('zip', codec.getId()).build()\n        assertSame codec, parser.zipAlgs.locate(header)\n    }\n\n    /**\n     * Asserts that if a .zip() builder is used, and its .and() method is not called, the change to the\n     * compression algorithm collection is still applied when building the parser.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testAddCompressionAlgorithmWithoutConjunction() {\n        def codec = new TestCompressionCodec(id: 'test')\n        builder.zip().add(codec) // no .and() call\n        def parser = builder.build()\n        def header = Jwts.header().add('zip', codec.getId()).build()\n        assertSame codec, parser.zipAlgs.locate(header)\n    }\n\n    @Test\n    void testAddCompressionAlgorithmsOverrideDefaults() {\n        def header = Jwts.header().add('zip', 'DEF').build()\n        def parser = builder.build()\n        assertSame Jwts.ZIP.DEF, parser.zipAlgs.apply(header) // standard implementation default\n\n        def alg = new TestCompressionCodec(id: 'DEF') // custom impl with standard identifier\n        parser = builder.zip().add(alg).and().build()\n        assertSame alg, parser.zipAlgs.apply(header) // custom one, not standard impl\n    }\n\n    @Test\n    void testCaseSensitiveCompressionAlgorithm() {\n        def standard = Jwts.header().add('zip', 'DEF').build()\n        def nonStandard = Jwts.header().add('zip', 'def').build()\n        def parser = builder.build()\n        assertSame Jwts.ZIP.DEF, parser.zipAlgs.apply(standard) // standard implementation default\n        try {\n            parser.zipAlgs.apply(nonStandard)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String msg = \"Unsupported JWT header ${DefaultHeader.COMPRESSION_ALGORITHM} value 'def'.\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testAddEncryptionAlgorithmsOverrideDefaults() {\n        final String standardId = Jwts.ENC.A256GCM.getId()\n        def header = Jwts.header().add('enc', standardId).build()\n        def parser = builder.build()\n        assertSame Jwts.ENC.A256GCM, parser.encAlgs.apply(header) // standard implementation default\n\n        def custom = new TestAeadAlgorithm(id: standardId) // custom impl with standard identifier\n        parser = builder.enc().add(custom).and().build()\n        assertSame custom, parser.encAlgs.apply(header) // custom one, not standard impl\n    }\n\n    /**\n     * Asserts that if an .enc() builder is used, and its .and() method is not called, the change to the\n     * encryption algorithm collection is still applied when building the parser.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testAddEncryptionAlgorithmWithoutConjunction() {\n        def alg = new TestAeadAlgorithm(id: 'test')\n        builder.enc().add(alg) // no .and() call\n        def parser = builder.build() as DefaultJwtParser\n        def header = Jwts.header().add('alg', 'foo').add('enc', alg.getId()).build() as JweHeader\n        assertSame alg, parser.encAlgs.apply(header)\n    }\n\n    @Test\n    void testCaseSensitiveEncryptionAlgorithm() {\n        def alg = Jwts.ENC.A256GCM\n        def standard = Jwts.header().add('alg', 'foo').add('enc', alg.id).build()\n        def nonStandard = Jwts.header().add('alg', 'foo').add('enc', alg.id.toLowerCase()).build()\n        def parser = builder.build()\n        assertSame alg, parser.encAlgs.apply(standard) // standard id\n        try {\n            parser.encAlgs.apply(nonStandard) // non-standard id\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String msg = \"Unsupported JWE header ${DefaultJweHeader.ENCRYPTION_ALGORITHM} value '${alg.id.toLowerCase()}'.\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testAddKeyAlgorithmsOverrideDefaults() {\n        final String standardId = Jwts.KEY.A256GCMKW.id\n        def header = Jwts.header().add('enc', Jwts.ENC.A256GCM.id).add('alg', standardId).build()\n        def parser = builder.build()\n        assertSame Jwts.KEY.A256GCMKW, parser.keyAlgs.apply(header) // standard implementation default\n\n        def custom = new TestKeyAlgorithm(id: standardId) // custom impl with standard identifier\n        parser = builder.key().add(custom).and().build()\n        assertSame custom, parser.keyAlgs.apply(header) // custom one, not standard impl\n    }\n\n    /**\n     * Asserts that if an .key() builder is used, and its .and() method is not called, the change to the\n     * key algorithm collection is still applied when building the parser.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testAddKeyAlgorithmWithoutConjunction() {\n        def alg = new TestKeyAlgorithm(id: 'test')\n        builder.key().add(alg) // no .and() call\n        def parser = builder.build() as DefaultJwtParser\n        def header = Jwts.header()\n                .add('enc', 'foo')\n                .add('alg', alg.getId()).build() as JweHeader\n        assertSame alg, parser.keyAlgs.apply(header)\n    }\n\n    @Test\n    void testCaseSensitiveKeyAlgorithm() {\n        def alg = Jwts.KEY.A256GCMKW\n        def hb = Jwts.header().add('enc', Jwts.ENC.A256GCM.id)\n        def standard = hb.add('alg', alg.id).build()\n        def nonStandard = hb.add('alg', alg.id.toLowerCase()).build()\n        def parser = builder.build()\n        assertSame alg, parser.keyAlgs.apply(standard) // standard id\n        try {\n            parser.keyAlgs.apply(nonStandard) // non-standard id\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String msg = \"Unsupported JWE header ${DefaultJweHeader.ALGORITHM} value '${alg.id.toLowerCase()}'.\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testAddSignatureAlgorithmsOverrideDefaults() {\n        final String standardId = Jwts.SIG.HS256.id\n        def header = Jwts.header().add('alg', standardId).build()\n        def parser = builder.build()\n        assertSame Jwts.SIG.HS256, parser.sigAlgs.apply(header) // standard implementation default\n\n        def custom = new TestMacAlgorithm(id: standardId) // custom impl with standard identifier\n        parser = builder.sig().add(custom).and().build()\n        assertSame custom, parser.sigAlgs.apply(header) // custom one, not standard impl\n    }\n\n    /**\n     * Asserts that if an .sig() builder is used, and its .and() method is not called, the change to the\n     * signature algorithm collection is still applied when building the parser.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testAddSignatureAlgorithmWithoutConjunction() {\n        def alg = new TestMacAlgorithm(id: 'test')\n        builder.sig().add(alg) // no .and() call\n        def parser = builder.build() as DefaultJwtParser\n        def header = Jwts.header().add('alg', alg.getId()).build() as JwsHeader\n        assertSame alg, parser.sigAlgs.apply(header)\n    }\n\n    @Test\n    void testCaseSensitiveSignatureAlgorithm() {\n        def alg = Jwts.SIG.HS256\n        def hb = Jwts.header().add('alg', alg.id)\n        def standard = hb.build()\n        def nonStandard = hb.add('alg', alg.id.toLowerCase()).build()\n        def parser = builder.build()\n        assertSame alg, parser.sigAlgs.apply(standard) // standard id\n        try {\n            parser.sigAlgs.apply(nonStandard) // non-standard id\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String msg = \"Unsupported JWS header ${DefaultJwsHeader.ALGORITHM} value '${alg.id.toLowerCase()}'.\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testCompressionCodecResolverAndExtraCompressionCodecs() {\n        def codec = new TestCompressionCodec(id: 'test')\n        def resolver = new CompressionCodecResolver() {\n            @Override\n            CompressionCodec resolveCompressionCodec(Header header) throws CompressionException {\n                return null\n            }\n        }\n        try {\n            builder.setCompressionCodecResolver(resolver).zip().add(codec).and().build()\n            fail()\n        } catch (IllegalStateException expected) {\n            String msg = \"Both 'zip()' and 'compressionCodecResolver' cannot be configured. Choose either.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEnableUnsecuredDecompressionWithoutEnablingUnsecuredJws() {\n        try {\n            builder.unsecuredDecompression().build()\n            fail()\n        } catch (IllegalStateException ise) {\n            String expected = \"'unsecuredDecompression' is only relevant if 'unsecured' \" + \"is also configured. Please read the JavaDoc of both features before enabling either \" + \"due to their security implications.\"\n            assertEquals expected, ise.getMessage()\n        }\n    }\n\n    @Test\n    void testDecompressUnprotectedJwtDefault() {\n        def codec = Jwts.ZIP.GZIP\n        String jwt = Jwts.builder().compressWith(codec).setSubject('joe').compact()\n        try {\n            builder.unsecured().build().parse(jwt)\n            fail()\n        } catch (UnsupportedJwtException e) {\n            String expected = String.format(DefaultJwtParser.UNPROTECTED_DECOMPRESSION_MSG, codec.getId())\n            assertEquals(expected, e.getMessage())\n        }\n    }\n\n    @Test\n    void testDecompressUnprotectedJwtEnabled() {\n        def codec = Jwts.ZIP.GZIP\n        String jws = Jwts.builder().compressWith(codec).setSubject('joe').compact()\n        def jwt = builder.unsecured().unsecuredDecompression().build().parse(jws)\n        assertEquals 'joe', ((Claims) jwt.getPayload()).getSubject()\n    }\n\n    @Test\n    void testDefaultDeserializer() {\n        JwtParser parser = builder.build() // perform ServiceLoader lookup\n        assertTrue parser.@deserializer instanceof Deserializer\n    }\n\n    @Test\n    void testUserSetDeserializerWrapped() {\n        Deserializer deserializer = niceMock(Deserializer)\n        JwtParser parser = builder.deserializeJsonWith(deserializer).build()\n        assertSame deserializer, parser.@deserializer\n    }\n\n    @Test\n    void testVerificationKeyAndSigningKeyResolverBothConfigured() {\n        def key = TestKeys.HS256\n        builder.verifyWith(key).setSigningKeyResolver(new LocatingKeyResolver(new ConstantKeyLocator(key, null)))\n        try {\n            builder.build()\n            fail()\n        } catch (IllegalStateException expected) {\n            String msg = \"Both a 'signingKeyResolver and a 'verifyWith' key cannot be configured. \" + \"Choose either, or prefer `keyLocator` when possible.\"\n            assertEquals(msg, expected.getMessage())\n        }\n    }\n\n    @Test\n    void testSetSigningKeyWithPrivateKey() {\n        try {\n            builder.setSigningKey(TestKeys.RS256.pair.private)\n            fail()\n        } catch (InvalidKeyException e) {\n            String msg = 'JWS verification key must be either a SecretKey (for MAC algorithms) or a PublicKey (for Signature algorithms).'\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    static class TestCompressionCodec implements CompressionCodec {\n\n        String id\n\n        @Override\n        String getAlgorithmName() {\n            return this.id\n        }\n\n        @Override\n        String getId() {\n            return this.id\n        }\n\n        @Override\n        byte[] compress(byte[] content) throws CompressionException {\n            return new byte[0]\n        }\n\n        @Override\n        byte[] decompress(byte[] compressed) throws CompressionException {\n            return new byte[0]\n        }\n\n        @Override\n        OutputStream compress(OutputStream out) throws CompressionException {\n            return out\n        }\n\n        @Override\n        InputStream decompress(InputStream inputStream) throws CompressionException {\n            return inputStream\n        }\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtParserTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport com.fasterxml.jackson.databind.ObjectMapper\nimport io.jsonwebtoken.*\nimport io.jsonwebtoken.impl.lang.JwtDateConverter\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.AbstractDeserializer\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.io.Serializer\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.lang.DateFormats\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.Keys\nimport org.junit.Before\nimport org.junit.Test\n\nimport javax.crypto.Mac\nimport javax.crypto.SecretKey\n\nimport static org.junit.Assert.*\n\n// NOTE to the casual reader: even though this test class appears mostly empty, the DefaultJwtParser\n// implementation is tested to 100% coverage.  The vast majority of its tests are in the JwtsTest class.  This class\n// just fills in any remaining test gaps.\n\nclass DefaultJwtParserTest {\n\n    // all whitespace chars as defined by Character.isWhitespace:\n    static final String WHITESPACE_STR = ' \\u0020 \\u2028 \\u2029 \\t \\n \\u000B \\f \\r \\u001C \\u001D \\u001E \\u001F '\n\n    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();\n\n    private DefaultJwtParser parser\n\n    private static String b64Url(def val) {\n        if (val instanceof String) val = Strings.utf8(val)\n        return Encoders.BASE64URL.encode(val)\n    }\n\n    private static byte[] serialize(Map<String, ?> map) {\n        def serializer = Services.get(Serializer)\n        ByteArrayOutputStream out = new ByteArrayOutputStream(512)\n        serializer.serialize(map, out)\n        return out.toByteArray()\n    }\n\n    @Before\n    void setUp() {\n        parser = Jwts.parser().build() as DefaultJwtParser\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testBase64UrlDecodeWithInvalidInput() {\n        parser.decode('20:SLDKJF;3993;----', 'test')\n    }\n\n    @Test\n    void testDesrializeJsonWithCustomSerializer() {\n        boolean invoked = false\n        def deserializer = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                invoked = true\n                return OBJECT_MAPPER.readValue(reader, Map.class)\n            }\n        }\n        def pb = Jwts.parser().deserializeJsonWith(deserializer)\n        assertFalse invoked\n\n        def key = Jwts.SIG.HS256.key().build()\n        String jws = Jwts.builder().claim('foo', 'bar').signWith(key, Jwts.SIG.HS256).compact()\n        assertFalse invoked\n\n        assertEquals 'bar', pb.verifyWith(key).build().parseSignedClaims(jws).getPayload().get('foo')\n        assertTrue invoked\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseJwsWithMissingAlg() {\n\n        String header = Encoders.BASE64URL.encode('{\"foo\":\"bar\"}'.getBytes(Strings.UTF_8))\n        String body = Encoders.BASE64URL.encode('{\"hello\":\"world\"}'.getBytes(Strings.UTF_8))\n        String compact = header + '.' + body + '.'\n\n        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)\n        Mac mac = Mac.getInstance('HmacSHA256')\n        mac.init(key)\n        byte[] signatureBytes = mac.doFinal(compact.getBytes(Strings.UTF_8))\n        String encodedSignature = Encoders.BASE64URL.encode(signatureBytes)\n\n        String invalidJws = compact + encodedSignature\n\n        Jwts.parser().verifyWith(key).build().parseSignedClaims(invalidJws)\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseJwsWithNullAlg() {\n\n        String header = Encoders.BASE64URL.encode('{\"alg\":null}'.getBytes(Strings.UTF_8))\n        String body = Encoders.BASE64URL.encode('{\"hello\":\"world\"}'.getBytes(Strings.UTF_8))\n        String compact = header + '.' + body + '.'\n\n        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)\n        Mac mac = Mac.getInstance('HmacSHA256')\n        mac.init(key)\n        byte[] signatureBytes = mac.doFinal(compact.getBytes(Strings.UTF_8))\n        String encodedSignature = Encoders.BASE64URL.encode(signatureBytes)\n\n        String invalidJws = compact + encodedSignature\n\n        Jwts.parser().verifyWith(key).build().parseEncryptedClaims(invalidJws)\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseJwsWithEmptyAlg() {\n\n        String header = Encoders.BASE64URL.encode('{\"alg\":\"  \"}'.getBytes(Strings.UTF_8))\n        String body = Encoders.BASE64URL.encode('{\"hello\":\"world\"}'.getBytes(Strings.UTF_8))\n        String compact = header + '.' + body + '.'\n\n        SecretKey key = Keys.secretKeyFor(SignatureAlgorithm.HS256)\n        Mac mac = Mac.getInstance('HmacSHA256')\n        mac.init(key)\n        byte[] signatureBytes = mac.doFinal(compact.getBytes(Strings.UTF_8))\n        String encodedSignature = Encoders.BASE64URL.encode(signatureBytes)\n\n        String invalidJws = compact + encodedSignature\n\n        Jwts.parser().verifyWith(key).build().parseSignedClaims(invalidJws)\n    }\n\n    /*\n    @Test\n    void testIsLikelyJsonWithEmptyString() {\n        assertFalse DefaultJwtParser.isLikelyJson(''.getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testIsLikelyJsonWithEmptyBytes() {\n        assertFalse DefaultJwtParser.isLikelyJson(Bytes.EMPTY)\n    }\n\n    @Test\n    void testIsLikelyJsonWithWhitespaceString() {\n        assertFalse DefaultJwtParser.isLikelyJson(WHITESPACE_STR.getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testIsLikelyJsonWithOnlyOpeningBracket() {\n        assertFalse DefaultJwtParser.isLikelyJson(' {... '.getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testIsLikelyJsonWithOnlyClosingBracket() {\n        assertFalse DefaultJwtParser.isLikelyJson(' } '.getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testIsLikelyJsonMinimalJsonObject() {\n        assertTrue DefaultJwtParser.isLikelyJson(\"{}\".getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testIsLikelyJsonWithLeadingAndTrailingWhitespace() {\n        // all whitespace chars as defined by Character.isWhitespace:\n        String claimsJson = WHITESPACE_STR + '{\"sub\":\"joe\"}' + WHITESPACE_STR\n        assertTrue DefaultJwtParser.isLikelyJson(claimsJson.getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testIsLikelyJsonWithLeadingTextBeforeJsonObject() {\n        // all whitespace chars as defined by Character.isWhitespace:\n        String claimsJson = ' x {\"sub\":\"joe\"}'\n        assertFalse DefaultJwtParser.isLikelyJson(claimsJson.getBytes(StandardCharsets.UTF_8))\n    }\n\n    @Test\n    void testIsLikelyJsonWithTrailingTextAfterJsonObject() {\n        // all whitespace chars as defined by Character.isWhitespace:\n        String claimsJson = '{\"sub\":\"joe\"} x'\n        assertFalse DefaultJwtParser.isLikelyJson(claimsJson.getBytes(StandardCharsets.UTF_8))\n    }\n     */\n\n    @Test\n    void testUnprotectedCritRejected() {\n        def map = [alg: \"none\", crit: [\"whatever\"]]\n        def header = b64Url(serialize(map))\n        String compact = header + '.doesntMatter.'\n        try {\n            Jwts.parser().unsecured().build().parse(compact)\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = String.format(DefaultJwtParser.CRIT_UNSECURED_MSG, map)\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testProtectedCritWithoutAssociatedHeader() {\n        def map = [alg: \"HS256\", crit: [\"whatever\"]]\n        def header = b64Url(serialize(map))\n        String compact = header + '.a.b'\n        try {\n            Jwts.parser().unsecured().build().parse(compact)\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = String.format(DefaultJwtParser.CRIT_MISSING_MSG, 'whatever', 'whatever', map)\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testProtectedCritWithUnsupportedHeader() {\n        def map = [alg: \"HS256\", crit: [\"whatever\"], whatever: 42]\n        def header = b64Url(serialize(map))\n        String compact = header + '.a.b'\n        try {\n            Jwts.parser().unsecured().build().parse(compact)\n            fail()\n        } catch (UnsupportedJwtException expected) {\n            String msg = String.format(DefaultJwtParser.CRIT_UNSUPPORTED_MSG, 'whatever', 'whatever', map)\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testProtectedCritWithSupportedHeader() {\n        def key = TestKeys.HS256\n        def crit = Collections.setOf('whatever')\n        String jws = Jwts.builder()\n                .header().critical().add(crit).and().add('whatever', 42).and()\n                .subject('me')\n                .signWith(key).compact()\n\n        def jwt = Jwts.parser().critical().add(crit).and().verifyWith(key).build().parseSignedClaims(jws)\n\n        // no exception thrown, as expected, check the header values:\n        def parsedCrit = jwt.getHeader().getCritical()\n        assertEquals 1, parsedCrit.size()\n        assertEquals 'whatever', parsedCrit.iterator().next()\n        assertEquals 42, jwt.getHeader().get('whatever')\n    }\n\n    @Test\n    void testExpiredExceptionMessage() {\n\n        long differenceMillis = 843 // arbitrary, anything > 0 is fine\n        def exp = JwtDateConverter.INSTANCE.applyFrom(System.currentTimeMillis() / 1000L)\n        def later = new Date(exp.getTime() + differenceMillis)\n        def s = Jwts.builder().expiration(exp).compact()\n\n        try {\n            Jwts.parser().unsecured().clock(new FixedClock(later)).build().parse(s)\n        } catch (ExpiredJwtException expected) {\n            def exp8601 = DateFormats.formatIso8601(exp, true)\n            def later8601 = DateFormats.formatIso8601(later, true)\n            String msg = \"JWT expired ${differenceMillis} milliseconds ago at ${exp8601}. \" +\n                    \"Current time: ${later8601}. Allowed clock skew: 0 milliseconds.\";\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testNotBeforeExceptionMessage() {\n\n        long differenceMillis = 3842 // arbitrary, anything > 0 is fine\n        def nbf = JwtDateConverter.INSTANCE.applyFrom(System.currentTimeMillis() / 1000L)\n        def earlier = new Date(nbf.getTime() - differenceMillis)\n        def s = Jwts.builder().notBefore(nbf).compact()\n\n        try {\n            Jwts.parser().unsecured().clock(new FixedClock(earlier)).build().parseUnsecuredClaims(s)\n        } catch (PrematureJwtException expected) {\n            def nbf8601 = DateFormats.formatIso8601(nbf, true)\n            def earlier8601 = DateFormats.formatIso8601(earlier, true)\n            String msg = \"JWT early by ${differenceMillis} milliseconds before ${nbf8601}. \" +\n                    \"Current time: ${earlier8601}. Allowed clock skew: 0 milliseconds.\";\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testInvalidB64UrlPayload() {\n        def jwt = Encoders.BASE64URL.encode(Strings.utf8('{\"alg\":\"none\"}'))\n        jwt += \".F!3!#.\" // <-- invalid Base64URL payload\n        try {\n            Jwts.parser().unsecured().build().parse(jwt)\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = 'Invalid Base64Url payload: <redacted>'\n            assertEquals msg, expected.message\n        }\n    }\n\n    @SuppressWarnings('GrDeprecatedAPIUsage')\n    @Test\n    void deprecatedAliases() { // TODO: delete this test when deleting these deprecated methods:\n\n        // parseContentJwt\n        byte[] data = Strings.utf8('hello')\n        def jwt = Jwts.builder().content(data).compact()\n        assertArrayEquals data, Jwts.parser().unsecured().build().parseContentJwt(jwt).getPayload()\n\n        // parseClaimsJwt\n        jwt = Jwts.builder().subject('me').compact()\n        assertEquals 'me', Jwts.parser().unsecured().build().parseClaimsJwt(jwt).getPayload().getSubject()\n\n        // parseContentJws\n        def key = TestKeys.HS256\n        jwt = Jwts.builder().content(data).signWith(key).compact()\n        assertArrayEquals data, Jwts.parser().verifyWith(key).build().parseContentJws(jwt).getPayload()\n\n        // parseClaimsJws\n        jwt = Jwts.builder().subject('me').signWith(key).compact()\n        assertEquals 'me', Jwts.parser().verifyWith(key).build().parseClaimsJws(jwt).getPayload().getSubject()\n\n        //parse(jwt, handler)\n        def value = 'foo'\n        def handler = new JwtHandlerAdapter() {\n            @Override\n            Object onClaimsJws(Jws jws) {\n                return value\n            }\n        }\n        assertEquals value, Jwts.parser().verifyWith(key).build().parse(jwt, handler)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultJwtTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.Jwt\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.io.Encoders\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNotEquals\n\nclass DefaultJwtTest {\n\n    @Test\n    void testToString() {\n        String compact = Jwts.builder().header().add('foo', 'bar').and().audience().add('jsmith').and().compact()\n        Jwt jwt = Jwts.parser().unsecured().build().parseUnsecuredClaims(compact)\n        assertEquals 'header={foo=bar, alg=none},payload={aud=[jsmith]}', jwt.toString()\n    }\n\n    @Test\n    void testByteArrayPayloadToString() {\n        byte[] bytes = 'hello JJWT'.getBytes(StandardCharsets.UTF_8)\n        String encoded = Encoders.BASE64URL.encode(bytes)\n        String compact = Jwts.builder().header().add('foo', 'bar').and().content(bytes).compact()\n        Jwt jwt = Jwts.parser().unsecured().build().parseUnsecuredContent(compact)\n        assertEquals \"header={foo=bar, alg=none},payload=$encoded\" as String, jwt.toString()\n    }\n\n    @Test\n    void testEqualsAndHashCode() {\n        String compact = Jwts.builder().claim('foo', 'bar').compact()\n        def parser = Jwts.parser().unsecured().build()\n        def jwt1 = parser.parseUnsecuredClaims(compact)\n        def jwt2 = parser.parseUnsecuredClaims(compact)\n        assertNotEquals jwt1, 'hello' as String\n        assertEquals jwt1, jwt1\n        assertEquals jwt2, jwt2\n        assertEquals jwt1, jwt2\n        assertEquals jwt1.hashCode(), jwt2.hashCode()\n    }\n\n    @SuppressWarnings('GrDeprecatedAPIUsage')\n    @Test\n    void testBodyAndPayloadSame() {\n        String compact = Jwts.builder().claim('foo', 'bar').compact()\n        def parser = Jwts.parser().unsecured().build()\n        def jwt1 = parser.parseUnsecuredClaims(compact)\n        def jwt2 = parser.parseUnsecuredClaims(compact)\n        assertEquals jwt1.getBody(), jwt1.getPayload()\n        assertEquals jwt2.getBody(), jwt2.getPayload()\n        assertEquals jwt1.getBody(), jwt2.getBody()\n        assertEquals jwt1.getPayload(), jwt2.getPayload()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultMutableJweHeaderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.security.DefaultHashAlgorithm\nimport io.jsonwebtoken.impl.security.DefaultRequest\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\nclass DefaultMutableJweHeaderTest {\n\n    static DefaultMutableJweHeader header\n\n    @Before\n    void setUp() {\n        header = new DefaultMutableJweHeader(Jwts.header() as DefaultJweHeaderMutator)\n    }\n\n    @SuppressWarnings('GroovyAssignabilityCheck')\n    private static void assertSymmetry(String propName, def val) {\n        def name = Strings.capitalize(propName)\n        switch (propName) {\n            case 'algorithm': header.add('alg', val); break // no setter\n            case 'compressionAlgorithm': header.add('zip', val); break // no setter\n            case 'critical': header.critical().add(val).and(); break // no setter\n            default: header.\"$propName\"(val)\n        }\n\n        if (val instanceof byte[]) {\n            assertArrayEquals val, header.\"get$name\"()\n        } else {\n            assertEquals val, header.\"get$name\"()\n        }\n    }\n\n    // ====================== Map Methods =======================\n\n    @Test\n    void testSize() {\n        assertEquals 0, header.size()\n        header.put('foo', 'bar')\n        assertEquals 1, header.size()\n    }\n\n    @Test\n    void testIsEmpty() {\n        assertTrue header.isEmpty()\n        header.put('foo', 'bar')\n        assertFalse header.isEmpty()\n    }\n\n    @Test\n    void testContainsKey() {\n        def key = 'foo'\n        assertFalse header.containsKey(key)\n        header.put(key, 'bar')\n        assertTrue header.containsKey(key)\n    }\n\n    @Test\n    void testContainsValue() {\n        def value = 'bar'\n        assertFalse header.containsValue(value)\n        header.put('foo', value)\n        assertTrue header.containsValue(value)\n    }\n\n    @Test\n    void testGet() {\n        def key = 'foo'\n        def value = 'bar'\n        assertNull header.get(key)\n        header.put(key, value)\n        assertEquals value, header.get(key)\n    }\n\n    @Test\n    void testKeySet() {\n        def key = 'foo'\n        def value = 'bar'\n        assertTrue header.keySet().isEmpty()\n        header.put(key, value)\n        assertFalse header.keySet().isEmpty()\n        assertEquals 1, header.keySet().size()\n        assertEquals key, header.keySet().iterator().next()\n\n        def i = header.keySet().iterator()\n        i.next()\n        i.remove() // assert keyset modification modifies state:\n        assertTrue header.keySet().isEmpty()\n    }\n\n    @Test\n    void testValues() {\n        def key = 'foo'\n        def value = 'bar'\n        assertTrue header.values().isEmpty()\n        header.put(key, value)\n        assertFalse header.values().isEmpty()\n        assertEquals 1, header.values().size()\n        assertEquals value, header.values().iterator().next()\n\n        def i = header.values().iterator()\n        i.next()\n        i.remove() // assert values modification modifies state:\n        assertTrue header.values().isEmpty()\n    }\n\n    @Test\n    void testEntrySet() {\n        def key = 'foo'\n        def value = 'bar'\n        assertTrue header.entrySet().isEmpty()\n        header.put(key, value)\n        assertFalse header.entrySet().isEmpty()\n        assertEquals 1, header.entrySet().size()\n        def entry = header.entrySet().iterator().next()\n        assertEquals key, entry.getKey()\n        assertEquals value, entry.getValue()\n\n        def i = header.entrySet().iterator()\n        i.next()\n        i.remove() // assert values modification modifies state:\n        assertTrue header.entrySet().isEmpty()\n    }\n\n    @Test\n    void testPut() {\n        header.put('foo', 'bar')\n        assertEquals 'bar', header.get('foo')\n    }\n\n    @Test\n    void testPutAll() {\n        def m = ['foo': 'bar', 'baz': 'bat']\n        header.putAll(m)\n        assertEquals m, header\n    }\n\n    @Test\n    void testRemove() {\n        header.put('foo', 'bar')\n        assertFalse header.isEmpty()\n        header.remove('foo')\n        assertTrue header.isEmpty()\n    }\n\n    @Test\n    void testClear() {\n        def m = ['foo': 'bar', 'baz': 'bat']\n        header.putAll(m)\n        assertEquals m, header\n        header.clear()\n        assertTrue header.isEmpty()\n    }\n\n    // ====================== Generic Header Methods =======================\n\n    @Test\n    void testType() {\n        assertSymmetry('type', 'foo')\n    }\n\n    @Test\n    void testContentType() {\n        assertSymmetry('contentType', 'text/plain')\n    }\n\n    @Test\n    void testAlg() {\n        assertSymmetry('algorithm', 'none')\n        assertSymmetry('algorithm', 'HS256')\n    }\n\n    @Test\n    void testCompressionAlgorithm() {\n        assertSymmetry('compressionAlgorithm', 'DEF')\n    }\n\n    // ====================== Protected Header Methods =======================\n\n    /**\n     * Asserts that if the protected-header-only 'jku' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testJwkSetUrl() {\n        URI uri = URI.create('https://github.com/jwtk/jjwt')\n        assertSymmetry('jwkSetUrl', uri)\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'jwk' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testJwk() {\n        def jwk = Jwks.builder().key(TestKeys.RS256.pair.public as RSAPublicKey).build()\n        assertSymmetry('jwk', jwk)\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'kid' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testKeyId() {\n        assertSymmetry('keyId', 'hello')\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'crit' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testCritical() {\n        def crit = ['exp', 'sub'] as Set<String>\n        assertSymmetry('critical', crit)\n    }\n\n    // ====================== X.509 Methods =======================\n\n    /**\n     * Asserts that if the protected-header-only 'x5u' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX09Url() {\n        def uri = URI.create('https://github.com/jwtk/jjwt')\n        assertSymmetry('x509Url', uri)\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'x5c' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX509Chain() {\n        def chain = TestKeys.RS256.chain\n        assertSymmetry('x509Chain', chain)\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'x5t' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX509Sha1Thumbprint() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        def request = new DefaultRequest(payload, null, null)\n        def x5t = DefaultHashAlgorithm.SHA1.digest(request)\n        String encoded = Encoders.BASE64URL.encode(x5t)\n\n        header.x509Sha1Thumbprint(x5t)\n        assertArrayEquals x5t, header.getX509Sha1Thumbprint()\n        assertEquals encoded, header.get('x5t')\n    }\n\n    /**\n     * Asserts that if the protected-header-only 'x5t#S256' member is set, but no JWE-only members are set, a\n     * JwsHeader is created.\n     */\n    @Test\n    void testX509Sha256Thumbprint() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        def request = new DefaultRequest(payload, null, null)\n        def x5tS256 = Jwks.HASH.@SHA256.digest(request)\n        String encoded = Encoders.BASE64URL.encode(x5tS256)\n\n        header.x509Sha256Thumbprint(x5tS256)\n        assertArrayEquals x5tS256, header.getX509Sha256Thumbprint()\n        assertEquals encoded, header.get('x5t#S256')\n    }\n\n    // ====================== JWE Header Methods =======================\n\n    @Test\n    void testEncryptionAlgorithm() {\n        def enc = Jwts.ENC.A256GCM.getId()\n        header.put('enc', enc)\n        assertEquals enc, header.getEncryptionAlgorithm()\n    }\n\n    @Test\n    void testEphemeralPublicKey() {\n        def key = TestKeys.ES256.pair.public\n        def jwk = Jwks.builder().key(key).build()\n        header.put('epk', jwk)\n        assertEquals jwk, header.getEphemeralPublicKey()\n    }\n\n    @Test\n    void testAgreementPartyUInfo() {\n        def info = Strings.utf8(\"UInfo\")\n        assertSymmetry('agreementPartyUInfo', info)\n    }\n\n    @Test\n    void testAgreementPartyUInfoString() {\n        def s = \"UInfo\"\n        def info = Strings.utf8(s)\n        header.agreementPartyVInfo(s)\n        assertArrayEquals info, header.getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testAgreementPartyVInfo() {\n        def info = Strings.utf8(\"VInfo\")\n        assertSymmetry('agreementPartyVInfo', info)\n    }\n\n    @Test\n    void testAgreementPartyVInfoString() {\n        def s = \"VInfo\"\n        def info = Strings.utf8(s)\n        header.agreementPartyVInfo(s)\n        assertArrayEquals info, header.getAgreementPartyVInfo()\n    }\n\n    @Test\n    void testPbes2Salt() {\n        byte[] salt = Bytes.randomBits(256)\n        header.put('p2s', salt)\n        assertArrayEquals salt, header.getPbes2Salt()\n    }\n\n    @Test\n    void testPbes2Count() {\n        int count = 4096\n        assertSymmetry('pbes2Count', count)\n    }\n\n    @Test\n    void testInitializationVector() {\n        byte[] val = Bytes.randomBits(96)\n        header.put('iv', val)\n        assertArrayEquals val, header.getInitializationVector()\n    }\n\n    @Test\n    void testAuthenticationTag() {\n        byte[] val = Bytes.randomBits(128)\n        header.put('tag', val)\n        assertArrayEquals val, header.getAuthenticationTag()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DefaultStubService.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.StubService\n\nclass DefaultStubService implements StubService {\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/DelegateAudienceCollectionTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.ClaimsMutator\nimport org.junit.Test\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.assertSame\n\nclass DelegateAudienceCollectionTest {\n\n    @Test\n    void clear() {\n        ClaimsMutator.AudienceCollection delegate = createMock(ClaimsMutator.AudienceCollection)\n        expect(delegate.clear()).andReturn(delegate)\n        replay(delegate)\n        def c = new DelegateAudienceCollection(this, delegate)\n        assertSame c, c.clear()\n        verify delegate\n    }\n\n    @Test\n    void remove() {\n        String val = 'hello'\n        ClaimsMutator.AudienceCollection delegate = createMock(ClaimsMutator.AudienceCollection)\n        expect(delegate.remove(same(val))).andReturn(delegate)\n        replay(delegate)\n        def c = new DelegateAudienceCollection(this, delegate)\n        assertSame c, c.remove(val)\n        verify delegate\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/FixedClockTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport org.junit.Test\nimport static org.junit.Assert.*\n\nclass FixedClockTest {\n\n    @Test\n    void testFixedClockDefaultConstructor() {\n\n        def clock = new FixedClock()\n\n        def date1 = clock.now()\n        Thread.sleep(100)\n        def date2 = clock.now()\n\n        assertSame date1, date2\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/IdLocatorTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.Identifiable\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.MalformedJwtException\nimport io.jsonwebtoken.UnsupportedJwtException\nimport io.jsonwebtoken.impl.lang.IdRegistry\nimport io.jsonwebtoken.impl.lang.Parameter\nimport io.jsonwebtoken.impl.lang.Parameters\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass IdLocatorTest {\n\n    private static final String exMsg = 'foo is required'\n    private static final Parameter<String> TEST_PARAM = Parameters.string('foo', 'Foo')\n\n    private static IdRegistry registry\n    private static IdLocator locator\n\n    @Before\n    void setUp() {\n        def a = new StringIdentifiable(value: 'A')\n        def b = new StringIdentifiable(value: 'B')\n        registry = new IdRegistry('Foo', [a, b], false)\n        locator = new IdLocator(TEST_PARAM, registry, 'foo', 'bar', exMsg)\n    }\n\n    @Test\n    void unrequiredHeaderValueTest() {\n        locator = new IdLocator(TEST_PARAM, registry, 'foo', 'bar', null)\n        def header = Jwts.header().add('a', 'b').build()\n        assertNull locator.apply(header)\n    }\n\n    @Test\n    void missingRequiredHeaderValueTest() {\n        def header = Jwts.header().build()\n        try {\n            locator.apply(header)\n            fail()\n        } catch (MalformedJwtException expected) {\n            assertEquals exMsg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void unlocatableJwtHeaderInstanceTest() {\n        def header = Jwts.header().add('foo', 'foo').build()\n        try {\n            locator.apply(header)\n        } catch (UnsupportedJwtException expected) {\n            String msg = \"Unsupported JWT header ${TEST_PARAM} value 'foo'.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void unlocatableJwsHeaderInstanceTest() {\n        def header = Jwts.header().add('alg', 'HS256').add('foo', 'foo').build()\n        try {\n            locator.apply(header)\n        } catch (UnsupportedJwtException expected) {\n            String msg = \"Unsupported JWS header ${TEST_PARAM} value 'foo'.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void unlocatableJweHeaderInstanceTest() {\n        def header = Jwts.header().add('alg', 'foo').add('enc', 'A256GCM').add('foo', 'foo').build()\n        try {\n            locator.apply(header)\n        } catch (UnsupportedJwtException expected) {\n            String msg = \"Unsupported JWE header ${TEST_PARAM} value 'foo'.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    static class StringIdentifiable implements Identifiable {\n        String value;\n\n        @Override\n        String getId() {\n            return value;\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/JwtTokenizerTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.MalformedJwtException\nimport io.jsonwebtoken.impl.io.Streams\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.nio.CharBuffer\n\nimport static org.junit.Assert.*\n\nclass JwtTokenizerTest {\n\n    private JwtTokenizer tokenizer\n\n    @Before\n    void setUp() {\n        tokenizer = new JwtTokenizer()\n    }\n\n    private def tokenize(CharSequence s) {\n        return tokenizer.tokenize(Streams.reader(s))\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseWithWhitespaceInBase64UrlHeader() {\n        def input = 'header .body.signature'\n        tokenize(input)\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseWithWhitespaceInBase64UrlBody() {\n        def input = 'header. body.signature'\n        tokenize(input)\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseWithWhitespaceInBase64UrlSignature() {\n        def input = 'header.body. signature'\n        tokenize(input)\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseWithWhitespaceInBase64UrlJweBody() {\n        def input = 'header.encryptedKey.initializationVector. body.authenticationTag'\n        tokenize(input)\n    }\n\n    @Test(expected = MalformedJwtException)\n    void testParseWithWhitespaceInBase64UrlJweTag() {\n        def input = 'header.encryptedKey.initializationVector.body. authenticationTag'\n        tokenize(input)\n    }\n\n    @Test\n    void readerExceptionResultsInMalformedJwtException() {\n        IOException ioe = new IOException('foo')\n        def reader = new StringReader('hello') {\n            @Override\n            int read(char[] chars) throws IOException {\n                throw ioe\n            }\n        }\n        try {\n            JwtTokenizer.read(reader, new char[0])\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = 'Unable to read compact JWT: foo'\n            assertEquals msg, expected.message\n            assertSame ioe, expected.cause\n        }\n    }\n\n    @Test\n    void testEmptyJws() {\n        def input = CharBuffer.wrap('header..digest'.toCharArray())\n        def t = tokenize(input)\n        assertTrue t instanceof TokenizedJwt\n        assertFalse t instanceof TokenizedJwe\n        assertEquals 'header', t.getProtected().toString()\n        assertEquals '', t.getPayload().toString()\n        assertEquals 'digest', t.getDigest().toString()\n    }\n\n    @Test\n    void testJwe() {\n\n        def input = 'header.encryptedKey.initializationVector.body.authenticationTag'\n\n        def t = tokenize(input)\n\n        assertNotNull t\n        assertTrue t instanceof TokenizedJwe\n        TokenizedJwe tjwe = (TokenizedJwe) t\n        assertEquals 'header', tjwe.getProtected()\n        assertEquals 'encryptedKey', tjwe.getEncryptedKey()\n        assertEquals 'initializationVector', tjwe.getIv()\n        assertEquals 'body', tjwe.getPayload()\n        assertEquals 'authenticationTag', tjwe.getDigest()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/ParameterMapTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\n\nimport io.jsonwebtoken.impl.lang.Parameter\nimport io.jsonwebtoken.impl.lang.Parameters\nimport io.jsonwebtoken.impl.security.Randoms\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.lang.Registry\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass ParameterMapTest {\n\n    private static final Parameter<String> DUMMY = Parameters.string('' + Randoms.secureRandom().nextInt(), \"RANDOM\")\n    private static final Parameter<BigInteger> SECRET = Parameters.secretBigInt('foo', 'foo')\n    private static final Set<Parameter<?>> PARAM_SET = Collections.setOf(DUMMY)\n    private static final Registry<String, Parameter<?>> PARAMS = Parameters.registry(PARAM_SET)\n    ParameterMap jwtMap\n\n    @Before\n    void setup() {\n        // dummy param to satisfy constructor:\n        jwtMap = new ParameterMap(PARAMS)\n    }\n\n    void unsupported(Closure<?> c) {\n        try {\n            c()\n            fail(\"Should have thrown\")\n        } catch (UnsupportedOperationException expected) {\n            String msg = \"${jwtMap.getName()} instance is immutable and may not be modified.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testImmutable() {\n        Map mutable = jwtMap\n        mutable.put('foo', 'bar')\n        Map immutable = new ParameterMap(PARAMS, mutable) // make immutable\n\n        unsupported { immutable.put('whatever', 'value') }\n        unsupported { immutable.putAll([a: 'b']) }\n        unsupported { immutable.remove('foo') }\n        unsupported { immutable.clear() }\n\n        mutable.clear() // no exception\n        assertEquals 0, mutable.size()\n    }\n\n    @Test\n    void testContainsKey() {\n        jwtMap.put('foo', 'bar')\n        assertTrue jwtMap.containsKey('foo')\n    }\n\n    @Test\n    void testContainsValue() {\n        jwtMap.put('foo', 'bar')\n        assertTrue jwtMap.containsValue('bar')\n    }\n\n    @Test\n    void testRemoveByPuttingNull() {\n        jwtMap.put('foo', 'bar')\n        assertTrue jwtMap.containsKey('foo')\n        assertTrue jwtMap.containsValue('bar')\n        jwtMap.put('foo', null)\n        assertFalse jwtMap.containsKey('foo')\n        assertFalse jwtMap.containsValue('bar')\n    }\n\n    @Test\n    void testPutAll() {\n        jwtMap.putAll([a: 'b', c: 'd'])\n        assertEquals jwtMap.size(), 2\n        assertEquals jwtMap.a, 'b'\n        assertEquals jwtMap.c, 'd'\n    }\n\n    @Test\n    void testPutAllWithNullArgument() {\n        jwtMap.putAll((Map) null)\n        assertEquals jwtMap.size(), 0\n    }\n\n    @Test\n    void testClear() {\n        jwtMap.put('foo', 'bar')\n        assertEquals jwtMap.size(), 1\n        jwtMap.clear()\n        assertEquals jwtMap.size(), 0\n    }\n\n    @Test\n    void testKeySet() {\n        jwtMap.putAll([a: 'b', c: 'd'])\n        assertEquals(jwtMap.keySet(), ['a', 'c'] as Set)\n    }\n\n    @Test\n    void testValues() {\n        jwtMap.putAll([a: 'b', c: 'd'])\n        def s = ['b', 'd']\n        assertTrue jwtMap.values().containsAll(s) && s.containsAll(jwtMap.values())\n    }\n\n    @Test\n    void testEquals() throws Exception {\n        def m1 = new ParameterMap(PARAMS)\n        m1.put(\"a\", \"a\")\n\n        def m2 = new ParameterMap(PARAMS)\n        m2.put(\"a\", \"a\")\n\n        assertEquals(m1, m2)\n    }\n\n    @Test\n    void testHashcode() throws Exception {\n        def hashCodeEmpty = jwtMap.hashCode()\n\n        jwtMap.put(\"a\", \"b\")\n        def hashCodeNonEmpty = jwtMap.hashCode()\n        assertTrue(hashCodeEmpty != hashCodeNonEmpty)\n\n        def identityHash = System.identityHashCode(jwtMap)\n        assertTrue(hashCodeNonEmpty != identityHash)\n    }\n\n    @Test\n    void testGetName() {\n        def map = new ParameterMap(PARAMS)\n        assertEquals 'Map', map.getName()\n    }\n\n    @Test\n    void testSetSecretFieldWithInvalidTypeValue() {\n        def map = new ParameterMap(Parameters.registry(SECRET))\n        def invalidValue = URI.create('https://whatever.com')\n        try {\n            map.put('foo', invalidValue)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            //Ensure <redacted> message so we don't show any secret value:\n            String msg = 'Invalid Map \\'foo\\' (foo) value: <redacted>. Values must be ' +\n                    'either String or java.math.BigInteger instances. Value type found: ' +\n                    'java.net.URI.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test(expected = IllegalStateException)\n    void testIteratorRemoveWithoutIteration() {\n        jwtMap.iterator().remove()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/RfcTests.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl\n\nimport io.jsonwebtoken.impl.io.CharSequenceReader\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.impl.security.Randoms\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.io.Encoders\n\nclass RfcTests {\n\n    static String encode(byte[] b) {\n        return Encoders.BASE64URL.encode(b)\n    }\n\n    static byte[] decode(String val) {\n        return Decoders.BASE64URL.decode(val)\n    }\n\n    static final String stripws(String s) {\n        return s.replaceAll('[\\\\s]', '')\n    }\n\n    static final Map<String, ?> jsonToMap(String json) {\n        Reader r = new CharSequenceReader(json)\n        Map<String, ?> m = Services.get(Deserializer).deserialize(r) as Map<String, ?>\n        return m\n    }\n\n    /**\n     * Returns a random string useful as a test value NOT to be used as a cryptographic key.\n     * @return a random string useful as a test value NOT to be used as a cryptographic key.\n     */\n    static String srandom() {\n        byte[] random = new byte[16]\n        Randoms.secureRandom().nextBytes(random)\n        return Encoders.BASE64URL.encode(random)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/compression/AbstractCompressionAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2015 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.compression\n\nimport io.jsonwebtoken.CompressionException\nimport io.jsonwebtoken.impl.lang.Bytes\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\n/**\n * @since 0.6.0\n */\nclass AbstractCompressionAlgorithmTest {\n\n    @Test\n    void testCompressNull() {\n        def alg = new ExceptionThrowingAlgorithm()\n        assertSame Bytes.EMPTY, alg.compress((byte[])null)\n    }\n\n    @Test\n    void testCompressEmpty() {\n        def alg = new ExceptionThrowingAlgorithm()\n        assertSame Bytes.EMPTY, alg.compress(new byte[0])\n    }\n\n    @Test(expected = CompressionException.class)\n    void testCompressWithException() {\n        def alg = new ExceptionThrowingAlgorithm()\n        alg.compress(new byte[1])\n    }\n\n    @Test\n    void testDecompressEmpty() {\n        def alg = new ExceptionThrowingAlgorithm()\n        assertSame Bytes.EMPTY, alg.decompress(new byte[0])\n    }\n\n    @Test(expected = CompressionException.class)\n    void testDecompressWithException() {\n        def alg = new ExceptionThrowingAlgorithm()\n        alg.decompress(new byte[1])\n    }\n\n    @Test\n    void testGetId() {\n        assertEquals \"Test\", new ExceptionThrowingAlgorithm().getId()\n    }\n\n    @Test\n    void testAlgorithmName() {\n        assertEquals \"Test\", new ExceptionThrowingAlgorithm().getAlgorithmName()\n    }\n\n    static class ExceptionThrowingAlgorithm extends AbstractCompressionAlgorithm {\n\n        ExceptionThrowingAlgorithm() {\n            super(\"Test\")\n        }\n\n        @Override\n        protected OutputStream doCompress(OutputStream out) throws IOException {\n            throw new IOException(\"Test Wrap OutputStream Exception\")\n        }\n\n        @Override\n        protected InputStream doDecompress(InputStream is) throws IOException {\n            throw new IOException(\"Test Wrap InputStream Exception\")\n        }\n\n        @Override\n        protected byte[] doDecompress(byte[] payload) throws IOException {\n            throw new IOException(\"Test Decompress Exception\")\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/compression/DeflateCompressionCodecTest.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.compression\n\nimport io.jsonwebtoken.CompressionException\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.io.Decoders\nimport org.junit.Test\n\nimport static org.junit.Assert.assertNotSame\n\n/**\n * @since 0.10.8\n */\nclass DeflateCompressionCodecTest {\n\n    /**\n     * Test case for <a href=\"https://github.com/jwtk/jjwt/issues/536\">Issue 536</a>.\n     */\n    @Test\n    void testBackwardsCompatibility_0_10_6() {\n        final String jwtFrom0106 = 'eyJhbGciOiJub25lIiwiemlwIjoiREVGIn0.eNqqVsosLlayUspNVdJRKi5NAjJLi1OLgJzMxBIlK0sTMzMLEwsDAx2l1IoCJSsTQwMjExOQQC0AAAD__w.'\n        Jwts.parser().unsecured().unsecuredDecompression().build().parseUnsecuredClaims(jwtFrom0106) // no exception should be thrown\n    }\n\n    /**\n     * Test to ensure that, even if the backwards-compatibility fallback method throws an exception, that the first\n     * one is retained/re-thrown to reflect the correct/expected implementation.\n     */\n    @Test\n    void testBackwardsCompatibilityRetainsFirstIOException() {\n\n        final String compressedFrom0_10_6 = 'eNqqVsosLlayUspNVdJRKi5NAjJLi1OLgJzMxBIlK0sTMzMLEwsDAx2l1IoCJSsTQwMjExOQQC0AAAD__w'\n        byte[] invalid = Decoders.BASE64URL.decode(compressedFrom0_10_6)\n\n        IOException unexpected = new IOException(\"foo\")\n\n        def codec = new DeflateCompressionAlgorithm() {\n            @Override\n            byte[] doDecompressBackCompat(byte[] compressed) throws IOException {\n                throw unexpected\n            }\n        }\n\n        try {\n            codec.decompress(invalid)\n        } catch (CompressionException ce) {\n            assertNotSame(unexpected, ce.getCause())\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/compression/YagCompressionCodec.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.compression\n\nimport io.jsonwebtoken.CompressionCodec\nimport io.jsonwebtoken.CompressionException\n\n/**\n * Yet Another GZIP CompressionCodec.  This codec has the same name as the Official GZIP impl. The IdLocator will NOT resolve this class.\n */\nclass YagCompressionCodec implements CompressionCodec {\n\n    @Override\n    String getId() {\n        return GzipCompressionAlgorithm.ID\n    }\n\n    @Override\n    String getAlgorithmName() {\n        return getId()\n    }\n\n    @Override\n    byte[] compress(byte[] content) throws CompressionException {\n        return new byte[0]\n    }\n\n    @Override\n    byte[] decompress(byte[] compressed) throws CompressionException {\n        return new byte[0]\n    }\n\n    @Override\n    OutputStream compress(OutputStream out) throws CompressionException {\n        return out\n    }\n\n    @Override\n    InputStream decompress(InputStream inputStream) throws CompressionException {\n        return inputStream\n    }\n}"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/ClosedInputStreamTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass ClosedInputStreamTest {\n\n    @Test\n    void read() {\n        assertEquals Streams.EOF, ClosedInputStream.INSTANCE.read()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/CodecTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.io.DecodingException\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass CodecTest {\n\n    @Test\n    void testDecodingExceptionThrowsIAE() {\n        CharSequence s = 't#t'\n        try {\n            Codec.BASE64URL.applyFrom(s)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            def cause = expected.getCause()\n            assertTrue cause instanceof DecodingException\n            String msg = \"Cannot decode input String. Cause: ${cause.getMessage()}\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/CountingInputStreamTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass CountingInputStreamTest {\n\n    @Test\n    void readEmpty() {\n        def stream = new CountingInputStream(Streams.of(Bytes.EMPTY))\n        stream.read()\n        assertEquals 0, stream.getCount()\n    }\n\n    @Test\n    void readSingle() {\n        def single = (byte) 0x18 // any random byte is fine\n        def data = new byte[1]; data[0] = single\n        def stream = new CountingInputStream(Streams.of(data))\n        assertEquals single, stream.read()\n        assertEquals 1, stream.getCount()\n    }\n\n    @Test\n    void testSkip() {\n        def data = Strings.utf8('hello world')\n        def stream = new CountingInputStream(Streams.of(data))\n        stream.skip(6)\n        assertEquals 6, stream.getCount()\n        int w = ('w' as char)\n        assertEquals w, stream.read()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/DecodingInputStreamTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.io.DecodingException\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass DecodingInputStreamTest {\n\n    @Test\n    void decodingException() {\n        def ins = new ByteArrayInputStream(Strings.utf8('test')) {\n            @Override\n            synchronized int read() {\n                throw new IOException(\"foo\")\n            }\n        }\n\n        def decoding = new DecodingInputStream(ins, 'base64url', 'payload')\n\n        try {\n            decoding.read()\n            fail()\n        } catch (DecodingException expected) {\n            String msg = 'Unable to base64url-decode payload: foo'\n            assertEquals msg, expected.message\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/DelegateStringDecoderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.io.Decoder\nimport io.jsonwebtoken.io.DecodingException\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.assertArrayEquals\n\n@SuppressWarnings('GrDeprecatedAPIUsage')\nclass DelegateStringDecoderTest {\n\n    @Test\n    void decode() {\n        def value = 'test'\n        def bytes = Strings.utf8(value)\n        def ins = Streams.of(bytes)\n        def test = new Decoder<CharSequence, byte[]>() {\n            @Override\n            byte[] decode(CharSequence s) throws DecodingException {\n                return Strings.utf8(value)\n            }\n        }\n        def d = new DelegateStringDecoder(test)\n\n        assertArrayEquals bytes, Streams.bytes(d.decode(ins), 'foo')\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/EncodingOutputStreamTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.io.EncodingException\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass EncodingOutputStreamTest {\n\n    @Test\n    void testEncodingException() {\n        def out = new ByteArrayOutputStream(128) {\n            @Override\n            synchronized void write(int b) {\n                throw new IOException('foo')\n            }\n        }\n        def wrapped = new EncodingOutputStream(out, 'base64url', 'payload')\n\n        try {\n            wrapped.write(1)\n            fail()\n        } catch (EncodingException expected) {\n            String msg = 'Unable to base64url-encode payload: foo'\n            assertEquals msg, expected.message\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/JsonObjectDeserializerTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.MalformedJwtException\nimport io.jsonwebtoken.io.DeserializationException\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass JsonObjectDeserializerTest {\n\n    /**\n     * It's possible for JSON parsers to throw a StackOverflowError when body is deeply nested. Since it's possible\n     * across multiple parsers, JJWT handles the exception when parsing.*/\n    @Test\n    void testStackOverflowError() {\n        def err = new StackOverflowError('foo')\n        // create one that will throw a StackOverflowError\n        def deser = new Deserializer() {\n            @Override\n            Object deserialize(byte[] bytes) throws DeserializationException {\n                fail() // shouldn't be called in this test\n                return null\n            }\n\n            @Override\n            Object deserialize(Reader reader) throws DeserializationException {\n                throw err\n            }\n        }\n        try {\n            // doesn't matter for this test, just has to be non-null:\n            def r = new StringReader(Strings.EMPTY)\n            new JsonObjectDeserializer(deser, 'claims').apply(r)\n            fail()\n        } catch (DeserializationException e) {\n            String msg = String.format(JsonObjectDeserializer.MALFORMED_COMPLEX_ERROR, 'claims', 'claims', 'foo')\n            assertEquals msg, e.message\n        }\n    }\n\n    /**\n     * Check that a DeserializationException is wrapped and rethrown as a MalformedJwtException with a developer friendly message.*/\n    @Test\n    void testDeserializationExceptionMessage() {\n        def ex = new IOException('foo')\n        // create one that will throw a StackOverflowError\n        def deser = new Deserializer() {\n            @Override\n            Object deserialize(byte[] bytes) throws DeserializationException {\n                fail() // should not be called in this test\n                return null\n            }\n\n            @Override\n            Object deserialize(Reader reader) throws DeserializationException {\n                throw ex\n            }\n        }\n        try {\n            // doesn't matter for this test, just has to be non-null:\n            def r = new StringReader(Strings.EMPTY)\n            new JsonObjectDeserializer(deser, 'claims').apply(r)\n            fail()\n        } catch (MalformedJwtException e) {\n            String msg = String.format(JsonObjectDeserializer.MALFORMED_ERROR, 'claims', 'foo')\n            assertEquals msg, e.message\n            assertSame ex, e.cause\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/StreamsTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport org.junit.Test\n\nimport java.util.concurrent.Callable\n\nimport static org.junit.Assert.*\n\nclass StreamsTest {\n\n    @Test\n    void runWrapsExceptionAsRuntimeIOException() {\n        def ex = new RuntimeException('foo')\n        def c = new Callable() {\n            @Override\n            Object call() throws Exception {\n                throw ex\n            }\n        }\n        try {\n            Streams.run(c, 'bar')\n            fail()\n        } catch (io.jsonwebtoken.io.IOException expected) {\n            String msg = 'IO failure: bar. Cause: foo'\n            assertEquals msg, expected.message\n            assertSame ex, expected.cause\n        }\n    }\n\n    @Test\n    void runWrapsExceptionAsRuntimeIOExceptionWithPunctuation() {\n        def ex = new RuntimeException('foo')\n        def c = new Callable() {\n            @Override\n            Object call() throws Exception {\n                throw ex\n            }\n        }\n        try {\n            Streams.run(c, 'bar.') // period at the end, don't add another\n            fail()\n        } catch (io.jsonwebtoken.io.IOException expected) {\n            String msg = 'IO failure: bar. Cause: foo'\n            assertEquals msg, expected.message\n            assertSame ex, expected.cause\n        }\n    }\n\n    @Test\n    void streamFromNullByteArray() {\n        def stream = Streams.of((byte[]) null)\n        assertNotNull stream\n        assertEquals 0, stream.available()\n        assertEquals(-1, stream.read())\n    }\n\n    @Test\n    void streamWithEmptyByteArray() {\n        def stream = Streams.of(Bytes.EMPTY)\n        assertNotNull stream\n        assertEquals 0, stream.available()\n        assertEquals(-1, stream.read())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/TeeOutputStreamTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertArrayEquals\nimport static org.junit.Assert.assertTrue\n\n/**\n * @since 0.12.0\n */\nclass TeeOutputStreamTest {\n\n    @Test\n    void flush() {\n        boolean aFlushed = false\n        boolean bFlushed = false\n        def a = new ByteArrayOutputStream() {\n            @Override\n            void flush() throws IOException {\n                aFlushed = true\n            }\n        }\n        def b = new ByteArrayOutputStream() {\n            @Override\n            void flush() throws IOException {\n                bFlushed = true\n            }\n        }\n        def tee = new TeeOutputStream(a, b)\n        tee.flush()\n        assertTrue aFlushed\n        assertTrue bFlushed\n    }\n\n    @Test\n    void close() {\n        boolean aClosed = false\n        boolean bClosed = false\n        def a = new ByteArrayOutputStream() {\n            @Override\n            void close() throws IOException {\n                aClosed = true\n            }\n        }\n        def b = new ByteArrayOutputStream() {\n            @Override\n            void close() throws IOException {\n                bClosed = true\n            }\n        }\n        def tee = new TeeOutputStream(a, b)\n        tee.close()\n        assertTrue aClosed\n        assertTrue bClosed\n    }\n\n    @Test\n    void writeByte() {\n        def a = new ByteArrayOutputStream()\n        def b = new ByteArrayOutputStream()\n        def tee = new TeeOutputStream(a, b)\n        byte aByte = 0x15 as byte // any random value is fine\n        byte[] expected = new byte[1]; expected[0] = aByte\n\n        tee.write(aByte)\n\n        assertArrayEquals expected, a.toByteArray()\n        assertArrayEquals expected, b.toByteArray()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/io/TestSerializer.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.io\n\nimport io.jsonwebtoken.io.SerializationException\nimport io.jsonwebtoken.io.Serializer\nimport io.jsonwebtoken.lang.Strings\n\nimport static org.junit.Assert.fail\n\nclass TestSerializer implements Serializer<Map<String, ?>> {\n\n    Throwable ex\n\n    @Override\n    byte[] serialize(Map<String, ?> stringMap) throws SerializationException {\n        fail(\"serialize(byte[]) should not be invoked.\")\n        return null\n    }\n\n    @Override\n    void serialize(Map<String, ?> map, OutputStream out) throws SerializationException {\n        def json = toJson(map)\n        if (Strings.hasText(json)) {\n            out.write(Strings.utf8(json))\n        } else {\n            Throwable t = ex != null ? ex : new UnsupportedOperationException(\"Override toJson\")\n            throw t\n        }\n    }\n\n    protected String toJson(Map<String, ?> m) {\n        return null\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/BigIntegerUBytesConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.io.Decoders\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass BigIntegerUBytesConverterTest {\n\n    private BigIntegerUBytesConverter CONVERTER = Converters.BIGINT_UBYTES as BigIntegerUBytesConverter\n\n    @Test\n    void testNegative() {\n        try {\n            CONVERTER.applyTo(BigInteger.valueOf(-1))\n            fail()\n        } catch (IllegalArgumentException expected) {\n            assertEquals BigIntegerUBytesConverter.NEGATIVE_MSG, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testZero() {\n        byte[] result = CONVERTER.applyTo(BigInteger.ZERO)\n        assertEquals 1, result.length\n        assertTrue result[0] == 0x00 as byte\n    }\n\n    @Test\n    void testStripSignByte() {\n        BigInteger val = BigInteger.valueOf(128)\n        byte[] bytes = val.toByteArray()\n        byte[] result = CONVERTER.applyTo(val)\n        assertEquals bytes.length - 1, result.length\n    }\n\n    /**\n     * Asserts security considerations in https://www.rfc-editor.org/rfc/rfc7638#section-7, 4th paragraph.\n     */\n    @Test\n    void testStripLeadingZeroBytes() {\n\n        byte[] bytes1 = Decoders.BASE64URL.decode(\"AAEAAQ\")\n        byte[] bytes2 = Decoders.BASE64URL.decode(\"AQAB\")\n\n        BigInteger bi1 = CONVERTER.applyFrom(bytes1)\n        BigInteger bi2 = CONVERTER.applyFrom(bytes2)\n\n        assertEquals bi1, bi2\n    }\n\n    /**\n     * Asserts https://www.rfc-editor.org/rfc/rfc7518.html#section-2, 'Base64urlUInt' definition, last sentence:\n     * <pre>Zero is represented as BASE64URL(single zero-valued octet), which is \"AA\".</pre>\n     */\n    @Test\n    void testZeroProducesAABase64Url() {\n        assertEquals 'AA', Converters.BIGINT.applyTo(BigInteger.ZERO)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/BytesTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.impl.security.Randoms\nimport org.junit.Test\n\nimport java.security.MessageDigest\n\nimport static org.junit.Assert.*\n\nclass BytesTest {\n\n    static final Random RANDOM = Randoms.secureRandom()\n\n    static final byte[] A = [0x00, 0x01, 0x02, 0x03, 0x04, 0x05] as byte[]\n    static final byte[] B = [0x02, 0x03] as byte[]\n    static final byte[] C = [0x05, 0x06] as byte[]\n    static final byte[] D = [0x06, 0x07] as byte[]\n\n    @Test\n    void testPrivateCtor() { // for code coverage only\n        new Bytes()\n    }\n\n    @Test\n    void testRandom() {\n        byte[] random = Bytes.random(12)\n        assertEquals 12, random.length\n    }\n\n    @Test\n    void testRandomBits() {\n        int count = 16\n        byte[] random = Bytes.randomBits(count * Byte.SIZE)\n        assertEquals count, random.length\n    }\n\n    @Test\n    void testRandomBitsZeroLength() {\n        try {\n            Bytes.randomBits(0)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            assertEquals 'numBytes argument must be >= 0', expected.getMessage()\n        }\n    }\n\n    @Test\n    void testRandomBitsNegativeLength() {\n        try {\n            Bytes.randomBits(-1)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            assertEquals 'numBytes argument must be >= 0', expected.getMessage()\n        }\n    }\n\n    @Test\n    void testIntToBytesToInt() {\n        int iterations = 10000\n        for (int i = 0; i < iterations; i++) {\n            int a = RANDOM.nextInt()\n            byte[] bytes = Bytes.toBytes(a)\n            int b = Bytes.toInt(bytes)\n            assertEquals a, b\n        }\n    }\n\n    @Test\n    void testLongToBytesToLong() {\n        int iterations = 10000\n        for (int i = 0; i < iterations; i++) {\n            long a = RANDOM.nextLong()\n            byte[] bytes = Bytes.toBytes(a)\n            long b = Bytes.toLong(bytes)\n            assertEquals a, b\n        }\n    }\n\n    @Test\n    void testConcatNull() {\n        byte[] output = Bytes.concat(null)\n        assertNotNull output\n        assertEquals 0, output.length\n    }\n\n    @Test\n    void testConcatSingle() {\n        byte[] bytes = new byte[32]\n        RANDOM.nextBytes(bytes)\n        byte[][] arg = [bytes] as byte[][]\n        byte[] output = Bytes.concat(arg)\n        assertTrue MessageDigest.isEqual(bytes, output)\n    }\n\n    @Test\n    void testConcatSingleEmpty() {\n        byte[] bytes = new byte[0]\n        byte[][] arg = [bytes] as byte[][]\n        byte[] output = Bytes.concat(arg)\n        assertNotNull output\n        assertEquals 0, output.length\n    }\n\n    @Test\n    void testConcatMultiple() {\n        byte[] a = new byte[32]; RANDOM.nextBytes(a)\n        byte[] b = new byte[16]; RANDOM.nextBytes(b)\n\n        byte[] output = Bytes.concat(a, b)\n\n        assertNotNull output\n        assertEquals a.length + b.length, output.length\n\n        byte[] partA = new byte[a.length]\n        System.arraycopy(output, 0, partA, 0, a.length)\n        assertTrue MessageDigest.isEqual(a, partA)\n\n        byte[] partB = new byte[b.length]\n        System.arraycopy(output, a.length, partB, 0, b.length)\n        assertTrue MessageDigest.isEqual(b, partB)\n    }\n\n    @Test\n    void testConcatMultipleWithOneEmpty() {\n\n        byte[] a = new byte[32]; RANDOM.nextBytes(a)\n        byte[] b = new byte[0]\n\n        byte[] output = Bytes.concat(a, b)\n\n        assertNotNull output\n        assertEquals a.length + b.length, output.length\n\n        byte[] partA = new byte[a.length]\n        System.arraycopy(output, 0, partA, 0, a.length)\n        assertTrue MessageDigest.isEqual(a, partA)\n\n        byte[] partB = new byte[b.length]\n        System.arraycopy(output, a.length, partB, 0, b.length)\n        assertTrue MessageDigest.isEqual(b, partB)\n    }\n\n    @Test\n    void testLength() {\n        int len = 32\n        assertEquals len, Bytes.length(new byte[len])\n    }\n\n    @Test\n    void testLengthZero() {\n        assertEquals 0, Bytes.length(new byte[0])\n    }\n\n    @Test\n    void testLengthNull() {\n        assertEquals 0, Bytes.length(null)\n    }\n\n    @Test\n    void testBitLength() {\n        int len = 32\n        byte[] a = new byte[len]\n        assertEquals len * Byte.SIZE, Bytes.bitLength(a)\n    }\n\n    @Test\n    void testBitLengthZero() {\n        assertEquals 0, Bytes.bitLength(new byte[0])\n    }\n\n    @Test\n    void testBitLengthNull() {\n        assertEquals 0, Bytes.bitLength(null)\n    }\n\n    @Test\n    void testIncrement() {\n\n        byte[] counter = Bytes.toBytes(0)\n        for (int i = 0; i < 100; i++) {\n            assertEquals i, Bytes.toInt(counter)\n            Bytes.increment(counter)\n        }\n\n        counter = Bytes.toBytes(Integer.MAX_VALUE - 1)\n\n        Bytes.increment(counter)\n        assertEquals Integer.MAX_VALUE, Bytes.toInt(counter)\n\n        //check correct integer overflow:\n        Bytes.increment(counter)\n        assertEquals Integer.MIN_VALUE, Bytes.toInt(counter)\n    }\n\n    @Test\n    void testIncrementEmpty() {\n        byte[] counter = new byte[0]\n        Bytes.increment(counter)\n        assertTrue MessageDigest.isEqual(new byte[0], counter)\n    }\n\n    @Test\n    void testIndexOfFromIndexOOB() {\n        int i = Bytes.indexOf(A, 0, A.length, B, 0, B.length, A.length)\n        assertEquals(-1, i)\n    }\n\n    @Test\n    void testIndexOfFromIndexOOBWithZeroLengthTarget() {\n        int i = Bytes.indexOf(A, 0, A.length, B, 0, 0, A.length)\n        assertEquals(A.length, i)\n    }\n\n    @Test\n    void testIndexOfFromIndexNegative() {\n        int i = Bytes.indexOf(A, 0, A.length, B, 0, B.length, -1) // should normalize fromIndex to be zero\n        assertEquals(2, i) // B starts at A index 2\n    }\n\n    @Test\n    void testIndexOfEmptyTargetIsZero() {\n        int i = Bytes.indexOf(A, Bytes.EMPTY)\n        assertEquals(0, i)\n    }\n\n    @Test\n    void testIndexOfOOBSrcIndex() {\n        int i = Bytes.indexOf(A, 3, 2, B, 1, A.length, 0)\n        assertEquals(-1, i)\n    }\n\n    @Test\n    void testIndexOfDisjointSrcAndTarget() {\n        int i = Bytes.indexOf(A, D)\n        assertEquals(-1, i)\n    }\n\n    @Test\n    void testIndexOfPartialMatch() {\n        int i = Bytes.indexOf(A, C)\n        assertEquals(-1, i)\n    }\n\n    @Test\n    void testIndexOfPartialMatchEndDifferent() {\n        byte[] toTest = [0x00, 0x01, 0x02, 0x03, 0x04, 0x06] // last byte is different in A\n        int i = Bytes.indexOf(A, toTest)\n        assertEquals(-1, i)\n    }\n\n    @Test\n    void testStartsWith() {\n        byte[] A = [0x01, 0x02, 0x03]\n        byte[] B = [0x01, 0x03]\n        byte[] C = [0x02, 0x03]\n        assertTrue Bytes.startsWith(A, A, 0)\n        assertFalse Bytes.startsWith(A, B)\n        assertTrue Bytes.endsWith(A, C)\n        assertFalse Bytes.startsWith(A, A, -1)\n        assertFalse Bytes.startsWith(C, A)\n    }\n\n    @Test\n    void testBytesLength() {\n        // zero bits means we don't need any bytes:\n        assertEquals 0, Bytes.length(0) // zero bits means we don't need any bytes\n        assertEquals 1, Bytes.length(1) // one bit needs at least 1 byte\n        assertEquals 1, Bytes.length(8) // 8 bits fits into 1 byte\n        assertEquals 2, Bytes.length(9) // need at least 2 bytes for 9 bits\n        assertEquals 66, Bytes.length(521) // P-521 curve order bit length\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testBytesLengthNegative() {\n        Bytes.length(-1)\n    }\n\n    @Test\n    void testClearNull() {\n        Bytes.clear(null) // no exception\n    }\n\n    @Test\n    void testClearEmpty() {\n        Bytes.clear(Bytes.EMPTY) // no exception\n    }\n\n    @Test\n    void testClear() {\n        int len = 16\n        byte[] bytes = Bytes.random(len)\n        boolean allZero = true\n        for (int i = 0; i < len; i++) {\n            if (bytes[i] != (byte) 0) {\n                allZero = false\n                break\n            }\n        }\n        assertFalse allZero // guarantee that we start with random bytes\n\n        Bytes.clear(bytes)\n\n        allZero = true\n        for (int i = 0; i < len; i++) {\n            if (bytes[i] != (byte) 0) {\n                allZero = false\n                break\n            }\n        }\n        assertTrue allZero // asserts zeroed out entirely\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/CollectionConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Encoders\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.*\n\nclass CollectionConverterTest {\n\n    private static final UriStringConverter ELEMENT_CONVERTER = new UriStringConverter(); //any will do\n\n    @Test\n    void testApplyToNull() {\n        assertNull Converters.forSet(ELEMENT_CONVERTER).applyTo(null)\n        assertNull Converters.forList(ELEMENT_CONVERTER).applyTo(null)\n    }\n\n    @Test\n    void testApplyToEmpty() {\n        def set = [] as Set\n        assertSame set, Converters.forSet(ELEMENT_CONVERTER).applyTo(set)\n        def list = [] as List\n        assertSame list, Converters.forList(ELEMENT_CONVERTER).applyTo(list)\n    }\n\n    @Test\n    void testApplyFromNull() {\n        assertNull Converters.forSet(ELEMENT_CONVERTER).applyFrom(null)\n        assertNull Converters.forList(ELEMENT_CONVERTER).applyFrom(null)\n    }\n\n    @Test\n    void testApplyFromEmpty() {\n        def set = Converters.forSet(ELEMENT_CONVERTER).applyFrom([] as Set)\n        assertNotNull set\n        assertTrue set.isEmpty()\n        def list = Converters.forList(ELEMENT_CONVERTER).applyFrom([])\n        assertNotNull list\n        assertTrue list.isEmpty()\n    }\n\n    @Test\n    void testApplyFromNonPrimitiveArray() {\n\n        String url = 'https://github.com/jwtk/jjwt'\n        URI uri = ELEMENT_CONVERTER.applyFrom(url)\n        def array = [url] as String[]\n\n        def set = Converters.forSet(ELEMENT_CONVERTER).applyFrom(array)\n        assertNotNull set\n        assertEquals 1, set.size()\n        assertEquals uri, set.iterator().next()\n\n        def list = Converters.forList(ELEMENT_CONVERTER).applyFrom(array)\n        assertNotNull list\n        assertEquals 1, list.size()\n        assertEquals uri, set.iterator().next()\n    }\n\n    @Test\n    void testApplyFromPrimitiveArray() {\n\n        // ensure the primitive array is not converted to a collection.  That is,\n        // a byte array of length 4 should not return a collection of size 4.  It should return a collection of size 1\n        // and that element is the byte array\n\n        Converter<String, Object> converter = new Converter<String, Object>() {\n            @Override\n            Object applyTo(String s) {\n                return Decoders.BASE64URL.decode(s);\n            }\n\n            @Override\n            String applyFrom(Object o) {\n                return Encoders.BASE64URL.encode((byte[]) o);\n            }\n        }\n\n        byte[] bytes = \"1234\".getBytes(StandardCharsets.UTF_8)\n        String s = converter.applyFrom(bytes)\n\n        def set = Converters.forSet(converter).applyFrom(bytes)\n        assertNotNull set\n        assertEquals 1, set.size()\n        assertEquals s, set.iterator().next()\n\n        def list = Converters.forList(converter).applyFrom(bytes)\n        assertNotNull list\n        assertEquals 1, list.size()\n        assertEquals s, set.iterator().next()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/CompactMediaTypeIdConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass CompactMediaTypeIdConverterTest {\n\n    private static final Converter<String, Object> converter = CompactMediaTypeIdConverter.INSTANCE\n\n    @Test(expected = IllegalArgumentException)\n    void testApplyToNull() {\n        converter.applyTo(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testApplyToEmpty() {\n        converter.applyTo('')\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testApplyToBlank() {\n        converter.applyTo('    ')\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testApplyFromNull() {\n        converter.applyFrom(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testApplyFromNonString() {\n        converter.applyFrom(42)\n    }\n\n    @Test\n    void testNonApplicationMediaType() {\n        String cty = 'foo'\n        assertEquals cty, converter.applyTo(cty)\n        // must auto-prepend 'application/' if no slash in cty value\n        // per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10:\n        assertEquals \"application/$cty\" as String, converter.applyFrom(cty)\n    }\n\n    @Test\n    void testApplicationMediaType() {\n        String cty = 'foo'\n        String mediaType = \"application/$cty\"\n        // assert it has been automatically compacted per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :\n        assertEquals cty, converter.applyTo(mediaType)\n    }\n\n    @Test\n    void testCaseInsensitiveApplicationMediaType() { // media type values are case insensitive\n        String cty = 'FoO'\n        String mediaType = \"aPpLiCaTiOn/$cty\"\n        // assert it has been automatically compacted per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :\n        assertEquals cty, converter.applyTo(mediaType)\n    }\n\n    @Test\n    void testApplicationMediaTypeWithMoreThanOneForwardSlash() {\n        String mediaType = \"application/foo;part=1/2\"\n        // cannot be compacted per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :\n        assertEquals mediaType, converter.applyTo(mediaType)\n    }\n\n    @Test\n    void testCaseInsensitiveApplicationMediaTypeWithMoreThanOneForwardSlash() {\n        String mediaType = \"aPpLiCaTiOn/foo;part=1/2\"\n        // cannot be compacted per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :\n        assertEquals mediaType, converter.applyTo(mediaType)\n    }\n\n    @Test\n    void testApplicationMediaTypeWithMoreThanOneForwardSlash2() {\n        String mediaType = \"application//test\"\n        // cannot be compacted per https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.10 :\n        assertEquals mediaType, converter.applyTo(mediaType)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/ConvertersTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nclass ConvertersTest {\n\n    @Test\n    void testPrivateCtor() { // only for code coverage\n        new Converters()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/DefaultCollectionMutatorTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.lang.Strings\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\n/**\n * @since 0.12.0\n */\nclass DefaultCollectionMutatorTest {\n\n    private int changeCount\n    private DefaultCollectionMutator m\n\n    @Before\n    void setUp() {\n        changeCount = 0\n        m = new DefaultCollectionMutator(null) {\n            @Override\n            protected void changed() {\n                changeCount++\n            }\n        }\n    }\n\n    @Test\n    void newInstance() {\n        def c = m.getCollection()\n        assertNotNull c\n        assertTrue c.isEmpty()\n    }\n\n    @Test\n    void addNull() {\n        m.add(null)\n        assertEquals 0, changeCount\n        assertTrue m.getCollection().isEmpty() // wasn't added\n    }\n\n    @Test\n    void addEmpty() {\n        m.add(Strings.EMPTY)\n        assertEquals 0, changeCount\n        assertTrue m.getCollection().isEmpty() // wasn't added\n    }\n\n    @Test\n    void add() {\n        def val = 'hello'\n        m.add(val)\n        assertEquals 1, changeCount\n        assertEquals Collections.singleton(val), m.getCollection()\n    }\n\n    @Test\n    void addDuplicateDoesNotTriggerChange() {\n        m.add('hello')\n        m.add('hello') //already in the set, no change should be reflected\n        assertEquals 1, changeCount\n    }\n\n    @Test\n    void addCollection() {\n        def vals = ['hello', 'world']\n        m.add(vals)\n        assertEquals vals.size(), m.getCollection().size()\n        assertTrue vals.containsAll(m.getCollection())\n        assertTrue m.getCollection().containsAll(vals)\n        def i = m.getCollection().iterator() // order retained\n        assertEquals vals[0], i.next()\n        assertEquals vals[1], i.next()\n        assertFalse i.hasNext()\n    }\n\n    /**\n     * Asserts that if a collection is added, each internal addition to the collection doesn't call changed(); instead\n     * changed() is only called once after they've all been added to the collection\n     */\n    @Test\n    void addCollectionTriggersSingleChange() {\n        def c = ['hello', 'world']\n        m.add(c)\n        assertEquals 1, changeCount // only one change triggered, not c.size()\n    }\n\n    @Test\n    void remove() {\n        m.add('hello').add('world')\n        m.remove('hello')\n        assertEquals Collections.singleton('world'), m.getCollection()\n    }\n\n    @Test\n    void removeMissingDoesNotTriggerChange() {\n        m.remove('foo') // not in the collection, no change should be registered\n        assertEquals 0, changeCount\n    }\n\n    @Test\n    void clear() {\n        m.add('one').add('two').add(['three', 'four'])\n        assertEquals 4, m.getCollection().size()\n        m.clear()\n        assertTrue m.getCollection().isEmpty()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/DefaultRegistryTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultRegistryTest {\n\n    DefaultRegistry<String, String> reg\n\n    @Before\n    void setUp() {\n        reg = new DefaultRegistry<>('test', 'id', ['a', 'b', 'c', 'd'], Functions.identity())\n    }\n\n    static void immutable(Closure c) {\n        try {\n            c.call()\n            fail()\n        } catch (UnsupportedOperationException expected) {\n            String msg = 'Registries are immutable and cannot be modified.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testImmutable() {\n        immutable { reg.put('foo', 'bar') }\n        immutable { reg.putAll([foo: 'bar']) }\n        immutable { reg.remove('kty') }\n        immutable { reg.clear() }\n    }\n\n    @Test\n    void testApplySameAsGet() {\n        def key = 'a'\n        assertEquals reg.apply(key), reg.get(key)\n    }\n\n    @Test\n    void testHashCode() {\n        assertEquals reg.@DELEGATE.hashCode(), reg.hashCode()\n    }\n\n    @Test\n    void testEqualsIdentity() {\n        assertEquals reg, reg\n    }\n\n    @Test\n    void testEqualsValues() {\n        def another = new DefaultRegistry<>('test', 'id', ['a', 'b', 'c', 'd'], Functions.identity())\n        assertEquals another, reg\n    }\n\n    @Test\n    void testNotEquals() {\n        assertFalse reg.equals(new Object())\n    }\n\n    @Test\n    void testToString() {\n        assertEquals '{a=a, b=b, c=c, d=d}', reg.toString()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/DelegatingMapTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DelegatingMapTest {\n\n    private Map<String, String> m\n    private DelegatingMap dm\n\n    @Before\n    void setUp() {\n        m = [a: 'b', c: 'd']\n        dm = new DelegatingMap(m)\n    }\n\n    @Test\n    void testSize() {\n        assertEquals m.size(), dm.size()\n    }\n\n    @Test\n    void testValues() {\n        def values = m.values()\n        assertEquals values, dm.values()\n        assertTrue values.containsAll(['b', 'd'])\n    }\n\n    @Test\n    void testGet() {\n        assertEquals m.a, dm.a\n        assertNull dm.whatever\n    }\n\n    @Test\n    void testClear() {\n        assertFalse dm.isEmpty()\n        dm.clear() // clear out\n        assertTrue m.isEmpty() // delegate should be clear too\n    }\n\n    @Test\n    void testIsEmpty() {\n        assertFalse dm.isEmpty()\n        assertFalse m.isEmpty()\n\n        dm.clear()\n\n        assertTrue dm.isEmpty()\n        assertTrue m.isEmpty()\n    }\n\n    @Test\n    void testContainsKey() {\n        assertTrue dm.containsKey('a')\n        assertFalse dm.containsKey('b')\n        assertTrue dm.containsKey('c')\n        assertFalse dm.containsKey('d')\n    }\n\n    @Test\n    void testContainsValue() {\n        assertFalse dm.containsValue('a')\n        assertTrue dm.containsValue('b')\n        assertFalse dm.containsValue('c')\n        assertTrue dm.containsValue('d')\n    }\n\n    @Test\n    void testPut() {\n        dm.put('e', 'f')\n        assertEquals 'f', m.e\n        assertEquals 3, m.size()\n        assertEquals 3, dm.size()\n    }\n\n    @Test\n    void testRemove() {\n        dm.remove('c')\n        assertEquals 1, m.size()\n        assertEquals 1, dm.size()\n        assertEquals 'b', m.a\n        assertEquals 'b', dm.a\n    }\n\n    @Test\n    void testPutAll() {\n        assertEquals 2, m.size()\n        assertEquals 2, dm.size()\n\n        dm.putAll(['1': '2', '3': '4'])\n\n        assertEquals 4, m.size()\n        assertEquals 4, dm.size()\n\n        assertTrue m.containsKey('a')\n        assertTrue m.containsKey('c')\n        assertTrue m.containsKey('1')\n        assertTrue m.containsKey('3')\n    }\n\n    @Test\n    void testKeySet() {\n        def set = ['a', 'c'] as Set<String>\n        assertEquals set, m.keySet()\n        assertEquals set, dm.keySet()\n    }\n\n    @Test\n    void testEntrySet() {\n        def entrySet = dm.entrySet()\n        assertEquals m.entrySet(), entrySet\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/EncodedObjectConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass EncodedObjectConverterTest {\n\n    @Test\n    void testApplyFromWithInvalidType() {\n        def converter = Converters.URI\n        assertTrue converter instanceof EncodedObjectConverter\n        int value = 42\n        try {\n            converter.applyFrom(value)\n            fail(\"IllegalArgumentException should have been thrown.\")\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Values must be either String or java.net.URI instances. \" +\n                    \"Value type found: java.lang.Integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/FunctionsTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.MalformedJwtException\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass FunctionsTest {\n\n    @Test\n    void testWrapFmt() {\n\n        def cause = new IllegalStateException(\"foo\")\n\n        def fn = Functions.wrapFmt(new CheckedFunction<Object, Object>() {\n            @Override\n            Object apply(Object o) throws Exception {\n                throw cause\n            }\n        }, MalformedJwtException, \"format me %s\")\n\n        try {\n            fn.apply('hi')\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = \"format me hi. Cause: foo\"\n            assertEquals msg, expected.getMessage()\n            assertSame cause, expected.getCause()\n        }\n    }\n\n    @Test\n    void testWrapFmtPropagatesExpectedExceptionTypeWithoutWrapping() {\n\n        def cause = new MalformedJwtException(\"foo\")\n\n        def fn = Functions.wrapFmt(new CheckedFunction<Object, Object>() {\n            @Override\n            Object apply(Object o) throws Exception {\n                throw cause\n            }\n        }, MalformedJwtException, \"format me %s\")\n\n        try {\n            fn.apply('hi')\n            fail()\n        } catch (MalformedJwtException expected) {\n            assertEquals \"foo\", expected.getMessage()\n            assertSame cause, expected\n        }\n    }\n\n    @Test\n    void testWrap() {\n\n        def cause = new IllegalStateException(\"foo\")\n\n        def fn = Functions.wrap(new Function<Object, Object>() {\n            @Override\n            Object apply(Object o) {\n                throw cause\n            }\n        }, MalformedJwtException, \"format me %s\", 'someArg')\n\n        try {\n            fn.apply('hi')\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = \"format me someArg. Cause: foo\"\n            assertEquals msg, expected.getMessage()\n            assertSame cause, expected.getCause()\n        }\n    }\n\n    @Test\n    void testWrapPropagatesExpectedExceptionTypeWithoutWrapping() {\n\n        def cause = new MalformedJwtException(\"foo\")\n\n        def fn = Functions.wrap(new Function<Object, Object>() {\n            @Override\n            Object apply(Object o) {\n                throw cause\n            }\n        }, MalformedJwtException, \"format me %s\", 'someArg')\n\n        try {\n            fn.apply('hi')\n            fail()\n        } catch (MalformedJwtException expected) {\n            assertEquals \"foo\", expected.getMessage()\n            assertSame cause, expected\n        }\n    }\n\n    @Test\n    void testFirstResultWithNullArgument() {\n        try {\n            Functions.firstResult(null)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals 'Function list cannot be null or empty.', iae.getMessage()\n        }\n    }\n\n    @Test\n    void testFirstResultWithEmptyArgument() {\n        Function<String, String>[] functions = [] as Function<String, String>[]\n        try {\n            Functions.firstResult(functions)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals 'Function list cannot be null or empty.', iae.getMessage()\n        }\n    }\n\n    @Test\n    void testFirstResultWithSingleNonNullValueFunction() {\n        Function<String, String> fn = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s\n                return s\n            }\n        }\n        assertEquals 'foo', Functions.firstResult(fn).apply('foo')\n    }\n\n    @Test\n    void testFirstResultWithSingleNullValueFunction() {\n        Function<String, String> fn = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s\n                return null\n            }\n        }\n        assertNull Functions.firstResult(fn).apply('foo')\n    }\n\n    @Test\n    void testFirstResultFallback() {\n        def fn1 = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s\n                return null\n            }\n        }\n        def fn2 = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s // ensure original input is retained, not output from fn1\n                return 'fn2'\n            }\n        }\n        assertEquals 'fn2', Functions.firstResult(fn1, fn2).apply('foo')\n    }\n\n    @Test\n    void testFirstResultAllNull() {\n        def fn1 = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s\n                return null\n            }\n        }\n        def fn2 = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s // ensure original input is retained, not output from fn1\n                return null\n            }\n        }\n        // everything returned null, so null should be returned:\n        assertNull Functions.firstResult(fn1, fn2).apply('foo')\n    }\n\n    @Test\n    void testFirstResultShortCircuit() {\n        def fn1 = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s\n                return null\n            }\n        }\n        def fn2 = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                assertEquals 'foo', s // ensure original argument is retained, not output from fn1\n                return 'fn2'\n            }\n        }\n        boolean invoked = false\n        def fn3 = new Function<String, String>() {\n            @Override\n            String apply(String s) {\n                invoked = true // should not be invoked\n                return 'fn3'\n            }\n        }\n        assertEquals 'fn2', Functions.firstResult(fn1, fn2, fn3).apply('foo')\n        assertFalse invoked\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/JwtDateConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertNull\n\nclass JwtDateConverterTest {\n\n    @Test\n    void testApplyToNull() {\n        assertNull JwtDateConverter.INSTANCE.applyTo(null)\n    }\n\n    @Test\n    void testApplyFromNull() {\n        assertNull JwtDateConverter.INSTANCE.applyFrom(null)\n    }\n\n    @Test\n    void testToDateWithNull() {\n        assertNull JwtDateConverter.toDate(null)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/LocatorFunctionTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.Header\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.Locator\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass LocatorFunctionTest {\n\n    @Test\n    void testApply() {\n        final int value = 42\n        def locator = new StaticLocator(value)\n        def fn = new LocatorFunction(locator)\n        assertEquals value, fn.apply(Jwts.header().build())\n    }\n\n    static class StaticLocator<T> implements Locator<T> {\n        private final T o;\n\n        StaticLocator(T o) {\n            this.o = o;\n        }\n\n        @Override\n        T locate(Header header) {\n            return o;\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/NestedIdentifiableCollectionTest.groovy",
    "content": "/*\n * Copyright © 2025 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.Identifiable\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass NestedIdentifiableCollectionTest {\n\n    private int changeCount\n    private NestedIdentifiableCollection c\n\n    @Before\n    void setUp() {\n        changeCount = 0\n        c = new NestedIdentifiableCollection(this, null) {\n            @Override\n            protected void changed() {\n                changeCount++\n            }\n        }\n    }\n\n    @Test\n    void defaultChangedDoesNothing() {\n        changeCount = 0\n        c = new NestedIdentifiableCollection(this, null)\n        c.changed() // no op as default, subclasses override if they need callback\n        assertEquals 0, changeCount // no change occurs by default\n    }\n\n    @Test\n    void newInstance() {\n        def m = c.getValues()\n        assertNotNull m\n        assertTrue m.isEmpty()\n    }\n\n    @Test\n    void addNull() {\n        c.add(null)\n        assertEquals 0, changeCount\n        assertTrue c.getValues().isEmpty() // wasn't added\n    }\n\n    @Test\n    void add() {\n        def val = new TestAlg('test', this)\n        c.add(val)\n        assertEquals 1, changeCount\n        def expected = ['test': val]\n        assertEquals expected, c.getValues()\n    }\n\n    @Test\n    void addEmptyCollection() {\n        assertEquals 0, changeCount\n        def empty = [] as List\n        c.add(empty)\n        assertEquals 0, changeCount\n    }\n\n    @Test\n    void addCollection() {\n        def val1 = new TestAlg('id1', this)\n        def val2 = new TestAlg('id2', this)\n        def vals = [val1, val2]\n        c.add(vals)\n        assertEquals vals.size(), c.getValues().size()\n        assertTrue vals.containsAll(c.getValues().values())\n        assertTrue c.getValues().values().containsAll(vals)\n        def i = c.getValues().values().iterator() // order retained\n        assertEquals vals[0], i.next()\n        assertEquals vals[1], i.next()\n        assertFalse i.hasNext()\n    }\n\n    /**\n     * Asserts that if a collection is added, each internal addition to the collection doesn't call changed(); instead\n     * changed() is only called once after they've all been added to the collection\n     */\n    @Test\n    void addCollectionTriggersSingleChange() {\n        def val1 = new TestAlg('id1', this)\n        def val2 = new TestAlg('id2', this)\n        def vals = [val1, val2]\n        this.c.add(vals)\n        assertEquals 1, changeCount // only one change triggered, not c.size()\n    }\n\n    @Test\n    void remove() {\n        def val1 = new TestAlg('id1', this)\n        def val2 = new TestAlg('id2', this)\n        c.add(val1).add(val2)\n        c.remove(val1)\n        assertFalse(c.values.values().contains(val1))\n        def expected = [val2] as Set\n        assertEquals expected, c.values.values() as Set\n    }\n\n    @Test\n    void removeNull() {\n        c.remove(null)\n        assertEquals 0, changeCount\n        assertTrue c.getValues().isEmpty()\n    }\n\n    @Test\n    void removeMissingDoesNotTriggerChange() {\n        def val = new TestAlg('id1', this)\n        c.remove(val) // not in the collection, no change should be registered\n        assertEquals 0, changeCount\n    }\n\n    @Test\n    void clear() {\n        def val1 = new TestAlg('id1', this)\n        def val2 = new TestAlg('id2', this)\n        def val3 = new TestAlg('id3', this)\n        def val4 = new TestAlg('id4', this)\n        c.add(val1).add(val2).add([val3, val4])\n        assertEquals 4, c.getValues().size()\n        c.clear()\n        assertTrue c.getValues().isEmpty()\n    }\n\n    @Test\n    void clearWhenEmpty() {\n        assertEquals 0, changeCount\n        c.clear()\n        assertEquals 0, changeCount\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void addIdentifiableWithNullId() {\n        c.add(new TestAlg(null, this))\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void addIdentifiableWithEmptyId() {\n        c.add(new TestAlg('  ', null))\n    }\n\n    @Test\n    void addIdentifiableWithSameIdEvictsExisting() {\n        c.add(new TestAlg('sameId', 'foo'))\n        c.add(new TestAlg('sameId', 'bar'))\n        assertEquals 2, changeCount\n        assertEquals 1, c.getValues().size() // second 'add' should evict first\n        assertEquals 'bar', ((TestAlg) c.getValues().values().toArray()[0]).obj\n    }\n\n    private class TestAlg implements Identifiable {\n        String id\n        Object obj\n\n        TestAlg(String id, Object obj) {\n            this.id = id\n            this.obj = obj\n        }\n\n        @Override\n        String getId() {\n            return id\n        }\n\n        @Override\n        int hashCode() {\n            return id.hashCode()\n        }\n\n        @Override\n        boolean equals(Object obj) {\n            return obj instanceof TestAlg && id == obj.getId()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/NullSafeConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNull\n\nclass NullSafeConverterTest {\n\n    @Test\n    void testNullArguments() {\n        def converter = new NullSafeConverter(new UriStringConverter())\n        assertNull converter.applyTo(null)\n        assertNull converter.applyFrom(null)\n    }\n\n    @Test\n    void testNonNullArguments() {\n        def converter = new NullSafeConverter(new UriStringConverter())\n        String url = 'https://github.com/jwtk/jjwt'\n        URI uri = new URI(url)\n        assertEquals url, converter.applyTo(uri)\n        assertEquals uri, converter.applyFrom(url)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/OptionalMethodInvokerTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.impl.security.KeysBridge\nimport io.jsonwebtoken.impl.security.TestKeys\nimport org.junit.Test\n\nimport javax.crypto.spec.SecretKeySpec\nimport java.lang.reflect.InvocationTargetException\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\nclass OptionalMethodInvokerTest {\n\n    @Test\n    void testClassDoesNotExist() {\n        def i = new OptionalMethodInvoker('com.foo.Bar', 'foo')\n        assertNull i.apply(null)\n    }\n\n    @Test\n    void testClassExistsButMethodDoesNotExist() {\n        def i = new OptionalMethodInvoker(Key.class.getName(), 'foo')\n        assertNull i.apply(null)\n    }\n\n    @Test\n    void testClassAndMethodExistWithValidArgument() {\n        def key = TestKeys.HS256\n        def i = new OptionalMethodInvoker(Key.class.getName(), 'getAlgorithm')\n        assertEquals key.getAlgorithm(), i.apply(key)\n    }\n\n    @Test\n    void testClassAndMethodExistWithInvalidTypeArgument() {\n        def i = new OptionalMethodInvoker(Key.class.getName(), 'getAlgorithm')\n        assertNull i.apply('Hello') // not a Key instance, should return null\n    }\n\n    @Test\n    void testClassAndMethodExistWithInvocationError() {\n        def key = TestKeys.HS256\n        String msg = 'bar'\n        def ex = new InvocationTargetException(new IllegalStateException('foo'), msg)\n        def i = new OptionalMethodInvoker<Key, String>(Key.class.getName(), 'getEncoded') {\n            @Override\n            protected String invoke(Key aKey) throws InvocationTargetException, IllegalAccessException {\n                throw ex\n            }\n        }\n        try {\n            i.apply(key) // getEncoded returns a byte array, not a String, should throw cast error\n            fail()\n        } catch (IllegalStateException ise) {\n            assertEquals ReflectionFunction.ERR_MSG + msg, ise.getMessage()\n            assertSame ex, ise.getCause()\n        }\n    }\n\n    @Test\n    void testStatic() {\n        def i = new OptionalMethodInvoker(KeysBridge.class.getName(), \"findBitLength\", Key.class, true)\n        int bits = 256\n        def key = new SecretKeySpec(Bytes.random((int)(bits / 8)), \"AES\")\n        assertEquals bits, i.apply(key)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/ParametersTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass ParametersTest {\n\n    static final Parameter<String> STRING = Parameters.builder(String.class).setId('foo').setName('FooName').build()\n    static final Parameter<Set<String>> STRSET = Parameters.builder(String.class).setId('fooSet').setName('FooSet').set().build()\n    static final Parameter<List<String>> STRLIST = Parameters.builder(String.class).setId('fooList').setName('FooList').list().build()\n\n    @Test\n    void testPrivateCtor() { // for code coverage only\n        new Parameters()\n    }\n\n    @Test\n    void testString() {\n        assertEquals 'FooName', STRING.getName()\n        assertEquals 'foo', STRING.getId()\n    }\n\n    @Test\n    void testSupports() {\n        assertTrue STRING.supports('bar') // any string\n    }\n\n    @Test\n    void testSupportsNull() {\n        assertTrue STRING.supports(null) // null values allowed by default\n    }\n\n    @Test\n    void testSupportsSet() {\n        def set = ['test'] as Set\n        assertTrue STRSET.supports(set)\n    }\n\n    @Test\n    void testSupportsSetNull() {\n        assertTrue STRSET.supports(null)\n    }\n\n    @Test\n    void testSupportsSetEmpty() {\n        def set = [] as Set\n        assertTrue STRSET.supports(set)\n    }\n\n    @Test\n    void testSupportsSetWrongElementType() {\n        def set = [42] as Set // correct collection type, but wrong element type\n        assertFalse STRSET.supports(set)\n    }\n\n    @Test\n    void testSupportsList() {\n        def list = ['test']\n        assertTrue STRLIST.supports(list)\n    }\n\n    @Test\n    void testSupportsListNull() {\n        assertTrue STRLIST.supports(null)\n    }\n\n    @Test\n    void testSupportsListEmpty() {\n        def list = [] as List\n        assertTrue STRLIST.supports(list)\n    }\n\n    @Test\n    void testSupportsListWrongElementType() {\n        def list = [42] // correct collection type, but wrong element type\n        assertFalse STRLIST.supports(list)\n    }\n\n    @Test\n    void testSupportsFailsForDifferentType() {\n        def param = Parameters.builder(String.class).setId('foo').setName('fooName').build()\n        Object val = 42\n        assertFalse param.supports(val)\n    }\n\n    @Test\n    void testCast() {\n        Object val = 'test'\n        assertEquals val, STRING.cast(val)\n    }\n\n    @Test\n    void testCastNull() {\n        assertNull STRING.cast(null)\n        assertNull STRSET.cast(null)\n        assertNull STRLIST.cast(null)\n    }\n\n    @Test\n    void testCastWrongType() {\n        try {\n            STRING.cast(42)\n            fail()\n        } catch (ClassCastException expected) {\n            String msg = 'Cannot cast java.lang.Integer to java.lang.String'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testCastSet() {\n        Object set = ['test'] as Set\n        assertSame set, STRSET.cast(set)\n    }\n\n    @Test\n    void testCastSetEmpty() {\n        Object set = [] as Set\n        assertSame set, STRSET.cast(set)\n    }\n\n    @Test\n    void testCastSetWrongType() {\n        try {\n            STRSET.cast(42) // not a set\n            fail()\n        } catch (ClassCastException expected) {\n            String msg = \"Cannot cast java.lang.Integer to java.util.Set<java.lang.String>\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testCastSetWrongElementType() {\n        Object set = [42] as Set\n        try {\n            STRSET.cast(set)\n            fail()\n        } catch (ClassCastException expected) {\n            String msg = \"Cannot cast java.util.LinkedHashSet to java.util.Set<java.lang.String>: At least \" +\n                    \"one element is not an instance of java.lang.String\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testCastList() {\n        Object list = ['test']\n        assertSame list, STRLIST.cast(list)\n    }\n\n    @Test\n    void testCastListEmpty() {\n        Object list = []\n        assertSame list, STRLIST.cast(list)\n    }\n\n    @Test\n    void testCastListWrongType() {\n        try {\n            STRLIST.cast(42) // not a list\n            fail()\n        } catch (ClassCastException expected) {\n            String msg = \"Cannot cast java.lang.Integer to java.util.List<java.lang.String>\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testCastListWrongElementType() {\n        Object list = [42]\n        try {\n            STRLIST.cast(list)\n            fail()\n        } catch (ClassCastException expected) {\n            String msg = \"Cannot cast java.util.ArrayList to java.util.List<java.lang.String>: At least \" +\n                    \"one element is not an instance of java.lang.String\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEquals() {\n        def a = Parameters.string('foo', \"NameA\")\n        def b = Parameters.builder(Object.class).setId('foo').setName(\"NameB\").build()\n        //ensure equality only based on id:\n        assertEquals a, b\n    }\n\n    @Test\n    void testHashCode() {\n        def a = Parameters.string('foo', \"NameA\")\n        def b = Parameters.builder(Object.class).setId('foo').setName(\"NameB\").build()\n        //ensure only based on id:\n        assertEquals a.hashCode(), b.hashCode()\n    }\n\n    @Test\n    void testToString() {\n        assertEquals \"'foo' (FooName)\", Parameters.string('foo', 'FooName').toString()\n    }\n\n    @Test\n    void testEqualsNonField() {\n        def param = Parameters.builder(String.class).setId('foo').setName(\"FooName\").build()\n        assertFalse param.equals(new Object())\n    }\n\n    @Test\n    void testBigIntegerBytesNull() {\n        assertNull Parameters.bytes(null)\n    }\n\n    @Test\n    void testBytesEqualsWhenBothAreNull() {\n        assertTrue Parameters.bytesEquals(null, null)\n    }\n\n    @Test\n    void testBytesEqualsIdentity() {\n        assertTrue Parameters.bytesEquals(BigInteger.ONE, BigInteger.ONE)\n    }\n\n    @Test\n    void testBytesEqualsWhenAIsNull() {\n        assertFalse Parameters.bytesEquals(null, BigInteger.ONE)\n    }\n\n    @Test\n    void testBytesEqualsWhenBIsNull() {\n        assertFalse Parameters.bytesEquals(BigInteger.ONE, null)\n    }\n\n    @Test\n    void testFieldValueEqualsWhenAIsNull() {\n        BigInteger a = null\n        BigInteger b = BigInteger.ONE\n        Parameter<BigInteger> param = Parameters.bigInt('foo', 'bar').build()\n        assertFalse Parameters.equals(a, b, param)\n    }\n\n    @Test\n    void testFieldValueEqualsWhenBIsNull() {\n        BigInteger a = BigInteger.ONE\n        BigInteger b = null\n        Parameter<BigInteger> param = Parameters.bigInt('foo', 'bar').build()\n        assertFalse Parameters.equals(a, b, param)\n    }\n\n    @Test\n    void testFieldValueEqualsSecretString() {\n        String a = 'hello'\n        String b = new String('hello'.toCharArray()) // new instance not in the string table (Groovy side effect)\n        Parameter<String> param = Parameters.builder(String.class).setId('foo').setName('bar').setSecret(true).build()\n        assertTrue Parameters.equals(a, b, param)\n    }\n\n    @Test\n    void testEqualsIdentity() {\n        ParameterReadable r = new TestParameterReadable()\n        assertTrue Parameters.equals(r, r, Parameters.string('foo', 'bar'))\n    }\n\n    @Test\n    void testEqualsWhenAIsNull() {\n        assertFalse Parameters.equals(null, \"hello\", Parameters.string('foo', 'bar'))\n    }\n\n    @Test\n    void testEqualsWhenAIsFieldReadableButBIsNot() {\n        ParameterReadable r = new TestParameterReadable()\n        assertFalse Parameters.equals(r, \"hello\", Parameters.string('foo', 'bar'))\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/PropagatingExceptionFunctionTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.security.SecurityException\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass PropagatingExceptionFunctionTest {\n\n    @Test\n    void testAssignableException() {\n\n        def ex = new SecurityException(\"test\")\n\n        def fn = new PropagatingExceptionFunction<>(new Function<Object, Object>() {\n            @Override\n            Object apply(Object t) {\n                throw ex\n            }\n        }, SecurityException.class, \"foo\")\n\n        try {\n            fn.apply(\"hi\")\n        } catch (Exception thrown) {\n            assertSame ex, thrown //because it was assignable, 'thrown' should not be a wrapper exception\n        }\n    }\n\n    @Test\n    void testExceptionMessageWithTrailingPeriod() {\n        String msg = 'foo.'\n        def ex = new IllegalArgumentException(\"test\")\n        def fn = new PropagatingExceptionFunction<>(new Function<Object, Object>() {\n            @Override\n            Object apply(Object t) {\n                throw ex\n            }\n        }, SecurityException.class, msg)\n\n        try {\n            fn.apply(\"hi\")\n        } catch (SecurityException expected) {\n            String expectedMsg =\"$msg Cause: test\" // expect $msg unaltered\n            assertEquals expectedMsg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testExceptionMessageWithoutTrailingPeriod() {\n        String msg = 'foo'\n        def ex = new IllegalArgumentException(\"test\")\n        def fn = new PropagatingExceptionFunction<>(new Function<Object, Object>() {\n            @Override\n            Object apply(Object t) {\n                throw ex\n            }\n        }, SecurityException.class, msg)\n\n        try {\n            fn.apply(\"hi\")\n        } catch (SecurityException expected) {\n            String expectedMsg =\"$msg. Cause: test\" // expect $msg to have a trailing period\n            assertEquals expectedMsg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/RedactedSupplierTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass RedactedSupplierTest {\n\n    @Test\n    void testEqualsWrappedSameValue() {\n        def value = 42\n        assertTrue new RedactedSupplier<>(value).equals(value)\n    }\n\n    @Test\n    void testEqualsWrappedDifferentValue() {\n        assertFalse new RedactedSupplier<>(42).equals(30)\n    }\n\n    @Test\n    void testEquals() {\n        assertTrue new RedactedSupplier<>(42).equals(new RedactedSupplier(42))\n    }\n\n    @Test\n    void testEqualsSameTypeDifferentValue() {\n        assertFalse new RedactedSupplier<>(42).equals(new RedactedSupplier(30))\n    }\n\n    @Test\n    void testEqualsIdentity() {\n        def supplier = new RedactedSupplier('hello')\n        assertEquals supplier, supplier\n    }\n\n    @Test\n    void testHashCode() {\n        int hashCode = 42.hashCode()\n        assertEquals hashCode, new RedactedSupplier(42).hashCode()\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/RedactedValueConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertNull\nimport static org.junit.Assert.assertSame\n\nclass RedactedValueConverterTest {\n\n    @Test\n    void testApplyToWithNullValue() {\n        def c = new RedactedValueConverter(new NullSafeConverter<URI, Object>(Converters.URI))\n        assertNull c.applyTo(null)\n    }\n\n    @Test\n    void testApplyFromWithNullValue() {\n        def c = new RedactedValueConverter(new NullSafeConverter<URI, Object>(Converters.URI))\n        assertNull c.applyFrom(null)\n    }\n\n    @Test\n    void testDelegateReturnsRedactedSupplierValue() {\n        def suri = 'https://jsonwebtoken.io'\n        def supplier = new RedactedSupplier(suri)\n        def delegate = new Converter() {\n            @Override\n            Object applyTo(Object o) {\n                return supplier\n            }\n\n            @Override\n            Object applyFrom(Object o) {\n                return null\n            }\n        }\n        def c = new RedactedValueConverter(delegate)\n\n        // ensure applyTo doesn't change or wrap the delegate return value that is already of type RedactedSupplier:\n        assertSame supplier, c.applyTo(suri)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/RequiredTypeConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\n/**\n * @since 0.12.0\n */\nclass RequiredTypeConverterTest {\n\n    @Test\n    void testApplyTo() {\n        def converter = new RequiredTypeConverter(Integer.class)\n        def val = 42\n        assertSame val, converter.applyTo(val)\n    }\n\n    @Test\n    void testApplyFromNull() {\n        def converter = new RequiredTypeConverter(Integer.class)\n        assertNull converter.applyFrom(null)\n    }\n\n    @Test\n    void testApplyFromInvalidType() {\n        def converter = new RequiredTypeConverter(Integer.class)\n        try {\n            converter.applyFrom('hello' as String)\n        } catch (IllegalArgumentException expected) {\n            String msg = 'Unsupported value type. Expected: java.lang.Integer, found: java.lang.String'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/ServicesTest.groovy",
    "content": "/*\n * Copyright (C) 2019 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport io.jsonwebtoken.StubService\nimport io.jsonwebtoken.impl.DefaultStubService\nimport org.junit.After\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNotNull\n\nclass ServicesTest {\n\n    @Test\n    void testSuccessfulLoading() {\n        def service = Services.get(StubService)\n        assertNotNull service\n        assertEquals(DefaultStubService, service.class)\n    }\n\n    @Test(expected = UnavailableImplementationException)\n    void testLoadUnavailable() {\n        Services.get(NoService.class)\n    }\n\n    @Test\n    void testPrivateConstructor() {\n        new Services(); // not allowed in Java, including here for test coverage\n    }\n\n    @Test\n    void testClassLoaderAccessorList() {\n        List<Services.ClassLoaderAccessor> accessorList = Services.CLASS_LOADER_ACCESSORS\n        assertEquals(\"Expected 3 ClassLoaderAccessor to be found\", 3, accessorList.size())\n        assertEquals(Thread.currentThread().getContextClassLoader(), accessorList.get(0).getClassLoader())\n        assertEquals(Services.class.getClassLoader(), accessorList.get(1).getClassLoader())\n        assertEquals(ClassLoader.getSystemClassLoader(), accessorList.get(2).getClassLoader())\n    }\n\n    @After\n    void resetCache() {\n        Services.reload();\n    }\n\n    interface NoService {} // no implementations\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/StringRegistryTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertNull\n\nclass StringRegistryTest {\n\n    @Test(expected = ClassCastException)\n    void testGetWithoutString() {\n        def registry = new StringRegistry('foo', 'id', ['one', 'two'], Functions.identity(), true)\n        assertNull registry.get(1) // not a string key\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/TestParameterReadable.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nclass TestParameterReadable implements ParameterReadable {\n\n    def value = null\n\n    @Override\n    Object get(Parameter param) {\n        return value\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/lang/UriStringConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.lang\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass UriStringConverterTest {\n\n    @Test\n    void testApplyTo() {\n        String url = 'https://github.com/jwtk/jjwt'\n        URI uri = new URI(url)\n        def converter = new UriStringConverter()\n        assertEquals url, converter.applyTo(uri)\n        assertEquals uri, converter.applyFrom(url)\n    }\n\n    @Test\n    void testApplyFromWithInvalidArgument() {\n        String val = '{}asdfasdfasd'\n        try {\n            new UriStringConverter().applyFrom(val)\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Unable to convert String value '${val}' to URI instance: Illegal character in path at index 0: ${val}\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractAsymmetricJwkBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport java.security.cert.X509Certificate\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\nclass AbstractAsymmetricJwkBuilderTest {\n\n    private static final X509Certificate CERT = TestKeys.RS256.cert\n    private static final List<X509Certificate> CHAIN = [CERT]\n    private static final RSAPublicKey PUB_KEY = CERT.getPublicKey() as RSAPublicKey\n\n    private static RsaPublicJwkBuilder builder() {\n        return Jwks.builder().key(PUB_KEY)\n    }\n\n    @Test\n    void testUse() {\n        def val = UUID.randomUUID().toString()\n        def jwk = builder().publicKeyUse(val).build()\n        assertEquals val, jwk.getPublicKeyUse()\n        assertEquals val, jwk.use\n\n        RSAPrivateKey privateKey = TestKeys.RS256.pair.private as RSAPrivateKey\n\n        jwk = builder().publicKeyUse(val).privateKey(privateKey).build()\n        assertEquals val, jwk.getPublicKeyUse()\n        assertEquals val, jwk.use\n    }\n\n    @Test\n    void testX509Url() {\n        def val = new URI(UUID.randomUUID().toString())\n        assertSame val, builder().x509Url(val).build().getX509Url()\n    }\n\n    @Test\n    void testX509CertificateChain() {\n        assertEquals CHAIN, builder().x509Chain(CHAIN).build().getX509Chain()\n    }\n\n    @Test\n    void testX509CertificateSha1Thumbprint() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        Request<byte[]> request = new DefaultRequest(payload, null, null)\n        def x5t = DefaultHashAlgorithm.SHA1.digest(request)\n        def encoded = Encoders.BASE64URL.encode(x5t)\n        def jwk = builder().x509Sha1Thumbprint(x5t).build()\n        assertArrayEquals x5t, jwk.getX509Sha1Thumbprint()\n        assertEquals encoded, jwk.get(AbstractAsymmetricJwk.X5T.getId())\n    }\n\n    @Test\n    void testX509CertificateSha1ThumbprintEnabled() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        Request<byte[]> request = new DefaultRequest(payload, null, null)\n        def x5t = DefaultHashAlgorithm.SHA1.digest(request)\n        def encoded = Encoders.BASE64URL.encode(x5t)\n        def jwk = builder().x509Chain(CHAIN).x509Sha1Thumbprint(true).build()\n        assertArrayEquals x5t, jwk.getX509Sha1Thumbprint()\n        assertEquals encoded, jwk.get(AbstractAsymmetricJwk.X5T.getId())\n    }\n\n    @Test\n    void testX509CertificateSha256Thumbprint() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        Request<byte[]> request = new DefaultRequest(payload, null, null)\n        def x5tS256 = Jwks.HASH.SHA256.digest(request)\n        def encoded = Encoders.BASE64URL.encode(x5tS256)\n        def jwk = builder().x509Sha256Thumbprint(x5tS256).build()\n        assertArrayEquals x5tS256, jwk.getX509Sha256Thumbprint()\n        assertEquals encoded, jwk.get(AbstractAsymmetricJwk.X5T_S256.getId())\n    }\n\n    @Test\n    void testX509CertificateSha256ThumbprintEnabled() {\n        def payload = Streams.of(TestKeys.RS256.cert.getEncoded())\n        Request<InputStream> request = new DefaultRequest(payload, null, null)\n        def x5tS256 = Jwks.HASH.SHA256.digest(request)\n        def encoded = Encoders.BASE64URL.encode(x5tS256)\n        def jwk = builder().x509Chain(CHAIN).x509Sha256Thumbprint(true).build()\n        assertArrayEquals x5tS256, jwk.getX509Sha256Thumbprint()\n        assertEquals encoded, jwk.get(AbstractAsymmetricJwk.X5T_S256.getId())\n    }\n\n    @Test\n    void testEcPrivateJwkFromPublicBuilder() {\n        def pair = TestKeys.ES256.pair\n\n        //start with a public key builder\n        def builder = Jwks.builder().key(pair.public as ECPublicKey)\n        assertTrue builder instanceof AbstractAsymmetricJwkBuilder.DefaultEcPublicJwkBuilder\n\n        //applying the private key turns it into a private key builder\n        builder = builder.privateKey(pair.private as ECPrivateKey)\n        assertTrue builder instanceof AbstractAsymmetricJwkBuilder.DefaultEcPrivateJwkBuilder\n\n        //building creates a private jwk:\n        def jwk = builder.build()\n        assertTrue jwk instanceof EcPrivateJwk\n\n        //which also has information for the public key:\n        jwk = jwk.toPublicJwk()\n        assertTrue jwk instanceof EcPublicJwk\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractCurveTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\nclass AbstractCurveTest {\n\n    AbstractCurve curve\n\n    @Before\n    void setUp() {\n        curve = new TestAbstractCurve('foo', 'bar')\n    }\n\n    @Test\n    void testGetId() {\n        assertEquals 'foo', curve.getId()\n    }\n\n    @Test\n    void testGetJcaName() {\n        assertEquals 'bar', curve.getJcaName()\n    }\n\n    @Test\n    void testHashcode() {\n        assertEquals 'foo'.hashCode(), curve.hashCode()\n    }\n\n    @Test\n    void testToString() {\n        assertEquals 'foo', curve.toString()\n    }\n\n    @Test\n    void testEqualsIdentity() {\n        //noinspection ChangeToOperator\n        assertTrue curve.equals(curve)\n    }\n\n    @Test\n    void testEqualsTypeMismatch() {\n        Object obj = new Integer(42)\n        //noinspection ChangeToOperator\n        assertFalse curve.equals(obj);\n    }\n\n    @Test\n    void testEqualsId() {\n        def other = new TestAbstractCurve('foo', 'asdfasdf')\n        //noinspection ChangeToOperator\n        assertTrue curve.equals(other)\n    }\n\n    @Test\n    void testNotEquals() {\n        def other = new TestAbstractCurve('abc', 'bar')\n        //noinspection ChangeToOperator\n        assertFalse curve.equals(other)\n    }\n\n    @Test\n    void testKeyPairBuilder() {\n        def builder = curve.keyPair()\n        assertEquals 'bar', builder.jcaName //builder is an instanceof DefaultKeyPairBuilder\n    }\n\n    static class TestAbstractCurve extends AbstractCurve {\n\n        def TestAbstractCurve(String id, String jcaName) {\n            super(id, jcaName)\n        }\n\n        @Override\n        boolean contains(Key key) {\n            return false\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractEcJwkFactoryTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.UnsupportedKeyException\nimport org.junit.Test\n\nimport java.security.interfaces.ECPrivateKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass AbstractEcJwkFactoryTest {\n\n    @Test\n    void testInvalidJwaCurveId() {\n        String id = 'foo'\n        try {\n            AbstractEcJwkFactory.getCurveByJwaId(id)\n            fail()\n        } catch (UnsupportedKeyException e) {\n            String msg = \"Unrecognized JWA EC curve id '$id'\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    /**\n     * Asserts correct behavior per https://github.com/jwtk/jjwt/issues/901\n     * @since 0.12.4\n     */\n    @Test\n    void fieldElementByteArrayLength() {\n\n        EcSignatureAlgorithmTest.algs().each { alg ->\n\n            def key = alg.keyPair().build().getPrivate() as ECPrivateKey\n            def jwk = Jwks.builder().key(key).build()\n\n            def json = Jwks.UNSAFE_JSON(jwk)\n            def map = Services.get(Deserializer).deserialize(new StringReader(json)) as Map<String, ?>\n            def xs = map.get(\"x\") as String\n            def ys = map.get(\"y\") as String\n            def ds = map.get(\"d\") as String\n\n            def x = Decoders.BASE64URL.decode(xs)\n            def y = Decoders.BASE64URL.decode(ys)\n            def d = Decoders.BASE64URL.decode(ds)\n\n            // most important part of the test: 'x' and 'y' decoded byte arrays must have a length equal to the curve\n            // field size (in bytes) per https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.2 and\n            // https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.1.3\n            int fieldSizeInBits = key.getParams().getCurve().getField().getFieldSize()\n            int fieldSizeInBytes = Bytes.length(fieldSizeInBits)\n            assertEquals fieldSizeInBytes, x.length\n            assertEquals fieldSizeInBytes, y.length\n\n            // and 'd' must have a length equal to the curve order size in bytes per\n            // https://datatracker.ietf.org/doc/html/rfc7518#section-6.2.2.1\n            int orderSizeInBytes = Bytes.length(key.params.order.bitLength())\n            assertEquals orderSizeInBytes, d.length\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractFamilyJwkFactoryTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.CheckedFunction\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.KeyException\nimport io.jsonwebtoken.security.MalformedKeyException\nimport org.junit.Test\n\nimport java.security.KeyFactory\nimport java.security.NoSuchAlgorithmException\nimport java.security.interfaces.ECPublicKey\n\nimport static org.junit.Assert.*\n\nclass AbstractFamilyJwkFactoryTest {\n\n    @Test\n    void testGenerateKeyPropagatesKeyException() {\n        // any AbstractFamilyJwkFactory subclass will do:\n        def factory = new EcPublicJwkFactory()\n        def ctx = new DefaultJwkContext()\n        ctx.put('hello', 'world')\n        def ex = new MalformedKeyException('foo')\n        try {\n            factory.generateKey(ctx, new CheckedFunction<KeyFactory, ECPublicKey>() {\n                @Override\n                ECPublicKey apply(KeyFactory keyFactory) throws Exception {\n                    throw ex\n                }\n            })\n            fail()\n        } catch (KeyException expected) {\n            assertSame ex, expected\n        }\n    }\n\n    @Test\n    void testGenerateKeyUnexpectedException() {\n        // any AbstractFamilyJwkFactory subclass will do:\n        def factory = new EcPublicJwkFactory()\n        def ctx = new DefaultJwkContext()\n        ctx.put('hello', 'world')\n        try {\n            factory.generateKey(ctx, new CheckedFunction<KeyFactory, ECPublicKey>() {\n                @Override\n                ECPublicKey apply(KeyFactory keyFactory) throws Exception {\n                    throw new NoSuchAlgorithmException(\"foo\")\n                }\n            })\n            fail()\n        } catch (InvalidKeyException expected) {\n            assertEquals 'Unable to create ECPublicKey from JWK {hello=world}: foo', expected.getMessage()\n        }\n    }\n\n    @Test\n    void testUnsupportedContext() {\n        def factory = new EcPublicJwkFactory() {\n            @Override\n            boolean supports(JwkContext<?> ctx) {\n                return false\n            }\n        }\n        try {\n            factory.createJwk(new DefaultJwkContext<ECPublicKey>())\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals 'Unsupported JwkContext.', iae.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractJwkBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.security.Jwk\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.MalformedKeyException\nimport io.jsonwebtoken.security.SecretJwk\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\nclass AbstractJwkBuilderTest {\n\n    private static AbstractJwkBuilder<SecretKey, SecretJwk, AbstractJwkBuilder> builder() {\n        return (AbstractJwkBuilder) Jwks.builder().key(TestKeys.NA256)\n    }\n\n    @Test\n    void testKeyType() {\n        def jwk = builder().build()\n        assertEquals 'oct', jwk.getType()\n        assertNotNull jwk.k // JWA id for raw key value\n    }\n\n    @Test\n    void testPut() {\n        def a = UUID.randomUUID()\n        def builder = builder()\n        builder.put('foo', a)\n        assertEquals a, builder.build().get('foo')\n    }\n\n    @Test\n    void testPutAll() {\n        def foo = UUID.randomUUID()\n        def bar = UUID.randomUUID().toString() //different type\n        def m = [foo: foo, bar: bar]\n        def jwk = builder().add(m).build()\n        assertEquals foo, jwk.foo\n        assertEquals bar, jwk.bar\n    }\n\n    @Test\n    void testRemove() {\n        def jwk = builder().add('foo', 'bar').delete('foo').build() as Jwk\n        assertNull jwk.get('foo')\n    }\n\n    @Test\n    void testClear() {\n        def builder = builder().add('foo', 'bar')\n        builder.clear()\n        def jwk = builder.build()\n        assertNull jwk.get('foo')\n    }\n\n    @Test\n    void testEmpty() {\n        def jwk = builder().add('foo', 'bar').empty().build() as Jwk\n        assertNull jwk.get('foo')\n    }\n\n    @Test\n    void testAlgorithm() {\n        def alg = 'someAlgorithm'\n        def jwk = builder().algorithm(alg).build()\n        assertEquals alg, jwk.getAlgorithm()\n        assertEquals alg, jwk.alg //test raw get via JWA member id\n    }\n\n    @Test\n    void testAlgorithmByPut() {\n        def alg = 'someAlgorithm'\n        def jwk = builder().add('alg', alg).build() //ensure direct put still is handled properly\n        assertEquals alg, jwk.getAlgorithm()\n        assertEquals alg, jwk.alg //test raw get via JWA member id\n    }\n\n    @Test\n    void testId() {\n        def kid = UUID.randomUUID().toString()\n        def jwk = builder().id(kid).build()\n        assertEquals kid, jwk.getId()\n        assertEquals kid, jwk.kid //test raw get via JWA member id\n    }\n\n    @Test\n    void testIdByPut() {\n        def kid = UUID.randomUUID().toString()\n        def jwk = builder().add('kid', kid).build()\n        assertEquals kid, jwk.getId()\n        assertEquals kid, jwk.kid //test raw get via JWA member id\n    }\n\n    @Test\n    //ensures that even if a raw single String value is present, it is represented as a Set per the JWA spec (string array)\n    void testOperationsByPutSingleStringValue() {\n        def s = 'wrapKey'\n        def op = Jwks.OP.get().get(s)\n        def canonical = Collections.setOf(s)\n        def idiomatic = Collections.setOf(op)\n        def jwk = builder().add('key_ops', s).build() // <-- put uses single raw String value, not a set\n        assertEquals idiomatic, jwk.getOperations() // <-- still get an idiomatic set\n        assertEquals canonical, jwk.key_ops         // <-- still get a canonical set\n    }\n\n    @Test\n    //ensures that even if a raw single KeyOperation value is present, it is represented as a Set per the JWA spec (string array)\n    void testOperationsByPutSingleIdiomaticValue() {\n        def s = 'wrapKey'\n        def op = Jwks.OP.get().get(s)\n        def canonical = Collections.setOf(s)\n        def idiomatic = Collections.setOf(op)\n        def jwk = builder().add('key_ops', op).build() // <-- put uses single raw KeyOperation value, not a set\n        assertEquals idiomatic, jwk.getOperations() // <-- still get an idiomatic set\n        assertEquals canonical, jwk.key_ops         // <-- still get a canonical set\n    }\n\n    @Test\n    void testOperation() {\n        def s = 'wrapKey'\n        def op = Jwks.OP.get().get(s)\n        def canonical = Collections.setOf(s)\n        def idiomatic = Collections.setOf(op)\n        def jwk = builder().operations().add(op).and().build()\n        assertEquals idiomatic, jwk.getOperations()\n        assertEquals canonical, jwk.key_ops\n    }\n\n    @Test\n    void testOperationCustom() {\n        def s = UUID.randomUUID().toString()\n        def op = Jwks.OP.builder().id(s).build()\n        def canonical = Collections.setOf(s)\n        def idiomatic = Collections.setOf(op)\n        def jwk = builder().operations().add(op).and().build()\n        assertEquals idiomatic, jwk.getOperations()\n        assertEquals canonical, jwk.key_ops\n    }\n\n    @Test\n    void testOperationCustomOverridesDefault() {\n        def s = 'sign'\n        def op = Jwks.OP.builder().id(s).related('verify').build()\n        def canonical = Collections.setOf(s)\n        def idiomatic = Collections.setOf(op)\n        def jwk = builder().operations().add(op).and().build()\n        assertEquals idiomatic, jwk.getOperations()\n        assertEquals canonical, jwk.key_ops\n        assertSame op, jwk.getOperations().iterator().next()\n\n        //now assert that the standard VERIFY operation treats this as related since it has the same ID:\n        canonical = Collections.setOf(s, 'verify')\n        idiomatic = Collections.setOf(op, Jwks.OP.VERIFY)\n        jwk = builder().operations().add(op).add(Jwks.OP.VERIFY).and().build() as Jwk\n        assertEquals idiomatic, jwk.getOperations()\n        assertEquals canonical, jwk.key_ops\n    }\n\n    @Test\n    void testOperations() {\n        def a = 'sign'\n        def b = 'verify'\n        def canonical = Collections.setOf(a, b)\n        def idiomatic = Collections.setOf(Jwks.OP.SIGN, Jwks.OP.VERIFY)\n        def jwk = builder().operations().add(idiomatic).and().build()\n        assertEquals idiomatic, jwk.getOperations()\n        assertEquals canonical, jwk.key_ops\n    }\n\n    @Test\n    void testOperationsUnrelated() {\n        try {\n            // exception thrown on setter, before calling build:\n            builder().operations().add(Collections.setOf(Jwks.OP.SIGN, Jwks.OP.ENCRYPT)).and()\n            fail()\n        } catch (IllegalArgumentException e) {\n            String msg = 'Unrelated key operations are not allowed. KeyOperation [\\'encrypt\\' (Encrypt content)] is ' +\n                    'unrelated to [\\'sign\\' (Compute digital signature or MAC)].'\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testOperationsPutUnrelatedStrings() {\n        try {\n            builder().add('key_ops', ['sign', 'encrypt']).build()\n            fail()\n        } catch (MalformedKeyException e) {\n            String msg = 'Unable to create JWK: Unrelated key operations are not allowed. KeyOperation ' +\n                    '[\\'encrypt\\' (Encrypt content)] is unrelated to [\\'sign\\' (Compute digital signature or MAC)].'\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testOperationsByCanonicalPut() {\n        def a = 'encrypt'\n        def b = 'decrypt'\n        def canonical = Collections.setOf(a, b)\n        def idiomatic = Collections.setOf(Jwks.OP.ENCRYPT, Jwks.OP.DECRYPT)\n        def jwk = builder().add('key_ops', canonical).build() // Set of String values, not KeyOperation objects\n        assertEquals idiomatic, jwk.getOperations()\n        assertEquals canonical, jwk.key_ops\n    }\n\n    @Test\n    void testOperationsByIdiomaticPut() {\n        def a = 'encrypt'\n        def b = 'decrypt'\n        def canonical = Collections.setOf(a, b)\n        def idiomatic = Collections.setOf(Jwks.OP.ENCRYPT, Jwks.OP.DECRYPT)\n        def jwk = builder().add('key_ops', idiomatic).build() // Set of KeyOperation values, not strings\n        assertEquals idiomatic, jwk.getOperations()\n        assertEquals canonical, jwk.key_ops\n    }\n\n    @Test\n    void testCustomOperationOverridesDefault() {\n        def op = Jwks.OP.builder().id('sign').description('Different Description')\n                .related(Jwks.OP.VERIFY.id).build()\n        def builder = builder().operationPolicy(Jwks.OP.policy().add(op).build())\n        def jwk = builder.operations().add(Collections.setOf(op, Jwks.OP.VERIFY)).and().build() as Jwk\n        assertSame op, jwk.getOperations().find({ it.id == 'sign' })\n    }\n\n    /**\n     * Asserts that if a .operations() builder is used, and its .and() method is not called, the change to the\n     * operations collection is still applied when building the JWK.\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/916\">JJWT Issue 916</a>\n     * @since 0.12.5\n     */\n    @Test\n    void testOperationsWithoutConjunction() {\n        def builder = builder()\n        builder.operations().clear().add(Jwks.OP.DERIVE_BITS) // no .and() call\n        def jwk = builder.build()\n        assertEquals(Jwks.OP.DERIVE_BITS, jwk.getOperations()[0])\n    }\n\n    @Test\n    void testProvider() {\n        def provider = TestKeys.BC\n        def jwk = builder().provider(provider).build()\n        assertEquals 'oct', jwk.getType()\n        assertSame provider, jwk.@context.@provider\n    }\n\n    @Test\n    void testFactoryThrowsIllegalArgumentException() {\n        def ctx = new DefaultJwkContext()\n        ctx.put('whatevs', 42)\n        //noinspection GroovyUnusedAssignment\n        JwkFactory factory = new JwkFactory() {\n            JwkContext newContext(JwkContext src, Key key) {\n                return null\n            }\n\n            @Override\n            Jwk createJwk(JwkContext jwkContext) {\n                throw new IllegalArgumentException(\"foo\")\n            }\n        }\n        def builder = new AbstractJwkBuilder(ctx, factory) {}\n        try {\n            builder.build()\n        } catch (MalformedKeyException expected) {\n            assertEquals 'Unable to create JWK: foo', expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractJwkTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.security.Jwk\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.SecretJwk\nimport org.junit.Before\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\nclass AbstractJwkTest {\n\n    AbstractJwk<? extends Key> jwk\n\n    static JwkContext<SecretKey> newCtx() {\n        return newCtx(null)\n    }\n\n    static JwkContext<SecretKey> newCtx(Map<String, ?> map) {\n        def ctx = new DefaultJwkContext(AbstractJwk.PARAMS)\n        ctx.put('kty', 'test')\n        if (!Collections.isEmpty(map as Map)) {\n            ctx.putAll(map)\n        }\n        ctx.setKey(TestKeys.HS256)\n        return ctx\n    }\n\n    static AbstractJwk<SecretKey> newJwk(JwkContext<SecretKey> ctx) {\n        return new AbstractJwk(ctx, Collections.of(AbstractJwk.KTY)) {\n            @Override\n            protected boolean equals(Jwk jwk) {\n                return this.@context.equals(jwk.@context)\n            }\n        }\n    }\n\n    @Before\n    void setUp() {\n        jwk = newJwk(newCtx())\n    }\n\n    @Test\n    void testGetFieldValue() {\n        assertEquals 'test', jwk.get(AbstractJwk.KTY)\n    }\n\n    @Test\n    void testContainsValue() {\n        assertTrue jwk.containsValue('test')\n        assertFalse jwk.containsValue('bar')\n    }\n\n    static void jwkImmutable(Closure c) {\n        try {\n            c.call()\n            fail()\n        } catch (UnsupportedOperationException expected) {\n            String msg = 'JWKs are immutable and may not be modified.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    static void jucImmutable(Closure c) {\n        try {\n            c.call()\n            fail()\n        } catch (UnsupportedOperationException expected) {\n            assertNull expected.getMessage() // java.util.Collections.unmodifiable* doesn't give a message\n        }\n    }\n\n    @Test\n    void testImmutable() {\n        jwk = newJwk(newCtx())\n        jwkImmutable { jwk.put('foo', 'bar') }\n        jwkImmutable { jwk.putAll([foo: 'bar']) }\n        jwkImmutable { jwk.remove('kty') }\n        jwkImmutable { jwk.clear() }\n    }\n\n    @Test\n    // ensure that any map or collection returned from the JWK is immutable as well:\n    void testCollectionsAreImmutable() {\n        def vals = [\n                map       : [foo: 'bar'],\n                list      : ['a'],\n                set       : ['b'] as Set,\n                collection: ['c'] as Collection\n        ]\n        jwk = newJwk(newCtx(vals))\n        jucImmutable { (jwk.get('map') as Map).remove('foo') }\n        jucImmutable { (jwk.get('list') as List).remove(0) }\n        jucImmutable { (jwk.get('set') as Set).remove('b') }\n        jucImmutable { (jwk.get('collection') as Collection).remove('c') }\n        jucImmutable { jwk.keySet().remove('map') }\n        jucImmutable { jwk.values().remove('a') }\n    }\n\n    @Test\n    // ensure that any array value returned from the JWK is a copy, so modifying it won't modify the original array\n    void testArraysAreCopied() {\n        def vals = [\n                array: ['a', 'b'] as String[]\n        ]\n        jwk = newJwk(newCtx(vals))\n        def returned = jwk.get('array')\n        assertTrue returned instanceof String[]\n        assertEquals 2, returned.length\n\n        //now modify it:\n        returned[0] = 'x'\n\n        //ensure the array structure hasn't changed:\n        def returned2 = jwk.get('array')\n        assertEquals 'a', returned2[0]\n        assertEquals 'b', returned2[1]\n    }\n\n    @Test\n    void testPrivateJwkToStringHasRedactedValues() {\n        def secretJwk = Jwks.builder().key(TestKeys.HS256).build()\n        assertTrue secretJwk.toString().contains('k=<redacted>')\n\n        def ecPrivJwk = Jwks.builder().key(TestKeys.ES256.pair.private).build()\n        assertTrue ecPrivJwk.toString().contains('d=<redacted>')\n\n        def rsaPrivJwk = Jwks.builder().key(TestKeys.RS256.pair.private).build()\n        String s = 'd=<redacted>, p=<redacted>, q=<redacted>, dp=<redacted>, dq=<redacted>, qi=<redacted>'\n        assertTrue rsaPrivJwk.toString().contains(s)\n    }\n\n    @Test\n    void testPrivateJwkHashCode() {\n        def secretJwk1 = Jwks.builder().key(TestKeys.HS256).add('hello', 'world').build()\n        def secretJwk2 = Jwks.builder().key(TestKeys.HS256).add('hello', 'world').build()\n        assertEquals secretJwk1.hashCode(), secretJwk2.hashCode()\n\n        def ecPrivJwk1 = Jwks.builder().key(TestKeys.ES256.pair.private).add('hello', 'ecworld').build()\n        def ecPrivJwk2 = Jwks.builder().key(TestKeys.ES256.pair.private).add('hello', 'ecworld').build()\n        assertEquals ecPrivJwk1.hashCode(), ecPrivJwk2.hashCode()\n\n        def rsaPrivJwk1 = Jwks.builder().key(TestKeys.RS256.pair.private).add('hello', 'rsaworld').build()\n        def rsaPrivJwk2 = Jwks.builder().key(TestKeys.RS256.pair.private).add('hello', 'rsaworld').build()\n        assertEquals rsaPrivJwk1.hashCode(), rsaPrivJwk2.hashCode()\n    }\n\n    @Test\n    void testEqualsWithNonJwk() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.HS256).build()\n        assertFalse jwk.equals(42)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AbstractSecureDigestAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.SecureRequest\nimport io.jsonwebtoken.security.SignatureException\nimport io.jsonwebtoken.security.VerifySecureDigestRequest\nimport org.junit.Test\n\nimport java.security.Key\nimport java.security.Provider\nimport java.security.PublicKey\nimport java.security.Security\n\nimport static org.junit.Assert.assertSame\nimport static org.junit.Assert.assertTrue\n\nclass AbstractSecureDigestAlgorithmTest {\n\n    @Test\n    void testSignAndVerifyWithExplicitProvider() {\n        Provider provider = Security.getProvider('BC')\n        def pair = Jwts.SIG.RS256.keyPair().build()\n        byte[] data = Strings.utf8('foo')\n        def payload = Streams.of(data)\n        byte[] signature = Jwts.SIG.RS256.digest(new DefaultSecureRequest<>(payload, provider, null, pair.getPrivate()))\n        payload.reset()\n        assertTrue Jwts.SIG.RS256.verify(new DefaultVerifySecureDigestRequest<PublicKey>(payload, provider, null, pair.getPublic(), signature))\n    }\n\n    @Test\n    void testSignFailsWithAnExternalException() {\n        def pair = Jwts.SIG.RS256.keyPair().build()\n        def ise = new IllegalStateException('foo')\n        def alg = new TestAbstractSecureDigestAlgorithm() {\n            @Override\n            protected byte[] doDigest(SecureRequest request) throws Exception {\n                throw ise\n            }\n        }\n        try {\n            def payload = Streams.of(Strings.utf8('foo'))\n            alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))\n        } catch (SignatureException e) {\n            assertTrue e.getMessage().startsWith('Unable to compute test signature with JCA algorithm \\'test\\' using key {')\n            assertTrue e.getMessage().endsWith('}: foo')\n            assertSame ise, e.getCause()\n        }\n    }\n\n    @Test\n    void testVerifyFailsWithExternalException() {\n        def pair = Jwts.SIG.RS256.keyPair().build()\n        def ise = new IllegalStateException('foo')\n        def alg = new TestAbstractSecureDigestAlgorithm() {\n            @Override\n            protected boolean doVerify(VerifySecureDigestRequest request) throws Exception {\n                throw ise\n            }\n        }\n        def data = Strings.utf8('foo')\n        def payload = Streams.of(data)\n        try {\n            byte[] signature = alg.digest(new DefaultSecureRequest(payload, null, null, pair.getPrivate()))\n            payload.reset()\n            alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, pair.getPublic(), signature))\n        } catch (SignatureException e) {\n            assertTrue e.getMessage().startsWith('Unable to verify test signature with JCA algorithm \\'test\\' using key {')\n            assertTrue e.getMessage().endsWith('}: foo')\n            assertSame ise, e.getCause()\n        }\n    }\n\n    class TestAbstractSecureDigestAlgorithm extends AbstractSecureDigestAlgorithm {\n\n        TestAbstractSecureDigestAlgorithm() {\n            super('test', 'test')\n        }\n\n        @Override\n        protected void validateKey(Key key, boolean signing) {\n        }\n\n        @Override\n        protected byte[] doDigest(SecureRequest request) throws Exception {\n            return new byte[1]\n        }\n\n        @Override\n        protected boolean doVerify(VerifySecureDigestRequest request) {\n            return false\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AesAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.security.SecureRandom\n\nimport static org.junit.Assert.*\n\n/**\n * @since 0.12.0\n */\nclass AesAlgorithmTest {\n\n    @Test(expected = IllegalArgumentException)\n    void testConstructorWithoutRequiredKeyLength() {\n        new TestAesAlgorithm('foo', 'foo', 0)\n    }\n\n    @Test\n    void testAssertKeyLength() {\n\n        def alg = new TestAesAlgorithm('foo', 'foo', 192)\n\n        SecretKey key = TestKeys.A128GCM //weaker than required\n\n        Request<byte[]> request = new DefaultSecureRequest(new byte[1], null, null, key)\n\n        try {\n            alg.assertKey(key)\n            fail()\n        } catch (SecurityException expected) {\n        }\n    }\n\n    @Test\n    void testValidateLengthKeyExceptionPropagated() {\n\n        def alg = new TestAesAlgorithm('foo', 'foo', 192)\n        def ex = new java.lang.SecurityException(\"HSM: not allowed\")\n        def key = new SecretKeySpec(new byte[1], 'AES') {\n            @Override\n            byte[] getEncoded() {\n                throw ex\n            }\n        }\n\n        try {\n            alg.validateLength(key, 192, true)\n            fail()\n        } catch (java.lang.SecurityException expected) {\n            assertSame ex, expected\n        }\n    }\n\n    @Test\n    void testValidateLengthKeyExceptionNotPropagated() {\n\n        def alg = new TestAesAlgorithm('foo', 'foo', 192)\n        def ex = new java.lang.SecurityException(\"HSM: not allowed\")\n        def key = new SecretKeySpec(new byte[1], 'AES') {\n            @Override\n            byte[] getEncoded() {\n                throw ex\n            }\n        }\n\n        //exception thrown, but we don't propagate:\n        assertNull alg.validateLength(key, 192, false)\n    }\n\n    @Test\n    void testAssertBytesWithLengthMismatch() {\n        int reqdBitLen = 192\n        def alg = new TestAesAlgorithm('foo', 'foo', reqdBitLen)\n        byte[] bytes = new byte[(reqdBitLen - 8) / Byte.SIZE]\n        try {\n            alg.assertBytes(bytes, 'test arrays', reqdBitLen)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            String msg = \"The 'foo' algorithm requires test arrays with a length of 192 bits (24 bytes).  \" +\n                    \"The provided key has a length of 184 bits (23 bytes).\"\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testGetSecureRandomWhenRequestHasSpecifiedASecureRandom() {\n\n        def alg = new TestAesAlgorithm('foo', 'foo', 128)\n\n        def secureRandom = new SecureRandom()\n\n        def ins = Streams.of('data')\n        def key = TestKeys.A256GCM\n        def aad = Strings.utf8('aad')\n        def req = new DefaultAeadRequest(ins, null, secureRandom, key, Streams.of(aad))\n\n        def returnedSecureRandom = alg.ensureSecureRandom(req)\n\n        assertSame(secureRandom, returnedSecureRandom)\n    }\n\n    static class TestAesAlgorithm extends AesAlgorithm implements AeadAlgorithm {\n\n        TestAesAlgorithm(String name, String transformationString, int requiredKeyLengthInBits) {\n            super(name, transformationString, requiredKeyLengthInBits)\n        }\n\n        @Override\n        void encrypt(AeadRequest req, AeadResult res) {\n        }\n\n        @Override\n        void decrypt(DecryptAeadRequest req, OutputStream res) {\n        }\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/AesGcmKeyAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.JweHeader\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.MalformedJwtException\nimport io.jsonwebtoken.impl.DefaultMutableJweHeader\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.lang.CheckedFunction\nimport io.jsonwebtoken.lang.Arrays\nimport io.jsonwebtoken.security.Keys\nimport io.jsonwebtoken.security.SecretKeyBuilder\nimport org.junit.Test\n\nimport javax.crypto.Cipher\nimport javax.crypto.spec.GCMParameterSpec\n\nimport static org.junit.Assert.*\n\nclass AesGcmKeyAlgorithmTest {\n\n    /**\n     * This tests asserts that our AeadAlgorithm implementation and the JCA 'AES/GCM/NoPadding' wrap algorithm\n     * produce the exact same values.  This should be the case when the transformation is identical, even though\n     * one uses Cipher.WRAP_MODE and the other uses a raw plaintext byte array.\n     */\n    @Test\n    void testAesWrapProducesSameResultAsAesAeadEncryptionAlgorithm() {\n\n        def alg = new GcmAesAeadAlgorithm(256)\n\n        def iv = new byte[12]\n        Randoms.secureRandom().nextBytes(iv)\n\n        def kek = alg.key().build()\n        def cek = alg.key().build()\n\n        final String jcaName = \"AES/GCM/NoPadding\"\n\n        JcaTemplate template = new JcaTemplate(jcaName)\n        byte[] jcaResult = template.withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            byte[] apply(Cipher cipher) throws Exception {\n                cipher.init(Cipher.WRAP_MODE, kek, new GCMParameterSpec(128, iv))\n                return cipher.wrap(cek)\n            }\n        })\n\n        //separate tag from jca ciphertext:\n        int ciphertextLength = jcaResult.length - 16 //AES block size in bytes (128 bits)\n        byte[] ciphertext = new byte[ciphertextLength]\n        System.arraycopy(jcaResult, 0, ciphertext, 0, ciphertextLength)\n\n        byte[] tag = new byte[16]\n        System.arraycopy(jcaResult, ciphertextLength, tag, 0, 16)\n\n        def out = new ByteArrayOutputStream(8192)\n        def encRequest = new DefaultAeadRequest(Streams.of(cek.getEncoded()), null, null, kek, null, iv)\n        def encResult = new DefaultAeadResult(out)\n        Jwts.ENC.A256GCM.encrypt(encRequest, encResult)\n\n        assertArrayEquals tag, encResult.digest\n        assertArrayEquals iv, encResult.iv\n        assertArrayEquals ciphertext, out.toByteArray()\n    }\n\n    static void assertAlgorithm(int keyLength) {\n\n        def alg = new AesGcmKeyAlgorithm(keyLength)\n        assertEquals 'A' + keyLength + 'GCMKW', alg.getId()\n\n        def template = new JcaTemplate('AES')\n\n        def header = Jwts.header().add('alg', alg.id).add('enc', 'foo')\n        def kek = template.generateSecretKey(keyLength)\n        def cek = template.generateSecretKey(keyLength)\n        def enc = new GcmAesAeadAlgorithm(keyLength) {\n            @Override\n            SecretKeyBuilder key() {\n                return Keys.builder(cek)\n            }\n        }\n\n        def delegate = new DefaultMutableJweHeader(header)\n        def ereq = new DefaultKeyRequest(kek, null, null, delegate, enc)\n\n        def result = alg.getEncryptionKey(ereq)\n\n        byte[] encryptedKeyBytes = result.getPayload()\n        assertFalse \"encryptedKey must be populated\", Arrays.length(encryptedKeyBytes) == 0\n\n        def jweHeader = header.build() as JweHeader\n\n        def req = new DefaultDecryptionKeyRequest(encryptedKeyBytes, null, null, jweHeader, enc, kek)\n        def dcek = alg.getDecryptionKey(req)\n\n        //Assert the decrypted key matches the original cek\n        assertEquals cek.algorithm, dcek.algorithm\n        assertArrayEquals cek.encoded, dcek.encoded\n    }\n\n    @Test\n    void testResultSymmetry() {\n        assertAlgorithm(128)\n        assertAlgorithm(192)\n        assertAlgorithm(256)\n    }\n\n    static void testDecryptionHeader(String headerName, Object value, String exmsg) {\n        int keyLength = 128\n        def alg = new AesGcmKeyAlgorithm(keyLength)\n        def template = new JcaTemplate('AES')\n        def headerBuilder = Jwts.header().add('alg', alg.id).add('enc', 'foo')\n        def kek = template.generateSecretKey(keyLength)\n        def cek = template.generateSecretKey(keyLength)\n        def enc = new GcmAesAeadAlgorithm(keyLength) {\n            @Override\n            SecretKeyBuilder key() {\n                return Keys.builder(cek)\n            }\n        }\n        def delegate = new DefaultMutableJweHeader(headerBuilder)\n        def ereq = new DefaultKeyRequest(kek, null, null, delegate, enc)\n        def result = alg.getEncryptionKey(ereq)\n\n        headerBuilder.remove(headerName)\n\n        headerBuilder.put(headerName, value)\n\n        byte[] encryptedKeyBytes = result.getPayload()\n\n        def header = headerBuilder.build() as JweHeader\n\n        try {\n            alg.getDecryptionKey(new DefaultDecryptionKeyRequest(encryptedKeyBytes, null, null, header, enc, kek))\n            fail()\n        } catch (MalformedJwtException iae) {\n            assertEquals exmsg, iae.getMessage()\n        }\n    }\n\n    static String missing(String id, String name) {\n        return \"JWE header is missing required '$id' ($name) value.\" as String\n    }\n\n    static String type(String name) {\n        return \"JWE header '${name}' value must be a String. Actual type: java.lang.Integer\" as String\n    }\n\n    static String length(String name, int requiredBitLength) {\n        return \"JWE header '${name}' decoded byte array must be ${Bytes.bitsMsg(requiredBitLength)} long. Actual length: ${Bytes.bitsMsg(16)}.\"\n    }\n\n    @Test\n    void testMissingHeaders() {\n        testDecryptionHeader('iv', null, missing('iv', 'Initialization Vector'))\n        testDecryptionHeader('tag', null, missing('tag', 'Authentication Tag'))\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ConcatKDFTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport org.junit.Before\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.nio.charset.StandardCharsets\nimport java.security.MessageDigest\n\nimport static org.junit.Assert.*\n\nclass ConcatKDFTest {\n\n    ConcatKDF CONCAT_KDF = EcdhKeyAlgorithm.CONCAT_KDF\n\n    private byte[] Z\n\n    @Before\n    void setUp() {\n        Z = new byte[16]\n        Randoms.secureRandom().nextBytes(Z)\n    }\n\n    @Test\n    void testNullOtherInfo() {\n        final int derivedKeyBitLength = 256\n        final byte[] OtherInfo = null\n\n        // exactly 1 Concat KDF iteration - derived key bit length of 256 is same as SHA-256 digest length:\n        def md = MessageDigest.getInstance(\"SHA-256\")\n        md.update([0, 0, 0, 1] as byte[])\n        md.update(Z)\n        md.update(Bytes.EMPTY) // null OtherInfo should equate to a Bytes.EMPTY argument here\n        byte[] digest = md.digest()\n\n        SecretKey key = CONCAT_KDF.deriveKey(Z, derivedKeyBitLength, OtherInfo)\n        byte[] derived = key.getEncoded()\n        assertNotNull(key)\n        assertArrayEquals(digest, derived)\n    }\n\n    @Test\n    void testEmptyOtherInfo() {\n        final int derivedKeyBitLength = 256\n        final byte[] OtherInfo = Bytes.EMPTY\n\n        // exactly 1 Concat KDF iteration - derived key bit length of 256 is same as SHA-256 digest length:\n        def md = MessageDigest.getInstance(\"SHA-256\")\n        md.update([0, 0, 0, 1] as byte[])\n        md.update(Z)\n        md.update(Bytes.EMPTY) // empty OtherInfo should equate to a Bytes.EMPTY argument here\n        byte[] digest = md.digest()\n\n        SecretKey key = CONCAT_KDF.deriveKey(Z, derivedKeyBitLength, OtherInfo)\n        byte[] derived = key.getEncoded()\n        assertNotNull(key)\n        assertArrayEquals(digest, derived)\n    }\n\n    @Test\n    void testPopulatedOtherInfo() {\n        final int derivedKeyBitLength = 256\n        final byte[] OtherInfo = 'whatever'.getBytes(StandardCharsets.UTF_8)\n\n        // exactly 1 Concat KDF iteration - derived key bit length of 256 is same as SHA-256 digest length:\n        def md = MessageDigest.getInstance(\"SHA-256\")\n        md.update([0, 0, 0, 1] as byte[])\n        md.update(Z)\n        md.update(OtherInfo) // ensure OtherInfo is included in the digest\n        byte[] digest = md.digest()\n\n        SecretKey key = CONCAT_KDF.deriveKey(Z, derivedKeyBitLength, OtherInfo)\n        byte[] derived = key.getEncoded()\n        assertNotNull(key)\n        assertArrayEquals(digest, derived)\n    }\n\n    @Test\n    void testNonPositiveBitLength() {\n        try {\n            CONCAT_KDF.deriveKey(Z, 0, null)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = 'derivedKeyBitLength must be a positive integer.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testDerivedKeyBitLengthBiggerThanJdkMax() {\n        byte[] Z = new byte[16]\n        long bitLength = Long.valueOf(Integer.MAX_VALUE) * 8L + 8L // one byte more than java byte arrays can handle\n        try {\n            CONCAT_KDF.deriveKey(Z, bitLength, null)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = 'derivedKeyBitLength may not exceed 17179869176 bits (2147483647 bytes). ' +\n                    'Specified size: 17179869184 bits (2147483648 bytes).'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ConstantKeyLocatorTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.UnsupportedJwtException\nimport io.jsonwebtoken.impl.DefaultHeader\nimport io.jsonwebtoken.impl.DefaultJweHeader\nimport io.jsonwebtoken.impl.DefaultJwsHeader\nimport org.junit.Test\n\nimport javax.crypto.spec.SecretKeySpec\n\nimport static org.junit.Assert.*\n\nclass ConstantKeyLocatorTest {\n\n    @Test\n    void testSignatureVerificationKey() {\n        def key = new SecretKeySpec(new byte[1], 'AES') //dummy key for testing\n        assertSame key, new ConstantKeyLocator(key, null).locate(new DefaultJwsHeader([:]))\n    }\n\n    @Test\n    void testSignatureVerificationKeyMissing() {\n        def locator = new ConstantKeyLocator(null, null)\n        try {\n            locator.locate(new DefaultJwsHeader([:]))\n        } catch (UnsupportedJwtException uje) {\n            String msg = 'Signed JWTs are not supported: the JwtParser has not been configured with a signature ' +\n                    'verification key or a KeyResolver. Consider configuring the JwtParserBuilder with one of these ' +\n                    'to ensure it can use the necessary key to verify JWS signatures.'\n            assertEquals msg, uje.getMessage()\n        }\n    }\n\n    @Test\n    void testDecryptionKey() {\n        def key = new SecretKeySpec(new byte[1], 'AES') //dummy key for testing\n        assertSame key, new ConstantKeyLocator(null, key).locate(new DefaultJweHeader([:]))\n    }\n\n    @Test\n    void testDecryptionKeyMissing() {\n        def locator = new ConstantKeyLocator(null, null)\n        try {\n            locator.locate(new DefaultJweHeader([:]))\n        } catch (UnsupportedJwtException uje) {\n            String msg = 'Encrypted JWTs are not supported: the JwtParser has not been configured with a decryption ' +\n                    'key or a KeyResolver. Consider configuring the JwtParserBuilder with one of these ' +\n                    'to ensure it can use the necessary key to decrypt JWEs.'\n            assertEquals msg, uje.getMessage()\n        }\n    }\n\n    @Test\n    void testApply() {\n        def locator = new ConstantKeyLocator(null, null)\n        assertNull locator.apply(new DefaultHeader([:]))\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/CryptoAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass CryptoAlgorithmTest {\n\n    @Test\n    void testEqualsSameInstance() {\n        def alg = new TestCryptoAlgorithm()\n        assertEquals alg, alg\n    }\n\n    @Test\n    void testEqualsSameNameAndJcaName() {\n        def alg1 = new TestCryptoAlgorithm()\n        def alg2 = new TestCryptoAlgorithm()\n        assertEquals alg1, alg2\n    }\n\n    @Test\n    void testEqualsSameNameButDifferentJcaName() {\n        def alg1 = new TestCryptoAlgorithm('test', 'test1')\n        def alg2 = new TestCryptoAlgorithm('test', 'test2')\n        assertNotEquals alg1, alg2\n    }\n\n    @Test\n    void testEqualsOtherType() {\n        assertNotEquals new TestCryptoAlgorithm(), new Object()\n    }\n\n    @Test\n    void testToString() {\n        assertEquals 'test', new TestCryptoAlgorithm().toString()\n    }\n\n    @Test\n    void testHashCode() {\n        int hash = 7\n        hash = 31 * hash + 'test'.hashCode()\n        hash = 31 * hash + 'jcaName'.hashCode()\n        assertEquals hash, new TestCryptoAlgorithm().hashCode()\n    }\n\n    @Test\n    void testEnsureSecureRandomWorksWithNullRequest() {\n        def alg = new TestCryptoAlgorithm()\n        def random = alg.ensureSecureRandom(null)\n        assertSame Randoms.secureRandom(), random\n    }\n\n    class TestCryptoAlgorithm extends CryptoAlgorithm {\n        TestCryptoAlgorithm() {\n            this('test', 'jcaName')\n        }\n\n        TestCryptoAlgorithm(String id, String jcaName) {\n            super(id, jcaName)\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultHashAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.HashAlgorithm\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport static org.junit.Assert.assertTrue\n\nclass DefaultHashAlgorithmTest {\n\n    static final def algs = [DefaultHashAlgorithm.SHA1, Jwks.HASH.SHA256]\n\n    @Test\n    void testDigestAndVerify() {\n        byte[] data = Strings.utf8('Hello World')\n        InputStream payload = Streams.of(data)\n        for (HashAlgorithm alg : algs) {\n            byte[] hash = alg.digest(new DefaultRequest<>(payload, null, null))\n            payload.reset()\n            assertTrue alg.verify(new DefaultVerifyDigestRequest(payload, null, null, hash))\n            payload.reset()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkContextTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.lang.Parameter\nimport io.jsonwebtoken.impl.lang.Parameters\nimport io.jsonwebtoken.io.Encoders\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultJwkContextTest {\n\n    @Test\n    void testX509Url() {\n        def uri = URI.create('https://github.com/jwtk/jjwt')\n        def ctx = new DefaultJwkContext()\n        ctx.x509Url(uri)\n        assertEquals uri, ctx.getX509Url()\n        assertEquals uri.toString(), ctx.get('x5u')\n    }\n\n    @Test\n    void testX509CertificateChain() {\n        def chain = TestKeys.RS256.chain\n        def ctx = new DefaultJwkContext()\n        ctx.x509Chain(chain)\n        assertEquals chain, ctx.getX509Chain()\n    }\n\n    @Test\n    void testX509CertificateSha1Thumbprint() {\n        def thumbprint = Bytes.randomBits(128)\n        def ctx = new DefaultJwkContext()\n        ctx.x509Sha1Thumbprint(thumbprint)\n        assertArrayEquals thumbprint, ctx.getX509Sha1Thumbprint()\n        assertEquals Encoders.BASE64URL.encode(thumbprint), ctx.get('x5t')\n    }\n\n    @Test\n    void testX509CertificateSha256Thumbprint() {\n        def thumbprint = Bytes.randomBits(256)\n        def ctx = new DefaultJwkContext()\n        ctx.x509Sha256Thumbprint(thumbprint)\n        assertArrayEquals thumbprint, ctx.getX509Sha256Thumbprint()\n        assertEquals Encoders.BASE64URL.encode(thumbprint), ctx.get('x5t#S256')\n    }\n\n    @Test\n    void testGetName() {\n        def ctx = new DefaultJwkContext()\n        assertEquals 'JWK', ctx.getName()\n    }\n\n    @Test\n    void testGetNameWhenSecretJwk() {\n        def ctx = new DefaultJwkContext(DefaultSecretJwk.PARAMS)\n        ctx.put('kty', 'oct')\n        assertEquals 'Secret JWK', ctx.getName()\n    }\n\n    @Test\n    void testGetNameWithGenericPublicKey() {\n        def ctx = new DefaultJwkContext()\n        ctx.setKey(TestKeys.ES256.pair.public)\n        assertEquals 'Public JWK', ctx.getName()\n    }\n\n    @Test\n    void testGetNameWithGenericPrivateKey() {\n        def ctx = new DefaultJwkContext()\n        ctx.setKey(TestKeys.ES256.pair.private)\n        assertEquals 'Private JWK', ctx.getName()\n    }\n\n    @Test\n    void testGetNameWithEdwardsPublicKey() {\n        def ctx = new DefaultJwkContext()\n        ctx.setKey(TestKeys.X448.pair.public)\n        ctx.setType(DefaultOctetPublicJwk.TYPE_VALUE)\n        assertEquals 'Octet Public JWK', ctx.getName()\n    }\n\n    @Test\n    void testGetNameWithEdwardsPrivateKey() {\n        def ctx = new DefaultJwkContext()\n        ctx.setKey(TestKeys.X448.pair.private)\n        ctx.setType(DefaultOctetPublicJwk.TYPE_VALUE)\n        assertEquals 'Octet Private JWK', ctx.getName()\n    }\n\n    @Test\n    void testGStringPrintsRedactedValues() {\n        // DO NOT REMOVE THIS METHOD: IT IS CRITICAL TO ENSURE GROOVY STRINGS DO NOT LEAK SECRET/PRIVATE KEY MATERIAL\n        def ctx = new DefaultJwkContext(DefaultSecretJwk.PARAMS)\n        ctx.put('kty', 'oct')\n        ctx.put('k', 'test')\n        String s = '[kty:oct, k:<redacted>]'\n        assertEquals \"$s\", \"$ctx\"\n    }\n\n    @Test\n    void testGStringToStringPrintsRedactedValues() {\n        def ctx = new DefaultJwkContext(DefaultSecretJwk.PARAMS)\n        ctx.put('kty', 'oct')\n        ctx.put('k', 'test')\n        String s = '{kty=oct, k=<redacted>}'\n        assertEquals \"$s\", \"${ctx.toString()}\"\n    }\n\n    @Test\n    void testFieldWithoutKey() {\n        def ctx = new DefaultJwkContext(DefaultSecretJwk.PARAMS)\n        Parameter param = Parameters.string('kid', 'My Key ID')\n        def newCtx = ctx.parameter(param)\n        assertSame param, newCtx.@PARAMS.get('kid')\n        assertNull newCtx.getKey()\n    }\n\n    @Test\n    void testFieldWithKey() {\n        def key = TestKeys.HS256\n        def ctx = new DefaultJwkContext(DefaultSecretJwk.PARAMS)\n        ctx.setKey(key)\n        Parameter param = Parameters.string('kid', 'My Key ID')\n        def newCtx = ctx.parameter(param)\n        assertSame param, newCtx.@PARAMS.get('kid') // registry created with custom param instead of default\n        assertSame key, newCtx.getKey() // copied over correctly\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkParserBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.CharSequenceReader\nimport io.jsonwebtoken.impl.io.ConvertingParser\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.io.AbstractDeserializer\nimport io.jsonwebtoken.io.DeserializationException\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.MalformedKeyException\nimport io.jsonwebtoken.security.SecretJwk\nimport org.junit.Test\n\nimport java.security.Key\nimport java.security.Provider\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass DefaultJwkParserBuilderTest {\n\n    // This JSON was borrowed from RFC7520Section3Test.FIGURE_2 and modified to\n    // replace the 'use' member with 'key_ops` for this test:\n    static String UNRELATED_OPS_JSON = Strings.trimAllWhitespace('''\n        {\n          \"kty\": \"EC\",\n          \"kid\": \"bilbo.baggins@hobbiton.example\",\n          \"key_ops\": [\"sign\", \"encrypt\"],\n          \"crv\": \"P-521\",\n          \"x\": \"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9\n                A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt\",\n          \"y\": \"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy\n                SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1\",\n          \"d\": \"AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb\n                KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt\"\n        }''')\n\n    @Test(expected = IllegalArgumentException)\n    void parseNull() {\n        Jwks.parser().build().parse((CharSequence) null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void parseEmpty() {\n        Jwks.parser().build().parse(Strings.EMPTY)\n    }\n\n    @Test\n    void testStaticFactoryMethod() {\n        assertTrue Jwks.parser() instanceof DefaultJwkParserBuilder\n    }\n\n    @Test\n    void testProvider() {\n        Provider provider = createMock(Provider)\n        def parser = Jwks.parser().provider(provider).build() as ConvertingParser\n        assertSame provider, parser.converter.supplier.provider\n    }\n\n    @Test\n    void testDeserializer() {\n        Deserializer<Map<String, ?>> deser = createMock(Deserializer)\n        def m = RFC7516AppendixA3Test.KEK_VALUES // any test key will do\n        expect(deser.deserialize((Reader) anyObject(Reader))).andReturn(m)\n        replay deser\n        def jwk = Jwks.parser().json(deser).build().parse('foo')\n        verify deser\n        assertTrue jwk instanceof SecretJwk\n        assertEquals m.kty, jwk.kty\n        assertEquals m.k, jwk.k.get()\n    }\n\n    @Test\n    void testOperationPolicy() {\n        def parser = Jwks.parser().build() as ConvertingParser\n\n        try {\n            // parse a JWK that has unrelated operations (prevented by default):\n            parser.parse(UNRELATED_OPS_JSON)\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"Unable to create JWK: Unrelated key operations are not allowed. KeyOperation \" +\n                    \"['encrypt' (Encrypt content)] is unrelated to ['sign' (Compute digital signature or MAC)].\"\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testOperationPolicyOverride() {\n        def policy = Jwks.OP.policy().unrelated().build()\n        def parser = Jwks.parser().operationPolicy(policy).build()\n        assertNotNull parser.parse(UNRELATED_OPS_JSON) // no exception because policy allows it\n    }\n\n    @Test\n    void testKeys() {\n\n        Set<Key> keys = new LinkedHashSet<>()\n        TestKeys.SECRET.each { keys.add(it) }\n        TestKeys.ASYM.each {\n            keys.add(it.pair.public)\n            keys.add(it.pair.private)\n        }\n\n        for (Key key : keys) {\n            //noinspection GroovyAssignabilityCheck\n            def jwk = Jwks.builder().key(key).build()\n            String json = Jwks.UNSAFE_JSON(jwk)\n\n            def parser = Jwks.parser().build()\n\n            // CharSequence parsing:\n            def parsed = parser.parse(json)\n            assertEquals jwk, parsed\n\n            // Reader parsing:\n            parsed = parser.parse(new CharSequenceReader(json))\n            assertEquals jwk, parsed\n\n            // InputStream parsing:\n            parsed = parser.parse(Streams.of(json))\n            assertEquals jwk, parsed\n        }\n    }\n\n    @Test\n    void testKeysWithProvider() {\n\n        Set<Key> keys = new LinkedHashSet<>()\n        TestKeys.HS.each { keys.add(it) }\n        TestKeys.ASYM.each {\n            keys.add(it.pair.public)\n            keys.add(it.pair.private)\n        }\n\n        def provider = TestKeys.BC //always used\n\n        for (Key key : keys) {\n            //noinspection GroovyAssignabilityCheck\n            def jwk = Jwks.builder().provider(provider).key(key).build()\n            String json = Jwks.UNSAFE_JSON(jwk)\n            def parsed = Jwks.parser().provider(provider).build().parse(json)\n            assertEquals jwk, parsed\n            assertSame provider, parsed.@context.@provider\n        }\n    }\n\n    @Test\n    void testDeserializationFailure() {\n        def deser = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                throw new DeserializationException('test')\n            }\n        }\n        def parser = new DefaultJwkParserBuilder().json(deser).build()\n        try {\n            parser.parse('foo')\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"Malformed JWK JSON: test\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkSetBuilderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.Jwk\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.KeyOperationPolicy\nimport io.jsonwebtoken.security.MalformedKeySetException\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\nclass DefaultJwkSetBuilderTest {\n\n    private DefaultJwkSetBuilder builder\n\n    static final Map<String, ?> SECRET_JWK_MAP = [\n            kty: 'oct',\n            k  : Encoders.BASE64URL.encode(TestKeys.HS256.getEncoded())\n    ]\n\n    private static void assertIllegal(String msg, def c) {\n        try {\n            c()\n            fail()\n        } catch (IllegalArgumentException expected) {\n            assertEquals msg, expected.message\n        }\n    }\n\n    private static void assertMalformed(String msg, def c) {\n        try {\n            c()\n            fail()\n        } catch (MalformedKeySetException expected) {\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Before\n    void setUp() {\n        builder = new DefaultJwkSetBuilder()\n    }\n\n    @Test\n    void testStaticFactoryMethod() {\n        assertTrue Jwks.set() instanceof DefaultJwkSetBuilder\n    }\n\n    @Test\n    void testEmpty() {\n        String msg = \"Missing required ${DefaultJwkSet.KEYS} parameter.\"\n        assertMalformed msg, { builder.build() }\n    }\n\n    @Test\n    void testNonEmptyWithoutKeys() {\n        String msg = \"Missing required ${DefaultJwkSet.KEYS} parameter.\"\n        assertMalformed msg, { builder.add('one', 'one').build() }\n    }\n\n    @Test\n    void testAddEntry() {\n        builder.add('one', 'one')\n        builder.add('keys', [SECRET_JWK_MAP])\n        def set = builder.build()\n        assertEquals 'one', set.one\n    }\n\n    @Test\n    void testDeleteEntry() {\n        builder.add('one', 'one')\n        builder.add('two', 'two')\n        builder.delete('two')\n        builder.add('keys', [SECRET_JWK_MAP])\n        def set = builder.build()\n        assertEquals 2, set.size() // 'one' + 'keys'\n        assertEquals 'one', set.one\n    }\n\n    @Test\n    void testBuilderEmpty() {\n        def set = builder.add('one', 'one').add('two', 'two')\n                .empty() // clear everything out\n                .add('keys', [SECRET_JWK_MAP]).build()\n        assertEquals 1, set.size() // only 'keys' remains\n        assertTrue set.containsKey('keys')\n    }\n\n    @Test\n    void testAddMap() {\n        def m = [one: 'one', two: 'two']\n        def set = builder.add(m).add('keys', [SECRET_JWK_MAP]).build()\n        assertEquals 3, set.size() // 'one' + 'two' + 'keys'\n        assertEquals 'one', set.one\n        assertEquals 'two', set.two\n        assertTrue set.containsKey('keys')\n    }\n\n    /**\n     * Asserts that a raw map put('keys',val) (not using the .add(jwk), .add(collection) or .keys methods) still\n     * converts to JWKs\n     */\n    @Test\n    void testPutKeysSingle() {\n        def key = TestKeys.HS256\n        def jwkMap = [\n                kty: 'oct',\n                k  : Encoders.BASE64URL.encode(key.getEncoded())\n        ]\n        def jwk = Jwks.builder().key(key).build()\n        def expected = [jwk] as Set\n        def set = builder.add('keys', [jwkMap]).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    /**\n     * Asserts that a raw map put('keys', val) (not using the .add(jwk), .add(collection) or .keys methods) still\n     * converts to JWKs\n     */\n    @Test\n    void testPutKeysMultiple() {\n        def key1 = TestKeys.HS256\n        def jwk1Map = [\n                kty: 'oct',\n                k  : Encoders.BASE64URL.encode(key1.getEncoded())\n        ]\n        def key2 = TestKeys.HS384\n        def jwk2Map = [\n                kty: 'oct',\n                k  : Encoders.BASE64URL.encode(key2.getEncoded())\n        ]\n        def jwk1 = Jwks.builder().key(key1).build()\n        def jwk2 = Jwks.builder().key(key2).build()\n        def expected = [jwk1, jwk2] as Set\n        def set = builder.add('keys', [jwk1Map, jwk2Map]).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testAddKeySingle() {\n        def key = TestKeys.HS256\n        def jwk = Jwks.builder().key(key).build()\n        def expected = [jwk] as Set\n        def set = builder.add(jwk).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testAddKeyNull() {\n        def key = TestKeys.HS256\n        def jwk = Jwks.builder().key(key).build()\n        def expected = [jwk] as Set\n        assertNotNull builder.add((Jwk<? extends Key>) null) // no exception thrown\n        def set = builder.add(jwk).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testAddKeyMultiple() {\n        def key1 = TestKeys.HS256\n        def key2 = TestKeys.HS384\n        def jwk1 = Jwks.builder().key(key1).build()\n        def jwk2 = Jwks.builder().key(key2).build()\n        def expected = [jwk1, jwk2] as Set\n        def set = builder.add(jwk1).add(jwk2).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testAddKeysSingle() {\n        def key = TestKeys.HS256\n        def jwk = Jwks.builder().key(key).build()\n        def expected = [jwk] as Set\n        def set = builder.add(expected).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testAddKeysMultiple() {\n        def key1 = TestKeys.HS256\n        def key2 = TestKeys.HS384\n        def jwk1 = Jwks.builder().key(key1).build()\n        def jwk2 = Jwks.builder().key(key2).build()\n        def expected = [jwk1, jwk2] as Set\n        def set = builder.add(expected).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testAddKeysEmpty() {\n        def key1 = TestKeys.HS256\n        def key2 = TestKeys.HS384\n        def jwk1 = Jwks.builder().key(key1).build()\n        def jwk2 = Jwks.builder().key(key2).build()\n        def expected = [jwk1, jwk2] as Set\n        assertNotNull builder.add([]) // no exception thrown\n        def set = builder.add(expected).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testSetKeysSingle() {\n        def key = TestKeys.HS256\n        def jwk = Jwks.builder().key(key).build()\n        def expected = [jwk] as Set\n        def set = builder.keys(expected).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testSetKeysMultiple() {\n        def key1 = TestKeys.HS256\n        def key2 = TestKeys.HS384\n        def jwk1 = Jwks.builder().key(key1).build()\n        def jwk2 = Jwks.builder().key(key2).build()\n        def expected = [jwk1, jwk2] as Set\n        def set = builder.keys(expected).build()\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testKeysFullReplacement() {\n        def key1 = TestKeys.HS256\n        def key2 = TestKeys.HS384\n        def jwk1 = Jwks.builder().key(key1).build()\n        def jwk2 = Jwks.builder().key(key2).build()\n        def expected = [jwk2] as Set\n        def set = builder.add(jwk1).keys(expected).build() // jwk1 won't be in the result\n        assertEquals expected, set.getKeys()\n    }\n\n    @Test\n    void testProvider() {\n        def key = TestKeys.HS256\n        def provider = TestKeys.BC\n        def jwkMap = [\n                kty: 'oct',\n                k  : Encoders.BASE64URL.encode(key.getEncoded())\n        ]\n        def jwk = Jwks.builder().provider(provider).key(key).build()\n        def set = builder.provider(TestKeys.BC).add('keys', [jwkMap]).build()\n        assertEquals jwk, set.getKeys().iterator().next()\n    }\n\n    @Test\n    void testDefaultKeyOperationPolicy() {\n\n        // default policy\n        def key = TestKeys.HS256\n        def goodMap = [\n                kty    : 'oct',\n                k      : Encoders.BASE64URL.encode(key.getEncoded()),\n                key_ops: ['sign']\n        ]\n        builder.add('keys', [goodMap]).build() // no exception\n\n        def badMap = [\n                kty    : 'oct',\n                k      : Encoders.BASE64URL.encode(key.getEncoded()),\n                key_ops: ['sign', 'encrypt'] // unrelated operations\n        ]\n\n        String msg = \"Invalid Map ${DefaultJwkSet.KEYS} value: \" +\n                \"[{kty=${badMap.kty}, k=${badMap.k}, key_ops=${badMap.key_ops}}]. \" +\n                \"Unable to create JWK: Unrelated key \" +\n                \"operations are not allowed. KeyOperation [${Jwks.OP.ENCRYPT}] is unrelated to \" +\n                \"[${Jwks.OP.SIGN}].\"\n\n        assertIllegal msg, { builder.add('keys', [badMap]).build() }\n    }\n\n    @Test\n    void testCustomKeyOperationPolicy() {\n        def key = TestKeys.HS256\n        def badMap = [\n                kty    : 'oct',\n                k      : Encoders.BASE64URL.encode(key.getEncoded()),\n                key_ops: ['sign', 'encrypt'] // unrelated, but we'll allow next:\n        ]\n        def policy = new DefaultKeyOperationPolicy(Jwks.OP.get().values(), true) // unrelated allowed\n        builder = builder.operationPolicy(policy) as DefaultJwkSetBuilder\n        builder.add('keys', [badMap]).build() // no exception thrown\n    }\n\n    @Test\n    void testNullPolicy() {\n        builder.operationPolicy(null)\n        // assert that the default policy has been applied instead of null:\n        def defaultPolicy = AbstractJwkBuilder.DEFAULT_OPERATION_POLICY\n        // ensure default has been applied instead of null:\n        assertSame defaultPolicy, builder.operationPolicy\n        assertSame defaultPolicy, builder.converter.JWK_CONVERTER.supplier.operationPolicy\n    }\n\n    @Test\n    void testPolicyChangeValidatesExistingJwks() {\n        def key = TestKeys.HS256\n        def badMap = [\n                kty    : 'oct',\n                k      : Encoders.BASE64URL.encode(key.getEncoded()),\n                key_ops: ['sign', 'encrypt'] // unrelated, but we'll allow next:\n        ]\n        KeyOperationPolicy policy = Jwks.OP.policy().unrelated().build()\n        def jwk = Jwks.builder().operationPolicy(policy).add(badMap).build()\n\n        builder.operationPolicy(policy)\n        builder.add(jwk) // allowed due to less restrictive policy\n\n        //now enable new more restrictive policy:\n        policy = AbstractJwkBuilder.DEFAULT_OPERATION_POLICY\n        String msg = \"Unrelated key operations are not allowed. KeyOperation \" +\n                \"[${Jwks.OP.ENCRYPT}] is unrelated to [${Jwks.OP.SIGN}].\"\n        assertIllegal msg, { builder.operationPolicy(policy) }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkSetParserBuilderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.io.AbstractDeserializer\nimport io.jsonwebtoken.io.DeserializationException\nimport io.jsonwebtoken.io.Parser\nimport io.jsonwebtoken.security.JwkSet\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.MalformedKeySetException\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultJwkSetParserBuilderTest {\n\n    private Parser<JwkSet> parser\n\n    private static void assertMalformed(String msg, Closure<?> c) {\n        try {\n            c()\n            fail()\n        } catch (MalformedKeySetException expected) {\n            assertEquals msg, expected.message\n        }\n    }\n\n    private void assertMalformed(String input, String msg) {\n        assertMalformed(msg, { parser.parse(input) })\n    }\n\n    private static void assertEmpty(JwkSet result) {\n        JwkSetConverterTest.assertEmpty(result)\n    }\n\n    private static DefaultJwkSetParserBuilder builder() {\n        return new DefaultJwkSetParserBuilder()\n    }\n\n    @Before\n    void setUp() {\n        parser = builder().build()\n    }\n\n    @Test\n    void testStaticFactoryMethod() {\n        assertTrue Jwks.setParser() instanceof DefaultJwkSetParserBuilder\n    }\n\n    /**\n     * Asserts that a deserialization problem is represented as a MalformedKeySetException\n     */\n    @Test\n    void testDeserializeException() {\n        def deser = new AbstractDeserializer() {\n            @Override\n            protected Object doDeserialize(Reader reader) throws Exception {\n                throw new DeserializationException('foo')\n            }\n        }\n        parser = new DefaultJwkSetParserBuilder().json(deser).build()\n\n        try {\n            parser.parse('foo')\n        } catch (MalformedKeySetException expected) {\n            String msg = \"Malformed JWK Set JSON: foo\"\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test(expected = MalformedKeySetException)\n    void testJsonNull() {\n        parser.parse('null')\n    }\n\n    @Test(expected = MalformedKeySetException)\n    void testNonJSONObject() {\n        parser.parse('42')\n    }\n\n    @Test(expected = MalformedKeySetException)\n    void testInvalidJSONObjectWithoutStringKeys() {\n        parser.parse('{42:42}') // non-string key\n    }\n\n    @Test\n    void testEmptyJsonObject() {\n        assertMalformed '{}', \"Missing required ${DefaultJwkSet.KEYS} parameter.\"\n    }\n\n    @Test\n    void testJsonObjectWithoutKeysMember() {\n        assertMalformed '{\"answerToLife\":42}', \"Missing required ${DefaultJwkSet.KEYS} parameter.\"\n    }\n\n    @Test\n    void testKeysMemberNull() {\n        assertMalformed '{\"keys\":null}', \"JWK Set ${DefaultJwkSet.KEYS} value cannot be null.\"\n    }\n\n    @Test\n    void testKeysMemberNonCollection() {\n        String msg = \"JWK Set ${DefaultJwkSet.KEYS} value must be a Collection (JSON Array). Type found: \" +\n                \"java.lang.Integer\"\n        assertMalformed '{\"keys\":42}', msg\n    }\n\n    @Test\n    void testKeysMemberEmptyCollection() {\n        assertMalformed '{\"keys\":[]}', \"JWK Set ${DefaultJwkSet.KEYS} collection cannot be empty.\"\n    }\n\n    @Test\n    void testKeysMemberCollectionWithNullElement() {\n        assertEmpty parser.parse('{\"keys\":[null]}')\n    }\n\n    @Test\n    void testKeysMemberCollectionWithNullElementNotIgnored() {\n        parser = builder().ignoreUnsupported(false).build()\n        assertMalformed '{\"keys\":[null]}', \"JWK Set keys[0]: JWK cannot be null.\"\n    }\n\n    @Test\n    void testKeysMemberCollectionWithNonObjectElement() {\n        assertEmpty parser.parse('{\"keys\":[42]}')\n    }\n\n    @Test\n    void testKeysMemberCollectionWithNonObjectElementNotIgnored() {\n        parser = builder().ignoreUnsupported(false).build()\n        String msg = \"JWK Set keys[0]: JWK must be a Map<String,?> (JSON Object). Type found: java.lang.Integer.\"\n        assertMalformed '{\"keys\":[42]}', msg\n    }\n\n    @Test\n    void testKeysMemberCollectionWithEmptyObjectElement() {\n        assertEmpty parser.parse('{\"keys\":[{}]}')\n    }\n\n    @Test\n    void testKeysMemberCollectionWithEmptyObjectElementNotIgnored() {\n        parser = builder().ignoreUnsupported(false).build()\n        String msg = \"JWK Set keys[0]: JWK is missing required ${AbstractJwk.KTY} parameter.\"\n        assertMalformed '{\"keys\":[{}]}', msg\n    }\n\n    @Test\n    void testJwkWithMissingKty() {\n        assertEmpty parser.parse('{\"keys\":[{\"hello\":42}]}')\n    }\n\n    @Test\n    void testJwkWithMissingKtyNotIgnored() {\n        parser = builder().ignoreUnsupported(false).build()\n        String msg = \"JWK Set keys[0]: JWK is missing required ${AbstractJwk.KTY} parameter.\"\n        assertMalformed '{\"keys\":[{\"hello\":42}]}', msg\n    }\n\n    @Test\n    void testJwkWithNullKty() {\n        assertEmpty parser.parse('{\"keys\":[{\"kty\":null}]}')\n    }\n\n    @Test\n    void testJwkWithNullKtyNotIgnored() {\n        parser = builder().ignoreUnsupported(false).build()\n        String msg = \"JWK Set keys[0]: JWK ${AbstractJwk.KTY} value cannot be null.\"\n        assertMalformed '{\"keys\":[{\"kty\":null}]}', msg\n    }\n\n    @Test\n    void testJwkWithEmptyKty() {\n        assertEmpty parser.parse('{\"keys\":[{\"kty\":\"\"}]}')\n    }\n\n    @Test\n    void testJwkWithEmptyKtyNotIgnored() {\n        parser = builder().ignoreUnsupported(false).build()\n        String msg = \"JWK Set keys[0]: JWK ${AbstractJwk.KTY} value cannot be empty.\"\n        assertMalformed '{\"keys\":[{\"kty\":\"\"}]}', msg\n    }\n\n    @Test\n    void testKeysElementWithMissingKeyMaterial() {\n        assertEmpty parser.parse('{\"keys\":[{\"kty\":\"oct\"}]}')\n    }\n\n    @Test\n    void testKeysElementWithMissingKeyMaterialNotIgnored() {\n        parser = builder().ignoreUnsupported(false).build()\n        String msg = \"JWK Set keys[0]: Secret JWK is missing required ${DefaultSecretJwk.K} value.\"\n        assertMalformed '{\"keys\":[{\"kty\":\"oct\"}]}', msg\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkSetTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.RedactedSupplier\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultJwkSetTest {\n\n    @Test\n    void testName() {\n        assertEquals \"JWK Set\", new DefaultJwkSet(DefaultJwkSet.KEYS, [:]).getName()\n    }\n\n    private static void unsupported(Closure<?> c) {\n        try {\n            c()\n            fail()\n        } catch (UnsupportedOperationException expected) {\n            String msg = 'JWK Set instance is immutable and may not be modified.'\n            assertEquals msg, expected.message\n        }\n    }\n\n    @Test\n    void testImmutable() {\n        def set = new DefaultJwkSet(DefaultJwkSet.KEYS, [a: 'b'])\n        unsupported { set.put('foo', 'bar') }\n        unsupported { set.putAll([c: 'd', e: 'f']) }\n        unsupported { set.remove('a') }\n        unsupported { set.clear() }\n    }\n\n    @Test(expected = UnsupportedOperationException)\n    void testGetKeysImmutable() {\n        def jwk = Jwks.builder().key(TestKeys.HS256).build()\n        def set = new DefaultJwkSet(DefaultJwkSet.KEYS, [keys: [jwk]])\n        def result = set.getKeys()\n        result.remove(jwk) // shouldn't be able\n        fail()\n    }\n\n    @Test(expected = UnsupportedOperationException)\n    void testIteratorImmutable() {\n        def jwk = Jwks.builder().key(TestKeys.HS256).build()\n        def set = new DefaultJwkSet(DefaultJwkSet.KEYS, [keys: [jwk]])\n        def i = set.iterator()\n        assertEquals jwk, i.next()\n        i.remove() // shouldn't be able to do this\n        fail()\n    }\n\n    /**\n     * Asserts that the raw 'keys' value is not a RedactedSupplier per https://github.com/jwtk/jjwt/issues/976,\n     * but an internal secret key parameter does have a RedactedSupplier\n     */\n    @Test\n    void testGetKeysNotRedactedSupplier() {\n        def jwk = Jwks.builder().key(TestKeys.HS256).build()\n        def set = new DefaultJwkSet(DefaultJwkSet.KEYS, [keys: [jwk]])\n        def keys = set.get('keys')\n        assertFalse keys instanceof RedactedSupplier\n        keys = keys as List\n        def element = keys[0] as Map// result is an array/list, so get first JWK in the list\n        assertTrue element.k instanceof RedactedSupplier // 'k' is a secret property, should be redacted\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultJwkThumbprintTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.HashAlgorithm\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.MessageDigest\n\nimport static org.junit.Assert.*\n\nclass DefaultJwkThumbprintTest {\n\n    private static String content = \"Hello World\"\n    private static HashAlgorithm alg = Jwks.HASH.SHA256\n    private static byte[] digest = alg.digest(new DefaultRequest<InputStream>(Streams.of(content), null, null))\n    private static String expectedToString = Encoders.BASE64URL.encode(digest)\n    private static String expectedUriString = DefaultJwkThumbprint.URI_PREFIX + alg.getId() + \":\" + expectedToString\n    private static URI expectedUri = URI.create(expectedUriString)\n\n    private DefaultJwkThumbprint thumbprint\n\n    @Before\n    void setUp() {\n        this.thumbprint = new DefaultJwkThumbprint(digest, alg)\n    }\n\n    @Test\n    void testGetHashAlgorithm() {\n        assertSame alg, thumbprint.getHashAlgorithm()\n    }\n\n    @Test\n    void testToByteArray() {\n        assertTrue MessageDigest.isEqual(digest, thumbprint.toByteArray())\n    }\n\n    @Test\n    void testToURI() {\n        assertEquals expectedUri, thumbprint.toURI()\n    }\n\n    @Test\n    void testHashCode() {\n        assertEquals io.jsonwebtoken.lang.Objects.nullSafeHashCode(digest, alg), thumbprint.hashCode()\n    }\n\n    @Test\n    void testIdentityEquals() {\n        assertEquals thumbprint, thumbprint\n    }\n\n    @Test\n    void testPropertyEquals() {\n        assertEquals thumbprint, new DefaultJwkThumbprint(digest, alg)\n    }\n\n    @Test\n    void testNotEquals() {\n        // invalid data type:\n        assertNotEquals new DefaultJwkThumbprint(digest, alg), new Object()\n\n        // same digest, different alg:\n        assertFalse thumbprint == new DefaultJwkThumbprint(digest, DefaultHashAlgorithm.SHA1)\n\n        // same alg, different digest:\n        def payload = Streams.of(Strings.utf8('Hello World!'))\n        byte[] digest2 = alg.digest(new DefaultRequest<>(payload, null, null))\n        assertFalse thumbprint == new DefaultJwkThumbprint(digest2, DefaultHashAlgorithm.SHA1)\n    }\n\n    @Test\n    void testToString() {\n        assertEquals expectedToString, thumbprint.toString()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultKeyOperationBuilderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultKeyOperationBuilderTest {\n\n    private DefaultKeyOperationBuilder builder\n\n    @Before\n    void setUp() {\n        this.builder = new DefaultKeyOperationBuilder()\n    }\n\n    @Test\n    void testId() {\n        def id = 'foo'\n        def op = builder.id(id).build() as DefaultKeyOperation\n        assertEquals id, op.id\n        assertEquals DefaultKeyOperation.CUSTOM_DESCRIPTION, op.description\n        assertFalse op.isRelated(Jwks.OP.SIGN)\n    }\n\n    @Test\n    void testDescription() {\n        def id = 'foo'\n        def description = 'test'\n        def op = builder.id(id).description(description).build()\n        assertEquals id, op.id\n        assertEquals 'test', op.description\n    }\n\n    @Test\n    void testRelated() {\n        def id = 'foo'\n        def related = 'related'\n        def opA = builder.id(id).related(related).build()\n        def opB = builder.id(related).related(id).build()\n        assertEquals id, opA.id\n        assertEquals related, opB.id\n        assertTrue opA.isRelated(opB)\n        assertTrue opB.isRelated(opA)\n        assertFalse opA.isRelated(Jwks.OP.SIGN)\n        assertFalse opA.isRelated(Jwks.OP.SIGN)\n    }\n\n    @Test\n    void testRelatedNull() {\n        def op = builder.id('foo').related(null).build()\n        assertTrue op.related.isEmpty()\n    }\n\n    @Test\n    void testRelatedEmpty() {\n        def op = builder.id('foo').related('  ').build()\n        assertTrue op.related.isEmpty()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultKeyOperationPolicyBuilderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.KeyOperation\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultKeyOperationPolicyBuilderTest {\n\n    DefaultKeyOperationPolicyBuilder builder\n\n    @Before\n    void setUp() {\n        builder = new DefaultKeyOperationPolicyBuilder()\n    }\n\n    @Test\n    void testDefault() {\n        def policy = builder.build()\n        assertTrue policy.operations.containsAll(Jwks.OP.get().values())\n        // unrelated operations not allowed:\n        def op = Jwks.OP.builder().id('foo').build()\n        try {\n            policy.validate([op, Jwks.OP.SIGN])\n            fail(\"Unrelated operations are not allowed by default.\")\n        } catch (IllegalArgumentException expected) {\n            String msg = 'Unrelated key operations are not allowed. KeyOperation ' +\n                    '[\\'sign\\' (Compute digital signature or MAC)] is unrelated to [\\'foo\\' (Custom key operation)].'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testAdd() {\n        def op = Jwks.OP.builder().id('foo').build()\n        def policy = builder.add(op).build()\n        assertTrue policy.operations.contains(op)\n    }\n\n    @Test\n    void testAddNull() {\n        def orig = builder.build()\n        def policy = builder.add((KeyOperation) null).build()\n        assertEquals orig, policy\n    }\n\n    @Test\n    void testAddCollection() {\n        def foo = Jwks.OP.builder().id('foo').build()\n        def bar = Jwks.OP.builder().id('bar').build()\n        def policy = builder.add([foo, bar]).build()\n        assertTrue policy.operations.contains(foo)\n        assertTrue policy.operations.contains(bar)\n    }\n\n    @Test\n    void testAddNullCollection() {\n        def orig = builder.build()\n        def policy = builder.add((Collection<KeyOperation>) null).build()\n        assertEquals orig, policy\n    }\n\n    @Test\n    void testAllowUnrelatedTrue() { // testDefault has it false as expected\n        def foo = Jwks.OP.builder().id('foo').build()\n        def policy = builder.unrelated().build()\n        policy.validate([foo, Jwks.OP.SIGN]) // no exception thrown since unrelated == true\n    }\n\n    @Test\n    void testHashCode() {\n        def a = builder.add(Jwks.OP.builder().id('foo').build()).build()\n        def b = builder.build()\n        assertFalse a.is(b) // identity equals is different\n        def ahc = a.hashCode()\n        def bhc = b.hashCode()\n        assertEquals ahc, bhc // still same hashcode\n    }\n\n    @Test\n    void testEquals() {\n        def a = builder.add(Jwks.OP.builder().id('foo').build()).build()\n        def b = builder.build()\n        assertFalse a.is(b) // identity equals is different\n        assertEquals a, b // but still equals\n    }\n\n    @Test\n    void testEqualsIdentity() {\n        def policy = builder.build()\n        assertEquals policy, policy\n    }\n\n    @SuppressWarnings('ChangeToOperator')\n    @Test\n    void testEqualsUnexpectedType() {\n        assertFalse builder.build().equals(new Object())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultKeyOperationTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass DefaultKeyOperationTest {\n\n    @Test\n    void testCustom() {\n        def op = new DefaultKeyOperation('foo')\n        assertEquals DefaultKeyOperation.CUSTOM_DESCRIPTION, op.getDescription()\n        assertNotNull op.related\n        assertTrue op.related.isEmpty()\n    }\n\n    @Test\n    void testUnrelated() {\n        assertFalse new DefaultKeyOperation('foo').isRelated(Jwks.OP.SIGN)\n    }\n\n    @Test\n    void testRelatedNull() {\n        assertFalse Jwks.OP.SIGN.isRelated(null)\n    }\n\n    @Test\n    void testRelatedEquals() {\n        def op = Jwks.OP.SIGN as DefaultKeyOperation\n        assertTrue op.isRelated(op)\n    }\n\n    @Test\n    void testRelatedTrue() {\n        def op = Jwks.OP.SIGN as DefaultKeyOperation\n        assertTrue op.isRelated(Jwks.OP.VERIFY)\n    }\n\n    @Test\n    void testRelatedFalse() {\n        def op = Jwks.OP.SIGN as DefaultKeyOperation\n        assertFalse op.isRelated(Jwks.OP.ENCRYPT)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultKeyPairBuilderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\n\nimport static org.junit.Assert.assertNotNull\nimport static org.junit.Assert.assertTrue\n\nclass DefaultKeyPairBuilderTest {\n\n    @Test\n    // simple test - just a JCA name, no extra config\n    void testSimpleBuild() {\n        def pair = new DefaultKeyPairBuilder(\"EC\").build()\n        assertNotNull pair\n        assertNotNull pair.getPrivate()\n        assertNotNull pair.getPublic()\n        assertTrue pair.getPrivate() instanceof ECPrivateKey\n        assertTrue pair.getPublic() instanceof ECPublicKey\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultKeyUseStrategyTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNull\n\nclass DefaultKeyUseStrategyTest {\n\n    final KeyUseStrategy strat = DefaultKeyUseStrategy.INSTANCE\n\n    private static KeyUsage usage(int trueIndex) {\n        boolean[] usage = new boolean[9]\n        usage[trueIndex] = true\n        return new KeyUsage(new TestX509Certificate(keyUsage: usage))\n    }\n\n    @Test\n    void testKeyEncipherment() {\n        assertEquals 'enc', strat.toJwkValue(usage(2))\n    }\n\n    @Test\n    void testDataEncipherment() {\n        assertEquals 'enc', strat.toJwkValue(usage(3))\n    }\n\n    @Test\n    void testKeyAgreement() {\n        assertEquals 'enc', strat.toJwkValue(usage(4))\n    }\n\n    @Test\n    void testDigitalSignature() {\n        assertEquals 'sig', strat.toJwkValue(usage(0))\n    }\n\n    @Test\n    void testNonRepudiation() {\n        assertEquals 'sig', strat.toJwkValue(usage(1))\n    }\n\n    @Test\n    void testKeyCertSign() {\n        assertEquals 'sig', strat.toJwkValue(usage(5))\n    }\n\n    @Test\n    void testCRLSign() {\n        assertEquals 'sig', strat.toJwkValue(usage(6))\n    }\n\n    @Test\n    void testEncipherOnly() {\n        assertNull strat.toJwkValue(usage(7))\n    }\n\n    @Test\n    void testDecipherOnly() {\n        assertNull strat.toJwkValue(usage(8))\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultMacAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.StandardCharsets\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\nclass DefaultMacAlgorithmTest {\n\n    static final byte[] payload = \"Hello World\".getBytes(StandardCharsets.UTF_8)\n    static final char[] passwordChars = \"correct horse battery staple\".toCharArray()\n\n    static <T extends Key> SecureRequest<InputStream, T> request(T key) {\n        return new DefaultSecureRequest<InputStream, T>(Streams.of(payload), null, null, key)\n    }\n\n    static DefaultMacAlgorithm newAlg() {\n        return new DefaultMacAlgorithm('HS256', 'HmacSHA256', 256)\n    }\n\n    /**\n     * Asserts a default Password instance can't be used (poor length/entropy)\n     */\n    @Test\n    void testWithPasswordSpec() {\n        def password = Keys.password(passwordChars)\n        try {\n            newAlg().digest(request(password))\n        } catch (InvalidKeyException expected) {\n            String msg = 'Passwords are intended for use with key derivation algorithms only.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    /**\n     * Asserts a Password instance that fakes a valid HmacSHA* JDK algorithm name can't be used\n     */\n    @Test\n    void testCustomPasswordWithValidAlgorithm() {\n        def password = new PasswordSpec(\"correct horse battery staple\".toCharArray()) {\n            @Override\n            String getAlgorithm() {\n                return 'HmacSHA256'\n            }\n        }\n        try {\n            newAlg().digest(request(password))\n        } catch (InvalidKeyException expected) {\n            String msg = 'Passwords are intended for use with key derivation algorithms only.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    /**\n     * Asserts a Password instance that fakes a valid HmacSHA* JDK algorithm name, and even has encoded bytes can't be used\n     */\n    @Test\n    void testWithCustomPasswordGetEncodedThrowsException() {\n        Password password = new PasswordSpec(\"correct horse\".toCharArray()) {\n            @Override\n            String getAlgorithm() {\n                return 'HmacSHA256'\n            }\n\n            @Override\n            byte[] getEncoded() {\n                throw new UnsupportedOperationException(\"Invalid\")\n            }\n        }\n\n        try {\n            newAlg().digest(request(password))\n        } catch (InvalidKeyException expected) {\n            String msg = 'Passwords are intended for use with key derivation algorithms only.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test(expected = SecurityException)\n    void testKeyGeneratorNoSuchAlgorithm() {\n        DefaultMacAlgorithm alg = new DefaultMacAlgorithm('HS256', 'foo', 256)\n        alg.key().build()\n    }\n\n    @Test\n    void testKeyGeneratorKeyLength() {\n        DefaultMacAlgorithm alg = new DefaultMacAlgorithm('HS256', 'HmacSHA256', 256)\n        assertEquals 256, alg.key().build().getEncoded().length * Byte.SIZE\n\n        alg = new DefaultMacAlgorithm('A128CBC-HS256', 'HmacSHA256', 128)\n        assertEquals 128, alg.key().build().getEncoded().length * Byte.SIZE\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testValidateNullKey() {\n        newAlg().validateKey(null, true)\n    }\n\n    @Test(expected = InvalidKeyException)\n    void testValidateKeyNoAlgorithm() {\n        newAlg().validateKey(new SecretKeySpec(new byte[1], ' '), true)\n    }\n\n    @Test(expected = InvalidKeyException)\n    void testValidateKeyInvalidJcaAlgorithm() {\n        newAlg().validateKey(new SecretKeySpec(new byte[1], 'foo'), true)\n    }\n\n    @Test\n    void testValidateKeyEncodedNotAvailable() {\n        def key = new SecretKeySpec(new byte[1], 'HmacSHA256') {\n            @Override\n            byte[] getEncoded() {\n                return null\n            }\n        }\n        // doesn't throw exception because it's likely an HSM key\n        newAlg().validateKey(key, true)\n    }\n\n    @Test\n    void testValidateKeyStandardAlgorithmWeakKey() {\n        byte[] bytes = new byte[24]\n        Randoms.secureRandom().nextBytes(bytes)\n        try {\n            newAlg().validateKey(new SecretKeySpec(bytes, 'HmacSHA256'), true)\n        } catch (WeakKeyException expected) {\n            String msg = 'The signing key\\'s size is 192 bits which is not secure enough for the HS256 algorithm. ' +\n                    'The JWT JWA Specification (RFC 7518, Section 3.2) states that keys used with HS256 MUST have a ' +\n                    'size >= 256 bits (the key size must be greater than or equal to the hash output size). ' +\n                    'Consider using the Jwts.SIG.HS256.key() builder to create a key guaranteed ' +\n                    'to be secure enough for HS256.  See https://tools.ietf.org/html/rfc7518#section-3.2 for more ' +\n                    'information.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testValidateKeyCustomAlgorithmWeakKey() {\n        byte[] bytes = new byte[24]\n        Randoms.secureRandom().nextBytes(bytes)\n        DefaultMacAlgorithm alg = new DefaultMacAlgorithm('foo', 'foo', 256)\n        try {\n            alg.validateKey(new SecretKeySpec(bytes, 'HmacSHA256'), true)\n        } catch (WeakKeyException expected) {\n            assertEquals 'The signing key\\'s size is 192 bits which is not secure enough for the foo algorithm. The foo algorithm requires keys to have a size >= 256 bits.', expected.getMessage()\n        }\n    }\n\n    @Test\n    void testFindByKeyWithNoAlgorithm() {\n        assertNull DefaultMacAlgorithm.findByKey(new TestSecretKey())\n    }\n\n    @Test\n    void testFindByKeyInvalidAlgorithm() {\n        assertNull DefaultMacAlgorithm.findByKey(new TestSecretKey(algorithm: 'foo'))\n    }\n\n    @Test\n    void testFindByKey() {\n        for (def mac : DefaultMacAlgorithm.JCA_NAME_MAP.values()) {\n            def key = mac.key().build()\n            assertSame mac, DefaultMacAlgorithm.findByKey(key)\n        }\n    }\n\n    @Test\n    void testFindByKeyNull() {\n        assertNull DefaultMacAlgorithm.findByKey(null)\n    }\n\n    @Test\n    void testFindByNonSecretKey() {\n        assertNull DefaultMacAlgorithm.findByKey(TestKeys.RS256.pair.public)\n    }\n\n    @Test\n    void testFindByWeakKey() {\n        for (def mac : DefaultMacAlgorithm.JCA_NAME_MAP.values()) {\n            def key = mac.key().build()\n            def encoded = new byte[key.getEncoded().length - 1] // one byte less than required\n            def weak = new TestSecretKey(algorithm: key.getAlgorithm(), format: key.getFormat(), encoded: encoded)\n            assertSame mac, DefaultMacAlgorithm.findByKey(key)\n            assertNull DefaultMacAlgorithm.findByKey(weak)\n        }\n    }\n\n    @Test\n    void testFindByLargerThanExpectedKey() {\n        for (def mac : DefaultMacAlgorithm.JCA_NAME_MAP.values()) {\n            def key = mac.key().build()\n            def encoded = new byte[key.getEncoded().length + 1] // one byte less than required\n            def strong = new TestSecretKey(algorithm: key.getAlgorithm(), format: key.getFormat(), encoded: encoded)\n            assertSame mac, DefaultMacAlgorithm.findByKey(strong)\n        }\n    }\n\n    @Test\n    void testFindByKeyOid() {\n        for (def mac : DefaultMacAlgorithm.JCA_NAME_MAP.values()) {\n            def key = mac.key().build()\n            def alg = key.getAlgorithm()\n            if (alg.endsWith('256')) {\n                alg = DefaultMacAlgorithm.HS256_OID\n            } else if (alg.endsWith('384')) {\n                alg = DefaultMacAlgorithm.HS384_OID\n            } else {\n                alg = DefaultMacAlgorithm.HS512_OID\n            }\n            def oidKey = new TestSecretKey(algorithm: alg, format: 'RAW', encoded: key.getEncoded())\n            assertSame mac, DefaultMacAlgorithm.findByKey(oidKey)\n        }\n    }\n\n    /**\n     * Asserts that generic secrets are accepted\n     */\n    @Test\n    void testValidateKeyAcceptsGenericSecret() {\n        def genericSecret = new SecretKey() {\n            @Override\n            String getAlgorithm() {\n                return 'GenericSecret'\n            }\n\n            @Override\n            String getFormat() {\n                return \"RAW\"\n            }\n\n            @Override\n            byte[] getEncoded() {\n                return Randoms.secureRandom().nextBytes(new byte[32])\n            }\n        }\n\n        newAlg().validateKey(genericSecret, true)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultMessageTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nclass DefaultMessageTest {\n\n    @Test(expected = IllegalArgumentException)\n    void testNullData() {\n        new DefaultMessage(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testEmptyByteArrayData() {\n        new DefaultMessage(new byte[0])\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultRsaKeyAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.WeakKeyException\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.assertEquals\n\nclass DefaultRsaKeyAlgorithmTest {\n\n    static final algs = [Jwts.KEY.RSA1_5, Jwts.KEY.RSA_OAEP, Jwts.KEY.RSA_OAEP_256] as List<DefaultRsaKeyAlgorithm>\n\n    @Test\n    void testValidateNonRSAKey() {\n        SecretKey key = Jwts.KEY.A128KW.key().build()\n        for (DefaultRsaKeyAlgorithm alg : algs) {\n            try {\n                alg.validate(key, true)\n            } catch (InvalidKeyException e) {\n                assertEquals 'Invalid RSA key algorithm name.', e.getMessage()\n            }\n            try {\n                alg.validate(key, false)\n            } catch (InvalidKeyException e) {\n                assertEquals 'Invalid RSA key algorithm name.', e.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testValidateRsaKeyWithoutKeySize() {\n        for (def alg : algs) {\n            // if RSAKey interface isn't exposed (e.g. PKCS11 or HSM), don't error:\n            alg.validate(new TestPublicKey(algorithm: 'RSA'), true)\n            alg.validate(new TestPrivateKey(algorithm: 'RSA'), false)\n        }\n    }\n\n    @Test\n    void testPssKey() {\n        for (DefaultRsaKeyAlgorithm alg : algs) {\n            RSAPublicKey key = createMock(RSAPublicKey)\n            expect(key.getAlgorithm()).andStubReturn(RsaSignatureAlgorithm.PSS_JCA_NAME)\n            replay(key)\n            try {\n                alg.validate(key, true)\n            } catch (InvalidKeyException expected) {\n                String msg = 'RSASSA-PSS keys may not be used for encryption, only digital signature algorithms.'\n                assertEquals msg, expected.getMessage()\n            }\n            verify(key)\n        }\n    }\n\n    @Test\n    void testPssOidKey() {\n        for (DefaultRsaKeyAlgorithm alg : algs) {\n            RSAPublicKey key = createMock(RSAPublicKey)\n            expect(key.getAlgorithm()).andStubReturn(RsaSignatureAlgorithm.PSS_OID)\n            replay(key)\n            try {\n                alg.validate(key, true)\n            } catch (InvalidKeyException expected) {\n                String msg = 'RSASSA-PSS keys may not be used for encryption, only digital signature algorithms.'\n                assertEquals msg, expected.getMessage()\n            }\n            verify(key)\n        }\n    }\n\n    @Test\n    void testWeakEncryptionKey() {\n        for (DefaultRsaKeyAlgorithm alg : algs) {\n            RSAPublicKey key = createMock(RSAPublicKey)\n            expect(key.getAlgorithm()).andStubReturn(\"RSA\")\n            expect(key.getModulus()).andReturn(BigInteger.ONE)\n            replay(key)\n            try {\n                alg.validate(key, true)\n            } catch (WeakKeyException e) {\n                String id = alg.getId()\n                String section = id.equals(\"RSA1_5\") ? \"4.2\" : \"4.3\"\n                String msg = \"The RSA encryption key size (aka modulus bit length) is 1 bits which is not secure \" +\n                        \"enough for the $id algorithm. The JWT JWA Specification (RFC 7518, Section $section) \" +\n                        \"states that RSA keys MUST have a size >= 2048 bits. \" +\n                        \"See https://www.rfc-editor.org/rfc/rfc7518.html#section-$section for more information.\"\n                assertEquals(msg, e.getMessage())\n            }\n            verify(key)\n        }\n    }\n\n    @Test\n    void testWeakDecryptionKey() {\n        for (DefaultRsaKeyAlgorithm alg : algs) {\n            RSAPrivateKey key = createMock(RSAPrivateKey)\n            expect(key.getAlgorithm()).andStubReturn(\"RSA\")\n            expect(key.getModulus()).andReturn(BigInteger.ONE)\n            replay(key)\n            try {\n                alg.validate(key, false)\n            } catch (WeakKeyException e) {\n                String id = alg.getId()\n                String section = id.equals(\"RSA1_5\") ? \"4.2\" : \"4.3\"\n                String msg = \"The RSA decryption key size (aka modulus bit length) is 1 bits which is not secure \" +\n                        \"enough for the $id algorithm. The JWT JWA Specification (RFC 7518, Section $section) \" +\n                        \"states that RSA keys MUST have a size >= 2048 bits. \" +\n                        \"See https://www.rfc-editor.org/rfc/rfc7518.html#section-$section for more information.\"\n                assertEquals(msg, e.getMessage())\n            }\n            verify(key)\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultRsaPrivateJwkTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\n\nimport io.jsonwebtoken.impl.lang.ParameterReadable\nimport io.jsonwebtoken.impl.lang.TestParameterReadable\nimport org.junit.Test\n\nimport java.security.spec.RSAOtherPrimeInfo\n\nimport static org.junit.Assert.assertFalse\nimport static org.junit.Assert.assertTrue\n\nclass DefaultRsaPrivateJwkTest {\n\n    @Test\n    void testEqualsOtherPrimesDifferentSizes() {\n        def info1 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        def info2 = new RSAOtherPrimeInfo(BigInteger.TEN, BigInteger.TEN, BigInteger.TEN)\n        ParameterReadable a = new TestParameterReadable(value: [info1, info2])\n        ParameterReadable b = new TestParameterReadable(value: [info1]) // different sizes\n        assertFalse DefaultRsaPrivateJwk.equalsOtherPrimes(a, b)\n    }\n\n    @Test\n    void testEqualsOtherPrimes() {\n        def info1 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        def info2 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        ParameterReadable a = new TestParameterReadable(value: [info1])\n        ParameterReadable b = new TestParameterReadable(value: [info2])\n        assertTrue DefaultRsaPrivateJwk.equalsOtherPrimes(a, b)\n    }\n\n    @Test\n    void testEqualsOtherPrimesIdentity() {\n        def info1 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        ParameterReadable a = new TestParameterReadable(value: [info1])\n        ParameterReadable b = new TestParameterReadable(value: [info1])\n        assertTrue DefaultRsaPrivateJwk.equalsOtherPrimes(a, b)\n    }\n\n    @Test\n    void testEqualsOtherPrimesNullElement() {\n        def info1 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        // sizes are the same, but one element is null:\n        ParameterReadable a = new TestParameterReadable(value: [null])\n        ParameterReadable b = new TestParameterReadable(value: [info1])\n        assertFalse DefaultRsaPrivateJwk.equalsOtherPrimes(a, b)\n    }\n\n    @Test\n    void testEqualsOtherPrimesInfoNotEqual() {\n        def info1 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        def info2 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.TEN) // different\n        ParameterReadable a = new TestParameterReadable(value: [info1])\n        ParameterReadable b = new TestParameterReadable(value: [info2])\n        assertFalse DefaultRsaPrivateJwk.equalsOtherPrimes(a, b)\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DefaultSecretKeyBuilderTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass DefaultSecretKeyBuilderTest {\n\n    @Test\n    void testInvalidBitLength() {\n        try {\n            //noinspection GroovyResultOfObjectAllocationIgnored\n            new DefaultSecretKeyBuilder(\"AES\", 127)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"bitLength must be an even multiple of 8\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DirectKeyAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.DefaultJweHeader\nimport io.jsonwebtoken.lang.Arrays\nimport io.jsonwebtoken.security.DecryptionKeyRequest\nimport org.junit.Test\n\nimport javax.crypto.spec.SecretKeySpec\nimport java.security.Key\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass DirectKeyAlgorithmTest {\n\n    @Test\n    void testId() {\n        assertEquals \"dir\", new DirectKeyAlgorithm().getId()\n    }\n\n    @Test\n    void testGetEncryptionKey() {\n        def alg = new DirectKeyAlgorithm()\n        def key = new SecretKeySpec(new byte[1], \"AES\")\n        def request = new DefaultKeyRequest(key, null, null, new DefaultJweHeader([:]), Jwts.ENC.A128GCM)\n        def result = alg.getEncryptionKey(request)\n        assertSame key, result.getKey()\n        assertEquals 0, Arrays.length(result.getPayload()) //must not have an encrypted key\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testGetEncryptionKeyWithNullRequest() {\n        new DirectKeyAlgorithm().getEncryptionKey(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testGetEncryptionKeyWithNullRequestKey() {\n        def key = new SecretKeySpec(new byte[1], \"AES\")\n        def request = new DefaultKeyRequest(key, null, null, new DefaultJweHeader([:]), Jwts.ENC.A128GCM) {\n            @Override\n            Key getPayload() {\n                return null\n            }\n        }\n        new DirectKeyAlgorithm().getEncryptionKey(request)\n    }\n\n    @Test\n    void testGetDecryptionKey() {\n        def alg = new DirectKeyAlgorithm()\n        DecryptionKeyRequest req = createMock(DecryptionKeyRequest)\n        def key = Jwts.ENC.A128GCM.key().build()\n        expect(req.getKey()).andReturn(key)\n        replay(req)\n        def result = alg.getDecryptionKey(req)\n        verify(req)\n        assertSame key, result\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testGetDecryptionKeyWithNullRequest() {\n        new DirectKeyAlgorithm().getDecryptionKey(null)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testGetDecryptionKeyWithNullRequestKey() {\n        DecryptionKeyRequest req = createMock(DecryptionKeyRequest)\n        expect(req.getKey()).andReturn(null)\n        replay(req)\n        new DirectKeyAlgorithm().getDecryptionKey(req)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/DispatchingJwkFactoryTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.EcPrivateJwk\nimport io.jsonwebtoken.security.EcPublicJwk\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.UnsupportedKeyException\nimport org.junit.Test\n\nimport java.security.Key\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\n\nimport static org.junit.Assert.*\n\nclass DispatchingJwkFactoryTest {\n\n    @Test(expected = IllegalArgumentException)\n    void testNullJwk() {\n        new DispatchingJwkFactory().createJwk(null)\n    }\n\n    @Test(expected = InvalidKeyException)\n    void testEmptyJwk() {\n        new DispatchingJwkFactory().createJwk(new DefaultJwkContext<Key>())\n    }\n\n    @Test(expected = UnsupportedKeyException)\n    void testUnknownKtyValue() {\n        def ctx = new DefaultJwkContext()\n        ctx.put('kty', 'foo')\n        new DispatchingJwkFactory().createJwk(ctx)\n    }\n\n    @Test\n    void testNewContextNoFamily() {\n        def ctx = new DefaultJwkContext()\n        def key = new TestKey(algorithm: 'foo')\n        try {\n            DispatchingJwkFactory.DEFAULT_INSTANCE.newContext(ctx, key)\n            fail()\n        } catch (UnsupportedKeyException uke) {\n            String msg = 'Unable to create JWK for unrecognized key of type io.jsonwebtoken.impl.security.TestKey: ' +\n                    'there is no known JWK Factory capable of creating JWKs for this key type.'\n            assertEquals msg, uke.getMessage()\n        }\n    }\n\n    @Test\n    void testUnknownKeyType() {\n        def key = new Key() {\n            @Override\n            String getAlgorithm() {\n                return null\n            }\n\n            @Override\n            String getFormat() {\n                return null\n            }\n\n            @Override\n            byte[] getEncoded() {\n                return new byte[0]\n            }\n        }\n        def ctx = new DefaultJwkContext().setKey(key)\n        try {\n            new DispatchingJwkFactory().createJwk(ctx)\n            fail()\n        } catch (UnsupportedKeyException uke) {\n            String msg = 'Unable to create JWK for unrecognized key of type io.jsonwebtoken.impl.security.DispatchingJwkFactoryTest$1: there is no known JWK Factory capable of creating JWKs for this key type.'\n            assertEquals msg, uke.getMessage()\n        }\n    }\n\n    @Test\n    void testEcKeyPairToKey() {\n\n        Map<String, String> m = [\n                'kty': 'EC',\n                'crv': 'P-256',\n                \"x\"  : \"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0\",\n                \"y\"  : \"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps\",\n                \"d\"  : \"0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo\"\n        ]\n\n        def ctx = new DefaultJwkContext()\n        ctx.putAll(m)\n\n        DispatchingJwkFactory factory = new DispatchingJwkFactory()\n        ctx = factory.newContext(ctx, null)\n\n        def jwk = factory.createJwk(ctx) as EcPrivateJwk\n        assertTrue jwk instanceof EcPrivateJwk\n        def key = jwk.toKey()\n        assertTrue key instanceof ECPrivateKey\n        String x = AbstractEcJwkFactory.toOctetString(key.params.curve, jwk.toPublicJwk().toKey().w.affineX)\n        String y = AbstractEcJwkFactory.toOctetString(key.params.curve, jwk.toPublicJwk().toKey().w.affineY)\n        String d = AbstractEcJwkFactory.toOctetString(key.params.curve, key.s)\n        assertEquals jwk.d.get(), d\n\n        //remove the 'd' mapping to represent only a public key:\n        m.remove(DefaultEcPrivateJwk.D.getId())\n        ctx = new DefaultJwkContext()\n        ctx.putAll(m)\n        ctx = factory.newContext(ctx, null)\n\n        jwk = factory.createJwk(ctx) as EcPublicJwk\n        assertTrue jwk instanceof EcPublicJwk\n        key = jwk.toKey() as ECPublicKey\n        assertTrue key instanceof ECPublicKey\n        assertEquals jwk.x, x\n        assertEquals jwk.y, y\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ECCurveTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\n\nimport org.junit.Test\n\nimport java.security.PublicKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.spec.ECParameterSpec\nimport java.security.spec.ECPoint\nimport java.security.spec.EllipticCurve\n\nimport static org.junit.Assert.*\n\nclass ECCurveTest {\n\n    static void assertContains(ECCurve curve, PublicKey pub) {\n        assertTrue(curve.contains(pub))\n    }\n\n    @Test\n    void testContainsKeyTrue() {\n        assertContains(ECCurve.P256, TestKeys.ES256.pair.public)\n        assertContains(ECCurve.P384, TestKeys.ES384.pair.public)\n        assertContains(ECCurve.P521, TestKeys.ES512.pair.public)\n    }\n\n    @Test\n    void testContainsKeyNull() {\n        ECCurve.VALUES.each {\n            assertFalse(it.contains(null))\n        }\n    }\n\n    @Test\n    void testContainsNonECPublicKey() {\n        ECCurve.VALUES.each {\n            assertFalse it.contains(TestKeys.HS256)\n        }\n    }\n\n    @Test\n    void testContainsKeyNullParams() {\n        ECCurve.VALUES.each {\n            assertFalse it.contains(new TestECPublicKey())\n        }\n    }\n\n    @Test\n    void testContainsKeyNullJcaCurve() {\n        def src = TestKeys.ES256.pair.public.getParams() as ECParameterSpec\n        def spec = new ECParameterSpec(src.curve, src.generator, src.order, src.cofactor) {\n            @Override\n            EllipticCurve getCurve() {\n                return null\n            }\n        }\n        def key = new TestECKey(params: spec)\n        ECCurve.VALUES.each {\n            assertFalse it.contains(key)\n        }\n    }\n\n    @Test\n    void testContainsKeyBadWPoint() {\n        ECCurve.VALUES.each {\n            def src = it.keyPair().build().public\n            def spec = src.getParams() as ECParameterSpec\n            def key = new TestECPublicKey(params: spec, w: new ECPoint(BigInteger.ONE, BigInteger.ONE))\n            assertFalse it.contains(key)\n        }\n    }\n\n    @Test\n    void testContainsTrue() {\n        ECCurve curve = ECCurve.P256\n        def pair = curve.keyPair().build()\n        ECPublicKey ecPub = (ECPublicKey) pair.getPublic()\n        assertTrue(curve.contains(ecPub.getW()))\n    }\n\n    @Test\n    void testContainsFalse() {\n        assertFalse(ECCurve.P256.contains(new ECPoint(BigInteger.ONE, BigInteger.ONE)))\n    }\n\n    @Test\n    void testFindByJcaEllipticCurve() {\n        ECCurve.VALUES.each {\n            it.equals(ECCurve.findByJcaCurve(it.toParameterSpec().getCurve()))\n        }\n    }\n\n    @Test\n    void testMultiplyInfinity() {\n        ECCurve.VALUES.each {\n            def result = it.multiply(ECPoint.POINT_INFINITY, BigInteger.valueOf(1))\n            assertEquals ECPoint.POINT_INFINITY, result\n\n        }\n    }\n\n    @Test\n    void testDoubleInfinity() {\n        ECCurve.VALUES.each {\n            def result = it.doublePoint(ECPoint.POINT_INFINITY)\n            assertEquals ECPoint.POINT_INFINITY, result\n        }\n    }\n\n    @Test\n    void testAddInfinity() {\n        ECCurve.VALUES.each {\n            def curve = it.spec.getCurve()\n            ECPoint point = new ECPoint(BigInteger.valueOf(1), BigInteger.valueOf(2)) // any point is fine for this test\n            def result = it.add(ECPoint.POINT_INFINITY, point)\n            //adding infinity to a point should return the point:\n            assertEquals point, result\n            //adding a point to infinity should return the point:\n            result = it.add(point, ECPoint.POINT_INFINITY)\n            assertEquals point, result\n        }\n    }\n\n    @Test\n    void testAddSamePointDoublesIt() {\n        ECCurve.VALUES.each {\n            def pair = it.keyPair().build()\n            def pub = pair.getPublic() as ECPublicKey\n            def point = pub.getW()\n            def doubled = it.doublePoint(point)\n            def added = it.add(point, point)\n            assertEquals doubled, added\n        }\n    }\n\n    @Test\n    void testFindByKeyNull() {\n        assertNull ECCurve.findByKey(null)\n    }\n\n    @Test\n    void testFindByKeyNotECKey() {\n        assertNull ECCurve.findByKey(TestKeys.HS256)\n    }\n\n    @Test\n    void testFindByKeyNullParams() {\n        assertNull ECCurve.findByKey(new TestECKey())\n    }\n\n    @Test\n    void testFindByKeyNullJcaCurve() {\n        def src = TestKeys.ES256.pair.public.getParams() as ECParameterSpec\n        def spec = new ECParameterSpec(src.curve, src.generator, src.order, src.cofactor) {\n            @Override\n            EllipticCurve getCurve() {\n                return null\n            }\n        }\n        assertNull ECCurve.findByKey(new TestECKey(params: spec))\n    }\n\n    @Test\n    void testFindByKeyWithNullWPoint() {\n        def spec = TestKeys.ES256.pair.public.getParams() as ECParameterSpec\n        assertNull ECCurve.findByKey(new TestECPublicKey(params: spec))\n    }\n\n    @Test\n    void testFindByKeyWithWPointNotOnCurve() {\n        def spec = TestKeys.ES256.pair.public.getParams() as ECParameterSpec\n        def key = new TestECPublicKey(params: spec, w: new ECPoint(BigInteger.ONE, BigInteger.ONE))\n        assertNull ECCurve.findByKey(key)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/EcPrivateJwkFactoryTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.MalformedKeyException\nimport org.junit.Test\n\nimport java.security.KeyFactory\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.spec.ECPublicKeySpec\nimport java.security.spec.InvalidKeySpecException\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass EcPrivateJwkFactoryTest {\n\n    @Test\n    void testDMissing() {\n        def values = ['kty': 'EC', 'crv': 'P-256', 'x': BigInteger.ONE, 'y': BigInteger.ONE]\n        try {\n            def ctx = new DefaultJwkContext(DefaultEcPrivateJwk.PARAMS)\n            ctx.putAll(values)\n            new EcPrivateJwkFactory().createJwkFromValues(ctx)\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"EC JWK is missing required 'd' (ECC Private Key) value.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testDerivePublicFails() {\n\n        def pair = Jwts.SIG.ES256.keyPair().build()\n        def priv = pair.getPrivate() as ECPrivateKey\n\n        final def context = new DefaultJwkContext(DefaultEcPrivateJwk.PARAMS)\n        context.setKey(priv)\n\n        def ex = new InvalidKeySpecException(\"invalid\")\n\n        def factory = new EcPrivateJwkFactory() {\n            @Override\n            protected ECPublicKey derivePublic(KeyFactory keyFactory, ECPublicKeySpec spec) throws InvalidKeySpecException {\n                throw ex\n            }\n        }\n\n        try {\n            factory.derivePublic(context)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = 'Unable to derive ECPublicKey from ECPrivateKey: invalid'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/EcPublicJwkFactoryTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.MalformedKeyException\nimport org.junit.Test\n\nimport java.security.spec.EllipticCurve\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass EcPublicJwkFactoryTest {\n\n    @Test\n    void testCurveMissing() {\n        try {\n            Jwks.builder().add(['kty': 'EC']).build()\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"EC JWK is missing required 'crv' (Curve) value.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testXMissing() {\n        try {\n            Jwks.builder().add(['kty': 'EC', 'crv': 'P-256']).build()\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"EC JWK is missing required 'x' (X Coordinate) value.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testYMissing() {\n        try {\n            String encoded = DefaultEcPublicJwk.X.applyTo(BigInteger.ONE)\n            Jwks.builder().add(['kty': 'EC', 'crv': 'P-256', 'x': encoded]).build()\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"EC JWK is missing required 'y' (Y Coordinate) value.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testPointNotOnCurve() {\n        try {\n            String encoded = DefaultEcPublicJwk.X.applyTo(BigInteger.ONE)\n            Jwks.builder().add(['kty': 'EC', 'crv': 'P-256', 'x': encoded, 'y': encoded]).build()\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = \"EC JWK x,y coordinates do not exist on elliptic curve 'P-256'. \" +\n                    \"This could be due simply to an incorrectly-created JWK or possibly an attempted \" +\n                    \"Invalid Curve Attack (see https://safecurves.cr.yp.to/twist.html for more information).\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testUnsupportedCurve() {\n        def curve = new EllipticCurve(new TestECField(fieldSize: 1), BigInteger.ONE, BigInteger.TEN)\n        try {\n            EcPublicJwkFactory.getJwaIdByCurve(curve)\n            fail()\n        } catch (InvalidKeyException e) {\n            assertEquals EcPublicJwkFactory.UNSUPPORTED_CURVE_MSG, e.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/EcSignatureAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//file:noinspection SpellCheckingInspection\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.JwtException\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.SignatureException\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\nimport java.security.KeyFactory\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.Signature\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.spec.ECParameterSpec\nimport java.security.spec.ECPoint\nimport java.security.spec.EllipticCurve\nimport java.security.spec.X509EncodedKeySpec\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass EcSignatureAlgorithmTest {\n\n    static Collection<EcSignatureAlgorithm> algs() {\n        return Jwts.SIG.get().values().findAll({ it instanceof EcSignatureAlgorithm }) as Collection<EcSignatureAlgorithm>\n    }\n\n    @Test\n    void testConstructorWithWeakKeyLength() {\n        try {\n            new EcSignatureAlgorithm(128, 'foo')\n        } catch (IllegalArgumentException iae) {\n            String msg = 'orderBitLength must equal 256, 384, or 521.'\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testFindByNoAlgKey() {\n        assertNull EcSignatureAlgorithm.findByKey(new TestKey())\n    }\n\n    @Test\n    void testFindOidKeys() {\n        for (def alg : EcSignatureAlgorithm.BY_OID.values()) {\n            String name = \"${alg.getId()}_OID\"\n            String oid = EcSignatureAlgorithm.metaClass.getAttribute(EcSignatureAlgorithm, name) as String\n            assertEquals oid, alg.OID\n            def key = new TestKey(algorithm: oid)\n            assertSame alg, EcSignatureAlgorithm.findByKey(key)\n        }\n    }\n\n    @Test\n    void testFindByWeakKey() {\n        ECPublicKey key = createMock(ECPublicKey)\n        ECParameterSpec spec = createMock(ECParameterSpec)\n        expect(key.getAlgorithm()).andStubReturn(\"EC\")\n        expect(key.getParams()).andStubReturn(spec)\n        expect(spec.getOrder()).andStubReturn(BigInteger.ONE)\n        replay key, spec\n        assertNull EcSignatureAlgorithm.findByKey(key)\n        verify key, spec\n    }\n\n    @Test\n    void testValidateKeyWithoutECOrECDSAAlgorithmName() {\n        PublicKey key = new TestPublicKey(algorithm: 'foo')\n        algs().each {\n            try {\n                it.validateKey(key, false)\n            } catch (InvalidKeyException e) {\n                String msg = 'Unrecognized EC key algorithm name.'\n                assertEquals msg, e.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testValidateECAlgorithmKeyThatDoesntUseECKeyInterface() {\n        PublicKey key = new TestPublicKey(algorithm: 'EC')\n        algs().each {\n            it.validateKey(key, false) //no exception - can't check for ECKey params (e.g. PKCS11 or HSM key)\n        }\n    }\n\n    @Test\n    void testIsValidRAndSWithoutEcKey() {\n        PublicKey key = createMock(PublicKey)\n        replay key\n        algs().each {\n            it.isValidRAndS(key, Bytes.EMPTY)\n            //no exception - can't check for ECKey params (e.g. PKCS11 or HSM key)\n        }\n        verify key\n    }\n\n    @Test\n    void testSignWithPublicKey() {\n        ECPublicKey key = TestKeys.ES256.pair.public as ECPublicKey\n        def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, key)\n        def alg = Jwts.SIG.ES256\n        try {\n            alg.digest(request)\n        } catch (InvalidKeyException e) {\n            String msg = \"${alg.getId()} signing keys must be PrivateKeys (implement ${PrivateKey.class.getName()}). \" +\n                    \"Provided key type: ${key.getClass().getName()}.\"\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    @Test\n    void testSignWithWeakKey() {\n        algs().each {\n            BigInteger order = BigInteger.ONE\n            ECParameterSpec spec = new ECParameterSpec(new EllipticCurve(new TestECField(), BigInteger.ONE, BigInteger.ONE), new ECPoint(BigInteger.ONE, BigInteger.ONE), order, 1)\n            ECPrivateKey priv = new TestECPrivateKey(algorithm: 'EC', params: spec)\n            def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, priv)\n            try {\n                it.digest(request)\n            } catch (InvalidKeyException expected) {\n                String msg = \"The provided Elliptic Curve signing key size (aka order bit length) is \" +\n                        \"${Bytes.bitsMsg(order.bitLength())}, but the '${it.getId()}' algorithm requires EC Keys with \" +\n                        \"${Bytes.bitsMsg(it.orderBitLength)} per \" +\n                        \"[RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).\" as String\n                assertEquals msg, expected.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testSignWithInvalidKeyFieldLength() {\n        def keypair = Jwts.SIG.ES256.keyPair().build()\n        def data = \"foo\".getBytes(StandardCharsets.UTF_8)\n        def req = new DefaultSecureRequest(Streams.of(data), null, null, keypair.private)\n        try {\n            Jwts.SIG.ES384.digest(req)\n        } catch (InvalidKeyException expected) {\n            String msg = \"The provided Elliptic Curve signing key size (aka order bit length) is \" +\n                    \"256 bits (32 bytes), but the 'ES384' algorithm requires EC Keys with \" +\n                    \"384 bits (48 bytes) per \" +\n                    \"[RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testVerifyWithPrivateKey() {\n        byte[] data = 'foo'.getBytes(StandardCharsets.UTF_8)\n        def payload = Streams.of(data)\n        algs().each {\n            payload.reset()\n            def pair = it.keyPair().build()\n            def key = pair.getPrivate()\n            def signRequest = new DefaultSecureRequest(payload, null, null, key)\n            byte[] signature = it.digest(signRequest)\n            payload.reset()\n            def verifyRequest = new DefaultVerifySecureDigestRequest(payload, null, null, key, signature)\n            try {\n                it.verify(verifyRequest)\n            } catch (InvalidKeyException e) {\n                String msg = \"${it.getId()} verification keys must be PublicKeys (implement \" +\n                        \"${PublicKey.class.name}). Provided key type: ${key.class.name}.\"\n                assertEquals msg, e.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testVerifyWithWeakKey() {\n        algs().each {\n            BigInteger order = BigInteger.ONE\n            ECParameterSpec spec = new ECParameterSpec(new EllipticCurve(new TestECField(), BigInteger.ONE, BigInteger.ONE), new ECPoint(BigInteger.ONE, BigInteger.ONE), order, 1)\n            ECPublicKey pub = new TestECPublicKey(algorithm: 'EC', params: spec)\n            def request = new DefaultVerifySecureDigestRequest(Streams.of(new byte[1]), null, null, pub, new byte[1])\n            try {\n                it.verify(request)\n            } catch (InvalidKeyException expected) {\n                String msg = \"The provided Elliptic Curve verification key size (aka order bit length) is \" +\n                        \"${Bytes.bitsMsg(order.bitLength())}, but the '${it.getId()}' algorithm requires EC Keys with \" +\n                        \"${Bytes.bitsMsg(it.orderBitLength)} per \" +\n                        \"[RFC 7518, Section 3.4](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).\" as String\n                assertEquals msg, expected.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void invalidDERSignatureToJoseFormatTest() {\n        def verify = { byte[] signature ->\n            try {\n                EcSignatureAlgorithm.transcodeDERToConcat(signature, 132)\n                fail()\n            } catch (JwtException e) {\n                assertEquals e.message, 'Invalid ECDSA signature format'\n            }\n        }\n        def signature = new byte[257]\n        Randoms.secureRandom().nextBytes(signature)\n        //invalid type\n        signature[0] = 34\n        verify(signature)\n        def shortSignature = new byte[7]\n        Randoms.secureRandom().nextBytes(shortSignature)\n        verify(shortSignature)\n        signature[0] = 48\n//        signature[1] = 0x81\n        signature[1] = -10\n        verify(signature)\n    }\n\n    @Test\n    void edgeCaseSignatureToConcatInvalidSignatureTest() {\n        try {\n            def signature = Decoders.BASE64.decode(\"MIGBAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\")\n            EcSignatureAlgorithm.transcodeDERToConcat(signature, 132)\n            fail()\n        } catch (JwtException e) {\n            assertEquals e.message, 'Invalid ECDSA signature format'\n        }\n    }\n\n    @Test\n    void edgeCaseSignatureToConcatInvalidSignatureBranchTest() {\n        try {\n            def signature = Decoders.BASE64.decode(\"MIGBAD4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\")\n            EcSignatureAlgorithm.transcodeDERToConcat(signature, 132)\n            fail()\n        } catch (JwtException e) {\n            assertEquals e.message, 'Invalid ECDSA signature format'\n        }\n    }\n\n    @Test\n    void edgeCaseSignatureToConcatInvalidSignatureBranch2Test() {\n        try {\n            def signature = Decoders.BASE64.decode(\"MIGBAj4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\")\n            EcSignatureAlgorithm.transcodeDERToConcat(signature, 132)\n            fail()\n        } catch (JwtException e) {\n            assertEquals e.message, 'Invalid ECDSA signature format'\n        }\n    }\n\n    @Test\n    void edgeCaseSignatureToConcatLengthTest() {\n        try {\n            def signature = Decoders.BASE64.decode(\"MIEAAGg3OVb/ZeX12cYrhK3c07TsMKo7Kc6SiqW++4CAZWCX72DkZPGTdCv2duqlupsnZL53hiG3rfdOLj8drndCU+KHGrn5EotCATdMSLCXJSMMJoHMM/ZPG+QOHHPlOWnAvpC1v4lJb32WxMFNz1VAIWrl9Aa6RPG1GcjCTScKjvEE\")\n            EcSignatureAlgorithm.transcodeDERToConcat(signature, 132)\n            fail()\n        } catch (JwtException expected) {\n        }\n    }\n\n    @Test\n    void invalidECDSASignatureFormatTest() {\n        try {\n            def signature = new byte[257]\n            Randoms.secureRandom().nextBytes(signature)\n            EcSignatureAlgorithm.transcodeConcatToDER(signature)\n            fail()\n        } catch (JwtException e) {\n            assertEquals 'Invalid ECDSA signature format.', e.message\n        }\n    }\n\n    @Test\n    void edgeCaseSignatureLengthTest() {\n        def signature = new byte[1]\n        EcSignatureAlgorithm.transcodeConcatToDER(signature)\n    }\n\n    @Test\n    void testPaddedSignatureToDER() {\n        def signature = new byte[32]\n        Randoms.secureRandom().nextBytes(signature)\n        signature[0] = 0 as byte\n        EcSignatureAlgorithm.transcodeConcatToDER(signature) //no exception\n    }\n\n    @Test\n    void ecdsaSignatureCompatTest() {\n        def fact = KeyFactory.getInstance(\"EC\")\n        def publicKey = \"MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQASisgweVL1tAtIvfmpoqvdXF8sPKTV9YTKNxBwkdkm+/auh4pR8TbaIfsEzcsGUVv61DFNFXb0ozJfurQ59G2XcgAn3vROlSSnpbIvuhKrzL5jwWDTaYa5tVF1Zjwia/5HUhKBkcPuWGXg05nMjWhZfCuEetzMLoGcHmtvabugFrqsAg=\"\n        def pub = fact.generatePublic(new X509EncodedKeySpec(Decoders.BASE64.decode(publicKey)))\n        def alg = Jwts.SIG.ES512\n        def verifier = { String token ->\n            def signatureStart = token.lastIndexOf('.')\n            def withoutSignature = token.substring(0, signatureStart)\n            def data = Strings.ascii(withoutSignature);\n            def payload = Streams.of(data)\n            def signature = Decoders.BASE64URL.decode(token.substring(signatureStart + 1))\n            assertTrue \"Signature do not match that of other implementations\", alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, pub, signature))\n        }\n        //Test verification for token created using https://github.com/auth0/node-jsonwebtoken/tree/v7.0.1\n        verifier(\"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30.Aab4x7HNRzetjgZ88AMGdYV2Ml7kzFbl8Ql2zXvBores7iRqm2nK6810ANpVo5okhHa82MQf2Q_Zn4tFyLDR9z4GAcKFdcAtopxq1h8X58qBWgNOc0Bn40SsgUc8wOX4rFohUCzEtnUREePsvc9EfXjjAH78WD2nq4tn-N94vf14SncQ\")\n        //Test verification for token created using https://github.com/jwt/ruby-jwt/tree/v1.5.4\n        verifier(\"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJ0ZXN0IjoidGVzdCJ9.AV26tERbSEwcoDGshneZmhokg-tAKUk0uQBoHBohveEd51D5f6EIs6cskkgwtfzs4qAGfx2rYxqQXr7LTXCNquKiAJNkTIKVddbPfped3_TQtmHZTmMNiqmWjiFj7Y9eTPMMRRu26w4gD1a8EQcBF-7UGgeH4L_1CwHJWAXGbtu7uMUn\")\n    }\n\n    @Test\n    void verifySwarmTest() {\n        algs().each { alg ->\n            def withoutSignature = \"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n            def keypair = alg.keyPair().build()\n            assertNotNull keypair\n            assertTrue keypair.getPublic() instanceof ECPublicKey\n            assertTrue keypair.getPrivate() instanceof ECPrivateKey\n            def data = Strings.ascii(withoutSignature)\n            def payload = Streams.of(data)\n            def signature = alg.digest(new DefaultSecureRequest<>(payload, null, null, keypair.private))\n            payload.reset()\n            assertTrue alg.verify(new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, signature))\n        }\n    }\n\n    // ===================== Begin imported EllipticCurveSignerTest test cases ==============================\n\n    /*\n\n    @Test\n    void testDoSignWithInvalidKeyException() {\n\n        SignatureAlgorithm alg = SignatureAlgorithm.ES256\n\n        KeyPair kp = Keys.keyPairFor(alg)\n        PrivateKey privateKey = kp.getPrivate()\n\n        String msg = 'foo'\n        final java.security.InvalidKeyException ex = new java.security.InvalidKeyException(msg)\n\n        def signer = new EllipticCurveSigner(alg, privateKey) {\n            @Override\n            protected byte[] doSign(byte[] data) throws java.security.InvalidKeyException, java.security.SignatureException {\n                throw ex\n            }\n        }\n\n        byte[] bytes = new byte[16]\n        SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(bytes)\n\n        try {\n            signer.digest(bytes)\n            fail();\n        } catch (io.jsonwebtoken.security.SignatureException se) {\n            assertEquals se.message, 'Invalid Elliptic Curve PrivateKey. ' + msg\n            assertSame se.cause, ex\n        }\n    }\n\n    @Test\n    void testDoSignWithJoseSignatureFormatException() {\n\n        SignatureAlgorithm alg = SignatureAlgorithm.ES256\n        KeyPair kp = EllipticCurveProvider.generateKeyPair(alg)\n        PublicKey publicKey = kp.getPublic();\n        PrivateKey privateKey = kp.getPrivate();\n\n        String msg = 'foo'\n        final JwtException ex = new JwtException(msg)\n\n        def signer = new EllipticCurveSigner(alg, privateKey) {\n            @Override\n            protected byte[] doSign(byte[] data) throws java.security.InvalidKeyException, java.security.SignatureException, JwtException {\n                throw ex\n            }\n        }\n\n        byte[] bytes = new byte[16]\n        SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(bytes)\n\n        try {\n            signer.digest(bytes)\n            fail();\n        } catch (io.jsonwebtoken.security.SignatureException se) {\n            assertEquals se.message, 'Unable to convert signature to JOSE format. ' + msg\n            assertSame se.cause, ex\n        }\n    }\n\n    @Test\n    void testDoSignWithJdkSignatureException() {\n\n        SignatureAlgorithm alg = SignatureAlgorithm.ES256\n        KeyPair kp = EllipticCurveProvider.generateKeyPair(alg)\n        PublicKey publicKey = kp.getPublic();\n        PrivateKey privateKey = kp.getPrivate();\n\n        String msg = 'foo'\n        final java.security.SignatureException ex = new java.security.SignatureException(msg)\n\n        def signer = new EllipticCurveSigner(alg, privateKey) {\n            @Override\n            protected byte[] doSign(byte[] data) throws java.security.InvalidKeyException, java.security.SignatureException {\n                throw ex\n            }\n        }\n\n        byte[] bytes = new byte[16]\n        SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(bytes)\n\n        try {\n            signer.digest(bytes)\n            fail();\n        } catch (io.jsonwebtoken.security.SignatureException se) {\n            assertEquals se.message, 'Unable to calculate signature using Elliptic Curve PrivateKey. ' + msg\n            assertSame se.cause, ex\n        }\n    }\n\n    @Test\n    void testDoVerifyWithInvalidKeyException() {\n\n        String msg = 'foo'\n        final java.security.InvalidKeyException ex = new java.security.InvalidKeyException(msg)\n        def alg = SignatureAlgorithm.ES512\n        def keypair = EllipticCurveProvider.generateKeyPair(alg)\n\n        def v = new EllipticCurveSignatureValidator(alg, EllipticCurveProvider.generateKeyPair(alg).public) {\n            @Override\n            protected boolean doVerify(Signature sig, PublicKey pk, byte[] data, byte[] signature) throws java.security.InvalidKeyException, java.security.SignatureException {\n                throw ex;\n            }\n        }\n\n        byte[] data = new byte[32]\n        SignatureProvider.DEFAULT_SECURE_RANDOM.nextBytes(data)\n\n        byte[] signature = new EllipticCurveSigner(alg, keypair.getPrivate()).digest(data)\n\n        try {\n            v.isValid(data, signature)\n            fail();\n        } catch (io.jsonwebtoken.security.SignatureException se) {\n            assertEquals se.message, 'Unable to verify Elliptic Curve signature using configured ECPublicKey. ' + msg\n            assertSame se.cause, ex\n        }\n    }\n\n     */\n\n    @Test\n    void ecdsaSignatureInteropTest() {\n        def fact = KeyFactory.getInstance(\"EC\")\n        def publicKey = \"MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQASisgweVL1tAtIvfmpoqvdXF8sPKTV9YTKNxBwkdkm+/auh4pR8TbaIfsEzcsGUVv61DFNFXb0ozJfurQ59G2XcgAn3vROlSSnpbIvuhKrzL5jwWDTaYa5tVF1Zjwia/5HUhKBkcPuWGXg05nMjWhZfCuEetzMLoGcHmtvabugFrqsAg=\"\n        def pub = fact.generatePublic(new X509EncodedKeySpec(Decoders.BASE64.decode(publicKey)))\n        def alg = Jwts.SIG.ES512\n        def verifier = { String token ->\n            def signatureStart = token.lastIndexOf('.')\n            def withoutSignature = token.substring(0, signatureStart)\n            def signature = token.substring(signatureStart + 1)\n\n            def data = withoutSignature.getBytes(StandardCharsets.US_ASCII)\n            def payload = Streams.of(data)\n            def sigBytes = Decoders.BASE64URL.decode(signature)\n            def request = new DefaultVerifySecureDigestRequest(payload, null, null, pub, sigBytes)\n            assert alg.verify(request), \"Signature do not match that of other implementations\"\n        }\n        //Test verification for token created using https://github.com/auth0/node-jsonwebtoken/tree/v7.0.1\n        verifier(\"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30.Aab4x7HNRzetjgZ88AMGdYV2Ml7kzFbl8Ql2zXvBores7iRqm2nK6810ANpVo5okhHa82MQf2Q_Zn4tFyLDR9z4GAcKFdcAtopxq1h8X58qBWgNOc0Bn40SsgUc8wOX4rFohUCzEtnUREePsvc9EfXjjAH78WD2nq4tn-N94vf14SncQ\")\n        //Test verification for token created using https://github.com/jwt/ruby-jwt/tree/v1.5.4\n        verifier(\"eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzUxMiJ9.eyJ0ZXN0IjoidGVzdCJ9.AV26tERbSEwcoDGshneZmhokg-tAKUk0uQBoHBohveEd51D5f6EIs6cskkgwtfzs4qAGfx2rYxqQXr7LTXCNquKiAJNkTIKVddbPfped3_TQtmHZTmMNiqmWjiFj7Y9eTPMMRRu26w4gD1a8EQcBF-7UGgeH4L_1CwHJWAXGbtu7uMUn\")\n    }\n\n    @Test\n    // asserts guard for JVM security bug CVE-2022-21449:\n    void legacySignatureCompatDefaultTest() {\n        def withoutSignature = \"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n        def alg = Jwts.SIG.ES512\n        def keypair = alg.keyPair().build()\n        def signature = Signature.getInstance(alg.jcaName as String)\n        def data = Strings.ascii(withoutSignature)\n        signature.initSign(keypair.private)\n        signature.update(data)\n        def signed = signature.sign()\n        def request = new DefaultVerifySecureDigestRequest(Streams.of(data), null, null, keypair.public, signed)\n        try {\n            alg.verify(request)\n            fail()\n        } catch (SignatureException expected) {\n            String signedBytesString = Bytes.bytesMsg(signed.length)\n            String msg = \"Unable to verify Elliptic Curve signature using provided ECPublicKey: Provided \" +\n                    \"signature is $signedBytesString but ES512 signatures must be exactly 1056 bits (132 bytes) \" +\n                    \"per [RFC 7518, Section 3.4 (validation)]\" +\n                    \"(https://www.rfc-editor.org/rfc/rfc7518.html#section-3.4).\" as String\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void legacySignatureCompatWhenEnabledTest() {\n        try {\n            System.setProperty(EcSignatureAlgorithm.DER_ENCODING_SYS_PROPERTY_NAME, 'true')\n\n            def withoutSignature = \"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n            def alg = Jwts.SIG.ES512\n            def keypair = alg.keyPair().build()\n            def signature = Signature.getInstance(alg.jcaName as String)\n            def data = Strings.ascii(withoutSignature)\n            def payload = Streams.of(data)\n            signature.initSign(keypair.private)\n            signature.update(data)\n            def signed = signature.sign()\n            def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, signed)\n            assertTrue alg.verify(request)\n        } finally {\n            System.clearProperty(EcSignatureAlgorithm.DER_ENCODING_SYS_PROPERTY_NAME)\n        }\n    }\n\n    @Test\n    // asserts guard for JVM security bug CVE-2022-21449:\n    void testVerifySignatureAllZeros() {\n        byte[] forgedSig = new byte[64]\n        def withoutSignature = \"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n        def alg = Jwts.SIG.ES256\n        def keypair = alg.keyPair().build()\n        def data = Strings.ascii(withoutSignature)\n        def payload = Streams.of(data)\n        def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, forgedSig)\n        assertFalse alg.verify(request)\n    }\n\n    @Test\n    // asserts guard for JVM security bug CVE-2022-21449:\n    void testVerifySignatureRZero() {\n        byte[] r = new byte[32]\n        byte[] s = new byte[32]; Arrays.fill(s, Byte.MAX_VALUE)\n        byte[] sig = new byte[r.length + s.length]\n        System.arraycopy(r, 0, sig, 0, r.length)\n        System.arraycopy(s, 0, sig, r.length, s.length)\n\n        def withoutSignature = \"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n        def alg = Jwts.SIG.ES256\n        def keypair = alg.keyPair().build()\n        def data = Strings.ascii(withoutSignature)\n        def payload = Streams.of(data)\n        def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, sig)\n        assertFalse alg.verify(request)\n    }\n\n    @Test\n    // asserts guard for JVM security bug CVE-2022-21449:\n    void testVerifySignatureSZero() {\n        byte[] r = new byte[32]; Arrays.fill(r, Byte.MAX_VALUE)\n        byte[] s = new byte[32]\n        byte[] sig = new byte[r.length + s.length]\n        System.arraycopy(r, 0, sig, 0, r.length)\n        System.arraycopy(s, 0, sig, r.length, s.length)\n\n        def withoutSignature = \"eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n        def alg = Jwts.SIG.ES256\n        def keypair = alg.keyPair().build()\n        def data = Strings.ascii(withoutSignature)\n        def payload = Streams.of(data)\n        def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, sig)\n        assertFalse alg.verify(request)\n    }\n\n    @Test\n    // asserts guard for JVM security bug CVE-2022-21449:\n    void ecdsaInvalidSignatureValuesTest() {\n        def withoutSignature = \"eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJ0ZXN0IjoidGVzdCIsImlhdCI6MTQ2NzA2NTgyN30\"\n        def invalidEncodedSignature = \"_____wAAAAD__________7zm-q2nF56E87nKwvxjJVH_____AAAAAP__________vOb6racXnoTzucrC_GMlUQ\"\n        def alg = Jwts.SIG.ES256\n        def keypair = alg.keyPair().build()\n        def data = Strings.ascii(withoutSignature)\n        def payload = Streams.of(data)\n        def invalidSignature = Decoders.BASE64URL.decode(invalidEncodedSignature)\n        def request = new DefaultVerifySecureDigestRequest(payload, null, null, keypair.public, invalidSignature)\n        assertFalse(\"Forged signature must not be considered valid.\", alg.verify(request))\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/EcdhKeyAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.JweHeader\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.MalformedJwtException\nimport io.jsonwebtoken.impl.DefaultJweHeader\nimport io.jsonwebtoken.impl.DefaultMutableJweHeader\nimport io.jsonwebtoken.security.DecryptionKeyRequest\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\n/**\n * The {@link EcdhKeyAlgorithm} class is mostly tested already in RFC Appendix tests, so this class\n * adds in tests for assertions/conditionals that aren't as easily tested elsewhere.\n */\nclass EcdhKeyAlgorithmTest {\n\n    private static Jwts.HeaderBuilder jwe() {\n        return Jwts.header().add('alg', 'foo').add('enc', 'bar')\n    }\n\n    @Test\n    void testEdwardsEncryptionWithRequestProvider() {\n        def alg = new EcdhKeyAlgorithm()\n        PublicKey encKey = TestKeys.X25519.pair.public as PublicKey\n        def header = new DefaultMutableJweHeader(Jwts.header())\n        def provider = TestKeys.BC\n        def request = new DefaultKeyRequest(encKey, provider, null, header, Jwts.ENC.A128GCM)\n        def result = alg.getEncryptionKey(request)\n        assertNotNull result.getKey()\n    }\n\n    @Test\n    void testEdwardsDecryptionWithRequestProvider() {\n        def alg = new EcdhKeyAlgorithm()\n        def enc = Jwts.ENC.A128GCM\n        PublicKey encKey = TestKeys.X25519.pair.public as PublicKey\n        PrivateKey decKey = TestKeys.X25519.pair.private as PrivateKey\n        def header = jwe()\n        def provider = TestKeys.BC\n\n        // encrypt\n        def delegate = new DefaultMutableJweHeader(header)\n        def request = new DefaultKeyRequest(encKey, provider, null, delegate, enc)\n        def result = alg.getEncryptionKey(request)\n        def cek = result.getKey()\n        def cekCiphertext = result.getPayload()\n\n        JweHeader jweHeader = header.build() as JweHeader\n\n        def decRequest = new DefaultDecryptionKeyRequest(cekCiphertext, provider, null, jweHeader, enc, decKey)\n        def resultCek = alg.getDecryptionKey(decRequest)\n        assertEquals(cek, resultCek)\n    }\n\n    @Test\n    void testDecryptionWithMissingEcPublicJwk() {\n\n        def alg = new EcdhKeyAlgorithm()\n        ECPrivateKey decryptionKey = TestKeys.ES256.pair.private as ECPrivateKey\n\n        def header = new DefaultJweHeader([:])\n\n        DecryptionKeyRequest req = new DefaultDecryptionKeyRequest('test'.getBytes(), null, null, header, Jwts.ENC.A128GCM, decryptionKey)\n\n        try {\n            alg.getDecryptionKey(req)\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = \"JWE header is missing required 'epk' (Ephemeral Public Key) value.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testDecryptionWithEcPublicJwkOnInvalidCurve() {\n\n        def alg = new EcdhKeyAlgorithm()\n        ECPrivateKey decryptionKey = TestKeys.ES256.pair.private as ECPrivateKey // Expected curve for this is P-256\n\n        // This uses curve P-384 instead, does not match private key, so it's unexpected:\n        def jwk = Jwks.builder().key(TestKeys.ES384.pair.public as ECPublicKey).build()\n        JweHeader header = jwe().add('epk', jwk).build() as JweHeader\n\n        DecryptionKeyRequest req = new DefaultDecryptionKeyRequest('test'.getBytes(), null, null, header, Jwts.ENC.A128GCM, decryptionKey)\n\n        try {\n            alg.getDecryptionKey(req)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = \"JWE Header 'epk' (Ephemeral Public Key) value does not represent a point on the \" +\n                    \"expected curve. Value: ${jwk.toString()}\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n\n    @Test\n    void testEncryptionWithInvalidPublicKey() {\n        def alg = new EcdhKeyAlgorithm()\n        PublicKey encKey = TestKeys.RS256.pair.public as PublicKey // not an elliptic curve key, must fail\n        def header = new DefaultMutableJweHeader(Jwts.header())\n        def request = new DefaultKeyRequest(encKey, null, null, header, Jwts.ENC.A128GCM)\n        try {\n            alg.getEncryptionKey(request)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = \"Unable to determine JWA-standard Elliptic Curve for encryption key [${KeysBridge.toString(encKey)}]\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testDecryptionWithInvalidPrivateKey() {\n        def alg = new EcdhKeyAlgorithm()\n        PrivateKey key = TestKeys.RS256.pair.private as PrivateKey // not an elliptic curve key, must fail\n        def jwk = Jwks.builder().key(TestKeys.RS256.pair.public as RSAPublicKey).build()\n        JweHeader header = jwe().add('epk', jwk).build() as JweHeader\n        def request = new DefaultDecryptionKeyRequest('test'.getBytes(), null, null, header, Jwts.ENC.A128GCM, key)\n        try {\n            alg.getDecryptionKey(request)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = \"Unable to determine JWA-standard Elliptic Curve for decryption key [${KeysBridge.toString(key)}]\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testDecryptionWithoutEpk() {\n        def alg = new EcdhKeyAlgorithm()\n        PrivateKey key = TestKeys.ES256.pair.private as PrivateKey // valid key\n        def header = new DefaultJweHeader([:]) // no 'epk' value\n        def request = new DefaultDecryptionKeyRequest('test'.getBytes(), null, null, header, Jwts.ENC.A128GCM, key)\n        try {\n            alg.getDecryptionKey(request)\n            fail()\n        } catch (MalformedJwtException expected) {\n            String msg = 'JWE header is missing required \\'epk\\' (Ephemeral Public Key) value.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testECDecryptionWithNonECEpk() {\n        def alg = new EcdhKeyAlgorithm()\n        PrivateKey key = TestKeys.ES256.pair.private as PrivateKey // valid key\n        def jwk = Jwks.builder().key(TestKeys.RS256.pair.public as RSAPublicKey).build() // invalid epk\n        JweHeader header = jwe().add('epk', jwk).build() as JweHeader\n        def request = new DefaultDecryptionKeyRequest('test'.getBytes(), null, null, header, Jwts.ENC.A128GCM, key)\n        try {\n            alg.getDecryptionKey(request)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = \"JWE Header ${DefaultJweHeader.EPK} value is not an Elliptic Curve Public JWK. Value: ${jwk.toString()}\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEdwardsDecryptionWithNonEdwardsEpk() {\n        def alg = new EcdhKeyAlgorithm()\n        PrivateKey key = TestKeys.X25519.pair.private as PrivateKey // valid key\n        def jwk = Jwks.builder().key(TestKeys.RS256.pair.public as RSAPublicKey).build() // invalid epk\n        JweHeader header = jwe().add('epk', jwk).build() as JweHeader\n        def request = new DefaultDecryptionKeyRequest('test'.getBytes(), null, null, header, Jwts.ENC.A128GCM, key)\n        try {\n            alg.getDecryptionKey(request)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = \"JWE Header ${DefaultJweHeader.EPK} value is not an Elliptic Curve Public JWK. Value: ${jwk.toString()}\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEdwardsDecryptionWithEpkOnDifferentCurve() {\n        def alg = new EcdhKeyAlgorithm()\n        PrivateKey key = TestKeys.X25519.pair.private as PrivateKey // valid key\n        def jwk = Jwks.builder().key(TestKeys.X448.pair.public as PublicKey).build() // epk is not on X25519\n        JweHeader header = jwe().add('epk', jwk).build() as JweHeader\n        def request = new DefaultDecryptionKeyRequest('test'.getBytes(), null, null, header, Jwts.ENC.A128GCM, key)\n        try {\n            alg.getDecryptionKey(request)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = 'JWE Header \\'epk\\' (Ephemeral Public Key) value does not represent a point on the ' +\n                    'expected curve. Value: {kty=OKP, crv=X448, ' +\n                    'x=XxQlWa22S36qjui_M2IBT5vg0CmmLJkpBhXeiuBptUxJ_nnD0uITBH5N9PHkhOM8gfGtNkh6Jwc}'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testAssertEcCurveFails() {\n        def key = TestKeys.HS256\n        try {\n            EcdhKeyAlgorithm.assertCurve(key)\n            fail()\n        } catch (InvalidKeyException expected) {\n            String msg = \"Unable to determine JWA-standard Elliptic Curve for decryption key [${KeysBridge.toString(key)}]\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/EdSignatureAlgorithmTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.UnsupportedJwtException\nimport io.jsonwebtoken.security.SignatureException\nimport org.junit.Test\n\nimport java.security.PrivateKey\nimport java.security.PublicKey\n\nimport static org.junit.Assert.*\n\nclass EdSignatureAlgorithmTest {\n\n    static EdSignatureAlgorithm alg = Jwts.SIG.EdDSA as EdSignatureAlgorithm\n\n    @Test\n    void testJcaName() {\n        // the JWT RFC id and the JDK standard name appen to be the same:\n        assertEquals alg.getId(), alg.getJcaName()\n    }\n\n    @Test\n    void testId() {\n        // https://www.rfc-editor.org/rfc/rfc8037#section-3.1:\n        assertEquals 'EdDSA', alg.getId()\n    }\n\n    @Test\n    void testKeyPairBuilder() {\n        def pair = alg.keyPair().build()\n        assertNotNull pair.public\n        assertTrue pair.public instanceof PublicKey\n        String algName = pair.public.getAlgorithm()\n        assertTrue alg.getId().equals(algName) || algName.equals(alg.preferredCurve.getId())\n\n        algName = pair.private.getAlgorithm()\n        assertTrue alg.getId().equals(algName) || algName.equals(alg.preferredCurve.getId())\n    }\n\n    /**\n     * Likely when keys are from an HSM or PKCS key store\n     */\n    @Test\n    void testGetRequestJcaNameByKeyAlgorithmNameOnly() {\n        def key = new TestKey(algorithm: EdwardsCurve.X25519.OID)\n        def payload = [0x00] as byte[]\n        def req = new DefaultSecureRequest(payload, null, null, key)\n        assertEquals 'X25519', alg.getJcaName(req) // Not the EdDSA default\n    }\n\n    @Test\n    void testEd25519SigVerifyWithEd448() {\n        testIncorrectVerificationKey(TestKeys.Ed25519.pair.private, TestKeys.Ed448.pair.public)\n    }\n\n    @Test\n    void testEd25519SigVerifyWithX25519() {\n        testInvalidVerificationKey(TestKeys.Ed25519.pair.private, TestKeys.X25519.pair.public)\n    }\n\n    @Test\n    void testEd25519SigVerifyWithX448() {\n        testInvalidVerificationKey(TestKeys.Ed25519.pair.private, TestKeys.X448.pair.public)\n    }\n\n    @Test\n    void testEd448SigVerifyWithEd25519() {\n        testIncorrectVerificationKey(TestKeys.Ed448.pair.private, TestKeys.Ed25519.pair.public)\n    }\n\n    @Test\n    void testEd448SigVerifyWithX25519() {\n        testInvalidVerificationKey(TestKeys.Ed448.pair.private, TestKeys.X25519.pair.public)\n    }\n\n    @Test\n    void testEd448SigVerifyWithX448() {\n        testInvalidVerificationKey(TestKeys.Ed448.pair.private, TestKeys.X448.pair.public)\n    }\n\n    static void testIncorrectVerificationKey(PrivateKey priv, PublicKey pub) {\n        try {\n            testSig(priv, pub)\n            fail()\n        } catch (SignatureException expected) {\n            // SignatureException message can differ depending on JDK version and if BC is enabled or not:\n            // BC Provider signature.verify() will just return false, but SunEC provider signature.verify() throws an\n            // exception with its own message.  As a result, we should always get a SignatureException, but we need\n            // to check the message for either scenario depending on the JVM version running the tests:\n            String exMsg = expected.getMessage()\n            String expectedMsg = 'JWT signature does not match locally computed signature. JWT validity cannot be asserted and should not be trusted.'\n            String expectedMsg2 = \"Unable to verify EdDSA signature with JCA algorithm 'EdDSA' using key {${pub}}: ${expected.getCause()?.getMessage()}\"\n            assertTrue exMsg.equals(expectedMsg) || exMsg.equals(expectedMsg2)\n        }\n    }\n\n    static void testInvalidVerificationKey(PrivateKey priv, PublicKey pub) {\n        try {\n            testSig(priv, pub)\n            fail()\n        } catch (UnsupportedJwtException expected) {\n            def cause = expected.getCause()\n            def keyCurve = EdwardsCurve.forKey(pub)\n            String expectedMsg = \"${keyCurve.getId()} keys may not be used with EdDSA digital signatures per \" +\n                    \"https://www.rfc-editor.org/rfc/rfc8037.html#section-3.2\"\n            assertEquals expectedMsg, cause.getMessage()\n        }\n    }\n\n    static void testSig(PrivateKey signing, PublicKey verification) {\n        String jwt = Jwts.builder().issuer('me').audience().add('you').and().signWith(signing, alg).compact()\n        def token = Jwts.parser().verifyWith(verification).build().parseSignedClaims(jwt)\n        assertEquals([alg: alg.getId()], token.header)\n        assertEquals 'me', token.getPayload().getIssuer()\n        assertEquals 'you', token.getPayload().getAudience().iterator().next()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/EdwardsCurveTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.security.InvalidKeyException\nimport org.junit.Test\n\nimport java.security.spec.PKCS8EncodedKeySpec\n\nimport static org.junit.Assert.*\n\nclass EdwardsCurveTest {\n\n    static final Collection<EdwardsCurve> curves = EdwardsCurve.VALUES\n\n    @SuppressWarnings('GroovyResultOfObjectAllocationIgnored')\n    @Test\n    void testInvalidOidTerminalNode() {\n        try {\n            new EdwardsCurve('foo', 200)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            String expected = 'Invalid Edwards Curve ASN.1 OID terminal node value'\n            assertEquals expected, iae.getMessage()\n        }\n    }\n\n\n    /**\n     * Asserts bit lengths defined in:\n     * - https://www.rfc-editor.org/rfc/rfc7748.html\n     * - https://www.rfc-editor.org/rfc/rfc8032\n     */\n    @Test\n    void testKeyBitLength() {\n        assertEquals(255, EdwardsCurve.X25519.getKeyBitLength())\n        assertEquals(255, EdwardsCurve.Ed25519.getKeyBitLength())\n        assertEquals(448, EdwardsCurve.X448.getKeyBitLength())\n        assertEquals(448, EdwardsCurve.Ed448.getKeyBitLength())\n    }\n\n    /**\n     * Asserts encoding lengths defined in:\n     * - https://www.rfc-editor.org/rfc/rfc7748.html\n     * - https://www.rfc-editor.org/rfc/rfc8032\n     */\n    @Test\n    void testEncodedKeyByteLength() {\n        assertEquals 32, EdwardsCurve.X25519.encodedKeyByteLength\n        assertEquals 32, EdwardsCurve.Ed25519.encodedKeyByteLength\n        assertEquals 56, EdwardsCurve.X448.encodedKeyByteLength\n        assertEquals 57, EdwardsCurve.Ed448.encodedKeyByteLength\n    }\n\n    @Test\n    void testIsEdwardsNullKey() {\n        assertFalse EdwardsCurve.isEdwards(null)\n    }\n\n    @Test\n    void testForKeyNonEdwards() {\n        def alg = 'foo'\n        def key = new TestKey(algorithm: alg)\n        try {\n            EdwardsCurve.forKey(key)\n        } catch (InvalidKeyException uke) {\n            String msg = \"Unrecognized Edwards Curve key: [${KeysBridge.toString(key)}]\"\n            assertEquals msg, uke.getMessage()\n        }\n    }\n\n    @Test\n    void testFindByKey() { // happy path test\n        for (def alg : EdwardsCurve.VALUES) {\n            def keyPair = alg.keyPair().build()\n            def pub = keyPair.public\n            def priv = keyPair.private\n            assertSame alg, EdwardsCurve.findByKey(pub)\n            assertSame alg, EdwardsCurve.findByKey(priv)\n        }\n    }\n\n    @Test\n    void testFindByNullKey() {\n        assertNull EdwardsCurve.findByKey(null)\n    }\n\n    @Test\n    void testFindByKeyUsingEncoding() {\n        curves.each {\n            def pair = TestKeys.forAlgorithm(it).pair\n            def key = new TestKey(algorithm: 'foo', encoded: pair.public.getEncoded())\n            def found = EdwardsCurve.findByKey(key)\n            assertEquals(it, found)\n        }\n    }\n\n    @Test\n    void testFindByKeyUsingInvalidEncoding() {\n        curves.each {\n            byte[] encoded = new byte[it.encodedKeyByteLength]\n            def key = new TestKey(algorithm: 'foo', encoded: encoded)\n            assertNull EdwardsCurve.findByKey(key)\n        }\n    }\n\n    @Test\n    void testFindByKeyUsingMalformedEncoding() {\n        curves.each {\n            byte[] encoded = EdwardsCurve.ASN1_OID_PREFIX // just the prefix isn't enough\n            def key = new TestKey(algorithm: 'foo', encoded: encoded)\n            assertNull EdwardsCurve.findByKey(key)\n        }\n    }\n\n    @Test\n    void testFindByKeyWithValidCurveButExcessiveLength() {\n        curves.each {\n            byte[] badValue = Bytes.random(it.encodedKeyByteLength + 1) // invalid size, too large\n            byte[] encoded = Bytes.concat(\n                    EdwardsCurve.publicKeyAsn1Prefix(badValue.length, it.ASN1_OID),\n                    badValue\n            )\n            def badKey = new TestPublicKey(encoded: encoded)\n            assertNull EdwardsCurve.findByKey(badKey)\n        }\n    }\n\n    @Test\n    void testFindByKeyWithValidCurveButWeakLength() {\n        curves.each {\n            byte[] badValue = Bytes.random(it.encodedKeyByteLength - 1) // invalid size, too small\n            byte[] encoded = Bytes.concat(\n                    EdwardsCurve.publicKeyAsn1Prefix(badValue.length, it.ASN1_OID),\n                    badValue\n            )\n            def badKey = new TestPublicKey(encoded: encoded)\n            assertNull EdwardsCurve.findByKey(badKey)\n        }\n    }\n\n    @Test\n    void testToPrivateKey() {\n        curves.each {\n            def pair = TestKeys.forAlgorithm(it).pair\n            def key = pair.getPrivate()\n            def d = it.getKeyMaterial(key)\n            def result = it.toPrivateKey(d, null)\n            assertEquals(key, result)\n        }\n    }\n\n    @Test\n    void testToPublicKey() {\n        curves.each {\n            def bundle = TestKeys.forAlgorithm(it)\n            def pair = bundle.pair\n            def key = pair.getPublic()\n            def x = it.getKeyMaterial(key)\n            def result = it.toPublicKey(x, null)\n            assertEquals(key, result)\n        }\n    }\n\n    @Test\n    void testToPrivateKeyInvalidLength() {\n        curves.each {\n            byte[] d = new byte[it.encodedKeyByteLength + 1] // more than required\n            Randoms.secureRandom().nextBytes(d)\n            try {\n                it.toPrivateKey(d, null)\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.id} encoded PrivateKey length. Should be \" +\n                        \"${Bytes.bytesMsg(it.encodedKeyByteLength)}, found ${Bytes.bytesMsg(d.length)}.\"\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testPrivateKeySpecJdk11() {\n        curves.each {\n            byte[] d = new byte[it.encodedKeyByteLength]; Randoms.secureRandom().nextBytes(d)\n            def keySpec = it.privateKeySpec(d, false) // standard = false for JDK 11 bug\n            assertTrue keySpec instanceof PKCS8EncodedKeySpec\n            def expectedEncoded = Bytes.concat(it.PRIVATE_KEY_JDK11_PREFIX, d)\n            assertArrayEquals expectedEncoded, ((PKCS8EncodedKeySpec)keySpec).getEncoded()\n        }\n    }\n\n    @Test\n    void testToPublicKeyInvalidLength() {\n        curves.each {\n            byte[] x = new byte[it.encodedKeyByteLength - 1] // less than required\n            Randoms.secureRandom().nextBytes(x)\n            try {\n                it.toPublicKey(x, null)\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.id} encoded PublicKey length. Should be \" +\n                        \"${Bytes.bytesMsg(it.encodedKeyByteLength)}, found ${Bytes.bytesMsg(x.length)}.\"\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    /**\n     * Ensures that if a DER NULL terminates the OID in the encoded key, the null tag is skipped.  This occurs in\n     * some SunCE key encodings.\n     */\n    @Test\n    void testGetKeyMaterialWithOidNullTerminator() {\n        byte[] DER_NULL = [0x05, 0x00] as byte[]\n        curves.each { it ->\n\n            byte[] x = new byte[it.encodedKeyByteLength]\n            Randoms.secureRandom().nextBytes(x)\n\n            byte[] encoded = Bytes.concat(\n                    [0x30, it.encodedKeyByteLength + 10 + DER_NULL.length, 0x30, 0x05] as byte[],\n                    it.ASN1_OID,\n                    DER_NULL, // this should be skipped when getting key material\n                    [0x03, it.encodedKeyByteLength + 1, 0x00] as byte[],\n                    x\n            )\n\n            def key = new TestKey(encoded: encoded)\n            byte[] material = it.getKeyMaterial(key)\n            assertArrayEquals(x, material)\n        }\n    }\n\n    @Test\n    void testGetKeyMaterialWithMissingEncodedBytes() {\n        def key = new TestKey(algorithm: 'foo')\n        curves.each {\n            try {\n                it.getKeyMaterial(key)\n                fail()\n            } catch (InvalidKeyException e) {\n                String msg = \"Missing required encoded bytes for key [${KeysBridge.toString(key)}].\"\n                assertEquals msg, e.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testGetKeyMaterialInvalidKeyEncoding() {\n        byte[] encoded = new byte[30]\n        Randoms.secureRandom().nextBytes(encoded)\n        //ensure random generator doesn't put in a byte that would cause other logic checks (0x03, 0x04, 0x05)\n        encoded[0] = 0x20 // anything other than 0x03, 0x04, 0x05\n        def key = new TestKey(encoded: encoded)\n        curves.each {\n            try {\n                it.getKeyMaterial(key)\n                fail()\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.getId()} ASN.1 encoding: Missing or incorrect algorithm OID.\" as String\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testGetKeyMaterialInvalidKeyLength() {\n        byte[] encoded = new byte[30]\n        Randoms.secureRandom().nextBytes(encoded)\n        //ensure random generator doesn't put in a byte that would cause other logic checks (0x03, 0x04, 0x05)\n        encoded[0] = 0x20 // anything other than 0x03, 0x04, 0x05\n        curves.each {\n            // prefix it with the OID to make it look valid:\n            encoded = Bytes.concat(it.ASN1_OID, encoded)\n            def key = new TestKey(encoded: encoded)\n            try {\n                it.getKeyMaterial(key)\n                fail()\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.getId()} ASN.1 encoding: Invalid key length.\" as String\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testPublicKeyMaterialInvalidBitSequence() {\n        int size = 0\n        curves.each {\n            try {\n                size = it.encodedKeyByteLength\n                byte[] keyBytes = new byte[size]\n                Randoms.secureRandom().nextBytes(keyBytes)\n                byte[] encoded = Bytes.concat(it.PUBLIC_KEY_ASN1_PREFIX, keyBytes)\n                encoded[11] = 0x01 // should always be zero\n                def key = new TestKey(encoded: encoded)\n                it.getKeyMaterial(key)\n                fail()\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.getId()} ASN.1 encoding: BIT STREAM should not indicate unused bytes.\" as String\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testPrivateKeyMaterialInvalidOctetSequence() {\n        int size = 0\n        curves.each {\n            try {\n                size = it.encodedKeyByteLength\n                byte[] keyBytes = new byte[size]\n                Randoms.secureRandom().nextBytes(keyBytes)\n                byte[] encoded = Bytes.concat(it.PRIVATE_KEY_ASN1_PREFIX, keyBytes)\n                encoded[14] = 0x0F // should always be 0x04 (ASN.1 SEQUENCE tag)\n                def key = new TestKey(encoded: encoded)\n                it.getKeyMaterial(key)\n                fail()\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.getId()} ASN.1 encoding: Invalid key length.\" as String\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testGetKeyMaterialTooShort() {\n        int size = 0\n        curves.each {\n            try {\n                size = it.encodedKeyByteLength - 1 // one less than required\n                byte[] keyBytes = new byte[size]\n                Randoms.secureRandom().nextBytes(keyBytes)\n                byte[] encoded = Bytes.concat(it.PUBLIC_KEY_ASN1_PREFIX, keyBytes)\n                encoded[10] = (byte) (size + 1) // ASN.1 size value (zero byte + key bytes)\n                def key = new TestKey(encoded: encoded)\n                it.getKeyMaterial(key)\n                fail()\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.getId()} ASN.1 encoding: Invalid key length.\" as String\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testGetKeyMaterialTooLong() {\n        int size = 0\n        curves.each {\n            try {\n                size = it.encodedKeyByteLength + 1 // one less than required\n                byte[] keyBytes = new byte[size]\n                Randoms.secureRandom().nextBytes(keyBytes)\n                byte[] encoded = Bytes.concat(it.PUBLIC_KEY_ASN1_PREFIX, keyBytes)\n                encoded[10] = (byte) (size + 1) // ASN.1 size value (zero byte + key bytes)\n                def key = new TestKey(encoded: encoded)\n                it.getKeyMaterial(key)\n                fail()\n            } catch (InvalidKeyException ike) {\n                String msg = \"Invalid ${it.getId()} ASN.1 encoding: Invalid key length.\" as String\n                assertEquals msg, ike.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testDerivePublicKeyFromPrivateKey() {\n        for (def curve : EdwardsCurve.VALUES) {\n            def pair = curve.keyPair().build() // generate a standard key pair using the JCA APIs\n            def pubKey = pair.getPublic()\n            def derivedPubKey = EdwardsCurve.derivePublic(pair.getPrivate())\n            // ensure our derived key matches the original JCA one:\n            assertEquals(pubKey, derivedPubKey)\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/EdwardsPublicKeyDeriverTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.security.InvalidKeyException\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass EdwardsPublicKeyDeriverTest {\n\n    @Test\n    void testDeriveWithNonEdwardsKey() {\n        def rsaPrivKey = Jwts.SIG.RS256.keyPair().build().getPrivate()\n        try {\n            EdwardsPublicKeyDeriver.INSTANCE.apply(rsaPrivKey)\n            fail()\n        } catch (InvalidKeyException uke) {\n            String expectedMsg = \"Unable to derive Edwards-curve PublicKey for specified PrivateKey: ${KeysBridge.toString(rsaPrivKey)}\"\n            assertEquals(expectedMsg, uke.getMessage())\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/FieldElementConverterTest.groovy",
    "content": "/*\n * Copyright © 2024 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\n/**\n * @since 0.12.4\n */\nclass FieldElementConverterTest {\n\n    static FieldElementConverter converter = FieldElementConverter.INSTANCE\n\n    @Test\n    void p384CoordinateNeedsPadding() {\n        def requiredByteLen = 48\n        def coordBytes = Bytes.random(requiredByteLen - 1) // one less to see if padding is applied\n        def coord = new BigInteger(1, coordBytes)\n        byte[] result = converter.applyTo(coord)\n        assertEquals requiredByteLen, result.length\n        assertEquals 0x00 as byte, result[0]\n        //ensure roundtrip works:\n        assertEquals coord, converter.applyFrom(result)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/GcmAesAeadAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\n\nimport static org.junit.Assert.assertArrayEquals\n\n/**\n * @since 0.12.0\n */\nclass GcmAesAeadAlgorithmTest {\n\n    final byte[] K =\n            [0xb1, 0xa1, 0xf4, 0x80, 0x54, 0x8f, 0xe1, 0x73, 0x3f, 0xb4, 0x3, 0xff, 0x6b, 0x9a, 0xd4, 0xf6,\n             0x8a, 0x7, 0x6e, 0x5b, 0x70, 0x2e, 0x22, 0x69, 0x2f, 0x82, 0xcb, 0x2e, 0x7a, 0xea, 0x40, 0xfc] as byte[]\n    final SecretKey KEY = new SecretKeySpec(K, \"AES\")\n\n    final byte[] P = \"The true sign of intelligence is not knowledge but imagination.\".getBytes(\"UTF-8\")\n\n    final byte[] IV = [0xe3, 0xc5, 0x75, 0xfc, 0x2, 0xdb, 0xe9, 0x44, 0xb4, 0xe1, 0x4d, 0xdb] as byte[]\n\n    final byte[] AAD =\n            [0x65, 0x79, 0x4a, 0x68, 0x62, 0x47, 0x63, 0x69, 0x4f, 0x69, 0x4a, 0x53, 0x55, 0x30, 0x45, 0x74,\n             0x54, 0x30, 0x46, 0x46, 0x55, 0x43, 0x49, 0x73, 0x49, 0x6d, 0x56, 0x75, 0x59, 0x79, 0x49, 0x36,\n             0x49, 0x6b, 0x45, 0x79, 0x4e, 0x54, 0x5a, 0x48, 0x51, 0x30, 0x30, 0x69, 0x66, 0x51] as byte[]\n\n    final byte[] E =\n            [0xe5, 0xec, 0xa6, 0xf1, 0x35, 0xbf, 0x73, 0xc4, 0xae, 0x2b, 0x49, 0x6d, 0x27, 0x7a, 0xe9, 0x60,\n             0x8c, 0xce, 0x78, 0x34, 0x33, 0xed, 0x30, 0xb, 0xbe, 0xdb, 0xba, 0x50, 0x6f, 0x68, 0x32, 0x8e,\n             0x2f, 0xa7, 0x3b, 0x3d, 0xb5, 0x7f, 0xc4, 0x15, 0x28, 0x52, 0xf2, 0x20, 0x7b, 0x8f, 0xa8, 0xe2,\n             0x49, 0xd8, 0xb0, 0x90, 0x8a, 0xf7, 0x6a, 0x3c, 0x10, 0xcd, 0xa0, 0x6d, 0x40, 0x3f, 0xc0] as byte[]\n\n    final byte[] T =\n            [0x5c, 0x50, 0x68, 0x31, 0x85, 0x19, 0xa1, 0xd7, 0xad, 0x65, 0xdb, 0xd3, 0x88, 0x5b, 0xd2, 0x91] as byte[]\n\n    /**\n     * Test that reflects https://tools.ietf.org/html/rfc7516#appendix-A.1\n     */\n    @Test\n    void testEncryptionAndDecryption() {\n\n        def alg = Jwts.ENC.A256GCM\n\n        def ins = Streams.of(P)\n        def aad = Streams.of(AAD)\n        def out = new ByteArrayOutputStream(8192)\n        def res = new DefaultAeadResult(out)\n        def req = new DefaultAeadRequest(ins, null, null, KEY, aad, IV)\n\n        alg.encrypt(req, res)\n        Streams.reset(aad)\n\n        byte[] ciphertext = out.toByteArray()\n\n        assertArrayEquals E, ciphertext\n        assertArrayEquals T, res.digest\n        assertArrayEquals IV, res.iv //shouldn't have been altered\n\n        // now test decryption:\n        out = new ByteArrayOutputStream(8192)\n        def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), KEY, aad, res.iv, res.digest)\n        alg.decrypt(dreq, out)\n        assertArrayEquals(P, out.toByteArray())\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testInstantiationWithInvalidKeyLength() {\n        new GcmAesAeadAlgorithm(5)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/HashAlgorithmsTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.lang.Registry\nimport io.jsonwebtoken.security.HashAlgorithm\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.*\n\nclass HashAlgorithmsTest {\n\n    static final Registry<String, HashAlgorithm> reg = Jwks.HASH.get()\n\n    static boolean contains(HashAlgorithm alg) {\n        return reg.values().contains(alg)\n    }\n\n    @Test\n    void testValues() {\n        assertEquals 6, reg.values().size()\n        assertTrue(contains(Jwks.HASH.SHA256)) // add more later\n    }\n\n    @Test\n    void testForKey() {\n        for (HashAlgorithm alg : reg.values()) {\n            assertSame alg, reg.forKey(alg.getId())\n        }\n    }\n\n    @Test\n    void testForKeyCaseInsensitive() {\n        for (HashAlgorithm alg : reg.values()) {\n            assertSame alg, reg.forKey(alg.getId().toLowerCase())\n        }\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testForKeyWithInvalidId() {\n        //unlike the 'get' paradigm, 'key' requires the value to exist\n        reg.forKey('invalid')\n    }\n\n    @Test\n    void testGet() {\n        for (HashAlgorithm alg : reg.values()) {\n            assertSame alg, reg.get(alg.getId())\n        }\n    }\n\n    @Test\n    void testGetCaseInsensitive() {\n        for (HashAlgorithm alg : reg.values()) {\n            assertSame alg, reg.get(alg.getId().toLowerCase())\n        }\n    }\n\n    @Test\n    void testGetWithInvalidId() {\n        // 'get' paradigm can return null if not found\n        assertNull reg.get('invalid')\n    }\n\n    static DefaultRequest<InputStream> request(String msg) {\n        byte[] data = msg.getBytes(StandardCharsets.UTF_8)\n        InputStream payload = Streams.of(data)\n        return new DefaultRequest<InputStream>(payload, null, null)\n    }\n\n    static void testSha(HashAlgorithm alg) {\n        String id = alg.getId()\n        int c = ('-' as char) as int\n        def digestLength = id.substring(id.lastIndexOf(c) + 1) as int\n        assertTrue alg.getJcaName().endsWith('' + digestLength)\n        def digest = alg.digest(request(\"hello\"))\n        assertEquals digestLength, (digest.length * Byte.SIZE)\n    }\n\n    @Test\n    void testSha256() {\n        testSha(Jwks.HASH.SHA256)\n    }\n\n    @Test\n    void testSha384() {\n        testSha(Jwks.HASH.SHA384)\n    }\n\n    @Test\n    void testSha512() {\n        testSha(Jwks.HASH.SHA512)\n    }\n\n    @Test\n    void testSha3_256() {\n        testSha(Jwks.HASH.SHA3_256)\n    }\n\n    @Test\n    void testSha3_384() {\n        testSha(Jwks.HASH.SHA3_384)\n    }\n\n    @Test\n    void testSha3_512() {\n        testSha(Jwks.HASH.SHA3_512)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/HmacAesAeadAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.AeadAlgorithm\nimport io.jsonwebtoken.security.SignatureException\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\n\nimport static org.junit.Assert.assertEquals\n\n/**\n * @since 0.12.0\n */\nclass HmacAesAeadAlgorithmTest {\n\n    @Test\n    void testKeyBitLength() {\n        // asserts that key lengths are double than what is usually expected for AES\n        // due to the encrypt-then-mac scheme requiring two separate keys\n        // (encrypt key is half of the generated key, mac key is the 2nd half of the generated key):\n        assertEquals 256, Jwts.ENC.A128CBC_HS256.getKeyBitLength()\n        assertEquals 384, Jwts.ENC.A192CBC_HS384.getKeyBitLength()\n        assertEquals 512, Jwts.ENC.A256CBC_HS512.getKeyBitLength()\n    }\n\n    @Test\n    void testGenerateKey() {\n        def algs = [\n                Jwts.ENC.A128CBC_HS256,\n                Jwts.ENC.A192CBC_HS384,\n                Jwts.ENC.A256CBC_HS512\n        ]\n        for (AeadAlgorithm alg : algs) {\n            SecretKey key = alg.key().build()\n            assertEquals alg.getKeyBitLength(), Bytes.bitLength(key.getEncoded())\n        }\n    }\n\n    @Test(expected = SignatureException)\n    void testDecryptWithInvalidTag() {\n\n        def alg = Jwts.ENC.A128CBC_HS256\n\n        SecretKey key = alg.key().build()\n\n        byte[] data = Strings.utf8('Hello World! Nice to meet you!')\n        def plaintext = Streams.of(data)\n\n        ByteArrayOutputStream out = new ByteArrayOutputStream(8192)\n        def res = new DefaultAeadResult(out)\n        def req = new DefaultAeadRequest(plaintext, null, null, key, null)\n\n        alg.encrypt(req, res)\n\n        def iv = res.getIv()\n        def realTag = res.getDigest()\n\n        //fake it:\n        def fakeTag = new byte[realTag.length]\n        Randoms.secureRandom().nextBytes(fakeTag)\n\n        byte[] ciphertext = out.toByteArray()\n        out = new ByteArrayOutputStream(8192)\n        def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), key, null, iv, fakeTag)\n        alg.decrypt(dreq, out)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/JcaTemplateTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.lang.CheckedFunction\nimport io.jsonwebtoken.lang.Classes\nimport io.jsonwebtoken.security.SecurityException\nimport io.jsonwebtoken.security.SignatureException\nimport org.bouncycastle.jce.provider.BouncyCastleProvider\nimport org.junit.Test\n\nimport javax.crypto.Cipher\nimport javax.crypto.Mac\nimport java.security.*\nimport java.security.cert.CertificateException\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\nimport java.security.spec.InvalidKeySpecException\nimport java.security.spec.KeySpec\nimport java.security.spec.PKCS8EncodedKeySpec\nimport java.security.spec.X509EncodedKeySpec\n\nimport static org.junit.Assert.*\n\nclass JcaTemplateTest {\n\n    static final Provider SUN_PROVIDER = Security.getProvider('SunJCE')\n    static final Provider BC_PROVIDER = new BouncyCastleProvider()\n\n    @Test\n    void testGetInstanceExceptionMessage() {\n        def factories = JcaTemplate.FACTORIES\n        for (def factory : factories) {\n            def clazz = factory.getInstanceClass()\n            try {\n                factory.get('foo', null)\n            } catch (SecurityException expected) {\n                if (clazz == Signature || clazz == Mac) {\n                    assertTrue expected instanceof SignatureException\n                }\n                String prefix = \"Unable to obtain 'foo' ${clazz.getSimpleName()} instance \" +\n                        \"from default JCA Provider: \"\n                assertTrue expected.getMessage().startsWith(prefix)\n            }\n        }\n    }\n\n    @Test\n    void testGetInstanceWithExplicitProviderExceptionMessage() {\n        def factories = JcaTemplate.FACTORIES\n        def provider = BC_PROVIDER\n        for (def factory : factories) {\n            def clazz = factory.getInstanceClass()\n            try {\n                factory.get('foo', provider)\n            } catch (SecurityException expected) {\n                if (clazz == Signature || clazz == Mac) {\n                    assertTrue expected instanceof SignatureException\n                }\n                String prefix = \"Unable to obtain 'foo' ${clazz.getSimpleName()} instance \" +\n                        \"from specified '${provider.toString()}' Provider: \"\n                assertTrue expected.getMessage().startsWith(prefix)\n            }\n        }\n    }\n\n    @Test\n    void testCallbackThrowsSecurityException() {\n        // tests that any callback that throws a SecurityException doesn't need to be wrapped\n        String msg = 'fubar'\n        def template = new JcaTemplate('AES/CBC/PKCS5Padding')\n        try {\n            template.withCipher(new CheckedFunction<Cipher, byte[]>() {\n                @Override\n                byte[] apply(Cipher cipher) throws Exception {\n                    throw new SecurityException(msg)\n                }\n            })\n        } catch (SecurityException ex) {\n            assertEquals msg, ex.getMessage()\n        }\n    }\n\n    @Test\n    void testNewCipherWithExplicitProvider() {\n        Provider provider = SUN_PROVIDER\n        def template = new JcaTemplate('AES/CBC/PKCS5Padding', provider)\n        template.withCipher(new CheckedFunction<Cipher, byte[]>() {\n            @Override\n            byte[] apply(Cipher cipher) throws Exception {\n                assertNotNull cipher\n                assertSame provider, cipher.getProvider()\n                return new byte[0]\n            }\n        })\n    }\n\n    @Test\n    void testInstanceFactoryFallbackFailureRetainsOriginalException() {\n        String alg = 'foo'\n        NoSuchAlgorithmException ex = new NoSuchAlgorithmException('foo')\n        def factory = new JcaTemplate.JcaInstanceFactory<Cipher>(Cipher.class) {\n            @Override\n            protected Cipher doGet(String jcaName, Provider provider) throws Exception {\n                throw ex\n            }\n\n            @Override\n            protected Provider findBouncyCastle() {\n                return null\n            }\n        }\n\n        try {\n            factory.get(alg, null)\n            fail()\n        } catch (SecurityException se) {\n            assertSame ex, se.getCause()\n            String msg = \"Unable to obtain '$alg' Cipher instance from default JCA Provider: $alg\"\n            assertEquals msg, se.getMessage()\n        }\n    }\n\n    @Test\n    void testWrapWithDefaultJcaProviderAndFallbackProvider() {\n        JcaTemplate.FACTORIES.each {\n            Provider fallback = TestKeys.BC\n            String jcaName = 'foo'\n            NoSuchAlgorithmException nsa = new NoSuchAlgorithmException(\"doesn't exist\")\n            Exception out = ((JcaTemplate.JcaInstanceFactory) it).wrap(nsa, jcaName, null, fallback)\n            assertTrue out instanceof SecurityException\n            String msg = \"Unable to obtain '${jcaName}' ${it.getId()} instance from default JCA Provider or fallback \" +\n                    \"'${fallback.toString()}' Provider: doesn't exist\"\n            assertEquals msg, out.getMessage()\n        }\n    }\n\n    @Test\n    void testFallbackWithBouncyCastle() {\n        def template = new JcaTemplate('foo')\n        try {\n            template.generateX509Certificate(Bytes.random(32))\n        } catch (SecurityException expected) {\n            String prefix = \"Unable to obtain 'foo' CertificateFactory instance from default JCA Provider: \"\n            assertTrue expected.getMessage().startsWith(prefix)\n            assertTrue expected.getCause() instanceof CertificateException\n        }\n    }\n\n    @Test\n    void testFallbackWithoutBouncyCastle() {\n        def template = new JcaTemplate('foo') {\n            @Override\n            protected Provider findBouncyCastle() {\n                return null\n            }\n        }\n        try {\n            template.generateX509Certificate(Bytes.random(32))\n        } catch (SecurityException expected) {\n            String prefix = \"Unable to obtain 'foo' CertificateFactory instance from default JCA Provider: \"\n            assertTrue expected.getMessage().startsWith(prefix)\n            assertTrue expected.getCause() instanceof CertificateException\n        }\n    }\n\n    static InvalidKeySpecException jdk8213363BugEx(String msg) {\n        // mock up JDK 11 bug behavior:\n        String className = 'sun.security.ec.XDHKeyFactory'\n        String methodName = 'engineGeneratePrivate'\n        def ste = new StackTraceElement(className, methodName, null, 0)\n        StackTraceElement[] stes = new StackTraceElement[1]\n        stes[0] = ste\n        def cause = new InvalidKeyException(msg)\n        def ex = new InvalidKeySpecException(cause) {\n            @Override\n            StackTraceElement[] getStackTrace() {\n                return stes\n            }\n        }\n        return ex\n    }\n\n    @Test\n    void testJdk8213363Bug() {\n        for (def bundle in [TestKeys.X25519, TestKeys.X448]) {\n            def privateKey = bundle.pair.private\n            byte[] d = bundle.alg.getKeyMaterial(privateKey)\n            byte[] prefix = new byte[2]; prefix[0] = (byte) 0x04; prefix[1] = (byte) d.length\n            byte[] pkcs8d = Bytes.concat(prefix, d)\n            int callCount = 0\n            def ex = jdk8213363BugEx(\"key length must be ${d.length}\")\n            def template = new Jdk8213363JcaTemplate(bundle.alg.id) {\n                @Override\n                protected PrivateKey generatePrivate(KeyFactory factory, KeySpec spec) throws InvalidKeySpecException {\n                    if (callCount == 0) { // simulate first attempt throwing an exception\n                        callCount++\n                        throw ex\n                    }\n                    // otherwise 2nd call due to fallback logic, simulate a successful call:\n                    return privateKey\n                }\n            }\n            assertSame privateKey, template.generatePrivate(new PKCS8EncodedKeySpec(pkcs8d))\n        }\n    }\n\n    @Test\n    void testGeneratePrivateRespecWithoutPkcs8() {\n        byte[] invalid = Bytes.random(456)\n        def template = new JcaTemplate('X448')\n        try {\n            template.generatePrivate(new X509EncodedKeySpec(invalid))\n            fail()\n        } catch (SecurityException expected) {\n            boolean jdk11OrLater = Classes.isAvailable('java.security.interfaces.XECPrivateKey')\n            String msg = 'KeyFactory callback execution failed: key spec not recognized'\n            if (jdk11OrLater) {\n                msg = 'KeyFactory callback execution failed: Only PKCS8EncodedKeySpec and XECPrivateKeySpec supported'\n            }\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testGeneratePrivateRespecTooSmall() {\n        byte[] invalid = Bytes.random(16)\n        def ex = jdk8213363BugEx(\"key length must be ${invalid.length}\")\n        def template = new Jdk8213363JcaTemplate('X25519') {\n            @Override\n            protected PrivateKey generatePrivate(KeyFactory factory, KeySpec spec) throws InvalidKeySpecException {\n                throw ex\n            }\n        }\n        try {\n            template.generatePrivate(new PKCS8EncodedKeySpec(invalid))\n            fail()\n        } catch (SecurityException expected) {\n            String msg = \"KeyFactory callback execution failed: java.security.InvalidKeyException: \" +\n                    \"key length must be ${invalid.length}\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testGeneratePrivateRespecTooLarge() {\n        byte[] invalid = Bytes.random(50)\n        def ex = jdk8213363BugEx(\"key length must be ${invalid.length}\")\n        def template = new Jdk8213363JcaTemplate('X448') {\n            @Override\n            protected PrivateKey generatePrivate(KeyFactory factory, KeySpec spec) throws InvalidKeySpecException {\n                throw ex\n            }\n        }\n        try {\n            template.generatePrivate(new PKCS8EncodedKeySpec(invalid))\n            fail()\n        } catch (SecurityException expected) {\n            String msg = \"KeyFactory callback execution failed: java.security.InvalidKeyException: \" +\n                    \"key length must be ${invalid.length}\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testGetJdk8213363BugExpectedSizeNoExMsg() {\n        InvalidKeyException ex = new InvalidKeyException()\n        def template = new JcaTemplate('X448')\n        assertEquals(-1, template.getJdk8213363BugExpectedSize(ex))\n    }\n\n    @Test\n    void testGetJdk8213363BugExpectedSizeExMsgDoesntMatch() {\n        InvalidKeyException ex = new InvalidKeyException('not what is expected')\n        def template = new JcaTemplate('X448')\n        assertEquals(-1, template.getJdk8213363BugExpectedSize(ex))\n    }\n\n    @Test\n    void testGetJdk8213363BugExpectedSizeExMsgDoesntContainNumber() {\n        InvalidKeyException ex = new InvalidKeyException('key length must be foo')\n        def template = new JcaTemplate('X448')\n        assertEquals(-1, template.getJdk8213363BugExpectedSize(ex))\n    }\n\n    @Test\n    void testRespecIfNecessaryWithoutPkcs8KeySpec() {\n        def spec = new X509EncodedKeySpec(Bytes.random(32))\n        def template = new JcaTemplate('X448')\n        assertNull template.respecIfNecessary(null, spec)\n    }\n\n    @Test\n    void testRespecIfNecessaryNotJdk8213363Bug() {\n        def ex = new InvalidKeySpecException('foo')\n        def template = new JcaTemplate('X448')\n        assertNull template.respecIfNecessary(ex, new PKCS8EncodedKeySpec(Bytes.random(32)))\n    }\n\n    @Test\n    void testIsJdk11() {\n        // determine which JDK the test is being run on in CI:\n        boolean testMachineIsJdk11 = System.getProperty('java.version').startsWith('11')\n        def template = new JcaTemplate('X448')\n        if (testMachineIsJdk11) {\n            assertTrue template.isJdk11()\n        } else {\n            assertFalse template.isJdk11()\n        }\n    }\n\n    @Test\n    void testCallbackThrowsException() {\n        def ex = new Exception(\"testing\")\n        def template = new JcaTemplate('AES/CBC/PKCS5Padding')\n        try {\n            template.withCipher(new CheckedFunction<Cipher, byte[]>() {\n                @Override\n                byte[] apply(Cipher cipher) throws Exception {\n                    throw ex\n                }\n            })\n        } catch (SecurityException e) {\n            assertEquals 'Cipher callback execution failed: testing', e.getMessage()\n            assertSame ex, e.getCause()\n        }\n    }\n\n    @Test\n    void testWithCertificateFactory() {\n        def template = new JcaTemplate('X.509')\n        X509Certificate expected = TestKeys.RS256.cert\n        X509Certificate cert = template.withCertificateFactory(new CheckedFunction<CertificateFactory, X509Certificate>() {\n            @Override\n            X509Certificate apply(CertificateFactory certificateFactory) throws Exception {\n                (X509Certificate) certificateFactory.generateCertificate(Streams.of(expected.getEncoded()))\n            }\n        })\n        assertEquals expected, cert\n    }\n\n    private static class Jdk8213363JcaTemplate extends JcaTemplate {\n        Jdk8213363JcaTemplate(String jcaName) {\n            super(jcaName)\n        }\n\n        @Override\n        protected boolean isJdk11() {\n            return true\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass JwkConverterTest {\n\n    static String typeString(def target) {\n        return JwkConverter.typeString(target)\n    }\n\n    @Test\n    void testJwkClassTypeString() {\n        assertEquals 'JWK', typeString(Jwk.class)\n    }\n\n    @Test\n    void testSecretJwkClassTypeString() {\n        assertEquals 'Secret JWK', typeString(SecretJwk.class)\n    }\n\n    @Test\n    void testSecretJwkTypeString() {\n        def jwk = Jwks.builder().key(TestKeys.HS256).build()\n        assertEquals 'Secret JWK', typeString(jwk)\n    }\n\n    @Test\n    void testPublicJwkClassTypeString() {\n        assertEquals 'Public JWK', typeString(PublicJwk.class)\n    }\n\n    @Test\n    void testEcPublicJwkClassTypeString() {\n        assertEquals 'EC Public JWK', typeString(EcPublicJwk.class)\n    }\n\n    @Test\n    void testEdPublicJwkClassTypeString() {\n        assertEquals 'Edwards Curve Public JWK', typeString(OctetPublicJwk.class)\n    }\n\n    @Test\n    void testRsaPublicJwkClassTypeString() {\n        assertEquals 'RSA Public JWK', typeString(RsaPublicJwk.class)\n    }\n\n    @Test\n    void testPrivateJwkClassTypeString() {\n        assertEquals 'Private JWK', typeString(PrivateJwk.class)\n    }\n\n    @Test\n    void testEcPrivateJwkClassTypeString() {\n        assertEquals 'EC Private JWK', typeString(EcPrivateJwk.class)\n    }\n\n    @Test\n    void testEdPrivateJwkClassTypeString() {\n        assertEquals 'Edwards Curve Private JWK', typeString(OctetPrivateJwk.class)\n    }\n\n    @Test\n    void testRsaPrivateJwkClassTypeString() {\n        assertEquals 'RSA Private JWK', typeString(RsaPrivateJwk.class)\n    }\n\n    @Test\n    void testPrivateJwk() {\n        JwkConverter<PrivateJwk> converter = new JwkConverter<>(PrivateJwk.class)\n        def jwk = Jwks.builder().key(TestKeys.HS256).build()\n        try {\n            converter.applyFrom(jwk)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Value must be a Private JWK, not a Secret JWK.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testRsaPrivateJwk() {\n        JwkConverter<RsaPublicJwk> converter = new JwkConverter<>(RsaPublicJwk.class)\n        def jwk = Jwks.builder().key(TestKeys.HS256).build()\n        try {\n            converter.applyFrom(jwk)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String msg = \"Value must be an RSA Public JWK, not a Secret JWK.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkSerializationTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.gson.io.GsonDeserializer\nimport io.jsonwebtoken.gson.io.GsonSerializer\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.io.Serializer\nimport io.jsonwebtoken.jackson.io.JacksonDeserializer\nimport io.jsonwebtoken.jackson.io.JacksonSerializer\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.lang.Supplier\nimport io.jsonwebtoken.orgjson.io.OrgJsonDeserializer\nimport io.jsonwebtoken.orgjson.io.OrgJsonSerializer\nimport io.jsonwebtoken.security.Jwk\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport java.security.Key\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertTrue\n\n/**\n * Asserts that serializing and deserializing private or secret key values works as expected without\n * exposing raw strings in the JWKs themselves (should be wrapped with RedactedSupplier instances) for toString safety.\n */\nclass JwkSerializationTest {\n\n    static String serialize(Serializer ser, def value) {\n        def out = new ByteArrayOutputStream()\n        ser.serialize(value, out)\n        return Strings.utf8(out.toByteArray())\n    }\n\n    static Map<String, ?> deserialize(Deserializer des, String value) {\n        return des.deserialize(new StringReader(value)) as Map<String, ?>\n    }\n\n    @Test\n    void testJacksonSecretJwk() {\n        testSecretJwk(new JacksonSerializer(), new JacksonDeserializer())\n    }\n\n    @Test\n    void testJacksonPrivateEcJwk() {\n        testPrivateEcJwk(new JacksonSerializer(), new JacksonDeserializer())\n    }\n\n    @Test\n    void testJacksonPrivateRsaJwk() {\n        testPrivateRsaJwk(new JacksonSerializer(), new JacksonDeserializer())\n    }\n\n    @Test\n    void testGsonSecretJwk() {\n        testSecretJwk(new GsonSerializer(), new GsonDeserializer())\n    }\n\n    @Test\n    void testGsonPrivateEcJwk() {\n        testPrivateEcJwk(new GsonSerializer(), new GsonDeserializer())\n    }\n\n    @Test\n    void testGsonPrivateRsaJwk() {\n        testPrivateRsaJwk(new GsonSerializer(), new GsonDeserializer())\n    }\n\n    @Test\n    void testOrgJsonSecretJwk() {\n        testSecretJwk(new OrgJsonSerializer(), new OrgJsonDeserializer())\n    }\n\n    @Test\n    void testOrgJsonPrivateEcJwk() {\n        testPrivateEcJwk(new OrgJsonSerializer(), new OrgJsonDeserializer())\n    }\n\n    @Test\n    void testOrgJsonPrivateRsaJwk() {\n        testPrivateRsaJwk(new OrgJsonSerializer(), new OrgJsonDeserializer())\n    }\n\n    static void testSecretJwk(Serializer ser, Deserializer des) {\n\n        def key = TestKeys.NA256\n        def jwk = Jwks.builder().key(key).id('id').build()\n        assertWrapped(jwk, ['k'])\n\n        // Ensure no Groovy or Java toString prints out secret values:\n        assertEquals '[kid:id, kty:oct, k:<redacted>]', \"$jwk\" as String // groovy gstring\n        assertEquals '{kid=id, kty=oct, k=<redacted>}', jwk.toString() // java toString\n\n        //but serialization prints the real value:\n        String json = serialize(ser, jwk)\n        // assert substrings here because JSON order is not guaranteed:\n        assertTrue json.contains('\"kid\":\"id\"')\n        assertTrue json.contains('\"kty\":\"oct\"')\n        assertTrue json.contains(\"\\\"k\\\":\\\"${jwk.k.get()}\\\"\" as String)\n\n        //now ensure it deserializes back to a JWK:\n        def map = deserialize(des, json)\n        def jwk2 = Jwks.builder().add(map).build()\n        assertTrue jwk.k instanceof Supplier\n        assertEquals jwk, jwk2\n        assertEquals jwk.k, jwk2.k\n        assertEquals jwk.k.get(), jwk2.k.get()\n    }\n\n    static void testPrivateEcJwk(Serializer ser, Deserializer des) {\n\n        def jwk = Jwks.builder().ecKeyPair(TestKeys.ES256.pair).id('id').build()\n        assertWrapped(jwk, ['d'])\n\n        // Ensure no Groovy or Java toString prints out secret values:\n        assertEquals '[kid:id, kty:EC, crv:P-256, x:ZWF7HQuzPoW_HarfomiU-HCMELJ486IzskTXL5fwuy4, y:Hf3WL_YAGj1XCSa5HSIAFsItY-SQNjRb1TdKQFEb3oU, d:<redacted>]', \"$jwk\" as String\n        // groovy gstring\n        assertEquals '{kid=id, kty=EC, crv=P-256, x=ZWF7HQuzPoW_HarfomiU-HCMELJ486IzskTXL5fwuy4, y=Hf3WL_YAGj1XCSa5HSIAFsItY-SQNjRb1TdKQFEb3oU, d=<redacted>}', jwk.toString()\n        // java toString\n\n        //but serialization prints the real value:\n        String json = serialize(ser, jwk)\n        // assert substrings here because JSON order is not guaranteed:\n        assertTrue json.contains('\"kid\":\"id\"')\n        assertTrue json.contains('\"kty\":\"EC\"')\n        assertTrue json.contains('\"crv\":\"P-256\"')\n        assertTrue json.contains('\"x\":\"ZWF7HQuzPoW_HarfomiU-HCMELJ486IzskTXL5fwuy4\"')\n        assertTrue json.contains('\"y\":\"Hf3WL_YAGj1XCSa5HSIAFsItY-SQNjRb1TdKQFEb3oU\"')\n        assertTrue json.contains(\"\\\"d\\\":\\\"${jwk.d.get()}\\\"\" as String)\n\n        //now ensure it deserializes back to a JWK:\n        def map = deserialize(des, json)\n        def jwk2 = Jwks.builder().add(map).build()\n        assertTrue jwk.d instanceof Supplier\n        assertEquals jwk, jwk2\n        assertEquals jwk.d, jwk2.d\n        assertEquals jwk.d.get(), jwk2.d.get()\n    }\n\n    private static assertWrapped(Map<String, ?> map, List<String> keys) {\n        for (String key : keys) {\n            def value = map.get(key)\n            assertTrue value instanceof Supplier\n            value = ((Supplier<?>) value).get()\n            assertTrue value instanceof String\n        }\n    }\n\n    private static assertEquals(Jwk<? extends Key> jwk1, Jwk<? extends Key> jwk2, List<String> keys) {\n        assertEquals jwk1, jwk2\n        for (String key : keys) {\n            assertTrue jwk1.get(key) instanceof Supplier\n            assertTrue jwk2.get(key) instanceof Supplier\n            assertEquals jwk1.get(key), jwk2.get(key)\n            assertEquals jwk1.get(key).get(), jwk2.get(key).get()\n        }\n    }\n\n    static void testPrivateRsaJwk(Serializer ser, Deserializer des) {\n\n        def jwk = Jwks.builder().rsaKeyPair(TestKeys.RS256.pair).id('id').build()\n        def privateFieldNames = ['d', 'p', 'q', 'dp', 'dq', 'qi']\n        assertWrapped(jwk, privateFieldNames)\n\n        // Ensure no Groovy or Java toString prints out secret values:\n        assertEquals '[kid:id, kty:RSA, n:vPYf1VSy58i6ic93goenzF5UO9oLxyiTSF64lGFUJ6_MBDydAvY9PS76ymvhUcSrsDUHgb0arsp6MDXOfZxYHn2C7o39n8-bQ7yS4hQm6kkl8KB5OiOkJFkFjEHrwnqykXygx1VFpcVpbBvxDn640ODEScWyoUUPd4sOK-esTt4D9-q0PXsXzfRT4eOrnpXHJTan_KK_a-UYmfWPr-xIEPUxnLPCD68mIHoSPAaJiv37SkAWHJ9-fm_DfnYTwTi0rxe2FRQ1-vkOxe6C2-n1ebsqCZPKr0J_2MfwqP0raxLfyGicxM5ee5RSTTRMCA4UyX5dubZvh2pLoaS8PCZajw, e:AQAB, d:<redacted>, p:<redacted>, q:<redacted>, dp:<redacted>, dq:<redacted>, qi:<redacted>]', \"$jwk\" as String\n        // groovy gstring\n        assertEquals '{kid=id, kty=RSA, n=vPYf1VSy58i6ic93goenzF5UO9oLxyiTSF64lGFUJ6_MBDydAvY9PS76ymvhUcSrsDUHgb0arsp6MDXOfZxYHn2C7o39n8-bQ7yS4hQm6kkl8KB5OiOkJFkFjEHrwnqykXygx1VFpcVpbBvxDn640ODEScWyoUUPd4sOK-esTt4D9-q0PXsXzfRT4eOrnpXHJTan_KK_a-UYmfWPr-xIEPUxnLPCD68mIHoSPAaJiv37SkAWHJ9-fm_DfnYTwTi0rxe2FRQ1-vkOxe6C2-n1ebsqCZPKr0J_2MfwqP0raxLfyGicxM5ee5RSTTRMCA4UyX5dubZvh2pLoaS8PCZajw, e=AQAB, d=<redacted>, p=<redacted>, q=<redacted>, dp=<redacted>, dq=<redacted>, qi=<redacted>}', jwk.toString()\n        // java toString\n\n        //but serialization prints the real value:\n        String json = serialize(ser, jwk)\n        // assert substrings here because JSON order is not guaranteed:\n        assertTrue json.contains('\"kid\":\"id\"')\n        assertTrue json.contains('\"kty\":\"RSA\"')\n        assertTrue json.contains('\"e\":\"AQAB\"')\n        assertTrue json.contains(\"\\\"n\\\":\\\"${jwk.n}\\\"\" as String) //public property, not wrapped\n        assertTrue json.contains(\"\\\"d\\\":\\\"${jwk.d.get()}\\\"\" as String) // all remaining should be wrapped\n        assertTrue json.contains(\"\\\"p\\\":\\\"${jwk.p.get()}\\\"\" as String)\n        assertTrue json.contains(\"\\\"q\\\":\\\"${jwk.q.get()}\\\"\" as String)\n        assertTrue json.contains(\"\\\"dp\\\":\\\"${jwk.dp.get()}\\\"\" as String)\n        assertTrue json.contains(\"\\\"dq\\\":\\\"${jwk.dq.get()}\\\"\" as String)\n        assertTrue json.contains(\"\\\"qi\\\":\\\"${jwk.qi.get()}\\\"\" as String)\n\n        //now ensure it deserializes back to a JWK:\n        def map = deserialize(des, json)\n        def jwk2 = Jwks.builder().add(map).build()\n        assertEquals(jwk, jwk2, privateFieldNames)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkSetConverterTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.JwkSet\nimport io.jsonwebtoken.security.MalformedKeySetException\nimport io.jsonwebtoken.security.SecretJwk\nimport io.jsonwebtoken.security.UnsupportedKeyException\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass JwkSetConverterTest {\n\n    private JwkSetConverter converter\n\n    @Before\n    void setUp() {\n        converter = new JwkSetConverter()\n    }\n\n    private void assertIllegal(Object input, String msg) {\n        try {\n            converter.applyFrom(input)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            assertEquals msg, expected.message\n        }\n    }\n\n    private void assertMalformed(Object input, String msg) {\n        try {\n            converter.applyFrom(input)\n            fail()\n        } catch (MalformedKeySetException expected) {\n            assertEquals msg, expected.message\n        }\n    }\n\n    static void assertEmpty(JwkSet result) {\n        // bad input ignored by default per https://www.rfc-editor.org/rfc/rfc7517.html#section-5 :\n        assertEquals 0, result.size()\n        assertEquals 0, result.getKeys().size()\n        assertFalse result.getKeys().iterator().hasNext()\n        assertFalse result.iterator().hasNext()\n    }\n\n    @Test\n    void testApplyToNull() {\n        assertNull converter.applyTo(null)\n    }\n\n    @Test\n    void testApplyToNonNull() {\n        def set = new DefaultJwkSet(DefaultJwkSet.KEYS, Collections.emptyMap())\n        assertSame set, converter.applyTo(set)\n    }\n\n    @Test\n    void testNull() {\n        assertIllegal null, \"Value cannot be null.\"\n    }\n\n    @Test\n    void testNonMap() {\n        def value = 42\n        String msg = \"Value must be a Map<String,?> (JSON Object). Type found: ${value.class.name}.\"\n        assertIllegal 42, msg\n    }\n\n    @Test\n    void testEmptyMap() {\n        assertMalformed [:], \"Missing required ${DefaultJwkSet.KEYS} parameter.\"\n    }\n\n    @Test\n    void testKeysMissing() {\n        def m = ['hello': 'world']\n        assertMalformed m, \"Missing required ${DefaultJwkSet.KEYS} parameter.\"\n    }\n\n    @Test\n    void testKeysNull() {\n        def m = [keys: null]\n        assertMalformed m, \"JWK Set ${DefaultJwkSet.KEYS} value cannot be null.\"\n    }\n\n    @Test\n    void testKeysNonCollection() {\n        def val = 42\n        def m = [keys: val]\n        String msg = \"JWK Set ${DefaultJwkSet.KEYS} value must be a Collection (JSON Array). \" +\n                \"Type found: ${val.class.name}\"\n        assertMalformed m, msg\n    }\n\n    @Test\n    void testKeysEmpty() {\n        def m = [keys: []]\n        assertMalformed m, \"JWK Set ${DefaultJwkSet.KEYS} collection cannot be empty.\"\n    }\n\n    @Test\n    void testMapWithNullKey() {\n        def m = new LinkedHashMap()\n        m.put(null, 'foo')\n        m.put('keys', [42])\n        assertIllegal m, \"JWK Set map key cannot be null.\"\n    }\n\n    @Test\n    void testMapWithNonStringKey() {\n        def key = 42\n        def m = new LinkedHashMap()\n        m.put(key, 42)\n        m.put('keys', [42])\n        String msg = \"JWK Set map keys must be Strings. Encountered key '${key}' of type ${key.class.name}\"\n        assertIllegal m, msg\n\n    }\n\n    @Test\n    void testJwkNull() {\n        def m = [keys: [null]]\n        assertEmpty converter.applyFrom(m)\n    }\n\n    @Test\n    void testJwkNullNotIgnored() {\n        converter = new JwkSetConverter(false)\n        def m = [keys: [null]]\n        assertMalformed m, \"JWK Set keys[0]: JWK cannot be null.\"\n    }\n\n    @Test\n    void testJwkNotAJSONObject() {\n        def val = 42\n        def m = [keys: [val]]\n        assertEmpty converter.applyFrom(m)\n    }\n\n    @Test\n    void testJwkNotAJSONObjectNotIgnored() {\n        converter = new JwkSetConverter(false)\n        def val = 42\n        def m = [keys: [val]]\n        String msg = \"JWK Set keys[0]: JWK must be a Map<String,?> (JSON Object). Type found: ${val.class.name}.\"\n        assertMalformed m, msg\n    }\n\n    @Test\n    void testJwkEmpty() {\n        def val = [:]\n        def m = [keys: [val]]\n        assertEmpty converter.applyFrom(m)\n    }\n\n    @Test\n    void testJwkEmptyNotIgnored() {\n        converter = new JwkSetConverter(false)\n        def val = [:]\n        def m = [keys: [val]]\n        String msg = \"JWK Set keys[0]: JWK is missing required ${AbstractJwk.KTY} parameter.\"\n        assertMalformed m, msg\n    }\n\n    @Test\n    void testJwkKtyNonString() {\n        def val = 42\n        def jwk = [kty: val]\n        def m = [keys: [jwk]]\n        // bad input ignored by default per https://www.rfc-editor.org/rfc/rfc7517.html#section-5 :\n        assertEmpty converter.applyFrom(m)\n    }\n\n    @Test\n    void testJwkKtyNonStringNotIgnored() {\n        converter = new JwkSetConverter(false)\n        def val = 42\n        def jwk = [kty: val]\n        def m = [keys: [jwk]]\n        String msg = \"JWK Set keys[0]: JWK ${AbstractJwk.KTY} value must be a String. Type found: ${val.class.name}\"\n        assertMalformed m, msg\n    }\n\n    @Test\n    void testJwkKtyEmpty() {\n        def val = ''\n        def jwk = [kty: val]\n        def m = [keys: [jwk]]\n        assertEmpty converter.applyFrom(m)\n    }\n\n    @Test\n    void testJwkKtyEmptyNotIgnored() {\n        converter = new JwkSetConverter(false)\n        def val = ''\n        def jwk = [kty: val]\n        def m = [keys: [jwk]]\n        String msg = \"JWK Set keys[0]: JWK ${AbstractJwk.KTY} value cannot be empty.\"\n        assertMalformed m, msg\n    }\n\n    @Test\n    void testJwkMissingKeyMaterial() {\n        def jwk = [kty: 'oct'] // missing 'k' parameter\n        def m = [keys: [jwk]]\n        assertEmpty converter.applyFrom(m)\n    }\n\n    @Test\n    void testJwkMissingKeyMaterialNotIgnored() {\n        converter = new JwkSetConverter(false)\n        def jwk = [kty: 'oct'] // missing 'k' parameter\n        def m = [keys: [jwk]]\n        String msg = \"JWK Set keys[0]: Secret JWK is missing required ${DefaultSecretJwk.K} value.\"\n        assertMalformed m, msg\n    }\n\n    /**\n     * Asserts that our exception message shows which key in the keys array failed.\n     */\n    @Test\n    void testExceptionMessageIncrements() {\n        converter = new JwkSetConverter(false)\n        def k = Encoders.BASE64URL.encode(TestKeys.HS256.getEncoded())\n        def good = [kty: 'oct', k: k]\n        def bad = [kty: 'oct']\n        def m = [keys: [good, bad]]\n        String msg = \"JWK Set keys[1]: Secret JWK is missing required ${DefaultSecretJwk.K} value.\"\n        assertMalformed m, msg\n    }\n\n    @Test\n    void testUnsupportedJwk() {\n        def m = [keys: [[kty: 'foo']]]\n        assertEmpty converter.applyFrom(m)\n    }\n\n    @Test\n    void testUnsupportedNotIgnored() {\n        converter = new JwkSetConverter(false)\n        def m = [keys: [[kty: 'foo']]]\n        try {\n            converter.applyFrom(m)\n            fail()\n        } catch (UnsupportedKeyException expected) {\n            String msg = \"JWK Set keys[0]: Unable to create JWK for unrecognized kty value 'foo': there is no known \" +\n                    \"JWK Factory capable of creating JWKs for this key type.\"\n            assertEquals msg, expected.message\n        }\n    }\n\n    /**\n     * Asserts that our exception message shows which key in the keys array failed.\n     */\n    @Test\n    void testJwkSucceeds() {\n        def k = Encoders.BASE64URL.encode(TestKeys.HS256.getEncoded())\n        def good = [kty: 'oct', k: k]\n        def m = [keys: [good]]\n        def jwkSet = converter.applyFrom(m)\n        assertNotNull jwkSet\n        assertNotNull jwkSet.getKeys()\n        assertEquals 1, jwkSet.getKeys().size()\n        assertTrue jwkSet.getKeys().iterator().next() instanceof SecretJwk\n    }\n\n    @Test\n    void testApplyFromExistingJwkSet() {\n        def k = Encoders.BASE64URL.encode(TestKeys.HS256.getEncoded())\n        def good = [kty: 'oct', k: k]\n        def m = [keys: [good]]\n        def jwkSet = converter.applyFrom(m)\n        assertSame jwkSet, converter.applyFrom(jwkSet)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/JwkThumbprintsTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.RfcTests\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.security.HashAlgorithm\nimport io.jsonwebtoken.security.JwkThumbprint\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\n\nimport static io.jsonwebtoken.impl.security.DefaultHashAlgorithm.SHA1\nimport static org.junit.Assert.assertEquals\n\nclass JwkThumbprintsTest {\n\n    static final HashAlgorithm SHA256 = Jwks.HASH.@SHA256\n\n    static byte[] digest(String json, HashAlgorithm alg) {\n        def payload = Streams.of(json)\n        def req = new DefaultRequest(payload, null, null)\n        return alg.digest(req)\n    }\n\n    static JwkThumbprint thumbprint(String json, HashAlgorithm alg) {\n        return new DefaultJwkThumbprint(digest(json, alg), alg)\n    }\n\n    @Test\n    void testSecretJwks() {\n        TestKeys.SECRET.each { SecretKey key ->\n            def jwk = Jwks.builder().key((SecretKey) key).idFromThumbprint().build()\n            def json = RfcTests.stripws(\"\"\"\n            {\"k\":\"${jwk.get('k').get()}\",\"kty\":\"oct\"}\n            \"\"\")\n            def s256t = thumbprint(json, SHA256)\n            assertEquals s256t, jwk.thumbprint()\n            assertEquals thumbprint(json, SHA1), jwk.thumbprint(SHA1)\n            assertEquals s256t.toString(), jwk.getId()\n        }\n    }\n\n    @Test\n    void testRsaKeyPair() {\n        def pair = TestKeys.RS256.pair\n        def privJwk = Jwks.builder().rsaKeyPair(pair).idFromThumbprint().build()\n        def pubJwk = privJwk.toPublicJwk()\n        def json = RfcTests.stripws(\"\"\"\n        {\"e\":\"${pubJwk.get('e')}\",\"kty\":\"RSA\",\"n\":\"${pubJwk.get('n')}\"}\n        \"\"\")\n\n        def s256t = thumbprint(json, SHA256)\n\n        assertEquals s256t, pubJwk.thumbprint()\n        assertEquals thumbprint(json, SHA1), pubJwk.thumbprint(SHA1)\n        assertEquals s256t.toString(), pubJwk.getId()\n\n        assertEquals thumbprint(json, SHA256), privJwk.thumbprint()\n        // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        assertEquals thumbprint(json, SHA1), privJwk.thumbprint(SHA1)\n        // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        assertEquals s256t.toString(), privJwk.getId()\n    }\n\n    @Test\n    void testEcKeyPair() {\n        def pair = TestKeys.ES256.pair\n        def privJwk = Jwks.builder().ecKeyPair(pair).idFromThumbprint().build()\n        def pubJwk = privJwk.toPublicJwk()\n        def json = RfcTests.stripws(\"\"\"\n        {\"crv\":\"${pubJwk.get('crv')}\",\"kty\":\"EC\",\"x\":\"${pubJwk.get('x')}\",\"y\":\"${pubJwk.get('y')}\"}\n        \"\"\")\n\n        def s256t = thumbprint(json, SHA256)\n\n        assertEquals s256t, pubJwk.thumbprint()\n        assertEquals thumbprint(json, SHA1), pubJwk.thumbprint(SHA1)\n        assertEquals s256t.toString(), pubJwk.getId()\n\n        assertEquals thumbprint(json, SHA256), privJwk.thumbprint()\n        // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        assertEquals thumbprint(json, SHA1), privJwk.thumbprint(SHA1)\n        // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        assertEquals s256t.toString(), privJwk.getId()\n    }\n\n    @Test\n    void testEdECKeyPair() {\n        def pair = TestKeys.Ed25519.pair\n        def privJwk = Jwks.builder().octetKeyPair(pair).idFromThumbprint().build()\n        def pubJwk = privJwk.toPublicJwk()\n        def json = RfcTests.stripws(\"\"\"\n        {\"crv\":\"${pubJwk.get('crv')}\",\"kty\":\"OKP\",\"x\":\"${pubJwk.get('x')}\"}\n        \"\"\")\n\n        def s256t = thumbprint(json, SHA256)\n\n        assertEquals s256t, pubJwk.thumbprint()\n        assertEquals thumbprint(json, SHA1), pubJwk.thumbprint(SHA1)\n        assertEquals s256t.toString(), pubJwk.getId()\n\n        assertEquals thumbprint(json, SHA256), privJwk.thumbprint()\n        // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        assertEquals thumbprint(json, SHA1), privJwk.thumbprint(SHA1)\n        // https://www.rfc-editor.org/rfc/rfc7638#section-3.2.1\n        assertEquals s256t.toString(), privJwk.getId()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/JwksTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.lang.Converters\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.security.MessageDigest\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.SecureRandom\nimport java.security.cert.X509Certificate\nimport java.security.interfaces.ECKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAKey\nimport java.security.interfaces.RSAPublicKey\nimport java.security.spec.ECParameterSpec\nimport java.security.spec.ECPoint\n\nimport static org.junit.Assert.*\n\nclass JwksTest {\n\n    private static final SecretKey SKEY = TestKeys.NA256\n    private static final java.security.KeyPair EC_PAIR = Jwts.SIG.ES256.keyPair().build()\n\n    private static String srandom() {\n        byte[] random = new byte[16]\n        Randoms.secureRandom().nextBytes(random)\n        return Encoders.BASE64URL.encode(random)\n    }\n\n    static void testProperty(String name, String id, def val, def expectedFieldValue = val) {\n        String cap = \"${name.capitalize()}\"\n        def key = name == 'publicKeyUse' || name == 'x509Chain' ? EC_PAIR.public : SKEY\n\n        //test non-null value:\n        //noinspection GroovyAssignabilityCheck\n        def builder = Jwks.builder().key(key).delete('alg') // delete alg put there by SecretKeyBuilder\n        builder.\"$name\"(val)\n        def jwk = builder.build()\n        assertEquals val, jwk.\"get${cap}\"()\n        assertEquals expectedFieldValue, jwk.\"${id}\"\n\n        //test null value:\n        builder = Jwks.builder().key(key).delete('alg')\n        try {\n            builder.\"$name\"(null)\n            fail(\"IAE should have been thrown\")\n        } catch (IllegalArgumentException ignored) {\n        }\n        jwk = builder.build()\n        assertNull jwk.\"get${cap}\"()\n        assertNull jwk.\"$id\"\n        assertFalse jwk.containsKey(id)\n\n        //test empty string value\n        builder = Jwks.builder().key(key).delete('alg')\n        if (val instanceof String) {\n            try {\n                builder.\"$name\"('   ' as String)\n                fail(\"IAE should have been thrown\")\n            } catch (IllegalArgumentException ignored) {\n            }\n            jwk = builder.build()\n            assertNull jwk.\"get${cap}\"()\n            assertNull jwk.\"$id\"\n            assertFalse jwk.containsKey(id)\n        }\n\n        //test empty value\n        if (val instanceof List) {\n            val = Collections.emptyList()\n        } else if (val instanceof Set) {\n            val = Collections.emptySet()\n        }\n        if (val instanceof Collection) {\n            try {\n                builder.\"$name\"(val)\n                fail(\"IAE should have been thrown\")\n            } catch (IllegalArgumentException ignored) {\n            }\n            jwk = builder.build()\n            assertNull jwk.\"get${cap}\"()\n            assertNull jwk.\"$id\"\n            assertFalse jwk.containsKey(id)\n        }\n    }\n\n    @Test\n    void testPrivateCtor() {\n        new Jwks() // for code coverage only\n    }\n\n    @Test\n    void testBuilderWithoutState() {\n        try {\n            Jwks.builder().build()\n            fail()\n        } catch (IllegalStateException ise) {\n            String msg = 'A java.security.Key or one or more name/value pairs must be provided to create a JWK.'\n            assertEquals msg, ise.getMessage()\n        }\n    }\n\n    @Test\n    void testBuilderWithSecretKey() {\n        def jwk = Jwks.builder().key(SKEY).build()\n        assertEquals 'oct', jwk.getType()\n        assertEquals 'oct', jwk.kty\n        String k = jwk.k.get() as String\n        assertNotNull k\n        assertTrue MessageDigest.isEqual(SKEY.encoded, Decoders.BASE64URL.decode(k))\n    }\n\n    @Test\n    void testAlgorithm() {\n        testProperty('algorithm', 'alg', srandom())\n    }\n\n    @Test\n    void testId() {\n        testProperty('id', 'kid', srandom())\n    }\n\n    @Test\n    void testSingleOperation() {\n        def op = Jwks.OP.ENCRYPT\n        def expected = [op] as Set<KeyOperation>\n        def canonical = [op.getId()] as Set<String>\n        def jwk = Jwks.builder().key(TestKeys.A128GCM).operations().add(op).and().build()\n        assertEquals expected, jwk.getOperations()\n        assertEquals canonical, jwk.get(AbstractJwk.KEY_OPS.id)\n    }\n\n    @Test\n    void testSingleOperationNull() {\n        def jwk = Jwks.builder().key(TestKeys.A128GCM).operations().add((KeyOperation) null).and().build()\n        //ignored null\n        assertNull jwk.getOperations() //nothing added\n        assertFalse jwk.containsKey(AbstractJwk.KEY_OPS.id)\n    }\n\n    @Test\n    void testSingleOperationAppends() {\n        def expected = [Jwks.OP.ENCRYPT, Jwks.OP.DECRYPT] as Set<KeyOperation>\n        def jwk = Jwks.builder().key(TestKeys.A128GCM)\n                .operations().add(Jwks.OP.ENCRYPT).add(Jwks.OP.DECRYPT).and()\n                .build()\n        assertEquals expected, jwk.getOperations()\n    }\n\n    @Test\n    void testOperations() {\n        def val = [Jwks.OP.SIGN, Jwks.OP.VERIFY] as Set<KeyOperation>\n        def jwk = Jwks.builder().key(TestKeys.NA256).operations().add(val).and().build()\n        assertEquals val, jwk.getOperations()\n    }\n\n    @Test\n    void testOperationsNull() {\n        def jwk = Jwks.builder().key(TestKeys.A128GCM).operations().add((Collection) null).and().build()\n        assertNull jwk.getOperations()\n        assertFalse jwk.containsKey(AbstractJwk.KEY_OPS.id)\n    }\n\n    @Test\n    void testOperationsEmpty() {\n        def jwk = Jwks.builder().key(TestKeys.A128GCM).operations().add(Collections.emptyList()).and().build()\n        assertNull jwk.getOperations()\n    }\n\n    @Test\n    void testPublicKeyUse() {\n        testProperty('publicKeyUse', 'use', srandom())\n    }\n\n    @Test\n    void testX509CertChain() {\n        //get a test cert:\n        X509Certificate cert = TestKeys.forAlgorithm(Jwts.SIG.RS256).cert\n        def sval = JwtX509StringConverter.INSTANCE.applyTo(cert)\n        testProperty('x509Chain', 'x5c', [cert], [sval])\n    }\n\n    @Test\n    void testX509Sha1Thumbprint() {\n        testX509Thumbprint(1)\n    }\n\n    @Test\n    void testX509Sha256Thumbprint() {\n        testX509Thumbprint(256)\n    }\n\n    @Test\n    void testRandom() {\n        def random = new SecureRandom()\n        def jwk = Jwks.builder().key(SKEY).random(random).build()\n        assertSame random, jwk.@context.getRandom()\n    }\n\n    @Test\n    void testNullRandom() {\n        assertNotNull Jwks.builder().key(SKEY).random(null).build()\n    }\n\n    static void testX509Thumbprint(int number) {\n        def algs = Jwts.SIG.get().values().findAll { it instanceof SignatureAlgorithm }\n\n        for (def alg : algs) {\n            //get test cert:\n            X509Certificate cert = TestKeys.forAlgorithm(alg).cert\n            def builder = Jwks.builder().chain(Arrays.asList(cert))\n\n            if (number == 1) {\n                builder.x509Sha1Thumbprint(true)\n            } // otherwise, when a chain is present, a sha256 thumbprint is calculated automatically\n\n            def jwkFromKey = builder.build() as PublicJwk\n            byte[] thumbprint = jwkFromKey.\"getX509Sha${number}Thumbprint\"()\n            assertNotNull thumbprint\n\n            //ensure base64url encoding/decoding of the thumbprint works:\n            def jwkFromValues = Jwks.builder().add(jwkFromKey).build() as PublicJwk\n            assertArrayEquals thumbprint, jwkFromValues.\"getX509Sha${number}Thumbprint\"() as byte[]\n        }\n    }\n\n    @Test\n    void testSecretJwks() {\n        Collection<MacAlgorithm> algs = Jwts.SIG.get().values().findAll({ it instanceof MacAlgorithm }) as Collection<MacAlgorithm>\n        for (def alg : algs) {\n            SecretKey secretKey = alg.key().build()\n            def jwk = Jwks.builder().key(secretKey).id('id').build()\n            assertEquals 'oct', jwk.getType()\n            assertTrue jwk.containsKey('k')\n            assertEquals 'id', jwk.getId()\n            assertEquals secretKey, jwk.toKey()\n        }\n    }\n\n    @Test\n    void testSecretKeyGetEncodedReturnsNull() {\n        SecretKey key = new TestSecretKey(algorithm: \"AES\")\n        try {\n            Jwks.builder().key(key).build()\n            fail()\n        } catch (InvalidKeyException expected) {\n            String causeMsg = \"Missing required encoded bytes for key [${KeysBridge.toString(key)}].\"\n            String msg = \"Unable to encode SecretKey to JWK: $causeMsg\"\n            assertEquals msg, expected.message\n            assertTrue expected.getCause() instanceof InvalidKeyException\n            assertEquals causeMsg, expected.getCause().getMessage()\n        }\n    }\n\n    @Test\n    void testSecretKeyGetEncodedThrowsException() {\n        String encodedMsg = \"not allowed\"\n        def encodedEx = new UnsupportedOperationException(encodedMsg)\n        SecretKey key = new TestSecretKey() {\n            @Override\n            byte[] getEncoded() {\n                throw encodedEx\n            }\n        }\n        try {\n            Jwks.builder().key(key).build()\n            fail()\n        } catch (InvalidKeyException expected) {\n            String causeMsg = \"Cannot obtain required encoded bytes from key [${KeysBridge.toString(key)}]: $encodedMsg\"\n            String msg = \"Unable to encode SecretKey to JWK: $causeMsg\"\n            assertEquals msg, expected.message\n            assertTrue expected.getCause() instanceof InvalidKeyException\n            assertEquals causeMsg, expected.cause.message\n            assertSame encodedEx, expected.getCause().getCause()\n        }\n    }\n\n    @Test\n    void testAsymmetricJwks() {\n\n        Collection<SignatureAlgorithm> algs = Jwts.SIG.get().values()\n                .findAll({ it instanceof SignatureAlgorithm }) as Collection<SignatureAlgorithm>\n\n        for (SignatureAlgorithm alg : algs) {\n\n            def pair = alg.keyPair().build()\n            PublicKey pub = pair.getPublic()\n            PrivateKey priv = pair.getPrivate()\n\n            // test individual keys\n            PublicJwk pubJwk = Jwks.builder().key(pub).publicKeyUse(\"sig\").build()\n            assertEquals pub, pubJwk.toKey()\n\n            def builder = Jwks.builder().key(priv).publicKeyUse('sig')\n            PrivateJwk privJwk = builder.build()\n            assertEquals priv, privJwk.toKey()\n            PublicJwk privPubJwk = privJwk.toPublicJwk()\n            assertEquals pubJwk, privPubJwk\n            assertEquals pub, pubJwk.toKey()\n            def jwkPair = privJwk.toKeyPair()\n            assertEquals pub, jwkPair.getPublic()\n            assertEquals priv, jwkPair.getPrivate()\n\n            // test pair\n            builder = Jwks.builder()\n            if (pub instanceof ECKey) {\n                builder = builder.ecKeyPair(pair)\n            } else if (pub instanceof RSAKey) {\n                builder = builder.rsaKeyPair(pair)\n            } else {\n                builder = builder.octetKeyPair(pair)\n            }\n            privJwk = builder.publicKeyUse(\"sig\").build() as PrivateJwk\n            assertEquals priv, privJwk.toKey()\n            privPubJwk = privJwk.toPublicJwk()\n            assertEquals pubJwk, privPubJwk\n            assertEquals pub, pubJwk.toKey()\n            jwkPair = privJwk.toKeyPair()\n            assertEquals pub, jwkPair.getPublic()\n            assertEquals priv, jwkPair.getPrivate()\n        }\n    }\n\n    @Test\n    void testInvalidEcCurvePoint() {\n        def algs = [Jwts.SIG.ES256, Jwts.SIG.ES384, Jwts.SIG.ES512]\n\n        for (SignatureAlgorithm alg : algs) {\n\n            def pair = alg.keyPair().build()\n            ECPublicKey pubKey = pair.getPublic() as ECPublicKey\n\n            EcPublicJwk jwk = Jwks.builder().key(pubKey).build()\n\n            //try creating a JWK with a bad point:\n            def badPubKey = new InvalidECPublicKey(pubKey)\n            try {\n                Jwks.builder().key(badPubKey).build()\n            } catch (InvalidKeyException ike) {\n                String curveId = jwk.get('crv')\n                String msg = EcPublicJwkFactory.keyContainsErrorMessage(curveId)\n                assertEquals msg, ike.getMessage()\n            }\n\n            BigInteger p = pubKey.getParams().getCurve().getField().getP()\n            def outOfFieldRange = [BigInteger.ZERO, BigInteger.ONE, p, p.add(BigInteger.valueOf(1))]\n            for (def x : outOfFieldRange) {\n                Map<String, ?> modified = new LinkedHashMap<>(jwk)\n                modified.put('x', Converters.BIGINT.applyTo(x))\n                try {\n                    Jwks.builder().add(modified).build()\n                } catch (InvalidKeyException ike) {\n                    String expected = EcPublicJwkFactory.jwkContainsErrorMessage(jwk.crv as String, modified)\n                    assertEquals(expected, ike.getMessage())\n                }\n            }\n            for (def y : outOfFieldRange) {\n                Map<String, ?> modified = new LinkedHashMap<>(jwk)\n                modified.put('y', Converters.BIGINT.applyTo(y))\n                try {\n                    Jwks.builder().add(modified).build()\n                } catch (InvalidKeyException ike) {\n                    String expected = EcPublicJwkFactory.jwkContainsErrorMessage(jwk.crv as String, modified)\n                    assertEquals(expected, ike.getMessage())\n                }\n            }\n        }\n    }\n\n    @Test\n    void testPublicJwkBuilderWithRSAPublicKey() {\n        def key = TestKeys.RS256.pair.public\n        // must cast to PublicKey to avoid Groovy's dynamic type dispatch to the key(RSAPublicKey) method:\n        def jwk = Jwks.builder().key((PublicKey) key).build()\n        assertNotNull jwk\n        assertTrue jwk instanceof RsaPublicJwk\n    }\n\n    @Test\n    void testPublicJwkBuilderWithECPublicKey() {\n        def key = TestKeys.ES256.pair.public\n        // must cast to PublicKey to avoid Groovy's dynamic type dispatch to the key(ECPublicKey) method:\n        def jwk = Jwks.builder().key((PublicKey) key).build()\n        assertNotNull jwk\n        assertTrue jwk instanceof EcPublicJwk\n    }\n\n    @Test\n    void testPublicJwkBuilderWithUnsupportedKey() {\n        def key = new TestPublicKey()\n        // must cast to PublicKey to avoid Groovy's dynamic type dispatch to the key(ECPublicKey) method:\n        try {\n            Jwks.builder().key((PublicKey) key)\n        } catch (UnsupportedKeyException expected) {\n            String msg = \"There is no builder that supports specified key [${KeysBridge.toString(key)}].\"\n            assertEquals(msg, expected.getMessage())\n            assertNotNull expected.getCause() // ensure we always retain a cause\n        }\n    }\n\n    @Test\n    void testPrivateJwkBuilderWithRSAPrivateKey() {\n        def key = TestKeys.RS256.pair.private\n        // must cast to PrivateKey to avoid Groovy's dynamic type dispatch to the key(RSAPrivateKey) method:\n        def jwk = Jwks.builder().key((PrivateKey) key).build()\n        assertNotNull jwk\n        assertTrue jwk instanceof RsaPrivateJwk\n    }\n\n    @Test\n    void testPrivateJwkBuilderWithECPrivateKey() {\n        def key = TestKeys.ES256.pair.private\n        // must cast to PrivateKey to avoid Groovy's dynamic type dispatch to the key(ECPrivateKey) method:\n        def jwk = Jwks.builder().key((PrivateKey) key).build()\n        assertNotNull jwk\n        assertTrue jwk instanceof EcPrivateJwk\n    }\n\n    @Test\n    void testPrivateJwkBuilderWithUnsupportedKey() {\n        def key = new TestPrivateKey()\n        try {\n            Jwks.builder().key((PrivateKey) key)\n        } catch (UnsupportedKeyException expected) {\n            String msg = \"There is no builder that supports specified key [${KeysBridge.toString(key)}].\"\n            assertEquals(msg, expected.getMessage())\n            assertNotNull expected.getCause() // ensure we always retain a cause\n        }\n    }\n\n    @Test\n    void testEcChain() {\n        TestKeys.EC.each {\n            ECPublicKey key = it.pair.public as ECPublicKey\n            def jwk = Jwks.builder().ecChain(it.chain).build()\n            assertEquals key, jwk.toKey()\n            assertEquals it.chain, jwk.getX509Chain()\n        }\n    }\n\n    @Test\n    void testRsaChain() {\n        TestKeys.RSA.each {\n            RSAPublicKey key = it.pair.public as RSAPublicKey\n            def jwk = Jwks.builder().rsaChain(it.chain).build()\n            assertEquals key, jwk.toKey()\n            assertEquals it.chain, jwk.getX509Chain()\n        }\n    }\n\n    @Test\n    void testOctetChain() {\n        TestKeys.EdEC.each { // no chains for XEC keys\n            PublicKey key = it.pair.public\n            def jwk = Jwks.builder().octetChain(it.chain).build()\n            assertEquals key, jwk.toKey()\n            assertEquals it.chain, jwk.getX509Chain()\n        }\n    }\n\n    @Test\n    void testRsaKeyPair() {\n        TestKeys.RSA.each {\n            java.security.KeyPair pair = it.pair\n            PrivateJwk jwk = Jwks.builder().rsaKeyPair(pair).build()\n            assertEquals it.pair.public, jwk.toPublicJwk().toKey()\n            assertEquals it.pair.private, jwk.toKey()\n        }\n    }\n\n    @Test\n    void testEcKeyPair() {\n        TestKeys.EC.each {\n            java.security.KeyPair pair = it.pair\n            PrivateJwk jwk = Jwks.builder().ecKeyPair(pair).build()\n            assertEquals it.pair.public, jwk.toPublicJwk().toKey()\n            assertEquals it.pair.private, jwk.toKey()\n        }\n    }\n\n    @Test\n    void testOctetKeyPair() {\n        TestKeys.EdEC.each {\n            java.security.KeyPair pair = it.pair\n            PrivateJwk jwk = Jwks.builder().octetKeyPair(pair).build()\n            assertEquals it.pair.public, jwk.toPublicJwk().toKey()\n            assertEquals it.pair.private, jwk.toKey()\n        }\n    }\n\n    @Test\n    void testKeyPair() {\n        TestKeys.ASYM.each {\n            java.security.KeyPair pair = it.pair\n            PrivateJwk jwk = Jwks.builder().keyPair(pair).build()\n            assertEquals it.pair.public, jwk.toPublicJwk().toKey()\n            assertEquals it.pair.private, jwk.toKey()\n        }\n    }\n\n    private static class InvalidECPublicKey implements ECPublicKey {\n\n        private final ECPublicKey good\n\n        InvalidECPublicKey(ECPublicKey good) {\n            this.good = good\n        }\n\n        @Override\n        ECPoint getW() {\n            return ECPoint.POINT_INFINITY // bad value, should make all 'contains' validations fail\n        }\n\n        @Override\n        String getAlgorithm() {\n            return good.getAlgorithm()\n        }\n\n        @Override\n        String getFormat() {\n            return good.getFormat()\n        }\n\n        @Override\n        byte[] getEncoded() {\n            return good.getEncoded()\n        }\n\n        @Override\n        ECParameterSpec getParams() {\n            return good.getParams()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/JwtX509StringConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.SecurityException\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.cert.CertificateEncodingException\nimport java.security.cert.CertificateException\nimport java.security.cert.X509Certificate\n\nimport static org.easymock.EasyMock.*\nimport static org.junit.Assert.*\n\nclass JwtX509StringConverterTest {\n\n    private JwtX509StringConverter converter\n\n    @Before\n    void setUp() {\n        converter = JwtX509StringConverter.INSTANCE\n    }\n\n    /**\n     * Ensures we can convert and convert-back all OpenSSL certs across all JVM versions automatically\n     * (because X25519 and X448 >= JDK 11 and Ed25519 and Ed448 are >= JDK 15), but they should still work on earlier\n     * JDKs due to JcaTemplate auto-fallback with BouncyCastle\n     */\n    @Test\n    void testOpenSSLCertRoundtrip() {\n        // X25519 and X448 don't have certs, so we filter to leave those out:\n        TestKeys.ASYM.findAll({ it.cert != null }).each {\n            X509Certificate cert = it.cert\n            String encoded = converter.applyTo(cert)\n            assertEquals cert, converter.applyFrom(encoded)\n        }\n    }\n\n    @Test\n    void testApplyToThrowsEncodingException() {\n\n        def ex = new CertificateEncodingException(\"foo\")\n\n        X509Certificate cert = createMock(X509Certificate)\n        expect(cert.getEncoded()).andThrow(ex)\n        replay cert\n\n        try {\n            converter.applyTo(cert)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String expectedMsg = \"Unable to access X509Certificate encoded bytes necessary to perform DER \" +\n                    \"Base64-encoding. Certificate: {${cert}}. Cause: \" + ex.getMessage()\n            assertSame ex, expected.getCause()\n            assertEquals expectedMsg, expected.getMessage()\n        }\n\n        verify cert\n    }\n\n    @Test\n    void testApplyToWithEmptyEncoding() {\n\n        X509Certificate cert = createMock(X509Certificate)\n        expect(cert.getEncoded()).andReturn(Bytes.EMPTY)\n        replay cert\n\n        try {\n            converter.applyTo(cert)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String expectedMsg = 'X509Certificate encoded bytes cannot be null or empty.  Certificate: ' +\n                    '{EasyMock for class java.security.cert.X509Certificate}.'\n            assertEquals expectedMsg, expected.getMessage()\n        }\n\n        verify cert\n    }\n\n    @Test\n    void testApplyFromBadBase64() {\n        String s = 'f$oo'\n        try {\n            converter.applyFrom(s)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String expectedMsg = \"Unable to convert Base64 String '$s' to X509Certificate instance. \" +\n                    \"Cause: Illegal base64 character: '\\$'\"\n            assertEquals expectedMsg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testApplyFromInvalidCertString() {\n        final String exMsg = \"nope: ${RsaSignatureAlgorithm.PSS_OID}\"\n        final CertificateException ex = new CertificateException(exMsg)\n        converter = new JwtX509StringConverter() {\n            @Override\n            protected X509Certificate toCert(byte[] der) throws SecurityException {\n                throw ex // ensure fails first and second time\n            }\n        }\n\n        def cert = TestKeys.RS256.cert\n        def validBase64 = Encoders.BASE64.encode(cert.getEncoded())\n\n        try {\n            converter.applyFrom(validBase64)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            String expectedMsg = \"Unable to convert Base64 String '$validBase64' to X509Certificate instance. Cause: ${exMsg}\"\n            assertEquals expectedMsg, expected.getMessage()\n            assertSame ex, expected.getCause()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/KeyOperationConverterTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass KeyOperationConverterTest {\n\n    @Test\n    void testApplyFromStandardId() {\n        Jwks.OP.get().values().each {\n            def id = it.id\n            def op = KeyOperationConverter.DEFAULT.applyFrom(id)\n            assertSame it, op\n        }\n    }\n\n    @Test\n    void testApplyFromCustomId() {\n        def id = 'custom'\n        def op = KeyOperationConverter.DEFAULT.applyFrom(id)\n        assertEquals id, op.id\n        assertEquals DefaultKeyOperation.CUSTOM_DESCRIPTION, op.description\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/KeyPairsTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport org.junit.Test\n\nimport java.security.Key\nimport java.security.KeyPair\nimport java.security.PublicKey\nimport java.security.interfaces.DSAPublicKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass KeyPairsTest {\n\n    @Test\n    void testPrivateCtor() { // for code coverage only\n        new KeyPairs()\n    }\n\n    @Test\n    void testGetKeyNullPair() {\n        try {\n            KeyPairs.getKey(null, ECPublicKey.class)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            assertEquals 'KeyPair cannot be null.', iae.getMessage()\n        }\n    }\n\n    @Test\n    void testUnrecognizedFamily() {\n        PublicKey pub = new TestECPublicKey()\n        KeyPair pair = new KeyPair(pub, new TestECPrivateKey())\n        Class clazz = DSAPublicKey // unrecognized --> no 'family' prefix in message\n        try {\n            KeyPairs.getKey(pair, clazz)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            String msg = \"KeyPair public key must be an instance of ${clazz.name}. Type found: ${pub.class.name}\"\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testGetKeyECMismatch() {\n        KeyPair pair = Jwts.SIG.RS256.keyPair().build()\n        Class clazz = ECPublicKey\n        try {\n            KeyPairs.getKey(pair, clazz)\n        } catch (IllegalArgumentException iae) {\n            String msg = \"EC KeyPair public key must be an instance of ${clazz.name}. Type found: ${pair.public.class.name}\"\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testGetKeyRSAMismatch() {\n        KeyPair pair = new KeyPair(new TestECPublicKey(), new TestECPrivateKey())\n        Class clazz = RSAPublicKey\n        try {\n            KeyPairs.getKey(pair, clazz)\n        } catch (IllegalArgumentException iae) {\n            String msg = \"RSA KeyPair public key must be an instance of ${clazz.name}. Type found: ${pair.public.class.name}\"\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testAssertPublicKeyTypeMismatch() {\n        Key key = new TestECPublicKey()\n        Class clazz = RSAPublicKey\n        String prefix = 'Foo '\n        try {\n            KeyPairs.assertKey(key, clazz, prefix)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            String msg = \"${prefix}public key must be an instance of ${clazz.name}. Type found: ${key.class.name}\"\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testAssertPrivateKeyTypeMismatch() {\n        Key key = new TestECPrivateKey()\n        Class clazz = RSAPrivateKey\n        String prefix = 'Foo '\n        try {\n            KeyPairs.assertKey(key, clazz, prefix)\n            fail()\n        } catch (IllegalArgumentException iae) {\n            String msg = \"${prefix}private key must be an instance of ${clazz.name}. Type found: ${key.class.name}\"\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    private void printMap(Map<?, ?> m, int indentCount) {\n        for (def entry : m.entrySet()) {\n            indentCount.times { print(\"\\t\") }\n            print \"${entry.key}: \"\n            if (entry.value instanceof Map) {\n                println()\n                printMap(entry.value as Map, indentCount + 1)\n            } else {\n                println \"${entry.value}\"\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/KeyUsageTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.assertFalse\nimport static org.junit.Assert.assertTrue\n\nclass KeyUsageTest {\n\n    private static KeyUsage usage(int trueIndex) {\n        boolean[] usage = new boolean[9]\n        usage[trueIndex] = true\n        return new KeyUsage(new TestX509Certificate(keyUsage: usage))\n    }\n\n    private KeyUsage ku\n\n    @Before\n    void setUp() {\n        ku = new KeyUsage(new TestX509Certificate())\n    }\n\n    @Test\n    void testNullCert() {\n        ku = new KeyUsage(null)\n        assertFalse ku.isCRLSign()\n        assertFalse ku.isDataEncipherment()\n        assertFalse ku.isDecipherOnly()\n        assertFalse ku.isDigitalSignature()\n        assertFalse ku.isEncipherOnly()\n        assertFalse ku.isKeyAgreement()\n        assertFalse ku.isKeyCertSign()\n        assertFalse ku.isKeyEncipherment()\n        assertFalse ku.isNonRepudiation()\n    }\n\n    @Test\n    void testCertWithNullKeyUsage() {\n        ku = new KeyUsage(new TestX509Certificate(keyUsage: null))\n        assertFalse ku.isCRLSign()\n        assertFalse ku.isDataEncipherment()\n        assertFalse ku.isDecipherOnly()\n        assertFalse ku.isDigitalSignature()\n        assertFalse ku.isEncipherOnly()\n        assertFalse ku.isKeyAgreement()\n        assertFalse ku.isKeyCertSign()\n        assertFalse ku.isKeyEncipherment()\n        assertFalse ku.isNonRepudiation()\n    }\n\n    @Test\n    void testDigitalSignature() {\n        assertFalse ku.isDigitalSignature() //default\n        assertTrue usage(0).isDigitalSignature()\n    }\n\n    @Test\n    void testNonRepudiation() {\n        assertFalse ku.isNonRepudiation()\n        assertTrue usage(1).isNonRepudiation()\n    }\n\n    @Test\n    void testKeyEncipherment() {\n        assertFalse ku.isKeyEncipherment()\n        assertTrue usage(2).isKeyEncipherment()\n    }\n\n    @Test\n    void testDataEncipherment() {\n        assertFalse ku.isDataEncipherment()\n        assertTrue usage(3).isDataEncipherment()\n    }\n\n    @Test\n    void testKeyAgreement() {\n        assertFalse ku.isKeyAgreement()\n        assertTrue usage(4).isKeyAgreement()\n    }\n\n    @Test\n    void testKeyCertSign() {\n        assertFalse ku.isKeyCertSign()\n        assertTrue usage(5).isKeyCertSign()\n    }\n\n    @Test\n    void testCRLSign() {\n        assertFalse ku.isCRLSign()\n        assertTrue usage(6).isCRLSign()\n    }\n\n    @Test\n    void testEncipherOnly() {\n        assertFalse ku.isEncipherOnly()\n        assertTrue usage(7).isEncipherOnly()\n    }\n\n    @Test\n    void testDecipherOnly() {\n        assertFalse ku.isDecipherOnly()\n        assertTrue usage(8).isDecipherOnly()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/KeysBridgeTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.security.Key\nimport java.security.PrivateKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertFalse\nimport static org.junit.Assert.assertTrue\n\nclass KeysBridgeTest {\n\n    @Test\n    void testToStringKeyNull() {\n        assertEquals 'null', KeysBridge.toString(null)\n    }\n\n    @Test\n    void testToStringPublicKey() {\n        // should just be key.toString(). Because it's a PublicKey, no danger of reporting key data\n        def key = TestKeys.ES256.pair.public\n        String s = KeysBridge.toString(key)\n        assertEquals key.toString(), s\n    }\n\n    static void testFormattedOutput(Key key) {\n        String s = KeysBridge.toString(key)\n        String expected = \"class: ${key.getClass().getName()}, algorithm: ${key.getAlgorithm()}, format: ${key.getFormat()}\" as String\n        assertEquals expected, s\n    }\n\n    @Test\n    void testToStringPrivateKey() {\n        testFormattedOutput(TestKeys.ES256.pair.private)\n    }\n\n    @Test\n    void testToStringSecretKey() {\n        testFormattedOutput(TestKeys.HS256)\n    }\n\n    @Test\n    void testToStringPassword() {\n        testFormattedOutput(new PasswordSpec(\"foo\".toCharArray()))\n    }\n\n    @Test\n    void testIsGenericSecret() {\n        def secretKeyWithAlg = { alg ->\n            new SecretKey() {\n                @Override\n                String getAlgorithm() {\n                    return alg\n                }\n\n                @Override\n                String getFormat() {\n                    return 'RAW'\n                }\n\n                @Override\n                byte[] getEncoded() {\n                    return new byte[0]\n                }\n            }\n        }\n\n        PrivateKey genericPrivateKey = new PrivateKey() {\n            @Override\n            String getAlgorithm() {\n                return \"Generic\"\n            }\n\n            @Override\n            String getFormat() {\n                return \"RAW\"\n            }\n\n            @Override\n            byte[] getEncoded() {\n                return new byte[0]\n            }\n        }\n\n        assertTrue KeysBridge.isGenericSecret(secretKeyWithAlg(\"GenericSecret\"))\n        assertTrue KeysBridge.isGenericSecret(secretKeyWithAlg(\"Generic Secret\"))\n        assertFalse KeysBridge.isGenericSecret(secretKeyWithAlg(\" Generic\"))\n        assertFalse KeysBridge.isGenericSecret(TestKeys.HS256)\n        assertFalse KeysBridge.isGenericSecret(TestKeys.A256GCM)\n        assertFalse KeysBridge.isGenericSecret(genericPrivateKey)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/LocatingKeyResolverTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.DefaultClaims\nimport io.jsonwebtoken.impl.DefaultJwsHeader\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.assertSame\n\nclass LocatingKeyResolverTest {\n\n    @Test(expected = IllegalArgumentException)\n    void testNullConstructor() {\n        new LocatingKeyResolver(null)\n    }\n\n    @Test\n    void testResolveSigningKeyClaims() {\n        def key = TestKeys.HS256\n        def locator = new ConstantKeyLocator(key, null)\n        def header = new DefaultJwsHeader([:])\n        def claims = new DefaultClaims()\n        assertSame key, new LocatingKeyResolver(locator).resolveSigningKey(header, claims)\n    }\n\n    @Test\n    void testResolveSigningKeyPayload() {\n        def key = TestKeys.HS256\n        def locator = new ConstantKeyLocator(key, null)\n        def header = new DefaultJwsHeader([:])\n        def payload = 'hello world'.getBytes(StandardCharsets.UTF_8)\n        assertSame key, new LocatingKeyResolver(locator).resolveSigningKey(header, payload)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/NoneSignatureAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.SecureRequest\nimport io.jsonwebtoken.security.SignatureException\nimport io.jsonwebtoken.security.VerifySecureDigestRequest\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertTrue\n\nclass NoneSignatureAlgorithmTest {\n\n    private NoneSignatureAlgorithm alg\n\n    @Before\n    void setUp() {\n        this.alg = new NoneSignatureAlgorithm()\n    }\n\n    @Test\n    void testName() {\n        assertEquals \"none\", alg.getId()\n    }\n\n    @Test(expected = SignatureException)\n    void testDigest() {\n        alg.digest((SecureRequest)null)\n    }\n\n    @Test(expected = SignatureException)\n    void testVerify() {\n        alg.verify((VerifySecureDigestRequest)null)\n    }\n\n    @Test\n    void testHashCode() {\n        assertEquals 'none'.hashCode(), alg.hashCode()\n    }\n\n    @Test\n    void testEquals() {\n        assertTrue alg == new NoneSignatureAlgorithm()\n    }\n\n    @Test\n    void testIdentityEquals() {\n        assertTrue alg == alg\n    }\n\n    @Test\n    void testToString() {\n        assertEquals alg.getId(), alg.toString()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/OctetJwksTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.RfcTests\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport java.security.PrivateKey\nimport java.security.PublicKey\n\nimport static org.junit.Assert.*\n\nclass OctetJwksTest {\n\n    /**\n     * Test case discovered during CI testing where a randomly-generated X25519 public key with a leading zero byte\n     * was not being decoded correctly.  This test asserts that this value is decoded correctly.\n     */\n    @Test\n    void testX25519PublicJson() {\n        String use = 'sig'\n        String kty = 'OKP'\n        String crv = 'X25519'\n        String x = 'AHwi7xPo5meUAGBDyzLZ9_ZwmmYA_SAMpdRFnsmggnI'\n        byte[] decoded = DefaultOctetPublicJwk.X.applyFrom(x)\n        assertEquals 0x00, decoded[0]\n\n        String json = RfcTests.stripws(\"\"\"\n        {\n           \"use\": \"$use\",\n           \"kty\": \"$kty\",\n           \"crv\": \"$crv\",\n           \"x\": \"$x\"\n        }\"\"\")\n        def jwk = Jwks.parser().build().parse(json) as OctetPublicJwk\n        assertEquals use, jwk.getPublicKeyUse()\n        assertEquals kty, jwk.getType()\n        assertEquals crv, jwk.get('crv')\n        assertEquals x, jwk.get('x')\n    }\n\n    /**\n     * Test case discovered during CI testing where a randomly-generated Ed448 public key with a leading zero byte was\n     * not being decoded correctly.  This test asserts that this value is decoded correctly.\n     */\n    @Test\n    void testEd448PublicJson() {\n        String use = 'sig'\n        String kty = 'OKP'\n        String crv = 'Ed448'\n        String x = 'AKxj_Iz2y6IHq5KipsOYZJyUjClO1IbT396KQK15DFryNwowKKBvswQLWytxXHgqGkpG5PUWkuQA'\n        byte[] decoded = DefaultOctetPublicJwk.X.applyFrom(x)\n        assertEquals 0x00, decoded[0]\n\n        String json = RfcTests.stripws(\"\"\"\n        {\n           \"use\": \"$use\",\n           \"kty\": \"$kty\",\n           \"crv\": \"$crv\",\n           \"x\": \"$x\"\n        }\"\"\")\n        def jwk = Jwks.parser().build().parse(json) as OctetPublicJwk\n        assertEquals use, jwk.getPublicKeyUse()\n        assertEquals kty, jwk.getType()\n        assertEquals crv, jwk.get('crv')\n        assertEquals x, jwk.get('x')\n    }\n\n    /**\n     * Test case discovered during CI testing where a randomly-generated Ed25519 private key with a leading zero byte\n     * was not being decoded correctly.  This test asserts that this value is decoded correctly.\n     */\n    @Test\n    void testEd25519PrivateJson() {\n        String use = 'sig'\n        String kty = 'OKP'\n        String crv = 'Ed25519'\n        String x = '9NAzPLMakU0R-tLgX7NmzUUg_fUGiDbrGOWqQ0F_s3g'\n        String d = 'AAfgb017BkHlLf_SqVBA_LqPhabpdh43dLXHfD6ggQ0'\n        byte[] decoded = DefaultOctetPrivateJwk.D.applyFrom(d)\n        assertEquals 0x00, decoded[0]\n        String json = RfcTests.stripws(\"\"\"\n        {\n           \"use\": \"$use\",\n           \"kty\": \"$kty\",\n           \"crv\": \"$crv\",\n           \"x\": \"$x\",\n           \"d\": \"$d\"\n        }\"\"\")\n        def jwk = Jwks.parser().build().parse(json) as OctetPrivateJwk\n        assertEquals use, jwk.getPublicKeyUse()\n        assertEquals kty, jwk.getType()\n        assertEquals crv, jwk.get('crv')\n        assertEquals x, jwk.get('x')\n        assertEquals d, jwk.get('d').get() // Supplier\n        def pubJwk = jwk.toPublicJwk()\n        assertEquals use, pubJwk.getPublicKeyUse()\n        assertEquals kty, pubJwk.getType()\n        assertEquals crv, pubJwk.get('crv')\n        assertEquals x, pubJwk.get('x')\n        assertNull pubJwk.get('d')\n    }\n\n    @Test\n    void testOctetKeyPairs() {\n\n        for (EdwardsCurve curve : EdwardsCurve.VALUES) {\n\n            def pair = curve.keyPair().build()\n            PublicKey pub = pair.getPublic()\n            PrivateKey priv = pair.getPrivate()\n\n            // test individual keys\n            PublicJwk pubJwk = Jwks.builder().octetKey(pub).publicKeyUse(\"sig\").build()\n            PublicJwk pubValuesJwk = Jwks.builder().add(pubJwk).build() as PublicJwk // ensure value map symmetry\n            assertEquals pubJwk, pubValuesJwk\n            assertEquals pub, pubJwk.toKey()\n            assertEquals pub, pubValuesJwk.toKey()\n\n            PrivateJwk privJwk = Jwks.builder().octetKey(priv).publicKey(pub).publicKeyUse(\"sig\").build()\n            PrivateJwk privValuesJwk = Jwks.builder().add(privJwk).build() as PrivateJwk // ensure value map symmetry\n            assertEquals privJwk, privValuesJwk\n            assertEquals priv, privJwk.toKey()\n            // we can't assert that priv.equals(privValuesJwk.toKey()) here because BouncyCastle uses PKCS8 V2 encoding\n            // while the JDK uses V1, and BC implementations check that the encodings are equal (instead of their\n            // actual key material).  Since we only care about the key material for JWK representations, and not the\n            // key's PKCS8 encoding, we check that their 'd' values are the same, not that the keys' encoding is:\n            byte[] privMaterial = curve.getKeyMaterial(priv)\n            byte[] jwkKeyMaterial = curve.getKeyMaterial(privValuesJwk.toKey())\n            assertArrayEquals privMaterial, jwkKeyMaterial\n\n            PublicJwk privPubJwk = privJwk.toPublicJwk()\n            assertEquals pubJwk, privPubJwk\n            assertEquals pubValuesJwk, privPubJwk\n            assertEquals pub, pubJwk.toKey()\n\n            def jwkPair = privJwk.toKeyPair()\n            assertEquals pub, jwkPair.getPublic()\n            assertEquals priv, jwkPair.getPrivate()\n            jwkPair = privValuesJwk.toKeyPair()\n            assertEquals pub, jwkPair.getPublic()\n            // see comments above about material equality instead of encoding equality\n            privMaterial = curve.getKeyMaterial(priv)\n            jwkKeyMaterial = curve.getKeyMaterial(jwkPair.getPrivate())\n            assertArrayEquals privMaterial, jwkKeyMaterial\n\n            // Test public-to-private builder coercion:\n            privJwk = Jwks.builder().octetKey(pub).privateKey(priv).publicKeyUse('sig').build()\n            privValuesJwk = Jwks.builder().add(privJwk).build() as PrivateJwk // ensure value map symmetry\n            assertEquals privJwk, privValuesJwk\n            assertEquals priv, privJwk.toKey()\n            // see comments above about material equality instead of encoding equality\n            privMaterial = curve.getKeyMaterial(priv)\n            jwkKeyMaterial = curve.getKeyMaterial(jwkPair.getPrivate())\n            assertArrayEquals privMaterial, jwkKeyMaterial\n\n            privPubJwk = privJwk.toPublicJwk()\n            pubValuesJwk = privValuesJwk.toPublicJwk()\n            assertEquals pubJwk, privPubJwk\n            assertEquals pubJwk, pubValuesJwk\n            assertEquals pub, pubJwk.toKey()\n            assertEquals pub, pubValuesJwk.toKey()\n\n            // test pair\n            privJwk = Jwks.builder().octetKeyPair(pair).publicKeyUse(\"sig\").build()\n            assertEquals priv, privJwk.toKey()\n            // see comments above about material equality instead of encoding equality\n            privMaterial = curve.getKeyMaterial(priv)\n            jwkKeyMaterial = curve.getKeyMaterial(privValuesJwk.toKey())\n            assertArrayEquals privMaterial, jwkKeyMaterial\n\n            privPubJwk = privJwk.toPublicJwk()\n            assertEquals pubJwk, privPubJwk\n            assertEquals pubValuesJwk, privPubJwk\n            assertEquals pub, pubJwk.toKey()\n\n            jwkPair = privJwk.toKeyPair()\n            assertEquals pub, jwkPair.getPublic()\n            assertEquals priv, jwkPair.getPrivate()\n        }\n    }\n\n    @Test\n    void testUnknownCurveId() {\n        def b = Jwks.builder()\n                .add(AbstractJwk.KTY.getId(), DefaultOctetPublicJwk.TYPE_VALUE)\n                .add(DefaultOctetPublicJwk.CRV.getId(), 'foo')\n        try {\n            b.build()\n            fail()\n        } catch (UnsupportedKeyException e) {\n            String msg = \"Unrecognized OKP JWK ${DefaultOctetPublicJwk.CRV} value 'foo'\" as String\n            assertEquals msg, e.getMessage()\n        }\n    }\n\n    /**\n     * Asserts that a Jwk built with an Edwards Curve private key does not accept an Edwards Curve public key\n     * on a different curve\n     */\n    @Test\n    void testPrivateKeyCurvePublicKeyMismatch() {\n        def priv = TestKeys.X448.pair.private\n        def mismatchedPub = TestKeys.X25519.pair.public\n        try {\n            Jwks.builder().octetKey(priv).publicKey(mismatchedPub).build()\n            fail()\n        } catch (InvalidKeyException ike) {\n            String msg = \"Specified Edwards Curve PublicKey does not match the specified PrivateKey's curve.\"\n            assertEquals msg, ike.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/PasswordSpecTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.Keys\nimport io.jsonwebtoken.security.Password\nimport org.junit.Before\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\n@SuppressWarnings('GroovyAccessibility')\nclass PasswordSpecTest {\n\n    private char[] PASSWORD\n    private PasswordSpec KEY\n\n    @Before\n    void setup() {\n        PASSWORD = \"whatever\".toCharArray()\n        KEY = new PasswordSpec(PASSWORD)\n    }\n\n    @Test\n    void testNewInstance() {\n        assertArrayEquals PASSWORD, KEY.toCharArray()\n        assertEquals PasswordSpec.NONE_ALGORITHM, KEY.getAlgorithm()\n        assertNull KEY.getFormat()\n    }\n\n    @Test\n    void testGetEncodedUnsupported() {\n        try {\n            KEY.getEncoded()\n            fail()\n        } catch (UnsupportedOperationException expected) {\n            assertEquals PasswordSpec.ENCODED_DISABLED_MSG, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testSymmetricChange() {\n        //assert change in backing array changes key as well:\n        PASSWORD[0] = 'b'\n        assertArrayEquals PASSWORD, KEY.toCharArray()\n    }\n\n    @Test\n    void testSymmetricDestroy() {\n        KEY.destroy()\n        assertTrue KEY.isDestroyed()\n        for(char c : PASSWORD) { //assert clearing key clears backing array:\n            assertTrue c == (char)'\\u0000'\n        }\n    }\n\n    @Test\n    void testDestroyIdempotent() {\n        testSymmetricDestroy()\n        //now do it again to assert idempotent result:\n        KEY.destroy()\n        assertTrue KEY.isDestroyed()\n        for(char c : PASSWORD) {\n            assertTrue c == (char)'\\u0000'\n        }\n    }\n\n    @Test\n    void testDestroyPreventsPassword() {\n        KEY.destroy()\n        try {\n            KEY.toCharArray()\n            fail()\n        } catch (IllegalStateException expected) {\n            assertEquals PasswordSpec.DESTROYED_MSG, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testEquals() {\n        Password key2 = Keys.password(PASSWORD)\n        assertArrayEquals KEY.toCharArray(), key2.toCharArray()\n        assertEquals KEY, key2\n        assertNotEquals KEY, new Object()\n    }\n\n    @Test\n    void testIdentityEquals() {\n        Password key = Keys.password(PASSWORD)\n        assertTrue key.equals(key)\n        assertNotEquals KEY, new Object()\n    }\n\n    @Test\n    void testHashCode() {\n        Password key2 = Keys.password(PASSWORD)\n        assertArrayEquals KEY.toCharArray(), key2.toCharArray()\n        assertEquals KEY.hashCode(), key2.hashCode()\n    }\n\n    @Test\n    void testToString() {\n        assertEquals '<redacted>', KEY.toString()\n        Password key2 = Keys.password(PASSWORD)\n        assertArrayEquals KEY.toCharArray(), key2.toCharArray()\n        assertEquals KEY.toString(), key2.toString()\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/Pbes2HsAkwAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.UnsupportedJwtException\nimport io.jsonwebtoken.impl.DefaultJweHeaderMutator\nimport io.jsonwebtoken.impl.DefaultMutableJweHeader\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.KeyRequest\nimport io.jsonwebtoken.security.Keys\nimport io.jsonwebtoken.security.Password\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\n@SuppressWarnings('SpellCheckingInspection')\nclass Pbes2HsAkwAlgorithmTest {\n\n    private static Password KEY = Keys.password(\"12345678\".toCharArray())\n    private static List<Pbes2HsAkwAlgorithm> ALGS = [Jwts.KEY.PBES2_HS256_A128KW,\n                                                     Jwts.KEY.PBES2_HS384_A192KW,\n                                                     Jwts.KEY.PBES2_HS512_A256KW] as List<Pbes2HsAkwAlgorithm>\n\n    @Test\n    void testInsufficientIterations() {\n        for (Pbes2HsAkwAlgorithm alg : ALGS) {\n            int iterations = 50 // must be 1000 or more\n            def header = Jwts.header().pbes2Count(iterations) as DefaultJweHeaderMutator\n            def mutable = new DefaultMutableJweHeader(header)\n            KeyRequest<Password> req = new DefaultKeyRequest<>(KEY, null, null, mutable, Jwts.ENC.A256GCM)\n            try {\n                alg.getEncryptionKey(req)\n                fail()\n            } catch (IllegalArgumentException iae) {\n                assertEquals Pbes2HsAkwAlgorithm.MIN_ITERATIONS_MSG_PREFIX + iterations, iae.getMessage()\n            }\n        }\n    }\n\n    /**\n     * @since 0.12.4\n     */\n    @Test\n    void testExceedsMaxIterations() {\n        for (Pbes2HsAkwAlgorithm alg : ALGS) {\n            def password = Keys.password('correct horse battery staple'.toCharArray())\n            def iterations = alg.MAX_ITERATIONS + 1\n            // we make the JWE string directly from JSON here (instead of using Jwts.builder()) to avoid\n            // the computational time it would take to create such JWEs with excessive iterations as well as\n            // avoid the builder throwing any exceptions (and this is what a potential attacker would do anyway):\n            def headerJson = \"\"\"\n            {\n              \"p2c\": ${iterations},\n              \"p2s\": \"831BG_z_ZxkN7Rnt5v1iYm1A0bn6VEuxpW4gV7YBMoE\",\n              \"alg\": \"${alg.id}\",\n              \"enc\": \"A256GCM\"\n            }\"\"\"\n            def jwe = Encoders.BASE64URL.encode(Strings.utf8(headerJson)) +\n                    '.OSAhMk3FtaCeZ5v1c8bWBgssEVqx2mCPUEnJUsg4hwIQyrUP-LCYkg.' +\n                    'K4R_-zb4qaZ3R0W8.sGS4mcT_xBhZC1d7G-g.kWqd_4sEsaKrWE_hMZ5HmQ'\n            try {\n                Jwts.parser().decryptWith(password).build().parse(jwe)\n            } catch (UnsupportedJwtException expected) {\n                String msg = \"JWE Header 'p2c' (PBES2 Count) value ${iterations} exceeds ${alg.id} maximum allowed \" +\n                        \"value ${alg.MAX_ITERATIONS}. The larger value is rejected to help mitigate potential \" +\n                        \"Denial of Service attacks.\"\n                //println msg\n                assertEquals msg, expected.message\n            }\n        }\n    }\n\n    // for manual/developer testing only.  Takes a long time and there is no deterministic output to assert\n    /*\n    @Test\n    void test() {\n\n        def alg = Jwts.KEY.PBES2_HS256_A128KW\n\n        int desiredMillis = 100\n        int iterations = Jwts.KEY.estimateIterations(alg, desiredMillis)\n        println \"Estimated iterations: $iterations\"\n\n        int tries = 30\n        int skip = 6\n        //double scale = 0.5035246727\n\n        def password = 'hellowor'.toCharArray()\n        def header = new DefaultJweHeader().pbes2Count(iterations)\n        def key = Keys.password(password)\n        def req = new DefaultKeyRequest(null, null, key, header, Jwts.ENC.A128GCM)\n        int sum = 0\n        for (int i = 0; i < tries; i++) {\n            long start = System.currentTimeMillis()\n            alg.getEncryptionKey(req)\n            long end = System.currentTimeMillis()\n            long duration = end - start\n            if (i >= skip) {\n                sum += duration\n            }\n            println \"Try $i: ${alg.id} took $duration millis\"\n        }\n        long avg = Math.round(sum / (tries - skip))\n        println \"Average duration: $avg\"\n        println \"scale factor: ${desiredMillis / avg}\"\n    }\n     */\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/Pkcs11Test.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Identifiable\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.lang.Assert\nimport io.jsonwebtoken.lang.Classes\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.security.*\nimport java.security.cert.X509Certificate\n\nimport static org.junit.Assert.assertEquals\n\nclass Pkcs11Test {\n\n    static final String PKCS11_LIB_ENV_VAR_NAME = 'JJWT_TEST_PKCS11_LIBRARY'\n    static final List<String> DEFAULT_PKCS11_LIB_LOCATIONS = [\n            // Tried in order:\n            '/opt/homebrew/lib/softhsm/libsofthsm2.so', // macos: brew install softhsm\n            '/usr/lib/softhsm/libsofthsm2.so',          // ubuntu: sudo apt-get install -y softhsm2\n            '/usr/local/lib/libsofthsm2.so',            // other *nixes?\n            '/usr/local/lib/softhsm/libsofthsm2.so',\n            'C:\\\\SoftHSM2\\\\lib\\\\softhsm2-x64.dll',      // https://github.com/disig/SoftHSM2-for-Windows\n            'C:\\\\SoftHSM2\\\\lib\\\\softhsm2.dll'           // https://github.com/disig/SoftHSM2-for-Windows\n    ]\n    //This pin equals the SoftHSM --so-pin and --pin values used in impl/src/test/scripts/softhsm:\n    static final char[] PIN = \"1234\".toCharArray()\n\n    private static PrivateKey getKey(KeyStore ks, Identifiable alg, char[] pin) {\n        // The SunPKCS11 KeyStore does not support Edwards Curve keys at all:\n        if (alg instanceof EdwardsCurve) return null\n        return ks.getKey(alg.id, pin) as PrivateKey\n    }\n\n    @SuppressWarnings(['GroovyAssignabilityCheck', 'UnnecessaryQualifiedReference'])\n    private static Provider getAvailablePkcs11Provider() {\n        String val = Strings.clean(System.getenv(PKCS11_LIB_ENV_VAR_NAME))\n        def paths = []\n        if (val != null) {\n            paths.add(val) // highest priority\n        }\n        paths.addAll(DEFAULT_PKCS11_LIB_LOCATIONS) // remaining priorities\n        File file = null\n        for (String path : paths) {\n            file = new File(path)\n            if (!file.exists()) { // relative path? try to resolve canonical/absolute:\n                URL url = Classes.getResource(path)\n                file = url != null ? new File(url.toURI()) : null\n            }\n            if (file?.exists()) {\n                file = file.getCanonicalFile()\n                break\n            }\n        }\n        Provider provider = null\n        if (file) { // found the lib file, reference it via inline config:\n            String config = \"\"\"--\n            name = jjwt\n            library = ${file.getCanonicalPath()}\n            # Needed for JWT ECDH-ES* algorithms using SunPKCS11 KeyAgreement:\n            # https://stackoverflow.com/questions/51663622/do-sunpkcs11-supports-ck-sensitive-attribute-for-derived-key-using-ecdh\n            attributes(generate,CKO_SECRET_KEY,CKK_GENERIC_SECRET) = {\n              CKA_SENSITIVE = false\n              CKA_EXTRACTABLE = true\n            }\n            \"\"\"\n            if (Provider.metaClass.respondsTo(Provider, 'configure', String)) { // JDK 9 or later\n                provider = Security.getProvider(\"SunPKCS11\")\n                provider = provider.configure(config) as Provider\n            } else { // JDK 8 or earlier:\n                try {\n                    provider = new sun.security.pkcs11.SunPKCS11(config)\n                } catch (Throwable ignored) { // MacOS on JDK 7: libsofthsm2.so is arm64, JDK is x86_64, can't load\n                }\n            }\n        }\n        return provider\n    }\n\n    private static KeyStore loadKeyStore(Provider provider) {\n        if (provider == null) return null\n        KeyStore ks = KeyStore.getInstance(\"PKCS11\", provider)\n        try {\n            ks.load(null, PIN)\n            return ks\n        } catch (Throwable ignored) { // JVM can't support the keys or certs in SoftHSM\n            return null\n        }\n    }\n\n    private static Map<Identifiable, SecretKey> findPkcs11SecretKeys(KeyStore ks) {\n        if (ks == null) return Collections.emptyMap()\n\n        Map<Identifiable, SecretKey> keys = new LinkedHashMap()\n\n        def prot = new KeyStore.PasswordProtection(PIN)\n\n        def algs = [] as List<Identifiable>\n        algs.addAll(Jwts.SIG.get().values().findAll({ it instanceof KeyBuilderSupplier }))\n        algs.addAll(Jwts.ENC.get().values())\n\n        algs.each { Identifiable alg ->\n            // find any previous one:\n            SecretKey key = ks.getKey(alg.id, PIN) as SecretKey\n            if (key == null) { // didn't exist, lazily create it in SoftHSM:\n                key = alg.key().build()\n                if (alg instanceof HmacAesAeadAlgorithm) {\n                    // PKCS11 provider doesn't like non-standard key lengths with the AES algorithm, so we'll\n                    // 'trick' it by using HmacSHA*** alg identifier\n                    def encoded = key.getEncoded()\n                    long bitlen = Bytes.bitLength(encoded)\n                    key = new SecretKeySpec(key.getEncoded(), \"HmacSHA${bitlen}\")\n                }\n                KeyStore.Entry entry = new KeyStore.SecretKeyEntry(key)\n                ks.setEntry(alg.id, entry, prot)\n                // it's saved now, but we need to look up the (restricted) PKCS11 representation to use that during\n                // testing to more accurately reflect real/restricted HSM access:\n                key = ks.getKey(alg.id, PIN) as SecretKey\n            }\n            keys.put(alg, key)\n        }\n\n        return keys\n    }\n\n    private static Map<String, TestKeys.Bundle> findPkcs11Bundles(KeyStore ks) {\n        if (ks == null) return Collections.emptyMap()\n\n        Map<String, TestKeys.Bundle> bundles = new LinkedHashMap()\n\n        def algs = []\n        algs.addAll(Jwts.SIG.get().values().findAll({\n            it instanceof KeyPairBuilderSupplier && it.id != 'EdDSA'\n        }))\n        algs.addAll(Jwks.CRV.get().values().findAll({ it instanceof EdwardsCurve }))\n\n        for (Identifiable alg : algs) {\n            def priv = getKey(ks, alg, PIN)\n            def cert = ks.getCertificate(alg.id) as X509Certificate\n            // cert will be null for any PS* algs since  SoftHSM2 doesn't support them yet:\n            // https://github.com/opendnssec/SoftHSMv2/issues/721\n            def pub = cert?.getPublicKey()\n            def bundle = new TestKeys.Bundle(alg, pub, priv, cert)\n            bundles.put(alg.id, bundle)\n        }\n\n        return Collections.<String, TestKeys.Bundle> unmodifiableMap(bundles)\n    }\n\n    static final Provider PKCS11 = getAvailablePkcs11Provider()\n\n    static final KeyStore KEYSTORE = loadKeyStore(PKCS11)\n\n    static final Map<Identifiable, SecretKey> PKCS11_SECRETKEYS = findPkcs11SecretKeys(KEYSTORE)\n\n    /**\n     * Maintainers note:\n     *\n     * This collection will only contain relevant entries when the following are true:\n     *\n     * 1. We're running on a machine that has an expected SoftHSM installation populated with entries\n     *    via the impl/src/test/scripts/softhsm script in this git repository.\n     *\n     * 2. The current JVM version supports the key algorithm identified in the PKCS11 PrivateKey or X509Certificate.\n     *    This means:\n     *      - On JDK < 15, Ed25519 and Ed448 PrivateKeys cannot be loaded (but their certs and PublicKeys may be\n     *        able to be loaded if/when Sun Provider implementation supports generic X509 encoding).\n     *      - On JDK < 11 X25519 and X448 PrivateKeys cannot be loaded (but their certs and PublicKeys may be).\n     *\n     * 3. RSASSA-PSS keys of any kind are not available because SoftHSM doesn't currently support them. See\n     *    https://github.com/opendnssec/SoftHSMv2/issues/721*/\n    static final Map<String, TestKeys.Bundle> PKCS11_BUNDLES = findPkcs11Bundles(KEYSTORE)\n\n    static TestKeys.Bundle findPkcs11(Identifiable alg) {\n        return PKCS11_BUNDLES.get(alg.getId())\n    }\n\n    static SecretKey findPkcs11SecretKey(Identifiable alg) {\n        return PKCS11_SECRETKEYS.get(alg)\n    }\n\n    static java.security.KeyPair findPkcs11Pair(Identifiable alg) {\n        return findPkcs11(alg as Identifiable)?.pair\n    }\n\n    /**\n     * @param keyProvider the explicit provider to use with JwtBuilder/Parser calls or {@code null} to use the JVM default\n     * provider(s).\n     */\n    static void testJws(Provider keyProvider) {\n\n        def algs = [] as List<Identifiable>\n        algs.addAll(Jwts.SIG.get().values().findAll({ it != Jwts.SIG.EdDSA })) // EdDSA accounted for by next two:\n        algs.add(Jwks.CRV.Ed25519)\n        algs.add(Jwks.CRV.Ed448)\n\n        for (Identifiable alg : algs) {\n            def signKey, verifyKey // same for Mac algorithms, priv/pub for sig algorithms\n            if (alg instanceof MacAlgorithm) {\n                signKey = verifyKey = findPkcs11SecretKey(alg)\n            } else { // SignatureAlgorithm\n                java.security.KeyPair pair = findPkcs11Pair(alg)\n                signKey = pair?.private\n                verifyKey = pair?.public\n            }\n            if (!signKey) continue // not supported by Either the SunPKCS11 provider or SoftHSM2, so we have to try next\n\n            alg = alg instanceof Curve ? Jwts.SIG.EdDSA : alg as SecureDigestAlgorithm\n\n            // We might need to specify the PKCS11 provider since we can't access the private key material:\n            def jws = Jwts.builder().provider(keyProvider).issuer('me').signWith(signKey, alg).compact()\n\n            def builder = Jwts.parser()\n            if (verifyKey instanceof SecretKey) {\n                // We only need to specify a provider during parsing for MAC HSM keys: SignatureAlgorithm verification\n                // only needs the PublicKey, and a recipient doesn't need/won't have an HSM for public material anyway.\n                verifyKey = Keys.builder(verifyKey).provider(keyProvider).build()\n                builder.verifyWith(verifyKey as SecretKey)\n            } else {\n                builder.verifyWith(verifyKey as PublicKey)\n            }\n            String iss = builder.build().parseSignedClaims(jws).getPayload().getIssuer()\n\n            assertEquals 'me', iss\n        }\n    }\n\n    @Test\n    void testJws() {\n        testJws(PKCS11)\n    }\n\n    // create a jwe and then decrypt it\n    static void encRoundtrip(TestKeys.Bundle bundle, def keyalg, Provider provider /* may be null */) {\n        def pair = bundle.pair\n        def pub = pair.public\n        def priv = pair.private\n        if (pub.getAlgorithm().startsWith(EdwardsCurve.OID_PREFIX)) {\n            // If < JDK 11, the PKCS11 KeyStore doesn't understand X25519 and X448 algorithms, and just returns\n            // a generic X509Key from the X.509 certificate, but that can't be used for encryption.  So we'll\n            // use BouncyCastle to try and load the public key that way.  This is ok for testing because the\n            // public key doesn't need to be a PKCS11 key since public key material is already available.\n            // Decryption does need to use a PKCS11 key however since that is what allows us to assert\n            // a valid test\n            def cert = new JcaTemplate(\"X.509\", TestKeys.BC).generateX509Certificate(bundle.cert.getEncoded())\n            bundle.cert = cert\n            bundle.chain = [cert]\n            bundle.pair = new java.security.KeyPair(cert.getPublicKey(), priv)\n            pub = bundle.pair.public\n        }\n\n        // Encryption uses the public key, and that key material is available, so no need for the PKCS11 provider:\n        String jwe = Jwts.builder().issuer('me').encryptWith(pub, keyalg, Jwts.ENC.A256GCM).compact()\n\n        // The private key can be null if SunPKCS11 doesn't support the key algorithm directly.  In this case\n        // encryption only worked because generic X.509 decoding (from the key certificate in the keystore) produced the\n        // public key.  So we can only decrypt if SunPKCS11 supports the private key, so check for non-null:\n        if (priv) {\n            // Decryption may need private material inside the HSM:\n            priv = Keys.builder(pair.private).publicKey(pub).provider(provider).build()\n\n            String iss = Jwts.parser().decryptWith(priv).build().parseEncryptedClaims(jwe).getPayload().getIssuer()\n            assertEquals 'me', iss\n        }\n    }\n\n    static void testJwe(Provider provider) {\n        def algs = []\n        algs.addAll(Jwts.SIG.get().values().findAll({\n            it.id.startsWith('RS') || it.id.startsWith('ES')\n            // unfortunately we can't also match .startsWith('PS') because SoftHSM2 doesn't support RSA-PSS keys :(\n            // see https://github.com/opendnssec/SoftHSMv2/issues/721\n        }))\n        // For Edwards key agreement, we can look up the public key via the X.509 cert, but SunPKCS11 doesn't\n        // support reading the private keys :(\n        // With the public key, we can at least encrypt, but we won't be able to decrypt since that needs the private key\n        algs.add(Jwks.CRV.X25519)\n        algs.add(Jwks.CRV.X448)\n\n        algs.each {\n\n            def bundle = findPkcs11(it as Identifiable) // bundle will be null with PSS* algs\n\n            if (bundle?.pair?.public) { // null on JDK 13 w/ X25519 and X448\n                String name = Assert.hasText(bundle.pair.public.algorithm, \"PublicKey algorithm cannot be null/empty\")\n                if (name == 'RSA') {\n                    // SunPKCS11 doesn't support RSA-OAEP* ciphers :(\n                    // So we can only try with RSA1_5 and we have to skip RSA_OAEP and RSA_OAEP_256:\n                    encRoundtrip(bundle, Jwts.KEY.RSA1_5, provider)\n                } else if (StandardCurves.findByKey(bundle.pair.public) != null) { // EC or Ed key\n                    // try all ECDH key algorithms:\n                    Jwts.KEY.get().values().findAll({ it.id.startsWith('ECDH-ES') }).each {\n                        encRoundtrip(bundle, it, provider)\n                    }\n                } else {\n                    throw new IllegalStateException(\"Unexpected key algorithm: $name\")\n                }\n            }\n        }\n    }\n\n    @Test\n    void testJwe() {\n        testJwe(PKCS11)\n    }\n\n    /**\n     * Ensures that for all JWE and JWS algorithms, when the PKCS11 provider is installed as a JVM provider, \n     * no calls to JwtBuilder/Parser .provider are needed, and no ProviderKeys (Keys.builder) calls are needed\n     * anywhere in application code.*/\n    @Test\n    void testPkcs11JvmProviderDoesNotRequireProviderKeys() {\n        if (PKCS11 == null) return; // couldn't load on MacOS (arm64 libsofthsm2.so) on JDK 7 (x86_64)\n        Security.addProvider(PKCS11)\n        try {\n            testJws(null)\n            testJwe(null)\n        } finally {\n            Security.removeProvider(PKCS11.getName())\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/PrivateConstructorsTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.lang.Functions\nimport io.jsonwebtoken.lang.Classes\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nclass PrivateConstructorsTest {\n\n    @Test\n    void testPrivateCtors() { // for code coverage only\n        new Classes()\n        new KeysBridge()\n        new JwksBridge()\n        new Functions()\n        new Jwts.SIG()\n        new Jwts.ENC()\n        new Jwts.KEY()\n        new Jwts.ZIP()\n        new Jwks.CRV()\n        new Jwks.HASH()\n        new Jwks.OP()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/PrivateECKeyTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertArrayEquals\nimport static org.junit.Assert.assertEquals\n\nclass PrivateECKeyTest {\n\n    @Test\n    void testGetAlgorithm() {\n        TestKeys.EC.each {\n            def priv = it.pair.private\n            def key = new PrivateECKey(priv, it.pair.public.getParams())\n            assertEquals priv.getAlgorithm(), key.getAlgorithm()\n        }\n    }\n\n    @Test\n    void testGetFormat() {\n        TestKeys.EC.each {\n            def priv = it.pair.private\n            def key = new PrivateECKey(priv, it.pair.public.getParams())\n            assertEquals priv.getFormat(), key.getFormat()\n        }\n    }\n\n    @Test\n    void testGetEncoded() {\n        TestKeys.EC.each {\n            def priv = it.pair.private\n            def key = new PrivateECKey(priv, it.pair.public.getParams())\n            assertArrayEquals priv.getEncoded(), key.getEncoded()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ProvidedKeyBuilderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.security.Keys\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.security.Provider\n\nimport static org.junit.Assert.assertSame\n\nclass ProvidedKeyBuilderTest {\n\n    @Test\n    void testBuildWithSpecifiedProviderKey() {\n        Provider provider = new TestProvider()\n        SecretKey key = new SecretKeySpec(Bytes.random(256), 'AES')\n        def providerKey = Keys.builder(key).provider(provider).build() as ProviderSecretKey\n\n        assertSame provider, providerKey.getProvider()\n        assertSame key, providerKey.getKey()\n\n        // now for the test: ensure that our provider key isn't wrapped again\n        SecretKey returned = Keys.builder(providerKey).provider(new TestProvider('different')).build()\n\n        assertSame providerKey, returned // not wrapped again\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ProvidedSecretKeyBuilderTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.Keys\nimport org.junit.Test\n\nimport static org.junit.Assert.assertSame\n\nclass ProvidedSecretKeyBuilderTest {\n\n    @Test\n    void testBuildPasswordWithoutProvider() {\n        def password = Keys.password('foo'.toCharArray())\n        assertSame password, Keys.builder(password).build() // does not wrap in ProviderKey\n    }\n\n    @Test\n    void testBuildPasswordWithProvider() {\n        def password = Keys.password('foo'.toCharArray())\n        assertSame password, Keys.builder(password).provider(new TestProvider()).build() // does not wrap in ProviderKey\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ProviderKeyTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport org.junit.Test\n\nimport java.security.Provider\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertSame\n\nclass ProviderKeyTest {\n\n    static final Provider PROVIDER = new TestProvider()\n\n    @Test(expected = IllegalArgumentException)\n    void testConstructorWithNullProvider() {\n        new ProviderKey<>(null, TestKeys.HS256)\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testConstructorWithNullKey() {\n        new ProviderKey<>(PROVIDER, null)\n    }\n\n    @Test\n    void testConstructorWithProviderKey() {\n        def key = new ProviderKey(PROVIDER, TestKeys.HS256)\n        // wrapping throws an exception:\n        try {\n            new ProviderKey<>(PROVIDER, key)\n        } catch (IllegalArgumentException iae) {\n            String msg = 'Nesting not permitted.'\n            assertEquals msg, iae.getMessage()\n        }\n    }\n\n    @Test\n    void testGetKey() {\n        def src = new TestKey()\n        def key = new ProviderKey(PROVIDER, src)\n        assertSame src, key.getKey()\n    }\n\n    @Test\n    void testGetProvider() {\n        def src = new TestKey()\n        def key = new ProviderKey(PROVIDER, src)\n        assertSame PROVIDER, key.getProvider()\n    }\n\n    @Test\n    void testGetAlgorithm() {\n        String name = 'myAlg'\n        def key = new ProviderKey(PROVIDER, new TestKey(algorithm: name))\n        assertEquals name, key.getAlgorithm()\n    }\n\n    @Test\n    void testGetFormat() {\n        String name = 'myFormat'\n        def key = new ProviderKey(PROVIDER, new TestKey(format: name))\n        assertEquals name, key.getFormat()\n    }\n\n    @Test\n    void testGetEncoded() {\n        byte[] encoded = Bytes.random(256)\n        def key = new ProviderKey(PROVIDER, new TestKey(encoded: encoded))\n        assertSame encoded, key.getEncoded()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ProvidersTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.lang.Classes\nimport org.junit.After\nimport org.junit.Before\nimport org.junit.Test\n\nimport java.security.Provider\nimport java.security.Security\n\nimport static org.junit.Assert.*\n\nclass ProvidersTest {\n\n    @Before\n    void before() {\n        cleanup() // ensure we start clean\n    }\n\n    @After\n    void after() {\n        cleanup() // ensure we end clean\n    }\n\n    static void cleanup() {\n        //ensure test environment is cleaned up:\n        Providers.BC_PROVIDER.set(null)\n        Security.removeProvider(\"BC\")\n        assertFalse bcRegistered() // ensure clean\n    }\n\n    static boolean bcRegistered() {\n        for (Provider p : Security.getProviders()) {\n            // do not reference the Providers class constant here - this is a utility method that could be used in\n            // other test classes that use static mocks and the `Provider` class might not be able to initialized\n            if (p.getClass().getName().equals(\"org.bouncycastle.jce.provider.BouncyCastleProvider\")) {\n                return true\n            }\n        }\n        return false\n    }\n\n    @Test\n    void testPrivateCtor() { // for code coverage only\n        new Providers()\n    }\n\n    @Test\n    void testBouncyCastleAlreadyExists() {\n\n        // ensure we don't have one yet:\n        assertNull Providers.BC_PROVIDER.get()\n        assertFalse bcRegistered()\n\n        //now register one in the JVM provider list:\n        Provider bc = Classes.newInstance(Providers.BC_PROVIDER_CLASS_NAME)\n        assertEquals \"BC\", bc.getName()\n        Security.addProvider(bc)\n        assertTrue bcRegistered() // ensure it exists in the system as expected\n\n        //now ensure that we find it and cache it:\n        def returned = Providers.findBouncyCastle()\n        assertSame bc, returned\n        assertSame bc, Providers.BC_PROVIDER.get() // ensure cached for future lookup\n\n        //ensure cache hit works:\n        assertSame bc, Providers.findBouncyCastle()\n\n        //cleanup() method will remove the provider from the system\n    }\n\n    @Test\n    void testBouncyCastleCreatedIfAvailable() {\n        // ensure we don't have one yet:\n        assertNull Providers.BC_PROVIDER.get()\n        assertFalse bcRegistered()\n\n        // ensure we can create one and cache it, *without* modifying the system JVM:\n        //now ensure that we find it and cache it:\n        def returned = Providers.findBouncyCastle()\n        assertNotNull returned\n        assertSame Providers.BC_PROVIDER.get(), returned //ensure cached for future lookup\n        assertFalse bcRegistered() //ensure we don't alter the system environment\n\n        assertSame returned, Providers.findBouncyCastle() //ensure cache hit\n        assertFalse bcRegistered() //ensure we don't alter the system environment\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/ProvidersWithoutBCTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.lang.Classes\nimport org.junit.After\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.powermock.core.classloader.annotations.PrepareForTest\nimport org.powermock.modules.junit4.PowerMockRunner\n\nimport static org.easymock.EasyMock.eq\nimport static org.easymock.EasyMock.expect\nimport static org.junit.Assert.assertFalse\nimport static org.junit.Assert.assertNull\nimport static org.powermock.api.easymock.PowerMock.*\n\n@RunWith(PowerMockRunner.class)\n@PrepareForTest([Classes])\nclass ProvidersWithoutBCTest {\n\n    @After\n    void after() {\n        ProvidersTest.cleanup() //ensure environment is clean\n    }\n\n    @Test\n    void testBouncyCastleClassNotAvailable() {\n        mockStatic(Classes)\n        expect(Classes.isAvailable(eq(\"org.bouncycastle.jce.provider.BouncyCastleProvider\"))).andReturn(Boolean.FALSE).anyTimes()\n        replay Classes\n        assertNull Providers.findBouncyCastle() // one should not be created/exist\n        verify Classes\n        assertFalse ProvidersTest.bcRegistered() // nothing should be in the environment\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7516AppendixA1Test.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwe\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPrivateJwk\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.StandardCharsets\nimport java.security.interfaces.RSAPrivateKey\n\nimport static org.junit.Assert.assertArrayEquals\nimport static org.junit.Assert.assertEquals\n\n/**\n * Tests successful parsing/decryption of a 'JWE using RSAES-OAEP and AES GCM' as defined in\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1\">RFC 7516, Appendix A.1</a>\n *\n * @since 0.12.0\n */\nclass RFC7516AppendixA1Test {\n\n    static String encode(byte[] b) {\n        return Encoders.BASE64URL.encode(b)\n    }\n\n    static byte[] decode(String val) {\n        return Decoders.BASE64URL.decode(val)\n    }\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1 :\n    final static String PLAINTEXT = 'The true sign of intelligence is not knowledge but imagination.' as String\n    final static byte[] PLAINTEXT_BYTES = [84, 104, 101, 32, 116, 114, 117, 101, 32, 115, 105, 103, 110, 32,\n                                           111, 102, 32, 105, 110, 116, 101, 108, 108, 105, 103, 101, 110, 99,\n                                           101, 32, 105, 115, 32, 110, 111, 116, 32, 107, 110, 111, 119, 108,\n                                           101, 100, 103, 101, 32, 98, 117, 116, 32, 105, 109, 97, 103, 105,\n                                           110, 97, 116, 105, 111, 110, 46] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1.1 :\n    final static String PROT_HEADER_STRING = '{\"alg\":\"RSA-OAEP\",\"enc\":\"A256GCM\"}' as String\n    final static String encodedHeader = 'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ' as String\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1.2\n    final static byte[] CEK_BYTES = [177, 161, 244, 128, 84, 143, 225, 115, 63, 180, 3, 255, 107, 154,\n                                     212, 246, 138, 7, 110, 91, 112, 46, 34, 105, 47, 130, 203, 46, 122,\n                                     234, 64, 252] as byte[]\n    final static SecretKey CEK = new SecretKeySpec(CEK_BYTES, \"AES\")\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1.3\n    final static Map<String, String> KEK_VALUES = [\n            \"kty\": \"RSA\",\n            \"n\"  : \"oahUIoWw0K0usKNuOR6H4wkf4oBUXHTxRvgb48E-BVvxkeDNjbC4he8rUW\" +\n                    \"cJoZmds2h7M70imEVhRU5djINXtqllXI4DFqcI1DgjT9LewND8MW2Krf3S\" +\n                    \"psk_ZkoFnilakGygTwpZ3uesH-PFABNIUYpOiN15dsQRkgr0vEhxN92i2a\" +\n                    \"sbOenSZeyaxziK72UwxrrKoExv6kc5twXTq4h-QChLOln0_mtUZwfsRaMS\" +\n                    \"tPs6mS6XrgxnxbWhojf663tuEQueGC-FCMfra36C9knDFGzKsNa7LZK2dj\" +\n                    \"YgyD3JR_MB_4NUJW_TqOQtwHYbxevoJArm-L5StowjzGy-_bq6Gw\",\n            \"e\"  : \"AQAB\",\n            \"d\"  : \"kLdtIj6GbDks_ApCSTYQtelcNttlKiOyPzMrXHeI-yk1F7-kpDxY4-WY5N\" +\n                    \"WV5KntaEeXS1j82E375xxhWMHXyvjYecPT9fpwR_M9gV8n9Hrh2anTpTD9\" +\n                    \"3Dt62ypW3yDsJzBnTnrYu1iwWRgBKrEYY46qAZIrA2xAwnm2X7uGR1hghk\" +\n                    \"qDp0Vqj3kbSCz1XyfCs6_LehBwtxHIyh8Ripy40p24moOAbgxVw3rxT_vl\" +\n                    \"t3UVe4WO3JkJOzlpUf-KTVI2Ptgm-dARxTEtE-id-4OJr0h-K-VFs3VSnd\" +\n                    \"VTIznSxfyrj8ILL6MG_Uv8YAu7VILSB3lOW085-4qE3DzgrTjgyQ\",\n            \"p\"  : \"1r52Xk46c-LsfB5P442p7atdPUrxQSy4mti_tZI3Mgf2EuFVbUoDBvaRQ-\" +\n                    \"SWxkbkmoEzL7JXroSBjSrK3YIQgYdMgyAEPTPjXv_hI2_1eTSPVZfzL0lf\" +\n                    \"fNn03IXqWF5MDFuoUYE0hzb2vhrlN_rKrbfDIwUbTrjjgieRbwC6Cl0\",\n            \"q\"  : \"wLb35x7hmQWZsWJmB_vle87ihgZ19S8lBEROLIsZG4ayZVe9Hi9gDVCOBm\" +\n                    \"UDdaDYVTSNx_8Fyw1YYa9XGrGnDew00J28cRUoeBB_jKI1oma0Orv1T9aX\" +\n                    \"IWxKwd4gvxFImOWr3QRL9KEBRzk2RatUBnmDZJTIAfwTs0g68UZHvtc\",\n            \"dp\" : \"ZK-YwE7diUh0qR1tR7w8WHtolDx3MZ_OTowiFvgfeQ3SiresXjm9gZ5KL\" +\n                    \"hMXvo-uz-KUJWDxS5pFQ_M0evdo1dKiRTjVw_x4NyqyXPM5nULPkcpU827\" +\n                    \"rnpZzAJKpdhWAgqrXGKAECQH0Xt4taznjnd_zVpAmZZq60WPMBMfKcuE\",\n            \"dq\" : \"Dq0gfgJ1DdFGXiLvQEZnuKEN0UUmsJBxkjydc3j4ZYdBiMRAy86x0vHCj\" +\n                    \"ywcMlYYg4yoC4YZa9hNVcsjqA3FeiL19rk8g6Qn29Tt0cj8qqyFpz9vNDB\" +\n                    \"UfCAiJVeESOjJDZPYHdHY8v1b-o-Z2X5tvLx-TCekf7oxyeKDUqKWjis\",\n            \"qi\" : \"VIMpMYbPf47dT1w_zDUXfPimsSegnMOA1zTaX7aGk_8urY6R8-ZW1FxU7\" +\n                    \"AlWAyLWybqq6t16VFd7hQd0y6flUK4SlOydB61gwanOsXGOAOv82cHq0E3\" +\n                    \"eL4HrtZkUuKvnPrMnsUUFlfUdybVzxyjz9JF_XyaY14ardLSjf4L_FNY\"\n    ]\n\n    final static byte[] ENCRYPTED_CEK_BYTES = [56, 163, 154, 192, 58, 53, 222, 4, 105, 218, 136, 218, 29, 94, 203,\n                                               22, 150, 92, 129, 94, 211, 232, 53, 89, 41, 60, 138, 56, 196, 216,\n                                               82, 98, 168, 76, 37, 73, 70, 7, 36, 8, 191, 100, 136, 196, 244, 220,\n                                               145, 158, 138, 155, 4, 117, 141, 230, 199, 247, 173, 45, 182, 214,\n                                               74, 177, 107, 211, 153, 11, 205, 196, 171, 226, 162, 128, 171, 182,\n                                               13, 237, 239, 99, 193, 4, 91, 219, 121, 223, 107, 167, 61, 119, 228,\n                                               173, 156, 137, 134, 200, 80, 219, 74, 253, 56, 185, 91, 177, 34, 158,\n                                               89, 154, 205, 96, 55, 18, 138, 43, 96, 218, 215, 128, 124, 75, 138,\n                                               243, 85, 25, 109, 117, 140, 26, 155, 249, 67, 167, 149, 231, 100, 6,\n                                               41, 65, 214, 251, 232, 87, 72, 40, 182, 149, 154, 168, 31, 193, 126,\n                                               215, 89, 28, 111, 219, 125, 182, 139, 235, 195, 197, 23, 234, 55, 58,\n                                               63, 180, 68, 202, 206, 149, 75, 205, 248, 176, 67, 39, 178, 60, 98,\n                                               193, 32, 238, 122, 96, 158, 222, 57, 183, 111, 210, 55, 188, 215,\n                                               206, 180, 166, 150, 166, 106, 250, 55, 229, 72, 40, 69, 214, 216,\n                                               104, 23, 40, 135, 212, 28, 127, 41, 80, 175, 174, 168, 115, 171, 197,\n                                               89, 116, 92, 103, 246, 83, 216, 182, 176, 84, 37, 147, 35, 45, 219,\n                                               172, 99, 226, 233, 73, 37, 124, 42, 72, 49, 242, 35, 127, 184, 134,\n                                               117, 114, 135, 206] as byte[]\n\n    final static String encodedEncryptedCek = 'OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe' +\n            'ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb' +\n            'Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV' +\n            'mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8' +\n            '1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi' +\n            '6UklfCpIMfIjf7iGdXKHzg' as String\n\n    // https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1.4\n    final static byte[] IV = [227, 197, 117, 252, 2, 219, 233, 68, 180, 225, 77, 219] as byte[]\n    final static String encodedIv = '48V1_ALb6US04U3b' as String\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1.5\n    final static byte[] AAD_BYTES = [101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69,\n                                     116, 84, 48, 70, 70, 85, 67, 73, 115, 73, 109, 86, 117, 89, 121, 73,\n                                     54, 73, 107, 69, 121, 78, 84, 90, 72, 81, 48, 48, 105, 102, 81] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1.6\n    final static byte[] CIPHERTEXT = [229, 236, 166, 241, 53, 191, 115, 196, 174, 43, 73, 109, 39, 122,\n                                      233, 96, 140, 206, 120, 52, 51, 237, 48, 11, 190, 219, 186, 80, 111,\n                                      104, 50, 142, 47, 167, 59, 61, 181, 127, 196, 21, 40, 82, 242, 32,\n                                      123, 143, 168, 226, 73, 216, 176, 144, 138, 247, 106, 60, 16, 205,\n                                      160, 109, 64, 63, 192] as byte[]\n    final static String encodedCiphertext =\n            '5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6jiSdiwkIr3ajwQzaBtQD_A' as String\n\n    final static byte[] TAG = [92, 80, 104, 49, 133, 25, 161, 215, 173, 101, 219, 211, 136, 91, 210, 145] as byte[]\n    final static String encodedTag = 'XFBoMYUZodetZdvTiFvSkQ'\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.1.7\n    final static String COMPLETE_JWE = '' +\n            'eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.' +\n            'OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe' +\n            'ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb' +\n            'Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV' +\n            'mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8' +\n            '1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi' +\n            '6UklfCpIMfIjf7iGdXKHzg.' +\n            '48V1_ALb6US04U3b.' +\n            '5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji' +\n            'SdiwkIr3ajwQzaBtQD_A.' +\n            'XFBoMYUZodetZdvTiFvSkQ' as String\n\n    @Test\n    void test() {\n        //ensure our test constants are correctly copied and match the RFC values:\n        assertEquals PLAINTEXT, new String(PLAINTEXT_BYTES, StandardCharsets.UTF_8)\n        assertEquals PROT_HEADER_STRING, new String(decode(encodedHeader), StandardCharsets.UTF_8)\n        assertEquals encodedEncryptedCek, encode(ENCRYPTED_CEK_BYTES)\n        assertEquals encodedIv, encode(IV)\n        assertArrayEquals AAD_BYTES, encodedHeader.getBytes(StandardCharsets.US_ASCII)\n        assertArrayEquals CIPHERTEXT, decode(encodedCiphertext)\n        assertArrayEquals TAG, decode(encodedTag)\n\n        //read the RFC Test JWK to get the private key for decrypting\n        RsaPrivateJwk jwk = Jwks.builder().add(KEK_VALUES).build() as RsaPrivateJwk\n        RSAPrivateKey privKey = jwk.toKey()\n\n        Jwe<byte[]> jwe = Jwts.parser().decryptWith(privKey).build().parseEncryptedContent(COMPLETE_JWE)\n        assertEquals PLAINTEXT, new String(jwe.getPayload(), StandardCharsets.UTF_8)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7516AppendixA2Test.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwe\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPrivateJwk\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.StandardCharsets\nimport java.security.interfaces.RSAPrivateKey\n\nimport static org.junit.Assert.assertArrayEquals\nimport static org.junit.Assert.assertEquals\n\n/**\n * Tests successful parsing/decryption of a 'JWE using RSAES-PKCS1-v1_5 and AES_128_CBC_HMAC_SHA_256' as defined in\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2\">RFC 7516, Appendix A.2</a>\n *\n * @since 0.12.0\n */\nclass RFC7516AppendixA2Test {\n\n    static String encode(byte[] b) {\n        return Encoders.BASE64URL.encode(b)\n    }\n\n    static byte[] decode(String val) {\n        return Decoders.BASE64URL.decode(val)\n    }\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2 :\n    final static String PLAINTEXT = 'Live long and prosper.' as String\n    final static byte[] PLAINTEXT_BYTES = [76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32,\n                                           112, 114, 111, 115, 112, 101, 114, 46] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.1 :\n    final static String PROT_HEADER_STRING = '{\"alg\":\"RSA1_5\",\"enc\":\"A128CBC-HS256\"}' as String\n    final static String encodedHeader = 'eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0' as String\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.2\n    final static byte[] CEK_BYTES = [4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106,\n                                     206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156,\n                                     44, 207] as byte[]\n    final static SecretKey CEK = new SecretKeySpec(CEK_BYTES, \"AES\")\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.3\n    final static Map<String, String> KEK_VALUES = [\n            \"kty\": \"RSA\",\n            \"n\"  : \"sXchDaQebHnPiGvyDOAT4saGEUetSyo9MKLOoWFsueri23bOdgWp4Dy1Wl\" +\n                    \"UzewbgBHod5pcM9H95GQRV3JDXboIRROSBigeC5yjU1hGzHHyXss8UDpre\" +\n                    \"cbAYxknTcQkhslANGRUZmdTOQ5qTRsLAt6BTYuyvVRdhS8exSZEy_c4gs_\" +\n                    \"7svlJJQ4H9_NxsiIoLwAEk7-Q3UXERGYw_75IDrGA84-lA_-Ct4eTlXHBI\" +\n                    \"Y2EaV7t7LjJaynVJCpkv4LKjTTAumiGUIuQhrNhZLuF_RJLqHpM2kgWFLU\" +\n                    \"7-VTdL1VbC2tejvcI2BlMkEpk1BzBZI0KQB0GaDWFLN-aEAw3vRw\",\n            \"e\"  : \"AQAB\",\n            \"d\"  : \"VFCWOqXr8nvZNyaaJLXdnNPXZKRaWCjkU5Q2egQQpTBMwhprMzWzpR8Sxq\" +\n                    \"1OPThh_J6MUD8Z35wky9b8eEO0pwNS8xlh1lOFRRBoNqDIKVOku0aZb-ry\" +\n                    \"nq8cxjDTLZQ6Fz7jSjR1Klop-YKaUHc9GsEofQqYruPhzSA-QgajZGPbE_\" +\n                    \"0ZaVDJHfyd7UUBUKunFMScbflYAAOYJqVIVwaYR5zWEEceUjNnTNo_CVSj\" +\n                    \"-VvXLO5VZfCUAVLgW4dpf1SrtZjSt34YLsRarSb127reG_DUwg9Ch-Kyvj\" +\n                    \"T1SkHgUWRVGcyly7uvVGRSDwsXypdrNinPA4jlhoNdizK2zF2CWQ\",\n            \"p\"  : \"9gY2w6I6S6L0juEKsbeDAwpd9WMfgqFoeA9vEyEUuk4kLwBKcoe1x4HG68\" +\n                    \"ik918hdDSE9vDQSccA3xXHOAFOPJ8R9EeIAbTi1VwBYnbTp87X-xcPWlEP\" +\n                    \"krdoUKW60tgs1aNd_Nnc9LEVVPMS390zbFxt8TN_biaBgelNgbC95sM\",\n            \"q\"  : \"uKlCKvKv_ZJMVcdIs5vVSU_6cPtYI1ljWytExV_skstvRSNi9r66jdd9-y\" +\n                    \"BhVfuG4shsp2j7rGnIio901RBeHo6TPKWVVykPu1iYhQXw1jIABfw-MVsN\" +\n                    \"-3bQ76WLdt2SDxsHs7q7zPyUyHXmps7ycZ5c72wGkUwNOjYelmkiNS0\",\n            \"dp\" : \"w0kZbV63cVRvVX6yk3C8cMxo2qCM4Y8nsq1lmMSYhG4EcL6FWbX5h9yuv\" +\n                    \"ngs4iLEFk6eALoUS4vIWEwcL4txw9LsWH_zKI-hwoReoP77cOdSL4AVcra\" +\n                    \"Hawlkpyd2TWjE5evgbhWtOxnZee3cXJBkAi64Ik6jZxbvk-RR3pEhnCs\",\n            \"dq\" : \"o_8V14SezckO6CNLKs_btPdFiO9_kC1DsuUTd2LAfIIVeMZ7jn1Gus_Ff\" +\n                    \"7B7IVx3p5KuBGOVF8L-qifLb6nQnLysgHDh132NDioZkhH7mI7hPG-PYE_\" +\n                    \"odApKdnqECHWw0J-F0JWnUd6D2B_1TvF9mXA2Qx-iGYn8OVV1Bsmp6qU\",\n            \"qi\" : \"eNho5yRBEBxhGBtQRww9QirZsB66TrfFReG_CcteI1aCneT0ELGhYlRlC\" +\n                    \"tUkTRclIfuEPmNsNDPbLoLqqCVznFbvdB7x-Tl-m0l_eFTj2KiqwGqE9PZ\" +\n                    \"B9nNTwMVvH3VRRSLWACvPnSiwP8N5Usy-WRXS-V7TbpxIhvepTfE0NNo\"\n    ]\n\n    final static byte[] ENCRYPTED_CEK_BYTES = [80, 104, 72, 58, 11, 130, 236, 139, 132, 189, 255, 205, 61, 86, 151,\n                                               176, 99, 40, 44, 233, 176, 189, 205, 70, 202, 169, 72, 40, 226, 181,\n                                               156, 223, 120, 156, 115, 232, 150, 209, 145, 133, 104, 112, 237, 156,\n                                               116, 250, 65, 102, 212, 210, 103, 240, 177, 61, 93, 40, 71, 231, 223,\n                                               226, 240, 157, 15, 31, 150, 89, 200, 215, 198, 203, 108, 70, 117, 66,\n                                               212, 238, 193, 205, 23, 161, 169, 218, 243, 203, 128, 214, 127, 253,\n                                               215, 139, 43, 17, 135, 103, 179, 220, 28, 2, 212, 206, 131, 158, 128,\n                                               66, 62, 240, 78, 186, 141, 125, 132, 227, 60, 137, 43, 31, 152, 199,\n                                               54, 72, 34, 212, 115, 11, 152, 101, 70, 42, 219, 233, 142, 66, 151,\n                                               250, 126, 146, 141, 216, 190, 73, 50, 177, 146, 5, 52, 247, 28, 197,\n                                               21, 59, 170, 247, 181, 89, 131, 241, 169, 182, 246, 99, 15, 36, 102,\n                                               166, 182, 172, 197, 136, 230, 120, 60, 58, 219, 243, 149, 94, 222,\n                                               150, 154, 194, 110, 227, 225, 112, 39, 89, 233, 112, 207, 211, 241,\n                                               124, 174, 69, 221, 179, 107, 196, 225, 127, 167, 112, 226, 12, 242,\n                                               16, 24, 28, 120, 182, 244, 213, 244, 153, 194, 162, 69, 160, 244,\n                                               248, 63, 165, 141, 4, 207, 249, 193, 79, 131, 0, 169, 233, 127, 167,\n                                               101, 151, 125, 56, 112, 111, 248, 29, 232, 90, 29, 147, 110, 169,\n                                               146, 114, 165, 204, 71, 136, 41, 252] as byte[]\n\n    final static String encodedEncryptedCek = 'UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm' +\n            '1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc' +\n            'HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF' +\n            'NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8' +\n            'rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv' +\n            '-B3oWh2TbqmScqXMR4gp_A' as String\n\n    // https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.4\n    final static byte[] IV = [3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101] as byte[]\n    final static String encodedIv = 'AxY8DCtDaGlsbGljb3RoZQ' as String\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.5\n    final static byte[] AAD_BYTES = [101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 83, 85, 48, 69,\n                                     120, 88, 122, 85, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105,\n                                     74, 66, 77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85,\n                                     50, 73, 110, 48] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.6\n    final static byte[] CIPHERTEXT = [40, 57, 83, 181, 119, 33, 133, 148, 198, 185, 243, 24, 152, 230, 6,\n                                      75, 129, 223, 127, 19, 210, 82, 183, 230, 168, 33, 215, 104, 143,\n                                      112, 56, 102] as byte[]\n    final static String encodedCiphertext = 'KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY' as String\n\n    final static byte[] TAG = [246, 17, 244, 190, 4, 95, 98, 3, 231, 0, 115, 157, 242, 203, 100, 191] as byte[]\n    final static String encodedTag = '9hH0vgRfYgPnAHOd8stkvw'\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.7\n    final static String COMPLETE_JWE =\n            \"eyJhbGciOiJSU0ExXzUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.\" +\n                    \"UGhIOguC7IuEvf_NPVaXsGMoLOmwvc1GyqlIKOK1nN94nHPoltGRhWhw7Zx0-kFm\" +\n                    \"1NJn8LE9XShH59_i8J0PH5ZZyNfGy2xGdULU7sHNF6Gp2vPLgNZ__deLKxGHZ7Pc\" +\n                    \"HALUzoOegEI-8E66jX2E4zyJKx-YxzZIItRzC5hlRirb6Y5Cl_p-ko3YvkkysZIF\" +\n                    \"NPccxRU7qve1WYPxqbb2Yw8kZqa2rMWI5ng8OtvzlV7elprCbuPhcCdZ6XDP0_F8\" +\n                    \"rkXds2vE4X-ncOIM8hAYHHi29NX0mcKiRaD0-D-ljQTP-cFPgwCp6X-nZZd9OHBv\" +\n                    \"-B3oWh2TbqmScqXMR4gp_A.\" +\n                    \"AxY8DCtDaGlsbGljb3RoZQ.\" +\n                    \"KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.\" +\n                    \"9hH0vgRfYgPnAHOd8stkvw\" as String\n\n    @Test\n    void test() {\n        //ensure our test constants are correctly copied and match the RFC values:\n        assertEquals PLAINTEXT, new String(PLAINTEXT_BYTES, StandardCharsets.UTF_8)\n        assertEquals PROT_HEADER_STRING, new String(decode(encodedHeader), StandardCharsets.UTF_8)\n        assertEquals encodedEncryptedCek, encode(ENCRYPTED_CEK_BYTES)\n        assertEquals encodedIv, encode(IV)\n        assertArrayEquals AAD_BYTES, encodedHeader.getBytes(StandardCharsets.US_ASCII)\n        assertArrayEquals CIPHERTEXT, decode(encodedCiphertext)\n        assertArrayEquals TAG, decode(encodedTag)\n\n        //read the RFC Test JWK to get the private key for decrypting\n        RsaPrivateJwk jwk = Jwks.builder().add(KEK_VALUES).build() as RsaPrivateJwk\n        RSAPrivateKey privKey = jwk.toKey()\n\n        Jwe<byte[]> jwe = Jwts.parser().decryptWith(privKey).build().parseEncryptedContent(COMPLETE_JWE)\n        assertEquals PLAINTEXT, new String(jwe.getPayload(), StandardCharsets.UTF_8)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7516AppendixA3Test.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwe\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.assertArrayEquals\nimport static org.junit.Assert.assertEquals\n\nclass RFC7516AppendixA3Test {\n\n    static String encode(byte[] b) {\n        return Encoders.BASE64URL.encode(b)\n    }\n\n    static byte[] decode(String val) {\n        return Decoders.BASE64URL.decode(val)\n    }\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3 :\n    final static String PLAINTEXT = 'Live long and prosper.' as String\n    final static byte[] PLAINTEXT_BYTES = [76, 105, 118, 101, 32, 108, 111, 110, 103, 32, 97, 110, 100, 32,\n                                           112, 114, 111, 115, 112, 101, 114, 46] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3.1 :\n    final static String PROT_HEADER_STRING = '{\"alg\":\"A128KW\",\"enc\":\"A128CBC-HS256\"}' as String\n    final static String encodedHeader = 'eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0' as String\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.2.2\n    final static byte[] CEK_BYTES = [4, 211, 31, 197, 84, 157, 252, 254, 11, 100, 157, 250, 63, 170, 106,\n                                     206, 107, 124, 212, 45, 111, 107, 9, 219, 200, 177, 0, 240, 143, 156,\n                                     44, 207] as byte[]\n    final static SecretKey CEK = new SecretKeySpec(CEK_BYTES, \"AES\")\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3.3\n    final static Map<String, String> KEK_VALUES = [\"kty\": \"oct\", \"k\": \"GawgguFyGrWKav7AX4VKUg\"]\n\n    final static byte[] ENCRYPTED_CEK_BYTES = [232, 160, 123, 211, 183, 76, 245, 132, 200, 128, 123, 75, 190, 216,\n                                               22, 67, 201, 138, 193, 186, 9, 91, 122, 31, 246, 90, 28, 139, 57, 3,\n                                               76, 124, 193, 11, 98, 37, 173, 61, 104, 57] as byte[]\n\n    final static String encodedEncryptedCek = '6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ' as String\n\n    // https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3.4\n    final static byte[] IV = [3, 22, 60, 12, 43, 67, 104, 105, 108, 108, 105, 99, 111, 116, 104, 101] as byte[]\n    final static String encodedIv = 'AxY8DCtDaGlsbGljb3RoZQ' as String\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3.5\n    final static byte[] AAD_BYTES = [101, 121, 74, 104, 98, 71, 99, 105, 79, 105, 74, 66, 77, 84, 73, 52,\n                                     83, 49, 99, 105, 76, 67, 74, 108, 98, 109, 77, 105, 79, 105, 74, 66,\n                                     77, 84, 73, 52, 81, 48, 74, 68, 76, 85, 104, 84, 77, 106, 85, 50, 73,\n                                     110, 48] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3.6\n    final static byte[] CIPHERTEXT = [40, 57, 83, 181, 119, 33, 133, 148, 198, 185, 243, 24, 152, 230, 6,\n                                      75, 129, 223, 127, 19, 210, 82, 183, 230, 168, 33, 215, 104, 143,\n                                      112, 56, 102] as byte[]\n    final static String encodedCiphertext = 'KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY' as String\n\n    final static byte[] TAG = [83, 73, 191, 98, 104, 205, 211, 128, 201, 189, 199, 133, 32, 38, 194, 85] as byte[]\n    final static String encodedTag = 'U0m_YmjN04DJvceFICbCVQ'\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3.7\n    final static String COMPLETE_JWE =\n            'eyJhbGciOiJBMTI4S1ciLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0.' +\n                    '6KB707dM9YTIgHtLvtgWQ8mKwboJW3of9locizkDTHzBC2IlrT1oOQ.' +\n                    'AxY8DCtDaGlsbGljb3RoZQ.' +\n                    'KDlTtXchhZTGufMYmOYGS4HffxPSUrfmqCHXaI9wOGY.' +\n                    'U0m_YmjN04DJvceFICbCVQ' as String\n\n    @Test\n    void test() {\n        //ensure our test constants are correctly copied and match the RFC values:\n        assertEquals PLAINTEXT, new String(PLAINTEXT_BYTES, StandardCharsets.UTF_8)\n        assertEquals PROT_HEADER_STRING, new String(decode(encodedHeader), StandardCharsets.UTF_8)\n        assertEquals encodedEncryptedCek, encode(ENCRYPTED_CEK_BYTES)\n        assertEquals encodedIv, encode(IV)\n        assertArrayEquals AAD_BYTES, encodedHeader.getBytes(StandardCharsets.US_ASCII)\n        assertArrayEquals CIPHERTEXT, decode(encodedCiphertext)\n        assertArrayEquals TAG, decode(encodedTag)\n\n        //read the RFC Test JWK to get the private key for decrypting\n        SecretJwk jwk = Jwks.builder().add(KEK_VALUES).build() as SecretJwk\n        SecretKey kek = jwk.toKey()\n\n        // test decryption per the RFC\n        Jwe<byte[]> jwe = Jwts.parser().decryptWith(kek).build().parseEncryptedContent(COMPLETE_JWE)\n        assertEquals PLAINTEXT, new String(jwe.getPayload(), StandardCharsets.UTF_8)\n\n        // now ensure that when JJWT does the encryption (i.e. a compact value is produced from JJWT, not from the RFC text),\n        // that the resulting compact string is identical to the RFC as described in\n        // https://www.rfc-editor.org/rfc/rfc7516.html#appendix-A.3.8 :\n\n        //ensure that the algorithm reflects the test harness values:\n        AeadAlgorithm enc = new HmacAesAeadAlgorithm(128) {\n            @Override\n            protected byte[] ensureInitializationVector(Request request) {\n                return IV\n            }\n\n            @Override\n            SecretKeyBuilder key() {\n                return Keys.builder(CEK)\n            }\n        }\n\n        String compact = Jwts.builder()\n                .setPayload(PLAINTEXT)\n                .encryptWith(kek, Jwts.KEY.A128KW, enc)\n                .compact()\n\n        assertEquals COMPLETE_JWE, compact\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7517AppendixA1Test.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.EcPublicJwk\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPublicJwk\nimport org.junit.Test\n\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertTrue\n\n/**\n * https://www.rfc-editor.org/rfc/rfc7517.html#appendix-A.1\n */\nclass RFC7517AppendixA1Test {\n\n    private static final List<Map<String, String>> keys = [\n            [\n                    \"kty\": \"EC\",\n                    \"crv\": \"P-256\",\n                    \"x\"  : \"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\",\n                    \"y\"  : \"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\",\n                    \"use\": \"enc\",\n                    \"kid\": \"1\"\n            ],\n            [\n                    \"kty\": \"RSA\",\n                    \"n\"  : \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx\" +\n                            \"4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMs\" +\n                            \"tn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2\" +\n                            \"QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbI\" +\n                            \"SD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqb\" +\n                            \"w0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\" as String,\n                    \"e\"  : \"AQAB\",\n                    \"alg\": \"RS256\",\n                    \"kid\": \"2011-04-29\"\n            ]\n    ]\n\n    @Test\n    void test() { // asserts we can parse and verify RFC values\n\n        def m = keys[0]\n        EcPublicJwk ecPubJwk = Jwks.builder().add(m).build() as EcPublicJwk\n        assertTrue ecPubJwk.toKey() instanceof ECPublicKey\n        assertEquals m.size(), ecPubJwk.size()\n        assertEquals m.kty, ecPubJwk.getType()\n        assertEquals m.crv, ecPubJwk.get('crv')\n        assertEquals m.x, ecPubJwk.get('x')\n        assertEquals m.y, ecPubJwk.get('y')\n        assertEquals m.use, ecPubJwk.getPublicKeyUse()\n        assertEquals m.kid, ecPubJwk.getId()\n\n        m = keys[1]\n        RsaPublicJwk rsaPublicJwk = Jwks.builder().add(m).build() as RsaPublicJwk\n        assertTrue rsaPublicJwk.toKey() instanceof RSAPublicKey\n        assertEquals m.size(), rsaPublicJwk.size()\n        assertEquals m.kty, rsaPublicJwk.getType()\n        assertEquals m.n, rsaPublicJwk.get('n')\n        assertEquals m.e, rsaPublicJwk.get('e')\n        assertEquals m.alg, rsaPublicJwk.getAlgorithm()\n        assertEquals m.kid, rsaPublicJwk.getId()\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7517AppendixA2Test.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Converters\nimport io.jsonwebtoken.security.EcPrivateJwk\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPrivateJwk\nimport org.junit.Test\n\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.RSAPrivateCrtKey\nimport java.security.spec.EllipticCurve\n\nimport static org.junit.Assert.*\n\n/**\n * https://www.rfc-editor.org/rfc/rfc7517.html#appendix-A.2\n */\nclass RFC7517AppendixA2Test {\n\n    private static final String ecEncode(EllipticCurve curve, BigInteger coord) {\n        return AbstractEcJwkFactory.toOctetString(curve, coord)\n    }\n\n    private static final String rsaEncode(BigInteger i) {\n        return Converters.BIGINT.applyTo(i) as String\n    }\n\n    private static final List<Map<String, String>> keys = [\n            [\n                    \"kty\": \"EC\",\n                    \"crv\": \"P-256\",\n                    \"x\"  : \"MKBCTNIcKUSDii11ySs3526iDZ8AiTo7Tu6KPAqv7D4\",\n                    \"y\"  : \"4Etl6SRW2YiLUrN5vfvVHuhp7x8PxltmWWlbbM4IFyM\",\n                    \"d\"  : \"870MB6gfuTJ4HtUnUvYMyJpr5eUZNP4Bk43bVdj3eAE\",\n                    \"use\": \"enc\",\n                    \"kid\": \"1\"\n            ],\n            [\n                    \"kty\": \"RSA\",\n                    \"n\"  : \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4\" +\n                            \"cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMst\" +\n                            \"n64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2Q\" +\n                            \"vzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbIS\" +\n                            \"D08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw\" +\n                            \"0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\",\n                    \"e\"  : \"AQAB\",\n                    \"d\"  : \"X4cTteJY_gn4FYPsXB8rdXix5vwsg1FLN5E3EaG6RJoVH-HLLKD9\" +\n                            \"M7dx5oo7GURknchnrRweUkC7hT5fJLM0WbFAKNLWY2vv7B6NqXSzUvxT0_YSfqij\" +\n                            \"wp3RTzlBaCxWp4doFk5N2o8Gy_nHNKroADIkJ46pRUohsXywbReAdYaMwFs9tv8d\" +\n                            \"_cPVY3i07a3t8MN6TNwm0dSawm9v47UiCl3Sk5ZiG7xojPLu4sbg1U2jx4IBTNBz\" +\n                            \"nbJSzFHK66jT8bgkuqsk0GjskDJk19Z4qwjwbsnn4j2WBii3RL-Us2lGVkY8fkFz\" +\n                            \"me1z0HbIkfz0Y6mqnOYtqc0X4jfcKoAC8Q\",\n                    \"p\"  : \"83i-7IvMGXoMXCskv73TKr8637FiO7Z27zv8oj6pbWUQyLPQBQxtPV\" +\n                            \"nwD20R-60eTDmD2ujnMt5PoqMrm8RfmNhVWDtjjMmCMjOpSXicFHj7XOuVIYQyqV\" +\n                            \"WlWEh6dN36GVZYk93N8Bc9vY41xy8B9RzzOGVQzXvNEvn7O0nVbfs\",\n                    \"q\"  : \"3dfOR9cuYq-0S-mkFLzgItgMEfFzB2q3hWehMuG0oCuqnb3vobLyum\" +\n                            \"qjVZQO1dIrdwgTnCdpYzBcOfW5r370AFXjiWft_NGEiovonizhKpo9VVS78TzFgx\" +\n                            \"kIdrecRezsZ-1kYd_s1qDbxtkDEgfAITAG9LUnADun4vIcb6yelxk\",\n                    \"dp\" : \"G4sPXkc6Ya9y8oJW9_ILj4xuppu0lzi_H7VTkS8xj5SdX3coE0oim\" +\n                            \"YwxIi2emTAue0UOa5dpgFGyBJ4c8tQ2VF402XRugKDTP8akYhFo5tAA77Qe_Nmtu\" +\n                            \"YZc3C3m3I24G2GvR5sSDxUyAN2zq8Lfn9EUms6rY3Ob8YeiKkTiBj0\",\n                    \"dq\" : \"s9lAH9fggBsoFR8Oac2R_E2gw282rT2kGOAhvIllETE1efrA6huUU\" +\n                            \"vMfBcMpn8lqeW6vzznYY5SSQF7pMdC_agI3nG8Ibp1BUb0JUiraRNqUfLhcQb_d9\" +\n                            \"GF4Dh7e74WbRsobRonujTYN1xCaP6TO61jvWrX-L18txXw494Q_cgk\",\n                    \"qi\" : \"GyM_p6JrXySiz1toFgKbWV-JdI3jQ4ypu9rbMWx3rQJBfmt0FoYzg\" +\n                            \"UIZEVFEcOqwemRN81zoDAaa-Bk0KWNGDjJHZDdDmFhW3AN7lI-puxk_mHZGJ11rx\" +\n                            \"yR8O55XLSe3SPmRfKwZI6yU24ZxvQKFYItdldUKGzO6Ia6zTKhAVRU\",\n                    \"alg\": \"RS256\",\n                    \"kid\": \"2011-04-29\"\n            ]\n    ]\n\n    @Test\n    void test() { // asserts we can parse and verify RFC values\n\n        def m = keys[0]\n        def jwk = Jwks.builder().add(m).build() as EcPrivateJwk\n        def key = jwk.toKey()\n        def curve = key.params.curve\n        assertTrue key instanceof ECPrivateKey\n        assertEquals m.size(), jwk.size()\n        assertEquals m.kty, jwk.getType()\n        assertEquals m.crv, jwk.get('crv')\n        assertEquals m.x, jwk.get('x')\n        assertEquals m.x, ecEncode(curve, jwk.toPublicJwk().toKey().w.affineX)\n        assertEquals m.y, jwk.get('y')\n        assertEquals m.y, ecEncode(curve, jwk.toPublicJwk().toKey().w.affineY)\n        assertEquals m.d, jwk.get('d').get()\n        assertEquals m.d, ecEncode(curve, key.s)\n        assertEquals m.use, jwk.getPublicKeyUse()\n        assertEquals m.kid, jwk.getId()\n\n        m = keys[1]\n        jwk = Jwks.builder().add(m).build() as RsaPrivateJwk\n        key = jwk.toKey() as RSAPrivateCrtKey\n        assertNotNull key\n        assertEquals m.size(), jwk.size()\n        assertEquals m.kty, jwk.getType()\n        assertEquals m.n, jwk.get('n')\n        assertEquals m.n, rsaEncode(key.modulus)\n        assertEquals m.e, jwk.get('e')\n        assertEquals m.e, rsaEncode(jwk.toPublicJwk().toKey().publicExponent)\n        assertEquals m.d, jwk.get('d').get()\n        assertEquals m.d, rsaEncode(key.privateExponent)\n        assertEquals m.p, jwk.get('p').get()\n        assertEquals m.p, rsaEncode(key.getPrimeP())\n        assertEquals m.q, jwk.get('q').get()\n        assertEquals m.q, rsaEncode(key.getPrimeQ())\n        assertEquals m.dp, jwk.get('dp').get()\n        assertEquals m.dp, rsaEncode(key.getPrimeExponentP())\n        assertEquals m.dq, jwk.get('dq').get()\n        assertEquals m.dq, rsaEncode(key.getPrimeExponentQ())\n        assertEquals m.qi, jwk.get('qi').get()\n        assertEquals m.qi, rsaEncode(key.getCrtCoefficient())\n        assertEquals m.alg, jwk.getAlgorithm()\n        assertEquals m.kid, jwk.getId()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7517AppendixA3Test.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.SecretJwk\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertNotNull\n\n/**\n * https://www.rfc-editor.org/rfc/rfc7517.html#appendix-A.3\n */\nclass RFC7517AppendixA3Test {\n\n    private static final String encode(SecretKey key) {\n        return Encoders.BASE64URL.encode(key.getEncoded())\n    }\n\n    private static final List<Map<String, String>> keys = [\n            [\n                    \"kty\": \"oct\",\n                    \"alg\": \"A128KW\",\n                    \"k\"  : \"GawgguFyGrWKav7AX4VKUg\"\n            ],\n            [\n                    \"kty\": \"oct\",\n                    \"k\"  : \"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\",\n                    \"kid\": \"HMAC key used in JWS spec Appendix A.1 example\"\n            ]\n    ]\n\n    @Test\n    void test() { // asserts we can parse and verify RFC values\n\n        def m = keys[0]\n        SecretJwk jwk = Jwks.builder().add(m).build() as SecretJwk\n        def key = jwk.toKey() as SecretKey\n        assertNotNull key\n        assertEquals m.size(), jwk.size()\n        assertEquals m.kty, jwk.getType()\n        assertEquals m.alg, jwk.getAlgorithm()\n        assertEquals m.k, jwk.get('k').get()\n        assertEquals m.k, encode(key)\n\n        m = keys[1]\n        jwk = Jwks.builder().add(m).build() as SecretJwk\n        key = jwk.toKey() as SecretKey\n        assertNotNull key\n        assertEquals m.size(), jwk.size()\n        assertEquals m.kty, jwk.getType()\n        assertEquals m.k, jwk.get('k').get()\n        assertEquals m.k, encode(key)\n        assertEquals m.kid, jwk.getId()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7517AppendixBTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Converters\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPublicJwk\nimport org.junit.Test\n\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\n/**\n * https://www.rfc-editor.org/rfc/rfc7517.html#appendix-B\n */\nclass RFC7517AppendixBTest {\n\n    private static final Map<String, String> jwkPairs = [\n            \"kty\": \"RSA\",\n            \"use\": \"sig\",\n            \"kid\": \"1b94c\",\n            \"n\"  : \"vrjOfz9Ccdgx5nQudyhdoR17V-IubWMeOZCwX_jj0hgAsz2J_pqYW08\" +\n                    \"PLbK_PdiVGKPrqzmDIsLI7sA25VEnHU1uCLNwBuUiCO11_-7dYbsr4iJmG0Q\" +\n                    \"u2j8DsVyT1azpJC_NG84Ty5KKthuCaPod7iI7w0LK9orSMhBEwwZDCxTWq4a\" +\n                    \"YWAchc8t-emd9qOvWtVMDC2BXksRngh6X5bUYLy6AyHKvj-nUy1wgzjYQDwH\" +\n                    \"MTplCoLtU-o-8SNnZ1tmRoGE9uJkBLdh5gFENabWnU5m1ZqZPdwS-qo-meMv\" +\n                    \"VfJb6jJVWRpl2SUtCnYG2C32qvbWbjZ_jBPD5eunqsIo1vQ\",\n            \"e\"  : \"AQAB\",\n            \"x5c\": [\n                    \"MIIDQjCCAiqgAwIBAgIGATz/FuLiMA0GCSqGSIb3DQEBBQUAMGIxCzAJB\" +\n                            \"gNVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYD\" +\n                            \"VQQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1\" +\n                            \"wYmVsbDAeFw0xMzAyMjEyMzI5MTVaFw0xODA4MTQyMjI5MTVaMGIxCzAJBg\" +\n                            \"NVBAYTAlVTMQswCQYDVQQIEwJDTzEPMA0GA1UEBxMGRGVudmVyMRwwGgYDV\" +\n                            \"QQKExNQaW5nIElkZW50aXR5IENvcnAuMRcwFQYDVQQDEw5CcmlhbiBDYW1w\" +\n                            \"YmVsbDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL64zn8/QnH\" +\n                            \"YMeZ0LncoXaEde1fiLm1jHjmQsF/449IYALM9if6amFtPDy2yvz3YlRij66\" +\n                            \"s5gyLCyO7ANuVRJx1NbgizcAblIgjtdf/u3WG7K+IiZhtELto/A7Fck9Ws6\" +\n                            \"SQvzRvOE8uSirYbgmj6He4iO8NCyvaK0jIQRMMGQwsU1quGmFgHIXPLfnpn\" +\n                            \"fajr1rVTAwtgV5LEZ4Iel+W1GC8ugMhyr4/p1MtcIM42EA8BzE6ZQqC7VPq\" +\n                            \"PvEjZ2dbZkaBhPbiZAS3YeYBRDWm1p1OZtWamT3cEvqqPpnjL1XyW+oyVVk\" +\n                            \"aZdklLQp2Btgt9qr21m42f4wTw+Xrp6rCKNb0CAwEAATANBgkqhkiG9w0BA\" +\n                            \"QUFAAOCAQEAh8zGlfSlcI0o3rYDPBB07aXNswb4ECNIKG0CETTUxmXl9KUL\" +\n                            \"+9gGlqCz5iWLOgWsnrcKcY0vXPG9J1r9AqBNTqNgHq2G03X09266X5CpOe1\" +\n                            \"zFo+Owb1zxtp3PehFdfQJ610CDLEaS9V9Rqp17hCyybEpOGVwe8fnk+fbEL\" +\n                            \"2Bo3UPGrpsHzUoaGpDftmWssZkhpBJKVMJyf/RuP2SmmaIzmnw9JiSlYhzo\" +\n                            \"4tpzd5rFXhjRbg4zW9C+2qok+2+qDM1iJ684gPHMIY8aLWrdgQTxkumGmTq\" +\n                            \"gawR+N5MDtdPTEQ0XfIBc2cJEUyMTY5MPvACWpkA6SdS4xSvdXK3IVfOWA==\"\n            ]\n    ]\n\n    @Test\n    void test() {\n        def m = jwkPairs\n        RsaPublicJwk jwk = Jwks.builder().add(m).build() as RsaPublicJwk\n        RSAPublicKey key = jwk.toKey()\n        assertNotNull key\n        assertEquals m.size(), jwk.size()\n        assertEquals m.kty, jwk.getType()\n        assertEquals m.use, jwk.getPublicKeyUse()\n        assertEquals m.kid, jwk.getId()\n        assertEquals m.n, Converters.BIGINT.applyTo(key.getModulus())\n        assertEquals m.e, Converters.BIGINT.applyTo(key.getPublicExponent())\n        def chain = jwk.getX509Chain()\n        assertNotNull chain\n        assertFalse chain.isEmpty()\n        assertEquals 1, chain.size()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7517AppendixCTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwe\nimport io.jsonwebtoken.JweHeader\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.TestSerializer\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.*\n\n/**\n * https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C\n */\n@SuppressWarnings('SpellCheckingInspection')\nclass RFC7517AppendixCTest {\n\n    private static final String rfcString(String s) {\n        return s.replaceAll('[\\\\s]', '')\n    }\n\n    // https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C.1\n    private static final String RFC_JWK_JSON = rfcString('''\n    {\n      \"kty\":\"RSA\",\n      \"kid\":\"juliet@capulet.lit\",\n      \"use\":\"enc\",\n      \"n\":\"t6Q8PWSi1dkJj9hTP8hNYFlvadM7DflW9mWepOJhJ66w7nyoK1gPNqFMSQRy\n           O125Gp-TEkodhWr0iujjHVx7BcV0llS4w5ACGgPrcAd6ZcSR0-Iqom-QFcNP\n           8Sjg086MwoqQU_LYywlAGZ21WSdS_PERyGFiNnj3QQlO8Yns5jCtLCRwLHL0\n           Pb1fEv45AuRIuUfVcPySBWYnDyGxvjYGDSM-AqWS9zIQ2ZilgT-GqUmipg0X\n           OC0Cc20rgLe2ymLHjpHciCKVAbY5-L32-lSeZO-Os6U15_aXrk9Gw8cPUaX1\n           _I8sLGuSiVdt3C_Fn2PZ3Z8i744FPFGGcG1qs2Wz-Q\",\n      \"e\":\"AQAB\",\n      \"d\":\"GRtbIQmhOZtyszfgKdg4u_N-R_mZGU_9k7JQ_jn1DnfTuMdSNprTeaSTyWfS\n           NkuaAwnOEbIQVy1IQbWVV25NY3ybc_IhUJtfri7bAXYEReWaCl3hdlPKXy9U\n           vqPYGR0kIXTQRqns-dVJ7jahlI7LyckrpTmrM8dWBo4_PMaenNnPiQgO0xnu\n           ToxutRZJfJvG4Ox4ka3GORQd9CsCZ2vsUDmsXOfUENOyMqADC6p1M3h33tsu\n           rY15k9qMSpG9OX_IJAXmxzAh_tWiZOwk2K4yxH9tS3Lq1yX8C1EWmeRDkK2a\n           hecG85-oLKQt5VEpWHKmjOi_gJSdSgqcN96X52esAQ\",\n      \"p\":\"2rnSOV4hKSN8sS4CgcQHFbs08XboFDqKum3sc4h3GRxrTmQdl1ZK9uw-PIHf\n           QP0FkxXVrx-WE-ZEbrqivH_2iCLUS7wAl6XvARt1KkIaUxPPSYB9yk31s0Q8\n           UK96E3_OrADAYtAJs-M3JxCLfNgqh56HDnETTQhH3rCT5T3yJws\",\n      \"q\":\"1u_RiFDP7LBYh3N4GXLT9OpSKYP0uQZyiaZwBtOCBNJgQxaj10RWjsZu0c6I\n           edis4S7B_coSKB0Kj9PaPaBzg-IySRvvcQuPamQu66riMhjVtG6TlV8CLCYK\n           rYl52ziqK0E_ym2QnkwsUX7eYTB7LbAHRK9GqocDE5B0f808I4s\",\n      \"dp\":\"KkMTWqBUefVwZ2_Dbj1pPQqyHSHjj90L5x_MOzqYAJMcLMZtbUtwKqvVDq3\n           tbEo3ZIcohbDtt6SbfmWzggabpQxNxuBpoOOf_a_HgMXK_lhqigI4y_kqS1w\n           Y52IwjUn5rgRrJ-yYo1h41KR-vz2pYhEAeYrhttWtxVqLCRViD6c\",\n      \"dq\":\"AvfS0-gRxvn0bwJoMSnFxYcK1WnuEjQFluMGfwGitQBWtfZ1Er7t1xDkbN9\n           GQTB9yqpDoYaN06H7CFtrkxhJIBQaj6nkF5KKS3TQtQ5qCzkOkmxIe3KRbBy\n           mXxkb5qwUpX5ELD5xFc6FeiafWYY63TmmEAu_lRFCOJ3xDea-ots\",\n      \"qi\":\"lSQi-w9CpyUReMErP1RsBLk7wNtOvs5EQpPqmuMvqW57NBUczScEoPwmUqq\n           abu9V0-Py4dQ57_bapoKRu1R90bvuFnU63SHWEFglZQvJDMeAvmj4sm-Fp0o\n           Yu_neotgQ0hzbI5gry7ajdYy9-2lNx_76aBZoOUu9HCJ-UsfSOI8\"\n     }\n     ''')\n\n    private static final byte[] RFC_JWK_JSON_BYTES =\n            [123, 34, 107, 116, 121, 34, 58, 34, 82, 83, 65, 34, 44, 34, 107,\n             105, 100, 34, 58, 34, 106, 117, 108, 105, 101, 116, 64, 99, 97, 112,\n             117, 108, 101, 116, 46, 108, 105, 116, 34, 44, 34, 117, 115, 101, 34,\n             58, 34, 101, 110, 99, 34, 44, 34, 110, 34, 58, 34, 116, 54, 81, 56,\n             80, 87, 83, 105, 49, 100, 107, 74, 106, 57, 104, 84, 80, 56, 104, 78,\n             89, 70, 108, 118, 97, 100, 77, 55, 68, 102, 108, 87, 57, 109, 87,\n             101, 112, 79, 74, 104, 74, 54, 54, 119, 55, 110, 121, 111, 75, 49,\n             103, 80, 78, 113, 70, 77, 83, 81, 82, 121, 79, 49, 50, 53, 71, 112,\n             45, 84, 69, 107, 111, 100, 104, 87, 114, 48, 105, 117, 106, 106, 72,\n             86, 120, 55, 66, 99, 86, 48, 108, 108, 83, 52, 119, 53, 65, 67, 71,\n             103, 80, 114, 99, 65, 100, 54, 90, 99, 83, 82, 48, 45, 73, 113, 111,\n             109, 45, 81, 70, 99, 78, 80, 56, 83, 106, 103, 48, 56, 54, 77, 119,\n             111, 113, 81, 85, 95, 76, 89, 121, 119, 108, 65, 71, 90, 50, 49, 87,\n             83, 100, 83, 95, 80, 69, 82, 121, 71, 70, 105, 78, 110, 106, 51, 81,\n             81, 108, 79, 56, 89, 110, 115, 53, 106, 67, 116, 76, 67, 82, 119, 76,\n             72, 76, 48, 80, 98, 49, 102, 69, 118, 52, 53, 65, 117, 82, 73, 117,\n             85, 102, 86, 99, 80, 121, 83, 66, 87, 89, 110, 68, 121, 71, 120, 118,\n             106, 89, 71, 68, 83, 77, 45, 65, 113, 87, 83, 57, 122, 73, 81, 50,\n             90, 105, 108, 103, 84, 45, 71, 113, 85, 109, 105, 112, 103, 48, 88,\n             79, 67, 48, 67, 99, 50, 48, 114, 103, 76, 101, 50, 121, 109, 76, 72,\n             106, 112, 72, 99, 105, 67, 75, 86, 65, 98, 89, 53, 45, 76, 51, 50,\n             45, 108, 83, 101, 90, 79, 45, 79, 115, 54, 85, 49, 53, 95, 97, 88,\n             114, 107, 57, 71, 119, 56, 99, 80, 85, 97, 88, 49, 95, 73, 56, 115,\n             76, 71, 117, 83, 105, 86, 100, 116, 51, 67, 95, 70, 110, 50, 80, 90,\n             51, 90, 56, 105, 55, 52, 52, 70, 80, 70, 71, 71, 99, 71, 49, 113,\n             115, 50, 87, 122, 45, 81, 34, 44, 34, 101, 34, 58, 34, 65, 81, 65,\n             66, 34, 44, 34, 100, 34, 58, 34, 71, 82, 116, 98, 73, 81, 109, 104,\n             79, 90, 116, 121, 115, 122, 102, 103, 75, 100, 103, 52, 117, 95, 78,\n             45, 82, 95, 109, 90, 71, 85, 95, 57, 107, 55, 74, 81, 95, 106, 110,\n             49, 68, 110, 102, 84, 117, 77, 100, 83, 78, 112, 114, 84, 101, 97,\n             83, 84, 121, 87, 102, 83, 78, 107, 117, 97, 65, 119, 110, 79, 69, 98,\n             73, 81, 86, 121, 49, 73, 81, 98, 87, 86, 86, 50, 53, 78, 89, 51, 121,\n             98, 99, 95, 73, 104, 85, 74, 116, 102, 114, 105, 55, 98, 65, 88, 89,\n             69, 82, 101, 87, 97, 67, 108, 51, 104, 100, 108, 80, 75, 88, 121, 57,\n             85, 118, 113, 80, 89, 71, 82, 48, 107, 73, 88, 84, 81, 82, 113, 110,\n             115, 45, 100, 86, 74, 55, 106, 97, 104, 108, 73, 55, 76, 121, 99,\n             107, 114, 112, 84, 109, 114, 77, 56, 100, 87, 66, 111, 52, 95, 80,\n             77, 97, 101, 110, 78, 110, 80, 105, 81, 103, 79, 48, 120, 110, 117,\n             84, 111, 120, 117, 116, 82, 90, 74, 102, 74, 118, 71, 52, 79, 120,\n             52, 107, 97, 51, 71, 79, 82, 81, 100, 57, 67, 115, 67, 90, 50, 118,\n             115, 85, 68, 109, 115, 88, 79, 102, 85, 69, 78, 79, 121, 77, 113, 65,\n             68, 67, 54, 112, 49, 77, 51, 104, 51, 51, 116, 115, 117, 114, 89, 49,\n             53, 107, 57, 113, 77, 83, 112, 71, 57, 79, 88, 95, 73, 74, 65, 88,\n             109, 120, 122, 65, 104, 95, 116, 87, 105, 90, 79, 119, 107, 50, 75,\n             52, 121, 120, 72, 57, 116, 83, 51, 76, 113, 49, 121, 88, 56, 67, 49,\n             69, 87, 109, 101, 82, 68, 107, 75, 50, 97, 104, 101, 99, 71, 56, 53,\n             45, 111, 76, 75, 81, 116, 53, 86, 69, 112, 87, 72, 75, 109, 106, 79,\n             105, 95, 103, 74, 83, 100, 83, 103, 113, 99, 78, 57, 54, 88, 53, 50,\n             101, 115, 65, 81, 34, 44, 34, 112, 34, 58, 34, 50, 114, 110, 83, 79,\n             86, 52, 104, 75, 83, 78, 56, 115, 83, 52, 67, 103, 99, 81, 72, 70,\n             98, 115, 48, 56, 88, 98, 111, 70, 68, 113, 75, 117, 109, 51, 115, 99,\n             52, 104, 51, 71, 82, 120, 114, 84, 109, 81, 100, 108, 49, 90, 75, 57,\n             117, 119, 45, 80, 73, 72, 102, 81, 80, 48, 70, 107, 120, 88, 86, 114,\n             120, 45, 87, 69, 45, 90, 69, 98, 114, 113, 105, 118, 72, 95, 50, 105,\n             67, 76, 85, 83, 55, 119, 65, 108, 54, 88, 118, 65, 82, 116, 49, 75,\n             107, 73, 97, 85, 120, 80, 80, 83, 89, 66, 57, 121, 107, 51, 49, 115,\n             48, 81, 56, 85, 75, 57, 54, 69, 51, 95, 79, 114, 65, 68, 65, 89, 116,\n             65, 74, 115, 45, 77, 51, 74, 120, 67, 76, 102, 78, 103, 113, 104, 53,\n             54, 72, 68, 110, 69, 84, 84, 81, 104, 72, 51, 114, 67, 84, 53, 84,\n             51, 121, 74, 119, 115, 34, 44, 34, 113, 34, 58, 34, 49, 117, 95, 82,\n             105, 70, 68, 80, 55, 76, 66, 89, 104, 51, 78, 52, 71, 88, 76, 84, 57,\n             79, 112, 83, 75, 89, 80, 48, 117, 81, 90, 121, 105, 97, 90, 119, 66,\n             116, 79, 67, 66, 78, 74, 103, 81, 120, 97, 106, 49, 48, 82, 87, 106,\n             115, 90, 117, 48, 99, 54, 73, 101, 100, 105, 115, 52, 83, 55, 66, 95,\n             99, 111, 83, 75, 66, 48, 75, 106, 57, 80, 97, 80, 97, 66, 122, 103,\n             45, 73, 121, 83, 82, 118, 118, 99, 81, 117, 80, 97, 109, 81, 117, 54,\n             54, 114, 105, 77, 104, 106, 86, 116, 71, 54, 84, 108, 86, 56, 67, 76,\n             67, 89, 75, 114, 89, 108, 53, 50, 122, 105, 113, 75, 48, 69, 95, 121,\n             109, 50, 81, 110, 107, 119, 115, 85, 88, 55, 101, 89, 84, 66, 55, 76,\n             98, 65, 72, 82, 75, 57, 71, 113, 111, 99, 68, 69, 53, 66, 48, 102,\n             56, 48, 56, 73, 52, 115, 34, 44, 34, 100, 112, 34, 58, 34, 75, 107,\n             77, 84, 87, 113, 66, 85, 101, 102, 86, 119, 90, 50, 95, 68, 98, 106,\n             49, 112, 80, 81, 113, 121, 72, 83, 72, 106, 106, 57, 48, 76, 53, 120,\n             95, 77, 79, 122, 113, 89, 65, 74, 77, 99, 76, 77, 90, 116, 98, 85,\n             116, 119, 75, 113, 118, 86, 68, 113, 51, 116, 98, 69, 111, 51, 90,\n             73, 99, 111, 104, 98, 68, 116, 116, 54, 83, 98, 102, 109, 87, 122,\n             103, 103, 97, 98, 112, 81, 120, 78, 120, 117, 66, 112, 111, 79, 79,\n             102, 95, 97, 95, 72, 103, 77, 88, 75, 95, 108, 104, 113, 105, 103,\n             73, 52, 121, 95, 107, 113, 83, 49, 119, 89, 53, 50, 73, 119, 106, 85,\n             110, 53, 114, 103, 82, 114, 74, 45, 121, 89, 111, 49, 104, 52, 49,\n             75, 82, 45, 118, 122, 50, 112, 89, 104, 69, 65, 101, 89, 114, 104,\n             116, 116, 87, 116, 120, 86, 113, 76, 67, 82, 86, 105, 68, 54, 99, 34,\n             44, 34, 100, 113, 34, 58, 34, 65, 118, 102, 83, 48, 45, 103, 82, 120,\n             118, 110, 48, 98, 119, 74, 111, 77, 83, 110, 70, 120, 89, 99, 75, 49,\n             87, 110, 117, 69, 106, 81, 70, 108, 117, 77, 71, 102, 119, 71, 105,\n             116, 81, 66, 87, 116, 102, 90, 49, 69, 114, 55, 116, 49, 120, 68,\n             107, 98, 78, 57, 71, 81, 84, 66, 57, 121, 113, 112, 68, 111, 89, 97,\n             78, 48, 54, 72, 55, 67, 70, 116, 114, 107, 120, 104, 74, 73, 66, 81,\n             97, 106, 54, 110, 107, 70, 53, 75, 75, 83, 51, 84, 81, 116, 81, 53,\n             113, 67, 122, 107, 79, 107, 109, 120, 73, 101, 51, 75, 82, 98, 66,\n             121, 109, 88, 120, 107, 98, 53, 113, 119, 85, 112, 88, 53, 69, 76,\n             68, 53, 120, 70, 99, 54, 70, 101, 105, 97, 102, 87, 89, 89, 54, 51,\n             84, 109, 109, 69, 65, 117, 95, 108, 82, 70, 67, 79, 74, 51, 120, 68,\n             101, 97, 45, 111, 116, 115, 34, 44, 34, 113, 105, 34, 58, 34, 108,\n             83, 81, 105, 45, 119, 57, 67, 112, 121, 85, 82, 101, 77, 69, 114, 80,\n             49, 82, 115, 66, 76, 107, 55, 119, 78, 116, 79, 118, 115, 53, 69, 81,\n             112, 80, 113, 109, 117, 77, 118, 113, 87, 53, 55, 78, 66, 85, 99,\n             122, 83, 99, 69, 111, 80, 119, 109, 85, 113, 113, 97, 98, 117, 57,\n             86, 48, 45, 80, 121, 52, 100, 81, 53, 55, 95, 98, 97, 112, 111, 75,\n             82, 117, 49, 82, 57, 48, 98, 118, 117, 70, 110, 85, 54, 51, 83, 72,\n             87, 69, 70, 103, 108, 90, 81, 118, 74, 68, 77, 101, 65, 118, 109,\n             106, 52, 115, 109, 45, 70, 112, 48, 111, 89, 117, 95, 110, 101, 111,\n             116, 103, 81, 48, 104, 122, 98, 73, 53, 103, 114, 121, 55, 97, 106,\n             100, 89, 121, 57, 45, 50, 108, 78, 120, 95, 55, 54, 97, 66, 90, 111,\n             79, 85, 117, 57, 72, 67, 74, 45, 85, 115, 102, 83, 79, 73, 56, 34,\n             125] as byte[]\n\n    // https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C.2\n    private static final String RFC_JWE_PROTECTED_HEADER_JSON = rfcString('''\n    {\n      \"alg\":\"PBES2-HS256+A128KW\",\n      \"p2s\":\"2WCTcJZ1Rvd_CJuJripQ1w\",\n      \"p2c\":4096,\n      \"enc\":\"A128CBC-HS256\",\n      \"cty\":\"jwk+json\"\n     }\n     ''')\n    private static final byte[] RFC_P2S = [217, 96, 147, 112, 150, 117, 70, 247, 127, 8, 155, 137, 174, 42, 80, 215] as byte[]\n    private static final int RFC_P2C = 4096\n    private static final String RFC_ENCODED_JWE_PROTECTED_HEADER = rfcString('''\n     eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2ZF9DSn\n     VKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5Ijoi\n     andrK2pzb24ifQ\n     ''')\n\n    // https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C.3\n    private static final byte[] RFC_CEK_BYTES = [111, 27, 25, 52, 66, 29, 20, 78, 92, 176, 56, 240, 65, 208, 82, 112,\n                                                 161, 131, 36, 55, 202, 236, 185, 172, 129, 23, 153, 194, 195, 48,\n                                                 253, 182] as byte[]\n    private static final SecretKey RFC_CEK = new SecretKeySpec(RFC_CEK_BYTES, \"AES\")\n\n    // https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C.4\n    private static final String RFC_SHARED_PASSPHRASE = 'Thus from my lips, by yours, my sin is purged.'\n    private static final byte[] RFC_SHARED_PASSPHRASE_BYTES = [\n            84, 104, 117, 115, 32, 102, 114, 111, 109, 32, 109, 121, 32, 108,\n            105, 112, 115, 44, 32, 98, 121, 32, 121, 111, 117, 114, 115, 44, 32,\n            109, 121, 32, 115, 105, 110, 32, 105, 115, 32, 112, 117, 114, 103,\n            101, 100, 46] as byte[]\n\n    // \"The Salt value (UTF8(Alg) || 0x00 || Salt Input) is\":\n    @SuppressWarnings('unused')\n    private static final byte[] RFC_SALT_VALUE = [80, 66, 69, 83, 50, 45, 72, 83, 50, 53, 54, 43, 65, 49, 50, 56, 75,\n                                                  87, 0, 217, 96, 147, 112, 150, 117, 70, 247, 127, 8, 155, 137, 174,\n                                                  42, 80, 215] as byte[]\n    @SuppressWarnings('unused')\n    private static final byte[] RFC_PBKDF2_DERIVED_KEY_BYTES =\n            [110, 171, 169, 92, 129, 92, 109, 117, 233, 242, 116, 233, 170, 14, 24, 75]\n\n    // https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C.6\n    private static final byte[] RFC_IV = [97, 239, 99, 214, 171, 54, 216, 57, 145, 72, 7, 93, 34, 31, 149, 156] as byte[]\n\n    // https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C.9\n    private static final String RFC_COMPACT_JWE = rfcString('''\n     eyJhbGciOiJQQkVTMi1IUzI1NitBMTI4S1ciLCJwMnMiOiIyV0NUY0paMVJ2ZF9DSn\n     VKcmlwUTF3IiwicDJjIjo0MDk2LCJlbmMiOiJBMTI4Q0JDLUhTMjU2IiwiY3R5Ijoi\n     andrK2pzb24ifQ.\n     TrqXOwuNUfDV9VPTNbyGvEJ9JMjefAVn-TR1uIxR9p6hsRQh9Tk7BA.\n     Ye9j1qs22DmRSAddIh-VnA.\n     AwhB8lxrlKjFn02LGWEqg27H4Tg9fyZAbFv3p5ZicHpj64QyHC44qqlZ3JEmnZTgQo\n     wIqZJ13jbyHB8LgePiqUJ1hf6M2HPLgzw8L-mEeQ0jvDUTrE07NtOerBk8bwBQyZ6g\n     0kQ3DEOIglfYxV8-FJvNBYwbqN1Bck6d_i7OtjSHV-8DIrp-3JcRIe05YKy3Oi34Z_\n     GOiAc1EK21B11c_AE11PII_wvvtRiUiG8YofQXakWd1_O98Kap-UgmyWPfreUJ3lJP\n     nbD4Ve95owEfMGLOPflo2MnjaTDCwQokoJ_xplQ2vNPz8iguLcHBoKllyQFJL2mOWB\n     wqhBo9Oj-O800as5mmLsvQMTflIrIEbbTMzHMBZ8EFW9fWwwFu0DWQJGkMNhmBZQ-3\n     lvqTc-M6-gWA6D8PDhONfP2Oib2HGizwG1iEaX8GRyUpfLuljCLIe1DkGOewhKuKkZ\n     h04DKNM5Nbugf2atmU9OP0Ldx5peCUtRG1gMVl7Qup5ZXHTjgPDr5b2N731UooCGAU\n     qHdgGhg0JVJ_ObCTdjsH4CF1SJsdUhrXvYx3HJh2Xd7CwJRzU_3Y1GxYU6-s3GFPbi\n     rfqqEipJDBTHpcoCmyrwYjYHFgnlqBZRotRrS95g8F95bRXqsaDY7UgQGwBQBwy665\n     d0zpvTasvfXf_c0MWAl-neFaKOW_Px6g4EUDjG1GWSXV9cLStLw_0ovdApDIFLHYHe\n     PyagyHjouQUuGiq7BsYwYrwaF06tgB8hV8omLNfMEmDPJaZUzMuHw6tBDwGkzD-tS_\n     ub9hxrpJ4UsOWnt5rGUyoN2N_c1-TQlXxm5oto14MxnoAyBQBpwIEgSH3Y4ZhwKBhH\n     PjSo0cdwuNdYbGPpb-YUvF-2NZzODiQ1OvWQBRHSbPWYz_xbGkgD504LRtqRwCO7CC\n     _CyyURi1sEssPVsMJRX_U4LFEOc82TiDdqjKOjRUfKK5rqLi8nBE9soQ0DSaOoFQZi\n     GrBrqxDsNYiAYAmxxkos-i3nX4qtByVx85sCE5U_0MqG7COxZWMOPEFrDaepUV-cOy\n     rvoUIng8i8ljKBKxETY2BgPegKBYCxsAUcAkKamSCC9AiBxA0UOHyhTqtlvMksO7AE\n     hNC2-YzPyx1FkhMoS4LLe6E_pFsMlmjA6P1NSge9C5G5tETYXGAn6b1xZbHtmwrPSc\n     ro9LWhVmAaA7_bxYObnFUxgWtK4vzzQBjZJ36UTk4OTB-JvKWgfVWCFsaw5WCHj6Oo\n     4jpO7d2yN7WMfAj2hTEabz9wumQ0TMhBduZ-QON3pYObSy7TSC1vVme0NJrwF_cJRe\n     hKTFmdlXGVldPxZCplr7ZQqRQhF8JP-l4mEQVnCaWGn9ONHlemczGOS-A-wwtnmwjI\n     B1V_vgJRf4FdpV-4hUk4-QLpu3-1lWFxrtZKcggq3tWTduRo5_QebQbUUT_VSCgsFc\n     OmyWKoj56lbxthN19hq1XGWbLGfrrR6MWh23vk01zn8FVwi7uFwEnRYSafsnWLa1Z5\n     TpBj9GvAdl2H9NHwzpB5NqHpZNkQ3NMDj13Fn8fzO0JB83Etbm_tnFQfcb13X3bJ15\n     Cz-Ww1MGhvIpGGnMBT_ADp9xSIyAM9dQ1yeVXk-AIgWBUlN5uyWSGyCxp0cJwx7HxM\n     38z0UIeBu-MytL-eqndM7LxytsVzCbjOTSVRmhYEMIzUAnS1gs7uMQAGRdgRIElTJE\n     SGMjb_4bZq9s6Ve1LKkSi0_QDsrABaLe55UY0zF4ZSfOV5PMyPtocwV_dcNPlxLgNA\n     D1BFX_Z9kAdMZQW6fAmsfFle0zAoMe4l9pMESH0JB4sJGdCKtQXj1cXNydDYozF7l8\n     H00BV_Er7zd6VtIw0MxwkFCTatsv_R-GsBCH218RgVPsfYhwVuT8R4HarpzsDBufC4\n     r8_c8fc9Z278sQ081jFjOja6L2x0N_ImzFNXU6xwO-Ska-QeuvYZ3X_L31ZOX4Llp-\n     7QSfgDoHnOxFv1Xws-D5mDHD3zxOup2b2TppdKTZb9eW2vxUVviM8OI9atBfPKMGAO\n     v9omA-6vv5IxUH0-lWMiHLQ_g8vnswp-Jav0c4t6URVUzujNOoNd_CBGGVnHiJTCHl\n     88LQxsqLHHIu4Fz-U2SGnlxGTj0-ihit2ELGRv4vO8E1BosTmf0cx3qgG0Pq0eOLBD\n     IHsrdZ_CCAiTc0HVkMbyq1M6qEhM-q5P6y1QCIrwg.\n     0HFmhOzsQ98nNWJjIHkR7A\n''')\n\n    @Test\n    void test() {\n\n        //ensure the bytes of the JSON string copied from the RFC matches the array definition in the RFC:\n        assertArrayEquals RFC_JWK_JSON_BYTES, RFC_JWK_JSON.getBytes(StandardCharsets.ISO_8859_1)\n        assertEquals RFC_ENCODED_JWE_PROTECTED_HEADER, Encoders.BASE64URL.encode(RFC_JWE_PROTECTED_HEADER_JSON.getBytes(StandardCharsets.UTF_8))\n        assertArrayEquals RFC_SHARED_PASSPHRASE_BYTES, RFC_SHARED_PASSPHRASE.getBytes(StandardCharsets.UTF_8)\n\n        //ensure that the KeyAlgorithm reflects test harness values:\n        def enc = new HmacAesAeadAlgorithm(128) {\n            @Override\n            SecretKeyBuilder key() {\n                return Keys.builder(RFC_CEK)\n            }\n\n            @Override\n            protected byte[] ensureInitializationVector(Request request) {\n                return RFC_IV\n            }\n        }\n        def alg = new Pbes2HsAkwAlgorithm(128) {\n            @Override\n            protected byte[] generateInputSalt(KeyRequest<?> request) {\n                return RFC_P2S\n            }\n        }\n        def ser = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertTrue m instanceof JweHeader\n                JweHeader header = (JweHeader) m\n\n                //assert the 5 values have been set per the RFC:\n                assertEquals 5, header.size()\n                assertEquals 'PBES2-HS256+A128KW', header.getAlgorithm()\n                assertEquals '2WCTcJZ1Rvd_CJuJripQ1w', header.p2s\n                assertEquals 4096, header.p2c\n                assertEquals 'A128CBC-HS256', header.getEncryptionAlgorithm()\n                assertEquals 'jwk+json', header.cty\n\n                //JSON serialization order isn't guaranteed, so now that we've asserted the values are correct,\n                //return the exact serialization order expected in the RFC test:\n                return RFC_JWE_PROTECTED_HEADER_JSON\n            }\n        }\n\n        Password key = Keys.password(RFC_SHARED_PASSPHRASE.toCharArray())\n\n        String compact = Jwts.builder()\n                .setPayload(RFC_JWK_JSON)\n                .header().contentType('jwk+json').pbes2Count(RFC_P2C).and()\n                .encryptWith(key, alg, enc)\n                .json(ser) //ensure header created as expected with an assertion serializer\n                .compact()\n\n        assertEquals RFC_COMPACT_JWE, compact\n\n        //ensure we can decrypt now:\n        Jwe<byte[]> jwe = Jwts.parser().decryptWith(key).build().parseEncryptedContent(compact)\n\n        assertEquals RFC_JWK_JSON, new String(jwe.getPayload(), StandardCharsets.UTF_8)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB1Test.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\n\nimport static org.junit.Assert.assertArrayEquals\n\n/**\n * Tests successful encryption and decryption using 'AES_128_CBC_HMAC_SHA_256' as defined in\n * <a href=\"https://tools.ietf.org/html/rfc7518#appendix-B.1\">RFC 7518, Appendix B.1</a>\n *\n * @since 0.12.0\n */\nclass RFC7518AppendixB1Test {\n\n    final byte[] K =\n            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n             0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f] as byte[]\n    final SecretKey KEY = new SecretKeySpec(K, \"AES\")\n\n    @SuppressWarnings('unused')\n    final byte[] MAC_KEY =\n            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f] as byte[]\n\n    @SuppressWarnings('unused')\n    final byte[] ENC_KEY =\n            [0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f] as byte[]\n\n    final byte[] P =\n            [0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,\n             0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,\n             0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,\n             0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,\n             0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69,\n             0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66,\n             0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,\n             0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65] as byte[]\n\n    final byte[] IV =\n            [0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04] as byte[]\n\n    final byte[] A =\n            [0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63,\n             0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20,\n             0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, 0x66, 0x73] as byte[]\n\n    @SuppressWarnings('unused')\n    final byte[] AL = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x50] as byte[]\n\n    final byte[] E =\n            [0xc8, 0x0e, 0xdf, 0xa3, 0x2d, 0xdf, 0x39, 0xd5, 0xef, 0x00, 0xc0, 0xb4, 0x68, 0x83, 0x42, 0x79,\n             0xa2, 0xe4, 0x6a, 0x1b, 0x80, 0x49, 0xf7, 0x92, 0xf7, 0x6b, 0xfe, 0x54, 0xb9, 0x03, 0xa9, 0xc9,\n             0xa9, 0x4a, 0xc9, 0xb4, 0x7a, 0xd2, 0x65, 0x5c, 0x5f, 0x10, 0xf9, 0xae, 0xf7, 0x14, 0x27, 0xe2,\n             0xfc, 0x6f, 0x9b, 0x3f, 0x39, 0x9a, 0x22, 0x14, 0x89, 0xf1, 0x63, 0x62, 0xc7, 0x03, 0x23, 0x36,\n             0x09, 0xd4, 0x5a, 0xc6, 0x98, 0x64, 0xe3, 0x32, 0x1c, 0xf8, 0x29, 0x35, 0xac, 0x40, 0x96, 0xc8,\n             0x6e, 0x13, 0x33, 0x14, 0xc5, 0x40, 0x19, 0xe8, 0xca, 0x79, 0x80, 0xdf, 0xa4, 0xb9, 0xcf, 0x1b,\n             0x38, 0x4c, 0x48, 0x6f, 0x3a, 0x54, 0xc5, 0x10, 0x78, 0x15, 0x8e, 0xe5, 0xd7, 0x9d, 0xe5, 0x9f,\n             0xbd, 0x34, 0xd8, 0x48, 0xb3, 0xd6, 0x95, 0x50, 0xa6, 0x76, 0x46, 0x34, 0x44, 0x27, 0xad, 0xe5,\n             0x4b, 0x88, 0x51, 0xff, 0xb5, 0x98, 0xf7, 0xf8, 0x00, 0x74, 0xb9, 0x47, 0x3c, 0x82, 0xe2, 0xdb] as byte[]\n\n    @SuppressWarnings('unused')\n    final byte[] M =\n            [0x65, 0x2c, 0x3f, 0xa3, 0x6b, 0x0a, 0x7c, 0x5b, 0x32, 0x19, 0xfa, 0xb3, 0xa3, 0x0b, 0xc1, 0xc4,\n             0xe6, 0xe5, 0x45, 0x82, 0x47, 0x65, 0x15, 0xf0, 0xad, 0x9f, 0x75, 0xa2, 0xb7, 0x1c, 0x73, 0xef] as byte[]\n\n    final byte[] T =\n            [0x65, 0x2c, 0x3f, 0xa3, 0x6b, 0x0a, 0x7c, 0x5b, 0x32, 0x19, 0xfa, 0xb3, 0xa3, 0x0b, 0xc1, 0xc4] as byte[]\n\n    @Test\n    void test() {\n\n        def alg = Jwts.ENC.A128CBC_HS256\n        def aad = Streams.of(A)\n        def out = new ByteArrayOutputStream(8192)\n        def result = new DefaultAeadResult(out)\n        def request = new DefaultAeadRequest(Streams.of(P), null, null, KEY, aad, IV)\n        alg.encrypt(request, result)\n\n        byte[] ciphertext = out.toByteArray()\n        byte[] tag = result.getDigest()\n        byte[] iv = result.getIv()\n\n        assertArrayEquals E, ciphertext\n        assertArrayEquals T, tag\n        assertArrayEquals IV, iv //shouldn't have been altered\n\n        // now test decryption:\n        out = new ByteArrayOutputStream(8192)\n        def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), KEY, aad, iv, tag)\n        alg.decrypt(dreq, out)\n        assertArrayEquals(P, out.toByteArray())\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB2Test.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.security.AeadRequest\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\n\nimport static org.junit.Assert.assertArrayEquals\n\n/**\n * Tests successful encryption and decryption using 'AES_192_CBC_HMAC_SHA_384' as defined in\n * <a href=\"https://tools.ietf.org/html/rfc7518#appendix-B.2\">RFC 7518, Appendix B.2</a>\n *\n * @since 0.12.0\n */\nclass RFC7518AppendixB2Test {\n\n    final byte[] K =\n            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n             0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\n             0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f] as byte[]\n    final SecretKey KEY = new SecretKeySpec(K, \"AES\")\n\n    final byte[] P =\n            [0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,\n             0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,\n             0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,\n             0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,\n             0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69,\n             0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66,\n             0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,\n             0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65] as byte[]\n\n    final byte[] IV =\n            [0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04] as byte[]\n\n    final byte[] A =\n            [0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63,\n             0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20,\n             0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, 0x66, 0x73] as byte[]\n\n    @SuppressWarnings('unused')\n    final byte[] AL = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x50] as byte[]\n\n    final byte[] E =\n            [0xea, 0x65, 0xda, 0x6b, 0x59, 0xe6, 0x1e, 0xdb, 0x41, 0x9b, 0xe6, 0x2d, 0x19, 0x71, 0x2a, 0xe5,\n             0xd3, 0x03, 0xee, 0xb5, 0x00, 0x52, 0xd0, 0xdf, 0xd6, 0x69, 0x7f, 0x77, 0x22, 0x4c, 0x8e, 0xdb,\n             0x00, 0x0d, 0x27, 0x9b, 0xdc, 0x14, 0xc1, 0x07, 0x26, 0x54, 0xbd, 0x30, 0x94, 0x42, 0x30, 0xc6,\n             0x57, 0xbe, 0xd4, 0xca, 0x0c, 0x9f, 0x4a, 0x84, 0x66, 0xf2, 0x2b, 0x22, 0x6d, 0x17, 0x46, 0x21,\n             0x4b, 0xf8, 0xcf, 0xc2, 0x40, 0x0a, 0xdd, 0x9f, 0x51, 0x26, 0xe4, 0x79, 0x66, 0x3f, 0xc9, 0x0b,\n             0x3b, 0xed, 0x78, 0x7a, 0x2f, 0x0f, 0xfc, 0xbf, 0x39, 0x04, 0xbe, 0x2a, 0x64, 0x1d, 0x5c, 0x21,\n             0x05, 0xbf, 0xe5, 0x91, 0xba, 0xe2, 0x3b, 0x1d, 0x74, 0x49, 0xe5, 0x32, 0xee, 0xf6, 0x0a, 0x9a,\n             0xc8, 0xbb, 0x6c, 0x6b, 0x01, 0xd3, 0x5d, 0x49, 0x78, 0x7b, 0xcd, 0x57, 0xef, 0x48, 0x49, 0x27,\n             0xf2, 0x80, 0xad, 0xc9, 0x1a, 0xc0, 0xc4, 0xe7, 0x9c, 0x7b, 0x11, 0xef, 0xc6, 0x00, 0x54, 0xe3] as byte[]\n\n    @SuppressWarnings('unused')\n    final byte[] M =\n            [0x84, 0x90, 0xac, 0x0e, 0x58, 0x94, 0x9b, 0xfe, 0x51, 0x87, 0x5d, 0x73, 0x3f, 0x93, 0xac, 0x20,\n             0x75, 0x16, 0x80, 0x39, 0xcc, 0xc7, 0x33, 0xd7, 0x45, 0x94, 0xf8, 0x86, 0xb3, 0xfa, 0xaf, 0xd4,\n             0x86, 0xf2, 0x5c, 0x71, 0x31, 0xe3, 0x28, 0x1e, 0x36, 0xc7, 0xa2, 0xd1, 0x30, 0xaf, 0xde, 0x57] as byte[]\n\n    final byte[] T =\n            [0x84, 0x90, 0xac, 0x0e, 0x58, 0x94, 0x9b, 0xfe, 0x51, 0x87, 0x5d, 0x73, 0x3f, 0x93, 0xac, 0x20,\n             0x75, 0x16, 0x80, 0x39, 0xcc, 0xc7, 0x33, 0xd7] as byte[]\n\n    @Test\n    void test() {\n\n        def alg = Jwts.ENC.A192CBC_HS384\n        def aad = Streams.of(A)\n        def out = new ByteArrayOutputStream(8192)\n        def result = new DefaultAeadResult(out)\n        AeadRequest req = new DefaultAeadRequest(Streams.of(P), null, null, KEY, aad, IV)\n        alg.encrypt(req, result)\n\n        byte[] ciphertext = out.toByteArray()\n        byte[] tag = result.getDigest()\n        byte[] iv = result.getIv()\n\n        assertArrayEquals E, ciphertext\n        assertArrayEquals T, tag\n        assertArrayEquals IV, iv //shouldn't have been altered\n\n        // now test decryption:\n        out = new ByteArrayOutputStream(8192)\n        def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), KEY, aad, iv, tag)\n        alg.decrypt(dreq, out)\n        assertArrayEquals(P, out.toByteArray())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixB3Test.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.security.AeadRequest\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\n\nimport static org.junit.Assert.assertArrayEquals\n\n/**\n * Tests successful encryption and decryption using 'AES_256_CBC_HMAC_SHA_512' as defined in\n * <a href=\"https://tools.ietf.org/html/rfc7518#appendix-B.3\">RFC 7518, Appendix B.3</a>\n *\n * @since 0.12.0\n */\nclass RFC7518AppendixB3Test {\n\n    final byte[] K =\n            [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n             0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\n             0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\n             0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f] as byte[]\n    final SecretKey KEY = new SecretKeySpec(K, \"AES\")\n\n    final byte[] P =\n            [0x41, 0x20, 0x63, 0x69, 0x70, 0x68, 0x65, 0x72, 0x20, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x20,\n             0x6d, 0x75, 0x73, 0x74, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x62, 0x65, 0x20, 0x72, 0x65, 0x71, 0x75,\n             0x69, 0x72, 0x65, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x62, 0x65, 0x20, 0x73, 0x65, 0x63, 0x72, 0x65,\n             0x74, 0x2c, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x69, 0x74, 0x20, 0x6d, 0x75, 0x73, 0x74, 0x20, 0x62,\n             0x65, 0x20, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x74, 0x6f, 0x20, 0x66, 0x61, 0x6c, 0x6c, 0x20, 0x69,\n             0x6e, 0x74, 0x6f, 0x20, 0x74, 0x68, 0x65, 0x20, 0x68, 0x61, 0x6e, 0x64, 0x73, 0x20, 0x6f, 0x66,\n             0x20, 0x74, 0x68, 0x65, 0x20, 0x65, 0x6e, 0x65, 0x6d, 0x79, 0x20, 0x77, 0x69, 0x74, 0x68, 0x6f,\n             0x75, 0x74, 0x20, 0x69, 0x6e, 0x63, 0x6f, 0x6e, 0x76, 0x65, 0x6e, 0x69, 0x65, 0x6e, 0x63, 0x65] as byte[]\n\n    final byte[] IV =\n            [0x1a, 0xf3, 0x8c, 0x2d, 0xc2, 0xb9, 0x6f, 0xfd, 0xd8, 0x66, 0x94, 0x09, 0x23, 0x41, 0xbc, 0x04] as byte[]\n\n    final byte[] A =\n            [0x54, 0x68, 0x65, 0x20, 0x73, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x20, 0x70, 0x72, 0x69, 0x6e, 0x63,\n             0x69, 0x70, 0x6c, 0x65, 0x20, 0x6f, 0x66, 0x20, 0x41, 0x75, 0x67, 0x75, 0x73, 0x74, 0x65, 0x20,\n             0x4b, 0x65, 0x72, 0x63, 0x6b, 0x68, 0x6f, 0x66, 0x66, 0x73] as byte[]\n\n    @SuppressWarnings('unused')\n    final byte[] AL = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x50] as byte[]\n\n    final byte[] E =\n            [0x4a, 0xff, 0xaa, 0xad, 0xb7, 0x8c, 0x31, 0xc5, 0xda, 0x4b, 0x1b, 0x59, 0x0d, 0x10, 0xff, 0xbd,\n             0x3d, 0xd8, 0xd5, 0xd3, 0x02, 0x42, 0x35, 0x26, 0x91, 0x2d, 0xa0, 0x37, 0xec, 0xbc, 0xc7, 0xbd,\n             0x82, 0x2c, 0x30, 0x1d, 0xd6, 0x7c, 0x37, 0x3b, 0xcc, 0xb5, 0x84, 0xad, 0x3e, 0x92, 0x79, 0xc2,\n             0xe6, 0xd1, 0x2a, 0x13, 0x74, 0xb7, 0x7f, 0x07, 0x75, 0x53, 0xdf, 0x82, 0x94, 0x10, 0x44, 0x6b,\n             0x36, 0xeb, 0xd9, 0x70, 0x66, 0x29, 0x6a, 0xe6, 0x42, 0x7e, 0xa7, 0x5c, 0x2e, 0x08, 0x46, 0xa1,\n             0x1a, 0x09, 0xcc, 0xf5, 0x37, 0x0d, 0xc8, 0x0b, 0xfe, 0xcb, 0xad, 0x28, 0xc7, 0x3f, 0x09, 0xb3,\n             0xa3, 0xb7, 0x5e, 0x66, 0x2a, 0x25, 0x94, 0x41, 0x0a, 0xe4, 0x96, 0xb2, 0xe2, 0xe6, 0x60, 0x9e,\n             0x31, 0xe6, 0xe0, 0x2c, 0xc8, 0x37, 0xf0, 0x53, 0xd2, 0x1f, 0x37, 0xff, 0x4f, 0x51, 0x95, 0x0b,\n             0xbe, 0x26, 0x38, 0xd0, 0x9d, 0xd7, 0xa4, 0x93, 0x09, 0x30, 0x80, 0x6d, 0x07, 0x03, 0xb1, 0xf6] as byte[]\n\n    @SuppressWarnings('unused')\n    final byte[] M =\n            [0x4d, 0xd3, 0xb4, 0xc0, 0x88, 0xa7, 0xf4, 0x5c, 0x21, 0x68, 0x39, 0x64, 0x5b, 0x20, 0x12, 0xbf,\n             0x2e, 0x62, 0x69, 0xa8, 0xc5, 0x6a, 0x81, 0x6d, 0xbc, 0x1b, 0x26, 0x77, 0x61, 0x95, 0x5b, 0xc5,\n             0xfd, 0x30, 0xa5, 0x65, 0xc6, 0x16, 0xff, 0xb2, 0xf3, 0x64, 0xba, 0xec, 0xe6, 0x8f, 0xc4, 0x07,\n             0x53, 0xbc, 0xfc, 0x02, 0x5d, 0xde, 0x36, 0x93, 0x75, 0x4a, 0xa1, 0xf5, 0xc3, 0x37, 0x3b, 0x9c] as byte[]\n\n    final byte[] T =\n            [0x4d, 0xd3, 0xb4, 0xc0, 0x88, 0xa7, 0xf4, 0x5c, 0x21, 0x68, 0x39, 0x64, 0x5b, 0x20, 0x12, 0xbf,\n             0x2e, 0x62, 0x69, 0xa8, 0xc5, 0x6a, 0x81, 0x6d, 0xbc, 0x1b, 0x26, 0x77, 0x61, 0x95, 0x5b, 0xc5] as byte[]\n\n    @Test\n    void test() {\n\n        def alg = Jwts.ENC.A256CBC_HS512\n        def aad = Streams.of(A)\n        def out = new ByteArrayOutputStream(8192)\n        def res = new DefaultAeadResult(out)\n        AeadRequest req = new DefaultAeadRequest(Streams.of(P), null, null, KEY, aad, IV)\n        alg.encrypt(req, res)\n        byte[] ciphertext = out.toByteArray()\n        byte[] tag = res.getDigest()\n        byte[] iv = res.getIv()\n\n        assertArrayEquals E, ciphertext\n        assertArrayEquals T, tag\n        assertArrayEquals IV, iv //shouldn't have been altered\n\n        // now test decryption:\n        out = new ByteArrayOutputStream(8192)\n        def dreq = new DefaultDecryptAeadRequest(Streams.of(ciphertext), KEY, aad, iv, tag)\n        alg.decrypt(dreq, out)\n        assertArrayEquals(P, out.toByteArray())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7518AppendixCTest.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Claims\nimport io.jsonwebtoken.Jwe\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.lang.Services\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Deserializer\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport java.security.KeyPair\nimport java.security.Provider\nimport java.security.SecureRandom\n\nimport static org.junit.Assert.*\n\nclass RFC7518AppendixCTest {\n\n    private static final String rfcString(String s) {\n        return s.replaceAll('[\\\\s]', '')\n    }\n\n    private static final Map<String, ?> fromEncoded(String s) {\n        byte[] json = Decoders.BASE64URL.decode(s)\n        return fromJson(Strings.utf8(json))\n    }\n\n    private static final Map<String, ?> fromJson(String s) {\n        return Services.get(Deserializer).deserialize(new StringReader(s)) as Map<String, ?>\n    }\n\n    private static EcPrivateJwk readJwk(String json) {\n        Map<String, ?> m = fromJson(json)\n        return Jwks.builder().add(m).build() as EcPrivateJwk\n    }\n\n    // https://www.rfc-editor.org/rfc/rfc7517.html#appendix-C.1\n    private static final String ALICE_EPHEMERAL_JWK_STRING = rfcString('''\n    {\"kty\":\"EC\",\n      \"crv\":\"P-256\",\n      \"x\":\"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0\",\n      \"y\":\"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps\",\n      \"d\":\"0_NxaRPUMQoAJt50Gz8YiTr8gRTwyEaCumd-MToTmIo\"\n     }''')\n\n    private static final String BOB_PRIVATE_JWK_STRING = rfcString('''\n    {\"kty\":\"EC\",\n      \"crv\":\"P-256\",\n      \"x\":\"weNJy2HscCSM6AEDTDg04biOvhFhyyWvOHQfeF_PxMQ\",\n      \"y\":\"e8lnCO-AlStT-NJVX-crhB7QRYhiix03illJOVAOyck\",\n      \"d\":\"VEmDZpDXXK8p8N0Cndsxs924q6nS1RXFASRl6BfUqdw\"\n     }''')\n\n    private static final String RFC_HEADER_JSON_STRING = rfcString('''\n    {\"alg\":\"ECDH-ES\",\n      \"enc\":\"A128GCM\",\n      \"apu\":\"QWxpY2U\",\n      \"apv\":\"Qm9i\",\n      \"epk\":\n       {\"kty\":\"EC\",\n        \"crv\":\"P-256\",\n        \"x\":\"gI0GAILBdu7T53akrFmMyGcsF3n5dO7MmwNBHKW5SV0\",\n        \"y\":\"SLW_xSffzlPWrHEVI30DHM_4egVwt3NQqeUD7nMFpps\"\n       }\n     }\n    ''')\n\n    private static final byte[] RFC_DERIVED_KEY =\n            [86, 170, 141, 234, 248, 35, 109, 32, 92, 34, 40, 205, 113, 167, 16, 26] as byte[]\n\n    @Test\n    void test() {\n        EcPrivateJwk aliceJwk = readJwk(ALICE_EPHEMERAL_JWK_STRING)\n        EcPrivateJwk bobJwk = readJwk(BOB_PRIVATE_JWK_STRING)\n\n        Map<String, ?> RFC_HEADER = fromJson(RFC_HEADER_JSON_STRING)\n\n        byte[] derivedKey = null\n\n        def alg = new EcdhKeyAlgorithm() {\n\n            //ensure keypair reflects required RFC test value:\n            @Override\n            protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {\n                return aliceJwk.toKeyPair().toJavaKeyPair()\n            }\n\n            @Override\n            KeyResult getEncryptionKey(KeyRequest request) throws SecurityException {\n                KeyResult result = super.getEncryptionKey(request)\n                // save result derived key so we can compare with the RFC value:\n                derivedKey = result.getKey().getEncoded()\n                return result\n            }\n        }\n\n        String jwe = Jwts.builder()\n                .header().agreementPartyUInfo(\"Alice\").agreementPartyVInfo(\"Bob\").and()\n                .claim(\"Hello\", \"World\")\n                .encryptWith(bobJwk.toPublicJwk().toKey(), alg, Jwts.ENC.A128GCM)\n                .compact()\n\n        // Ensure the protected header produced by JJWT is identical to the one in the RFC:\n        String encodedProtectedHeader = jwe.substring(0, jwe.indexOf('.'))\n        Map<String, ?> protectedHeader = fromEncoded(encodedProtectedHeader)\n        assertEquals RFC_HEADER, protectedHeader\n\n        assertNotNull derivedKey\n        assertArrayEquals RFC_DERIVED_KEY, derivedKey\n\n        // now reverse the process and ensure it all works:\n        Jwe<Claims> claimsJwe = Jwts.parser()\n                .decryptWith(bobJwk.toKey())\n                .build().parseEncryptedClaims(jwe)\n\n        assertEquals RFC_HEADER, claimsJwe.getHeader()\n        assertEquals \"World\", claimsJwe.getPayload().get(\"Hello\")\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7520Section3Test.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertTrue\n\n/**\n * Tests successful parsing/creation of RFC 7520, Section 3\n * <a href=\"https://www.rfc-editor.org/rfc/rfc7520.html#section-3\">JSON Web Key Examples</a>.\n *\n * @since 0.12.0\n */\nclass RFC7520Section3Test {\n\n    static final String FIGURE_2 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"EC\",\n      \"kid\": \"bilbo.baggins@hobbiton.example\",\n      \"use\": \"sig\",\n      \"crv\": \"P-521\",\n      \"x\": \"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9\n            A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt\",\n      \"y\": \"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy\n            SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1\",\n      \"d\": \"AAhRON2r9cqXX1hg-RoI6R1tX5p2rUAYdmpHZoC1XNM56KtscrX6zb\n            KipQrCW9CGZH3T4ubpnoTKLDYJ_fF3_rJt\"\n    }''')\n\n    static final String FIGURE_4 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"RSA\",\n      \"kid\": \"bilbo.baggins@hobbiton.example\",\n      \"use\": \"sig\",\n      \"n\": \"n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT\n          -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV\n          wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-\n          oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde\n          3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC\n          LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g\n          HdrNP5zw\",\n      \"e\": \"AQAB\",\n      \"d\": \"bWUC9B-EFRIo8kpGfh0ZuyGPvMNKvYWNtB_ikiH9k20eT-O1q_I78e\n          iZkpXxXQ0UTEs2LsNRS-8uJbvQ-A1irkwMSMkK1J3XTGgdrhCku9gRld\n          Y7sNA_AKZGh-Q661_42rINLRCe8W-nZ34ui_qOfkLnK9QWDDqpaIsA-b\n          MwWWSDFu2MUBYwkHTMEzLYGqOe04noqeq1hExBTHBOBdkMXiuFhUq1BU\n          6l-DqEiWxqg82sXt2h-LMnT3046AOYJoRioz75tSUQfGCshWTBnP5uDj\n          d18kKhyv07lhfSJdrPdM5Plyl21hsFf4L_mHCuoFau7gdsPfHPxxjVOc\n          OpBrQzwQ\",\n      \"p\": \"3Slxg_DwTXJcb6095RoXygQCAZ5RnAvZlno1yhHtnUex_fp7AZ_9nR\n          aO7HX_-SFfGQeutao2TDjDAWU4Vupk8rw9JR0AzZ0N2fvuIAmr_WCsmG\n          peNqQnev1T7IyEsnh8UMt-n5CafhkikzhEsrmndH6LxOrvRJlsPp6Zv8\n          bUq0k\",\n      \"q\": \"uKE2dh-cTf6ERF4k4e_jy78GfPYUIaUyoSSJuBzp3Cubk3OCqs6grT\n          8bR_cu0Dm1MZwWmtdqDyI95HrUeq3MP15vMMON8lHTeZu2lmKvwqW7an\n          V5UzhM1iZ7z4yMkuUwFWoBvyY898EXvRD-hdqRxHlSqAZ192zB3pVFJ0\n          s7pFc\",\n      \"dp\": \"B8PVvXkvJrj2L-GYQ7v3y9r6Kw5g9SahXBwsWUzp19TVlgI-YV85q\n          1NIb1rxQtD-IsXXR3-TanevuRPRt5OBOdiMGQp8pbt26gljYfKU_E9xn\n          -RULHz0-ed9E9gXLKD4VGngpz-PfQ_q29pk5xWHoJp009Qf1HvChixRX\n          59ehik\",\n      \"dq\": \"CLDmDGduhylc9o7r84rEUVn7pzQ6PF83Y-iBZx5NT-TpnOZKF1pEr\n          AMVeKzFEl41DlHHqqBLSM0W1sOFbwTxYWZDm6sI6og5iTbwQGIC3gnJK\n          bi_7k_vJgGHwHxgPaX2PnvP-zyEkDERuf-ry4c_Z11Cq9AqC2yeL6kdK\n          T1cYF8\",\n      \"qi\": \"3PiqvXQN0zwMeE-sBvZgi289XP9XCQF3VWqPzMKnIgQp7_Tugo6-N\n          ZBKCQsMf3HaEGBjTVJs_jcK8-TRXvaKe-7ZMaQj8VfBdYkssbu0NKDDh\n          jJ-GtiseaDVWt7dcH0cfwxgFUHpQh7FoCrjFJ6h6ZEpMF6xmujs4qMpP\n          z8aaI4\"\n    }''')\n\n    static final String FIGURE_5 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"oct\",\n      \"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\",\n      \"use\": \"sig\",\n      \"alg\": \"HS256\",\n      \"k\": \"hJtXIZ2uSN5kbQfbtTNWbpdmhkV8FJG-Onbc6mxCcYg\"\n    }\n    ''')\n\n    static final String FIGURE_6 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"oct\",\n      \"kid\": \"1e571774-2e08-40da-8308-e8d68773842d\",\n      \"use\": \"enc\",\n      \"alg\": \"A256GCM\",\n      \"k\": \"AAPapAv4LbFbiVawEjagUBluYqN5rhna-8nuldDvOx8\"\n    }\n    ''')\n\n    @Test\n    void testSection3_1() { // EC Public Key\n        String jwkString = Strings.trimAllWhitespace('''\n        {\n          \"kty\": \"EC\",\n          \"kid\": \"bilbo.baggins@hobbiton.example\",\n          \"use\": \"sig\",\n          \"crv\": \"P-521\",\n          \"x\": \"AHKZLLOsCOzz5cY97ewNUajB957y-C-U88c3v13nmGZx6sYl_oJXu9\n                A5RkTKqjqvjyekWF-7ytDyRXYgCF5cj0Kt\",\n          \"y\": \"AdymlHvOiLxXkEhayXQnNCvDX4h9htZaCJN34kfmC6pV5OhQHiraVy\n                SsUdaQkAgDPrwQrJmbnX9cwlGfP-HqHZR1\"\n        }''')\n\n        EcPublicJwk jwk = Jwks.parser().build().parse(jwkString) as EcPublicJwk\n        assertEquals 'EC', jwk.getType()\n        assertEquals 'sig', jwk.getPublicKeyUse()\n        assertEquals 'bilbo.baggins@hobbiton.example', jwk.getId()\n        assertEquals 'P-521', jwk.get('crv')\n        assertTrue jwk.toKey() instanceof ECPublicKey\n    }\n\n    @Test\n    void testSection3_2() { // EC Private Key\n        EcPrivateJwk jwk = Jwks.parser().build().parse(FIGURE_2) as EcPrivateJwk\n        assertEquals 'EC', jwk.getType()\n        assertEquals 'sig', jwk.getPublicKeyUse()\n        assertEquals 'bilbo.baggins@hobbiton.example', jwk.getId()\n        assertEquals 'P-521', jwk.get('crv')\n        assertTrue jwk.toKey() instanceof ECPrivateKey\n    }\n\n    @Test\n    void testSection3_3() { // RSA Public Key\n        String s = Strings.trimAllWhitespace('''\n        {\n          \"kty\": \"RSA\",\n          \"kid\": \"bilbo.baggins@hobbiton.example\",\n          \"use\": \"sig\",\n          \"n\": \"n4EPtAOCc9AlkeQHPzHStgAbgs7bTZLwUBZdR8_KuKPEHLd4rHVTeT\n                -O-XV2jRojdNhxJWTDvNd7nqQ0VEiZQHz_AJmSCpMaJMRBSFKrKb2wqV\n                wGU_NsYOYL-QtiWN2lbzcEe6XC0dApr5ydQLrHqkHHig3RBordaZ6Aj-\n                oBHqFEHYpPe7Tpe-OfVfHd1E6cS6M1FZcD1NNLYD5lFHpPI9bTwJlsde\n                3uhGqC0ZCuEHg8lhzwOHrtIQbS0FVbb9k3-tVTU4fg_3L_vniUFAKwuC\n                LqKnS2BYwdq_mzSnbLY7h_qixoR7jig3__kRhuaxwUkRz5iaiQkqgc5g\n                HdrNP5zw\",\n          \"e\": \"AQAB\"\n        }''')\n\n        RsaPublicJwk jwk = Jwks.parser().build().parse(s) as RsaPublicJwk\n        assertEquals 'RSA', jwk.getType()\n        assertEquals 'sig', jwk.getPublicKeyUse()\n        assertEquals 'bilbo.baggins@hobbiton.example', jwk.getId()\n        assertEquals 256, Bytes.length(Decoders.BASE64URL.decode(jwk.get('n') as String))\n        assertTrue jwk.toKey() instanceof RSAPublicKey\n    }\n\n    @Test\n    void testSection3_4() { // RSA Private Key\n        RsaPrivateJwk jwk = Jwks.parser().build().parse(FIGURE_4) as RsaPrivateJwk\n        assertEquals 'RSA', jwk.getType()\n        assertEquals 'sig', jwk.getPublicKeyUse()\n        assertEquals 'bilbo.baggins@hobbiton.example', jwk.getId()\n        assertEquals 256, Bytes.length(Decoders.BASE64URL.decode(jwk.get('n') as String))\n        assertTrue Bytes.length(Decoders.BASE64URL.decode(jwk.get('d') as String)) <= 256\n        assertTrue jwk.toKey() instanceof RSAPrivateKey\n    }\n\n    @Test\n    void testSection3_5() { // Symmetric Key (MAC)\n        SecretJwk jwk = Jwks.parser().build().parse(FIGURE_5) as SecretJwk\n        assertEquals 'oct', jwk.getType()\n        assertEquals '018c0ae5-4d9b-471b-bfd6-eef314bc7037', jwk.getId()\n        assertEquals 'sig', jwk.get('use')\n        assertEquals 'HS256', jwk.getAlgorithm()\n        SecretKey key = jwk.toKey()\n        assertEquals 256, Bytes.bitLength(key.getEncoded())\n    }\n\n    @Test\n    void testSection3_6() { // Symmetric Key (Encryption)\n        SecretJwk jwk = Jwks.parser().build().parse(FIGURE_6) as SecretJwk\n        assertEquals 'oct', jwk.getType()\n        assertEquals '1e571774-2e08-40da-8308-e8d68773842d', jwk.getId()\n        assertEquals 'enc', jwk.get('use')\n        assertEquals 'A256GCM', jwk.getAlgorithm()\n        SecretKey key = jwk.toKey()\n        assertEquals 256, Bytes.bitLength(key.getEncoded())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7520Section4Test.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.TestSerializer\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.EcPrivateJwk\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPrivateJwk\nimport io.jsonwebtoken.security.SecretJwk\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.nio.charset.StandardCharsets\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.RSAPrivateKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertTrue\n\nclass RFC7520Section4Test {\n\n    static final byte[] utf8(String s) {\n        return s.getBytes(StandardCharsets.UTF_8)\n    }\n\n    static final String utf8(byte[] bytes) {\n        return new String(bytes, StandardCharsets.UTF_8)\n    }\n\n    static final String b64Url(byte[] bytes) {\n        return Encoders.BASE64URL.encode(bytes)\n    }\n\n    static final byte[] b64Url(String s) {\n        return Decoders.BASE64URL.decode(s)\n    }\n\n    static final String FIGURE_7 =\n            \"It\\u2019s a dangerous business, Frodo, going out your \" +\n                    \"door. You step onto the road, and if you don't keep your feet, \" +\n                    \"there\\u2019s no knowing where you might be swept off \" +\n                    \"to.\"\n\n    static final String FIGURE_8 = Strings.trimAllWhitespace('''\n    SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH\n    lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk\n    b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm\n    UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4\n    ''')\n\n    static final String FIGURE_9 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"RS256\",\n      \"kid\": \"bilbo.baggins@hobbiton.example\"\n    }\n    ''')\n\n    static final String FIGURE_10 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX\n    hhbXBsZSJ9\n    ''')\n\n    static final String FIGURE_13 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJSUzI1NiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX\n    hhbXBsZSJ9\n    .\n    SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH\n    lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk\n    b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm\n    UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4\n    .\n    MRjdkly7_-oTPTS3AXP41iQIGKa80A0ZmTuV5MEaHoxnW2e5CZ5NlKtainoFmK\n    ZopdHM1O2U4mwzJdQx996ivp83xuglII7PNDi84wnB-BDkoBwA78185hX-Es4J\n    IwmDLJK3lfWRa-XtL0RnltuYv746iYTh_qHRD68BNt1uSNCrUCTJDt5aAE6x8w\n    W1Kt9eRo4QPocSadnHXFxnt8Is9UzpERV0ePPQdLuW3IS_de3xyIrDaLGdjluP\n    xUAhb6L2aXic1U12podGU0KLUQSE_oI-ZnmKJ3F4uOZDnd6QZWJushZ41Axf_f\n    cIe8u9ipH84ogoree7vjbU5y18kDquDg\n    ''')\n\n    static final String FIGURE_16 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"PS384\",\n      \"kid\": \"bilbo.baggins@hobbiton.example\"\n    }\n    ''')\n\n    static final String FIGURE_17 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX\n    hhbXBsZSJ9\n    ''')\n\n    static final String FIGURE_20 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJQUzM4NCIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX\n    hhbXBsZSJ9\n    .\n    SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH\n    lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk\n    b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm\n    UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4\n    .\n    cu22eBqkYDKgIlTpzDXGvaFfz6WGoz7fUDcfT0kkOy42miAh2qyBzk1xEsnk2I\n    pN6-tPid6VrklHkqsGqDqHCdP6O8TTB5dDDItllVo6_1OLPpcbUrhiUSMxbbXU\n    vdvWXzg-UD8biiReQFlfz28zGWVsdiNAUf8ZnyPEgVFn442ZdNqiVJRmBqrYRX\n    e8P_ijQ7p8Vdz0TTrxUeT3lm8d9shnr2lfJT8ImUjvAA2Xez2Mlp8cBE5awDzT\n    0qI0n6uiP1aCN_2_jLAeQTlqRHtfa64QQSUmFAAjVKPbByi7xho0uTOcbH510a\n    6GYmJUAfmWjwZ6oD4ifKo8DYM-X72Eaw\n    ''')\n\n    static final String FIGURE_23 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"ES512\",\n      \"kid\": \"bilbo.baggins@hobbiton.example\"\n    }\n    ''')\n\n    static final String FIGURE_24 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX\n    hhbXBsZSJ9\n    ''')\n\n    static final String FIGURE_27 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJFUzUxMiIsImtpZCI6ImJpbGJvLmJhZ2dpbnNAaG9iYml0b24uZX\n    hhbXBsZSJ9\n    .\n    SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH\n    lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk\n    b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm\n    UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4\n    .\n    AE_R_YZCChjn4791jSQCrdPZCNYqHXCTZH0-JZGYNlaAjP2kqaluUIIUnC9qvb\n    u9Plon7KRTzoNEuT4Va2cmL1eJAQy3mtPBu_u_sDDyYjnAMDxXPn7XrT0lw-kv\n    AD890jl8e2puQens_IEKBpHABlsbEPX6sFY8OcGDqoRuBomu9xQ2\n    ''')\n\n    static final String FIGURE_30 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"HS256\",\n      \"kid\": \"018c0ae5-4d9b-471b-bfd6-eef314bc7037\"\n    }\n    ''')\n\n    static final String FIGURE_31 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW\n    VlZjMxNGJjNzAzNyJ9\n    ''')\n\n    static final String FIGURE_34 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW\n    VlZjMxNGJjNzAzNyJ9\n    .\n    SXTigJlzIGEgZGFuZ2Vyb3VzIGJ1c2luZXNzLCBGcm9kbywgZ29pbmcgb3V0IH\n    lvdXIgZG9vci4gWW91IHN0ZXAgb250byB0aGUgcm9hZCwgYW5kIGlmIHlvdSBk\n    b24ndCBrZWVwIHlvdXIgZmVldCwgdGhlcmXigJlzIG5vIGtub3dpbmcgd2hlcm\n    UgeW91IG1pZ2h0IGJlIHN3ZXB0IG9mZiB0by4\n    .\n    s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0\n    ''')\n\n    static final String FIGURE_37 = FIGURE_30 // same in RFC\n    static final String FIGURE_38 = FIGURE_31 // same in RFC\n    static final String FIGURE_41 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJIUzI1NiIsImtpZCI6IjAxOGMwYWU1LTRkOWItNDcxYi1iZmQ2LW\n    VlZjMxNGJjNzAzNyJ9\n    .\n    .\n    s0h6KThzkfBBBkLspW1h84VsJZFTsPPqMDA7g1Md7p0\n    ''')\n\n\n    static {\n        //ensure our representations match the RFC:\n        assert FIGURE_7.equals(utf8(b64Url(FIGURE_8)))\n        assert FIGURE_10.equals(b64Url(utf8(FIGURE_9)))\n        assert FIGURE_17.equals(b64Url(utf8(FIGURE_16)))\n        assert FIGURE_24.equals(b64Url(utf8(FIGURE_23)))\n        assert FIGURE_31.equals(b64Url(utf8(FIGURE_30)))\n        assert FIGURE_38.equals(b64Url(utf8(FIGURE_37)))\n    }\n\n    @Test\n    void testSection4_1() {\n\n        RsaPrivateJwk jwk = Jwks.parser().build().parse(RFC7520Section3Test.FIGURE_4) as RsaPrivateJwk\n        RSAPrivateKey key = jwk.toKey()\n\n        def alg = Jwts.SIG.RS256\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // Serializer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def writer = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 2, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals jwk.getId(), m.get('kid')\n                //everything has been asserted per the RFC - reflect the exact order as shown in the RFC:\n                return FIGURE_9\n            }\n        }\n        String result = Jwts.builder()\n                .json(writer) // assert input, return RFC ordered string\n                .header().keyId(jwk.getId()).and()\n                .setPayload(FIGURE_7)\n                .signWith(key, alg)\n                .compact()\n\n        assertEquals FIGURE_13, result\n\n        // Assert round trip works as expected:\n        def parsed = Jwts.parser().verifyWith(jwk.toPublicJwk().toKey()).build().parseSignedContent(result)\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals jwk.getId(), parsed.header.getKeyId()\n        assertEquals FIGURE_7, utf8(parsed.payload)\n    }\n\n    @Test\n    void testSection4_2() {\n\n        RsaPrivateJwk jwk = Jwks.parser().build().parse(RFC7520Section3Test.FIGURE_4) as RsaPrivateJwk\n        RSAPrivateKey key = jwk.toKey()\n\n        def alg = Jwts.SIG.PS384\n        String kid = 'bilbo.baggins@hobbiton.example'\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // Serializer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def ser = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 2, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals kid, m.get('kid')\n                //everything has been asserted per the RFC - return the exact order as shown in the RFC:\n                return FIGURE_16\n            }\n        }\n\n        String result = Jwts.builder()\n                .json(ser) // assert input, return RFC ordered string\n                .header().keyId(kid).and()\n                .setPayload(FIGURE_7)\n                .signWith(key, alg)\n                .compact()\n\n\n        // As reminded in https://www.rfc-editor.org/rfc/rfc7520.html#section-4.2, it is not possible to\n        // generate the same exact signature because RSASSA-PSS uses random data during signature creation\n        // so we at least assert that our result starts with the RFC value, ignoring the final signature\n        assertTrue result.startsWith(FIGURE_20.substring(0, FIGURE_20.lastIndexOf('.')))\n\n        // even though we can't know what the signature output is ahead of time due to random data, we can assert\n        // the signature to guarantee a round trip works as expected:\n        def parsed = Jwts.parser()\n                .verifyWith(jwk.toPublicJwk().toKey())\n                .build().parseSignedContent(result)\n\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals kid, parsed.header.getKeyId()\n        assertEquals FIGURE_7, utf8(parsed.payload)\n    }\n\n    @Test\n    void testSection4_3() {\n\n        EcPrivateJwk jwk = Jwks.parser().build().parse(RFC7520Section3Test.FIGURE_2) as EcPrivateJwk\n        ECPrivateKey key = jwk.toKey()\n\n        def alg = Jwts.SIG.ES512\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // Serializer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def ser = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 2, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals jwk.getId(), m.get('kid')\n                //everything has been asserted per the RFC - return the exact order as shown in the RFC:\n                return FIGURE_23\n            }\n        }\n\n        String result = Jwts.builder()\n                .json(ser) // assert input, return RFC ordered string\n                .header().keyId(jwk.getId()).and()\n                .setPayload(FIGURE_7)\n                .signWith(key, alg)\n                .compact()\n\n        // As reminded in https://www.rfc-editor.org/rfc/rfc7520.html#section-4.3, it is not possible to\n        // generate the same exact signature because RSASSA-PSS uses random data during signature creation\n        // so we at least assert that our result starts with the RFC value, ignoring the final signature\n        assertTrue result.startsWith(FIGURE_27.substring(0, FIGURE_27.lastIndexOf('.')))\n\n        // even though we can't know what the signature output is ahead of time due to random data, we can assert\n        // the signature to guarantee a round trip works as expected:\n        def parsed = Jwts.parser()\n                .verifyWith(jwk.toPublicJwk().toKey())\n                .build().parseSignedContent(result)\n\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals jwk.getId(), parsed.header.getKeyId()\n        assertEquals FIGURE_7, utf8(parsed.payload)\n    }\n\n    @Test\n    void testSection4_4() {\n\n        SecretJwk jwk = Jwks.parser().build().parse(RFC7520Section3Test.FIGURE_5) as SecretJwk\n        SecretKey key = jwk.toKey()\n\n        def alg = Jwts.SIG.HS256\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // Serializer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def writer = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 2, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals jwk.getId(), m.get('kid')\n                //everything has been asserted per the RFC - return the exact order as shown in the RFC:\n                return FIGURE_30\n            }\n        }\n\n        String result = Jwts.builder()\n                .json(writer) // assert input, return RFC ordered string\n                .header().keyId(jwk.getId()).and()\n                .setPayload(FIGURE_7)\n                .signWith(key, alg)\n                .compact()\n\n        assertEquals FIGURE_34, result\n\n        // Assert round trip works as expected:\n        def parsed = Jwts.parser().verifyWith(key).build().parseSignedContent(result)\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals jwk.getId(), parsed.header.getKeyId()\n        assertEquals FIGURE_7, utf8(parsed.payload)\n    }\n\n    @Test\n    void testSection4_5() {\n\n        SecretJwk jwk = Jwks.parser().build().parse(RFC7520Section3Test.FIGURE_5) as SecretJwk\n        SecretKey key = jwk.toKey()\n\n        def alg = Jwts.SIG.HS256\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // Serializer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def writer = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 2, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals jwk.getId(), m.get('kid')\n                //everything has been asserted per the RFC - return the exact order as shown in the RFC:\n                return FIGURE_37\n            }\n        }\n\n        String result = Jwts.builder()\n                .json(writer) // assert input, return RFC ordered string\n                .header().keyId(jwk.getId()).and()\n                .setPayload(FIGURE_7)\n                .signWith(key, alg)\n                .compact()\n\n        String detached = result.substring(0, result.indexOf('.')) + '..' +\n                result.substring(result.lastIndexOf('.') + 1, result.length())\n\n        assertEquals FIGURE_41, detached\n\n        // Assert round trip works as expected:\n        def parsed = Jwts.parser().verifyWith(key).build().parseSignedContent(result)\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals jwk.getId(), parsed.header.getKeyId()\n        assertEquals FIGURE_7, utf8(parsed.payload)\n    }\n\n    // void testSection4_6() {}  we don't support non-compact JSON serialization yet\n    // void testSection4_7() {}  we don't support non-compact JSON serialization yet\n    // void testSection4_8() {}  we don't support non-compact JSON serialization yet\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7520Section5Test.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.TestSerializer\nimport io.jsonwebtoken.impl.lang.CheckedFunction\nimport io.jsonwebtoken.io.Decoders\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.lang.Strings\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport javax.crypto.Cipher\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.nio.charset.StandardCharsets\nimport java.security.KeyPair\nimport java.security.Provider\nimport java.security.SecureRandom\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.assertEquals\n\nclass RFC7520Section5Test {\n\n    static final byte[] utf8(String s) {\n        return s.getBytes(StandardCharsets.UTF_8)\n    }\n\n    static final String utf8(byte[] bytes) {\n        return new String(bytes, StandardCharsets.UTF_8)\n    }\n\n    static final String b64Url(byte[] bytes) {\n        return Encoders.BASE64URL.encode(bytes)\n    }\n\n    static final byte[] b64Url(String s) {\n        return Decoders.BASE64URL.decode(s)\n    }\n\n    static final String FIGURE_72 =\n            \"You can trust us to stick with you through thick and \" +\n                    \"thin\\u2013to the bitter end. And you can trust us to \" +\n                    \"keep any secret of yours\\u2013closer than you keep it \" +\n                    \"yourself. But you cannot trust us to let you face trouble \" +\n                    \"alone, and go off without a word. We are your friends, Frodo.\"\n\n    static final String FIGURE_73 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"RSA\",\n      \"kid\": \"frodo.baggins@hobbiton.example\",\n      \"use\": \"enc\",\n      \"n\": \"maxhbsmBtdQ3CNrKvprUE6n9lYcregDMLYNeTAWcLj8NnPU9XIYegT\n          HVHQjxKDSHP2l-F5jS7sppG1wgdAqZyhnWvXhYNvcM7RfgKxqNx_xAHx\n          6f3yy7s-M9PSNCwPC2lh6UAkR4I00EhV9lrypM9Pi4lBUop9t5fS9W5U\n          NwaAllhrd-osQGPjIeI1deHTwx-ZTHu3C60Pu_LJIl6hKn9wbwaUmA4c\n          R5Bd2pgbaY7ASgsjCUbtYJaNIHSoHXprUdJZKUMAzV0WOKPfA6OPI4oy\n          pBadjvMZ4ZAj3BnXaSYsEZhaueTXvZB4eZOAjIyh2e_VOIKVMsnDrJYA\n          VotGlvMQ\",\n      \"e\": \"AQAB\",\n      \"d\": \"Kn9tgoHfiTVi8uPu5b9TnwyHwG5dK6RE0uFdlpCGnJN7ZEi963R7wy\n          bQ1PLAHmpIbNTztfrheoAniRV1NCIqXaW_qS461xiDTp4ntEPnqcKsyO\n          5jMAji7-CL8vhpYYowNFvIesgMoVaPRYMYT9TW63hNM0aWs7USZ_hLg6\n          Oe1mY0vHTI3FucjSM86Nff4oIENt43r2fspgEPGRrdE6fpLc9Oaq-qeP\n          1GFULimrRdndm-P8q8kvN3KHlNAtEgrQAgTTgz80S-3VD0FgWfgnb1PN\n          miuPUxO8OpI9KDIfu_acc6fg14nsNaJqXe6RESvhGPH2afjHqSy_Fd2v\n          pzj85bQQ\",\n      \"p\": \"2DwQmZ43FoTnQ8IkUj3BmKRf5Eh2mizZA5xEJ2MinUE3sdTYKSLtaE\n          oekX9vbBZuWxHdVhM6UnKCJ_2iNk8Z0ayLYHL0_G21aXf9-unynEpUsH\n          7HHTklLpYAzOOx1ZgVljoxAdWNn3hiEFrjZLZGS7lOH-a3QQlDDQoJOJ\n          2VFmU\",\n      \"q\": \"te8LY4-W7IyaqH1ExujjMqkTAlTeRbv0VLQnfLY2xINnrWdwiQ93_V\n          F099aP1ESeLja2nw-6iKIe-qT7mtCPozKfVtUYfz5HrJ_XY2kfexJINb\n          9lhZHMv5p1skZpeIS-GPHCC6gRlKo1q-idn_qxyusfWv7WAxlSVfQfk8\n          d6Et0\",\n      \"dp\": \"UfYKcL_or492vVc0PzwLSplbg4L3-Z5wL48mwiswbpzOyIgd2xHTH\n          QmjJpFAIZ8q-zf9RmgJXkDrFs9rkdxPtAsL1WYdeCT5c125Fkdg317JV\n          RDo1inX7x2Kdh8ERCreW8_4zXItuTl_KiXZNU5lvMQjWbIw2eTx1lpsf\n          lo0rYU\",\n      \"dq\": \"iEgcO-QfpepdH8FWd7mUFyrXdnOkXJBCogChY6YKuIHGc_p8Le9Mb\n          pFKESzEaLlN1Ehf3B6oGBl5Iz_ayUlZj2IoQZ82znoUrpa9fVYNot87A\n          CfzIG7q9Mv7RiPAderZi03tkVXAdaBau_9vs5rS-7HMtxkVrxSUvJY14\n          TkXlHE\",\n      \"qi\": \"kC-lzZOqoFaZCr5l0tOVtREKoVqaAYhQiqIRGL-MzS4sCmRkxm5vZ\n          lXYx6RtE1n_AagjqajlkjieGlxTTThHD8Iga6foGBMaAr5uR1hGQpSc7\n          Gl7CF1DZkBJMTQN6EshYzZfxW08mIO8M6Rzuh0beL6fG9mkDcIyPrBXx\n          2bQ_mM\"\n    }\n    ''')\n\n    static final String FIGURE_74 = '3qyTVhIWt5juqZUCpfRqpvauwB956MEJL2Rt-8qXKSo'\n    static final String FIGURE_75 = 'bbd5sTkYwhAIqfHsx8DayA'\n    static final String FIGURE_76 = Strings.trimAllWhitespace('''\n    laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePF\n    vG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2G\n    Xfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcG\n    TSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8Vl\n    zNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOh\n    MBs9M8XL223Fg47xlGsMXdfuY-4jaqVw\n    ''')\n\n    static final String FIGURE_77 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"RSA1_5\",\n      \"kid\": \"frodo.baggins@hobbiton.example\",\n      \"enc\": \"A128CBC-HS256\"\n    }\n    ''')\n    static final String FIGURE_78 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLm\n    V4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\n    ''')\n\n    static final String FIGURE_81 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJSU0ExXzUiLCJraWQiOiJmcm9kby5iYWdnaW5zQGhvYmJpdG9uLm\n    V4YW1wbGUiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0\n    .\n    laLxI0j-nLH-_BgLOXMozKxmy9gffy2gTdvqzfTihJBuuzxg0V7yk1WClnQePF\n    vG2K-pvSlWc9BRIazDrn50RcRai__3TDON395H3c62tIouJJ4XaRvYHFjZTZ2G\n    Xfz8YAImcc91Tfk0WXC2F5Xbb71ClQ1DDH151tlpH77f2ff7xiSxh9oSewYrcG\n    TSLUeeCt36r1Kt3OSj7EyBQXoZlN7IxbyhMAfgIe7Mv1rOTOI5I8NQqeXXW8Vl\n    zNmoxaGMny3YnGir5Wf6Qt2nBq4qDaPdnaAuuGUGEecelIO1wx1BpyIfgvfjOh\n    MBs9M8XL223Fg47xlGsMXdfuY-4jaqVw\n    .\n    bbd5sTkYwhAIqfHsx8DayA\n    .\n    0fys_TY_na7f8dwSfXLiYdHaA2DxUjD67ieF7fcVbIR62JhJvGZ4_FNVSiGc_r\n    aa0HnLQ6s1P2sv3Xzl1p1l_o5wR_RsSzrS8Z-wnI3Jvo0mkpEEnlDmZvDu_k8O\n    WzJv7eZVEqiWKdyVzFhPpiyQU28GLOpRc2VbVbK4dQKPdNTjPPEmRqcaGeTWZV\n    yeSUvf5k59yJZxRuSvWFf6KrNtmRdZ8R4mDOjHSrM_s8uwIFcqt4r5GX8TKaI0\n    zT5CbL5Qlw3sRc7u_hg0yKVOiRytEAEs3vZkcfLkP6nbXdC_PkMdNS-ohP78T2\n    O6_7uInMGhFeX4ctHG7VelHGiT93JfWDEQi5_V9UN1rhXNrYu-0fVMkZAKX3VW\n    i7lzA6BP430m\n    .\n    kvKuFBXHe5mQr4lqgobAUg\n    ''')\n\n    static final String FIGURE_84 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"RSA\",\n      \"kid\": \"samwise.gamgee@hobbiton.example\",\n      \"use\": \"enc\",\n      \"n\": \"wbdxI55VaanZXPY29Lg5hdmv2XhvqAhoxUkanfzf2-5zVUxa6prHRr\n          I4pP1AhoqJRlZfYtWWd5mmHRG2pAHIlh0ySJ9wi0BioZBl1XP2e-C-Fy\n          XJGcTy0HdKQWlrfhTm42EW7Vv04r4gfao6uxjLGwfpGrZLarohiWCPnk\n          Nrg71S2CuNZSQBIPGjXfkmIy2tl_VWgGnL22GplyXj5YlBLdxXp3XeSt\n          sqo571utNfoUTU8E4qdzJ3U1DItoVkPGsMwlmmnJiwA7sXRItBCivR4M\n          5qnZtdw-7v4WuR4779ubDuJ5nalMv2S66-RPcnFAzWSKxtBDnFJJDGIU\n          e7Tzizjg1nms0Xq_yPub_UOlWn0ec85FCft1hACpWG8schrOBeNqHBOD\n          FskYpUc2LC5JA2TaPF2dA67dg1TTsC_FupfQ2kNGcE1LgprxKHcVWYQb\n          86B-HozjHZcqtauBzFNV5tbTuB-TpkcvJfNcFLlH3b8mb-H_ox35FjqB\n          SAjLKyoeqfKTpVjvXhd09knwgJf6VKq6UC418_TOljMVfFTWXUxlnfhO\n          OnzW6HSSzD1c9WrCuVzsUMv54szidQ9wf1cYWf3g5qFDxDQKis99gcDa\n          iCAwM3yEBIzuNeeCa5dartHDb1xEB_HcHSeYbghbMjGfasvKn0aZRsnT\n          yC0xhWBlsolZE\",\n      \"e\": \"AQAB\",\n      \"alg\": \"RSA-OAEP\",\n      \"d\": \"n7fzJc3_WG59VEOBTkayzuSMM780OJQuZjN_KbH8lOZG25ZoA7T4Bx\n          cc0xQn5oZE5uSCIwg91oCt0JvxPcpmqzaJZg1nirjcWZ-oBtVk7gCAWq\n          -B3qhfF3izlbkosrzjHajIcY33HBhsy4_WerrXg4MDNE4HYojy68TcxT\n          2LYQRxUOCf5TtJXvM8olexlSGtVnQnDRutxEUCwiewfmmrfveEogLx9E\n          A-KMgAjTiISXxqIXQhWUQX1G7v_mV_Hr2YuImYcNcHkRvp9E7ook0876\n          DhkO8v4UOZLwA1OlUX98mkoqwc58A_Y2lBYbVx1_s5lpPsEqbbH-nqIj\n          h1fL0gdNfihLxnclWtW7pCztLnImZAyeCWAG7ZIfv-Rn9fLIv9jZ6r7r\n          -MSH9sqbuziHN2grGjD_jfRluMHa0l84fFKl6bcqN1JWxPVhzNZo01yD\n          F-1LiQnqUYSepPf6X3a2SOdkqBRiquE6EvLuSYIDpJq3jDIsgoL8Mo1L\n          oomgiJxUwL_GWEOGu28gplyzm-9Q0U0nyhEf1uhSR8aJAQWAiFImWH5W\n          _IQT9I7-yrindr_2fWQ_i1UgMsGzA7aOGzZfPljRy6z-tY_KuBG00-28\n          S_aWvjyUc-Alp8AUyKjBZ-7CWH32fGWK48j1t-zomrwjL_mnhsPbGs0c\n          9WsWgRzI-K8gE\",\n      \"p\": \"7_2v3OQZzlPFcHyYfLABQ3XP85Es4hCdwCkbDeltaUXgVy9l9etKgh\n          vM4hRkOvbb01kYVuLFmxIkCDtpi-zLCYAdXKrAK3PtSbtzld_XZ9nlsY\n          a_QZWpXB_IrtFjVfdKUdMz94pHUhFGFj7nr6NNxfpiHSHWFE1zD_AC3m\n          Y46J961Y2LRnreVwAGNw53p07Db8yD_92pDa97vqcZOdgtybH9q6uma-\n          RFNhO1AoiJhYZj69hjmMRXx-x56HO9cnXNbmzNSCFCKnQmn4GQLmRj9s\n          fbZRqL94bbtE4_e0Zrpo8RNo8vxRLqQNwIy85fc6BRgBJomt8QdQvIgP\n          gWCv5HoQ\",\n      \"q\": \"zqOHk1P6WN_rHuM7ZF1cXH0x6RuOHq67WuHiSknqQeefGBA9PWs6Zy\n          KQCO-O6mKXtcgE8_Q_hA2kMRcKOcvHil1hqMCNSXlflM7WPRPZu2qCDc\n          qssd_uMbP-DqYthH_EzwL9KnYoH7JQFxxmcv5An8oXUtTwk4knKjkIYG\n          RuUwfQTus0w1NfjFAyxOOiAQ37ussIcE6C6ZSsM3n41UlbJ7TCqewzVJ\n          aPJN5cxjySPZPD3Vp01a9YgAD6a3IIaKJdIxJS1ImnfPevSJQBE79-EX\n          e2kSwVgOzvt-gsmM29QQ8veHy4uAqca5dZzMs7hkkHtw1z0jHV90epQJ\n          JlXXnH8Q\",\n      \"dp\": \"19oDkBh1AXelMIxQFm2zZTqUhAzCIr4xNIGEPNoDt1jK83_FJA-xn\n          x5kA7-1erdHdms_Ef67HsONNv5A60JaR7w8LHnDiBGnjdaUmmuO8XAxQ\n          J_ia5mxjxNjS6E2yD44USo2JmHvzeeNczq25elqbTPLhUpGo1IZuG72F\n          ZQ5gTjXoTXC2-xtCDEUZfaUNh4IeAipfLugbpe0JAFlFfrTDAMUFpC3i\n          XjxqzbEanflwPvj6V9iDSgjj8SozSM0dLtxvu0LIeIQAeEgT_yXcrKGm\n          pKdSO08kLBx8VUjkbv_3Pn20Gyu2YEuwpFlM_H1NikuxJNKFGmnAq9Lc\n          nwwT0jvoQ\",\n      \"dq\": \"S6p59KrlmzGzaQYQM3o0XfHCGvfqHLYjCO557HYQf72O9kLMCfd_1\n          VBEqeD-1jjwELKDjck8kOBl5UvohK1oDfSP1DleAy-cnmL29DqWmhgwM\n          1ip0CCNmkmsmDSlqkUXDi6sAaZuntyukyflI-qSQ3C_BafPyFaKrt1fg\n          dyEwYa08pESKwwWisy7KnmoUvaJ3SaHmohFS78TJ25cfc10wZ9hQNOrI\n          ChZlkiOdFCtxDqdmCqNacnhgE3bZQjGp3n83ODSz9zwJcSUvODlXBPc2\n          AycH6Ci5yjbxt4Ppox_5pjm6xnQkiPgj01GpsUssMmBN7iHVsrE7N2iz\n          nBNCeOUIQ\",\n      \"qi\": \"FZhClBMywVVjnuUud-05qd5CYU0dK79akAgy9oX6RX6I3IIIPckCc\n          iRrokxglZn-omAY5CnCe4KdrnjFOT5YUZE7G_Pg44XgCXaarLQf4hl80\n          oPEf6-jJ5Iy6wPRx7G2e8qLxnh9cOdf-kRqgOS3F48Ucvw3ma5V6KGMw\n          QqWFeV31XtZ8l5cVI-I3NzBS7qltpUVgz2Ju021eyc7IlqgzR98qKONl\n          27DuEES0aK0WE97jnsyO27Yp88Wa2RiBrEocM89QZI1seJiGDizHRUP4\n          UZxw9zsXww46wy0P6f9grnYp7t8LkyDDk8eoI4KX6SNMNVcyVS9IWjlq\n          8EzqZEKIA\"\n    }\n    ''')\n    static final String FIGURE_85 = 'mYMfsggkTAm0TbvtlFh2hyoXnbEzJQjMxmgLN3d8xXA'\n    static final String FIGURE_86 = '-nBoKLH0YkLZPSI9'\n    static final String FIGURE_87 = Strings.trimAllWhitespace('''\n    rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQi\n    beYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyu\n    cvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58\n    -Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8Bpx\n    KdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pK\n    IIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7\n    pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ\n    fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe3\n    8UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU\n    06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5\n    Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDR\n    s\n    ''')\n    static final String FIGURE_88 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"RSA-OAEP\",\n      \"kid\": \"samwise.gamgee@hobbiton.example\",\n      \"enc\": \"A256GCM\"\n    }''')\n    static final String FIGURE_89 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG\n    9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0\n    ''')\n    static final String FIGURE_92 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJSU0EtT0FFUCIsImtpZCI6InNhbXdpc2UuZ2FtZ2VlQGhvYmJpdG\n    9uLmV4YW1wbGUiLCJlbmMiOiJBMjU2R0NNIn0\n    .\n    rT99rwrBTbTI7IJM8fU3Eli7226HEB7IchCxNuh7lCiud48LxeolRdtFF4nzQi\n    beYOl5S_PJsAXZwSXtDePz9hk-BbtsTBqC2UsPOdwjC9NhNupNNu9uHIVftDyu\n    cvI6hvALeZ6OGnhNV4v1zx2k7O1D89mAzfw-_kT3tkuorpDU-CpBENfIHX1Q58\n    -Aad3FzMuo3Fn9buEP2yXakLXYa15BUXQsupM4A1GD4_H4Bd7V3u9h8Gkg8Bpx\n    KdUV9ScfJQTcYm6eJEBz3aSwIaK4T3-dwWpuBOhROQXBosJzS1asnuHtVMt2pK\n    IIfux5BC6huIvmY7kzV7W7aIUrpYm_3H4zYvyMeq5pGqFmW2k8zpO878TRlZx7\n    pZfPYDSXZyS0CfKKkMozT_qiCwZTSz4duYnt8hS4Z9sGthXn9uDqd6wycMagnQ\n    fOTs_lycTWmY-aqWVDKhjYNRf03NiwRtb5BE-tOdFwCASQj3uuAgPGrO2AWBe3\n    8UjQb0lvXn1SpyvYZ3WFc7WOJYaTa7A8DRn6MC6T-xDmMuxC0G7S2rscw5lQQU\n    06MvZTlFOt0UvfuKBa03cxA_nIBIhLMjY2kOTxQMmpDPTr6Cbo8aKaOnx6ASE5\n    Jx9paBpnNmOOKH35j_QlrQhDWUN6A2Gg8iFayJ69xDEdHAVCGRzN3woEI2ozDR\n    s\n    .\n    -nBoKLH0YkLZPSI9\n    .\n    o4k2cnGN8rSSw3IDo1YuySkqeS_t2m1GXklSgqBdpACm6UJuJowOHC5ytjqYgR\n    L-I-soPlwqMUf4UgRWWeaOGNw6vGW-xyM01lTYxrXfVzIIaRdhYtEMRBvBWbEw\n    P7ua1DRfvaOjgZv6Ifa3brcAM64d8p5lhhNcizPersuhw5f-pGYzseva-TUaL8\n    iWnctc-sSwy7SQmRkfhDjwbz0fz6kFovEgj64X1I5s7E6GLp5fnbYGLa1QUiML\n    7Cc2GxgvI7zqWo0YIEc7aCflLG1-8BboVWFdZKLK9vNoycrYHumwzKluLWEbSV\n    maPpOslY2n525DxDfWaVFUfKQxMF56vn4B9QMpWAbnypNimbM8zVOw\n    .\n    UCGiqJxhBI3IFVdPalHHvA\n    ''')\n\n    static final String FIGURE_95 = Strings.trimAllWhitespace('''\n    {\n      \"keys\": [\n        {\n          \"kty\": \"oct\",\n          \"kid\": \"77c7e2b8-6e13-45cf-8672-617b5b45243a\",\n          \"use\": \"enc\",\n          \"alg\": \"A128GCM\",\n          \"k\": \"XctOhJAkA-pD9Lh7ZgW_2A\"\n        },\n        {\n          \"kty\": \"oct\",\n          \"kid\": \"81b20965-8332-43d9-a468-82160ad91ac8\",\n          \"use\": \"enc\",\n          \"alg\": \"A128KW\",\n          \"k\": \"GZy6sIZ6wl9NJOKB-jnmVQ\"\n        },\n        {\n          \"kty\": \"oct\",\n          \"kid\": \"18ec08e1-bfa9-4d95-b205-2b4dd1d4321d\",\n          \"use\": \"enc\",\n          \"alg\": \"A256GCMKW\",\n          \"k\": \"qC57l_uxcm7Nm3K-ct4GFjx8tM1U8CZ0NLBvdQstiS8\"\n        }\n      ]\n    }\n    ''')\n    static final String FIGURE_96 = 'entrap_o\\u2013peter_long\\u2013credit_tun'\n    static final String FIGURE_97 = 'uwsjJXaBK407Qaf0_zpcpmr1Cs0CC50hIUEyGNEt3m0'\n    static final String FIGURE_98 = 'VBiCzVHNoLiR3F4V82uoTQ'\n    static final String FIGURE_99 = '8Q1SzinasR3xchYz6ZZcHA'\n    static final String FIGURE_101 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"PBES2-HS512+A256KW\",\n      \"p2s\": \"8Q1SzinasR3xchYz6ZZcHA\",\n      \"p2c\": 8192,\n      \"cty\": \"jwk-set+json\",\n      \"enc\": \"A128CBC-HS256\"\n    }\n    ''')\n    static final String FIGURE_102 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3\n    hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl\n    bmMiOiJBMTI4Q0JDLUhTMjU2In0\n    ''')\n    static final String FIGURE_105 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJQQkVTMi1IUzUxMitBMjU2S1ciLCJwMnMiOiI4UTFTemluYXNSM3\n    hjaFl6NlpaY0hBIiwicDJjIjo4MTkyLCJjdHkiOiJqd2stc2V0K2pzb24iLCJl\n    bmMiOiJBMTI4Q0JDLUhTMjU2In0\n    .\n    d3qNhUWfqheyPp4H8sjOWsDYajoej4c5Je6rlUtFPWdgtURtmeDV1g\n    .\n    VBiCzVHNoLiR3F4V82uoTQ\n    .\n    23i-Tb1AV4n0WKVSSgcQrdg6GRqsUKxjruHXYsTHAJLZ2nsnGIX86vMXqIi6IR\n    sfywCRFzLxEcZBRnTvG3nhzPk0GDD7FMyXhUHpDjEYCNA_XOmzg8yZR9oyjo6l\n    TF6si4q9FZ2EhzgFQCLO_6h5EVg3vR75_hkBsnuoqoM3dwejXBtIodN84PeqMb\n    6asmas_dpSsz7H10fC5ni9xIz424givB1YLldF6exVmL93R3fOoOJbmk2GBQZL\n    _SEGllv2cQsBgeprARsaQ7Bq99tT80coH8ItBjgV08AtzXFFsx9qKvC982KLKd\n    PQMTlVJKkqtV4Ru5LEVpBZXBnZrtViSOgyg6AiuwaS-rCrcD_ePOGSuxvgtrok\n    AKYPqmXUeRdjFJwafkYEkiuDCV9vWGAi1DH2xTafhJwcmywIyzi4BqRpmdn_N-\n    zl5tuJYyuvKhjKv6ihbsV_k1hJGPGAxJ6wUpmwC4PTQ2izEm0TuSE8oMKdTw8V\n    3kobXZ77ulMwDs4p\n    .\n    0HlwodAhOCILG5SQ2LQ9dg\n    ''')\n\n    static final String FIGURE_108 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"EC\",\n      \"kid\": \"peregrin.took@tuckborough.example\",\n      \"use\": \"enc\",\n      \"crv\": \"P-384\",\n      \"x\": \"YU4rRUzdmVqmRtWOs2OpDE_T5fsNIodcG8G5FWPrTPMyxpzsSOGaQL\n          pe2FpxBmu2\",\n      \"y\": \"A8-yxCHxkfBz3hKZfI1jUYMjUhsEveZ9THuwFjH2sCNdtksRJU7D5-\n          SkgaFL1ETP\",\n      \"d\": \"iTx2pk7wW-GqJkHcEkFQb2EFyYcO7RugmaW3mRrQVAOUiPommT0Idn\n          YK2xDlZh-j\"\n    }\n    ''')\n    static final String FIGURE_109 = 'Nou2ueKlP70ZXDbq9UrRwg'\n    static final String FIGURE_110 = 'mH-G2zVqgztUtnW_'\n    static final String FIGURE_111 = Strings.trimAllWhitespace('''\n    {\n      \"kty\": \"EC\",\n      \"crv\": \"P-384\",\n      \"x\": \"uBo4kHPw6kbjx5l0xowrd_oYzBmaz-GKFZu4xAFFkbYiWgutEK6iuE\n          DsQ6wNdNg3\",\n      \"y\": \"sp3p5SGhZVC2faXumI-e9JU2Mo8KpoYrFDr5yPNVtW4PgEwZOyQTA-\n          JdaY8tb7E0\",\n      \"d\": \"D5H4Y_5PSKZvhfVFbcCYJOtcGZygRgfZkpsBr59Icmmhe9sW6nkZ8W\n          fwhinUfWJg\"\n    }\n    ''')\n    public static final String FIGURE_113 = Strings.trimAllWhitespace('''\n    {\n      \"alg\": \"ECDH-ES+A128KW\",\n      \"kid\": \"peregrin.took@tuckborough.example\",\n      \"epk\": {\n        \"kty\": \"EC\",\n        \"crv\": \"P-384\",\n        \"x\": \"uBo4kHPw6kbjx5l0xowrd_oYzBmaz-GKFZu4xAFFkbYiWgutEK6i\n            uEDsQ6wNdNg3\",\n        \"y\": \"sp3p5SGhZVC2faXumI-e9JU2Mo8KpoYrFDr5yPNVtW4PgEwZOyQT\n            A-JdaY8tb7E0\"\n      },\n      \"enc\": \"A128GCM\"\n    }\n    ''')\n    static final String FIGURE_114 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdH\n    Vja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAt\n    Mzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NH\n    hBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMy\n    ZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWT\n    h0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0\n    ''')\n    static final String FIGURE_117 = Strings.trimAllWhitespace('''\n    eyJhbGciOiJFQ0RILUVTK0ExMjhLVyIsImtpZCI6InBlcmVncmluLnRvb2tAdH\n    Vja2Jvcm91Z2guZXhhbXBsZSIsImVwayI6eyJrdHkiOiJFQyIsImNydiI6IlAt\n    Mzg0IiwieCI6InVCbzRrSFB3Nmtiang1bDB4b3dyZF9vWXpCbWF6LUdLRlp1NH\n    hBRkZrYllpV2d1dEVLNml1RURzUTZ3TmROZzMiLCJ5Ijoic3AzcDVTR2haVkMy\n    ZmFYdW1JLWU5SlUyTW84S3BvWXJGRHI1eVBOVnRXNFBnRXdaT3lRVEEtSmRhWT\n    h0YjdFMCJ9LCJlbmMiOiJBMTI4R0NNIn0\n    .\n    0DJjBXri_kBcC46IkU5_Jk9BqaQeHdv2\n    .\n    mH-G2zVqgztUtnW_\n    .\n    tkZuOO9h95OgHJmkkrfLBisku8rGf6nzVxhRM3sVOhXgz5NJ76oID7lpnAi_cP\n    WJRCjSpAaUZ5dOR3Spy7QuEkmKx8-3RCMhSYMzsXaEwDdXta9Mn5B7cCBoJKB0\n    IgEnj_qfo1hIi-uEkUpOZ8aLTZGHfpl05jMwbKkTe2yK3mjF6SBAsgicQDVCkc\n    Y9BLluzx1RmC3ORXaM0JaHPB93YcdSDGgpgBWMVrNU1ErkjcMqMoT_wtCex3w0\n    3XdLkjXIuEr2hWgeP-nkUZTPU9EoGSPj6fAS-bSz87RCPrxZdj_iVyC6QWcqAu\n    07WNhjzJEPc4jVntRJ6K53NgPQ5p99l3Z408OUqj4ioYezbS6vTPlQ\n    .\n    WuGzxmcreYjpHGJoa17EBg\n    ''')\n\n    static {\n        //ensure our representations match the RFC:\n        assert FIGURE_77.equals(utf8(b64Url(FIGURE_78)))\n        assert FIGURE_88.equals(utf8(b64Url(FIGURE_89)))\n        assert FIGURE_101.equals(utf8(b64Url(FIGURE_102)))\n        assert FIGURE_113.equals(utf8(b64Url(FIGURE_114)))\n    }\n\n    // https://www.rfc-editor.org/rfc/rfc7520.html#section-5.1\n    @Test\n    void testSection5_1() {\n\n        RsaPrivateJwk jwk = Jwks.parser().build().parse(FIGURE_73) as RsaPrivateJwk\n        RSAPublicKey key = jwk.toPublicJwk().toKey()\n\n        def alg = new DefaultRsaKeyAlgorithm(StandardKeyAlgorithms.RSA1_5_ID, StandardKeyAlgorithms.RSA1_5_TRANSFORMATION) {\n            @Override\n            SecretKey generateCek(KeyRequest<?> request) {\n                byte[] encoded = b64Url(FIGURE_74) // ensure RFC required value\n                return new SecretKeySpec(encoded, \"AES\")\n            }\n\n            @Override\n            protected JcaTemplate jca(Request<?> request) {\n                return new JcaTemplate(getJcaName()) {\n                    // overrides parent, Groovy doesn't pick it up due to generics signature:\n                    @SuppressWarnings('unused')\n                    byte[] withCipher(CheckedFunction<Cipher, byte[]> fn) throws SecurityException {\n                        return b64Url(FIGURE_76)\n                    }\n                }\n            }\n        }\n\n        def enc = new HmacAesAeadAlgorithm(128) {\n            @Override\n            protected byte[] ensureInitializationVector(Request request) {\n                return b64Url(FIGURE_75) // ensure RFC required value\n            }\n        }\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // Serializer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def ser = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 3, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals jwk.getId(), m.get('kid')\n                assertEquals enc.getId(), m.get('enc')\n                return FIGURE_77\n            }\n        }\n        String result = Jwts.builder()\n                .json(ser) // assert input, return RFC ordered string\n                .header().keyId(jwk.getId()).and()\n                .setPayload(FIGURE_72)\n                .encryptWith(key, alg, enc)\n                .compact()\n\n        assertEquals FIGURE_81, result\n\n        // Assert round trip works as expected:\n        def parsed = Jwts.parser().decryptWith(jwk.toKey()).build().parseEncryptedContent(result)\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals jwk.getId(), parsed.header.getKeyId()\n        assertEquals enc.getId(), parsed.header.getEncryptionAlgorithm()\n        assertEquals FIGURE_72, utf8(parsed.payload)\n    }\n\n    @Test\n    void testSection5_2() {\n\n        RsaPrivateJwk jwk = Jwks.parser().build().parse(FIGURE_84) as RsaPrivateJwk\n        RSAPublicKey key = jwk.toPublicJwk().toKey()\n\n        def alg = new DefaultRsaKeyAlgorithm(StandardKeyAlgorithms.RSA_OAEP_ID, StandardKeyAlgorithms.RSA_OAEP_TRANSFORMATION) {\n            @Override\n            SecretKey generateCek(KeyRequest<?> request) {\n                byte[] encoded = b64Url(FIGURE_85) // ensure RFC required value\n                return new SecretKeySpec(encoded, \"AES\")\n            }\n\n            @Override\n            protected JcaTemplate jca(Request<?> request) {\n                return new JcaTemplate(getJcaName()) {\n                    // overrides parent, Groovy doesn't pick it up due to generics signature:\n                    @SuppressWarnings('unused')\n                    byte[] withCipher(CheckedFunction<Cipher, byte[]> fn) throws SecurityException {\n                        return b64Url(FIGURE_87)\n                    }\n                }\n            }\n        }\n\n        def enc = new GcmAesAeadAlgorithm(256) {\n            @Override\n            protected byte[] ensureInitializationVector(Request request) {\n                return b64Url(FIGURE_86) // ensure RFC required value\n            }\n        }\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // writer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def ser = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 3, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals jwk.getId(), m.get('kid')\n                assertEquals enc.getId(), m.get('enc')\n                return FIGURE_88\n            }\n        }\n\n        String result = Jwts.builder()\n                .json(ser) // assert input, return RFC ordered string\n                .header().keyId(jwk.getId()).and()\n                .setPayload(FIGURE_72)\n                .encryptWith(key, alg, enc)\n                .compact()\n\n        assertEquals FIGURE_92, result\n\n        // Assert round trip works as expected:\n        def parsed = Jwts.parser().decryptWith(jwk.toKey()).build().parseEncryptedContent(result)\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals jwk.getId(), parsed.header.getKeyId()\n        assertEquals enc.getId(), parsed.header.getEncryptionAlgorithm()\n        assertEquals FIGURE_72, utf8(parsed.payload)\n    }\n\n    @Test\n    void testSection5_3() {\n\n        def key = Keys.password(FIGURE_96.toCharArray())\n        String cty = 'jwk-set+json'\n        int p2c = 8192\n\n        def wrapAlg = new AesWrapKeyAlgorithm(256) {\n            @Override\n            SecretKey generateCek(KeyRequest<?> request) {\n                byte[] encoded = b64Url(FIGURE_97) // ensure RFC value\n                return new SecretKeySpec(encoded, \"AES\")\n            }\n        }\n        def alg = new Pbes2HsAkwAlgorithm(512, wrapAlg) {\n            @Override\n            protected byte[] generateInputSalt(KeyRequest<?> request) {\n                return b64Url(FIGURE_99) // ensure RFC value\n            }\n        }\n        def enc = new HmacAesAeadAlgorithm(128) {\n            @Override\n            protected byte[] ensureInitializationVector(Request request) {\n                return b64Url(FIGURE_98) // ensure RFC value\n            }\n        }\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // writer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def ser = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 5, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals FIGURE_99, m.get('p2s')\n                assertEquals p2c, m.get('p2c')\n                assertEquals cty, m.get('cty')\n                assertEquals enc.getId(), m.get('enc')\n                return FIGURE_101\n            }\n        }\n\n        String result = Jwts.builder()\n                .json(ser) // assert input, return RFC ordered string\n                .header().contentType(cty).pbes2Count(p2c).and()\n                .setPayload(FIGURE_95)\n                .encryptWith(key, alg, enc)\n                .compact()\n\n        assertEquals FIGURE_105, result\n\n        // Assert round trip works as expected:\n        def parsed = Jwts.parser().decryptWith(key).build().parseEncryptedContent(result)\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals FIGURE_99, b64Url(parsed.header.getPbes2Salt())\n        assertEquals p2c, parsed.header.getPbes2Count()\n\n        assertEquals cty, parsed.header.get('cty') // compact form\n        assertEquals \"application/$cty\" as String, parsed.header.getContentType() // normalized form\n\n        assertEquals enc.getId(), parsed.header.getEncryptionAlgorithm()\n        assertEquals FIGURE_95, utf8(parsed.payload)\n    }\n\n    @Test\n    void testSection5_4() {\n\n        def jwk = Jwks.parser().build().parse(FIGURE_108) as EcPrivateJwk\n        def encKey = jwk.toPublicJwk().toKey()\n\n        def wrapAlg = new AesWrapKeyAlgorithm(128) {\n            @Override\n            SecretKey generateCek(KeyRequest request) {\n                byte[] encoded = b64Url(FIGURE_109) // ensure RFC value\n                return new SecretKeySpec(encoded, \"AES\")\n            }\n        }\n        def RFC_EPK = Jwks.parser().build().parse(FIGURE_111) as EcPrivateJwk\n        def alg = new EcdhKeyAlgorithm(wrapAlg) {\n            @Override\n            protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {\n                return new KeyPair(RFC_EPK.toPublicJwk().toKey(), RFC_EPK.toKey()) // ensure RFC value\n            }\n        }\n        def enc = new GcmAesAeadAlgorithm(128) {\n            @Override\n            protected byte[] ensureInitializationVector(Request request) {\n                return b64Url(FIGURE_110)\n            }\n        }\n\n        // because Maps are not guaranteed to have the same order as defined in the RFC, we create an asserting\n        // writer here to check the constructed data, and then, after guaranteeing the same data, return\n        // the order expected by the RFC\n        def ser = new TestSerializer() {\n            @Override\n            protected String toJson(Map<String, ?> m) {\n                assertEquals 4, m.size()\n                assertEquals alg.getId(), m.get('alg')\n                assertEquals jwk.getId(), m.get('kid')\n                assertEquals enc.getId(), m.get('enc')\n                assertEquals RFC_EPK.toPublicJwk(), m.get('epk')\n                return FIGURE_113\n            }\n        }\n\n        String result = Jwts.builder()\n                .json(ser) // assert input, return RFC ordered string\n                .header().keyId(jwk.getId()).and()\n                .setPayload(FIGURE_72)\n                .encryptWith(encKey, alg, enc)\n                .compact()\n\n        assertEquals FIGURE_117, result\n\n        // Assert round trip works as expected:\n        def parsed = Jwts.parser().decryptWith(jwk.toKey()).build().parseEncryptedContent(result)\n        assertEquals alg.getId(), parsed.header.getAlgorithm()\n        assertEquals enc.getId(), parsed.header.getEncryptionAlgorithm()\n        assertEquals jwk.getId(), parsed.header.getKeyId()\n        assertEquals RFC_EPK.toPublicJwk(), parsed.header.getEphemeralPublicKey()\n        assertEquals FIGURE_72, utf8(parsed.payload)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC7638Section3Dot1Test.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.RfcTests\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\n\nimport static org.junit.Assert.assertArrayEquals\nimport static org.junit.Assert.assertEquals\n\nclass RFC7638Section3Dot1Test extends RfcTests {\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7638#section-3.1\n    static final String KEY_JSON = stripws('''\n    {\n      \"kty\": \"RSA\",\n      \"n\": \"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAt\n            VT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn6\n            4tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FD\n            W2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n9\n            1CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINH\n            aQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\",\n      \"e\": \"AQAB\",\n      \"alg\": \"RS256\",\n      \"kid\": \"2011-04-29\"\n     }\n''')\n\n    static final String KEY_THUMBPRINT_JSON = stripws('''\n     {\"e\":\"AQAB\",\"kty\":\"RSA\",\"n\":\"0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2\n     aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCi\n     FV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65Y\n     GjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n\n     91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_x\n     BniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw\"}\n     ''')\n\n    static final byte[] KEY_THUMBPRINT_JSON_UTF8_BYTES =\n            [123, 34, 101, 34, 58, 34, 65, 81, 65, 66, 34, 44, 34, 107, 116, 121,\n             34, 58, 34, 82, 83, 65, 34, 44, 34, 110, 34, 58, 34, 48, 118, 120,\n             55, 97, 103, 111, 101, 98, 71, 99, 81, 83, 117, 117, 80, 105, 76, 74,\n             88, 90, 112, 116, 78, 57, 110, 110, 100, 114, 81, 109, 98, 88, 69,\n             112, 115, 50, 97, 105, 65, 70, 98, 87, 104, 77, 55, 56, 76, 104, 87,\n             120, 52, 99, 98, 98, 102, 65, 65, 116, 86, 84, 56, 54, 122, 119, 117,\n             49, 82, 75, 55, 97, 80, 70, 70, 120, 117, 104, 68, 82, 49, 76, 54,\n             116, 83, 111, 99, 95, 66, 74, 69, 67, 80, 101, 98, 87, 75, 82, 88,\n             106, 66, 90, 67, 105, 70, 86, 52, 110, 51, 111, 107, 110, 106, 104,\n             77, 115, 116, 110, 54, 52, 116, 90, 95, 50, 87, 45, 53, 74, 115, 71,\n             89, 52, 72, 99, 53, 110, 57, 121, 66, 88, 65, 114, 119, 108, 57, 51,\n             108, 113, 116, 55, 95, 82, 78, 53, 119, 54, 67, 102, 48, 104, 52, 81,\n             121, 81, 53, 118, 45, 54, 53, 89, 71, 106, 81, 82, 48, 95, 70, 68,\n             87, 50, 81, 118, 122, 113, 89, 51, 54, 56, 81, 81, 77, 105, 99, 65,\n             116, 97, 83, 113, 122, 115, 56, 75, 74, 90, 103, 110, 89, 98, 57, 99,\n             55, 100, 48, 122, 103, 100, 65, 90, 72, 122, 117, 54, 113, 77, 81,\n             118, 82, 76, 53, 104, 97, 106, 114, 110, 49, 110, 57, 49, 67, 98, 79,\n             112, 98, 73, 83, 68, 48, 56, 113, 78, 76, 121, 114, 100, 107, 116,\n             45, 98, 70, 84, 87, 104, 65, 73, 52, 118, 77, 81, 70, 104, 54, 87,\n             101, 90, 117, 48, 102, 77, 52, 108, 70, 100, 50, 78, 99, 82, 119,\n             114, 51, 88, 80, 107, 115, 73, 78, 72, 97, 81, 45, 71, 95, 120, 66,\n             110, 105, 73, 113, 98, 119, 48, 76, 115, 49, 106, 70, 52, 52, 45, 99,\n             115, 70, 67, 117, 114, 45, 107, 69, 103, 85, 56, 97, 119, 97, 112,\n             74, 122, 75, 110, 113, 68, 75, 103, 119, 34, 125] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7638#section-3.1\n    static final byte[] KEY_S256_DIGEST =\n            [55, 54, 203, 177, 120, 124, 184, 48, 156, 119, 238, 140, 55, 5, 197,\n             225, 111, 251, 158, 133, 151, 21, 144, 31, 30, 76, 89, 177, 17, 130,\n             245, 123] as byte[]\n\n    // defined in https://www.rfc-editor.org/rfc/rfc7638#section-3.1\n    static final String KEY_S256_DIGEST_B64URL = 'NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs' as String\n\n    /**\n     * Asserts we produce the expected output as shown in\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7638#section-3.1\">RFC 7638, Section 3.1</a>.\n     */\n    @Test\n    void testRFC7638Section3_1() {\n        //assert our test values are correct:\n        assertEquals(KEY_S256_DIGEST_B64URL, encode(KEY_S256_DIGEST))\n        assertArrayEquals(KEY_THUMBPRINT_JSON_UTF8_BYTES, KEY_THUMBPRINT_JSON.getBytes(StandardCharsets.UTF_8))\n\n        def jwk = Jwks.parser().build().parse(KEY_JSON)\n\n        def thumbprint = jwk.thumbprint()\n\n        assertArrayEquals(KEY_S256_DIGEST, thumbprint.toByteArray())\n        assertEquals(KEY_S256_DIGEST_B64URL, thumbprint.toString())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RFC8037AppendixATest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.RfcTests\nimport io.jsonwebtoken.security.Curve\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.OctetPrivateJwk\nimport io.jsonwebtoken.security.OctetPublicJwk\nimport org.junit.Test\n\nimport java.nio.charset.StandardCharsets\nimport java.security.*\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.assertTrue\n\nclass RFC8037AppendixATest {\n\n    // https://www.rfc-editor.org/rfc/rfc8037#appendix-A.1 :\n    static final String A1_ED25519_PRIVATE_JWK_STRING = RfcTests.stripws('''\n    {\n      \"kty\":\"OKP\",\n      \"crv\":\"Ed25519\",\n      \"d\":\"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A\",\n      \"x\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\"\n    }''')\n\n    // https://www.rfc-editor.org/rfc/rfc8037#appendix-A.2\n    static final String A2_ED25519_PUBLIC_JWK_STRING = RfcTests.stripws('''\n    {\n      \"kty\":\"OKP\",\"crv\":\"Ed25519\",\n      \"x\":\"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo\"\n    }''')\n\n    // https://www.rfc-editor.org/rfc/rfc8037#appendix-A.3\n    static final A3_JWK_THUMBPRINT_B64URL = 'kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k'\n    static final A3_JWK_THUMMBPRINT_HEX = '90facafea9b1556698540f70c0117a22ea37bd5cf3ed3c47093c1707282b4b89'\n\n    static final A4_JWS_PAYLOAD = 'Example of Ed25519 signing'\n\n    static final String A4_JWS_COMPACT = RfcTests.stripws('''\n    eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc.hgyY0il_MGCj\n    P0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_Mu\n    M0KAg''')\n\n    static OctetPrivateJwk a1Jwk() {\n        Jwks.parser().build().parse(A1_ED25519_PRIVATE_JWK_STRING) as OctetPrivateJwk\n    }\n\n    static OctetPublicJwk a2Jwk() {\n        Jwks.parser().build().parse(A2_ED25519_PUBLIC_JWK_STRING) as OctetPublicJwk\n    }\n\n\n    @Test\n    void testSections_A1_A2_and_A3() {\n        def privJwk = a1Jwk()\n        assertTrue privJwk instanceof OctetPrivateJwk\n        PrivateKey privKey = privJwk.toKey() as PrivateKey\n        PublicKey pubKey = privJwk.toPublicJwk().toKey() as PublicKey\n\n        def builtPrivJwk = Jwks.builder().key(privKey).publicKey(pubKey).build()\n\n        //output should equal RFC input:\n        assertEquals privJwk, builtPrivJwk\n\n        // Our built public JWK must reflect the RFC public JWK string value:\n        def a2PubJwk = a2Jwk()\n        assertTrue a2PubJwk instanceof OctetPublicJwk\n        PublicKey a2PubJwkKey = a2PubJwk.toKey() as PublicKey\n\n        assertEquals a2PubJwk, privJwk.toPublicJwk()\n        assertEquals a2PubJwkKey, pubKey\n\n        // Assert Section A.3 values:\n        def privThumbprint = privJwk.thumbprint()\n        def pubThumbprint = a2PubJwk.thumbprint()\n        assertEquals(privThumbprint, pubThumbprint)\n\n        assertEquals A3_JWK_THUMBPRINT_B64URL, privThumbprint.toString()\n        assertEquals A3_JWK_THUMBPRINT_B64URL, pubThumbprint.toString()\n\n        assertEquals A3_JWK_THUMMBPRINT_HEX, privThumbprint.toByteArray().encodeHex().toString()\n        assertEquals A3_JWK_THUMMBPRINT_HEX, pubThumbprint.toByteArray().encodeHex().toString()\n    }\n\n    @Test\n    void test_Sections_A4_and_A5() {\n        def privJwk = a1Jwk()\n        String compact = Jwts.builder()\n                .content(A4_JWS_PAYLOAD.getBytes(StandardCharsets.UTF_8))\n                .signWith(privJwk.toKey() as PrivateKey, Jwts.SIG.EdDSA)\n                .compact()\n        assertEquals A4_JWS_COMPACT, compact\n\n        def pubJwk = a2Jwk()\n        def payloadBytes = Jwts.parser().verifyWith(pubJwk.toKey()).build().parse(compact).getPayload() as byte[]\n        def payload = new String(payloadBytes, StandardCharsets.UTF_8)\n        assertEquals A4_JWS_PAYLOAD, payload\n    }\n\n\n    /**\n     * https://www.rfc-editor.org/rfc/rfc8037#appendix-A indicates the public/private key pairs used for test\n     * vectors for sections A6 and A7 are defined in <a href=\"https://www.rfc-editor.org/rfc/rfc7748\">RFC 7748</a>.\n     * Diffie-Hellman curve 25519 (X25519) test vectors are in\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7748#section-6.1\">RFC 7748, Section 6.1</a> specifically.\n     */\n    @Test\n    void testSectionA6() { // defined in https://www.rfc-editor.org/rfc/rfc8037#appendix-A.6\n\n        // These two values are defined in https://www.rfc-editor.org/rfc/rfc7748#section-6.1:\n        def bobPubKeyHex = 'de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f'\n        def bobPrivKeyHex = '5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb'\n\n        //convert these two values to a JWK for convenient reference:\n        def bobPrivJwk = Jwks.builder().add([\n                kty: \"OKP\", crv: \"X25519\", kid: \"Bob\",\n                x  : bobPubKeyHex.decodeHex().encodeBase64Url() as String,\n                d  : bobPrivKeyHex.decodeHex().encodeBase64Url() as String\n        ]).build() as OctetPrivateJwk\n\n        // RFC-specified test vectors to be used during DH calculation:\n        def rfcEphemeralSecretHex = RfcTests.stripws('''\n        77 07 6d 0a 73 18 a5 7d 3c 16 c1 72 51 b2 66 45\n        df 4c 2f 87 eb c0 99 2a b1 77 fb a5 1d b9 2c 2a''')\n\n        def rfcEphemeralPubKeyHex = RfcTests.stripws('''\n        85 20 f0 09 89 30 a7 54 74 8b 7d dc b4 3e f7 5a\n        0d bf 3a 0d 26 38 1a f4 eb a4 a9 8e aa 9b 4e 6a''')\n\n        //Turn these two values into a Java KeyPair, and ensure it is used during key algorithm execution:\n        final OctetPrivateJwk ephemJwk = Jwks.builder().add([\n                kty: \"OKP\",\n                crv: \"X25519\",\n                x  : rfcEphemeralPubKeyHex.decodeHex().encodeBase64Url() as String,\n                d  : rfcEphemeralSecretHex.decodeHex().encodeBase64Url() as String\n        ]).build() as OctetPrivateJwk\n\n        // ensure this is used during key algorithm execution per the RFC test case:\n        def alg = new EcdhKeyAlgorithm(Jwts.KEY.A128KW) {\n            @Override\n            protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {\n                return ephemJwk.toKeyPair().toJavaKeyPair()\n            }\n        }\n\n        // the RFC test vectors don't specify a JWE body/content, so we'll just add a random issuer claim and verify\n        // that on decryption:\n        final String issuer = RfcTests.srandom()\n\n        // Create the test case JWE with the 'kid' header to ensure the output matches the RFC expected value:\n        String jwe = Jwts.builder()\n                .header().keyId(bobPrivJwk.getId()).and()\n                .setIssuer(issuer)\n                .encryptWith(bobPrivJwk.toPublicJwk().toKey() as PublicKey, alg, Jwts.ENC.A128GCM)\n                .compact()\n\n        // the constructed JWE should have the following protected header:\n        String rfcExpectedProtectedHeaderJson = RfcTests.stripws('''\n        {\n          \"alg\": \"ECDH-ES+A128KW\",\n          \"epk\": {\n            \"kty\": \"OKP\",\n            \"crv\": \"X25519\",\n            \"x\": \"hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo\"\n          },\n          \"enc\": \"A128GCM\",\n          \"kid\": \"Bob\"\n        }''')\n\n        String jweHeaderJson = new String(jwe.substring(0, jwe.indexOf('.')).decodeBase64Url(), StandardCharsets.UTF_8)\n\n        // since JSON key/value ordering in JSON strings is not guaranteed, we change them to Maps and do equality\n        // assertions that way:\n        def rfcExpectedHeaderMap = RfcTests.jsonToMap(rfcExpectedProtectedHeaderJson)\n        def jweHeaderMap = RfcTests.jsonToMap(jweHeaderJson)\n        assertEquals(rfcExpectedHeaderMap, jweHeaderMap)\n        assertEquals(rfcExpectedHeaderMap.get('epk'), jweHeaderMap.get('epk'))\n\n        //ensure that bob can decrypt:\n        def jwt = Jwts.parser().decryptWith(bobPrivJwk.toKey() as PrivateKey).build().parseEncryptedClaims(jwe)\n\n        assertEquals(issuer, jwt.getPayload().getIssuer())\n    }\n\n    /**\n     * https://www.rfc-editor.org/rfc/rfc8037#appendix-A indicates the public/private key pairs used for test\n     * vectors for sections A6 and A7 are defined in <a href=\"https://www.rfc-editor.org/rfc/rfc7748\">RFC 7748</a>.\n     * For Diffie-Hellman curve 448 (X448) test vectors are in\n     * <a href=\"https://www.rfc-editor.org/rfc/rfc7748#section-6.2\">RFC 7748, Section 6.2</a> specifically.\n     */\n    @Test\n    void testSectionA7() { // defined in https://www.rfc-editor.org/rfc/rfc8037#appendix-A.7\n\n        // These two values are defined in https://www.rfc-editor.org/rfc/rfc7748#section-6.2\n        // (Appendex A.7 oddly refers to this key holder as \"Dave\" when their own referenced RFC test vectors\n        // (RFC 7748, Section 6.2) calls this holder \"Bob\".  We'll keep the 'bob' variable name references, but change\n        // the 'kid' value to \"Dave\" to match Section A.7 header values:\n        def bobPubKeyHex = '3eb7a829b0cd20f5bcfc0b599b6feccf6da4627107bdb0d4f345b43027d8b972fc3e34fb4232a13ca706dcb57aec3dae07bdc1c67bf33609'\n        def bobPrivKeyHex = '1c306a7ac2a0e2e0990b294470cba339e6453772b075811d8fad0d1d6927c120bb5ee8972b0d3e21374c9c921b09d1b0366f10b65173992d'\n\n        //convert these two values to a JWK for convenient reference:\n        def bobPrivJwk = Jwks.builder().add([\n                kty: \"OKP\", crv: \"X448\", kid: \"Dave\", // \"Dave\" instead of expected \"Bob\"\n                x  : bobPubKeyHex.decodeHex().encodeBase64Url() as String,\n                d  : bobPrivKeyHex.decodeHex().encodeBase64Url() as String\n        ]).build() as OctetPrivateJwk\n\n        // RFC-specified test vectors to be used during DH calculation:\n        def rfcEphemeralSecretHex = RfcTests.stripws('''\n        9a 8f 49 25 d1 51 9f 57 75 cf 46 b0 4b 58 00 d4\n        ee 9e e8 ba e8 bc 55 65 d4 98 c2 8d d9 c9 ba f5\n        74 a9 41 97 44 89 73 91 00 63 82 a6 f1 27 ab 1d\n        9a c2 d8 c0 a5 98 72 6b''')\n\n        def rfcEphemeralPubKeyHex = RfcTests.stripws('''\n        9b 08 f7 cc 31 b7 e3 e6 7d 22 d5 ae a1 21 07 4a\n        27 3b d2 b8 3d e0 9c 63 fa a7 3d 2c 22 c5 d9 bb\n        c8 36 64 72 41 d9 53 d4 0c 5b 12 da 88 12 0d 53\n        17 7f 80 e5 32 c4 1f a0''')\n\n        //Turn these two values into a Java KeyPair, and ensure it is used during key algorithm execution:\n        final OctetPrivateJwk ephemJwk = Jwks.builder().add([\n                kty: \"OKP\",\n                crv: \"X448\",\n                x  : rfcEphemeralPubKeyHex.decodeHex().encodeBase64Url() as String,\n                d  : rfcEphemeralSecretHex.decodeHex().encodeBase64Url() as String\n        ]).build() as OctetPrivateJwk\n\n        // ensure this is used during key algorithm execution per the RFC test case:\n        def alg = new EcdhKeyAlgorithm(Jwts.KEY.A256KW) {\n            @Override\n            protected KeyPair generateKeyPair(Curve curve, Provider provider, SecureRandom random) {\n                return ephemJwk.toKeyPair().toJavaKeyPair()\n            }\n        }\n\n        // the RFC test vectors don't specify a JWE body/content, so we'll just add a random issuer claim and verify\n        // that on decryption:\n        final String issuer = RfcTests.srandom()\n\n        // Create the test case JWE with the 'kid' header to ensure the output matches the RFC expected value:\n        String jwe = Jwts.builder()\n                .header().keyId(bobPrivJwk.getId()).and() //value will be \"Dave\" as noted above\n                .issuer(issuer)\n                .encryptWith(bobPrivJwk.toPublicJwk().toKey() as PublicKey, alg, Jwts.ENC.A256GCM)\n                .compact()\n\n        // the constructed JWE should have the following protected header:\n        String rfcExpectedProtectedHeaderJson = RfcTests.stripws('''\n        {\n          \"alg\": \"ECDH-ES+A256KW\",\n          \"epk\": {\n            \"kty\": \"OKP\",\n            \"crv\": \"X448\",\n            \"x\": \"mwj3zDG34-Z9ItWuoSEHSic70rg94Jxj-qc9LCLF2bvINmRyQdlT1AxbEtqIEg1TF3-A5TLEH6A\"\n          },\n          \"enc\": \"A256GCM\", \n          \"kid\":\"Dave\"\n        }''')\n\n        String jweHeaderJson = new String(jwe.substring(0, jwe.indexOf('.')).decodeBase64Url(), StandardCharsets.UTF_8)\n\n        // since JSON key/value ordering in JSON strings is not guaranteed, we change them to Maps and do equality\n        // assertions that way:\n        def rfcExpectedHeaderMap = RfcTests.jsonToMap(rfcExpectedProtectedHeaderJson)\n        def jweHeaderMap = RfcTests.jsonToMap(jweHeaderJson)\n        assertEquals(rfcExpectedHeaderMap, jweHeaderMap)\n        assertEquals(rfcExpectedHeaderMap.get('epk'), jweHeaderMap.get('epk'))\n\n        //ensure that Bob (\"Dave\") can decrypt:\n        def jwt = Jwts.parser().decryptWith(bobPrivJwk.toKey() as PrivateKey).build().parseEncryptedClaims(jwe)\n\n        //assert that we've decrypted and the value in the body/content is as expected:\n        assertEquals(issuer, jwt.getPayload().getIssuer())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RSAOtherPrimeInfoConverterTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.MalformedKeyException\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass RSAOtherPrimeInfoConverterTest {\n\n    @Test\n    void testApplyFromNull() {\n        try {\n            RSAOtherPrimeInfoConverter.INSTANCE.applyFrom(null)\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = 'RSA JWK \\'oth\\' (Other Prime Info) element cannot be null.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testApplyFromWithoutMap() {\n        try {\n            RSAOtherPrimeInfoConverter.INSTANCE.applyFrom(42)\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = 'RSA JWK \\'oth\\' (Other Prime Info) must contain map elements of ' +\n                    'name/value pairs. Element type found: java.lang.Integer'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testApplyFromWithEmptyMap() {\n        try {\n            RSAOtherPrimeInfoConverter.INSTANCE.applyFrom([:])\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = 'RSA JWK \\'oth\\' (Other Prime Info) element map cannot be empty.'\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testApplyFromWithMalformedMap() {\n        try {\n            RSAOtherPrimeInfoConverter.INSTANCE.applyFrom(['r':2])\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"Invalid JWK 'r' (Prime Factor) value: <redacted>. Values must be either String or \" +\n                    \"java.math.BigInteger instances. Value type found: java.lang.Integer.\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RandomsTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport java.security.SecureRandom\n\nimport static org.junit.Assert.assertTrue\n\n/**\n * @since 0.12.0\n */\nclass RandomsTest {\n\n    @Test\n    void testPrivateCtor() { //for code coverage only\n        new Randoms()\n    }\n\n    @Test\n    void testSecureRandom() {\n        def random = Randoms.secureRandom()\n        assertTrue random instanceof SecureRandom\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RsaPrivateJwkFactoryTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.impl.lang.Converters\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.Jwks\nimport io.jsonwebtoken.security.RsaPrivateJwk\nimport io.jsonwebtoken.security.UnsupportedKeyException\nimport org.junit.Test\n\nimport java.security.interfaces.RSAMultiPrimePrivateCrtKey\nimport java.security.interfaces.RSAPrivateCrtKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\nimport java.security.spec.KeySpec\nimport java.security.spec.RSAMultiPrimePrivateCrtKeySpec\nimport java.security.spec.RSAOtherPrimeInfo\n\nimport static org.junit.Assert.*\n\nclass RsaPrivateJwkFactoryTest {\n\n    @Test\n    void testGetPublicExponentFailure() {\n\n        def key = new TestRSAPrivateKey(null) {\n            @Override\n            BigInteger getModulus() {\n                return null\n            }\n        }\n\n        try {\n            Jwks.builder().key(key).build()\n            fail()\n        } catch (UnsupportedKeyException expected) {\n            String msg = String.format(RsaPrivateJwkFactory.PUB_EXPONENT_EX_MSG, KeysBridge.toString(key))\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testFailedPublicKeyDerivation() {\n        def key = new RSAPrivateCrtKey() {\n            @Override\n            BigInteger getPublicExponent() {\n                return BigInteger.ZERO\n            }\n\n            @Override\n            BigInteger getPrimeP() {\n                return null\n            }\n\n            @Override\n            BigInteger getPrimeQ() {\n                return null\n            }\n\n            @Override\n            BigInteger getPrimeExponentP() {\n                return null\n            }\n\n            @Override\n            BigInteger getPrimeExponentQ() {\n                return null\n            }\n\n            @Override\n            BigInteger getCrtCoefficient() {\n                return null\n            }\n\n            @Override\n            BigInteger getPrivateExponent() {\n                return null\n            }\n\n            @Override\n            String getAlgorithm() {\n                return null\n            }\n\n            @Override\n            String getFormat() {\n                return null\n            }\n\n            @Override\n            byte[] getEncoded() {\n                return new byte[0]\n            }\n\n            @Override\n            BigInteger getModulus() {\n                return BigInteger.ZERO\n            }\n        } as RSAPrivateKey\n\n        try {\n            Jwks.builder().key(key).build()\n            fail()\n        } catch (InvalidKeyException expected) {\n            String prefix = 'Unable to derive RSAPublicKey from RSAPrivateKey {kty=RSA}. Cause: '\n            assertTrue expected.getMessage().startsWith(prefix)\n        }\n    }\n\n    @Test\n    void testMultiPrimePrivateKey() {\n        def pair = TestKeys.RS256.pair\n        RSAPrivateCrtKey priv = pair.private as RSAPrivateCrtKey\n\n        def info1 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        def info2 = new RSAOtherPrimeInfo(BigInteger.TEN, BigInteger.TEN, BigInteger.TEN)\n        def infos = [info1, info2]\n\n        //build up test key:\n        RSAMultiPrimePrivateCrtKey key = new TestRSAMultiPrimePrivateCrtKey(priv, infos)\n\n        RsaPrivateJwk jwk = Jwks.builder().key(key).build()\n\n        List<RSAOtherPrimeInfo> oth = jwk.get('oth') as List<RSAOtherPrimeInfo>\n        assertTrue oth instanceof List\n        assertEquals 2, oth.size()\n\n        Map one = oth.get(0) as Map\n        assertEquals one.r, RSAOtherPrimeInfoConverter.PRIME_FACTOR.applyTo(info1.prime)\n        assertEquals one.d, RSAOtherPrimeInfoConverter.FACTOR_CRT_EXPONENT.applyTo(info1.crtCoefficient)\n        assertEquals one.t, RSAOtherPrimeInfoConverter.FACTOR_CRT_COEFFICIENT.applyTo(info1.crtCoefficient)\n\n        Map two = oth.get(1) as Map\n        assertEquals two.r, RSAOtherPrimeInfoConverter.PRIME_FACTOR.applyTo(info2.prime)\n        assertEquals two.d, RSAOtherPrimeInfoConverter.FACTOR_CRT_EXPONENT.applyTo(info2.crtCoefficient)\n        assertEquals two.t, RSAOtherPrimeInfoConverter.FACTOR_CRT_COEFFICIENT.applyTo(info2.crtCoefficient)\n    }\n\n    @Test\n    void testMultiPrimePrivateKeyWithoutExtraInfo() {\n        def pair = TestKeys.RS256.pair\n        RSAPrivateCrtKey priv = pair.private as RSAPrivateCrtKey\n        RSAPublicKey pub = pair.public as RSAPublicKey\n\n        RsaPrivateJwk jwk = Jwks.builder().key(priv).publicKey(pub).build()\n        // an RSAMultiPrimePrivateCrtKey without OtherInfo elements is treated the same as a normal RSAPrivateCrtKey,\n        // so ensure they are equal:\n        RSAMultiPrimePrivateCrtKey key = new TestRSAMultiPrimePrivateCrtKey(priv, null)\n        RsaPrivateJwk jwk2 = Jwks.builder().key(key).publicKey(pub).build()\n        assertEquals jwk, jwk2\n        assertNull jwk.get(DefaultRsaPrivateJwk.OTHER_PRIMES_INFO.getId())\n        assertNull jwk2.get(DefaultRsaPrivateJwk.OTHER_PRIMES_INFO.getId())\n    }\n\n    @Test\n    void testNonCrtPrivateKey() {\n        //tests a standard RSAPrivateKey (not a RSAPrivateCrtKey or RSAMultiPrimePrivateCrtKey):\n        def pair = TestKeys.RS256.pair\n        RSAPrivateCrtKey privCrtKey = pair.private as RSAPrivateCrtKey\n        RSAPublicKey pub = pair.public as RSAPublicKey\n\n        def priv = new TestRSAPrivateKey(privCrtKey)\n\n        RsaPrivateJwk jwk = Jwks.builder().key(priv).publicKey(pub).build()\n        assertEquals 4, jwk.size() // kty, public exponent, modulus, private exponent\n        assertEquals 'RSA', jwk.getType()\n        assertEquals Converters.BIGINT.applyTo(pub.getModulus()), jwk.get(DefaultRsaPublicJwk.MODULUS.getId())\n        assertEquals Converters.BIGINT.applyTo(pub.getPublicExponent()), jwk.get(DefaultRsaPublicJwk.PUBLIC_EXPONENT.getId())\n        assertEquals Converters.BIGINT.applyTo(priv.getPrivateExponent()), jwk.get(DefaultRsaPrivateJwk.PRIVATE_EXPONENT.getId()).get()\n    }\n\n    @Test\n    void testCreateJwkFromMinimalValues() { // no optional private values\n        def pair = TestKeys.RS256.pair\n        RSAPublicKey pub = pair.public as RSAPublicKey\n        RSAPrivateKey priv = new TestRSAPrivateKey(pair.private as RSAPrivateKey)\n        def jwk = Jwks.builder().key(priv).publicKey(pub).build()\n        //minimal values: kty, modulus, public exponent, private exponent = 4 params:\n        assertEquals 4, jwk.size()\n        def map = new LinkedHashMap(jwk)\n        assertEquals 4, map.size()\n\n        def jwkFromValues = Jwks.builder().add(map).build()\n\n        //ensure they're equal:\n        assertEquals jwk, jwkFromValues\n    }\n\n    @Test\n    void testCreateJwkFromMultiPrimeValues() {\n        def pair = TestKeys.RS256.pair\n        RSAPrivateCrtKey priv = pair.private as RSAPrivateCrtKey\n        RSAPublicKey pub = pair.public as RSAPublicKey\n\n        def info1 = new RSAOtherPrimeInfo(BigInteger.ONE, BigInteger.ONE, BigInteger.ONE)\n        def info2 = new RSAOtherPrimeInfo(BigInteger.TEN, BigInteger.TEN, BigInteger.TEN)\n        def infos = [info1, info2]\n        RSAMultiPrimePrivateCrtKey key = new TestRSAMultiPrimePrivateCrtKey(priv, infos)\n\n        final RsaPrivateJwk jwk = Jwks.builder().key(key).publicKey(pub).build()\n\n        //we have to test the class directly and override, since the dummy MultiPrime values won't be accepted by the\n        //JVM:\n        def factory = new RsaPrivateJwkFactory() {\n            @Override\n            protected RSAPrivateKey generateFromSpec(JwkContext<RSAPrivateKey> ctx, KeySpec keySpec) {\n                assertTrue keySpec instanceof RSAMultiPrimePrivateCrtKeySpec\n                RSAMultiPrimePrivateCrtKeySpec spec = (RSAMultiPrimePrivateCrtKeySpec) keySpec\n                assertEquals key.modulus, spec.modulus\n                assertEquals key.publicExponent, spec.publicExponent\n                assertEquals key.privateExponent, spec.privateExponent\n                assertEquals key.primeP, spec.primeP\n                assertEquals key.primeQ, spec.primeQ\n                assertEquals key.primeExponentP, spec.primeExponentP\n                assertEquals key.primeExponentQ, spec.primeExponentQ\n                assertEquals key.crtCoefficient, spec.crtCoefficient\n\n                for (int i = 0; i < infos.size(); i++) {\n                    RSAOtherPrimeInfo orig = infos.get(i)\n                    RSAOtherPrimeInfo copy = spec.otherPrimeInfo[i]\n                    assertEquals orig.prime, copy.prime\n                    assertEquals orig.exponent, copy.exponent\n                    assertEquals orig.crtCoefficient, copy.crtCoefficient\n\n                }\n                return new TestRSAMultiPrimePrivateCrtKey(priv, infos)\n            }\n        }\n\n        def returned = factory.createJwkFromValues(jwk.@context)\n\n        assertEquals jwk, returned\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/RsaSignatureAlgorithmTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.lang.CheckedFunction\nimport io.jsonwebtoken.lang.Assert\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.WeakKeyException\nimport org.junit.Test\n\nimport java.security.KeyPair\nimport java.security.KeyPairGenerator\nimport java.security.PublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.easymock.EasyMock.createMock\nimport static org.junit.Assert.*\n\nclass RsaSignatureAlgorithmTest {\n\n    static final Collection<RsaSignatureAlgorithm> algs = Jwts.SIG.get().values().findAll({\n        it instanceof RsaSignatureAlgorithm\n    }) as Collection<RsaSignatureAlgorithm>\n\n    @Test\n    void testKeyPairBuilder() {\n        algs.each {\n            def pair = it.keyPair().build()\n            assertNotNull pair.public\n            assertTrue pair.public instanceof RSAPublicKey\n            assertEquals it.preferredKeyBitLength, pair.public.modulus.bitLength()\n            assertTrue pair.private instanceof RSAPrivateKey\n            assertEquals it.preferredKeyBitLength, pair.private.modulus.bitLength()\n        }\n    }\n\n    @Test\n    void testValidateKeyWithoutRSAorRSASSAPSSAlgorithmName() {\n        PublicKey key = new TestPublicKey(algorithm: 'foo')\n        algs.each {\n            try {\n                it.validateKey(key, false)\n            } catch (InvalidKeyException e) {\n                String msg = 'Unrecognized RSA or RSASSA-PSS key algorithm name.'\n                assertEquals msg, e.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testValidateRSAAlgorithmKeyThatDoesntUseRSAKeyInterface() {\n        PublicKey key = new TestPublicKey(algorithm: 'RSA')\n        algs.each {\n            it.validateKey(key, false) //no exception - can't check for RSAKey length\n        }\n    }\n\n    @Test\n    void testValidateKeyWithoutRsaKey() {\n        PublicKey key = TestKeys.ES256.pair.public // not an RSA key\n        algs.each {\n            try {\n                it.validateKey(key, false)\n            } catch (InvalidKeyException e) {\n                String msg = 'Unrecognized RSA or RSASSA-PSS key algorithm name.'\n                assertEquals msg, e.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testValidateSigningKeyNotPrivate() {\n        RSAPublicKey key = createMock(RSAPublicKey)\n        def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, key)\n        try {\n            Jwts.SIG.RS256.digest(request)\n            fail()\n        } catch (InvalidKeyException e) {\n            String expected = \"RS256 signing keys must be PrivateKeys (implement java.security.PrivateKey). \" +\n                    \"Provided key type: ${key.getClass().getName()}.\"\n            assertEquals expected, e.getMessage()\n        }\n    }\n\n    @Test\n    void testValidateSigningKeyWeakKey() {\n        def gen = KeyPairGenerator.getInstance(\"RSA\")\n        gen.initialize(1024) //too week for any JWA RSA algorithm\n        def rsaPair = gen.generateKeyPair()\n\n        def pssPair = new JcaTemplate(RsaSignatureAlgorithm.PSS_JCA_NAME)\n                .withKeyPairGenerator(new CheckedFunction<KeyPairGenerator, KeyPair>() {\n                    @Override\n                    KeyPair apply(KeyPairGenerator generator) throws Exception {\n                        generator.initialize(1024)\n                        return generator.generateKeyPair()\n                    }\n                })\n\n        algs.each {\n            def pair = it.getId().startsWith(\"PS\") ? pssPair : rsaPair\n            def request = new DefaultSecureRequest(Streams.of(new byte[1]), null, null, pair.getPrivate())\n            try {\n                it.digest(request)\n                fail()\n            } catch (WeakKeyException expected) {\n                String id = it.getId()\n                String section = id.startsWith('PS') ? '3.5' : '3.3'\n                String msg = \"The RSA signing key size (aka modulus bit length) is 1024 bits which is not secure \" +\n                        \"enough for the ${it.getId()} algorithm.  The JWT JWA Specification (RFC 7518, Section \" +\n                        \"${section}) states that RSA keys \" +\n                        \"MUST have a size >= 2048 bits.  Consider using the Jwts.SIG.${id}.keyPair() \" +\n                        \"builder to create a KeyPair guaranteed to be secure enough for ${id}.  See \" +\n                        \"https://tools.ietf.org/html/rfc7518#section-${section} for more information.\"\n                assertEquals msg, expected.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testFindByKeyWithNoAlgorithm() {\n        assertNull RsaSignatureAlgorithm.findByKey(new TestPrivateKey())\n    }\n\n    @Test\n    void testFindByKeyInvalidAlgorithm() {\n        assertNull DefaultMacAlgorithm.findByKey(new TestPrivateKey(algorithm: 'foo'))\n    }\n\n    @Test\n    void testFindByKey() {\n        for (def alg : algs) {\n            def pair = TestKeys.forAlgorithm(alg).pair\n            assertSame alg, RsaSignatureAlgorithm.findByKey(pair.public)\n            assertSame alg, RsaSignatureAlgorithm.findByKey(pair.private)\n        }\n    }\n\n    @Test\n    void testFindByKeyNull() {\n        assertNull RsaSignatureAlgorithm.findByKey(null)\n    }\n\n    @Test\n    void testFindByNonAsymmetricKey() {\n        assertNull RsaSignatureAlgorithm.findByKey(TestKeys.HS256)\n    }\n\n    @Test\n    void testFindByWeakKey() {\n        for (def alg : algs) {\n            def pair = TestKeys.forAlgorithm(alg).pair\n            byte[] mag = new byte[255] // one byte less than 256 (2048 bits) which is the minimum\n            Randoms.secureRandom().nextBytes(mag)\n            def modulus = new BigInteger(1, mag)\n            //def modulus = pair.public.modulus\n            def weakPub = new TestRSAKey(pair.public); weakPub.modulus = modulus\n            def weakPriv = new TestRSAKey(pair.private); weakPriv.modulus = modulus\n            assertNull RsaSignatureAlgorithm.findByKey(weakPub)\n            assertNull RsaSignatureAlgorithm.findByKey(weakPriv)\n        }\n    }\n\n    @Test\n    void testFindByLargerThanExpectedKey() {\n        for (def alg : algs) {\n            def pair = TestKeys.forAlgorithm(alg).pair\n            int bitlen = alg.preferredKeyBitLength + 1 // one more bit than required\n            int len = Bytes.length(bitlen)\n            def mag = new byte[len]\n            Randoms.secureRandom().nextBytes(mag)\n            mag[0] = 0x01 // ensure first byte is non-zero so BigInteger doesnt discard leading zero bytes\n            def modulus = new BigInteger(1, mag)\n            bitlen = modulus.bitLength()\n            Assert.gt(bitlen, alg.preferredKeyBitLength, \"Invalid modulus creation\")\n            def strongPub = new TestRSAKey(pair.public); strongPub.modulus = modulus\n            def strongPriv = new TestRSAKey(pair.private); strongPriv.modulus = modulus\n            assertSame alg, RsaSignatureAlgorithm.findByKey(strongPub)\n            assertSame alg, RsaSignatureAlgorithm.findByKey(strongPriv)\n        }\n    }\n\n    @Test\n    void testFindByKeyOid() {\n        for (def entry : RsaSignatureAlgorithm.PKCSv15_ALGS.entrySet()) {\n            def oid = entry.getKey()\n            def alg = entry.getValue()\n            def oidKey = new TestPrivateKey(algorithm: oid)\n            assertSame alg, RsaSignatureAlgorithm.findByKey(oidKey)\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/SecretJwkFactoryTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.io.Encoders\nimport io.jsonwebtoken.security.*\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\n/**\n * The {@link SecretJwkFactory} is tested in other classes (JwksTest, JwkParserTest, etc) - this class exists\n * primarily to fill in coverage gaps where necessary.\n *\n * @since 0.12.0\n */\nclass SecretJwkFactoryTest {\n\n    private static Set<MacAlgorithm> macAlgs() {\n        return Jwts.SIG.get().values().findAll({ it -> it instanceof MacAlgorithm }) as Collection<MacAlgorithm>\n    }\n\n    @Test\n    // if a jwk does not have an 'alg' or 'use' param, we default to an AES key\n    void testNoAlgNoSigJcaName() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA256).build()\n        SecretJwk result = Jwks.builder().add(jwk).build() as SecretJwk\n        assertEquals 'AES', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    void testJwkHS256AlgSetsKeyJcaNameCorrectly() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.HS256).build()\n        SecretJwk result = Jwks.builder().add(jwk).build() as SecretJwk\n        assertEquals 'HmacSHA256', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    void testSignOpSetsKeyHmacSHA256() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA256).build()\n        SecretJwk result = Jwks.builder().add(jwk).operations().add(Jwks.OP.SIGN).and().build() as SecretJwk\n        assertNull result.getAlgorithm()\n        assertNull result.get('use')\n        assertEquals 'HmacSHA256', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    void testJwkHS384AlgSetsKeyJcaNameCorrectly() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.HS384).build()\n        SecretJwk result = Jwks.builder().add(jwk).build() as SecretJwk\n        assertEquals 'HmacSHA384', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    void testSignOpSetsKeyHmacSHA384() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA384).build()\n        SecretJwk result = Jwks.builder().add(jwk).operations().add(Jwks.OP.SIGN).and().build() as SecretJwk\n        assertNull result.getAlgorithm()\n        assertNull result.get('use')\n        assertEquals 'HmacSHA384', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    void testJwkHS512AlgSetsKeyJcaNameCorrectly() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.HS512).build()\n        SecretJwk result = Jwks.builder().add(jwk).build() as SecretJwk\n        assertEquals 'HmacSHA512', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    void testSignOpSetsKeyHmacSHA512() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA512).build()\n        SecretJwk result = Jwks.builder().add(jwk).operations().add(Jwks.OP.SIGN).and().build() as SecretJwk\n        assertNull result.getAlgorithm()\n        assertNull result.get('use')\n        assertEquals 'HmacSHA512', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    // no 'alg' jwk property, but 'use' is 'sig', so forces jcaName to be HmacSHA256\n    void testNoAlgAndSigUseForHS256() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA256).build()\n        assertFalse jwk.containsKey('alg')\n        assertFalse jwk.containsKey('use')\n        SecretJwk result = Jwks.builder().add(jwk).add('use', 'sig').build() as SecretJwk\n        assertEquals 'HmacSHA256', result.toKey().getAlgorithm() // jcaName has been changed to a sig algorithm\n    }\n\n    @Test\n    // no 'alg' jwk property, but 'use' is 'sig', so forces jcaName to be HmacSHA384\n    void testNoAlgAndSigUseForHS384() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA384).build()\n        assertFalse jwk.containsKey('alg')\n        assertFalse jwk.containsKey('use')\n        SecretJwk result = Jwks.builder().add(jwk).add('use', 'sig').build() as SecretJwk\n        assertEquals 'HmacSHA384', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    // no 'alg' jwk property, but 'use' is 'sig', so forces jcaName to be HmacSHA512\n    void testNoAlgAndSigUseForHS512() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA512).build()\n        assertFalse jwk.containsKey('alg')\n        assertFalse jwk.containsKey('use')\n        SecretJwk result = Jwks.builder().add(jwk).add('use', 'sig').build() as SecretJwk\n        assertEquals 'HmacSHA512', result.toKey().getAlgorithm()\n    }\n\n    @Test\n    // no 'alg' jwk property, but 'use' is something other than 'sig', so jcaName should default to AES\n    void testNoAlgAndNonSigUse() {\n        SecretJwk jwk = Jwks.builder().key(TestKeys.NA256).build()\n        assertFalse jwk.containsKey('alg')\n        assertFalse jwk.containsKey('use')\n        SecretJwk result = Jwks.builder().add(jwk).add('use', 'foo').build() as SecretJwk\n        assertEquals 'AES', result.toKey().getAlgorithm()\n    }\n\n    /**\n     * @since 0.12.4\n     */\n    @Test\n    // 'oct' type, but 'alg' value is not a secret key algorithm (and therefore malformed)\n    void testMismatchedAlgorithm() {\n        try {\n            Jwks.builder().key(TestKeys.NA256).add('alg', Jwts.SIG.RS256.getId()).build()\n            fail()\n        } catch (MalformedKeyException expected) {\n            String msg = \"Invalid Secret JWK ${AbstractJwk.ALG} value 'RS256'. Secret JWKs may only be used with \" +\n                    \"symmetric (secret) key algorithms.\"\n            assertEquals msg, expected.message\n        }\n    }\n\n    /**\n     * Test the case where a jwk `alg` value is present, but the key material doesn't match that algs key length\n     * requirements.  This would be a malformed key.\n     */\n    @Test\n    void testSizeMismatchedSecretJwk() {\n        //first get a valid HS256 JWK:\n        SecretJwk validJwk = Jwks.builder().key(TestKeys.HS256).build()\n\n        //now associate it with an alg identifier that is more than the key is capable of:\n        try {\n            Jwks.builder().add(validJwk)\n                    .add('alg', 'HS384')\n                    .build()\n            fail()\n        } catch (WeakKeyException expected) {\n            String msg = \"Secret JWK 'alg' (Algorithm) value is 'HS384', but the 'k' (Key Value) length is smaller \" +\n                    \"than the HS384 minimum length of 384 bits (48 bytes) required by \" +\n                    \"[JWA RFC 7518, Section 3.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2), 2nd \" +\n                    \"paragraph: 'A key of the same size as the hash output or larger MUST be used with this \" +\n                    \"algorithm.'\"\n            assertEquals msg, expected.getMessage()\n        }\n    }\n\n    /**\n     * Test when a {@code k} size is smaller, equal to, and larger than the minimum required number of bits/bytes for\n     * a given HmacSHA* algorithm. The RFCs indicate smaller-than is not allowed, while equal-to and greater-than are\n     * allowed.\n     *\n     * This test asserts this allowed behavior per https://github.com/jwtk/jjwt/issues/905\n     * @see <a href=\"https://github.com/jwtk/jjwt/issues/905\">JJWT Issue 905</a>\n     * @since 0.12.4\n     */\n    @Test\n    void testAllowedKeyLengths() {\n\n        def parser = Jwks.parser().build()\n\n        for (MacAlgorithm alg : macAlgs()) {\n\n            // 3 key length sizes for each alg to test:\n            // index 0: smaller than minimum required\n            // index 1: minimum required\n            // index 2: more than minimum required:\n            def sizes = [alg.keyBitLength - Byte.SIZE, alg.keyBitLength, alg.keyBitLength + Byte.SIZE]\n\n            for (int i = 0; i < sizes.size(); i++) {\n\n                def kBitLength = sizes.get(i)\n                def k = Bytes.random(Bytes.length(kBitLength))\n\n                def jwkJson = \"\"\"\n                {\n                  \"kid\": \"${UUID.randomUUID().toString()}\",\n                  \"kty\": \"oct\",\n                  \"alg\": \"${alg.getId()}\",\n                  \"k\": \"${Encoders.BASE64URL.encode(k)}\"\n                }\"\"\".toString()\n\n                def jwk\n                try {\n                    jwk = parser.parse(jwkJson)\n                } catch (WeakKeyException expected) {\n                    assertEquals(\"Should only occur on index 0 with less-than-minimum key length\", 0, i)\n                    String msg = \"Secret JWK 'alg' (Algorithm) value is '${alg.getId()}', but the 'k' (Key Value) \" +\n                            \"length is smaller than the ${alg.getId()} minimum length of \" +\n                            \"${Bytes.bitsMsg(alg.keyBitLength)} required by \" +\n                            \"[JWA RFC 7518, Section 3.2](https://www.rfc-editor.org/rfc/rfc7518.html#section-3.2), \" +\n                            \"2nd paragraph: 'A key of the same size as the hash output or larger MUST be used with \" +\n                            \"this algorithm.'\"\n                    assertEquals msg, expected.getMessage()\n                    continue // expected for index 0 (purposefully weak key), so let loop continue\n                }\n\n                // otherwise not weak, sizes should reflect equal-to or greater-than alg bitlength sizes\n                assert jwk instanceof SecretJwk\n                assertEquals alg.getId(), jwk.getAlgorithm()\n                def bytes = jwk.toKey().getEncoded()\n                assertTrue Bytes.bitLength(bytes) >= alg.keyBitLength\n                assertEquals Bytes.length(kBitLength), jwk.toKey().getEncoded().length\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/StandardCurvesTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.Jwks\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass StandardCurvesTest {\n\n    static final StandardCurves curves = (StandardCurves) Jwks.CRV.get()\n\n    @Test\n    void testFindById() {\n        curves.values().each {\n            assertSame it, curves.get(it.getId())\n        }\n    }\n\n    @Test\n    void testFindByNullKey() {\n        assertNull StandardCurves.findByKey(null)\n    }\n\n    @Test\n    void testKeyPairBuilders() {\n        curves.values().each {\n            def pair = it.keyPair().build()\n            if (it instanceof ECCurve) {\n                assertEquals ECCurve.KEY_PAIR_GENERATOR_JCA_NAME, pair.getPublic().getAlgorithm()\n                assertEquals ECCurve.KEY_PAIR_GENERATOR_JCA_NAME, pair.getPrivate().getAlgorithm()\n            } else { // edwards curve\n                String jcaName = it.getJcaName()\n                String pubAlg = pair.getPublic().getAlgorithm()\n                String privAlg = pair.getPrivate().getAlgorithm()\n\n                if (jcaName.startsWith('X')) { // X*** curves\n                    //BC will retain exact alg, OpenJDK >= 11 will use 'XDH' instead, both are valid:\n                    assertTrue(pubAlg.equals(jcaName) || pubAlg.equals('XDH'))\n                    assertTrue(privAlg.equals(jcaName) || privAlg.equals('XDH'))\n                } else { // Ed*** curves\n                    //BC will retain exact alg, OpenJDK >= 15 will use 'EdDSA' instead, both are valid:\n                    assertTrue(pubAlg.equals(jcaName) || pubAlg.equals('EdDSA'))\n                    assertTrue(privAlg.equals(jcaName) || privAlg.equals('EdDSA'))\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/StandardSecureDigestAlgorithmsTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport org.junit.Test\n\nimport static org.junit.Assert.assertNull\n\nclass StandardSecureDigestAlgorithmsTest {\n\n    @Test\n    void testFindByPublicSigningKey() {\n        //public keys are not supported for signing:\n        assertNull StandardSecureDigestAlgorithms.findBySigningKey(new TestPublicKey())\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestAeadAlgorithm.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.*\n\nclass TestAeadAlgorithm implements AeadAlgorithm {\n\n    String id\n    int keyBitLength = 256\n\n    @Override\n    String getId() {\n        return id\n    }\n\n    @Override\n    void encrypt(AeadRequest request, AeadResult result) throws SecurityException {\n    }\n\n    @Override\n    void decrypt(DecryptAeadRequest request, OutputStream out) throws SecurityException {\n    }\n\n    @Override\n    SecretKeyBuilder key() {\n        return null\n    }\n\n    @Override\n    int getKeyBitLength() {\n        return keyBitLength\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestCertificates.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Identifiable\nimport io.jsonwebtoken.lang.Classes\nimport io.jsonwebtoken.lang.Strings\nimport org.bouncycastle.asn1.pkcs.PrivateKeyInfo\nimport org.bouncycastle.asn1.x509.SubjectPublicKeyInfo\nimport org.bouncycastle.jce.provider.BouncyCastleProvider\nimport org.bouncycastle.openssl.PEMKeyPair\nimport org.bouncycastle.openssl.PEMParser\n\nimport java.nio.charset.StandardCharsets\nimport java.security.PrivateKey\nimport java.security.Provider\nimport java.security.PublicKey\nimport java.security.cert.X509Certificate\nimport java.security.spec.KeySpec\nimport java.security.spec.PKCS8EncodedKeySpec\nimport java.security.spec.X509EncodedKeySpec\n\n/**\n * For test cases that need to read certificate and/or PEM files.  Encapsulates BouncyCastle API to\n * this class so it doesn't need to propagate across other test classes.\n *\n * MAINTAINERS NOTE:\n *\n * If this logic is ever needed in the impl or api modules, do not keep the\n * name of this class - it was quickly thrown together and it isn't appropriately named for exposure in a public\n * module.  Thought/design is necessary to see if/how cert/pem reading should be exposed in an easy-to-use and\n * maintain API (e.g. probably a builder).\n *\n * The only purpose of this class and its methods are to:\n *   1) be used in Test classes only, and\n *   2) encapsulate the BouncyCastle API so it is not exposed to other Test classes.\n */\nclass TestCertificates {\n\n    static Provider BC = new BouncyCastleProvider()\n\n    private static String relativePath(String basename) {\n        String packageName = TestCertificates.class.getPackage().getName()\n        return Strings.replace(packageName, \".\", \"/\") + \"/\" + basename\n    }\n\n    private static InputStream getResourceStream(String filename) {\n        String resourcePath = relativePath(filename)\n        return Classes.getResourceAsStream(resourcePath)\n    }\n\n    private static PEMParser getParser(String filename) {\n        InputStream is = getResourceStream(filename)\n        return new PEMParser(new BufferedReader(new InputStreamReader(is, StandardCharsets.ISO_8859_1)))\n    }\n\n    private static String keyJcaName(Identifiable alg) {\n        String jcaName = alg.getId()\n        if (jcaName.startsWith('ES')) {\n            jcaName = 'EC'\n        } else if (jcaName.startsWith('PS')) {\n            jcaName = 'RSASSA-PSS'\n        } else if (jcaName.startsWith('RS')) {\n            jcaName = 'RSA'\n        }\n        return jcaName\n    }\n\n    private static PublicKey readPublicKey(Identifiable alg) {\n        PEMParser parser = getParser(alg.id + '.pub.pem')\n        parser.withCloseable {\n            SubjectPublicKeyInfo info = it.readObject() as SubjectPublicKeyInfo\n            JcaTemplate template = new JcaTemplate(keyJcaName(alg))\n            return template.generatePublic(new X509EncodedKeySpec(info.getEncoded()))\n        }\n    }\n\n    private static X509Certificate readCert(Identifiable alg, Provider provider) {\n        InputStream is = getResourceStream(alg.id + '.crt.pem')\n        JcaTemplate template = new JcaTemplate(\"X.509\", provider)\n        return template.generateX509Certificate(is.getBytes())\n    }\n\n    private static PrivateKey readPrivateKey(Identifiable alg) {\n        final String id = alg.id\n        PEMParser parser = getParser(id + '.pkcs8.pem')\n        parser.withCloseable {\n            PrivateKeyInfo info\n            Object object = it.readObject()\n            if (object instanceof PEMKeyPair) {\n                info = ((PEMKeyPair) object).getPrivateKeyInfo()\n            } else {\n                info = (PrivateKeyInfo) object\n            }\n            final KeySpec spec = new PKCS8EncodedKeySpec(info.getEncoded())\n            return new JcaTemplate(keyJcaName(alg), null).generatePrivate(spec)\n        }\n    }\n\n    static TestKeys.Bundle readBundle(Identifiable alg) {\n\n        PublicKey pub = readPublicKey(alg) as PublicKey\n        PrivateKey priv = readPrivateKey(alg) as PrivateKey\n\n        // If the public key loaded is a BC key, the default provider doesn't understand the cert key OID\n        // (for example, an Ed25519 key on JDK 8 which doesn't natively support such keys). This means the\n        // X.509 certificate should also be loaded by BC; otherwise the Sun X.509 CertificateFactory returns\n        // a certificate with certificate.getPublicKey() being a sun X509Key instead of the type-specific key we want:\n        Provider provider = null\n        if (pub.getClass().getName().startsWith(\"org.bouncycastle\")) {\n            provider = BC\n        }\n        X509Certificate cert = readCert(alg, provider) as X509Certificate\n        PublicKey certPub = cert.getPublicKey()\n        assert pub.equals(certPub)\n\n        return new TestKeys.Bundle(alg, pub, priv, cert)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestECField.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.spec.ECField\n\nclass TestECField implements ECField {\n\n    int fieldSize\n\n    @Override\n    int getFieldSize() {\n        return fieldSize\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestECKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.interfaces.ECKey\nimport java.security.spec.ECParameterSpec\n\nclass TestECKey extends TestKey implements ECKey {\n\n    ECParameterSpec params\n\n    @Override\n    ECParameterSpec getParams() {\n        return params\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestECPrivateKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.interfaces.ECPrivateKey\n\nclass TestECPrivateKey extends TestECKey implements ECPrivateKey {\n\n    BigInteger s\n\n    @Override\n    BigInteger getS() {\n        return s\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestECPublicKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.interfaces.ECPublicKey\nimport java.security.spec.ECPoint\n\nclass TestECPublicKey extends TestECKey implements ECPublicKey {\n\n    ECPoint w\n\n    @Override\n    ECPoint getW() {\n        return w\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.Key\n\nclass TestKey implements Key {\n\n    String algorithm\n    String format\n    byte[] encoded\n\n    @Override\n    String getAlgorithm() {\n        return algorithm\n    }\n\n    @Override\n    String getFormat() {\n        return format\n    }\n\n    @Override\n    byte[] getEncoded() {\n        return encoded\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestKeyAlgorithm.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.*\n\nimport javax.crypto.SecretKey\n\nclass TestKeyAlgorithm implements KeyAlgorithm {\n\n    String id\n\n    @Override\n    String getId() {\n        return id\n    }\n\n    @Override\n    KeyResult getEncryptionKey(KeyRequest request) throws SecurityException {\n        return null\n    }\n\n    @Override\n    SecretKey getDecryptionKey(DecryptionKeyRequest request) throws SecurityException {\n        return null\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestKeys.groovy",
    "content": "/*\n * Copyright (C) 2021 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.Identifiable\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.lang.Collections\nimport io.jsonwebtoken.security.Jwks\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.security.KeyPair\nimport java.security.PrivateKey\nimport java.security.Provider\nimport java.security.PublicKey\nimport java.security.cert.X509Certificate\n\n/**\n * Test helper with cached keys to save time across tests (so we don't have to constantly dynamically generate keys)\n */\nclass TestKeys {\n\n    static Provider BC = TestCertificates.BC\n\n    // =======================================================\n    // Secret Keys\n    // =======================================================\n    static SecretKey HS256 = Jwts.SIG.HS256.key().build()\n    static SecretKey HS384 = Jwts.SIG.HS384.key().build()\n    static SecretKey HS512 = Jwts.SIG.HS512.key().build()\n    static Collection<SecretKey> HS = Collections.setOf(HS256, HS384, HS512)\n\n    static SecretKey NA256 = new SecretKeySpec(HS256.encoded, \"NONE\")\n    static SecretKey NA384 = new SecretKeySpec(HS384.encoded, \"NONE\")\n    static SecretKey NA512 = new SecretKeySpec(HS512.encoded, \"NONE\")\n    static Collection<SecretKey> NA = [NA256, NA384, NA512]\n\n    static SecretKey A128GCM, A192GCM, A256GCM, A128KW, A192KW, A256KW, A128GCMKW, A192GCMKW, A256GCMKW\n    static Collection<SecretKey> AGCM\n    static {\n        A128GCM = A128KW = A128GCMKW = Jwts.ENC.A128GCM.key().build()\n        A192GCM = A192KW = A192GCMKW = Jwts.ENC.A192GCM.key().build()\n        A256GCM = A256KW = A256GCMKW = Jwts.ENC.A256GCM.key().build()\n        AGCM = Collections.setOf(A128GCM, A192GCM, A256GCM)\n    }\n\n    static SecretKey A128CBC_HS256 = Jwts.ENC.A128CBC_HS256.key().build()\n    static SecretKey A192CBC_HS384 = Jwts.ENC.A192CBC_HS384.key().build()\n    static SecretKey A256CBC_HS512 = Jwts.ENC.A256CBC_HS512.key().build()\n    static Collection<SecretKey> ACBC = Collections.setOf(A128CBC_HS256, A192CBC_HS384, A256CBC_HS512)\n\n    static Collection<SecretKey> SECRET = new LinkedHashSet<>()\n    static {\n        SECRET.addAll(HS)\n        SECRET.addAll(NA)\n        SECRET.addAll(AGCM)\n        SECRET.addAll(ACBC)\n    }\n\n    // =======================================================\n    // Elliptic Curve Keys & Certificates\n    // =======================================================\n    static Bundle ES256 = TestCertificates.readBundle(Jwts.SIG.ES256)\n    static Bundle ES384 = TestCertificates.readBundle(Jwts.SIG.ES384)\n    static Bundle ES512 = TestCertificates.readBundle(Jwts.SIG.ES512)\n    static Set<Bundle> EC = Collections.setOf(ES256, ES384, ES512)\n\n    static Bundle Ed25519 = TestCertificates.readBundle(Jwks.CRV.Ed25519)\n    static Bundle Ed448 = TestCertificates.readBundle(Jwks.CRV.Ed448)\n    // just an alias for Ed448 for now:\n    static Bundle EdDSA = Ed448\n    static Bundle X25519 = TestCertificates.readBundle(EdwardsCurve.X25519)\n    static Bundle X448 = TestCertificates.readBundle(EdwardsCurve.X448)\n    static Set<Bundle> EdEC = Collections.setOf(EdDSA, Ed25519, Ed448, X25519, X448)\n\n    // =======================================================\n    // RSA Keys & Certificates\n    // =======================================================\n    static Bundle RS256 = TestCertificates.readBundle(Jwts.SIG.RS256)\n    static Bundle RS384 = TestCertificates.readBundle(Jwts.SIG.RS384)\n    static Bundle RS512 = TestCertificates.readBundle(Jwts.SIG.RS512)\n    static Bundle PS256 = TestCertificates.readBundle(Jwts.SIG.PS256)\n    static Bundle PS384 = TestCertificates.readBundle(Jwts.SIG.PS384)\n    static Bundle PS512 = TestCertificates.readBundle(Jwts.SIG.PS512)\n//    static Set<Bundle> PKCSv15 = Collections.setOf(RS256, RS384, RS512)\n//    static Set<Bundle> RSASSA_PSS = Collections.setOf(PS256, PS384, PS512)\n    static Set<Bundle> RSA = Collections.setOf(RS256, RS384, RS512, PS256, PS384, PS512)\n\n    static Set<Bundle> ASYM = new LinkedHashSet<>()\n    static {\n        ASYM.addAll(EC)\n        ASYM.addAll(EdEC)\n        ASYM.addAll(RSA)\n    }\n\n    static Bundle forAlgorithm(Identifiable alg) {\n        String id = alg.getId()\n        return TestKeys.metaClass.getAttribute(TestKeys, id) as Bundle\n    }\n\n    static class Bundle {\n\n        Identifiable alg\n        X509Certificate cert\n        List<X509Certificate> chain\n        KeyPair pair\n\n        Bundle(Identifiable alg, PublicKey publicKey, PrivateKey privateKey, X509Certificate cert = null) {\n            this.alg = alg\n            this.cert = cert\n            this.chain = cert != null ? Collections.of(cert) : Collections.<X509Certificate> emptyList()\n            this.pair = new KeyPair(publicKey, privateKey);\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestMacAlgorithm.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport io.jsonwebtoken.security.*\n\nimport javax.crypto.SecretKey\n\nclass TestMacAlgorithm implements MacAlgorithm {\n\n    String id\n    MacAlgorithm delegate\n\n    @Override\n    String getId() {\n        return id\n    }\n\n    @Override\n    byte[] digest(SecureRequest<InputStream, SecretKey> request) throws SecurityException {\n        return delegate.digest(request)\n    }\n\n    @Override\n    boolean verify(VerifySecureDigestRequest<SecretKey> request) throws SecurityException {\n        return delegate.verify(request)\n    }\n\n    @Override\n    SecretKeyBuilder key() {\n        return delegate.key()\n    }\n\n    @Override\n    int getKeyBitLength() {\n        return delegate.getKeyBitLength()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestPrivateKey.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport javax.security.auth.DestroyFailedException\nimport javax.security.auth.Destroyable\nimport java.security.PrivateKey\n\nclass TestPrivateKey extends TestKey implements PrivateKey, Destroyable {\n\n    boolean destroyed\n\n    @Override\n    void destroy() throws DestroyFailedException {\n        destroyed = true\n    }\n\n    @Override\n    boolean isDestroyed() {\n        return destroyed\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestProvider.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.Provider\n\nclass TestProvider extends Provider {\n\n    TestProvider() {\n        this('test')\n    }\n\n    TestProvider(String name) {\n        super(name, 1.0d, 'info')\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestPublicKey.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.PublicKey\n\nclass TestPublicKey extends TestKey implements PublicKey {\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestRSAKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.interfaces.RSAKey\n\nclass TestRSAKey extends TestKey implements RSAKey {\n\n    final def src\n    BigInteger modulus\n\n    TestRSAKey(def key) {\n        this.src = key\n        this.algorithm = key?.getAlgorithm()\n        this.format = key?.getFormat()\n        this.encoded = key?.getEncoded()\n        this.modulus = key?.getModulus()\n    }\n\n    @Override\n    BigInteger getModulus() {\n        return this.modulus\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestRSAMultiPrimePrivateCrtKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.interfaces.RSAMultiPrimePrivateCrtKey\nimport java.security.interfaces.RSAPrivateCrtKey\nimport java.security.spec.RSAOtherPrimeInfo\n\nclass TestRSAMultiPrimePrivateCrtKey extends TestRSAPrivateKey implements RSAMultiPrimePrivateCrtKey {\n\n    private final List<RSAOtherPrimeInfo> infos\n\n    TestRSAMultiPrimePrivateCrtKey(RSAPrivateCrtKey src, List<RSAOtherPrimeInfo> infos) {\n        super(src)\n        this.infos = infos\n    }\n\n    @Override\n    BigInteger getPublicExponent() {\n        return src.publicExponent\n    }\n\n    @Override\n    BigInteger getPrimeP() {\n        return src.primeP\n    }\n\n    @Override\n    BigInteger getPrimeQ() {\n        return src.primeQ\n    }\n\n    @Override\n    BigInteger getPrimeExponentP() {\n        return src.primeExponentP\n    }\n\n    @Override\n    BigInteger getPrimeExponentQ() {\n        return src.primeExponentQ\n    }\n\n    @Override\n    BigInteger getCrtCoefficient() {\n        return src.crtCoefficient\n    }\n\n    @Override\n    RSAOtherPrimeInfo[] getOtherPrimeInfo() {\n        return infos as RSAOtherPrimeInfo[]\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestRSAPrivateKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.interfaces.RSAPrivateKey\n\nclass TestRSAPrivateKey extends TestRSAKey implements RSAPrivateKey {\n\n    TestRSAPrivateKey(RSAPrivateKey key) {\n        super(key)\n    }\n\n    @Override\n    BigInteger getPrivateExponent() {\n        return src.privateExponent\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestSecretKey.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport javax.crypto.SecretKey\n\nclass TestSecretKey extends TestKey implements SecretKey {\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/impl/security/TestX509Certificate.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.impl.security\n\nimport java.security.*\nimport java.security.cert.*\n\nclass TestX509Certificate extends X509Certificate {\n\n    private boolean[] keyUsage = new boolean[9]\n\n    @Override\n    void checkValidity() throws CertificateExpiredException, CertificateNotYetValidException {\n\n    }\n\n    @Override\n    void checkValidity(Date date) throws CertificateExpiredException, CertificateNotYetValidException {\n\n    }\n\n    @Override\n    int getVersion() {\n        return 0\n    }\n\n    @Override\n    BigInteger getSerialNumber() {\n        return null\n    }\n\n    @Override\n    Principal getIssuerDN() {\n        return null\n    }\n\n    @Override\n    Principal getSubjectDN() {\n        return null\n    }\n\n    @Override\n    Date getNotBefore() {\n        return null\n    }\n\n    @Override\n    Date getNotAfter() {\n        return null\n    }\n\n    @Override\n    byte[] getTBSCertificate() throws CertificateEncodingException {\n        return new byte[0]\n    }\n\n    @Override\n    byte[] getSignature() {\n        return new byte[0]\n    }\n\n    @Override\n    String getSigAlgName() {\n        return null\n    }\n\n    @Override\n    String getSigAlgOID() {\n        return null\n    }\n\n    @Override\n    byte[] getSigAlgParams() {\n        return new byte[0]\n    }\n\n    @Override\n    boolean[] getIssuerUniqueID() {\n        return new boolean[0]\n    }\n\n    @Override\n    boolean[] getSubjectUniqueID() {\n        return new boolean[0]\n    }\n\n    @Override\n    boolean[] getKeyUsage() {\n        return this.keyUsage\n    }\n\n    @Override\n    int getBasicConstraints() {\n        return 0\n    }\n\n    @Override\n    byte[] getEncoded() throws CertificateEncodingException {\n        return new byte[0]\n    }\n\n    @Override\n    void verify(PublicKey key) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {\n\n    }\n\n    @Override\n    void verify(PublicKey key, String sigProvider) throws CertificateException, NoSuchAlgorithmException, InvalidKeyException, NoSuchProviderException, SignatureException {\n\n    }\n\n    @Override\n    String toString() {\n        return null\n    }\n\n    @Override\n    PublicKey getPublicKey() {\n        return null\n    }\n\n    @Override\n    boolean hasUnsupportedCriticalExtension() {\n        return false\n    }\n\n    @Override\n    Set<String> getCriticalExtensionOIDs() {\n        return null\n    }\n\n    @Override\n    Set<String> getNonCriticalExtensionOIDs() {\n        return null\n    }\n\n    @Override\n    byte[] getExtensionValue(String oid) {\n        return new byte[0]\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/issues/Issue365Test.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.issues\n\nimport io.jsonwebtoken.Header\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.Locator\nimport io.jsonwebtoken.impl.DefaultJwtBuilder\nimport io.jsonwebtoken.impl.DefaultJwtParser\nimport io.jsonwebtoken.impl.security.TestKeys\nimport io.jsonwebtoken.impl.security.TestPrivateKey\nimport io.jsonwebtoken.impl.security.TestPublicKey\nimport io.jsonwebtoken.security.InvalidKeyException\nimport io.jsonwebtoken.security.KeyAlgorithm\nimport io.jsonwebtoken.security.SignatureAlgorithm\nimport org.junit.Test\n\nimport java.security.Key\nimport java.security.PrivateKey\nimport java.security.PublicKey\n\nimport static org.junit.Assert.assertEquals\nimport static org.junit.Assert.fail\n\nclass Issue365Test {\n\n\n    private static final Collection<SignatureAlgorithm> sigalgs() {\n        def algs = Jwts.SIG.get().values()\n                .findAll({ it -> it instanceof SignatureAlgorithm })\n        return algs as Collection<SignatureAlgorithm>\n    }\n\n    private static final Collection<KeyAlgorithm<PublicKey, PrivateKey>> asymKeyAlgs() {\n        def algs = Jwts.KEY.get().values()\n                .findAll({ it -> it.id.startsWith('R') || it.id.startsWith('E') })\n        return algs as Collection<KeyAlgorithm<PublicKey, PrivateKey>>\n    }\n\n    private static final Collection<SignatureAlgorithm> sigalgs = sigalgs()\n\n    private static final Collection<KeyAlgorithm<PublicKey, PrivateKey>> asymKeyAlgs = asymKeyAlgs()\n\n    @Test\n    void testSignWithPublicKey() {\n\n\n        for (def alg : sigalgs) {\n            def pair = TestKeys.forAlgorithm(alg).pair\n            try {\n                Jwts.builder().issuer('me').signWith(pair.public, alg).compact()\n                fail()\n            } catch (IllegalArgumentException expected) {\n                assertEquals DefaultJwtBuilder.PUB_KEY_SIGN_MSG, expected.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testVerifyWithPrivateKey() {\n        for (def alg : sigalgs) {\n            def pair = TestKeys.forAlgorithm(alg).pair\n            String jws = Jwts.builder().issuer('me').signWith(pair.private).compact()\n            try {\n                Jwts.parser().verifyWith(pair.private).build().parseSignedClaims(jws)\n                fail()\n            } catch (IllegalArgumentException expected) {\n                assertEquals DefaultJwtParser.PRIV_KEY_VERIFY_MSG, expected.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testVerifyWithKeyLocatorPrivateKey() {\n        for (def alg : sigalgs) {\n            def pair = TestKeys.forAlgorithm(alg).pair\n            String jws = Jwts.builder().issuer('me').signWith(pair.private).compact()\n            try {\n                Jwts.parser().keyLocator(new Locator<Key>() {\n                    @Override\n                    Key locate(Header header) {\n                        return pair.private\n                    }\n                })\n                        .build().parseSignedClaims(jws)\n                fail()\n            } catch (InvalidKeyException expected) {\n                assertEquals DefaultJwtParser.PRIV_KEY_VERIFY_MSG, expected.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testEncryptWithPrivateKey() {\n        for (def alg : asymKeyAlgs) {\n            try {\n                Jwts.builder().issuer('me').encryptWith(new TestPrivateKey(), alg, Jwts.ENC.A256GCM).compact()\n                fail()\n            } catch (IllegalArgumentException expected) {\n                assertEquals DefaultJwtBuilder.PRIV_KEY_ENC_MSG, expected.getMessage()\n            }\n        }\n    }\n\n    @Test\n    void testDecryptWithPublicKey() {\n        def pub = TestKeys.RS256.pair.public\n        String jwe = Jwts.builder().issuer('me').encryptWith(pub, Jwts.KEY.RSA1_5, Jwts.ENC.A256GCM).compact()\n        try {\n            Jwts.parser().decryptWith(new TestPublicKey()).build().parseEncryptedClaims(jwe)\n            fail()\n        } catch (IllegalArgumentException expected) {\n            assertEquals DefaultJwtParser.PUB_KEY_DECRYPT_MSG, expected.getMessage()\n        }\n    }\n\n    @Test\n    void testDecryptWithKeyLocatorPublicKey() {\n        def pub = TestKeys.RS256.pair.public\n        String jwe = Jwts.builder().issuer('me').encryptWith(pub, Jwts.KEY.RSA1_5, Jwts.ENC.A256GCM).compact()\n        try {\n            Jwts.parser().keyLocator(new Locator<Key>() {\n                @Override\n                Key locate(Header header) {\n                    return pub\n                }\n            })\n                    .build().parseEncryptedClaims(jwe)\n            fail()\n        } catch (InvalidKeyException expected) {\n            assertEquals DefaultJwtParser.PUB_KEY_DECRYPT_MSG, expected.getMessage()\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/issues/Issue438Test.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.issues\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.UnsupportedJwtException\nimport io.jsonwebtoken.impl.security.TestKeys\nimport org.junit.Test\n\n/**\n * https://github.com/jwtk/jjwt/issues/438\n */\nclass Issue438Test {\n\n    @Test(expected = UnsupportedJwtException /* not IllegalArgumentException */)\n    void testIssue438() {\n        String jws = Jwts.builder().issuer('test').signWith(TestKeys.RS256.pair.private).compact()\n        Jwts.parser().verifyWith(TestKeys.HS256).build().parseSignedClaims(jws)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/issues/Issue858Test.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.issues\n\nimport io.jsonwebtoken.Jwts\nimport org.junit.Test\n\nimport static org.junit.Assert.assertEquals\n\nclass Issue858Test {\n\n    @Test\n    void testEmptyAndNullEntries() {\n        def jwt = Jwts.builder()\n                .subject('Joe')\n                .claim('foo', '')       // empty allowed\n                .claim('list', [])            // empty allowed\n                .claim('map', [:])            // empty map allowed\n                .claim('another', null) // null not allowed (same behavior since <= 0.11.5), won't be added\n                .compact()\n\n        def claims = Jwts.parser().unsecured().build().parseUnsecuredClaims(jwt).getPayload()\n        assertEquals 4, claims.size()\n        assertEquals 'Joe', claims.getSubject()\n        assertEquals '', claims.get('foo')\n        assertEquals([], claims.get('list'))\n        assertEquals([:], claims.get('map'))\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/security/EncryptionAlgorithmsTest.groovy",
    "content": "/*\n * Copyright (C) 2018 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.io.Streams\nimport io.jsonwebtoken.impl.security.DefaultAeadRequest\nimport io.jsonwebtoken.impl.security.DefaultAeadResult\nimport io.jsonwebtoken.impl.security.DefaultDecryptAeadRequest\nimport io.jsonwebtoken.impl.security.GcmAesAeadAlgorithm\nimport io.jsonwebtoken.lang.Registry\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\n/**\n * Tests the {@link Jwts.ENC} implementation.\n *\n * @since 0.12.0\n */\nclass EncryptionAlgorithmsTest {\n\n    private static final String PLAINTEXT =\n            '''Bacon ipsum dolor amet venison beef pork chop, doner jowl pastrami ground round alcatra.\n               Beef leberkas filet mignon ball tip pork spare ribs kevin short loin ribeye ground round\n               biltong jerky short ribs corned beef. Strip steak turducken meatball porchetta beef ribs\n               shoulder pork belly doner salami corned beef kielbasa cow filet mignon drumstick. Bacon\n               tenderloin pancetta flank frankfurter ham kevin leberkas meatball turducken beef ribs.\n               Cupim short loin short ribs shankle tenderloin. Ham ribeye hamburger flank tenderloin\n               cupim t-bone, shank tri-tip venison salami sausage pancetta. Pork belly chuck salami\n               alcatra sirloin.\n\n               以ケ ホゥ婧詃 橎ちゅぬ蛣埣 禧ざしゃ蟨廩 椥䤥グ曣わ 基覧 滯っ䶧きょメ Ủ䧞以ケ妣 择禤槜谣お 姨のドゥ,\n               らボみょば䪩 苯礊觊ツュ婃 䩦ディふげセ げセりょ 禤槜 Ủ䧞以ケ妣 せがみゅちょ䰯 择禤槜谣お 難ゞ滧 蝥ちゃ,\n               滯っ䶧きょメ らボみょば䪩 礯みゃ楦と饥 椥䤥グ ウァ槚 訤をりゃしゑ びゃ驨も氩簥 栨キョ奎婨榞 ヌに楃 以ケ,\n               姚奊べ 椥䤥グ曣わ 栨キョ奎婨榞 ちょ䰯 Ủ䧞以ケ妣 誧姨のドゥろ よ苯礊 く涥, りゅぽ槞 馣ぢゃ尦䦎ぎ\n               大た䏩䰥ぐ 郎きや楺橯 䧎キェ, 難ゞ滧 栧择 谯䧟簨訧ぎょ 椥䤥グ曣わ'''\n\n    private static final byte[] PLAINTEXT_BYTES = PLAINTEXT.getBytes(\"UTF-8\")\n\n    private static final String AAD = 'You can get with this, or you can get with that'\n    private static final byte[] AAD_BYTES = AAD.getBytes(\"UTF-8\")\n\n    private static final Registry<String, AeadAlgorithm> registry = Jwts.ENC.get()\n\n    static boolean contains(AeadAlgorithm alg) {\n        return registry.containsValue(alg)\n    }\n\n    @Test\n    void testValues() {\n        assertEquals 6, registry.values().size()\n        assertTrue(contains(Jwts.ENC.A128CBC_HS256) &&\n                contains(Jwts.ENC.A192CBC_HS384) &&\n                contains(Jwts.ENC.A256CBC_HS512) &&\n                contains(Jwts.ENC.A128GCM) &&\n                contains(Jwts.ENC.A192GCM) &&\n                contains(Jwts.ENC.A256GCM)\n        )\n    }\n\n    @Test\n    void testForKey() {\n        for (AeadAlgorithm alg : registry.values()) {\n            assertSame alg, registry.forKey(alg.getId())\n        }\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testForIdWithInvalidId() {\n        //unlike the 'get' paradigm, 'key' requires the value to exist\n        registry.forKey('invalid')\n    }\n\n    @Test\n    void testGet() {\n        for (AeadAlgorithm alg : registry.values()) {\n            assertSame alg, registry.get(alg.getId())\n        }\n    }\n\n    @Test\n    void testGetWithInvalidId() {\n        // 'get' paradigm can return null if not found\n        assertNull registry.get('invalid')\n    }\n\n    @Test\n    void testWithoutAad() {\n\n        for (AeadAlgorithm alg : registry.values()) {\n\n            def key = alg.key().build()\n\n            def out = new ByteArrayOutputStream()\n            def request = new DefaultAeadRequest(Streams.of(PLAINTEXT_BYTES), null, null, key, null)\n            def result = new DefaultAeadResult(out)\n\n            alg.encrypt(request, result)\n            byte[] iv = result.getIv()\n            byte[] tag = result.getDigest() //there is always a tag, even if there is no AAD\n            assertNotNull tag\n\n            byte[] ciphertextBytes = out.toByteArray()\n\n            //AES GCM always results in ciphertext the same length as the plaintext:\n            if (alg instanceof GcmAesAeadAlgorithm) {\n                assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)\n            }\n\n            def ciphertext = Streams.of(ciphertextBytes)\n            out = new ByteArrayOutputStream(8192)\n            def dreq = new DefaultDecryptAeadRequest(ciphertext, key, null, iv, tag)\n            alg.decrypt(dreq, out)\n            byte[] decryptedPlaintextBytes = out.toByteArray()\n\n            assertArrayEquals(PLAINTEXT_BYTES, decryptedPlaintextBytes)\n        }\n    }\n\n    @Test\n    void testWithAad() {\n\n        for (AeadAlgorithm alg : registry.values()) {\n\n            def key = alg.key().build()\n\n            def plaintextIn = Streams.of(PLAINTEXT_BYTES)\n            def out = new ByteArrayOutputStream(8192)\n            def aad = Streams.of(AAD_BYTES)\n            def req = new DefaultAeadRequest(plaintextIn, null, null, key, aad)\n            def res = new DefaultAeadResult(out)\n\n            alg.encrypt(req, res)\n            byte[] iv = res.getIv()\n            byte[] tag = res.getDigest()\n            byte[] ciphertextBytes = out.toByteArray()\n            Streams.reset(aad)\n\n            //AES GCM always results in ciphertext the same length as the plaintext:\n            if (alg instanceof GcmAesAeadAlgorithm) {\n                assertEquals(ciphertextBytes.length, PLAINTEXT_BYTES.length)\n            }\n\n            def ciphertext = Streams.of(ciphertextBytes)\n            out = new ByteArrayOutputStream(8192)\n            def dreq = new DefaultDecryptAeadRequest(ciphertext, key, aad, iv, tag)\n            alg.decrypt(dreq, out)\n            byte[] decryptedPlaintextBytes = out.toByteArray()\n            assertArrayEquals(PLAINTEXT_BYTES, decryptedPlaintextBytes)\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/security/JwksCRVTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport io.jsonwebtoken.impl.security.ECCurve\nimport io.jsonwebtoken.impl.security.EdwardsCurve\nimport io.jsonwebtoken.impl.security.StandardCurves\nimport org.junit.Test\n\nimport static org.junit.Assert.assertSame\nimport static org.junit.Assert.assertTrue\n\nclass JwksCRVTest {\n\n    @Test\n    void testRegistry() {\n        assertTrue Jwks.CRV.get() instanceof StandardCurves\n    }\n\n    @Test\n    void testInstances() {\n        assertSame ECCurve.P256, Jwks.CRV.P256\n        assertSame ECCurve.P384, Jwks.CRV.P384\n        assertSame ECCurve.P521, Jwks.CRV.P521\n        assertSame EdwardsCurve.X25519, Jwks.CRV.X25519\n        assertSame EdwardsCurve.X448, Jwks.CRV.X448\n        assertSame EdwardsCurve.Ed25519, Jwks.CRV.Ed25519\n        assertSame EdwardsCurve.Ed448, Jwks.CRV.Ed448\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/security/JwksOPTest.groovy",
    "content": "/*\n * Copyright © 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport io.jsonwebtoken.impl.security.StandardKeyOperations\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass JwksOPTest {\n\n    @Test\n    void testRegistry() {\n        assertTrue Jwks.OP.get() instanceof StandardKeyOperations\n    }\n\n    static void testInstance(KeyOperation op, String id, String description, KeyOperation related) {\n        assertEquals id, op.getId()\n        assertEquals description, op.getDescription()\n        if (related) {\n            assertTrue op.isRelated(related)\n        }\n        assertEquals id.hashCode(), op.hashCode()\n        assertEquals \"'$id' ($description)\" as String, op.toString()\n        assertTrue op.equals(op)\n        assertTrue op.is(op)\n        assertTrue op == op\n        assertEquals op, Jwks.OP.get().get(id)\n        assertSame op, Jwks.OP.get().get(id)\n    }\n\n    @Test\n    void testInstances() {\n        testInstance(Jwks.OP.SIGN, 'sign', 'Compute digital signature or MAC', Jwks.OP.VERIFY)\n        testInstance(Jwks.OP.VERIFY, 'verify', 'Verify digital signature or MAC', Jwks.OP.SIGN)\n        testInstance(Jwks.OP.ENCRYPT, 'encrypt', 'Encrypt content', Jwks.OP.DECRYPT)\n        testInstance(Jwks.OP.DECRYPT, 'decrypt', 'Decrypt content and validate decryption, if applicable', Jwks.OP.ENCRYPT)\n        testInstance(Jwks.OP.WRAP_KEY, 'wrapKey', 'Encrypt key', Jwks.OP.UNWRAP_KEY)\n        testInstance(Jwks.OP.UNWRAP_KEY, 'unwrapKey', 'Decrypt key and validate decryption, if applicable', Jwks.OP.WRAP_KEY)\n\n        testInstance(Jwks.OP.DERIVE_KEY, 'deriveKey', 'Derive key', null)\n        assertFalse Jwks.OP.DERIVE_KEY.isRelated(Jwks.OP.DERIVE_BITS)\n\n        testInstance(Jwks.OP.DERIVE_BITS, 'deriveBits', 'Derive bits not to be used as a key', null)\n        assertFalse Jwks.OP.DERIVE_BITS.isRelated(Jwks.OP.DERIVE_KEY)\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/security/KeyAlgorithmsTest.groovy",
    "content": "/*\n * Copyright (C) 2020 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport io.jsonwebtoken.Jwts\nimport org.junit.Test\n\nimport java.security.Key\n\nimport static org.junit.Assert.*\n\n/**\n * Tests {@link Jwts.KEY} values.\n *\n * @since 0.12.0\n */\nclass KeyAlgorithmsTest {\n\n    static boolean contains(KeyAlgorithm<? extends Key, ? extends Key> alg) {\n        return Jwts.KEY.get().values().contains(alg)\n    }\n\n    @Test\n    void testValues() {\n        assertEquals 17, Jwts.KEY.get().values().size()\n        assertTrue(contains(Jwts.KEY.DIRECT) &&\n                contains(Jwts.KEY.A128KW) &&\n                contains(Jwts.KEY.A192KW) &&\n                contains(Jwts.KEY.A256KW) &&\n                contains(Jwts.KEY.A128GCMKW) &&\n                contains(Jwts.KEY.A192GCMKW) &&\n                contains(Jwts.KEY.A256GCMKW) &&\n                contains(Jwts.KEY.PBES2_HS256_A128KW) &&\n                contains(Jwts.KEY.PBES2_HS384_A192KW) &&\n                contains(Jwts.KEY.PBES2_HS512_A256KW) &&\n                contains(Jwts.KEY.RSA1_5) &&\n                contains(Jwts.KEY.RSA_OAEP) &&\n                contains(Jwts.KEY.RSA_OAEP_256) &&\n                contains(Jwts.KEY.ECDH_ES) &&\n                contains(Jwts.KEY.ECDH_ES_A128KW) &&\n                contains(Jwts.KEY.ECDH_ES_A192KW) &&\n                contains(Jwts.KEY.ECDH_ES_A256KW)\n        )\n    }\n\n    @Test\n    void testForKey() {\n        for (KeyAlgorithm alg : Jwts.KEY.get().values()) {\n            assertSame alg, Jwts.KEY.get().forKey(alg.getId())\n        }\n    }\n\n    @Test(expected = IllegalArgumentException)\n    void testForKeyWithInvalidId() {\n        //unlike the 'get' paradigm, 'key' requires the value to exist\n        Jwts.KEY.get().forKey('invalid')\n    }\n\n    @Test\n    void testGet() {\n        for (KeyAlgorithm alg : Jwts.KEY.get().values()) {\n            assertSame alg, Jwts.KEY.get().get(alg.getId())\n        }\n    }\n\n    @Test\n    void testGetWithInvalidId() {\n        // 'get' paradigm can return null if not found\n        assertNull Jwts.KEY.get().get('invalid')\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/security/KeysImplTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport io.jsonwebtoken.SignatureAlgorithm\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport java.security.KeyPair\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\nclass KeysImplTest {\n\n    @Test\n    void testPrivateCtor() { //for code coverage purposes only\n        new Keys()\n    }\n\n    @Test\n    void testSecretKeyFor() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n\n            String name = alg.name()\n\n            if (alg.isHmac()) {\n                SecretKey key = Keys.secretKeyFor(alg)\n                assertEquals alg.minKeyLength, key.getEncoded().length * 8 //convert byte count to bit count\n                assertEquals alg.jcaName, key.algorithm\n                alg.assertValidSigningKey(key)\n                alg.assertValidVerificationKey(key)\n                assertEquals alg, SignatureAlgorithm.forSigningKey(key) // https://github.com/jwtk/jjwt/issues/381\n            } else {\n                try {\n                    Keys.secretKeyFor(alg)\n                    fail()\n                } catch (IllegalArgumentException expected) {\n                    assertEquals \"The $name algorithm does not support shared secret keys.\" as String, expected.message\n                }\n\n            }\n        }\n    }\n\n    @Test\n    void testKeyPairFor() {\n\n        for (SignatureAlgorithm alg : SignatureAlgorithm.values()) {\n\n            String name = alg.name()\n\n            if (alg.isRsa()) {\n\n                KeyPair pair = Keys.keyPairFor(alg)\n                assertNotNull pair\n\n                PublicKey pub = pair.getPublic()\n                assert pub instanceof RSAPublicKey\n                def keyAlgName = alg.jcaName.equals(\"RSASSA-PSS\") ? \"RSASSA-PSS\" : alg.familyName\n                assertEquals keyAlgName, pub.algorithm\n                assertEquals alg.digestLength * 8, pub.modulus.bitLength()\n\n                PrivateKey priv = pair.getPrivate()\n                assert priv instanceof RSAPrivateKey\n                assertEquals keyAlgName, priv.algorithm\n                assertEquals alg.digestLength * 8, priv.modulus.bitLength()\n\n            } else if (alg.isEllipticCurve()) {\n\n                KeyPair pair = Keys.keyPairFor(alg);\n                assertNotNull pair\n\n                int len = alg.minKeyLength\n                String asn1oid = \"secp${len}r1\"\n                String suffix = len == 256 ? \", X9.62 prime${len}v1\" : '' //the JDK only adds this extra suffix to the secp256r1 curve name and not secp384r1 or secp521r1 curve names\n                String jdkParamName = \"$asn1oid [NIST P-${len}${suffix}]\" as String\n\n                PublicKey pub = pair.getPublic()\n                assert pub instanceof ECPublicKey\n                assertEquals \"EC\", pub.algorithm\n                if (pub.params.hasProperty('name')) { // JDK <= 14\n                    assertEquals jdkParamName, pub.params.name\n                } else { // JDK >= 15\n                    assertEquals asn1oid, pub.params.nameAndAliases[0]\n                }\n                assertEquals alg.minKeyLength, pub.params.order.bitLength()\n\n                PrivateKey priv = pair.getPrivate()\n                assert priv instanceof ECPrivateKey\n                assertEquals \"EC\", priv.algorithm\n                if (pub.params.hasProperty('name')) { // JDK <= 14\n                    assertEquals jdkParamName, priv.params.name\n                } else { // JDK >= 15\n                    assertEquals asn1oid, priv.params.nameAndAliases[0]\n                }\n                assertEquals alg.minKeyLength, priv.params.order.bitLength()\n\n            } else {\n                try {\n                    Keys.keyPairFor(alg)\n                    fail()\n                } catch (IllegalArgumentException expected) {\n                    assertEquals \"The $name algorithm does not support Key Pairs.\" as String, expected.message\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/security/KeysTest.groovy",
    "content": "/*\n * Copyright (C) 2014 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n//file:noinspection GrDeprecatedAPIUsage\npackage io.jsonwebtoken.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.impl.DefaultJwtBuilder\nimport io.jsonwebtoken.impl.lang.Bytes\nimport io.jsonwebtoken.impl.security.*\nimport org.junit.Test\n\nimport javax.crypto.SecretKey\nimport javax.crypto.spec.SecretKeySpec\nimport java.security.KeyPair\nimport java.security.PrivateKey\nimport java.security.PublicKey\nimport java.security.SecureRandom\nimport java.security.interfaces.ECPrivateKey\nimport java.security.interfaces.ECPublicKey\nimport java.security.interfaces.RSAPrivateKey\nimport java.security.interfaces.RSAPublicKey\n\nimport static org.junit.Assert.*\n\n@SuppressWarnings('GroovyAccessibility')\nclass KeysTest {\n\n    private static final Random RANDOM = new SecureRandom()\n\n    static byte[] bytes(int sizeInBits) {\n        byte[] bytes = new byte[sizeInBits / Byte.SIZE]\n        RANDOM.nextBytes(bytes)\n        return bytes\n    }\n\n    @Test\n    void testPrivateCtor() { //for code coverage purposes only\n        //noinspection GroovyResultOfObjectAllocationIgnored\n        new Keys()\n        new KeysBridge()\n    }\n\n    @Test\n    void testHmacShaKeyForWithNullArgument() {\n        try {\n            Keys.hmacShaKeyFor(null)\n        } catch (InvalidKeyException expected) {\n            assertEquals 'SecretKey byte array cannot be null.', expected.message\n        }\n    }\n\n    @Test\n    void testHmacShaKeyForWithWeakKey() {\n        int numBytes = 31\n        int numBits = numBytes * 8\n        try {\n            Keys.hmacShaKeyFor(new byte[numBytes])\n        } catch (WeakKeyException expected) {\n            assertEquals \"The specified key byte array is \" + numBits + \" bits which \" +\n                    \"is not secure enough for any JWT HMAC-SHA algorithm.  The JWT \" +\n                    \"JWA Specification (RFC 7518, Section 3.2) states that keys used with HMAC-SHA algorithms MUST have a \" +\n                    \"size >= 256 bits (the key size must be greater than or equal to the hash \" +\n                    \"output size).  Consider using the Jwts.SIG.HS256.key() builder (or \" +\n                    \"HS384.key() or HS512.key()) to create a key guaranteed to be secure enough \" +\n                    \"for your preferred HMAC-SHA algorithm.  See \" +\n                    \"https://tools.ietf.org/html/rfc7518#section-3.2 for more information.\" as String, expected.message\n        }\n    }\n\n    @Test\n    void testHmacShaWithValidSizes() {\n        for (int i : [256, 384, 512]) {\n            byte[] bytes = bytes(i)\n            def key = Keys.hmacShaKeyFor(bytes)\n            assertTrue key instanceof SecretKeySpec\n            assertEquals \"HmacSHA$i\" as String, key.getAlgorithm()\n            assertTrue Arrays.equals(bytes, key.getEncoded())\n        }\n    }\n\n    @Test\n    void testHmacShaLargerThan512() {\n        def key = Keys.hmacShaKeyFor(bytes(520))\n        assertTrue key instanceof SecretKeySpec\n        assertEquals 'HmacSHA512', key.getAlgorithm()\n        assertTrue key.getEncoded().length * Byte.SIZE >= 512\n    }\n\n    @Test\n    @Deprecated\n    void testDeprecatedSecretKeyFor() {\n\n        for (io.jsonwebtoken.SignatureAlgorithm alg : io.jsonwebtoken.SignatureAlgorithm.values()) {\n\n            String name = alg.name()\n\n            if (alg.isHmac()) {\n                SecretKey key = Keys.secretKeyFor(alg)\n                assertEquals alg.minKeyLength, key.getEncoded().length * 8 //convert byte count to bit count\n                assertEquals alg.jcaName, key.algorithm\n                alg.assertValidSigningKey(key)\n                alg.assertValidVerificationKey(key)\n                assertEquals alg, io.jsonwebtoken.SignatureAlgorithm.forSigningKey(key)\n                // https://github.com/jwtk/jjwt/issues/381\n            } else {\n                try {\n                    Keys.secretKeyFor(alg)\n                    fail()\n                } catch (IllegalArgumentException expected) {\n                    assertEquals \"The $name algorithm does not support shared secret keys.\" as String, expected.message\n                }\n\n            }\n        }\n    }\n\n    @Test\n    void testSecretKeyFor() {\n        for (SecureDigestAlgorithm alg : Jwts.SIG.get().values()) {\n            if (alg instanceof MacAlgorithm) {\n                SecretKey key = alg.key().build()\n                assertEquals alg.getKeyBitLength(), Bytes.bitLength(key.getEncoded())\n                assertEquals alg.jcaName, key.algorithm\n                assertEquals alg, DefaultJwtBuilder.forSigningKey(key) // https://github.com/jwtk/jjwt/issues/381\n            }\n        }\n    }\n\n    @Test\n    @Deprecated\n    void testDeprecatedKeyPairFor() {\n\n        for (io.jsonwebtoken.SignatureAlgorithm alg : io.jsonwebtoken.SignatureAlgorithm.values()) {\n\n            String name = alg.name()\n\n            if (alg.isRsa()) {\n\n                KeyPair pair = Keys.keyPairFor(alg)\n                assertNotNull pair\n\n                PublicKey pub = pair.getPublic()\n                assert pub instanceof RSAPublicKey\n                def keyAlgName = alg.jcaName.equals(\"RSASSA-PSS\") ? \"RSASSA-PSS\" : alg.familyName\n                assertEquals keyAlgName, pub.algorithm\n                assertEquals alg.digestLength * 8, pub.modulus.bitLength()\n\n                PrivateKey priv = pair.getPrivate()\n                assert priv instanceof RSAPrivateKey\n                assertEquals keyAlgName, priv.algorithm\n                assertEquals alg.digestLength * 8, priv.modulus.bitLength()\n\n            } else if (alg.isEllipticCurve()) {\n\n                KeyPair pair = Keys.keyPairFor(alg)\n                assertNotNull pair\n\n                int len = alg.minKeyLength\n                String asn1oid = \"secp${len}r1\"\n                String suffix = len == 256 ? \", X9.62 prime${len}v1\" : ''\n                //the JDK only adds this extra suffix to the secp256r1 curve name and not secp384r1 or secp521r1 curve names\n                String jdkParamName = \"$asn1oid [NIST P-${len}${suffix}]\" as String\n\n                PublicKey pub = pair.getPublic()\n                assert pub instanceof ECPublicKey\n                assertEquals \"EC\", pub.algorithm\n                if (pub.params.hasProperty('name')) { // JDK <= 14\n                    assertEquals jdkParamName, pub.params.name\n                } else { // JDK >= 15\n                    assertEquals asn1oid, pub.params.nameAndAliases[0]\n                }\n                assertEquals alg.minKeyLength, pub.params.order.bitLength()\n\n                PrivateKey priv = pair.getPrivate()\n                assert priv instanceof ECPrivateKey\n                assertEquals \"EC\", priv.algorithm\n                if (priv.params.hasProperty('name')) { // JDK <= 14\n                    assertEquals jdkParamName, priv.params.name\n                } else { // JDK >= 15\n                    assertEquals asn1oid, priv.params.nameAndAliases[0]\n                }\n                assertEquals alg.minKeyLength, priv.params.order.bitLength()\n\n            } else {\n                try {\n                    Keys.keyPairFor(alg)\n                    fail()\n                } catch (IllegalArgumentException expected) {\n                    assertEquals \"The $name algorithm does not support Key Pairs.\" as String, expected.message\n                }\n            }\n        }\n    }\n\n    @Test\n    void testKeyPairBuilder() {\n\n        Collection<SignatureAlgorithm> algs = Jwts.SIG.get().values()\n                .findAll({ it instanceof KeyPairBuilderSupplier }) as Collection<SignatureAlgorithm>\n\n        for (SignatureAlgorithm alg : algs) {\n\n            String id = alg.getId()\n\n            if (id.startsWith(\"RS\") || id.startsWith(\"PS\")) {\n\n                def pair = alg.keyPair().build()\n                assertNotNull pair\n\n                PublicKey pub = pair.getPublic()\n                assert pub instanceof RSAPublicKey\n                assertEquals alg.preferredKeyBitLength, pub.modulus.bitLength()\n\n                PrivateKey priv = pair.getPrivate()\n                assert priv instanceof RSAPrivateKey\n                assertEquals alg.preferredKeyBitLength, priv.modulus.bitLength()\n\n            } else if (id == \"EdDSA\") {\n\n                def pair = alg.keyPair().build()\n                assertNotNull pair\n\n                PublicKey pub = pair.getPublic()\n                assert pub instanceof PublicKey\n                assertTrue EdwardsCurve.isEdwards(pub)\n\n                PrivateKey priv = pair.getPrivate()\n                assert priv instanceof PrivateKey\n                assertTrue EdwardsCurve.isEdwards(priv)\n\n            } else if (id.startsWith(\"ES\")) {\n\n                def pair = alg.keyPair().build()\n                assertNotNull pair\n\n                int len = alg.orderBitLength\n                String asn1oid = \"secp${len}r1\"\n                String suffix = len == 256 ? \", X9.62 prime${len}v1\" : ''\n                //the JDK only adds this extra suffix to the secp256r1 curve name and not secp384r1 or secp521r1 curve names\n                String jdkParamName = \"$asn1oid [NIST P-${len}${suffix}]\" as String\n\n                PublicKey pub = pair.getPublic()\n                assert pub instanceof ECPublicKey\n                assertEquals \"EC\", pub.algorithm\n                if (pub.params.hasProperty('name')) { // JDK <= 14\n                    assertEquals jdkParamName, pub.params.name\n                } else { // JDK >= 15\n                    assertEquals asn1oid, pub.params.nameAndAliases[0]\n                }\n                assertEquals alg.orderBitLength, pub.params.order.bitLength()\n\n                PrivateKey priv = pair.getPrivate()\n                assert priv instanceof ECPrivateKey\n                assertEquals \"EC\", priv.algorithm\n                if (priv.params.hasProperty('name')) { // JDK <= 14\n                    assertEquals jdkParamName, priv.params.name\n                } else { // JDK >= 15\n                    assertEquals asn1oid, priv.params.nameAndAliases[0]\n                }\n                assertEquals alg.orderBitLength, priv.params.order.bitLength()\n\n            } else {\n                // unexpected algorithm that is not accounted for in this test:\n                fail()\n            }\n        }\n    }\n\n    @Test\n    void testForPassword() {\n        def password = \"whatever\".toCharArray()\n        Password key = Keys.password(password)\n        assertArrayEquals password, key.toCharArray()\n        assertTrue key instanceof PasswordSpec\n    }\n\n    @Test\n    void testAssociateWithECKey() {\n        def priv = new TestPrivateKey(algorithm: 'EC')\n        def pub = TestKeys.ES256.pair.public as ECPublicKey\n        def result = Keys.builder(priv).publicKey(pub).build()\n        assertTrue result instanceof PrivateECKey\n        def key = result as PrivateECKey\n        assertSame priv, key.getKey()\n        assertSame pub.getParams(), key.getParams()\n    }\n\n    @Test\n    void testAssociateWithKeyThatDoesntNeedToBeWrapped() {\n        def pair = TestKeys.RS256.pair\n        assertSame pair.private, Keys.builder(pair.private).publicKey(pair.public).build()\n    }\n}\n"
  },
  {
    "path": "impl/src/test/groovy/io/jsonwebtoken/security/StandardAlgorithmsTest.groovy",
    "content": "/*\n * Copyright (C) 2023 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.security\n\nimport io.jsonwebtoken.Jwts\nimport io.jsonwebtoken.lang.Registry\nimport org.junit.Test\n\nimport static org.junit.Assert.*\n\nclass StandardAlgorithmsTest {\n\n    static final List<Registry<String,?>> registries = [Jwts.SIG.get(), Jwts.ENC.get(), Jwts.KEY.get(), Jwts.ZIP.get(), Jwks.HASH.get()]\n\n    private static void eachRegAlg(Closure c) {\n        registries.each { reg -> reg.values().each { c(reg, it) } }\n    }\n\n    @Test\n    void testSize() {\n        assertEquals 14, Jwts.SIG.get().size()\n        assertEquals 6, Jwts.ENC.get().size()\n        assertEquals 17, Jwts.KEY.get().size()\n        assertEquals 2, Jwts.ZIP.get().size()\n        assertEquals 6, Jwks.HASH.get().size()\n    }\n\n    @Test\n    void testForKey() {\n        eachRegAlg { reg, alg ->\n            assertSame alg, reg.forKey(alg.getId())\n        }\n    }\n\n    @Test\n    void testForKeyWithInvalidId() {\n        //unlike the 'get' paradigm, 'forKey' requires the value to exist\n        registries.each { reg ->\n            //noinspection GroovyUnusedCatchParameter\n            try {\n                reg.forKey('invalid')\n                fail()\n            } catch (IllegalArgumentException expected) {\n            }\n        }\n    }\n\n    @Test\n    void testGet() {\n        eachRegAlg { reg, alg ->\n            assertSame alg, reg.get(alg.getId())\n        }\n    }\n\n    @Test\n    void testGetWithInvalidId() {\n        // 'get' paradigm can return null if not found\n        registries.each { reg ->\n            assertNull reg.get('invalid')\n        }\n    }\n\n    @SuppressWarnings('GroovyUnusedCatchParameter')\n    @Test\n    void testGetWithoutStringKey() {\n        registries.each { reg ->\n            try {\n                assertNull reg.get(2) // not a string, should fail\n                fail()\n            } catch (ClassCastException expected) { // allowed per Map#get contract\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "impl/src/test/resources/META-INF/services/io.jsonwebtoken.StubService",
    "content": "io.jsonwebtoken.impl.DefaultStubService"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES256.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIICHDCCAcOgAwIBAgIUQ2ayZDtMg0Ki2SGbqCHSkJryuCQwCgYIKoZIzj0EAwIw\nYzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh\nbiBGcmFuY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwE\namp3dDAgFw0yMzA4MjkwMDEzMDNaGA8zMDIzMDkwNjAwMTMwM1owYzELMAkGA1UE\nBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lz\nY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwEamp3dDBZMBMG\nByqGSM49AgEGCCqGSM49AwEHA0IABGVhex0Lsz6Fvx2q36JolPhwjBCyePOiM7JE\n1y+X8LsuHf3WL/YAGj1XCSa5HSIAFsItY+SQNjRb1TdKQFEb3oWjUzBRMB0GA1Ud\nDgQWBBSVudCfjvNUFN7gNYzbVHBFPF7QRjAfBgNVHSMEGDAWgBSVudCfjvNUFN7g\nNYzbVHBFPF7QRjAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0cAMEQCICJ4\nYcDTZf6r2BTRb4CStJ2SQxCSbZgDEqzkk+Dg26ENAiBEXdHd3b24W3XAi8k4xRPm\nX0hYNTq7HzGzIcZ9zGiZpQ==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES256.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKAne0CF1mPPdITlu\nblG8ROELgOEd5LuqxJ6wDt09yfihRANCAARlYXsdC7M+hb8dqt+iaJT4cIwQsnjz\nojOyRNcvl/C7Lh391i/2ABo9VwkmuR0iABbCLWPkkDY0W9U3SkBRG96F\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES256.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZWF7HQuzPoW/HarfomiU+HCMELJ4\n86IzskTXL5fwuy4d/dYv9gAaPVcJJrkdIgAWwi1j5JA2NFvVN0pAURvehQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES384.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIICWjCCAeCgAwIBAgIUZSvFvN9Rb/hrRtLbAGhs9VaYqu8wCgYIKoZIzj0EAwIw\nYzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh\nbiBGcmFuY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwE\namp3dDAgFw0yMzA4MjkwMDEzMDNaGA8zMDIzMDkwNjAwMTMwM1owYzELMAkGA1UE\nBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lz\nY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwEamp3dDB2MBAG\nByqGSM49AgEGBSuBBAAiA2IABGh1HNAppC6FoHwnH/1os4qRzZEElOYQqMVsHwL9\nPbth+ksVsrr7eH9sqQALb/jl+4y6i+kE4Fz8UDoDIq5iaATmYo/fUn4FW2rVSPLz\nATr5vcNRfh1xi8g3P4sRvwlrv6NTMFEwHQYDVR0OBBYEFB6hjC+CesR+Jl/3YTLi\n5ZL8M/HkMB8GA1UdIwQYMBaAFB6hjC+CesR+Jl/3YTLi5ZL8M/HkMA8GA1UdEwEB\n/wQFMAMBAf8wCgYIKoZIzj0EAwIDaAAwZQIwAIhLJaUW/JM5QCPW0GTkTTQDwVHE\ngoaT3egMvn7vD908lrpdNozeGjY5Mh3HaZCxAjEAgJ7SL8mp7HA+LQQMX03+ImJC\nqsPtciVu50U4cdEXnDj7T2cbRRVC1WWrNRGZ0qDv\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES384.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDCyzsm+32MR1A+yoWSp\nu+VE4NxuICamQ2snfJt0F5dAYJtQMRT1xswLuhDKE0BQPWKhZANiAARodRzQKaQu\nhaB8Jx/9aLOKkc2RBJTmEKjFbB8C/T27YfpLFbK6+3h/bKkAC2/45fuMuovpBOBc\n/FA6AyKuYmgE5mKP31J+BVtq1Ujy8wE6+b3DUX4dcYvINz+LEb8Ja78=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES384.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEaHUc0CmkLoWgfCcf/WizipHNkQSU5hCo\nxWwfAv09u2H6SxWyuvt4f2ypAAtv+OX7jLqL6QTgXPxQOgMirmJoBOZij99SfgVb\natVI8vMBOvm9w1F+HXGLyDc/ixG/CWu/\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES512.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIICpTCCAgagAwIBAgIUXQGIAhM4A/cmccKgugyIuJqlEMQwCgYIKoZIzj0EAwIw\nYzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNh\nbiBGcmFuY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwE\namp3dDAgFw0yMzA4MjkwMDEzMDNaGA8zMDIzMDkwNjAwMTMwM1owYzELMAkGA1UE\nBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFuY2lz\nY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwEamp3dDCBmzAQ\nBgcqhkjOPQIBBgUrgQQAIwOBhgAEAKPbC355nVH+ITyxuq+Io09QFcvXUU7SUwFi\nXGWwTtlZg9hIaEt1hJe3mwshF/hvhNkIyg558ZVdKFmEXCSzGgwqADAHWCd4gV14\nM1WSbFSsrfu/ddTQ6zoVIONlr/rodTs5yHh/Uoe7kzQ/ny85YIvLFxWnn8InuzBt\nlbNeGwttWCGYo1MwUTAdBgNVHQ4EFgQU87TESp0xdY96hLC5vxucb6bK4j0wHwYD\nVR0jBBgwFoAU87TESp0xdY96hLC5vxucb6bK4j0wDwYDVR0TAQH/BAUwAwEB/zAK\nBggqhkjOPQQDAgOBjAAwgYgCQgH+B3yHa3q9+HzRq+tL5yU89nAhD1ADx1koB0bv\nPIrtdnMJ1Ei2A4W2s0xcCuRwPCTIE/ZYO1UZZhNd5h5qfEyUrQJCAaEFMdDPDrKi\ng5z35SZcEQjSMkKwBXMQrC9W4MmJRoHXmdlrra4u2VSB0x3N7s2HXJZdIp5ffLyl\nkr4SflbOQAX5\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES512.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAKu6PEna7zQnJKAJm\npX1BXxq5RQRb6DiAzZA8Pp/GE19c3k/gnXH5GME9/uQUIa2VUZ6Yahuhufg+JvFI\nD46x9IqhgYkDgYYABACj2wt+eZ1R/iE8sbqviKNPUBXL11FO0lMBYlxlsE7ZWYPY\nSGhLdYSXt5sLIRf4b4TZCMoOefGVXShZhFwksxoMKgAwB1gneIFdeDNVkmxUrK37\nv3XU0Os6FSDjZa/66HU7Och4f1KHu5M0P58vOWCLyxcVp5/CJ7swbZWzXhsLbVgh\nmA==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/ES512.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQAo9sLfnmdUf4hPLG6r4ijT1AVy9dR\nTtJTAWJcZbBO2VmD2EhoS3WEl7ebCyEX+G+E2QjKDnnxlV0oWYRcJLMaDCoAMAdY\nJ3iBXXgzVZJsVKyt+7911NDrOhUg42Wv+uh1OznIeH9Sh7uTND+fLzlgi8sXFaef\nwie7MG2Vs14bC21YIZg=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/Ed25519.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIB3TCCAY+gAwIBAgIUTvomSDbNXFaCzfJQTH1PHWe+GlIwBQYDK2VwMGMxCzAJ\nBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJh\nbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTALBgNVBAsMBGpqd3Qw\nIBcNMjMwODI5MDAxMzAzWhgPMzAyMzA5MDYwMDEzMDNaMGMxCzAJBgNVBAYTAlVT\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRgw\nFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTALBgNVBAsMBGpqd3QwKjAFBgMrZXAD\nIQDDHxTyZqXnvSZdmS5a4Gu/X0EPcqMfwIP6mI3rm/ob6qNTMFEwHQYDVR0OBBYE\nFGTVTDvYvGLCDAqD60GqtHOwqF+SMB8GA1UdIwQYMBaAFGTVTDvYvGLCDAqD60Gq\ntHOwqF+SMA8GA1UdEwEB/wQFMAMBAf8wBQYDK2VwA0EA39fQxxQwcTABeUUD4jFQ\n1Ypn6CACuZI7B2LZqBvylVcLCdENA1UNI1fZc+3akDluo9mM8502pJz43YPBHggP\nDw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/Ed25519.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VwBCIEIMOwpslwByhdqA3jP8cPDbAvYiYNU0r8TmIpqo87rXy3\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/Ed25519.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAwx8U8mal570mXZkuWuBrv19BD3KjH8CD+piN65v6G+o=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/Ed448.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIICKDCCAaigAwIBAgIUHYqLFUtAL/bTcNZVKa3Ec/NtuF0wBQYDK2VxMGMxCzAJ\nBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJh\nbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTALBgNVBAsMBGpqd3Qw\nIBcNMjMwODI5MDAxMzAzWhgPMzAyMzA5MDYwMDEzMDNaMGMxCzAJBgNVBAYTAlVT\nMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4gRnJhbmNpc2NvMRgw\nFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTALBgNVBAsMBGpqd3QwQzAFBgMrZXED\nOgCoHk4Amky987uTdM3OqYjhfF19lBnJc6BZFV0iTtdq0JPDyvb9T3kwQOEUsJPg\nTh5bio1tWos6uoCjUzBRMB0GA1UdDgQWBBRkKJWakPyQ43rlJ3e1g2eIuaV1PzAf\nBgNVHSMEGDAWgBRkKJWakPyQ43rlJ3e1g2eIuaV1PzAPBgNVHRMBAf8EBTADAQH/\nMAUGAytlcQNzAMi2RfC+D+qv4MjN83863Y/x9uIBIQ5yPxqC1Wpg2Eh5gcl8BSHK\n0CkWCWL1H2g/nwxgg+Zx47sMAPETrMp8oTE4ohDmRYG+K7B5mOUrjU22UxjbSP55\nRWUSu7iaetPHNxUNYM9U7WZmebz2meI441EVAA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/Ed448.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMEcCAQAwBQYDK2VxBDsEOdkMuoGEPRrYF+JkBH30MRnluPYscGnG2b4+HJUswsh9\nlLqmeQFwmwtxeKxY38ItkfJKujQ3Ff5gOQ==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/Ed448.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMEMwBQYDK2VxAzoAqB5OAJpMvfO7k3TNzqmI4XxdfZQZyXOgWRVdIk7XatCTw8r2\n/U95MEDhFLCT4E4eW4qNbVqLOrqA\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS256.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIERTCCAvmgAwIBAgIUYgFGJuaMZbB82JXRsRA7urh+Np4wQQYJKoZIhvcNAQEK\nMDSgDzANBglghkgBZQMEAgEFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgEF\nAKIDAgEgMGMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD\nVQQHDA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTAL\nBgNVBAsMBGpqd3QwIBcNMjMwODI5MDAxMzAyWhgPMzAyMzA5MDYwMDEzMDJaMGMx\nCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4g\nRnJhbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTALBgNVBAsMBGpq\nd3QwggFWMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIBBQChHDAaBgkqhkiG\n9w0BAQgwDQYJYIZIAWUDBAIBBQCiAwIBIAOCAQ8AMIIBCgKCAQEA7rlL4t7gFcls\nS/LwPA2oYiBJ66zIcFxU+GoibEWeaeHJDWigfw3gxrDEK4OSHZjqRXYCvQykrKxX\nf7YoHXpDZhjlM3wevYQK9q9rzcII65IkLjMKXD7OoEcph7W6n1VQOyNB3XJtGGtf\nIjD74+/kF2FKTqpTugERrZ790UNLm/vHEPY9qP8VmYvfLzroimDa/QsNWvQQgKQg\nX0CBd3DYIXQU9Gf49ZiHxEHhFuywG+gxr4Yorm3IFZ88GVwpNEvmwQCHqETOzc/r\nU+AMhk3JssOoldpgALoseEWS60ezMVEUtZq81YGUkXUMUVUC3+D2HMrywjJ9qVkH\nbMqoG4eEwwIDAQABo1MwUTAdBgNVHQ4EFgQUPTOordbAnj8ix1pKVm9sdZNBQ4Aw\nHwYDVR0jBBgwFoAUPTOordbAnj8ix1pKVm9sdZNBQ4AwDwYDVR0TAQH/BAUwAwEB\n/zBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcNAQEI\nMA0GCWCGSAFlAwQCAQUAogMCASADggEBADV48ptfXyNlX2pxqLxBVYaEzYiSw3hA\nU36SXA7fHUEhcs6FCkj/qg5TqMUGvpuMRiG81cXqGJ8u0HYCMOKvhh8gf915TveA\neTpv1qS8Vh9RFy5XWQL+kklkXYFmEjJj+C7zsEwKNYj5jhVUU8HQapuuyW2KA6DC\nnlfrwxiI7U+gx0A9TYQXWEEPA/P+6QuHQ0izJ+fnjhGVy0JhxyaUqgOoP+NCufrM\nfptsMkjBvpCeVXp3Eq3TdPY0vQ0CERWkW8Qr+JNHZZGRbB6Jb+ZJYxJirWzgAcj2\nFnaWz0NsB2a9b17dz1cD/BcV7YxrSqaGFnTDyIgPzkRN8jBg61avMoc=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS256.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIIE8QIBADBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZI\nhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASAEggSnMIIEowIBAAKCAQEA7rlL4t7g\nFclsS/LwPA2oYiBJ66zIcFxU+GoibEWeaeHJDWigfw3gxrDEK4OSHZjqRXYCvQyk\nrKxXf7YoHXpDZhjlM3wevYQK9q9rzcII65IkLjMKXD7OoEcph7W6n1VQOyNB3XJt\nGGtfIjD74+/kF2FKTqpTugERrZ790UNLm/vHEPY9qP8VmYvfLzroimDa/QsNWvQQ\ngKQgX0CBd3DYIXQU9Gf49ZiHxEHhFuywG+gxr4Yorm3IFZ88GVwpNEvmwQCHqETO\nzc/rU+AMhk3JssOoldpgALoseEWS60ezMVEUtZq81YGUkXUMUVUC3+D2HMrywjJ9\nqVkHbMqoG4eEwwIDAQABAoIBAHJ1gJeV8g4wJc8ie6HnkID/50Fq9i29b3Yt+TQ9\niw9MVQgrTqysfEX30g7lBiVPwJ+uTfDTw48REODod0Ju8SreK+LsE5cdXN5bTI56\nhqlgSB2olkKVUJ/TjuuFLCYiExZPuNBTAVDQhmwP3W40AoJdQPIHw54uzgmXbi4s\nHG/8MotLdJHbeP68s/Cf0ebGUy1yHjOS86vbskZ/DjIFtbnEfAU8OkV9AFVeFEKc\ncdkrZltFbqCuZZ7DtrRk471zejqVq16F3iEw6cf41qV81sFSzBe2h5YHM28NLu40\n0vPRBjeMdgfIz2hNUvbNRYC/pl+1cMVQXWIYSqDcS8EAjE0CgYEA/7o13ZiHqGsn\nhPVYj7Wifw6cvEY/Ywv/1/WphUBZkuBlPS36C6t5UhGB4ab1aZDqVFVAUfsHEZt1\nuqNoF9wve42iDFhMKDn0yV/xwBk3OZSTQ0JhX5C/kEmWsAw0O3ygG5CcgAbGj11c\n3xDguKqkkLK2v9vnhMg9EPPgL2TKFRcCgYEA7vpyFVqAlb9lMK17+8M++gSnUwjb\n1pylDC8Zfau8m75AbGAIDWY4AsGFca59FjWyyiHknOSC5R4cNs35HiknYhiJpBUm\n4kMgcZZAU3AktVL3DsWeo3eV/6ja43JX/rq9m+azEX3q2qJlKfzkoomx5lWHj5gq\nc6ObG4k282nucTUCgYBZVEWus7JnnY6/fijCgpNRyNvtVKidw7pKSRE/b9waV3Jl\n7aKT4wFNLrptBbJifvGsJd+DA6pTdzeny573/r1DbpU1tL5dqukcUvySuvw0i/bp\nHs3+4QRZtasCsjCouv7+wgQ5IKTJvbZMYYvuVgWIWjVGTd3Q31Wdj2M3iwCgXwKB\ngQCEzeEARN8YWNifCInSC1rADj3+QvoIddyyvKnp0LprwnqCv4s6BwgxX+IMnu8c\nnJLTCarGFac4NFdxjV1XiX89YG19JdQKAUvSU7FDrRp5ObXaG7BhH1/YR7n8k9qa\n0KP2M2pn2hXdkkmt38AfI24dloJTJjjRMqZL0yEafE/p0QKBgBWTSG9ieCYSliev\nLdGuXCjeVAXDEeJivmE4np6nrLIFxag2rdUeTv581uWX7+qmAK3oogzeqnxuZwDe\n6y04FKmETUr/mxCraKBNX1ArViqDqJ7dQRZf/rj955kbmjTWRAbhSfXH6cZs57A8\nN5Z7WX4w4Cv7iaB359HAVAe7iX9l\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS256.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMIIBVjBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcN\nAQEIMA0GCWCGSAFlAwQCAQUAogMCASADggEPADCCAQoCggEBAO65S+Le4BXJbEvy\n8DwNqGIgSeusyHBcVPhqImxFnmnhyQ1ooH8N4MawxCuDkh2Y6kV2Ar0MpKysV3+2\nKB16Q2YY5TN8Hr2ECvava83CCOuSJC4zClw+zqBHKYe1up9VUDsjQd1ybRhrXyIw\n++Pv5BdhSk6qU7oBEa2e/dFDS5v7xxD2Paj/FZmL3y866Ipg2v0LDVr0EICkIF9A\ngXdw2CF0FPRn+PWYh8RB4RbssBvoMa+GKK5tyBWfPBlcKTRL5sEAh6hEzs3P61Pg\nDIZNybLDqJXaYAC6LHhFkutHszFRFLWavNWBlJF1DFFVAt/g9hzK8sIyfalZB2zK\nqBuHhMMCAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS384.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIFRTCCA3mgAwIBAgIUVGlRHG89nOaXmhYycgWFGmD7LHkwQQYJKoZIhvcNAQEK\nMDSgDzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIF\nAKIDAgEwMGMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD\nVQQHDA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTAL\nBgNVBAsMBGpqd3QwIBcNMjMwODI5MDAxMzAyWhgPMzAyMzA5MDYwMDEzMDJaMGMx\nCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4g\nRnJhbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTALBgNVBAsMBGpq\nd3QwggHWMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG\n9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMAOCAY8AMIIBigKCAYEA2XNWI2phPHvS\npP6qpr3Qajm5rsFUfBKU732PXcP88gdoXZz25Vw8FcC+sbVgOeiDQD3VEU9f+uju\nAMb4GTihdae9DDit/hcQOuH3iK6Iacur8xH8VO6pKpwOUsoGhMj17WDGv9A458rT\nVsNpQ/g2E5rhWpPXZ2Nu2dNsqBOtA3xlK5VfmCa4slxOWah5TXtfxAxmUOX6c9Z1\nyq50EfCLzkQFcQHIamlW94XKkES3E2atM4/cIrR0whlvbN4J7Bkt03iWW1F2nlHb\nrw7huCXieWESyA1+jFx6DW4cCSanCrXUmkFvQr+MPayYqcr/zz8Sk/7tijlKfODz\niaAV45Gq4FvVEDCzdbmeqGG4GXSjTjEIqoSd23Pbh2U/p4OQh5trmV3iKK4gR0O3\nUAwJR7Itu0H1ZGn8ejwS1RNnig4erxwceulnxe4XrTWS9IKmeF9ayOTQIxRDB7KV\nvjZZzylnXphAdfFb9Y20GIX7SpXNSL3IeBpiEZWX1IksMoqPzv/RAgMBAAGjUzBR\nMB0GA1UdDgQWBBTsSIoj4kNXWjhTo8p1HhA9GHGnKzAfBgNVHSMEGDAWgBTsSIoj\n4kNXWjhTo8p1HhA9GHGnKzAPBgNVHRMBAf8EBTADAQH/MEEGCSqGSIb3DQEBCjA0\noA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCi\nAwIBMAOCAYEAKznBAMH2OjxqwSVtoSL456m2c4HrlxodB7iCp8NPYyus/Wh49HCl\nYX0A0V9iSuNihUXSy+aAzLnZZ/01wECutLlmKjjnJsUvZZwPGq2YBNFvjZAGuIu0\nNuH3fvm42d/fTTqyvow5AjpcRDkesl3T5bBGhQE8q2DglsUmXp383wTTFfnRBboV\ns8A0U4vOKPie1rqNNaVDEV9UjwLfucKHT53esJnTtttLNkCXEmXsWQSkE+hhhREf\nfsuiB2XPlKGzd98PzX14UXwX7g7iCSeMm2PswzZuVyyKB82O0IH3nrvx1074mwhZ\n0BECOaJWc/2XvchQMG3FaTdjtpALN7wqMUqU1cogMs8DrIsut78l3Ow4cQ9BacIw\nKnPtVw0CHm3a+PzX+VW1GIV0IcU303qGjTYbx+B6zHDDJZn/9IUPxbqftcY/IaWh\nztg/xrc25jbGqFphVUEryAQiY7SgeFcK+k9vjOvz1+mYb5PVaXpwUDFBW3n7Bt75\nTu8gNp56smZn\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS384.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIIHMgIBADBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAgUAoRwwGgYJKoZI\nhvcNAQEIMA0GCWCGSAFlAwQCAgUAogMCATAEggboMIIG5AIBAAKCAYEA2XNWI2ph\nPHvSpP6qpr3Qajm5rsFUfBKU732PXcP88gdoXZz25Vw8FcC+sbVgOeiDQD3VEU9f\n+ujuAMb4GTihdae9DDit/hcQOuH3iK6Iacur8xH8VO6pKpwOUsoGhMj17WDGv9A4\n58rTVsNpQ/g2E5rhWpPXZ2Nu2dNsqBOtA3xlK5VfmCa4slxOWah5TXtfxAxmUOX6\nc9Z1yq50EfCLzkQFcQHIamlW94XKkES3E2atM4/cIrR0whlvbN4J7Bkt03iWW1F2\nnlHbrw7huCXieWESyA1+jFx6DW4cCSanCrXUmkFvQr+MPayYqcr/zz8Sk/7tijlK\nfODziaAV45Gq4FvVEDCzdbmeqGG4GXSjTjEIqoSd23Pbh2U/p4OQh5trmV3iKK4g\nR0O3UAwJR7Itu0H1ZGn8ejwS1RNnig4erxwceulnxe4XrTWS9IKmeF9ayOTQIxRD\nB7KVvjZZzylnXphAdfFb9Y20GIX7SpXNSL3IeBpiEZWX1IksMoqPzv/RAgMBAAEC\nggGABbxn34fJGFgY0p7gIAVeR8sNhZlWce5ojNe2Ti4jEDxXSa7gCWVRNW6v6Q6k\nguqJ+coXwnziRoNZ8d3NwhaPFcQutbGSD2OK207GIY4fDZFgkAkfq5rfDf9vVko7\neUm2yQP2Qi2LLrwYLo/5iSA1RvedLa88LZ8/F5Je6aEZPYAMyCVI0AHnedb6/xz1\nVDCmskx0b3pdjKwxz76h065RdcewEA7Qu1Pbhix6jyss3B9nfrZzA1xYvxz+JjRG\nNTrlwNZ9Mt4WFQvTX3BUzX7xxZn5NdfVXW94+NEQkRieosEVjFHyWke9kuOC95LD\nElio8xE432HP0IdZfwRT4T7/rQ/+Mw/xH9+VtzgTrCia6PrDqkPa50HnayxaaVl4\nX/3epPTxGX/uW/Ig/jZAkYrrzVP0bEl+XIhxfUnB6DF/LFwd11c5uYJvmreX7Fac\nUthIcmrTnQhxn8g4AmuhZdMZsVjYOF3yJpWq38s0OILQpZXYVJibMq2CokMrnD7p\nKcUxAoHBAPwZGf5lwxa3muPf2MKaZFuad6GBIkFe9g4XjUA0rPsgnMt9nb4GsPLJ\naliMeTqbVcu3TQ8fS7xXuOjHGx+dDTVTarpV5u41VHgBlBw772kNibbEB0UqXOV+\nq/r6XbcgKQEs/tz+hVuBRiNVg82MqznHjEp6Ekf5BDNXOuEaQaEmX5U4UMqlQxln\nJQKnLmKcS+RQJYpSQNlpw5U8RJ7x6b7LsGbsoJ9n0ZEMWfR+u1h0UzFptAb4o314\nInGZ68+2CQKBwQDc0PMcRuQxUgAKt/CWgzKDQD9wlW/WUwLAXCpJRPGHF01LRGdY\ngVjGRAMZ8CF/UXhVfUd3jqf2J7ZMUVmaLUT+aoTogsW+rxwgfLyg9g4HUfkK/5wp\nGpAhxJQ8fbBSNCjj/0V74fl9n7m46X6om//4UICvVEG1wJeJvvWLBFIP4Qzn5zRq\nXFLVTd8ztmyT/Nd62GkELyj7ellNkeVKNKyvhwcDQhwkTsMaqK6FXFAckLLsvRNJ\n7NYqJu+0DbcvLYkCgcEA93WAYYrsjIEgJq0Vbjj1aEHhSoSi5n5bk4uk2LCcWEoz\n/z/INr8EtN3naRJC8beG6Vh96Ok0g6WsWbsQMeENFRpT+qLV82AgEUijZW+j24Ax\nfVlBNbCWzaOhF9TpZxfHiGLtrmqc5yynd4m6vmtlrGrnmDfpeALFD8yBfHM0lwY+\n7w//plvA2M+5sbf/vUZk7LGLmBKTm5bJKNWnGkqmwuXYu79tD+xt9y6jom9AYVyW\nSTvUPr+UZFYnoVGQ+yxZAoHAVqftcCBl9vD+MTakRPzxus5g1xbeD9b90m2Y7q4O\ntvwvCiWrBPGl3BDewrQZATUAq1QB0up6AcDt6p9WMYoodEtrIzAG2GEyAZHSGLzX\nHopN2MIdD4hsHcRehCqzIl9z2J3aL9arqWAga2++k/68gj9dcPD45JHTJmx5QfgN\nGEwyW2PBjyfyHeF0gX/KtnzYN05sUAcN5zrJhwaFXAy15CByYRX1o04BhRnDe3SR\nv9QNU5iT1EQMe/hRw3BKfko5AoHBAN0M//C1pjoD5iHYJ+CJJ+Gnovg2SCLzPUtx\nR84UTjYhmQDjgLiv9gRFniv6iq57CaOW1UVPj+Epg/q3G23cAokN4iLZzWuq1jvf\nyd3gSXR/+LlzK8TzdIzrvL7eYMlYN9umnLIuwMz34sozgdi5+4IEzupdvZSkrug8\nGVkCg12B3lO+7zVrMJo0q7w8SPJ4h6H09J3q4CnY+TVb35LFh/kCf5uefnBSnK9q\nN4MxgtU+1WQMK0D8peI9JiWA38g8OA==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS384.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMIIB1jBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAgUAoRwwGgYJKoZIhvcN\nAQEIMA0GCWCGSAFlAwQCAgUAogMCATADggGPADCCAYoCggGBANlzViNqYTx70qT+\nqqa90Go5ua7BVHwSlO99j13D/PIHaF2c9uVcPBXAvrG1YDnog0A91RFPX/ro7gDG\n+Bk4oXWnvQw4rf4XEDrh94iuiGnLq/MR/FTuqSqcDlLKBoTI9e1gxr/QOOfK01bD\naUP4NhOa4VqT12djbtnTbKgTrQN8ZSuVX5gmuLJcTlmoeU17X8QMZlDl+nPWdcqu\ndBHwi85EBXEByGppVveFypBEtxNmrTOP3CK0dMIZb2zeCewZLdN4lltRdp5R268O\n4bgl4nlhEsgNfoxceg1uHAkmpwq11JpBb0K/jD2smKnK/88/EpP+7Yo5Snzg84mg\nFeORquBb1RAws3W5nqhhuBl0o04xCKqEndtz24dlP6eDkIeba5ld4iiuIEdDt1AM\nCUeyLbtB9WRp/Ho8EtUTZ4oOHq8cHHrpZ8XuF601kvSCpnhfWsjk0CMUQweylb42\nWc8pZ16YQHXxW/WNtBiF+0qVzUi9yHgaYhGVl9SJLDKKj87/0QIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS512.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIGRTCCA/mgAwIBAgIUUf+7IJtXpglVqL+xP7JtYwrgyMgwQQYJKoZIhvcNAQEK\nMDSgDzANBglghkgBZQMEAgMFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgMF\nAKIDAgFAMGMxCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYD\nVQQHDA1TYW4gRnJhbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTAL\nBgNVBAsMBGpqd3QwIBcNMjMwODI5MDAxMzAzWhgPMzAyMzA5MDYwMDEzMDNaMGMx\nCzAJBgNVBAYTAlVTMRMwEQYDVQQIDApDYWxpZm9ybmlhMRYwFAYDVQQHDA1TYW4g\nRnJhbmNpc2NvMRgwFgYDVQQKDA9qc29ud2VidG9rZW4uaW8xDTALBgNVBAsMBGpq\nd3QwggJWMEEGCSqGSIb3DQEBCjA0oA8wDQYJYIZIAWUDBAIDBQChHDAaBgkqhkiG\n9w0BAQgwDQYJYIZIAWUDBAIDBQCiAwIBQAOCAg8AMIICCgKCAgEAy4hMVFayFuUI\n3gefbHlo0iCVrxaG0PlOGFs7NbRemH7EEky7Pkc/Q4x5g36pAkdQh3zliutPBy5w\nI7WkqEn/zT4piS44F/2ZH6PnDXaju78uEvW1wQpmvb3288COmkMjDcLMRCeyZ9BG\neCO5j2L5AC1FwhvG9+HuUvoAyt67nuVqgeXvGFh1sRB7/jeX8bBdIXIgWubTR+uW\njH2iBZlaK/K14g8bYENXvVSbBxjq0OR0LUsv6Tf6a+tTQ7UpakbVZi31962PyTwc\nXFosNul7Mq4yCI3XiSi0cOWUCVnzh8A3dd/fm+7SjcdJSoatLDa9L7CwTyEtLxuX\naygIaFy/bNUjZcJS9KDsutLGaF6JtQTwwqakqKCo/1ImKePvRbW+L9lak9WS7fA5\nNwHQbY2OCH3zoNREjeoqaR95n7gFATNAUN0vyXxkgw2MJ7uuwulNE8wPkCFUltmT\n/au/JcnOTQMGKfP3+Yg9ACOypt0IIyNmxPRH+Hqa8wX+w+JfmVBhZ48yTB6mj2X0\nIyOAqNO08duep+8r98q1xOnaCiYZpsmBFk/9LiDnqnKbKCsEBRLr30dKkix6YeLW\ncgdqkDCWwL3JSArk4gNBk+6sTgHKhbil0+NPL9EzB1Iz5HF2XUPsTXOLAHArTw98\nFQJNgxn0S8z1uT+WERuhlcVudhPfejMCAwEAAaNTMFEwHQYDVR0OBBYEFHOC64vz\nWD65qqNBdjErX8N4Z9unMB8GA1UdIwQYMBaAFHOC64vzWD65qqNBdjErX8N4Z9un\nMA8GA1UdEwEB/wQFMAMBAf8wQQYJKoZIhvcNAQEKMDSgDzANBglghkgBZQMEAgMF\nAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgMFAKIDAgFAA4ICAQB9UAVdGuA6\nCdyHkadMwkFpcZ5ztT9woGtnTSGTYWqlvKT/pxoSkZuoKj9vmGOS7FE5ZGFZvaN6\nNO03PKfIj/v7PmX9fxBYZ0l+Bxg70rXx6FK6VeJumZcfsaAiCKt2+vcWaCJ4ddhI\nEFmOfDo7tRG1mNU83GF7/9hZOkJSusr3FwnHgmYUlVf3V3HYKkiZtsURXV4dH+Dk\n+d6bjWepGdUifVBadvWTl9Lw7qK0Zbx0G2SK32E9ekP9dsO/+PH5RgUaoQa1tYrw\nu24trpzVymVP7nuvgVYyiQucu6wwCfUpLGJmor8OlvNBj2paM/xxRLt5WvG2S5lZ\njjYqmGvANX9W2fy3cUPSKWl2ZQQHGUdhczGmdMn+erwoKV+5WBYWASSXzrErUpfD\nUcTwaJMDz9nRoMRPkFcPsriq21pEK0JpViNeOuJSgTquzp3JpB31n9u0UjJDR/EO\nONUHFbW5H2Np9DflMmEG4+Biw3lrMzjjCcSUC5pK5E/KVw+Fkpoq/suhjxj0180/\nwL3mZrLZuOe3OE4D7a3B0/uRUTn7veUTmfO6g+m6KdOKwv84zsxHL5xunlzO4DjN\nI+4zEcyrPgSiT1kZM+fTYSllfFFh2UpLXWzjn0JUVFQDguFxTYsKXKS76kOrIx7R\nDYz1MOY7zd4mHdL3so5aUbmIqMa+a6KEEg==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS512.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIIJeAIBADBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAwUAoRwwGgYJKoZI\nhvcNAQEIMA0GCWCGSAFlAwQCAwUAogMCAUAEggkuMIIJKgIBAAKCAgEAy4hMVFay\nFuUI3gefbHlo0iCVrxaG0PlOGFs7NbRemH7EEky7Pkc/Q4x5g36pAkdQh3zliutP\nBy5wI7WkqEn/zT4piS44F/2ZH6PnDXaju78uEvW1wQpmvb3288COmkMjDcLMRCey\nZ9BGeCO5j2L5AC1FwhvG9+HuUvoAyt67nuVqgeXvGFh1sRB7/jeX8bBdIXIgWubT\nR+uWjH2iBZlaK/K14g8bYENXvVSbBxjq0OR0LUsv6Tf6a+tTQ7UpakbVZi31962P\nyTwcXFosNul7Mq4yCI3XiSi0cOWUCVnzh8A3dd/fm+7SjcdJSoatLDa9L7CwTyEt\nLxuXaygIaFy/bNUjZcJS9KDsutLGaF6JtQTwwqakqKCo/1ImKePvRbW+L9lak9WS\n7fA5NwHQbY2OCH3zoNREjeoqaR95n7gFATNAUN0vyXxkgw2MJ7uuwulNE8wPkCFU\nltmT/au/JcnOTQMGKfP3+Yg9ACOypt0IIyNmxPRH+Hqa8wX+w+JfmVBhZ48yTB6m\nj2X0IyOAqNO08duep+8r98q1xOnaCiYZpsmBFk/9LiDnqnKbKCsEBRLr30dKkix6\nYeLWcgdqkDCWwL3JSArk4gNBk+6sTgHKhbil0+NPL9EzB1Iz5HF2XUPsTXOLAHAr\nTw98FQJNgxn0S8z1uT+WERuhlcVudhPfejMCAwEAAQKCAgAGNOFS/xPOM+zJzIS1\ni5xBMCIwZSj2TWvuvTV4hUgPMWpsPm/FTenldu6rrlycB62yfAIJ8vQFfYqI5Dyh\nryQAT8F3f/PQ83hMaTSeCfyjOIjApkKFIPqSFa1msHwIwzxZ6pNNDsLXfJfxiPFb\nKIL8WOUULsGqBHc+i4YjqZgiF8/gJzFb1jK8lAqb7XkSMzUb1H2dGAXgXxRHs3sR\n3aPMzEl0m85TaKpPyTkzBbT/asAKM41B+OWHjfULjwY0yfUu+P7TrzS/x7f7rvpD\nMGqD2KEI9r5YXefmu3GAuX/+J0PpscqBWE6OaUHYZnP4cbDiN+qgdxwIIDjFWUKs\nYAusSYWWZeOXbu29BxPoL1kk4QxL+i3VUIGfLBkCuDSHKB/I/zTbRghDKnOwMHc5\nvS2b12Hx1PidSxBXwH2t7GBQ3YcNHASIkqkIgly91qmW9cl7H7zQm/tsNj543eTR\no8IL6IBtiVi4Ja1LldPKc1IGHB4IM7bd+KpQTwY9glmauyWIrnduiRtXme8H3w8K\nIlUKobM5cUdy+yBLtSxNh0pZ2lDb+0N5LKCOix3QcH62D+mDZra/+tQah4iUalHh\nZqyA8yo1P4yruupxHud3PQ6eD/FWzjss2bEqjo+AojHR9nryHffD5hpI2ni5fFUx\n4T+WdnQ8r5OG0ysdqkJumbKiOQKCAQEA5b6IG0S7atRGytfb1Tn4OWYodC2T5GN0\nrCISOIKpp0ji5DzJLkCIF/dZ9mkmCsqKZt2cylGYbjRC2RaYY7gQwYun6PObjUIF\npGj6bg9Dfhp5aWSQutiejtpzLeuSJjCMSDUJiASz3xRI9TbHuNNjKE1jp3D02PPj\nM/fln8ekWzOm65NCYdZwek8+YpgDb55WZWjWKsZsswhgw6yQIjTnC3C5xuPIHc2x\n68z6zeS3CrCKwJY6qHDJFmuCYiQsbNBBiqdEFGXoaS+Vy+khcJfdv5767pzgZzek\n5QApif+10imYcsuGoy02cL0oyPTXKsFQAkO5VeqVlaj3hEoEOBuvSwKCAQEA4srn\nlUBlVntZWLRsestil0F0y9IHsK/nCq0TGDXNWp5naQ/lT9PgqVQpwR5dz9U7OaKj\nReenEQxFCg/4JI3aXh4hKdMxbpOkG+1qOaY1Ar4s1dWLkD1OWeFtwEjYKSHtPKnL\n19akuKqB4L7UjytoaLswRZf3lhZhcDgd6Y1g6RqPYtqqnwvZ12NVVqocR9IUnBTA\nmhMk7R0tIsggds+oqez4B0eVx3FwRjL7TXmrVZeVUE7t05MP6QiANwKpolrAkjlX\nEHIhLCROR5Q1ZyMDXJHtkgA/COINudwmzMLqRaSn8ryL8Xn4f8z0ajEgcezjUfBt\ntwtwawO4RiAr5sBHuQKCAQEAkYpjFMs041c4xZV7eRexLUOPSxH4h42NwuIOouf7\na7MbsTTkyb0tuekDf7ta0yk+Bi5L/ks0glPvKTFMNpfLXaEILOXuW81AX8f1JbXb\nrs48rcx8dzF1ONAgeS2rty+4HqIiuJ0qCZ8DHPyoB2k6frSP9enz4mCWRTy8pbzG\nXNRa4Q+31N0RAhqjTbg5LQOkfbetPQnYoI4lJrBx2omi+DdgKSPxiRfep5+CHt7O\nKiJus9Q3sq9IZECVJ3D9B63iZ2DRGw737XKstbGpcndyjcq78l9FNX4losC4j+iD\nGXEqV0ahs0uYGlFqveuFR1uRQO4AQfJK8rVIn/B3vcekAwKCAQEA2UB975+sc9fd\nkvfjIw5J95Mgi087Rqp6rON28y429aPgc+hiRsI06IBTL0gjncAp5+BAf+qVQ+N5\nD1aU4o9wq9A4/JPvOnn8LzSTGX56MZJz6LOT6iyQLdGhDR261ExHsmEFgFGBodTU\nbbLgc/WlIw6OA1y8M+5kkNdw8BYay7JBwPSvlYQIvifNzCNQzAwW6h2HExFbwji8\n0CDd2HK8o2r5fh+4/0FPyC92RJVU5705r6CseozaJOWbzRaj4X8GEg0TthFebaap\nxi/XqGEGC1tPNRk/SQcjFvQpxuTA/s3ohMLRXBT3U5XGXSOKbRf7+rZSo5I1so3x\nrFuNMpLnIQKCAQEAqkG1sLbyn8pS9Nns9pn+BshZ2zlMLYhPUejPpeYKhA13QBNe\nVM+7jDDI4KIk8uuHNXfe3G/vb1vSozlTuXInFHYtOktNXeOV4CKehgHOTyozIaBK\noYiVS9imF5CR82OpI9KnOOmDAxRW8UVMb3VgnmvhC3X1/GWuPXIBwgU58NGZf1Vk\nsChfyo5FYiUo01z2b8eOBht9FD8/JBsG6ctfQsQfyIhnYjvqEs0R+q9uUTdxQXLb\nneU7WQ+MgesSNThAslivE7yUqdiCscY/9PUECw86qBNYMGOUrk06YKc8xzLXFbiN\n9i1lZIAIX72l6VudSsaKtvlfM1g9OadvIDtwLQ==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/PS512.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMIICVjBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAwUAoRwwGgYJKoZIhvcN\nAQEIMA0GCWCGSAFlAwQCAwUAogMCAUADggIPADCCAgoCggIBAMuITFRWshblCN4H\nn2x5aNIgla8WhtD5ThhbOzW0Xph+xBJMuz5HP0OMeYN+qQJHUId85YrrTwcucCO1\npKhJ/80+KYkuOBf9mR+j5w12o7u/LhL1tcEKZr299vPAjppDIw3CzEQnsmfQRngj\nuY9i+QAtRcIbxvfh7lL6AMreu57laoHl7xhYdbEQe/43l/GwXSFyIFrm00frlox9\nogWZWivyteIPG2BDV71UmwcY6tDkdC1LL+k3+mvrU0O1KWpG1WYt9fetj8k8HFxa\nLDbpezKuMgiN14kotHDllAlZ84fAN3Xf35vu0o3HSUqGrSw2vS+wsE8hLS8bl2so\nCGhcv2zVI2XCUvSg7LrSxmheibUE8MKmpKigqP9SJinj70W1vi/ZWpPVku3wOTcB\n0G2Njgh986DURI3qKmkfeZ+4BQEzQFDdL8l8ZIMNjCe7rsLpTRPMD5AhVJbZk/2r\nvyXJzk0DBinz9/mIPQAjsqbdCCMjZsT0R/h6mvMF/sPiX5lQYWePMkwepo9l9CMj\ngKjTtPHbnqfvK/fKtcTp2gomGabJgRZP/S4g56pymygrBAUS699HSpIsemHi1nIH\napAwlsC9yUgK5OIDQZPurE4ByoW4pdPjTy/RMwdSM+Rxdl1D7E1ziwBwK08PfBUC\nTYMZ9EvM9bk/lhEboZXFbnYT33ozAgMBAAE=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/README.md",
    "content": "## Test Key Files\n\nAll test key files in this directory were generated with `openssl` version 3 or greater by executing the `genkeys` \nscript in this directory.  For example:\n    \n    $ openssl version\n    OpenSSL 3.1.2 1 Aug 2023 (Library: OpenSSL 3.1.2 1 Aug 2023)\n    $ ./genkeys\n\nThe `genkeys` script creates, for each relevant JWA standard algorithm ID:\n\n  1. A (non-password-protected) PKCS8 private key, e.g. `RS256.pkcs8.pem`\n  2. It's complement public key as an X.509 public key .pem file, e.g. `RS256.pub.pem`\n  3. A self-signed X.509 certificate for the associated key valid for 1000 years, e.g. `RS256.crt.pem`\n\nEach file name is prefixed with the JWA (signature or curve) algorithm identifier, allowing for easy file lookup\nbased on the `SignatureAlgorithm#getId()` or `EdwardsCurve#getId()` value when authoring tests.\n\n> **Warning**\n>\n> Naturally, these files are intended for testing purposes only and should never be used in a production system.\n\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS256.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIDqTCCApGgAwIBAgIUIGbRZYiiyZA4PjJ7isZTsopo5kYwDQYJKoZIhvcNAQEL\nBQAwYzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM\nDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UE\nCwwEamp3dDAgFw0yMzA4MjkwMDEzMDJaGA8zMDIzMDkwNjAwMTMwMlowYzELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFu\nY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwEamp3dDCC\nASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALz2H9VUsufIuonPd4KHp8xe\nVDvaC8cok0heuJRhVCevzAQ8nQL2PT0u+spr4VHEq7A1B4G9Gq7KejA1zn2cWB59\ngu6N/Z/Pm0O8kuIUJupJJfCgeTojpCRZBYxB68J6spF8oMdVRaXFaWwb8Q5+uNDg\nxEnFsqFFD3eLDivnrE7eA/fqtD17F830U+Hjq56VxyU2p/yiv2vlGJn1j6/sSBD1\nMZyzwg+vJiB6EjwGiYr9+0pAFhyffn5vw352E8E4tK8XthUUNfr5DsXugtvp9Xm7\nKgmTyq9Cf9jH8Kj9K2sS38honMTOXnuUUk00TAgOFMl+Xbm2b4dqS6GkvDwmWo8C\nAwEAAaNTMFEwHQYDVR0OBBYEFG0iP1JdIxRY8iBOzCetcpJ3PL6pMB8GA1UdIwQY\nMBaAFG0iP1JdIxRY8iBOzCetcpJ3PL6pMA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZI\nhvcNAQELBQADggEBAEKeOKghwmm/ixb0QLDDW/6PmiWNf9ZP7+iI/JfACh+k8xcT\nfB9HkErb0lQIJDTbH3a9qcohDnGYXFjLVmCVpXliTzX/4Wd5PTmV0SWwx2KKU5Fn\nGgrSP5bUHByI5PKjpI7tlaf2Ug/CMPog/M4Cg6N4bX164VpeN0ik40xVs+GIKm3e\nFgmioA3zmm0qRDxAXM/Gk5QoOMxPs00mHajIqgQmBwXXRHRZaawX2EGckt3VUmNl\nV99yFxOQ1hefA94IpiaSYMzRvzOovG0pWOp8vZDLP7hKQhTcbX3ClTlhdkIKg04X\n/exWU+XKc1fNB+41kex1XgpBy34GJkUFqfrLe5Y=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS256.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC89h/VVLLnyLqJ\nz3eCh6fMXlQ72gvHKJNIXriUYVQnr8wEPJ0C9j09LvrKa+FRxKuwNQeBvRquynow\nNc59nFgefYLujf2fz5tDvJLiFCbqSSXwoHk6I6QkWQWMQevCerKRfKDHVUWlxWls\nG/EOfrjQ4MRJxbKhRQ93iw4r56xO3gP36rQ9exfN9FPh46uelcclNqf8or9r5RiZ\n9Y+v7EgQ9TGcs8IPryYgehI8BomK/ftKQBYcn35+b8N+dhPBOLSvF7YVFDX6+Q7F\n7oLb6fV5uyoJk8qvQn/Yx/Co/StrEt/IaJzEzl57lFJNNEwIDhTJfl25tm+Hakuh\npLw8JlqPAgMBAAECggEAAIs3rh/+cdEaWaou6UGJPtBFIQMwlHzf6BUGJjCBS+LV\nBfrDygbIK8f2VOwG5LO9QMER3xJv+Zw/ranzwa6tc1s8VoH5xbfqLsGOdKlVL4+J\nxs2swsB2gdwM1t7izvrBFgfFEZMgMej+l0mrvjVTREjZUKhe8WdRpOjuQ1f5LXoI\n7dRX/5vgp53aWfuOt/0X7Gsxw16tQ8n9/zWqrXynGDlzsMyxyphXi+9ELa160W8Y\nN3QK0UOX50MarlLLf/6cOiPYz/Cm38UqwSRpwxHzRhUMSDEH+hb9dxkUKUQAhbiz\nojGCe1BQtoK6wBYFYsXWbiY+xcmac82wj1Z0f0EpoQKBgQD8b7gUUgd5Hm4F0p63\n8jA6Vay52R8Cz+Iii9ftPinhR72+1S7gDy8oCd9zl/VjM2UbVRvI5SInRkBZFs4m\ng2tZCEVwqQkTC//4TLjy27Qz3UT5VN3kWcOK5E7RND+442wdo41+B1njQQGNnzmW\ntzWDTQaxcR5rNY5/BcalMEfGIQKBgQC/oQNHFN3PLdFNADyI6ad/co4h2tSJ+83d\nbOdrpILMv+DsXi7yzR5zzA5dHVRqs2h8lY3NhHLIcp0hvL7dA3CcT6DLesvSgbsS\nSJJdgJATwi4Pp7Fffxa/ct4djk6Wkls/nAcR2/fM+lS7lGz8LLmtoUDPADj7jGgD\nA5rAWQSqrwKBgQCIkuTz0YGLjOQXsGEAwj5HgUzG6+o0OkZtTF2RVH2SDZ9h3LLU\ntEJeFiFXx9ISTp8YD47NvPIib4am7IiyG437iFcRYdKwBGEDdHbnpegz2zXS85Bt\nWAdMYMMnum3zWM+IpZEKq219XxE5Dvk4SnzgQc8qNzou5LXokTZs7tcWIQKBgDzc\n19yorPZTeAl7zL4zb+aTrL7l8OFOX4k3QJ04p+599uM72q91JHnk0p8SZLBrAQGo\nwlwG+Cnf9TY0623o3MhYphpaiwf1+kOJVytpXNlZsCV6vmQ1SjVON2utuhoqq96d\nIMW0VpT84RKexqqlTefuslXMnUyPwK1MZMc4vrmzAoGBAILG+EEfiUEfuwrszfdN\nsY2HBa5sCbkV4N8+fWOaoSD3dCrggvZW1mwgbrEmQb0J5aDZyftd7/u4D1E2ucHj\nzHm4UhhzfrUyTDDvlqiMJ05FAFugEFJKtKLweKZPGqLiVVcv9MpCSTCDQ3GQxkBi\nBtXgOe7lw+m4XzVt8EnQt/vL\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS256.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvPYf1VSy58i6ic93goen\nzF5UO9oLxyiTSF64lGFUJ6/MBDydAvY9PS76ymvhUcSrsDUHgb0arsp6MDXOfZxY\nHn2C7o39n8+bQ7yS4hQm6kkl8KB5OiOkJFkFjEHrwnqykXygx1VFpcVpbBvxDn64\n0ODEScWyoUUPd4sOK+esTt4D9+q0PXsXzfRT4eOrnpXHJTan/KK/a+UYmfWPr+xI\nEPUxnLPCD68mIHoSPAaJiv37SkAWHJ9+fm/DfnYTwTi0rxe2FRQ1+vkOxe6C2+n1\nebsqCZPKr0J/2MfwqP0raxLfyGicxM5ee5RSTTRMCA4UyX5dubZvh2pLoaS8PCZa\njwIDAQAB\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS384.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIEqTCCAxGgAwIBAgIUWOyOdJFOZHVUDw5snyzN83zDRCUwDQYJKoZIhvcNAQEL\nBQAwYzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM\nDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UE\nCwwEamp3dDAgFw0yMzA4MjkwMDEzMDJaGA8zMDIzMDkwNjAwMTMwMlowYzELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFu\nY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwEamp3dDCC\nAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAJ5/3aiszcm8LQPou1jWnDRI\n343iDlp0ffVu3FB38vMf70Rcvtf7xffWXq+nl4HSt5H8PEv1ki2dkEpwwyQAVLVC\ntLmmqRWkazqt7IqOJRCJxaEjShWuoNGa887vT9sDFYb4/pPR/CgfICseFbsQ7xUM\nEAxe56Dz3dZEVYGDZOYXtdYbE5QB84o7/MftQAII4+URjlbaEVZyUG/WNqx/y66Y\nMVS9avYLYZQmHvnyVfc5tdFgYT184hCLUjgsoBTE0GcwJkkKaQT+ElxIyrgrvm8V\nnfzuSckgVjQbhVvuXz2x92bN20tYUZwInAHxL9nK28Ir1Wx/27sJBF7IDzfxuG+p\nCYHXhBPiLO0fqYB5GaPk8YwlXH9B+JfLsx/xVKyCbRImp2/UE8/kRh41ZvJjrRsi\nHflFTvmSBS312M4HXKBwKWNcvO+E09zOwl2ZZfCZm5n/9rOICETMC0EzAi9bUpHs\nlFi5BuTEFjHX/hrXNr0KSrZzXqFUFNEDOIgCx28lbQIDAQABo1MwUTAdBgNVHQ4E\nFgQUWS8geAwVhFrBAjnwvKJsvue1UVAwHwYDVR0jBBgwFoAUWS8geAwVhFrBAjnw\nvKJsvue1UVAwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAYEAaN7H\nZprHX1OnSrbsWEv/hyTiqJQLozqgQmWRvb5cAUQjVcIOQ+uhNA36D97aCxQVRa0m\n1FlsrZAI7P3yfMNX94uRciJNiyK+/6MC/xt6qJ4NAmO+TBH4nwpEwbgfIRpx9gJZ\nkXN8Up+O7QlLc6NNdKEIoXoVh9qkA+XG/V1k/Yqh/cHxJnwxx1Jh9U/fy5Pa5DVj\nNHlCus73vZYU2Nbny5UAG/jhGtuPjwk0vnSPWYPT/NlObCWfQa9HcRLj50zI99pb\n9vM13h+U2G3iBRAlwRD02L3inOaNjUeJbdJErDuNK4TBd5teRLHksNysofAL3Php\nQt6YPzjLe4hUPErUTtXfaKViEI6yHolfGBob7edNxmUnQbs/oEXkvY+emu8hvze4\naPC4PtgBFhH/WZPe6RQFKNaomeebICHM13+nzxVEGxV9lUvNshaPDGQgUU+EQ6Ge\n+aA+pdi+n1QnzWwB9/oTcQOhXu1uoWY0jzG4AGmou+qEIB9RrpFc49WX1+08\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS384.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIIG/QIBADANBgkqhkiG9w0BAQEFAASCBucwggbjAgEAAoIBgQCef92orM3JvC0D\n6LtY1pw0SN+N4g5adH31btxQd/LzH+9EXL7X+8X31l6vp5eB0reR/DxL9ZItnZBK\ncMMkAFS1QrS5pqkVpGs6reyKjiUQicWhI0oVrqDRmvPO70/bAxWG+P6T0fwoHyAr\nHhW7EO8VDBAMXueg893WRFWBg2TmF7XWGxOUAfOKO/zH7UACCOPlEY5W2hFWclBv\n1jasf8uumDFUvWr2C2GUJh758lX3ObXRYGE9fOIQi1I4LKAUxNBnMCZJCmkE/hJc\nSMq4K75vFZ387knJIFY0G4Vb7l89sfdmzdtLWFGcCJwB8S/ZytvCK9Vsf9u7CQRe\nyA838bhvqQmB14QT4iztH6mAeRmj5PGMJVx/QfiXy7Mf8VSsgm0SJqdv1BPP5EYe\nNWbyY60bIh35RU75kgUt9djOB1ygcCljXLzvhNPczsJdmWXwmZuZ//aziAhEzAtB\nMwIvW1KR7JRYuQbkxBYx1/4a1za9Ckq2c16hVBTRAziIAsdvJW0CAwEAAQKCAYAd\nT4kWscomldl/QREwRxPA6X8J9nVwDd3jPtqYOO5hPpUZP8t9Fo7QG8EL28K9W9Hd\nudcOtv2O2PX/hiXYKQWBNbJFKMhY+7xmsBYvs0Swb8Hv4B5Jry8HRA/1QzUxy7q1\n6KLvhCQM6WCiCKC9JM8Jxd7L4tsT3TU3dBCZ8En+8QCL9RrkQo4ekKRY9othNPKs\nAFihwBb3tREh9WvL76Aji/qHcLXwhT9IzdnpoAJ8wxvX+epRnCAULieIwtvhq1mS\nHlBbGaaqITyzhxlzEt7fw1ka3oQ6uIRxHlQpakJssqo/HscToXGIdySCHZ8Qwc++\n3d7tYAJBnl5DBOXNz6tr3rjxix0S+lsY3H4ihkxokKKeGFVEdGkFf49lhqrGAmLB\n2GOUjI2FtRMoxEyayHOx/wbY0+HvKG2P5JP6BpMT1U86U5Y2248nfMt+5gm12QSQ\nkYjunhewPPh+SjKQVMxcxksly24i63k/Q/s4rexvy9ybe4YhgXtrOVHHiiC7HlEC\ngcEA2PkN6z2+mqULwty9q36ylhu1XqMsfhf35irusbRKNjwNmvBY985Oca7DtH59\n0+IygD+GxpSmfAkrbTHZNo6VEEVtOiXUI7fCdnH/VEaX64Dx/k6mKLxpmzd71NS9\nFGPuJJhcMYAAx5Wk4HucqhWqD3Jh7Jthi7cAS8MSSHOD/iaC8bnfjBdsOof0rW9y\n0wqWSJXX6CcLD/Xb99M+CyY4oydEaRjAhInIgbR7NN/boumkY15Fflkq7bdMDFH0\nJ9Z5AoHBALsCScvGsV6YSu0zNaI4TPeGof0ySENGfNW5IoEnp93QkwRhGeK7PGvU\nGEHpgiPM6oNheF8S2mIEyDzgUx55YuYusNZxJ3CCqoZnDPmb8YmlABkwJl5CcNXj\n99EoI95D42/RdL2qXuv9X19YBnBfs+48gF6bBQh/9Af+tceV9NMS2GWZmfxycUEi\nD3YFqyxJgcMjeXYtxRt5o9kqLiWCaEa7728PNCZhUGhpKiWNzUcXkaCXJma4GmyU\nk4FtdVmZlQKBwQCBgEU6fuPg0VmvuKjMTxawWWFrVuEbcZrYmg9VqVISBM3qCEJR\nxaU0XScZ99WKPZv+x+vdYqPrrF1rEzGeSoPV7lo/NozjtK4wm+HVnzzVp2TIcJDk\nB3DQ39DdOwyPuwVMelOsh8XvWfXKtnzPV5blGVQxMJyME3HtxkSHUcsaSkollNdE\nekZyuOrlCXvzUoJYWHdBbOxBXnEn/cEuTmXHm4xNXiSp9sLiB6Lx8BrbpbAkTwQT\nYY0pzRlq0Q91J6ECgcAUktnfi0p0J7kGg33BDQSarrsfieqdTCHruWRsZRp4sruZ\n3bzlTsgE7N6GUdQ5cA/UyGJfw0k3Q2NsHxnF3oDc5gIadXRrUlTEWI364Acgp4Bt\nRPjToeecAGqBHjNj/oAFCzwWqamruMJHUP3UWxMGgK12uwNAviLwxjrlbD/1ALvB\n4bYpKcX/7mlZCKKeSq/18e8o6zwmG6nE+Hj/M2uZbI+Y3klUd5xLAFbcrs8IpTUm\nP7q/zj1J+MaJlKs2YkkCgcAnNpqAF/XS0BxyvGgLAlK80ImQV0Ji/Yg7oymugFbw\nLkD12srxK0zSZ3K3FDfhEL3Mt4ZBg04UFrGCEIzsZJ0ueCWZv3/ubffBwLZY/RcB\nrHu20yupE4p+leBhGSK6eB7zCRF00M4p0mcIGDT9d8ynCn9rnxY9r7fQ/cvI6wxg\nsxeJhp38RDj6a6qGvDXU3Ggl+rxuLxsk9ye1bV4j8ewEgXeBl4Ac9JrRmrDc8Y/H\nU8coNQy9seXH/B3X1mCFKp8=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS384.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAnn/dqKzNybwtA+i7WNac\nNEjfjeIOWnR99W7cUHfy8x/vRFy+1/vF99Zer6eXgdK3kfw8S/WSLZ2QSnDDJABU\ntUK0uaapFaRrOq3sio4lEInFoSNKFa6g0Zrzzu9P2wMVhvj+k9H8KB8gKx4VuxDv\nFQwQDF7noPPd1kRVgYNk5he11hsTlAHzijv8x+1AAgjj5RGOVtoRVnJQb9Y2rH/L\nrpgxVL1q9gthlCYe+fJV9zm10WBhPXziEItSOCygFMTQZzAmSQppBP4SXEjKuCu+\nbxWd/O5JySBWNBuFW+5fPbH3Zs3bS1hRnAicAfEv2crbwivVbH/buwkEXsgPN/G4\nb6kJgdeEE+Is7R+pgHkZo+TxjCVcf0H4l8uzH/FUrIJtEianb9QTz+RGHjVm8mOt\nGyId+UVO+ZIFLfXYzgdcoHApY1y874TT3M7CXZll8Jmbmf/2s4gIRMwLQTMCL1tS\nkeyUWLkG5MQWMdf+Gtc2vQpKtnNeoVQU0QM4iALHbyVtAgMBAAE=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS512.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIFqTCCA5GgAwIBAgIUUhXhOkGdJoScj8HrtuNh1YzVhoQwDQYJKoZIhvcNAQEL\nBQAwYzELMAkGA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcM\nDVNhbiBGcmFuY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UE\nCwwEamp3dDAgFw0yMzA4MjkwMDEzMDJaGA8zMDIzMDkwNjAwMTMwMlowYzELMAkG\nA1UEBhMCVVMxEzARBgNVBAgMCkNhbGlmb3JuaWExFjAUBgNVBAcMDVNhbiBGcmFu\nY2lzY28xGDAWBgNVBAoMD2pzb253ZWJ0b2tlbi5pbzENMAsGA1UECwwEamp3dDCC\nAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBALdOw5t27xjG/P5sLEyTrDhf\nz8RgHkgKS3vvZoFP1KemXga5dM/+isnwiCN3oSX5wYUNp1iORyBCA7YyUVv26Ay1\nfOZZjtUoxD6E6JDhYA5gAPbmsksmxeTkTfUmtDrnp2QEix/MSdTgT4/yA8uphz53\n2FprCmH555KEQ6j4bgC9MvzPsjb9swHy4uRCC+gt/K296YdVmp0TaQtIzuszP0fB\nY/yKHysi8ROEqsu44/q59E0uQtKJArv+73BWL7tjTDX/VBr6sZ9dg6hmCstlpmNA\ndpu5mZbIDV5duYOkXU4uW2KXpB8QYlyVtGPLEDlzOEb26vEu3+GOxDDi4zQP8Ffs\n6cpJEFYfcTtdHo4evDJRS7C7dK7P0ugEYcJAWyequJfOTrI9ZONGor3beiCIlsLL\nDw4h9pDZcJaGU2o5ykj3qOjo1Xeb1/OScxpy4u7geGl00o9hbxc6Wkn9NzhyZyQW\nxz5k7pj8dC6ArY30cumMd30n+WV4PfcXkhFrkYdIY/ylV43YARhX1HonrSdKLCo3\nmIFKcdq7qlhid2QVK/+mcEvrfWxcHOaizq1LBrWoSSpXvq6Nn2i8Yt2i+VoYzrBO\npxFYizXw4WHxoD7uEpvruh6Umi6SWSUbZj+RyQM4VmYvZM45V9PJ4XwF5PYKD49o\n7UMIjQPMMwYw6/iWu1E/AgMBAAGjUzBRMB0GA1UdDgQWBBSaQaeMwWJiQ03nes7u\nmE1O+GPSZTAfBgNVHSMEGDAWgBSaQaeMwWJiQ03nes7umE1O+GPSZTAPBgNVHRMB\nAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4ICAQCrnYPvh0Or407Vq7r2UcBy4APc\nFMM0Xy1O4mW6p+cfN6WxgRC/aq84zXpEP+nAe9qfDFqDEaEEVtn43XXMRnFXWDE/\neZFW0sHWt0qKmzbN2JqMIi1ERc186+hlTLvoNE7wkAdSIE2AO6Yh5bMHOcOHVZm9\nRZu5BGkunmnc1ipxrkmwf0vwdkw7CVQNjK0T68PlwH4smMev6R8W4UI+Bq6R/6VH\ndMX1rjdOCD6nh8OXAn45CCK6iUPp0thm/OvKtmKexLro1BhSEiG5Bu7bw11iDhI+\n0hw84bCgQmfFnUYzt4WRq0MZdlJ7HcjHqEJ06SM3EV1ZFjt1VhI95BuNRkrTS4gk\n1jTjhRANmgvr0GzGZQfzADY8JX1NgXcgGQnGQksBtfHgbV/h67PCgugaMOANTIs7\nEieZrI13CVp7HcBI22lSXlTjT2uaHhVXbdy/oKldPWhf79y9BhViHwuRWVpWJx+G\ntZWY66Q5yOwDoRqHM0/MMwrPSw66G6wIwsyWtShaqI9agg2urp0ryfsHUy/UR5Z5\n9X8GHJLMjRa0skr8caOylZ+9U7HFTtOg62lnb6L9Zp1SoEL001nk2oCDyBuQkA1X\nFXVQjItyKojRFG7Axif4Nags+wMlVu19B6qtI42UTdQxW7h+OV/k7mKkwXK9Ceoe\nGdGwZVylI7sJ4u/LGw==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS512.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMIIJQwIBADANBgkqhkiG9w0BAQEFAASCCS0wggkpAgEAAoICAQC3TsObdu8Yxvz+\nbCxMk6w4X8/EYB5ICkt772aBT9Snpl4GuXTP/orJ8Igjd6El+cGFDadYjkcgQgO2\nMlFb9ugMtXzmWY7VKMQ+hOiQ4WAOYAD25rJLJsXk5E31JrQ656dkBIsfzEnU4E+P\n8gPLqYc+d9haawph+eeShEOo+G4AvTL8z7I2/bMB8uLkQgvoLfytvemHVZqdE2kL\nSM7rMz9HwWP8ih8rIvEThKrLuOP6ufRNLkLSiQK7/u9wVi+7Y0w1/1Qa+rGfXYOo\nZgrLZaZjQHabuZmWyA1eXbmDpF1OLltil6QfEGJclbRjyxA5czhG9urxLt/hjsQw\n4uM0D/BX7OnKSRBWH3E7XR6OHrwyUUuwu3Suz9LoBGHCQFsnqriXzk6yPWTjRqK9\n23ogiJbCyw8OIfaQ2XCWhlNqOcpI96jo6NV3m9fzknMacuLu4HhpdNKPYW8XOlpJ\n/Tc4cmckFsc+ZO6Y/HQugK2N9HLpjHd9J/lleD33F5IRa5GHSGP8pVeN2AEYV9R6\nJ60nSiwqN5iBSnHau6pYYndkFSv/pnBL631sXBzmos6tSwa1qEkqV76ujZ9ovGLd\novlaGM6wTqcRWIs18OFh8aA+7hKb67oelJouklklG2Y/kckDOFZmL2TOOVfTyeF8\nBeT2Cg+PaO1DCI0DzDMGMOv4lrtRPwIDAQABAoICAA7Ma4tB/fR/SrkULOazvKFt\nJBNY+n4wt+kAqe2s7ws9KaBxBBwEky98B9RBL1UnBXdYjlbeXvdHjRAo83DcDTN1\ndWi3+Il/Od9IOz7zCvOuJCB1UxXbwqSDb+282wEEZvrynklfZI3XMWUlZya1KtIh\nZRSLnrIhNY+aYPH5DbxpNH68xR5JEKoKx0QhNt/e0o7eOEZbjq4Op+99cxhW3KLO\nMI8Yltfp1rzk0XsSk/PPuQgqwMH0G1s9A2yLcLK38KpNUNIVb2s2ZqTHiX3j2MZw\n060nvd1dN4MTnaCh5NclHpiQ4HLnj4v6RAQTG94KI2l15qq48OG/SlnIqgoFHWSA\nbLYKEACXXvDJeDaspbSV1LGOHnbXy/bYnaUTL5JHGVQMzlyAw19P81WyePWQm07M\nmQtYXwGNsZJCridfoFR0To1cXIm/lP4uTf9N6R2oqB8puo7UiO3RHFz96aJOFojg\nxQKGlaDqv2ep6i3elWUIqgbf3Ar552eHfRGaf/VdPGL/p1bsvxOsW6EVnXOLvnkm\nQ8WxlfycUMO1x/r8ESkUkQywWvK4CrHgC3mbCsQXjOS5PPpwCZ1xmywLdnaTS8dg\naNqovKOXN5TugylAX6iEl67LKgQ3JNni4h1VlZHzu+F1mBlkOeM8jdYhQTPLyrmU\nKC/cqTgANqvnCGZpXt2RAoIBAQD7apcvntneq5SRZujLFDtxVTW+i61jth3oXqF8\nVENZUaXeSRp+wTaOdmLy+Qq6DEYe7ztxPlg5n+Ag7aGQsuhjWagGDjcqRc1lQ+RV\njRW+F9jI4dle2IGgfE8GENJnkjZmGSkDUos44ruouR19pwbs975VmF23IS79k6to\n3/mqA6zA1khVxwF3k7eMHnyp+izpDiaBQazy699Jmq6ahP3cmc30pl+HiD+Hdw91\ny9gCYbnUkyewZ+EZXL2Shq9QT5ZmOjFz17EFPIS5qcT/OfL+not/ws3p309IS/19\neDGoHB8HWNf9KXiuKU8vMY5goX/DHUtUKfNLz0LNdIF9xToPAoIBAQC6pkwBNUtZ\ncnooB2kX7hBU/Ge1EJIXMnF0goaqdtVqQEUMp333HIGFCenfapN2AGQc+aAXViOj\ntEvWwMUyCW4lFkNl5Fmv4T0e/aKVV+73mU5RbKPDpnZHC+a+N+Ej/e/YP6tjXDFb\nhgq+H+JFNF5C7zlQu9/EKI8HIn9q7eFlnallWPJIBuztyUp6iEvH1IejQFGTRycJ\nLvwAh/b/utiTvpwGOmPidJO/wW/Czy9vxwYnju0stjyGDS6nb0lOVHklUtDM/GeA\nPm7INCnH1fatgNOs7SYaku4Frnwf1gnYZQ69dWL7Z0qYciLd6QmyhHAJmEI97Ve0\nRE6XjiL1QWXRAoIBAQC5KRVTYwA998uhElNcTPhCTWkZfjEigFuiaR8xO7WmMHWi\nMeCrfYy6ewIAw91ci/GQkswKaMn9FnuwhJI6yShRExSl8Q47udC9RbUCNLfQmit0\nNrEqIvXExghFBVqQIKSjqOAFrGMQcBuY9Ux83+g/2W16CI7scinzYzAYOuvahH5U\nPvxi//9am5XQQhu565/rvBork7zV41U9FgiFkHCOaU+/YFB2tqdjExJ1xLy/dE2x\n+vZ9+uNTFHQhV8QBt7uiajVFhZK/soRlYFzPQ1RspUNDwqp4FZiEPELihwS7NIq9\nlHbt4f8Y9R92OF8NV6OKhSaXQ5YFPQ2L1sQPZpONAoIBAQCTYClVLuKO/wALSUam\n+Xd101KDulDP9il1SHbSdEAwxAyBYGLq0lxGUryShxFmNArYghXxNzeu0/ap284Y\noy+QIxMWigZzBFMBfF16tFLgt/EKA9EO9AoiMPiBq5eelqxhwGwwaSQj0yP6WSyN\nXjsreL51y9J0jV3Z0VhwcuHtHV8awe+UBbvgSXcAZ0wGvf5XXbrLonvlGW8rcDAM\nXlkR14hUtpgVv9zFpRP86yDWjnyCka0eB8qkQhZbaFime7aoTMrHgGis7x8D/4QZ\n4Q0ElFsPCLV0eB1u6QXjEVr2vVy1JdIBFd7lngF/3Limd0ILoWh0g0oj8Wdo7XcW\n1gtxAoIBAHwep6P3WcnSuYMxUqYWYR1OFl3K/jddblwh9Sf659xNEG7PLEwy21Nj\nUJPBILVdqDoKpotf1q5P88qYyUt1JPFLxAEe60t/NUQ6eg5v1je8MraeeIuZx4qg\nm/+aviCYbmrs6xdKq4ha7CjMEv88epY9gGSc5yOoCNRtm8CCptAyhMbwkNpqpb8a\nZhyjZoni3pArsacM19FS3u6YTi2iwXdUXAI0tO2FP3+XaC8SAn2lN3bj9qtUTU8S\nMwGxTVCmE4L/hXea6Cn49Erf6H3a9QXrtUIMiQd9iClCEKdGTNuAdAM34suNyf/P\nA2Q5Ddd8nOBROKLGOJQnlWeHwuqHnHk=\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/RS512.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAt07Dm3bvGMb8/mwsTJOs\nOF/PxGAeSApLe+9mgU/Up6ZeBrl0z/6KyfCII3ehJfnBhQ2nWI5HIEIDtjJRW/bo\nDLV85lmO1SjEPoTokOFgDmAA9uaySybF5ORN9Sa0OuenZASLH8xJ1OBPj/IDy6mH\nPnfYWmsKYfnnkoRDqPhuAL0y/M+yNv2zAfLi5EIL6C38rb3ph1WanRNpC0jO6zM/\nR8Fj/IofKyLxE4Sqy7jj+rn0TS5C0okCu/7vcFYvu2NMNf9UGvqxn12DqGYKy2Wm\nY0B2m7mZlsgNXl25g6RdTi5bYpekHxBiXJW0Y8sQOXM4Rvbq8S7f4Y7EMOLjNA/w\nV+zpykkQVh9xO10ejh68MlFLsLt0rs/S6ARhwkBbJ6q4l85Osj1k40aivdt6IIiW\nwssPDiH2kNlwloZTajnKSPeo6OjVd5vX85JzGnLi7uB4aXTSj2FvFzpaSf03OHJn\nJBbHPmTumPx0LoCtjfRy6Yx3fSf5ZXg99xeSEWuRh0hj/KVXjdgBGFfUeietJ0os\nKjeYgUpx2ruqWGJ3ZBUr/6ZwS+t9bFwc5qLOrUsGtahJKle+ro2faLxi3aL5WhjO\nsE6nEViLNfDhYfGgPu4Sm+u6HpSaLpJZJRtmP5HJAzhWZi9kzjlX08nhfAXk9goP\nj2jtQwiNA8wzBjDr+Ja7UT8CAwEAAQ==\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/X25519.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIBgzCCATUCFEcl4MgdEi4IGwRwLbdU7hvhVwH8MAUGAytlcDBjMQswCQYDVQQG\nEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNj\nbzEYMBYGA1UECgwPanNvbndlYnRva2VuLmlvMQ0wCwYDVQQLDARqand0MCAXDTIz\nMDgyOTAwMTMwM1oYDzMwMjMwOTA2MDAxMzAzWjBjMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEYMBYGA1UE\nCgwPanNvbndlYnRva2VuLmlvMQ0wCwYDVQQLDARqand0MCowBQYDK2VuAyEA3UpG\n2jKsHGL52tU3K87tqPS3j2arQO9oGu8W3eT5BkgwBQYDK2VwA0EAh8bwXk5oV5vK\npcv2WyF1jt3E7eiK+DBbOnAdbu86Je887oy8JypSfUwCartW55+MkD/+IJZTNhFa\nGal7t0UqBA==\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/X25519.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMC4CAQAwBQYDK2VuBCIEIECBHYocYAlHkDtyWQC2luStNys0iYQwZ1CrtRZ8kVhk\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/X25519.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VuAyEA3UpG2jKsHGL52tU3K87tqPS3j2arQO9oGu8W3eT5Bkg=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/X448.crt.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN CERTIFICATE-----\nMIIBzTCCAU0CFFayc15yJtacar3KWzbbJbmr3GueMAUGAytlcTBjMQswCQYDVQQG\nEwJVUzETMBEGA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNj\nbzEYMBYGA1UECgwPanNvbndlYnRva2VuLmlvMQ0wCwYDVQQLDARqand0MCAXDTIz\nMDgyOTAwMTMwM1oYDzMwMjMwOTA2MDAxMzAzWjBjMQswCQYDVQQGEwJVUzETMBEG\nA1UECAwKQ2FsaWZvcm5pYTEWMBQGA1UEBwwNU2FuIEZyYW5jaXNjbzEYMBYGA1UE\nCgwPanNvbndlYnRva2VuLmlvMQ0wCwYDVQQLDARqand0MEIwBQYDK2VvAzkAXxQl\nWa22S36qjui/M2IBT5vg0CmmLJkpBhXeiuBptUxJ/nnD0uITBH5N9PHkhOM8gfGt\nNkh6JwcwBQYDK2VxA3MAkjzcFHJoPuc/cNyoNq5DT5yCMYKzu0OhnDNmMQTSbmCC\nqJMAobd9pBVqG2LQI7SlB8beP8/gI2wArsjJYmCttBgQR5YaxO0I8cRsX++2Asl8\nalIYnJFhPR1j9j37JAz/TFyGIw6kA9efOtPFIgUtCyoA\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/X448.pkcs8.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PRIVATE KEY-----\nMEYCAQAwBQYDK2VvBDoEOLhgwDP91iAq39CN2sVdt+wYX0ocn+qZnwpmXzmyZnPj\niec8mcz2DVlE25HO1R0qGI05VRuTJSP+\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/X448.pub.pem",
    "content": "#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n-----BEGIN PUBLIC KEY-----\nMEIwBQYDK2VvAzkAXxQlWa22S36qjui/M2IBT5vg0CmmLJkpBhXeiuBptUxJ/nnD\n0uITBH5N9PHkhOM8gfGtNkh6Jwc=\n-----END PUBLIC KEY-----\n"
  },
  {
    "path": "impl/src/test/resources/io/jsonwebtoken/impl/security/genkeys",
    "content": "#!/usr/bin/env bash\n#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nset -Eeuo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/\n\nfunction main() {\n\n  local names=\n  local name=\n\n  if ! command -v openssl >/dev/null 2>&1; then\n    echo \"openssl is not available\"\n    return 1\n  fi\n\n  names=('RS256' 'RS384' 'RS512' 'PS256' 'PS384' 'PS512' 'ES256' 'ES384' 'ES512' 'Ed25519' 'Ed448' 'X25519' 'X448' )\n\n  for name in \"${names[@]}\"; do\n\n    local args=()\n    local x509args=()\n    local privfile=\"${name}.pkcs8.pem\"\n    local pubfile=\"${name}.pub.pem\"\n    local certfile=\"${name}.crt.pem\"\n    local size=\"${name:2:3}\"\n    local keysize=\"${size}\"\n    local algorithm=\"${name}\"\n\n    if [[ \"${name}\" = RS* || \"${name}\" = PS* ]]; then\n      algorithm='RSA'\n      keysize=$((size * 8)) # 256 -> 2048, 384-> 3072, 512 -> 4096\n      args+=( '-pkeyopt' \"rsa_keygen_bits:${keysize}\" )\n    fi\n    if [[ \"${name}\" = PS* ]]; then\n      algorithm='RSA-PSS'\n      local saltlen=$((size / 8))\n      args+=( '-pkeyopt' \"rsa_pss_keygen_md:sha${size}\" '-pkeyopt' \"rsa_pss_keygen_mgf1_md:sha${size}\" '-pkeyopt' \"rsa_pss_keygen_saltlen:${saltlen}\" )\n      x509args+=( '-sigopt' 'rsa_padding_mode:pss' '-sigopt' \"rsa_pss_saltlen:${saltlen}\" '-sigopt' \"rsa_mgf1_md:sha${size}\" \"-sha${size}\" )\n    elif [[ \"${name}\" = ES* ]]; then\n      algorithm='EC'\n      if [[ \"${size}\" == '512' ]]; then size=521; fi\n      args+=( '-pkeyopt' \"ec_paramgen_curve:P-${size}\" )\n    fi\n\n    # generate the private key:\n    openssl genpkey -algorithm \"${algorithm}\" \"${args[@]}\" -out \"${privfile}\" 2>/dev/null\n\n    # derive the public key from the private key:\n    openssl pkey -in \"${privfile}\" -out \"${pubfile}\" -pubout\n\n    # create a self-signed certificate:\n    if [[ \"${name}\" = X* ]]; then\n      # X25519 and X448 can't be self-signed (they can't be used for signatures, only key agreement), so we'll force\n      # creating a cert ('using the -force_pubkey option), signing with the Ed* keys instead:\n      local edname=\"Ed${name:1}\" # strip X, replace with Ed\n      openssl req -new -key \"${edname}.pkcs8.pem\" -out \"${edname}.csr\" -subj '/C=US/ST=California/L=San Francisco/O=jsonwebtoken.io/OU=jjwt'\n      openssl x509 -req -in \"${edname}.csr\" -CAkey \"${edname}.pkcs8.pem\" -CA \"${edname}.crt.pem\" -force_pubkey \"${pubfile}\" -out \"${certfile}\" -CAcreateserial -days 365250 2>/dev/null\n      # cleanup intermediate files:\n      rm -rf \"${edname}.csr\"\n      rm -rf \"${edname}.crt.srl\"\n    else # create a normal self signed certificate:\n      openssl req -new -x509 -key \"${privfile}\" -out \"${certfile}\" -days 365250 -subj '/C=US/ST=California/L=San Francisco/O=jsonwebtoken.io/OU=jjwt' \"${x509args[@]}\"\n    fi\n\n  done # end name loop\n}\nmain \"$@\"\n"
  },
  {
    "path": "impl/src/test/resources/io.jsonwebtoken.io.Deserializer.test.gson",
    "content": "io.jsonwebtoken.gson.io.GsonDeserializer"
  },
  {
    "path": "impl/src/test/resources/io.jsonwebtoken.io.Deserializer.test.orgjson",
    "content": "io.jsonwebtoken.orgjson.io.OrgJsonDeserializer"
  },
  {
    "path": "impl/src/test/resources/io.jsonwebtoken.io.Serializer.test.gson",
    "content": "io.jsonwebtoken.gson.io.GsonSerializer"
  },
  {
    "path": "impl/src/test/resources/io.jsonwebtoken.io.Serializer.test.orgjson",
    "content": "io.jsonwebtoken.orgjson.io.OrgJsonSerializer"
  },
  {
    "path": "impl/src/test/resources/io.jsonwebtoken.io.compression.CompressionCodec.test.override",
    "content": "io.jsonwebtoken.impl.compression.YagCompressionCodec"
  },
  {
    "path": "impl/src/test/scripts/softhsm",
    "content": "#!/usr/bin/env bash\n#\n# Copyright © 2023 jsonwebtoken.io\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\nset -Eeuo pipefail # https://vaneyckt.io/posts/safer_bash_scripts_with_set_euxo_pipefail/\n\n_readlink() {\n  $(type -p greadlink readlink | head -1) \"$1\" # prefer greadlink if it exists\n}\n\n_dirpath() {\n  [[ -z \"$1\" ]] && echo \"_dirpath: a directory argument is required.\" >&2 && return 1\n  [[ ! -d \"$1\" ]] && echo \"_dirpath: argument is not a directory: $1\" >&2 && return 1\n  local dirpath\n  dirpath=\"$(cd -P \"$1\" && pwd)\"\n  echo \"$dirpath\"\n}\n\n_filepath() {\n  [[ -d \"$1\" ]] && echo \"_filepath: directory arguments are not permitted\" >&2 && return 1\n  local dirname filename canonical_dir\n  dirname=\"$(dirname \"$1\")\"\n  filename=\"$(basename \"$1\")\"\n  canonical_dir=\"$(_dirpath \"$dirname\")\"\n  echo \"$canonical_dir/$filename\"\n}\n\n##\n# Returns the canonical filesystem path of the specified argument\n# Argument must be a directory or a file\n##\n_path() {\n  local target=\"$1\"\n  if [[ -d \"$target\" ]]; then # target is a directory, get its canonical path:\n    target=\"$(_dirpath \"$target\")\"\n  else\n    while [[ -L \"$target\" ]]; do # target is a symlink, so resolve it\n      target=\"$(_readlink \"$target\")\"\n      if [[ \"$target\" != /* ]]; then # target doesn't start with '/', so it's not yet absolute.  Fix that:\n        target=\"$(_filepath \"$target\")\"\n      fi\n    done\n    target=\"$(_filepath \"$target\")\"\n  fi\n  echo \"$target\"\n}\n\n# global vars used across functions\nSOFTHSM2_CONF=\"${SOFTHSM2_CONF:-}\"\nscript_file=\nscript_dir=\nscript_name=\nuser_home=\ntest_keys_dir=\nplatform=\nlibsofthsm2=\nglobalconf=\nuserconf=\nconfdir=\ntokendir=\n\nscript_file=\"$(_path \"${BASH_SOURCE[0]}\")\" # canonicalize\nscript_dir=\"$(dirname \"$script_file\")\"\nscript_name=\"$(basename \"${script_file}\")\"\nuser_home=\"$(_path \"${HOME}\")\"\n\n_softhsmu() {\n  softhsm2-util --so-pin 1234 --pin 1234 --token jjwt \"$@\"\n}\n\n_pkcs11t() {\n  pkcs11-tool --module \"${libsofthsm2}\" --so-pin 1234 --pin 1234 --token-label jjwt \"$@\"\n}\n\n_log() {\n  echo \"${script_name}: $1\"\n}\n\n_errexit() {\n  _log \"$1, exiting.\"\n  # cleanup any leftover intermediate DER files that may exist\n  cd \"${test_keys_dir}\" >/dev/null 2>&1 && rm -rf -- *.der >/dev/null 2>&1\n  exit 1\n}\n\n# Common setup logic necessary for both 'import' and 'configure'\n_setup() {\n\n  if ! command -v command -v softhsm2-util >/dev/null 2>&1; then\n    _errexit \"softhsm2-util command is not available. Install with 'brew install softhsm' or 'sudo apt-get -y install softhsm2'\"\n  fi\n  if ! command -v pkcs11-tool >/dev/null 2>&1; then\n    _errexit \"pkcs11-tool command is not available. Install with 'brew install opensc' or 'sudo apt-get -y install opensc'\"\n  fi\n\n  test_keys_dir=\"${script_dir}/../resources/io/jsonwebtoken/impl/security\"\n  test_keys_dir=\"$(_path ${test_keys_dir})\" # canonicalize\n\n  platform='macos'\n  globalconf='/opt/homebrew/etc/softhsm/softhsm2.conf'\n  libsofthsm2='/opt/homebrew/lib/softhsm/libsofthsm2.so'\n  if [[ ! -f \"${libsofthsm2}\" ]]; then # backup (build softhsm from source)\n    libsofthsm2='/usr/local/lib/softhsm/libsofthsm2.so'\n  fi\n  if [[ ! -f \"${libsofthsm2}\" ]]; then # assume CI (Ubuntu)\n    platform='ubuntu'\n    globalconf='/etc/softhsm/softhsm2.conf'\n    libsofthsm2='/usr/lib/softhsm/libsofthsm2.so'\n  fi\n  [[ -f \"${libsofthsm2}\" ]] || _errexit \"cannot locate libsofthsm2.so\"\n\n  userconf=\"${user_home}/.config/softhsm2/softhsm2.conf\" # canonical due to user_home above\n}\n\n_assert_conf() {\n  softhsm2-util --show-slots >/dev/null 2>&1 || _errexit \"Invalid or missing SoftHSM configuration, check ${userconf} or ${globalconf}\"\n}\n\n_configure() {\n\n  local confdir=\n  local opt=\"${1:-}\"\n  local tokendir=\"${2:-}\"\n  if [[ \"${opt}\" == '--tokendir' ]]; then\n    [[ -n \"${tokendir}\" ]] || _errexit \"--tokendir value cannot be empty\"\n    tokendir=\"$(_path \"${tokendir}\")\" #canonicalize\n  fi\n\n  _setup\n\n  if ! softhsm2-util --show-slots >/dev/null 2>&1; then # missing or erroneous config\n\n    if [[ -z \"${SOFTHSM2_CONF}\" && ! -f \"${userconf}\" ]]; then # no env var or file, try to create it:\n\n      _log \"Creating ${userconf} file...\"\n\n      export SOFTHSM2_CONF=\"${userconf}\"\n      confdir=\"$(dirname \"${SOFTHSM2_CONF}\")\"\n      [[ -n \"${tokendir}\" ]] || tokendir=\"${confdir}/tokens\" # assign default\n\n      mkdir -p \"${confdir}\" || _errexit \"unable to ensure ${confdir} exists\"\n      mkdir -p \"${tokendir}\" || _errexit \"unable to ensure ${tokendir} exists\"\n\n      cat <<EOF >>\"${SOFTHSM2_CONF}\"\n      # SoftHSM v2 configuration file\n\n      directories.tokendir = ${tokendir}\n      objectstore.backend = file\n\n      # ERROR, WARNING, INFO, DEBUG\n      log.level = DEBUG\n\n      # If CKF_REMOVABLE_DEVICE flag should be set\n      slots.removable = false\n\n      # Enable and disable PKCS#11 mechanisms using slots.mechanisms.\n      slots.mechanisms = ALL\n\n      # If the library should reset the state on fork\n      library.reset_on_fork = false\nEOF\n      local retval=\"$?\"\n      [[ \"${retval}\" -eq 0 ]] && _log \"created ${SOFTHSM2_CONF}\" || _errexit \"unable to create ${SOFTHSM2_CONF}\"\n    fi\n  fi\n\n  _assert_conf\n}\n\n_import() {\n\n  local name algid index\n\n  _setup\n  _assert_conf\n  cd \"${test_keys_dir}\" || _errexit \"Unable to cd to ${test_keys_dir}\"\n\n  echo\n  # delete any existing JJWT slot/tokens\n  if softhsm2-util --show-slots | grep 'Label:' | grep 'jjwt' >/dev/null 2>&1; then\n    _log \"deleting existing softhsm jjwt slot...\"\n    softhsm2-util --delete-token --token jjwt || _errexit \"unable to delete jjwt slot\"\n  fi\n\n  echo\n  _log \"creating softhsm jjwt slot...\"\n  softhsm2-util --init-token --free --label jjwt --so-pin 1234 --pin 1234 || _errexit \"unable to create jjwt slot\"\n  echo\n\n  index=0\n  for name in $(# name will be unqualified, e.g. RS256.pkcs8.pem\n    ls *.pkcs8.pem | sort -f\n  ); do\n\n    algid=\"${name%%.*}\" # RS256.pkcs8.pem --> RS256\n    local privpem privder privpkcs1 pubpem pubder certpem certder hexid\n    privpem=\"${name}\"\n    privpkcs1=\"${algid}.priv.pkcs1.pem\"\n    privder=\"${algid}.priv.der\"\n    pubpem=\"${algid}.pub.pem\"\n    pubder=\"${algid}.pub.der\"\n    certpem=\"${algid}.crt.pem\"\n    certder=\"${algid}.crt.der\"\n\n    hexid=\"$(printf '%04x' ${index})\"\n    hexid=\"${hexid^^}\"\n\n    _log \"Creating temporary ${algid} der files for pkcs11-tool import\"\n    if [[ \"${algid}\" = RS* ]]; then # https://github.com/OpenSC/OpenSC/issues/2854\n      openssl pkey -in \"${privpem}\" -out \"${privpkcs1}\" -traditional || _errexit \"can't create ${algid} private key pkcs1 file\"\n      privpem=\"${privpkcs1}\" # reassign\n    fi\n    openssl pkey -in \"${privpem}\" -out \"${privder}\" -outform DER || { rm -rf \"${privpkcs1}\"; _errexit \"can't create ${algid} private key der file\"; }\n    rm -rf \"${privpkcs1}\" # in case we generated it\n    [[ -f \"${privder}\" ]] || _errexit \"can't create ${algid} private key der file\"\n\n    openssl pkey -in \"${pubpem}\" -pubin -out \"${pubder}\" -outform DER || _errexit \"can't create ${algid} public key der file\"\n    openssl x509 -in \"${certpem}\" -out \"${certder}\" -outform DER || _errexit \"can't create ${algid} x509 cert der file\"\n\n    _log \"Importing ${algid} keypair with id ${hexid}\"\n    if [[ \"${algid}\" != PS* ]]; then # no softhsm2 RSA-PSS support: https://github.com/opendnssec/SoftHSMv2/issues/721\n      if [[ \"${algid}\" = Ed* || \"${algid}\" = RS* || \"${algid}\" = X* ]]; then\n        # pkcs11-tool backed by softhsm2 cannot import the private key .der files, so use softhsm2-util directly:\n        _softhsmu --import \"${name}\" --label \"${algid}\" --id \"${hexid}\" || _errexit \"can't import ${algid} key pair\"\n      else # ES*\n        _pkcs11t --write-object \"${privder}\" --usage-derive --label \"${algid}\" --type privkey --id \"${hexid}\" || _errexit \"can't import ${algid} private key\"\n        _pkcs11t --write-object \"${pubder}\" --usage-derive --label \"${algid}\" --type pubkey --id \"${hexid}\" || _errexit \"can't import ${algid} public key\"\n      fi\n    fi\n\n    _log \"Importing ${algid} x509 cert with id ${hexid}\"\n    _pkcs11t --write-object \"${certder}\" --label \"${algid}\" --type cert --id \"${hexid}\" || _errexit \"can't import x509 cert\"\n\n    _log \"Deleting temporary ${algid} der files\"\n    rm -rf -- *.der\n    echo\n\n    index=$((index + 1)) # increment id counter\n\n  done # end name loop\n}\n\nmain() {\n  local command=\"${1:-}\"\n  local retval=0\n  case \"$command\" in\n  \"\" | \"-h\" | \"--help\")\n    echo \"usage: softhsm [options...] <command>\"\n    echo\n    echo \"commands:\"\n    echo \"   help         Display this help notice\"\n    echo \"   configure    Ensure SoftHSM2 user config file ~/.config/softhsm2/softhsm2.conf exists\"\n    echo \"   import       (Re)create SoftHSM2 'jjwt' slot and import all JJWT test keys and certificates\"\n    echo\n    echo \"For further help, search for answers or ask a question here:\"\n    echo \"https://github.com/jwtk/jjwt/discussions/new/choose\"\n    ;;\n  \"import\")\n    shift 1\n    _import \"$@\"\n    retval=\"$?\"\n    ;;\n  \"configure\")\n    shift 1\n    _configure \"$@\"\n    retval=\"$?\"\n    ;;\n  *)\n    echo \"softhsm: no such command '$command'\" >&2\n    exit 1\n    ;;\n  esac\n\n  return ${retval}\n}\nmain \"$@\"\nexit \"$?\"\n"
  },
  {
    "path": "mvnw",
    "content": "#!/bin/sh\n# ----------------------------------------------------------------------------\n# Licensed to the Apache Software Foundation (ASF) under one\n# or more contributor license agreements.  See the NOTICE file\n# distributed with this work for additional information\n# regarding copyright ownership.  The ASF licenses this file\n# to you under the Apache License, Version 2.0 (the\n# \"License\"); you may not use this file except in compliance\n# with the License.  You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing,\n# software distributed under the License is distributed on an\n# \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\n# KIND, either express or implied.  See the License for the\n# specific language governing permissions and limitations\n# under the License.\n# ----------------------------------------------------------------------------\n\n# ----------------------------------------------------------------------------\n# Apache Maven Wrapper startup batch script, version 3.2.0\n#\n# Optional ENV vars\n# -----------------\n#   JAVA_HOME - location of a JDK home dir, required when download maven via java source\n#   MVNW_REPOURL - repo url base for downloading maven distribution\n#   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven\n#   MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output\n# ----------------------------------------------------------------------------\n\nset -euf\n[ \"${MVNW_VERBOSE-}\" != debug ] || set -x\n\n# OS specific support.\nnative_path() { printf %s\\\\n \"$1\"; }\ncase \"$(uname)\" in\n(CYGWIN*|MINGW*) [ -z \"${JAVA_HOME-}\" ] || JAVA_HOME=\"$(cygpath --unix \"$JAVA_HOME\")\"\n                 native_path() { cygpath --path --windows \"$1\"; } ;;\nesac\n\n# set JAVACMD and JAVACCMD\nset_java_home() {\n  # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched\n  if [ -n \"${JAVA_HOME-}\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n      # IBM's JDK on AIX uses strange locations for the executables\n      JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n      JAVACCMD=\"$JAVA_HOME/jre/sh/javac\"\n    else\n      JAVACMD=\"$JAVA_HOME/bin/java\"\n      JAVACCMD=\"$JAVA_HOME/bin/javac\"\n\n      if [ ! -x \"$JAVACMD\" ] || [ ! -x \"$JAVACCMD\" ] ; then\n        echo \"The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run.\" >&2\n        echo \"JAVA_HOME is set to \\\"$JAVA_HOME\\\", but \\\"\\$JAVA_HOME/bin/java\\\" or \\\"\\$JAVA_HOME/bin/javac\\\" does not exist.\" >&2\n        return 1\n      fi\n    fi\n  else\n    JAVACMD=\"$('set' +e; 'unset' -f command 2>/dev/null; 'command' -v java)\" || :\n    JAVACCMD=\"$('set' +e; 'unset' -f command 2>/dev/null; 'command' -v javac)\" || :\n\n    if [ ! -x \"${JAVACMD-}\" ] || [ ! -x \"${JAVACCMD-}\" ] ; then\n      echo \"The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run.\" >&2\n      return 1\n    fi\n  fi\n}\n\n# hash string like Java String::hashCode\nhash_string() {\n  str=\"${1:-}\" h=0\n  while [ -n \"$str\" ]; do\n    h=$(( ( h * 31 + $(LC_CTYPE=C printf %d \"'$str\") ) % 4294967296 ))\n    str=\"${str#?}\"\n  done\n  printf %x\\\\n $h\n}\n\nverbose() { :; }\n[ \"${MVNW_VERBOSE-}\" != true ] || verbose() { printf %s\\\\n \"${1-}\"; }\n\ndie() {\n  printf %s\\\\n \"$1\" >&2\n  exit 1\n}\n\n# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties\nwhile IFS=\"=\" read -r key value; do\n  case \"${key-}\" in\n    distributionUrl) distributionUrl=\"${value-}\" ;;\n    distributionSha256Sum) distributionSha256Sum=\"${value-}\" ;;\n  esac\ndone < \"${0%/*}/.mvn/wrapper/maven-wrapper.properties\"\n[ -n \"${distributionUrl-}\" ] || die \"cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties\"\n\n\ncase \"${distributionUrl##*/}\" in\n(maven-mvnd-*bin.*)\n  MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/\n  case \"${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)\" in\n  (*AMD64:CYGWIN*|*AMD64:MINGW*) distributionPlatform=windows-amd64 ;;\n  (:Darwin*x86_64) distributionPlatform=darwin-amd64 ;;\n  (:Darwin*arm64) distributionPlatform=darwin-aarch64 ;;\n  (:Linux*x86_64*) distributionPlatform=linux-amd64 ;;\n  (*) echo \"Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version\" >&2\n      distributionPlatform=linux-amd64\n      ;;\n  esac\n  distributionUrl=\"${distributionUrl%-bin.*}-$distributionPlatform.zip\"\n  ;;\n(maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;;\n(*) MVN_CMD=\"mvn${0##*/mvnw}\" _MVNW_REPO_PATTERN=/org/apache/maven/ ;;\nesac\n\n# apply MVNW_REPOURL and calculate MAVEN_HOME\n# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>\n[ -z \"${MVNW_REPOURL-}\" ] || distributionUrl=\"$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*\"$_MVNW_REPO_PATTERN\"}\"\ndistributionUrlName=\"${distributionUrl##*/}\"\ndistributionUrlNameMain=\"${distributionUrlName%.*}\"\ndistributionUrlNameMain=\"${distributionUrlNameMain%-bin}\"\nMAVEN_HOME=\"$HOME/.m2/wrapper/dists/${distributionUrlNameMain-}/$(hash_string \"$distributionUrl\")\"\n\nexec_maven() {\n  unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || :\n  exec \"$MAVEN_HOME/bin/$MVN_CMD\" \"$@\" || die \"cannot exec $MAVEN_HOME/bin/$MVN_CMD\"\n}\n\nif [ -d \"$MAVEN_HOME\" ]; then\n  verbose \"found existing MAVEN_HOME at $MAVEN_HOME\"\n  exec_maven \"$@\"\nfi\n\ncase \"${distributionUrl-}\" in\n(*?-bin.zip|*?maven-mvnd-?*-?*.zip) ;;\n(*) die \"distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'\" ;;\nesac\n\n# prepare tmp dir\nif TMP_DOWNLOAD_DIR=\"$(mktemp -d)\" && [ -d \"$TMP_DOWNLOAD_DIR\" ]; then\n  clean() { rm -rf -- \"$TMP_DOWNLOAD_DIR\"; }\n  trap clean HUP INT TERM EXIT\nelse\n  die \"cannot create temp dir\"\nfi\n\nmkdir -p -- \"${MAVEN_HOME%/*}\"\n\n# Download and Install Apache Maven\nverbose \"Couldn't find MAVEN_HOME, downloading and installing it ...\"\nverbose \"Downloading from: $distributionUrl\"\nverbose \"Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName\"\n\n# select .zip or .tar.gz\nif ! command -v unzip >/dev/null; then\n  distributionUrl=\"${distributionUrl%.zip}.tar.gz\"\n  distributionUrlName=\"${distributionUrl##*/}\"\nfi\n\n# verbose opt\n__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR=''\n[ \"${MVNW_VERBOSE-}\" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v\n\n# normalize http auth\ncase \"${MVNW_PASSWORD:+has-password}\" in\n'') MVNW_USERNAME='' MVNW_PASSWORD='' ;;\nhas-password) [ -n \"${MVNW_USERNAME-}\" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;;\nesac\n\nif [ -z \"${MVNW_USERNAME-}\" ] && command -v wget > /dev/null; then\n  verbose \"Found wget ... using wget\"\n  wget ${__MVNW_QUIET_WGET:+\"$__MVNW_QUIET_WGET\"} \"$distributionUrl\" -O \"$TMP_DOWNLOAD_DIR/$distributionUrlName\"\nelif [ -z \"${MVNW_USERNAME-}\" ] && command -v curl > /dev/null; then\n  verbose \"Found curl ... using curl\"\n  curl ${__MVNW_QUIET_CURL:+\"$__MVNW_QUIET_CURL\"} -f -L -o \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" \"$distributionUrl\"\nelif set_java_home; then\n  verbose \"Falling back to use Java to download\"\n  javaSource=\"$TMP_DOWNLOAD_DIR/Downloader.java\"\n  targetZip=\"$TMP_DOWNLOAD_DIR/$distributionUrlName\"\n  cat > \"$javaSource\" <<-END\n\tpublic class Downloader extends java.net.Authenticator\n\t{\n\t  protected java.net.PasswordAuthentication getPasswordAuthentication()\n\t  {\n\t    return new java.net.PasswordAuthentication( System.getenv( \"MVNW_USERNAME\" ), System.getenv( \"MVNW_PASSWORD\" ).toCharArray() );\n\t  }\n\t  public static void main( String[] args ) throws Exception\n\t  {\n\t    setDefault( new Downloader() );\n\t    java.nio.file.Files.copy( new java.net.URL( args[0] ).openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() );\n\t  }\n\t}\n\tEND\n  # For Cygwin/MinGW, switch paths to Windows format before running javac and java\n  verbose \" - Compiling Downloader.java ...\"\n  \"$(native_path \"$JAVACCMD\")\" \"$(native_path \"$javaSource\")\"\n  verbose \" - Running Downloader.java ...\"\n  \"$(native_path \"$JAVACMD\")\" -cp \"$(native_path \"$TMP_DOWNLOAD_DIR\")\" Downloader \"$distributionUrl\" \"$(native_path \"$targetZip\")\"\nfi\n\n# If specified, validate the SHA-256 sum of the Maven distribution zip file\nif [ -n \"${distributionSha256Sum-}\" ]; then\n  distributionSha256Result=false\n  if [ \"$MVN_CMD\" = mvnd.sh ]; then\n    echo \"Checksum validation is not supported for maven-mvnd.\" >&2\n    echo \"Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\" >&2\n    exit 1\n  elif command -v sha256sum > /dev/null; then\n    if echo \"$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName\" | sha256sum -c > /dev/null 2>&1; then\n      distributionSha256Result=true\n    fi\n  elif command -v shasum > /dev/null; then\n    if echo \"$distributionSha256Sum  $TMP_DOWNLOAD_DIR/$distributionUrlName\" | shasum -a 256 -c > /dev/null 2>&1; then\n      distributionSha256Result=true\n    fi\n  else\n    echo \"Checksum validation was requested but neither 'sha256sum' or 'shasum' are available.\" >&2\n    echo \"Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\" >&2\n    exit 1\n  fi\n  if [ $distributionSha256Result = false ]; then\n    echo \"Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised.\" >&2\n    echo \"If you updated your Maven version, you need to update the specified distributionSha256Sum property.\" >&2\n    exit 1\n  fi\nfi\n\n# unzip and move\nif command -v unzip > /dev/null; then\n  unzip ${__MVNW_QUIET_UNZIP:+\"$__MVNW_QUIET_UNZIP\"} \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -d \"$TMP_DOWNLOAD_DIR\"\nelse\n  tar xzf${__MVNW_QUIET_TAR:+\"$__MVNW_QUIET_TAR\"} \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -C \"$TMP_DOWNLOAD_DIR\"\nfi\nprintf %s\\\\n \"$distributionUrl\" > \"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url\"\nmv -- \"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain\" \"$MAVEN_HOME\" || [ -d \"$MAVEN_HOME\" ] || die \"fail to move MAVEN_HOME\"\n\nclean || :\nexec_maven \"$@\"\n"
  },
  {
    "path": "mvnw.cmd",
    "content": "<# : batch portion\r\n@REM ----------------------------------------------------------------------------\r\n@REM Licensed to the Apache Software Foundation (ASF) under one\r\n@REM or more contributor license agreements.  See the NOTICE file\r\n@REM distributed with this work for additional information\r\n@REM regarding copyright ownership.  The ASF licenses this file\r\n@REM to you under the Apache License, Version 2.0 (the\r\n@REM \"License\"); you may not use this file except in compliance\r\n@REM with the License.  You may obtain a copy of the License at\r\n@REM\r\n@REM    http://www.apache.org/licenses/LICENSE-2.0\r\n@REM\r\n@REM Unless required by applicable law or agreed to in writing,\r\n@REM software distributed under the License is distributed on an\r\n@REM \"AS IS\" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY\r\n@REM KIND, either express or implied.  See the License for the\r\n@REM specific language governing permissions and limitations\r\n@REM under the License.\r\n@REM ----------------------------------------------------------------------------\r\n\r\n@REM ----------------------------------------------------------------------------\r\n@REM Apache Maven Wrapper startup batch script, version 3.2.0\r\n@REM\r\n@REM Optional ENV vars\r\n@REM   MVNW_REPOURL - repo url base for downloading maven distribution\r\n@REM   MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven\r\n@REM   MVNW_VERBOSE - true: enable verbose log; others: silence the output\r\n@REM ----------------------------------------------------------------------------\r\n\r\n@IF \"%__MVNW_ARG0_NAME__%\"==\"\" (SET __MVNW_ARG0_NAME__=%~nx0)\r\n@SET __MVNW_CMD__=\r\n@SET __MVNW_ERROR__=\r\n@SET __MVNW_PSMODULEP_SAVE=%PSModulePath%\r\n@SET PSModulePath=\r\n@FOR /F \"usebackq tokens=1* delims==\" %%A IN (`powershell -noprofile \"& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}\"`) DO @(\r\n  IF \"%%A\"==\"MVN_CMD\" (set __MVNW_CMD__=%%B) ELSE IF \"%%B\"==\"\" (echo %%A) ELSE (echo %%A=%%B)\r\n)\r\n@SET PSModulePath=%__MVNW_PSMODULEP_SAVE%\r\n@SET __MVNW_PSMODULEP_SAVE=\r\n@SET __MVNW_ARG0_NAME__=\r\n@SET MVNW_USERNAME=\r\n@SET MVNW_PASSWORD=\r\n@IF NOT \"%__MVNW_CMD__%\"==\"\" (%__MVNW_CMD__% %*)\r\n@echo Cannot start maven from wrapper >&2 && exit /b 1\r\n@GOTO :EOF\r\n: end batch / begin powershell #>\r\n\r\n$ErrorActionPreference = \"Stop\"\r\nif ($env:MVNW_VERBOSE -eq \"true\") {\r\n  $VerbosePreference = \"Continue\"\r\n}\r\n\r\n# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties\r\n$distributionUrl = (Get-Content -Raw \"$scriptDir/.mvn/wrapper/maven-wrapper.properties\" | ConvertFrom-StringData).distributionUrl\r\nif (!$distributionUrl) {\r\n  Write-Error \"cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties\"\r\n}\r\n\r\nswitch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) {\r\n  \"maven-mvnd-*\" {\r\n    $USE_MVND = $true\r\n    $distributionUrl = $distributionUrl -replace '-bin\\.[^.]*$',\"-windows-amd64.zip\"\r\n    $MVN_CMD = \"mvnd.cmd\"\r\n    break\r\n  }\r\n  default {\r\n    $USE_MVND = $false\r\n    $MVN_CMD = $script -replace '^mvnw','mvn'\r\n    break\r\n  }\r\n}\r\n\r\n# apply MVNW_REPOURL and calculate MAVEN_HOME\r\n# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-<version>,maven-mvnd-<version>-<platform>}/<hash>\r\nif ($env:MVNW_REPOURL) {\r\n  $MVNW_REPO_PATTERN = if ($USE_MVND) { \"/org/apache/maven/\" } else { \"/maven/mvnd/\" }\r\n  $distributionUrl = \"$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')\"\r\n}\r\n$distributionUrlName = $distributionUrl -replace '^.*/',''\r\n$distributionUrlNameMain = $distributionUrlName -replace '\\.[^.]*$','' -replace '-bin$',''\r\n$MAVEN_HOME_PARENT = \"$HOME/.m2/wrapper/dists/$distributionUrlNameMain\"\r\n$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString(\"x2\")}) -join ''\r\n$MAVEN_HOME = \"$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME\"\r\n\r\nif (Test-Path -Path \"$MAVEN_HOME\" -PathType Container) {\r\n  Write-Verbose \"found existing MAVEN_HOME at $MAVEN_HOME\"\r\n  Write-Output \"MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD\"\r\n  exit $?\r\n}\r\n\r\nif (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) {\r\n  Write-Error \"distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl\"\r\n}\r\n\r\n# prepare tmp dir\r\n$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile\r\n$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path \"$TMP_DOWNLOAD_DIR_HOLDER.dir\"\r\n$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null\r\ntrap {\r\n  if ($TMP_DOWNLOAD_DIR.Exists) {\r\n    try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }\r\n    catch { Write-Warning \"Cannot remove $TMP_DOWNLOAD_DIR\" }\r\n  }\r\n}\r\n\r\nNew-Item -Itemtype Directory -Path \"$MAVEN_HOME_PARENT\" -Force | Out-Null\r\n\r\n# Download and Install Apache Maven\r\nWrite-Verbose \"Couldn't find MAVEN_HOME, downloading and installing it ...\"\r\nWrite-Verbose \"Downloading from: $distributionUrl\"\r\nWrite-Verbose \"Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName\"\r\n\r\n$webclient = New-Object System.Net.WebClient\r\nif ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) {\r\n  $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD)\r\n}\r\n[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12\r\n$webclient.DownloadFile($distributionUrl, \"$TMP_DOWNLOAD_DIR/$distributionUrlName\") | Out-Null\r\n\r\n# If specified, validate the SHA-256 sum of the Maven distribution zip file\r\n$distributionSha256Sum = (Get-Content -Raw \"$scriptDir/.mvn/wrapper/maven-wrapper.properties\" | ConvertFrom-StringData).distributionSha256Sum\r\nif ($distributionSha256Sum) {\r\n  if ($USE_MVND) {\r\n    Write-Error \"Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties.\"\r\n  }\r\n  if ((Get-FileHash \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) {\r\n    Write-Error \"Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property.\"\r\n  }\r\n}\r\n\r\n# unzip and move\r\nExpand-Archive \"$TMP_DOWNLOAD_DIR/$distributionUrlName\" -DestinationPath \"$TMP_DOWNLOAD_DIR\" | Out-Null\r\nRename-Item -Path \"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain\" -NewName $MAVEN_HOME_NAME | Out-Null\r\ntry {\r\n  Move-Item -Path \"$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME\" -Destination $MAVEN_HOME_PARENT | Out-Null\r\n} catch {\r\n  if (! (Test-Path -Path \"$MAVEN_HOME\" -PathType Container)) {\r\n    Write-Error \"fail to move MAVEN_HOME\"\r\n  }\r\n} finally {\r\n  try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null }\r\n  catch { Write-Warning \"Cannot remove $TMP_DOWNLOAD_DIR\" }\r\n}\r\n\r\nWrite-Output \"MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD\"\r\n"
  },
  {
    "path": "pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright (C) 2014-2023 jsonwebtoken.io\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd\">\n    <modelVersion>4.0.0</modelVersion>\n\n    <groupId>io.jsonwebtoken</groupId>\n    <artifactId>jjwt-root</artifactId>\n    <version>0.14.0-SNAPSHOT</version>\n    <name>JJWT</name>\n    <description>JSON Web Token support for the JVM and Android</description>\n    <packaging>pom</packaging>\n    <url>https://github.com/jwtk/jjwt</url>\n\n    <organization>\n        <name>jsonwebtoken.io</name>\n        <url>https://github.com/jwtk/jjwt</url>\n    </organization>\n    <inceptionYear>2014</inceptionYear>\n\n    <licenses>\n        <license>\n            <name>Apache-2.0</name>\n            <url>https://www.apache.org/licenses/LICENSE-2.0</url>\n            <distribution>repo</distribution>\n        </license>\n    </licenses>\n\n    <developers>\n        <developer>\n            <name>Les Hazlewood</name>\n            <email>121180+lhazlewood@users.noreply.github.com</email>\n            <organization>JJWT</organization>\n            <organizationUrl>https://github.com/jwtk/jjwt</organizationUrl>\n        </developer>\n    </developers>\n\n    <scm>\n        <connection>scm:git:https://github.com/jwtk/jjwt.git</connection>\n        <developerConnection>scm:git:https://github.com/jwtk/jjwt.git</developerConnection>\n        <url>https://github.com/jwtk/jjwt.git</url>\n        <tag>HEAD</tag>\n    </scm>\n    <issueManagement>\n        <system>GitHub Issues</system>\n        <url>https://github.com/jwtk/jjwt/issues</url>\n    </issueManagement>\n    <ciManagement>\n        <system>TravisCI</system>\n        <url>https://travis-ci.org/jwtk/jjwt</url>\n    </ciManagement>\n    <distributionManagement>\n        <snapshotRepository>\n            <id>ossrh</id>\n            <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n        </snapshotRepository>\n        <repository>\n            <id>ossrh</id>\n            <url>https://ossrh-staging-api.central.sonatype.com/service/local/staging/deploy/maven2/</url>\n        </repository>\n    </distributionManagement>\n\n    <repositories>\n        <repository>\n            <id>ossrh</id>\n            <name>OSSRH Snapshots</name>\n            <url>https://central.sonatype.com/repository/maven-snapshots/</url>\n            <releases>\n                <enabled>false</enabled>\n            </releases>\n            <snapshots>\n                <enabled>true</enabled>\n            </snapshots>\n        </repository>\n    </repositories>\n\n    <properties>\n\n        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>\n        <arguments />\n\n        <jjwt.root>${basedir}</jjwt.root>\n        <jjwt.previousVersion>0.11.2</jjwt.previousVersion>\n\n        <maven.jar.version>3.3.0</maven.jar.version>\n        <maven.compiler.version>3.11.0</maven.compiler.version>\n        <maven.javadoc.version>3.1.1</maven.javadoc.version> <!-- max version allowed for JDK 7 builds -->\n        <maven.source.version>3.2.1</maven.source.version>\n        <maven.resources.version>3.1.0</maven.resources.version>\n        <maven.gpg.version>1.6</maven.gpg.version> <!-- max version allowed for JDK 7 builds -->\n        <maven.japicmp.version>0.13.1</maven.japicmp.version> <!-- max version allowed for JDK 7 builds -->\n        <gmavenplus.version>1.6.1</gmavenplus.version> <!-- higher version used in jdk8AndLater profile below -->\n        <maven.license.version>4.2.rc3</maven.license.version>\n        <maven.license.skipExistingHeaders>true</maven.license.skipExistingHeaders>\n\n        <jdk.version>7</jdk.version>\n        <buildNumber>${user.name}-${maven.build.timestamp}</buildNumber>\n\n        <jackson.version>2.12.7.1</jackson.version>\n        <orgjson.version>20231013</orgjson.version>\n        <gson.version>2.11.0</gson.version>\n\n        <maven.javadoc.additionalOptions />\n\n        <!-- Optional Runtime Dependencies: -->\n        <bouncycastle.version>1.78.1</bouncycastle.version>\n        <bcprov.artifactId>bcprov-jdk18on</bcprov.artifactId>\n        <bcpkix.artifactId>bcpkix-jdk18on</bcpkix.artifactId>\n\n        <!-- Test Dependencies: Only required for testing when building.  Not required by users at runtime: -->\n        <groovy.version>2.5.16</groovy.version> <!-- higher version used in jdk8AndLater profile below -->\n        <easymock.version>3.6</easymock.version> <!-- higher version used in jdk8AndLater profile below -->\n        <junit.version>4.12</junit.version>\n        <powermock.version>2.0.0-beta.5</powermock.version> <!-- higher version used in jdk8AndLater profile below -->\n        <failsafe.plugin.version>3.0.0-M5</failsafe.plugin.version>\n        <surefire.plugin.version>3.0.0-M5</surefire.plugin.version>\n        <clover.version>4.3.1</clover.version> <!-- max version allowed for JDK 7 builds -->\n        <clover.db>${jjwt.root}/target/clover/clover.db</clover.db>\n        <surefire.argLine />\n        <test.addOpens>\n            --add-opens java.base/java.lang=ALL-UNNAMED, <!-- Needed by EasyMock/cglib -->\n            --add-opens java.desktop/java.beans=ALL-UNNAMED, <!-- Needed by EasyMock/cglib -->\n            --add-opens java.base/java.lang.ref=ALL-UNNAMED, <!-- Needed by PowerMock -->\n            <!-- needed by KeysImplTest.testKeyPairFor, KeysTest.testDeprecatedKeyPairFor, and\n                 KeysTest.testKeyPairBuilder: -->\n            --add-opens java.base/sun.security.util=ALL-UNNAMED\n        </test.addOpens>\n\n    </properties>\n\n    <modules>\n        <module>api</module>\n        <module>impl</module>\n        <module>extensions</module>\n        <module>tdjar</module>\n        <module>bom</module>\n    </modules>\n\n    <dependencyManagement>\n        <dependencies>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-api</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-impl</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-jackson</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-orgjson</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>io.jsonwebtoken</groupId>\n                <artifactId>jjwt-gson</artifactId>\n                <version>${project.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.fasterxml.jackson.core</groupId>\n                <artifactId>jackson-databind</artifactId>\n                <version>${jackson.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>org.json</groupId>\n                <artifactId>json</artifactId>\n                <version>${orgjson.version}</version>\n            </dependency>\n            <dependency>\n                <groupId>com.google.code.gson</groupId>\n                <artifactId>gson</artifactId>\n                <version>${gson.version}</version>\n            </dependency>\n\n            <!-- Used only during testing for PS256, PS384 and PS512 since JDK <= 10 doesn't support them: -->\n            <dependency>\n                <groupId>org.bouncycastle</groupId>\n                <artifactId>${bcprov.artifactId}</artifactId>\n                <version>${bouncycastle.version}</version>\n                <scope>test</scope>\n            </dependency>\n            <dependency>\n                <groupId>org.bouncycastle</groupId>\n                <artifactId>${bcpkix.artifactId}</artifactId>\n                <version>${bouncycastle.version}</version>\n                <scope>test</scope>\n            </dependency>\n        </dependencies>\n    </dependencyManagement>\n\n    <dependencies>\n        <!-- Test Dependencies: Only required for testing when building.  Not required by users at runtime: -->\n        <dependency>\n            <groupId>org.codehaus.groovy</groupId>\n            <artifactId>groovy</artifactId>\n            <version>${groovy.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.easymock</groupId>\n            <artifactId>easymock</artifactId>\n            <version>${easymock.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.powermock</groupId>\n            <artifactId>powermock-module-junit4</artifactId>\n            <version>${powermock.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.powermock</groupId>\n            <artifactId>powermock-api-easymock</artifactId>\n            <version>${powermock.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.powermock</groupId>\n            <artifactId>powermock-core</artifactId>\n            <version>${powermock.version}</version>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>junit</groupId>\n            <artifactId>junit</artifactId>\n            <version>4.13.1</version>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n\n    <build>\n        <pluginManagement>\n            <plugins>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-release-plugin</artifactId>\n                    <version>2.5.3</version>\n                    <dependencies>\n                        <dependency>\n                            <groupId>org.apache.maven.scm</groupId>\n                            <artifactId>maven-scm-provider-gitexe</artifactId>\n                            <version>1.9.5</version>\n                        </dependency>\n                    </dependencies>\n                    <configuration>\n                        <mavenExecutorId>forked-path</mavenExecutorId>\n                        <releaseProfiles>ossrh</releaseProfiles>\n                        <autoVersionSubmodules>true</autoVersionSubmodules>\n                    </configuration>\n                </plugin>\n                <plugin>\n                    <groupId>org.openclover</groupId>\n                    <artifactId>clover-maven-plugin</artifactId>\n                    <version>${clover.version}</version>\n                    <configuration>\n                        <cloverDatabase>${clover.db}</cloverDatabase>\n                        <!--\n                        cloverDatabase>${user.home}/.clover/${project.groupId}/jjwt/clover.db</cloverDatabase>\n                        <snapshot>${user.home}/.clover/${project.groupId}/jjwt/clover.snapshot</snapshot>\n                        <historyDir>${user.home}/.clover/${project.groupId}/jjwt</historyDir> -->\n                        <excludes>\n                            <exclude>io/jsonwebtoken/lang/*</exclude>\n                            <exclude>io/jsonwebtoken/all/JavaReadmeTest.java</exclude>\n                            <!-- Imported from commons-codec 585497f09b026f6602daf986723a554e051bdfe6, don't\n                            need full coverage: -->\n                            <exclude>io/jsonwebtoken/impl/io/CodecPolicy.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/BaseNCodec.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/Base64Codec.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/BaseNCodecOutputStream.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/BaseNCodecInputStream.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/Base64OutputStream.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/Base64InputStream.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/FilteredInputStream.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/FilteredOutputStream.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/io/CharSequenceReader.java</exclude>\n                            <exclude>io/jsonwebtoken/impl/lang/AddOpens.java</exclude>\n                        </excludes>\n                        <methodPercentage>100.000000%</methodPercentage>\n                        <statementPercentage>100.000000%</statementPercentage>\n                        <conditionalPercentage>100.000000%</conditionalPercentage>\n                        <targetPercentage>100.000000%</targetPercentage>\n                    </configuration>\n                </plugin>\n                <plugin>\n                    <groupId>com.mycila</groupId>\n                    <artifactId>license-maven-plugin</artifactId>\n                    <version>${maven.license.version}</version>\n                    <configuration>\n                        <skipExistingHeaders>${maven.license.skipExistingHeaders}</skipExistingHeaders>\n                        <mapping>\n                            <toml>SCRIPT_STYLE</toml> <!-- yaml -->\n                            <pem>SCRIPT_STYLE</pem> <!-- any will do -->\n                        </mapping>\n                        <licenseSets>\n                            <licenseSet>\n                                <header>${jjwt.root}/src/license/header.txt</header>\n                                <excludes>\n                                    <exclude>**/license/header.txt</exclude>\n                                    <exclude>**/*.test.orgjson</exclude>\n                                    <exclude>**/*.test.gson</exclude>\n                                    <exclude>**/*.test.override</exclude>\n                                    <exclude>**/*.bnd</exclude>\n                                    <exclude>LICENSE</exclude>\n                                    <exclude>**/mvnw</exclude>\n                                    <exclude>**/lombok.config</exclude>\n                                    <exclude>.gitattributes</exclude>\n                                    <exclude>**/genkeys</exclude>\n                                    <exclude>**/softhsm</exclude>\n                                    <exclude>**.adoc</exclude>\n                                </excludes>\n                            </licenseSet>\n                        </licenseSets>\n                    </configuration>\n                    <dependencies>\n                        <dependency>\n                            <groupId>com.mycila</groupId>\n                            <artifactId>license-maven-plugin-git</artifactId>\n                            <version>${maven.license.version}</version>\n                        </dependency>\n                    </dependencies>\n                    <executions>\n                        <execution>\n                            <goals>\n                                <goal>check</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-javadoc-plugin</artifactId>\n                    <version>${maven.javadoc.version}</version>\n                    <executions>\n                        <execution>\n                            <id>attach-javadocs</id>\n                            <goals>\n                                <goal>jar</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                    <configuration>\n                        <source>${jdk.version}</source>\n                        <failOnError>true</failOnError>\n                        <failOnWarnings>false</failOnWarnings>\n                        <additionalOptions>${maven.javadoc.additionalOptions}</additionalOptions>\n                    </configuration>\n                    <dependencies>\n                        <!-- Workaround for Java 9 -->\n                        <dependency>\n                            <groupId>commons-lang</groupId>\n                            <artifactId>commons-lang</artifactId>\n                            <version>2.6</version>\n                        </dependency>\n                    </dependencies>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-jar-plugin</artifactId>\n                    <version>${maven.jar.version}</version>\n                    <configuration>\n                        <archive>\n                            <manifest>\n                                <addDefaultSpecificationEntries>true</addDefaultSpecificationEntries>\n                                <addDefaultImplementationEntries>true</addDefaultImplementationEntries>\n                            </manifest>\n                            <manifestFile>${project.build.outputDirectory}/META-INF/MANIFEST.MF</manifestFile>\n                        </archive>\n                    </configuration>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-source-plugin</artifactId>\n                    <version>${maven.source.version}</version>\n                    <executions>\n                        <execution>\n                            <id>attach-sources</id>\n                            <goals>\n                                <goal>jar-no-fork</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-resources-plugin</artifactId>\n                    <version>${maven.resources.version}</version>\n                </plugin>\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-gpg-plugin</artifactId>\n                    <version>${maven.gpg.version}</version>\n                    <executions>\n                        <execution>\n                            <id>sign-artifacts</id>\n                            <phase>verify</phase>\n                            <goals>\n                                <goal>sign</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                </plugin>\n                <plugin>\n                    <!-- japicmp will scan code for binary breaking changes, Open api/target/japicmp/japicmp.html\n                         for a report of the changes since ${jjwt.previousVersion} -->\n                    <groupId>com.github.siom79.japicmp</groupId>\n                    <artifactId>japicmp-maven-plugin</artifactId>\n                    <version>${maven.japicmp.version}</version>\n                    <configuration>\n                        <oldVersion>\n                            <dependency>\n                                <groupId>${project.groupId}</groupId>\n                                <artifactId>${project.artifactId}</artifactId>\n                                <version>${jjwt.previousVersion}</version>\n                                <type>jar</type>\n                            </dependency>\n                        </oldVersion>\n                        <parameter>\n                            <onlyModified>true</onlyModified>\n                            <!-- <breakBuildOnBinaryIncompatibleModifications>true</breakBuildOnBinaryIncompatibleModifications> -->\n                            <!-- TODO: enable after 1.0 -->\n                            <breakBuildBasedOnSemanticVersioning>true</breakBuildBasedOnSemanticVersioning>\n\n                            <!-- All of the following can be removed after 0.11.1 is released: -->\n                            <!-- <excludes>\n                                <exclude>io.jsonwebtoken.Header#getAlgorithm()</exclude>\n                                <exclude>io.jsonwebtoken.Header#setAlgorithm(java.lang.String)</exclude>\n                                <exclude>io.jsonwebtoken.JwsHeader#getAlgorithm()</exclude>\n                                <exclude>io.jsonwebtoken.JwsHeader#setAlgorithm(java.lang.String)</exclude>\n                                <exclude>io.jsonwebtoken.lang.Assert#notNull(java.lang.Object,java.lang.String)</exclude>\n                            </excludes> -->\n                        </parameter>\n                    </configuration>\n                    <executions>\n                        <execution>\n                            <id>japicmp</id>\n                            <goals>\n                                <goal>cmp</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                </plugin>\n\n                <!-- The following plugin section is used in jjwt-jackson and jjwt-orgjson, to repackage (and verify)\n                     binary compatibility with previous versions. In v0.11.0 the implementations changed packages to\n                     avoid split package issues with Java 9+ see: https://github.com/jwtk/jjwt/issues/399 -->\n                <!-- TODO: remove these deprecated packages and this config before v1.0 -->\n                <plugin>\n                    <groupId>org.apache.maven.plugins</groupId>\n                    <artifactId>maven-shade-plugin</artifactId>\n                    <version>3.2.1</version>\n                    <configuration>\n                        <shadedClassifierName>deprecated</shadedClassifierName>\n                        <shadedArtifactAttached>true</shadedArtifactAttached>\n                        <createDependencyReducedPom>false</createDependencyReducedPom>\n                        <artifactSet>\n                            <includes>\n                                <include>${project.groupId}:${project.artifactId}</include>\n                            </includes>\n                        </artifactSet>\n                        <transformers>\n                            <transformer implementation=\"org.apache.maven.plugins.shade.resource.ServicesResourceTransformer\" />\n                        </transformers>\n                    </configuration>\n                    <executions>\n                        <execution>\n                            <phase>package</phase>\n                            <goals>\n                                <goal>shade</goal>\n                            </goals>\n                        </execution>\n                    </executions>\n                </plugin>\n            </plugins>\n        </pluginManagement>\n        <plugins>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-enforcer-plugin</artifactId>\n                <version>1.4.1</version>\n                <executions>\n                    <execution>\n                        <id>enforce-banned-dependencies</id>\n                        <goals>\n                            <goal>enforce</goal>\n                        </goals>\n                        <configuration>\n                            <rules>\n                                <bannedDependencies>\n                                    <searchTransitive>true</searchTransitive>\n                                    <excludes>\n                                        <exclude>commons-logging</exclude>\n                                    </excludes>\n                                </bannedDependencies>\n                            </rules>\n                            <fail>true</fail>\n                        </configuration>\n                    </execution>\n                </executions>\n            </plugin>\n<!--            <plugin>-->\n<!--                <groupId>org.apache.maven.plugins</groupId>-->\n<!--                <artifactId>maven-compiler-plugin</artifactId>-->\n<!--                <version>${maven.compiler.version}</version>-->\n<!--                <executions>-->\n<!--                    <execution>-->\n<!--                        <id>default-compile</id>-->\n<!--                        <configuration>-->\n<!--                            <release>9</release>-->\n<!--                            &lt;!&ndash; no excludes: compile everything to ensure module-info contains right entries &ndash;&gt;-->\n<!--                        </configuration>-->\n<!--                    </execution>-->\n<!--                    <execution>-->\n<!--                        <id>base-compile</id>-->\n<!--                        <goals>-->\n<!--                            <goal>compile</goal>-->\n<!--                        </goals>-->\n<!--                        <configuration>-->\n<!--                            &lt;!&ndash; recompile everything for target VM except the module-info.java &ndash;&gt;-->\n<!--                            <excludes>-->\n<!--                                <exclude>module-info.java</exclude>-->\n<!--                            </excludes>-->\n<!--                        </configuration>-->\n<!--                    </execution>-->\n<!--                </executions>-->\n<!--                &lt;!&ndash; defaults for compile and testCompile &ndash;&gt;-->\n<!--                <configuration>-->\n<!--                    <release>${jdk.version}</release>-->\n<!--&lt;!&ndash;                    &lt;!&ndash; Only required when Maven runtime JAVA_HOME isn't at least Java 9 and when haven't configured the maven-toolchains-plugin &ndash;&gt;&ndash;&gt;-->\n<!--&lt;!&ndash;                    <jdkToolchain>&ndash;&gt;-->\n<!--&lt;!&ndash;                        <version>9</version>&ndash;&gt;-->\n<!--&lt;!&ndash;                    </jdkToolchain>&ndash;&gt;-->\n<!--                </configuration>-->\n<!--            </plugin>-->\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-compiler-plugin</artifactId>\n                <version>${maven.compiler.version}</version>\n                <configuration>\n                    <source>${jdk.version}</source>\n                    <target>${jdk.version}</target>\n                    <encoding>${project.build.sourceEncoding}</encoding>\n                </configuration>\n            </plugin>\n            <!-- Allow for writing tests in Groovy: -->\n            <plugin>\n                <groupId>org.codehaus.gmavenplus</groupId>\n                <artifactId>gmavenplus-plugin</artifactId>\n                <version>${gmavenplus.version}</version>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>addSources</goal>\n                            <goal>addTestSources</goal>\n                            <goal>generateStubs</goal>\n                            <goal>compile</goal>\n                            <goal>generateTestStubs</goal>\n                            <goal>compileTests</goal>\n                            <goal>removeStubs</goal>\n                            <goal>removeTestStubs</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <dependencies>\n                    <dependency>\n                        <groupId>org.codehaus.groovy</groupId>\n                        <artifactId>groovy</artifactId>\n                        <version>${groovy.version}</version>\n                    </dependency>\n                </dependencies>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-surefire-plugin</artifactId>\n                <version>${surefire.plugin.version}</version>\n                <configuration>\n                    <argLine>${surefire.argLine}</argLine>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-failsafe-plugin</artifactId>\n                <version>${failsafe.plugin.version}</version>\n                <configuration>\n                    <includes>\n                        <include>**/*IT.java</include>\n                        <include>**/*IT.groovy</include>\n                        <include>**/*ITCase.java</include>\n                        <include>**/*ITCase.groovy</include>\n                    </includes>\n                    <excludes>\n                        <exclude>**/*ManualIT.java</exclude>\n                        <exclude>**/*ManualIT.groovy</exclude>\n                    </excludes>\n                </configuration>\n                <executions>\n                    <execution>\n                        <goals>\n                            <goal>integration-test</goal>\n                            <goal>verify</goal>\n                        </goals>\n                    </execution>\n                </executions>\n            </plugin>\n            <plugin>\n                <groupId>org.openclover</groupId>\n                <artifactId>clover-maven-plugin</artifactId>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-release-plugin</artifactId>\n            </plugin>\n            <plugin>\n                <groupId>org.sonatype.plugins</groupId>\n                <artifactId>nexus-staging-maven-plugin</artifactId>\n                <version>1.6.8</version>\n                <extensions>true</extensions>\n                <configuration>\n                    <serverId>ossrh</serverId>\n                    <nexusUrl>https://ossrh-staging-api.central.sonatype.com/</nexusUrl>\n                    <autoReleaseAfterClose>false</autoReleaseAfterClose>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.felix</groupId>\n                <artifactId>maven-bundle-plugin</artifactId>\n                <version>3.5.0</version>\n                <executions>\n                    <execution>\n                        <id>bundle-manifest</id>\n                        <phase>process-classes</phase>\n                        <goals>\n                            <goal>manifest</goal>\n                        </goals>\n                    </execution>\n                </executions>\n                <configuration>\n                    <instructions>\n                        <_include>-bnd.bnd</_include>\n                    </instructions>\n                </configuration>\n            </plugin>\n            <plugin>\n                <groupId>org.apache.maven.plugins</groupId>\n                <artifactId>maven-jar-plugin</artifactId>\n            </plugin>\n            <plugin>\n                <groupId>io.jsonwebtoken.coveralls</groupId>\n                <artifactId>coveralls-maven-plugin</artifactId>\n                <version>4.4.1</version>\n            </plugin>\n        </plugins>\n    </build>\n    <profiles>\n        <profile>\n            <id>jdk7</id>\n            <activation>\n                <jdk>1.7</jdk>\n            </activation>\n            <properties>\n                <maven.jar.version>3.2.2</maven.jar.version>\n                <maven.compiler.version>3.8.1</maven.compiler.version>\n                <orgjson.version>20230618</orgjson.version>\n                <bcprov.artifactId>bcprov-jdk15to18</bcprov.artifactId>\n                <bcpkix.artifactId>bcpkix-jdk15to18</bcpkix.artifactId>\n            </properties>\n        </profile>\n        <profile>\n            <id>jdk8AndLater</id>\n            <activation>\n                <jdk>[1.8,)</jdk>\n            </activation>\n            <properties>\n                <gmavenplus.version>3.0.2</gmavenplus.version>\n                <groovy.version>3.0.19</groovy.version>\n                <easymock.version>4.2</easymock.version>\n                <powermock.version>2.0.7</powermock.version>\n                <maven.japicmp.version>0.15.6</maven.japicmp.version>\n                <failsafe.plugin.version>3.1.2</failsafe.plugin.version>\n                <surefire.plugin.version>3.1.2</surefire.plugin.version>\n            </properties>\n        </profile>\n<!--        <profile>-->\n<!--            <id>jdk7And8</id>-->\n<!--            <activation>-->\n<!--                <jdk>[1.7,9)</jdk>-->\n<!--            </activation>-->\n<!--            <build>-->\n<!--                <plugins>-->\n<!--                    <plugin>-->\n<!--                        <groupId>org.apache.maven.plugins</groupId>-->\n<!--                        <artifactId>maven-compiler-plugin</artifactId>-->\n<!--                        <version>${maven.compiler.version}</version>-->\n<!--                        <configuration>-->\n<!--                            <source>${jdk.version}</source>-->\n<!--                            <target>${jdk.version}</target>-->\n<!--                            <release />-->\n<!--                            <encoding>${project.build.sourceEncoding}</encoding>-->\n<!--                            <excludes>-->\n<!--                                <exclude>module-info.java</exclude>-->\n<!--                            </excludes>-->\n<!--                        </configuration>-->\n<!--                    </plugin>-->\n<!--                </plugins>-->\n<!--            </build>-->\n<!--        </profile>-->\n        <profile>\n            <!-- Added profile to address https://github.com/jwtk/jjwt/issues/364 -->\n            <id>jdk9AndLater</id>\n            <activation>\n                <jdk>[1.9,)</jdk>\n            </activation>\n            <properties>\n                <maven.compiler.version>3.11.0</maven.compiler.version>\n                <surefire.useModulePath>false</surefire.useModulePath>\n                <maven.javadoc.additionalOptions>-html5</maven.javadoc.additionalOptions>\n                <surefire.argLine>${test.addOpens}, --illegal-access=debug</surefire.argLine>\n            </properties>\n        </profile>\n        <profile>\n            <id>jdk17AndLater</id>\n            <activation>\n                <jdk>[17,)</jdk>\n            </activation>\n            <properties>\n                <maven.javadoc.additionalOptions>-html5</maven.javadoc.additionalOptions>\n                <surefire.argLine>${test.addOpens}</surefire.argLine>\n            </properties>\n        </profile>\n        <profile>\n            <id>jdk21AndLater</id>\n            <activation>\n                <jdk>[21,)</jdk>\n            </activation>\n            <properties>\n                <!-- normally this is 1.7, but as of 21, JDK 8 is the lowest source/target -->\n                <jdk.version>8</jdk.version>\n            </properties>\n        </profile>\n        <profile>\n            <id>docs</id>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-source-plugin</artifactId>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-javadoc-plugin</artifactId>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n        <profile>\n            <id>ossrh</id>\n            <build>\n                <plugins>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-source-plugin</artifactId>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-javadoc-plugin</artifactId>\n                    </plugin>\n                    <plugin>\n                        <groupId>org.apache.maven.plugins</groupId>\n                        <artifactId>maven-gpg-plugin</artifactId>\n                    </plugin>\n                </plugins>\n            </build>\n        </profile>\n    </profiles>\n</project>\n"
  },
  {
    "path": "src/license/header.txt",
    "content": "Copyright © ${license.git.copyrightCreationYear} ${project.organization.name}\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License."
  },
  {
    "path": "tdjar/pom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\n  ~ Copyright 2020 JWTK\n  ~\n  ~ Licensed under the Apache License, Version 2.0 (the \"License\");\n  ~ you may not use this file except in compliance with the License.\n  ~ You may obtain a copy of the License at\n  ~\n  ~     http://www.apache.org/licenses/LICENSE-2.0\n  ~\n  ~ Unless required by applicable law or agreed to in writing, software\n  ~ distributed under the License is distributed on an \"AS IS\" BASIS,\n  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n  ~ See the License for the specific language governing permissions and\n  ~ limitations under the License.\n  -->\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://maven.apache.org/POM/4.0.0 https://maven.apache.org/maven-v4_0_0.xsd\">\n\n    <modelVersion>4.0.0</modelVersion>\n\n    <parent>\n        <groupId>io.jsonwebtoken</groupId>\n        <artifactId>jjwt-root</artifactId>\n        <version>0.14.0-SNAPSHOT</version>\n        <relativePath>../pom.xml</relativePath>\n    </parent>\n\n    <artifactId>jjwt</artifactId>\n    <name>JJWT :: Legacy Transitive Dependency Jar</name>\n    <description>Legacy dependency. Please update your dependencies as documented here:\n        https://github.com/jwtk/jjwt#installation\n    </description>\n    <packaging>jar</packaging>\n\n    <properties>\n        <jjwt.root>${basedir}/..</jjwt.root>\n    </properties>\n\n    <dependencies>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-api</artifactId>\n        </dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-impl</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n        <dependency>\n            <groupId>io.jsonwebtoken</groupId>\n            <artifactId>jjwt-jackson</artifactId>\n            <scope>runtime</scope>\n        </dependency>\n\n        <!-- Testing only: -->\n        <dependency>\n            <groupId>org.bouncycastle</groupId>\n            <artifactId>${bcprov.artifactId}</artifactId>\n            <scope>test</scope>\n        </dependency>\n        <dependency>\n            <groupId>org.bouncycastle</groupId>\n            <artifactId>${bcpkix.artifactId}</artifactId>\n            <scope>test</scope>\n        </dependency>\n    </dependencies>\n</project>\n"
  },
  {
    "path": "tdjar/src/test/groovy/io/jsonwebtoken/all/BasicTest.groovy",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.all\n\nimport io.jsonwebtoken.*\nimport io.jsonwebtoken.security.Keys\nimport org.junit.Test\n\nimport static org.hamcrest.CoreMatchers.equalTo\nimport static org.hamcrest.CoreMatchers.notNullValue\nimport static org.hamcrest.MatcherAssert.assertThat\n\n/**\n * This test ensures that the included dependency are all that is needed to use JJWT.\n */\nclass BasicTest {\n\n    @Test\n    void basicUsageTest() {\n        def key = Keys.secretKeyFor(SignatureAlgorithm.HS256)\n\n        String token = Jwts.builder()\n            .setSubject(\"test-user\")\n            .claim(\"test\", \"basicUsageTest\")\n            .signWith(key, SignatureAlgorithm.HS256)\n            .compact()\n\n        JwtParser parser = Jwts.parser()\n            .setSigningKey(key)\n            .build()\n\n        Jwt<Header, Claims> result = parser.parseSignedClaims(token)\n        assertThat result, notNullValue()\n        assertThat result.getBody().getSubject(), equalTo(\"test-user\")\n        assertThat result.getBody().get(\"test\", String), equalTo(\"basicUsageTest\")\n    }\n}\n"
  },
  {
    "path": "tdjar/src/test/java/io/jsonwebtoken/all/JavaReadmeTest.java",
    "content": "/*\n * Copyright (C) 2022 jsonwebtoken.io\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.jsonwebtoken.all;\n\nimport io.jsonwebtoken.Claims;\nimport io.jsonwebtoken.Jws;\nimport io.jsonwebtoken.Jwts;\nimport io.jsonwebtoken.security.AeadAlgorithm;\nimport io.jsonwebtoken.security.Curve;\nimport io.jsonwebtoken.security.EcPrivateJwk;\nimport io.jsonwebtoken.security.EcPublicJwk;\nimport io.jsonwebtoken.security.Jwk;\nimport io.jsonwebtoken.security.Jwks;\nimport io.jsonwebtoken.security.KeyAlgorithm;\nimport io.jsonwebtoken.security.Keys;\nimport io.jsonwebtoken.security.MacAlgorithm;\nimport io.jsonwebtoken.security.OctetPrivateJwk;\nimport io.jsonwebtoken.security.OctetPublicJwk;\nimport io.jsonwebtoken.security.Password;\nimport io.jsonwebtoken.security.RsaPrivateJwk;\nimport io.jsonwebtoken.security.RsaPublicJwk;\nimport io.jsonwebtoken.security.SecretJwk;\nimport io.jsonwebtoken.security.SecretKeyAlgorithm;\nimport io.jsonwebtoken.security.SignatureAlgorithm;\nimport org.junit.Test;\n\nimport javax.crypto.SecretKey;\nimport java.nio.charset.StandardCharsets;\nimport java.security.KeyPair;\nimport java.security.PrivateKey;\nimport java.security.PublicKey;\nimport java.security.interfaces.ECPrivateKey;\nimport java.security.interfaces.ECPublicKey;\nimport java.security.interfaces.RSAPrivateKey;\nimport java.security.interfaces.RSAPublicKey;\nimport java.util.Set;\n\nimport static io.jsonwebtoken.security.Jwks.builder;\nimport static org.junit.Assert.assertArrayEquals;\n\n/**\n * Test cases to ensure snippets in README.md work/compile as expected with the Java language (not Groovy):\n *\n * @since 0.12.0\n */\npublic class JavaReadmeTest {\n\n    /**\n     * {@code README.md#jws-unencoded-detached}\n     */\n    @Test\n    public void testExampleDetachedUnencodedPayload() {\n        // create a test key for this example:\n        SecretKey testKey = Jwts.SIG.HS512.key().build();\n\n        String message = \"Hello World. It's a Beautiful Day!\";\n        byte[] content = message.getBytes(StandardCharsets.UTF_8);\n\n        String jws = Jwts.builder().signWith(testKey) // #1\n                .content(content)                     // #2\n                .encodePayload(false)            // #3\n                .compact();\n\n        Jws<byte[]> parsed = Jwts.parser().verifyWith(testKey) // 1\n                .build()\n                .parseSignedContent(jws, content);             // 2\n\n        assertArrayEquals(content, parsed.getPayload());\n    }\n\n    /**\n     * {@code README.md#jws-unencoded-nondetached}\n     */\n    @Test\n    public void testExampleNonDetachedUnencodedPayload() {\n        // create a test key for this example:\n        SecretKey testKey = Jwts.SIG.HS512.key().build();\n\n        String claimsString = \"{\\\"sub\\\":\\\"joe\\\",\\\"iss\\\":\\\"me\\\"}\";\n\n        String jws = Jwts.builder().signWith(testKey) // #1\n                .content(claimsString)                // #2\n                .encodePayload(false)            // #3\n                .compact();\n\n        Jws<Claims> parsed = Jwts.parser().verifyWith(testKey) // 1\n                .critical().add(\"b64\").and()                   // 2\n                .build()\n                .parseSignedClaims(jws);                          // 3\n\n        assert \"joe\".equals(parsed.getPayload().getSubject());\n        assert \"me\".equals(parsed.getPayload().getIssuer());\n\n        parsed = Jwts.parser().verifyWith(testKey)\n                .build()\n                .parseSignedClaims(jws, claimsString.getBytes(StandardCharsets.UTF_8)); // <---\n\n        assert \"joe\".equals(parsed.getPayload().getSubject());\n        assert \"me\".equals(parsed.getPayload().getIssuer());\n    }\n\n    /**\n     * {@code README.md#example-jws-hs}\n     */\n    @Test\n    public void testExampleJwsHS() {\n        // Create a test key suitable for the desired HMAC-SHA algorithm:\n        MacAlgorithm alg = Jwts.SIG.HS512; //or HS384 or HS256\n        SecretKey key = alg.key().build();\n\n        String message = \"Hello World!\";\n        byte[] content = message.getBytes(StandardCharsets.UTF_8);\n\n        // Create the compact JWS:\n        String jws = Jwts.builder().content(content, \"text/plain\").signWith(key, alg).compact();\n\n        // Parse the compact JWS:\n        content = Jwts.parser().verifyWith(key).build().parseSignedContent(jws).getPayload();\n\n        assert message.equals(new String(content, StandardCharsets.UTF_8));\n    }\n\n    /**\n     * {@code README.md#example-jws-rsa}\n     */\n    @Test\n    public void testExampleJwsRSA() {\n        // Create a test key suitable for the desired RSA signature algorithm:\n        SignatureAlgorithm alg = Jwts.SIG.RS512; //or PS512, RS256, etc...\n        KeyPair pair = alg.keyPair().build();\n\n        // Bob creates the compact JWS with his RSA private key:\n        String jws = Jwts.builder().subject(\"Alice\")\n                .signWith(pair.getPrivate(), alg) // <-- Bob's RSA private key\n                .compact();\n\n        // Alice receives and verifies the compact JWS came from Bob:\n        String subject = Jwts.parser()\n                .verifyWith(pair.getPublic()) // <-- Bob's RSA public key\n                .build().parseSignedClaims(jws).getPayload().getSubject();\n\n        assert \"Alice\".equals(subject);\n    }\n\n    /**\n     * {@code README.md#example-jws-ecdsa}\n     */\n    @Test\n    public void testExampleJwsECDSA() {\n        // Create a test key suitable for the desired ECDSA signature algorithm:\n        SignatureAlgorithm alg = Jwts.SIG.ES512; //or ES256 or ES384\n        KeyPair pair = alg.keyPair().build();\n\n        // Bob creates the compact JWS with his EC private key:\n        String jws = Jwts.builder().subject(\"Alice\")\n                .signWith(pair.getPrivate(), alg) // <-- Bob's EC private key\n                .compact();\n\n        // Alice receives and verifies the compact JWS came from Bob:\n        String subject = Jwts.parser()\n                .verifyWith(pair.getPublic()) // <-- Bob's EC public key\n                .build().parseSignedClaims(jws).getPayload().getSubject();\n\n        assert \"Alice\".equals(subject);\n    }\n\n    /**\n     * {@code README.md#example-jws-eddsa}\n     */\n    @Test\n    public void testExampleJwsEdDSA() {\n        // Create a test key suitable for the EdDSA signature algorithm using Ed25519 or Ed448 keys:\n        Curve curve = Jwks.CRV.Ed25519; //or Ed448\n        KeyPair pair = curve.keyPair().build();\n\n        // Bob creates the compact JWS with his Edwards Curve private key:\n        String jws = Jwts.builder().subject(\"Alice\")\n                .signWith(pair.getPrivate(), Jwts.SIG.EdDSA) // <-- Bob's Edwards Curve private key w/ EdDSA\n                .compact();\n\n        // Alice receives and verifies the compact JWS came from Bob:\n        String subject = Jwts.parser()\n                .verifyWith(pair.getPublic()) // <-- Bob's Edwards Curve public key\n                .build().parseSignedClaims(jws).getPayload().getSubject();\n\n        assert \"Alice\".equals(subject);\n    }\n\n    /**\n     * {@code README.md#example-jwe-dir}\n     */\n    @Test\n    public void testExampleJweDir() {\n        // Create a test key suitable for the desired payload encryption algorithm:\n        // (A*GCM algorithms are recommended, but require JDK 8 or later)\n        AeadAlgorithm enc = Jwts.ENC.A256GCM; //or A128GCM, A192GCM, A256CBC-HS512, etc...\n        SecretKey key = enc.key().build();\n\n        String message = \"Live long and prosper.\";\n        byte[] content = message.getBytes(StandardCharsets.UTF_8);\n\n        // Create the compact JWE:\n        String jwe = Jwts.builder().content(content, \"text/plain\").encryptWith(key, enc).compact();\n\n        // Parse the compact JWE:\n        content = Jwts.parser().decryptWith(key).build().parseEncryptedContent(jwe).getPayload();\n\n        assert message.equals(new String(content, StandardCharsets.UTF_8));\n    }\n\n    /**\n     * {@code README.md#example-jwe-rsa}\n     */\n    @Test\n    public void testExampleJweRSA() {\n        // Create a test KeyPair suitable for the desired RSA key algorithm:\n        KeyPair pair = Jwts.SIG.RS512.keyPair().build();\n\n        // Choose the key algorithm used encrypt the payload key:\n        KeyAlgorithm<PublicKey, PrivateKey> alg = Jwts.KEY.RSA_OAEP_256; //or RSA_OAEP or RSA1_5\n        // Choose the Encryption Algorithm to encrypt the payload:\n        AeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n        // Bob creates the compact JWE with Alice's RSA public key so only she may read it:\n        String jwe = Jwts.builder().audience().add(\"Alice\").and()\n                .encryptWith(pair.getPublic(), alg, enc) // <-- Alice's RSA public key\n                .compact();\n\n        // Alice receives and decrypts the compact JWE:\n        Set<String> audience = Jwts.parser()\n                .decryptWith(pair.getPrivate()) // <-- Alice's RSA private key\n                .build().parseEncryptedClaims(jwe).getPayload().getAudience();\n\n        assert audience.contains(\"Alice\");\n    }\n\n    /**\n     * {@code README.md#example-jwe-aeskw}\n     */\n    @Test\n    public void testExampleJweAESKW() {\n        // Create a test SecretKey suitable for the desired AES Key Wrap algorithm:\n        SecretKeyAlgorithm alg = Jwts.KEY.A256GCMKW; //or A192GCMKW, A128GCMKW, A256KW, etc...\n        SecretKey key = alg.key().build();\n\n        // Chooose the Encryption Algorithm used to encrypt the payload:\n        AeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n        // Create the compact JWE:\n        String jwe = Jwts.builder().issuer(\"me\").encryptWith(key, alg, enc).compact();\n\n        // Parse the compact JWE:\n        String issuer = Jwts.parser().decryptWith(key).build()\n                .parseEncryptedClaims(jwe).getPayload().getIssuer();\n\n        assert \"me\".equals(issuer);\n    }\n\n    /**\n     * {@code README.md#example-jwe-ecdhes}\n     */\n    @Test\n    public void testExampleJweECDHES() {\n        // Create a test KeyPair suitable for the desired EC key algorithm:\n        KeyPair pair = Jwts.SIG.ES512.keyPair().build();\n\n        // Choose the key algorithm used encrypt the payload key:\n        KeyAlgorithm<PublicKey, PrivateKey> alg = Jwts.KEY.ECDH_ES_A256KW; //ECDH_ES_A192KW, etc...\n        // Choose the Encryption Algorithm to encrypt the payload:\n        AeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n        // Bob creates the compact JWE with Alice's EC public key so only she may read it:\n        String jwe = Jwts.builder().audience().add(\"Alice\").and()\n                .encryptWith(pair.getPublic(), alg, enc) // <-- Alice's EC public key\n                .compact();\n\n        // Alice receives and decrypts the compact JWE:\n        Set<String> audience = Jwts.parser()\n                .decryptWith(pair.getPrivate()) // <-- Alice's EC private key\n                .build().parseEncryptedClaims(jwe).getPayload().getAudience();\n\n        assert audience.contains(\"Alice\");\n    }\n\n    /**\n     * {@code README.md#example-jwe-password}\n     */\n    @Test\n    public void testExampleJwePassword() {\n        //DO NOT use this example password in a real app, it is well-known to password crackers\n        String pw = \"correct horse battery staple\";\n        Password password = Keys.password(pw.toCharArray());\n\n        // Choose the desired PBES2 key derivation algorithm:\n        KeyAlgorithm<Password, Password> alg = Jwts.KEY.PBES2_HS512_A256KW; //or PBES2_HS384...\n\n        // Optionally choose the number of PBES2 computational iterations to use to derive the key.\n        // This is optional - if you do not specify a value, JJWT will automatically choose a value\n        // based on your chosen PBES2 algorithm and OWASP PBKDF2 recommendations here:\n        // https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html#pbkdf2\n        //\n        // If you do specify a value, ensure the iterations are large enough for your desired alg\n        //int pbkdf2Iterations = 120000; //for HS512. Needs to be much higher for smaller hash algs.\n\n        // Choose the Encryption Algorithm used to encrypt the payload:\n        AeadAlgorithm enc = Jwts.ENC.A256GCM; //or A192GCM, A128GCM, A256CBC-HS512, etc...\n\n        // Create the compact JWE:\n        String jwe = Jwts.builder().issuer(\"me\")\n                // Optional work factor is specified in the header:\n                //.header().pbes2Count(pbkdf2Iterations)).and()\n                .encryptWith(password, alg, enc)\n                .compact();\n\n        // Parse the compact JWE:\n        String issuer = Jwts.parser().decryptWith(password)\n                .build().parseEncryptedClaims(jwe).getPayload().getIssuer();\n\n        assert \"me\".equals(issuer);\n    }\n\n    @Test\n    public void testExampleSecretJwk() {\n        SecretKey key = Jwts.SIG.HS512.key().build(); // or HS384 or HS256\n        SecretJwk jwk = builder().key(key).idFromThumbprint().build();\n\n        assert jwk.getId().equals(jwk.thumbprint().toString());\n        assert key.equals(jwk.toKey());\n\n        String unsafeJwkJson = Jwks.UNSAFE_JSON(jwk); // returns raw key material\n        Jwk<?> parsed = Jwks.parser().build().parse(unsafeJwkJson);\n\n        assert parsed instanceof SecretJwk;\n        assert jwk.equals(parsed);\n    }\n\n    @Test\n    public void testExampleRsaPublicJwk() {\n        RSAPublicKey key = (RSAPublicKey) Jwts.SIG.RS512.keyPair().build().getPublic();\n        RsaPublicJwk jwk = builder().key(key).idFromThumbprint().build();\n\n        assert jwk.getId().equals(jwk.thumbprint().toString());\n        assert key.equals(jwk.toKey());\n\n        String jwkJson = Jwks.json(jwk);\n        Jwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\n        assert parsed instanceof RsaPublicJwk;\n        assert jwk.equals(parsed);\n    }\n\n    @Test\n    public void testExampleRsaPrivateJwk() {\n        KeyPair pair = Jwts.SIG.RS512.keyPair().build();\n        RSAPublicKey pubKey = (RSAPublicKey) pair.getPublic();\n        RSAPrivateKey privKey = (RSAPrivateKey) pair.getPrivate();\n\n        RsaPrivateJwk privJwk = builder().key(privKey).idFromThumbprint().build();\n        RsaPublicJwk pubJwk = privJwk.toPublicJwk();\n\n        assert privJwk.getId().equals(privJwk.thumbprint().toString());\n        assert pubJwk.getId().equals(pubJwk.thumbprint().toString());\n        assert privKey.equals(privJwk.toKey());\n        assert pubKey.equals(pubJwk.toKey());\n\n        String unsafeJwkJson = Jwks.UNSAFE_JSON(privJwk); // returns raw key material\n        Jwk<?> parsed = Jwks.parser().build().parse(unsafeJwkJson);\n\n        assert parsed instanceof RsaPrivateJwk;\n        assert privJwk.equals(parsed);\n    }\n\n    @Test\n    public void testExampleEcPublicJwk() {\n        ECPublicKey key = (ECPublicKey) Jwts.SIG.ES512.keyPair().build().getPublic();\n        EcPublicJwk jwk = builder().key(key).idFromThumbprint().build();\n\n        assert jwk.getId().equals(jwk.thumbprint().toString());\n        assert key.equals(jwk.toKey());\n\n        String jwkJson = Jwks.json(jwk);\n        Jwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\n        assert parsed instanceof EcPublicJwk;\n        assert jwk.equals(parsed);\n    }\n\n    @Test\n    public void testExampleEcPrivateJwk() {\n        KeyPair pair = Jwts.SIG.ES512.keyPair().build();\n        ECPublicKey pubKey = (ECPublicKey) pair.getPublic();\n        ECPrivateKey privKey = (ECPrivateKey) pair.getPrivate();\n\n        EcPrivateJwk privJwk = builder().key(privKey).idFromThumbprint().build();\n        EcPublicJwk pubJwk = privJwk.toPublicJwk();\n\n        assert privJwk.getId().equals(privJwk.thumbprint().toString());\n        assert pubJwk.getId().equals(pubJwk.thumbprint().toString());\n        assert privKey.equals(privJwk.toKey());\n        assert pubKey.equals(pubJwk.toKey());\n\n        String unsafeJwkJson = Jwks.UNSAFE_JSON(privJwk); // returns raw key material\n        Jwk<?> parsed = Jwks.parser().build().parse(unsafeJwkJson);\n\n        assert parsed instanceof EcPrivateJwk;\n        assert privJwk.equals(parsed);\n    }\n\n    @Test\n    public void testExampleEdEcPublicJwk() {\n        PublicKey key = Jwks.CRV.Ed25519.keyPair().build().getPublic(); // or Ed448, X25519, X448\n        OctetPublicJwk<PublicKey> jwk = builder().octetKey(key).idFromThumbprint().build();\n\n        assert jwk.getId().equals(jwk.thumbprint().toString());\n        assert key.equals(jwk.toKey());\n\n        String jwkJson = Jwks.json(jwk);\n        Jwk<?> parsed = Jwks.parser().build().parse(jwkJson);\n\n        assert parsed instanceof OctetPublicJwk;\n        assert jwk.equals(parsed);\n    }\n\n    @Test\n    public void testExampleEdEcPrivateJwk() {\n        KeyPair pair = Jwks.CRV.Ed448.keyPair().build(); // or Ed25519, X25519, X448\n        PublicKey pubKey = pair.getPublic();\n        PrivateKey privKey = pair.getPrivate();\n\n        OctetPrivateJwk<PrivateKey, PublicKey> privJwk = builder().octetKey(privKey).idFromThumbprint().build();\n        OctetPublicJwk<PublicKey> pubJwk = privJwk.toPublicJwk();\n\n        assert privJwk.getId().equals(privJwk.thumbprint().toString());\n        assert pubJwk.getId().equals(pubJwk.thumbprint().toString());\n        assert privKey.equals(privJwk.toKey());\n        assert pubKey.equals(pubJwk.toKey());\n\n        String unsafeJwkJson = Jwks.UNSAFE_JSON(privJwk); // returns raw key material\n        Jwk<?> parsed = Jwks.parser().build().parse(unsafeJwkJson);\n\n        assert parsed instanceof OctetPrivateJwk;\n        assert privJwk.equals(parsed);\n    }\n\n    @Test\n    public void testExampleJwkToString() {\n        String json = \"{\\\"kty\\\":\\\"oct\\\",\" +\n                \"\\\"k\\\":\\\"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow\\\",\" +\n                \"\\\"kid\\\":\\\"HMAC key used in https://www.rfc-editor.org/rfc/rfc7515#appendix-A.1.1 example.\\\"}\";\n\n        Jwk<?> jwk = Jwks.parser().build().parse(json);\n\n        String expected = \"{kty=oct, k=<redacted>, kid=HMAC key used in https://www.rfc-editor.org/rfc/rfc7515#appendix-A.1.1 example.}\";\n        assert expected.equals(jwk.toString());\n    }\n}\n"
  }
]