pax_global_header00006660000000000000000000000064133401466720014520gustar00rootroot0000000000000052 comment=7335f2adb2288af5b39c99c4f242839201ec16ff keychain-0.1.3/000077500000000000000000000000001334014667200133145ustar00rootroot00000000000000keychain-0.1.3/.gitignore000066400000000000000000000000221334014667200152760ustar00rootroot00000000000000docs node_modules keychain-0.1.3/.travis.yml000066400000000000000000000001071334014667200154230ustar00rootroot00000000000000sudo: false language: node_js node_js: "8.10" script: npm run test keychain-0.1.3/LICENSE000066400000000000000000000021171334014667200143220ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2016 Anvil Research, Inc. http://anvil.io Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. keychain-0.1.3/README.md000066400000000000000000000031201334014667200145670ustar00rootroot00000000000000# KeyChain for use with Web Cryptography API in Node.js ## Usage Install the package ```bash $ npm install https://github.com/anvilresearch/keychain.git ``` Load the module ```javascript const KeyChain = require('keychain') ``` Create a new instance by passing a descriptive object to the `KeyChain` constructor. This object can have any naming or nesting scheme, as long as the last nested object contains parameters describing key generation. At a bare minimum, this must include an `alg` property with a JWA algorithm name as its value. Currently, `RS256`, `RS384`, and `RS512` are supported. ```javascript let keys = new KeyChain({ a: { b: { alg: 'RS256' } }, c: { d: { alg: 'RS256' } }, e: { f: { alg: 'RS256', modulusLength: 2048 } // default is 4096 }) ``` This initialized a KeyChain instance but didn't generate keys. To generate keys according to the object passed to the keychain, call `rotate()`. The `rotate()` method returns a promise for the keychain. ```javascript keys.rotate() ``` Once keys have been generated, they can be accessed as CryptoKey or JWK objects, according to the object structure defined by the caller. Access CryptoKey objects for Web Crypto API operations: ```javascript keys.a.b.privateKey keys.a.b.publicKey ``` Access JWK objects: ```javascript keys.a.b.privateJwk keys.a.b.publicJwk ``` Key rotation also generates a JWK Set in object and JSON form: ```javascript keys.jwks // JWK Set object keys.jwkSet // JWK Set JSON string ``` ## Running tests ### Nodejs ```bash $ npm test ``` ## MIT License Copyright (c) 2016 Anvil Research, Inc. keychain-0.1.3/examples/000077500000000000000000000000001334014667200151325ustar00rootroot00000000000000keychain-0.1.3/examples/generate.js000066400000000000000000000006441334014667200172660ustar00rootroot00000000000000const fs = require('fs') const KeyChain = require('../src') let defaults = { alg: 'RS256', modulusLength: 2048 } let descriptor = { token: { sig: defaults }, id_token: { sig: defaults }, userinfo: { enc: { alg: 'RS256', usages: ['sign', 'verify'] } } } KeyChain.generate(descriptor).then(keys => { fs.writeFileSync('keychain.json', JSON.stringify(keys, null, 2)) console.log(keys) }) .catch(console.log) keychain-0.1.3/examples/restore.js000066400000000000000000000002651334014667200171560ustar00rootroot00000000000000const data = require('../keychain.json') const KeyChain = require('../src/KeyChain') KeyChain.restore(data).then(keys => console.log(keys.token.sig.publicKey)).catch(console.log) keychain-0.1.3/jsdoc.json000066400000000000000000000002661334014667200153150ustar00rootroot00000000000000{ "source": { "include": [ "./src" ] }, "opts": { "destination": "./docs/build", "readme": "./README.md" }, "plugins": [ "plugins/markdown" ] } keychain-0.1.3/package-lock.json000066400000000000000000000313471334014667200165400ustar00rootroot00000000000000{ "name": "@solid/keychain", "version": "0.1.3", "lockfileVersion": 1, "requires": true, "dependencies": { "@trust/keyto": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@trust/keyto/-/keyto-0.3.4.tgz", "integrity": "sha512-OAqKvuSEPIu2zCnIHzBthvGnV8nKmpv7cBlRMngLzJZzZI9CanyuSfnEI1xC4sH4TwqA0XJR7Mb0oX4bwymXIw==", "requires": { "asn1.js": "^4.9.1", "base64url": "^3.0.0", "elliptic": "^6.4.0" } }, "@trust/webcrypto": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@trust/webcrypto/-/webcrypto-0.9.2.tgz", "integrity": "sha512-5iMAVcGYKhqLJGjefB1nzuQSqUJTru0nG4CytpBT/GGp1Piz/MVnj2jORdYf4JBYzggCIa8WZUr2rchP2Ngn/w==", "requires": { "@trust/keyto": "^0.3.4", "base64url": "^3.0.0", "elliptic": "^6.4.0", "node-rsa": "^0.4.0", "text-encoding": "^0.6.1" } }, "asn1": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" }, "asn1.js": { "version": "4.10.1", "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-4.10.1.tgz", "integrity": "sha512-p32cOF5q0Zqs9uBiONKYLm6BClCoBCM5O9JfeUSlnQLBTxYdTK+pW+nXflm8UkKd2UYlEbYz5qEi0JuZR9ckSw==", "requires": { "bn.js": "^4.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0" } }, "assertion-error": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", "dev": true }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true }, "base64url": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/base64url/-/base64url-3.0.0.tgz", "integrity": "sha512-LIVmqIrIWuiqTvn4RzcrwCOuHo2DD6tKmKBPXXlr4p4n4l6BZBkwFTIa3zu1XkX5MbZgro4a6BvPi+n2Mns5Gg==" }, "bn.js": { "version": "4.11.8", "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "brorand": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" }, "browser-stdout": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, "chai": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chai/-/chai-4.1.2.tgz", "integrity": "sha1-D2RYS6ZC8PKs4oBiefTwbKI61zw=", "dev": true, "requires": { "assertion-error": "^1.0.1", "check-error": "^1.0.1", "deep-eql": "^3.0.0", "get-func-name": "^2.0.0", "pathval": "^1.0.0", "type-detect": "^4.0.0" } }, "check-error": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.2.tgz", "integrity": "sha1-V00xLt2Iu13YkS6Sht1sCu1KrII=", "dev": true }, "commander": { "version": "2.15.1", "resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz", "integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==", "dev": true }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "dev": true, "requires": { "ms": "2.0.0" } }, "deep-eql": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", "dev": true, "requires": { "type-detect": "^4.0.0" } }, "diff": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==", "dev": true }, "dirty-chai": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/dirty-chai/-/dirty-chai-2.0.1.tgz", "integrity": "sha512-ys79pWKvDMowIDEPC6Fig8d5THiC0DJ2gmTeGzVAoEH18J8OzLud0Jh7I9IWg3NSk8x2UocznUuFmfHCXYZx9w==", "dev": true }, "elliptic": { "version": "6.4.1", "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.4.1.tgz", "integrity": "sha512-BsXLz5sqX8OHcsh7CqBMztyXARmGQ3LWPtGjJi6DiJHq5C/qvi9P3OqgswKSDftbu8+IoI/QDTAm2fFnQ9SZSQ==", "requires": { "bn.js": "^4.4.0", "brorand": "^1.0.1", "hash.js": "^1.0.0", "hmac-drbg": "^1.0.0", "inherits": "^2.0.1", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.0" } }, "escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", "dev": true }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, "get-func-name": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.0.tgz", "integrity": "sha1-6td0q+5y4gQJQzoGY2YCPdaIekE=", "dev": true }, "glob": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.2.tgz", "integrity": "sha512-MJTUg1kjuLeQCJ+ccE4Vpa6kKVXkPYJ2mOCQyUuKLcLQsdrMCpBPUi8qVE6+YuaJkozeA9NusTAw3hLr8Xe5EQ==", "dev": true, "requires": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", "dev": true }, "has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", "dev": true }, "hash.js": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.5.tgz", "integrity": "sha512-eWI5HG9Np+eHV1KQhisXWwM+4EPPYe5dFX1UZZH7k/E3JzDEazVH+VGlZi6R94ZqImq+A3D1mCEtrFIfg/E7sA==", "requires": { "inherits": "^2.0.3", "minimalistic-assert": "^1.0.1" } }, "he": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/he/-/he-1.1.1.tgz", "integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=", "dev": true }, "hmac-drbg": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "requires": { "hash.js": "^1.0.3", "minimalistic-assert": "^1.0.0", "minimalistic-crypto-utils": "^1.0.1" } }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "requires": { "once": "^1.3.0", "wrappy": "1" } }, "inherits": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, "minimalistic-crypto-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" }, "minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "requires": { "brace-expansion": "^1.1.7" } }, "minimist": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true }, "mkdirp": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "requires": { "minimist": "0.0.8" } }, "mocha": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-5.2.0.tgz", "integrity": "sha512-2IUgKDhc3J7Uug+FxMXuqIyYzH7gJjXECKe/w43IGgQHTSj3InJi+yAA7T24L9bQMRKiUEHxEX37G5JpVUGLcQ==", "dev": true, "requires": { "browser-stdout": "1.3.1", "commander": "2.15.1", "debug": "3.1.0", "diff": "3.5.0", "escape-string-regexp": "1.0.5", "glob": "7.1.2", "growl": "1.10.5", "he": "1.1.1", "minimatch": "3.0.4", "mkdirp": "0.5.1", "supports-color": "5.4.0" } }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", "dev": true }, "node-rsa": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/node-rsa/-/node-rsa-0.4.2.tgz", "integrity": "sha1-1jkXKewWqDDtWjgEKzFX0tXXJTA=", "requires": { "asn1": "0.2.3" } }, "once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "requires": { "wrappy": "1" } }, "path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true }, "pathval": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.0.tgz", "integrity": "sha1-uULm1L3mUwBe9rcTYd74cn0GReA=", "dev": true }, "supports-color": { "version": "5.4.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.4.0.tgz", "integrity": "sha512-zjaXglF5nnWpsq470jSv6P9DwPvgLkuapYmfDm3JWOm0vkNTVF2tI4UrN2r6jH1qM/uc/WtxYY1hYoA2dOKj5w==", "dev": true, "requires": { "has-flag": "^3.0.0" } }, "text-encoding": { "version": "0.6.4", "resolved": "https://registry.npmjs.org/text-encoding/-/text-encoding-0.6.4.tgz", "integrity": "sha1-45mpgiV6J22uQou5KEXLcb3CbRk=" }, "type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true }, "wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true } } } keychain-0.1.3/package.json000066400000000000000000000021301334014667200155760ustar00rootroot00000000000000{ "name": "@solid/keychain", "version": "0.1.3", "description": "KeyChain for use with Web Cryptography API in Node.js", "main": "src/index.js", "directories": { "test": "test" }, "scripts": { "test": "mocha test", "jsdoc": "jsdoc -c jsdoc.json -r" }, "repository": { "type": "git", "url": "https://github.com/anvilresearch/keychain.git" }, "author": "", "contributors": [ { "name": "Christian Smith", "email": "smith@anvil.io", "url": "http://anvil.io" }, { "name": "Greg Linklater", "email": "greglink49@gmail.com", "url": "https://github.com/EternalDeiwos" }, { "name": "Dmitri Zagidulin", "url": "https://github.com/dmitrizagidulin" } ], "license": "MIT", "bugs": { "url": "https://github.com/anvilresearch/keychain/issues" }, "homepage": "https://github.com/anvilresearch/keychain#README", "dependencies": { "@trust/webcrypto": ">=0.9.2", "base64url": "^3.0.0" }, "devDependencies": { "chai": "^4.1.2", "dirty-chai": "^2.0.1", "mocha": "^5.2.0" } } keychain-0.1.3/src/000077500000000000000000000000001334014667200141035ustar00rootroot00000000000000keychain-0.1.3/src/KeyChain.js000066400000000000000000000101561334014667200161370ustar00rootroot00000000000000/** * Dependencies */ const base64url = require('base64url') const supportedAlgorithms = require('./algorithms') /** * KeyChain */ class KeyChain { /** * constructor */ constructor (data) { // use data as the descriptor if descriptor property is missing if (!data.descriptor) { data = { descriptor: data } } Object.assign(this, data) } /** * generate */ static generate (descriptor) { let keys = new KeyChain(descriptor) return keys.rotate() } /** * generateKey * * @param {Object} params * @return {Promise} */ static generateKey (params) { let normalizedAlgorithm = supportedAlgorithms.normalize('generateKey', params.alg) if (normalizedAlgorithm instanceof Error) { return Promise.reject(normalizedAlgorithm) } let algorithm = new normalizedAlgorithm(params) return algorithm.generateKey() } /** * importKey * * @param {Object} params * @param {Object} jwk * @return {Promise} */ static importKey (jwk) { let {alg} = jwk let normalizedAlgorithm = supportedAlgorithms.normalize('importKey', alg) if (normalizedAlgorithm instanceof Error) { return Promise.reject(normalizedAlgorithm) } let algorithm = new normalizedAlgorithm({alg}) return algorithm.importKey(jwk) } /** * restore */ static restore (data) { let keys = new KeyChain(data) return keys.importKeys().then(() => keys) } /** * importKeys */ importKeys ({props, object, container, descriptor} = {}) { if (!descriptor) { descriptor = this.descriptor } if (!props) { props = Object.keys(descriptor) } if (!object) { object = this } // import key if (props.includes('alg')) { return KeyChain.importKey(object).then(cryptoKey => { if (cryptoKey.type === 'private') { Object.defineProperty(container, 'privateKey', { enumerable: false, value: cryptoKey }) } if (cryptoKey.type === 'public') { Object.defineProperty(container, 'publicKey', { enumerable: false, value: cryptoKey }) } }) // recurse } else { return Promise.all( props.map(name => { let subDescriptor = descriptor[name] let subObject = object[name] let subProps = Object.keys(subObject) //console.log('RECURSE WITH', name, subDescriptor, subObject, subProps) this.importKeys({ descriptor: subDescriptor, object: subObject, container: object, props: subProps }) }) ) } } /** * rotate * * @param {Object} context * @returns {Promise} */ rotate ({source, container, jwks} = {}) { // initial call requires no arguments // these values are passed when recursing if (!source) { source = this.descriptor } if (!container) { container = this } if (!jwks) { jwks = this.jwks = { keys: [] } } // do as much in parallel as possible return Promise.all( Object.keys(source).map(key => { let params = source[key] // generate key(pair), assign resulting object to keychain, // and add JWK for public key to JWK Set if (params.alg) { return KeyChain.generateKey(params).then(result => { container[key] = result if (result.publicJwk) { jwks.keys.push(result.publicJwk) } }) // recurse } else if (typeof params === 'object') { if (!container[key]) { container[key] = {} } return this.rotate({ source: source[key], container: container[key], jwks }) // invalid descriptor } else { throw new InvalidDescriptorError(key, value) } }) ).then(() => { // cache the JSON serialization of the keychain for publication this.jwkSet = JSON.stringify(this.jwks) // resulting value is the keychain itself return this }) } } /** * Export */ module.exports = KeyChain keychain-0.1.3/src/algorithms/000077500000000000000000000000001334014667200162545ustar00rootroot00000000000000keychain-0.1.3/src/algorithms/RsaKeyPair.js000066400000000000000000000051051334014667200206250ustar00rootroot00000000000000/** * Dependencies */ const crypto = require('@trust/webcrypto') const base64url = require('base64url') /** * RsaKeyPair */ class RsaKeyPair { /** * constructor * * @param params {Object} Options hashmap * @param params.alg {string} For example, 'RS256' * @param params.modulusLength {number} * @param params.publicExponent {BufferSource} For example, a Uint8Array * @param params.usages {Array} */ constructor (params) { let name = 'RSASSA-PKCS1-v1_5' let {alg, modulusLength, publicExponent, usages} = params let hashLengthValid = alg.match(/(256|384|512)$/) let hashLength = hashLengthValid && hashLengthValid.shift() let hash = { name: `SHA-${hashLength}` } if (!hashLength) { throw new Error('Invalid hash length') } if (!modulusLength) { modulusLength = 4096 } if (!publicExponent) { publicExponent = new Uint8Array([0x01, 0x00, 0x01]) } if (!usages) { usages = ['sign', 'verify'] } this.algorithm = {name, modulusLength, publicExponent, hash} this.extractable = true this.usages = usages } /** * generateKey */ generateKey () { let {algorithm, extractable, usages} = this return crypto.subtle .generateKey(algorithm, extractable, usages) .then(this.setCryptoKeyPair) .then(this.setJwkKeyPair) } /** * importKey */ importKey (jwk) { let {name, hash} = this.algorithm let algorithm = {name, hash} let extractable = true let usages = jwk.key_ops return crypto.subtle .importKey('jwk', jwk, algorithm, extractable, usages) } /** * setCryptoKeyPair */ setCryptoKeyPair (cryptoKeyPair) { let result = {} Object.defineProperty(result, 'privateKey', { enumerable: false, value: cryptoKeyPair.privateKey }) Object.defineProperty(result, 'publicKey', { enumerable: false, value: cryptoKeyPair.publicKey }) return result } /** * setJwkKeyPair */ setJwkKeyPair (result) { return Promise.all([ crypto.subtle.exportKey('jwk', result.privateKey), crypto.subtle.exportKey('jwk', result.publicKey) ]) .then(jwks => { let [privateJwk, publicJwk] = jwks result.privateJwk = Object.assign({ kid: base64url(Buffer.from(crypto.getRandomValues(new Uint8Array(8)))) }, privateJwk) result.publicJwk = Object.assign({ kid: base64url(Buffer.from(crypto.getRandomValues(new Uint8Array(8)))) }, publicJwk) return result }) } } /** * Export */ module.exports = RsaKeyPair keychain-0.1.3/src/algorithms/SupportedAlgorithms.js000066400000000000000000000024501334014667200226320ustar00rootroot00000000000000/** * Dependencies */ const NotSupportedError = require('../errors/NotSupportedError') /** * Operations */ const operations = [ 'importKey', 'generateKey' ] /** * SupportedAlgorithms */ class SupportedAlgorithms { /** * constructor */ constructor () { operations.forEach(op => { this[op] = {} }) } /** * Supported Operations */ static get operations () { return operations } /** * define * * @description * Register Web Crypto API algorithm parameter for an algorithm * and operation. * * @param {string} alg * @param {string} op * @param {Object} argument */ define (alg, op, argument) { let registeredAlgorithms = this[op] registeredAlgorithms[alg] = argument } /** * normalize * * @description * Map JWA alg name to Web Crypto API algorithm parameter * * @param {string} op * @param {Object} alg * * @returns {Object} */ normalize (op, alg) { let registeredAlgorithms = this[op] if (!registeredAlgorithms) { return new Error(`Operation '${op}' is not supported`) } let argument = registeredAlgorithms[alg] if (!argument) { return new NotSupportedError(alg) } return argument } } /** * Export */ module.exports = SupportedAlgorithms keychain-0.1.3/src/algorithms/index.js000066400000000000000000000012401334014667200177160ustar00rootroot00000000000000/** * Dependencies */ const SupportedAlgorithms = require('./SupportedAlgorithms') const RsaKeyPair = require('./RsaKeyPair') /** * Supported Algorithms */ let supportedAlgorithms = new SupportedAlgorithms() /** * RSASSA-PKCS1-v1_5 */ supportedAlgorithms.define('RS256', 'generateKey', RsaKeyPair) supportedAlgorithms.define('RS384', 'generateKey', RsaKeyPair) supportedAlgorithms.define('RS512', 'generateKey', RsaKeyPair) supportedAlgorithms.define('RS256', 'importKey', RsaKeyPair) supportedAlgorithms.define('RS384', 'importKey', RsaKeyPair) supportedAlgorithms.define('RS512', 'importKey', RsaKeyPair) /** * Export */ module.exports = supportedAlgorithms keychain-0.1.3/src/errors/000077500000000000000000000000001334014667200154175ustar00rootroot00000000000000keychain-0.1.3/src/errors/NotSupportedError.js000066400000000000000000000003341334014667200214350ustar00rootroot00000000000000/** * NotSupportedError */ class NotSupportedError extends Error { constructor (alg) { super() this.message = `${alg} is not a supported algorithm` } } /** * Export */ module.exports = NotSupportedError keychain-0.1.3/src/errors/index.js000066400000000000000000000001111334014667200170550ustar00rootroot00000000000000module.exports = { NotSupportedError: require('./NotSupportedError') } keychain-0.1.3/src/index.js000066400000000000000000000000471334014667200155510ustar00rootroot00000000000000module.exports = require('./KeyChain') keychain-0.1.3/test/000077500000000000000000000000001334014667200142735ustar00rootroot00000000000000keychain-0.1.3/test/KeyChainSpec.js000066400000000000000000000034161334014667200171430ustar00rootroot00000000000000'use strict' /** * Test dependencies */ const chai = require('chai') /** * Assertions */ chai.use(require('dirty-chai')) chai.should() let expect = chai.expect /** * Code under test */ const KeyChain = require('../src/index') const testKeys = require('./resources/keys.json') describe('KeyChain', () => { describe('constructor', () => { it('uses data as the descriptor if descriptor property is missing', () => { let data = {} let keys = new KeyChain(data) expect(keys.descriptor).to.equal(data) }) }) describe('static restore()', () => { it('imports key data into a new KeyChain instance', () => { return KeyChain.restore(testKeys.keys) .then(keys => { expect(keys).to.be.an.instanceof(KeyChain) expect(keys.descriptor).to.exist() expect(keys.jwks).to.exist() }) }) }) describe('static generate()', () => { const descriptor = { "id_token": { "signing": { "RS256": { "alg": "RS256", "modulusLength": 2048 }, "RS512": { "alg": "RS512", "modulusLength": 2048 } }, "encryption": {} }, "token": { "signing": { "RS256": { "alg": "RS256", "modulusLength": 2048 }, "RS384": { "alg": "RS384", "modulusLength": 2048 } }, "encryption": {} } } it('should generate a new key chain using a given descriptor', () => { return KeyChain.generate(descriptor) .then(keys => { expect(keys).to.be.an.instanceof(KeyChain) expect(keys.descriptor).to.exist() expect(keys.jwks).to.exist() }) }) }) }) keychain-0.1.3/test/NotSupportedErrorSpec.js000066400000000000000000000010601334014667200211210ustar00rootroot00000000000000'use strict' /** * Test dependencies */ const chai = require('chai') /** * Assertions */ chai.should() let expect = chai.expect /** * Code under test */ const { NotSupportedError } = require('../src/errors/index') describe('NotSupportedError', () => { describe('constructor', () => { it('composes an "algorithm not supported" error message', done => { try { throw new NotSupportedError('RS256') } catch (err) { expect(err.message).to.equal('RS256 is not a supported algorithm') done() } }) }) }) keychain-0.1.3/test/RsaKeyPairSpec.js000066400000000000000000000007441334014667200174630ustar00rootroot00000000000000'use strict' /** * Test dependencies */ const chai = require('chai') /** * Assertions */ chai.should() let expect = chai.expect /** * Code under test */ const RsaKeyPair = require('../src/algorithms/RsaKeyPair') describe('RsaKeyPair', () => { describe('constructor', () => { it('throws an error on an invalid hash length', () => { let params = { alg: 'RSA222' } expect(() => new RsaKeyPair(params)) .to.throw(/Invalid hash length/) }) }) }) keychain-0.1.3/test/SupportedAlgorithmsSpec.js000066400000000000000000000026251334014667200214700ustar00rootroot00000000000000'use strict' /** * Test dependencies */ const chai = require('chai') /** * Assertions */ chai.should() let expect = chai.expect /** * Code under test */ const SupportedAlgorithms = require('../src/algorithms/SupportedAlgorithms') describe('SupportedAlgorithms', () => { describe('constructor', () => { it('initializes empty operations objects', () => { let sa = new SupportedAlgorithms() expect(sa.importKey).to.eql({}) expect(sa.generateKey).to.eql({}) }) }) describe('static operations getter', () => { it('should return the list of operations', () => { expect(SupportedAlgorithms.operations) .to.eql([ 'importKey', 'generateKey' ]) }) }) describe('normalize()', () => { let supportedAlgorithms beforeEach(() => { supportedAlgorithms = new SupportedAlgorithms() }) it('should return an error for an unsupported operation', () => { let result = supportedAlgorithms.normalize('invalidOp', 'RS256') expect(result).to.be.an.instanceof(Error) expect(result.message).to.equal("Operation 'invalidOp' is not supported") }) it('should return an error for an unsupported algorithm', () => { let result = supportedAlgorithms.normalize('generateKey', 'invalidAlg') expect(result).to.be.an.instanceof(Error) expect(result.message).to.equal('invalidAlg is not a supported algorithm') }) }) }) keychain-0.1.3/test/resources/000077500000000000000000000000001334014667200163055ustar00rootroot00000000000000keychain-0.1.3/test/resources/keys.json000066400000000000000000000551451334014667200201650ustar00rootroot00000000000000{ "keys": { "descriptor": { "id_token": { "signing": { "RS256": { "alg": "RS256", "modulusLength": 2048 }, "RS384": { "alg": "RS384", "modulusLength": 2048 }, "RS512": { "alg": "RS512", "modulusLength": 2048 } }, "encryption": {} }, "token": { "signing": { "RS256": { "alg": "RS256", "modulusLength": 2048 }, "RS384": { "alg": "RS384", "modulusLength": 2048 }, "RS512": { "alg": "RS512", "modulusLength": 2048 } }, "encryption": {} }, "userinfo": { "encryption": {} }, "register": { "signing": { "RS256": { "alg": "RS256", "modulusLength": 2048 } } } }, "jwks": { "keys": [ { "kid": "6b65j-W37Vc", "kty": "RSA", "alg": "RS256", "n": "xykqKb0EPomxUR-W_4oXSqFVwEoD_ZdqSiFfYH-a9r8yGfmugq-fLEuuolQSqrzR3l9U0prBBUeICYBjfuTdRinhMbqkwm8R7_U6dptHe2yILYHLAl0oEooSDKaFMe90h7yDaWiahOewnhh4BWRc_KRNATqx0XGfVmj7Vt4QQifk_xJYZPbLClf8YJ20wKPSebfDzTdh6Jv3sM6ASo5-1PQJNqvk7Dy632E3zIqcQn8wRqQ3hDCJmX3uvMQ3oQNCpJDSvO1kuB0msMWwBwzq3QtUZcDjXovVpi2j3SZfc8X1nlh2H4hge3ATwb1az6IX_OQgn4r1UIsKqIUsTocIrw", "e": "AQAB", "key_ops": [ "verify" ], "ext": true }, { "kid": "7xYXMFNYT0E", "kty": "RSA", "alg": "RS384", "n": "sSxcfdyfj2GhC_ndw96Y80yI6InuSChNOF7gg3XZCJIxyISLJ3CccDvQmvcmxiP_C1CFFZW9LHXInTqcPCW4jeccIXDjs9u1huwiqE-zS_nQbd87JcX3qNsrYcgmQ46ely8GxOJwij9IO4h-U6iIWG54htkFuIuYU-6NSF1z8EGl3maT8bYVKWxhKnMBF_ixrPn2fWLvB6Z5FTqLgRIPVz1d93NbOZxca9ESjCUHP8OfT17F9EqIswRwmIXJdimBIZuC5fSTkem3SRFBqtrIjIxLXrCEfnnWHJeyCxOU8i6IIhKllRhotJpoTE8hWEhZHXiaPZAlRzeujH7_rnXt0Q", "e": "AQAB", "key_ops": [ "verify" ], "ext": true }, { "kid": "vQNpC7gQk9A", "kty": "RSA", "alg": "RS512", "n": "y27t6i08vm8PJXODHVS2lGa_6dsK9SJHxJEDkVltUf__goPs5P5mvpjXGeRCE_-WA6ixUi-BhwDHIANKF6WCfMMNwNf-XcJ4kvploY2YdtKE7XHbPhQPt5NS3kw9UFss8zLv_GyS4uFl_PlTRTW1u-6Ot7LlFaGukJ-1PfBCmMA_1UJiWxwNIpS4Bel1G8zqau0uFwnrnIiynQr8GMW9yzYY6DDfCAuKGV5Uqg8h2eNGDPLWVqSK6zDoHjeVOJ_0cgMn0R1aY3DOaTgAoXvqLI8_mcoSIIaVBz8ZVGFMqT5pXMfhD0RPsfAavW_SVhQ2Pq9FwEmGZ6WDBzHCsGL7-Q", "e": "AQAB", "key_ops": [ "verify" ], "ext": true }, { "kid": "4AYDLtE4UFQ", "kty": "RSA", "alg": "RS256", "n": "2a5BkHg5QqV49HMveOqPJFiyxWm7NJuF9R4e55AHNGDudGo6t5m79YhCX5K4jRs1aaEllTSW1aJAgrYw9QGn7PB_RY3V6zPSRUQfv5USVmh-Uc75xXDeOg1rWuoa5gXN97o0gE0lLEH4hVAGLbw6FpfyMMUBQk7WhYPjuMo1ebzmVSXhpP-kCasal4eb_R2X7Ln6vhWIKXmeQcetn6tMRg3g4AWilJIkaFKxJLlpZlKTJYJaG7604SX_t7_fZWgOzXFwJ_-nnOMY0UxtSmBXQ6gMyUwLWV0PKHEwC8B3TtaEFoytiCuXKsdugdvuYc48Ib5KKduPpsdVU9onO2Pg5Q", "e": "AQAB", "key_ops": [ "verify" ], "ext": true }, { "kid": "Zpq8J7aPOTs", "kty": "RSA", "alg": "RS384", "n": "tsTU-3wVZKChqec6RFKyqfHTuaVO2k4i6V3d2pB8Pei94kIRe6SllvUwvXsHyQfYyenbtOkvgxoCyyrhvqhTeUgCYU_KLpgxTeoe5WfjkHJg9O-2fXIcWX-tC33jUzf0snc5GGRRNfQcB915o1J_vB6yI-0t6zD4IHJyAhm8_7T5DYsb9o5n_f6F6FMxSWN27UESf_v3Nbfd_6x8S1yDiAjp3-JBZ56TMyprnMxoCNDHpxTL0cQ5yUOdpJbiM4BB3_HK5VIHIi8BNKIG3lC4US5zQP8KWrXtYJ1JfyHTGVNNgRHnRvngbDNi05safHMjsnrQkM3NY1Oaut0V2K8Dyw", "e": "AQAB", "key_ops": [ "verify" ], "ext": true }, { "kid": "ohnoGTjONV4", "kty": "RSA", "alg": "RS512", "n": "tNQQr15whsDdse6g3TWpQxeQgUfDbhisHhirKbFvwa_fQzQ_TpV0Z1z2djHdxCRsKLm0WBeIPW222o_UjeDhgtYjLvknI92KWyK2xbwFTy6mBjUqwWrLcRBEzSSI0_u_u0VmjNUq6iHPW7e4fxXf8R198lEEZQiScLCZ6nPggoqUp4JvZze_Y3CEVt8OsO10DvQnjNLYzl5Iz_jajnPtGHnuLoqnv5IltbjrvFT9ZdBiYk4fejlyuwW5DYlcXOmsPml-6o9k-qKZIqIjtOv7kAFVSFJ-Cm6NdFtRjmWIsqjQQ3AZRvSGqXkL1qXMY4N7zwo68TaBiaHm79vVzKdwnw", "e": "AQAB", "key_ops": [ "verify" ], "ext": true }, { "kid": "0wSgJc5P4VU", "kty": "RSA", "alg": "RS256", "n": "wrK3PPcXAHsv9M4U_emCKo1DAfrGn3lJOizbmti16xPjrFEnwpXIVOkU7KLDMnN2TDkqW_dpaYxXf270ezDTACj3r7QKuE-3X_jAcS-tCDZxjFnDPkvPz8vhkR8LaR4GVWDEosUTYnNp9rpAWTyJs4s39HKOu5AlRKFUcjYZCE0U_WJu50MkYIQ9qiQVEjTpWlRIe-ieIEEAHg5KZYs7DeNXvOTpSjuBZxjaCgaXFx_x-mJwK1is_7ct5iomyxVgXjxWzz-FKO0c85OhtJNYJLOXADcVY-Te-CppkHFRUkBX5kjt4C5oaLy8zxBRmKUBn71sWfdY1D5gSmEaHrMAPQ", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } ] }, "id_token": { "signing": { "RS256": { "privateJwk": { "kid": "n-EbI7z0roE", "kty": "RSA", "alg": "RS256", "n": "xykqKb0EPomxUR-W_4oXSqFVwEoD_ZdqSiFfYH-a9r8yGfmugq-fLEuuolQSqrzR3l9U0prBBUeICYBjfuTdRinhMbqkwm8R7_U6dptHe2yILYHLAl0oEooSDKaFMe90h7yDaWiahOewnhh4BWRc_KRNATqx0XGfVmj7Vt4QQifk_xJYZPbLClf8YJ20wKPSebfDzTdh6Jv3sM6ASo5-1PQJNqvk7Dy632E3zIqcQn8wRqQ3hDCJmX3uvMQ3oQNCpJDSvO1kuB0msMWwBwzq3QtUZcDjXovVpi2j3SZfc8X1nlh2H4hge3ATwb1az6IX_OQgn4r1UIsKqIUsTocIrw", "e": "AQAB", "d": "btXB2SiPzB3kyDjEV8IJ5EaU6kZGokI5rLeV1KYvH1KYF0yUibYi5wvXTA9ttAcQ5Kg5FFt7P-znECKbPGsxmXKFGJ--QVZ-rQPUXV1dEpr7zheFWvNs8aRVFyU0yu1v9Ho8x9Sm6X_nEpSbdqS7-v2UvTJk8yJAgl46QcWjVqzRRpIa_EloQnBTL1qRln5HWQPatbZynuu0D2qFQGkqk8oQpsFM0MnU6TFYEetb6jG4BtJrtC6tb53-3ZvnQUw8vqR-snB-ZALgG2mhybE6gi9uJxn_eYHl50a-fdWm9bV_7a0PCGOMxaf4EErcZyM3CMOrxB6ePD4qaexM8PrAwQ", "p": "77MnRfFyZomTMCkEn-AE4yilkvf6bpuHzEGPI_w2WCYekHUqJnAMYmmVEY9jDsU2ZkFbFiFwz1Etkw8DTWCNnbsUAaSREKjQQKgdVWiaF4Xd2YBnPr1R2tQPg5Oj3EUwiZO54si9PPX2bn9zAVOHDoPJNEwHsY2qQVQo46yQ29U", "q": "1LRIRkqbcH4gHl7rrO5vceBoBaP77FtME6j7ftHAr7eBRIc05vTe0_mLOl_x8S1zrJfFUod0wKPDM3K3ul-LKhYCB5Ia9fjxVz_mT111b2vFpsLtcmearcVlAabc1BMJnlT8mSQDbHSKOgFp39gWG8A7Spum9hT_n6nj2B8vKHM", "dp": "KdZ8YWHuVOVocnyGWR1x3PBoTgnIhWvSPR2oTWClX6dvqEk7DTB7iOXi5gjAAoD7qMd5jZhSp20E5mk1a1kR_tj7efnudBjzGIX_cLvsQXTVLzSRUMscsXRYLOAlNrpA6ZKGXIYrOau40rqeKToaFbd89ArdQOl4MSTuaibhte0", "dq": "haEhiu-SShEi8pGltIYzg7J9B5YnTi1pVSyjm-ABEyPRVZEv_i021KuA-dKPOHraokLTV-77vY7ksqzOmost7enXqpPy1jdov-zIL3JCthZaHVN3Q5mtRqCtvWTsdVvkfhFchIVolTl-IcUDk_q4oQSa-1tggFde5RshpoSo5ts", "qi": "vKuzSnS_ClrWjtZhmLqCqwNxsFEuIP38RvsfSmKCUihMw0UX3N-OspicUQALfZdpK7L2rgFSzhB56JnTez0nZ3cOinFpzUe9tklFbpqq-KSwDY4cumWK0WDGdEdj2lkFQMn5e4W-Y2td66xkwBW9JVnk0ayh2QRUxlMM-pP6FAQ", "key_ops": [ "sign" ], "ext": true }, "publicJwk": { "kid": "6b65j-W37Vc", "kty": "RSA", "alg": "RS256", "n": "xykqKb0EPomxUR-W_4oXSqFVwEoD_ZdqSiFfYH-a9r8yGfmugq-fLEuuolQSqrzR3l9U0prBBUeICYBjfuTdRinhMbqkwm8R7_U6dptHe2yILYHLAl0oEooSDKaFMe90h7yDaWiahOewnhh4BWRc_KRNATqx0XGfVmj7Vt4QQifk_xJYZPbLClf8YJ20wKPSebfDzTdh6Jv3sM6ASo5-1PQJNqvk7Dy632E3zIqcQn8wRqQ3hDCJmX3uvMQ3oQNCpJDSvO1kuB0msMWwBwzq3QtUZcDjXovVpi2j3SZfc8X1nlh2H4hge3ATwb1az6IX_OQgn4r1UIsKqIUsTocIrw", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } }, "RS384": { "privateJwk": { "kid": "TnbTOYnTHCI", "kty": "RSA", "alg": "RS384", "n": "sSxcfdyfj2GhC_ndw96Y80yI6InuSChNOF7gg3XZCJIxyISLJ3CccDvQmvcmxiP_C1CFFZW9LHXInTqcPCW4jeccIXDjs9u1huwiqE-zS_nQbd87JcX3qNsrYcgmQ46ely8GxOJwij9IO4h-U6iIWG54htkFuIuYU-6NSF1z8EGl3maT8bYVKWxhKnMBF_ixrPn2fWLvB6Z5FTqLgRIPVz1d93NbOZxca9ESjCUHP8OfT17F9EqIswRwmIXJdimBIZuC5fSTkem3SRFBqtrIjIxLXrCEfnnWHJeyCxOU8i6IIhKllRhotJpoTE8hWEhZHXiaPZAlRzeujH7_rnXt0Q", "e": "AQAB", "d": "EUWm2qpIb9zvq1A3m2G2_XkHm8vei_BJ3uX48zfo6Zn--nJZcoXa6mIaaxEPGZ_mvD6kSr9nSs9MuG8_TenLfSH7rxLnT-BqaPXaRxhF_XEkLaHmjT8obrC3IPg3Krzbnxd5jHYazI9wxkT_M76pVB2o6FR950jMl9oYseZSaCiErIPZZitM5QhKpVWo3S5D6iYoSVHFXHUhSV4L7Abkp7FordJzA5VYbj6A9592xTH8IF2qtZKcM2OJ_KJ9wOSWswb2jnv7yJh5RyO5gfFKfQwV8KTeZN0fif_ndqoMnmTBEPAfgzKeJoHX5EMqQz5G3O7UPZcgW7rYwWfZDcr8xQ", "p": "29TMN9RIyi6nJz-EaV02OriUj-gB5GoTgu1TZr0YKVyQDuaKHaErEU67GZ0JlVwZ36XgWt6govuaQcX-vFx44NvAimi9sGI5Sqv9ulvdWS0vfnBwLCj5S-H5ZKxu5iNyr8Tjxg3ToZ_mll9jp8Xwl6Xn5UZf9IYEzxfeCT0l9MM", "q": "zlLT8TXhh7XstPvy88YskC5z9-Tp2XEeNgu4nLBPTEBk7fhNvWnywV-QDcPGhe_5WkYXW7BG0Ou3D1yBbQzGKM2KxPuR6cZB9Z_hiWfM-Xnx-jglYI8PcxByupMc6w2qweXPc31oEHtmMM3nfK4_ylBZpTOjMK4oZTxEi6Inmds", "dp": "yEu2mvPIMreZQ5CDFAGDBLPjYM9S0gy4pGWaxRRr7FvTQlBZ7a9Ib2ed4DOOZNFbHcSkjAcms_mtTlqZxIQ4-yAe6b2PgCOu8dONxnwnTTZ3fKvz-LiIlNRbiJf4H6aSu4YYxv8YIb5wufhF87rNgU7_4I5jpYdq4Ept21skt0M", "dq": "Df80faKoICrJWiXVf5AsKcPc_i47ZxLzd7sn1Qlo98VOk45Kx9TOMSwoe6aSazeydNtYblHxG-9BxfsClMFPGCmJhn-CkJdwlhc4l7fBAhOxxMXWU0yhQa8V-W5Ngey1bt4GStFxI90bO6hcUesITNNSoZB1O7I071k9VTO0-A0", "qi": "21qJrht8jm1Cj2NmxndhOyaI5Z8aISDFRe8kdrfkei07IW3wdRfWcKZf8nsOCUbYzG-xmPJWZU14op944Usa6RMPXve6V5E0Ll7EcvhFm6-c9oOZ0RLg8UDyXF1UDui-m-M1jsCOEGmrRLRGrKnIBnrVVMkt44nIujXIeAz7PDg", "key_ops": [ "sign" ], "ext": true }, "publicJwk": { "kid": "7xYXMFNYT0E", "kty": "RSA", "alg": "RS384", "n": "sSxcfdyfj2GhC_ndw96Y80yI6InuSChNOF7gg3XZCJIxyISLJ3CccDvQmvcmxiP_C1CFFZW9LHXInTqcPCW4jeccIXDjs9u1huwiqE-zS_nQbd87JcX3qNsrYcgmQ46ely8GxOJwij9IO4h-U6iIWG54htkFuIuYU-6NSF1z8EGl3maT8bYVKWxhKnMBF_ixrPn2fWLvB6Z5FTqLgRIPVz1d93NbOZxca9ESjCUHP8OfT17F9EqIswRwmIXJdimBIZuC5fSTkem3SRFBqtrIjIxLXrCEfnnWHJeyCxOU8i6IIhKllRhotJpoTE8hWEhZHXiaPZAlRzeujH7_rnXt0Q", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } }, "RS512": { "privateJwk": { "kid": "szGUtBe72lk", "kty": "RSA", "alg": "RS512", "n": "y27t6i08vm8PJXODHVS2lGa_6dsK9SJHxJEDkVltUf__goPs5P5mvpjXGeRCE_-WA6ixUi-BhwDHIANKF6WCfMMNwNf-XcJ4kvploY2YdtKE7XHbPhQPt5NS3kw9UFss8zLv_GyS4uFl_PlTRTW1u-6Ot7LlFaGukJ-1PfBCmMA_1UJiWxwNIpS4Bel1G8zqau0uFwnrnIiynQr8GMW9yzYY6DDfCAuKGV5Uqg8h2eNGDPLWVqSK6zDoHjeVOJ_0cgMn0R1aY3DOaTgAoXvqLI8_mcoSIIaVBz8ZVGFMqT5pXMfhD0RPsfAavW_SVhQ2Pq9FwEmGZ6WDBzHCsGL7-Q", "e": "AQAB", "d": "e8qc7AtrE1bxmJCaEJckI1oDHoM0P8cEdRDpYuFQLL3i9cDhvtcwHntTKWZE73ZwCvn0LVbLMyO-CDT4OTt2FAV9sQO-vb9BL-8_OPe491VTnknSaAw9JDB6FNu_084npDYy4yAOohEWxsHRciQk2p9oKVCrJmakm00UNQSnAduk_sEANvMK79AfiarRx21fyOw50G0uOLNehbHLJHGDb9QPTmNh2_XIEW6NDSUiR7gLPpzBlneLR9ck_lSk6Ii75aN-TAy-ktIqhhM0jVpVNPaQoyOGABD2nN9OPsbT66Mh7ZrXFK-EMUEaFC1L1saMaMVbnuiTTvz3EtT0L74wAQ", "p": "7iOEAM3eGvZDO-ARQU-y2zxA4ZtESlQwyKdCyFqg3F6AnXbjzK1WnSFkeIjmV39Ouz6MeTVoKMt0VE4Th4yV9qLaEZtn6Qm4RXiwDtH31OxBz2XGfRRNvfdU4gZjYWiT9ZfKzCQ4gzsmv4VWRXH1YJB9Dg6GGTwY7bw878F2urk", "q": "2rEJnxL_lw18ePPPgFoMw39TFEWz3sRab2wmsC9ZkhLx-5tKmuiz-bWiq6CoTmfDaNGITrrhRRzlhuNWNJ3UPyRcXTxDNumPDtFs0BFBnUromCVMzkjTlA8bSl6L8m0k_6c5V1-T1HhlWTvaSDRfHAo2F58_vYrCAPvLO8Skq0E", "dp": "mBRMT6spRXSxmUJOIrSAF2xSTwG2SfWcNfhDOOpPwowDfQwZDHRCVRSnMibTW1ts7z1RvnJx82ODBffalv0cJCb6NrZVVMilSpB5du12ZR8AJC_t46EcyM10AuhrfSCpJPVsSJR1FrGm3DaN0smlF6cOhfSWhPURQnmtPLwDU9k", "dq": "SW6k4mtIs3ntz9deV6TlIEgCp434Jdcc3skb_JEJrIj8BBZS6M02kaSx6JtbqGgjL3EcKgOKXOClGbQAYE6W1FTrRstTbn7icIFV3k_NDnn2vA_aSHIfyVmBOmmg6FhJ0iZelL4dL3z12w7D7vbW8N1k6G2qw4RyPE5QIdOXz4E", "qi": "Sg5A4bfbzGFvlVwzajJjksjMNpUF2klisrlodDkP9NMlnP5EaXraKZiVeWbaMH5TZJvJHuEe6dVZCN1p57aFdFqFqob8MpOmmkqpRX47MCfMBklusmeJN6Bwsdr7XlXywDAWsANIZMY98_HQwWydqcPWG8zWioUr8ccCVtaoyYc", "key_ops": [ "sign" ], "ext": true }, "publicJwk": { "kid": "vQNpC7gQk9A", "kty": "RSA", "alg": "RS512", "n": "y27t6i08vm8PJXODHVS2lGa_6dsK9SJHxJEDkVltUf__goPs5P5mvpjXGeRCE_-WA6ixUi-BhwDHIANKF6WCfMMNwNf-XcJ4kvploY2YdtKE7XHbPhQPt5NS3kw9UFss8zLv_GyS4uFl_PlTRTW1u-6Ot7LlFaGukJ-1PfBCmMA_1UJiWxwNIpS4Bel1G8zqau0uFwnrnIiynQr8GMW9yzYY6DDfCAuKGV5Uqg8h2eNGDPLWVqSK6zDoHjeVOJ_0cgMn0R1aY3DOaTgAoXvqLI8_mcoSIIaVBz8ZVGFMqT5pXMfhD0RPsfAavW_SVhQ2Pq9FwEmGZ6WDBzHCsGL7-Q", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } } }, "encryption": {} }, "token": { "signing": { "RS256": { "privateJwk": { "kid": "BFZFPsxzZMc", "kty": "RSA", "alg": "RS256", "n": "2a5BkHg5QqV49HMveOqPJFiyxWm7NJuF9R4e55AHNGDudGo6t5m79YhCX5K4jRs1aaEllTSW1aJAgrYw9QGn7PB_RY3V6zPSRUQfv5USVmh-Uc75xXDeOg1rWuoa5gXN97o0gE0lLEH4hVAGLbw6FpfyMMUBQk7WhYPjuMo1ebzmVSXhpP-kCasal4eb_R2X7Ln6vhWIKXmeQcetn6tMRg3g4AWilJIkaFKxJLlpZlKTJYJaG7604SX_t7_fZWgOzXFwJ_-nnOMY0UxtSmBXQ6gMyUwLWV0PKHEwC8B3TtaEFoytiCuXKsdugdvuYc48Ib5KKduPpsdVU9onO2Pg5Q", "e": "AQAB", "d": "yBSBMxcuGnIaASY2hj-inlfgyc9rQOmKcUjkQxeBZ8WIp_xUSBnIR-CckVg8xYYyzApQjxotYeOmw_5OdYS2IvTTJs09yNhy9zOsst2EY2PcZ7mEntQljtpyyLfRTYgcTw0Z4KwH9OwWUALECxmtksLK28TYaWgpicM2ds115ZPmMls4ru3wRFUiU4ec0v_l5TFFDfmPVU_Bl5DO8xT2J6pwIoBxBABS6JP_afPaTo-fXxsS46gQq4LVP7lu9on0DWizuPFsb97RYbrKwmjHmUOOLIEFGihoESHFfS-tH13n2dPh4ahKL6fObHFB14VQdn7PM8TQ3fCZpFDauzwDQQ", "p": "_rzslg9rtA4fVKb5VjtDrVEukOcESdF_32JLcpEJG3fPNwGJjAoy0Eoc4PP71afYN1B1q4KtmfzL9oa8mJZhCHxfC_jE5DFIt3l5AsG8nharVVDvEkQFG0ut6BVlt2Wz98n0Nkv0PEqBrl5mNWWz2qtEtyDLjssToNIkllrF-uk", "q": "2sJVWRtGNA4NXTRdr-o0UIfHLFcPfK9FQJsE5wFBatAYXXhz7jMPANoXJYs0gK-nqXbyyKEPlznpNXAnT-Lr0Jw9QAdT1NSedpb8W1Kkt79jYKEa8xpnJiGNCK24ZapyJnmToXaZcPVTd_VtYh2JH0-ifihq4xbevY4Mb6KRAJ0", "dp": "LZn8cFCKY00z5p-NexZY4ynpQB9a8SwDzCV4hCXaj4Q_IAWybdNVjVv3F34V0wWc-yn4Jp_aG_rZOGSeVmJ1NGOvUCFUfdUkw1OHiirMbTB5s2gtpANxNpWxqB5MeyxFr1ID2cR3EGP2GxTjDyQJsld-kZnZVtKbzOpRimjtkCE", "dq": "AfWZpBmtcEvr7LjKWggD_fCpZQlGlzxOp3x798G4H0fL4BJzH2APs1dAu9JaXwizvL3XANsIc7dOEm91uq5ypESx-7_VDWP2I2PI0NyVoxuwvS3UOvcyk1HbnIdJzodPTG7sVpbkWvGnlLx7BVT0ZHZnIzAjfPM4fYQnMlzHZbU", "qi": "qYndnKxUTbNab4CP4cdbLyAXmdbr85U0DSX47Uj-U87GXRCkwbXn7r0nMPTqPftfAwHZwhCIiC28TGfjQ_35BqXBUflNCJ770PCzx2WBikMuJIdvzbGQwIQS5qiYXUKvYEV59I9BkbCnYuxNzzSvA9xKzgf-PkWymxfmXIRp1G4", "key_ops": [ "sign" ], "ext": true }, "publicJwk": { "kid": "4AYDLtE4UFQ", "kty": "RSA", "alg": "RS256", "n": "2a5BkHg5QqV49HMveOqPJFiyxWm7NJuF9R4e55AHNGDudGo6t5m79YhCX5K4jRs1aaEllTSW1aJAgrYw9QGn7PB_RY3V6zPSRUQfv5USVmh-Uc75xXDeOg1rWuoa5gXN97o0gE0lLEH4hVAGLbw6FpfyMMUBQk7WhYPjuMo1ebzmVSXhpP-kCasal4eb_R2X7Ln6vhWIKXmeQcetn6tMRg3g4AWilJIkaFKxJLlpZlKTJYJaG7604SX_t7_fZWgOzXFwJ_-nnOMY0UxtSmBXQ6gMyUwLWV0PKHEwC8B3TtaEFoytiCuXKsdugdvuYc48Ib5KKduPpsdVU9onO2Pg5Q", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } }, "RS384": { "privateJwk": { "kid": "8NYFaM-6LhY", "kty": "RSA", "alg": "RS384", "n": "tsTU-3wVZKChqec6RFKyqfHTuaVO2k4i6V3d2pB8Pei94kIRe6SllvUwvXsHyQfYyenbtOkvgxoCyyrhvqhTeUgCYU_KLpgxTeoe5WfjkHJg9O-2fXIcWX-tC33jUzf0snc5GGRRNfQcB915o1J_vB6yI-0t6zD4IHJyAhm8_7T5DYsb9o5n_f6F6FMxSWN27UESf_v3Nbfd_6x8S1yDiAjp3-JBZ56TMyprnMxoCNDHpxTL0cQ5yUOdpJbiM4BB3_HK5VIHIi8BNKIG3lC4US5zQP8KWrXtYJ1JfyHTGVNNgRHnRvngbDNi05safHMjsnrQkM3NY1Oaut0V2K8Dyw", "e": "AQAB", "d": "c6MbwjXi7zT2tCH5aqsMIZxAwm_F8TeIvTBHovbRtPXh1hrJJvqoTle5y9jgtx_A2Reei9sR1ZgKdnMeO1wS3GRkJR-vo0VlxI9BlxHhGE5V7AMS6lHdf_7ZBLFUuB1qM9RdCoLGIyXc6lkFkgErkmG1gieZavojs5vVaGDShNHww_YQ3O_yQMnOoFaVOH4Wxh-lZx2thiyTAiZ3SnqkTzYII3mCWz8zMCx25s3A9e-HIhdt5Mz0p0T3URfHIjpbBth8h5uZd9whIpH-ZJo8x4X_569Z13b3JybMMbPtpKT7ejkppQy4S0V8YUMVcGSUuok3JJZakp3I2veLSpK_uQ", "p": "4492ySXLEOw_gcoTlPdMpQ84CQ8GBPohsGvCsIrc136GgfRwHY2vytHgnob-5mNAyOi8cO6MR_gTEVZ8V2scDW1PTiQmFnIVpZyKVamP-sGM68bNgpzKxpx1IrDdmH5XuixsOMIcaSi9D2dOlGJjVfen6QC-9rvUpAi3yjw3V68", "q": "zZxQXZbBIoZp41cSU_MwQSmemiJBbil4y5_rJS_oJdL0cp2s90Cv-SBvDckpjeXK4yehi-aNqPOcg2bo5YOcglR_MF1Tqyxekr23I5xPR9uD-OGRPzGX9fLZp5u7h-eweDBiUCmJ7l6-9re0L2Ynmmr9Xtfk6SIFRgaPBxSUgKU", "dp": "FAckZT-2R58DV-D5KDs7u6tDO0jfNsDtBiH12KPgG_c-z2E8HN3Dgw5Fvq4S8SP7J7OUIjxcjFkBFJ2QBAGhTm04daAo2gDgdtr4MeHikidGx9mPbNdv4VsT1CHM3wb_oZsXS0eKSJcVTK3C9LxUEdTiKleudvjBFk7OHjZ-iHE", "dq": "tRlFP-mU21AA3BjJL-A3VG3QIHqjnuAwYUnN18dGdmpEnN98hZsYZBnJNx9D1pPcDA_QHmVHyNQpuyNdZypxI-pAzNym4VIHSsCHGnqG6gfTSdeEE2EfM6ZfR13H44CS0onrTigIMUiKcsyDjGwx3bT2zh7ipOUuN-t29wmNc3E", "qi": "JxHBM21UtXO_X8crZeKSTANOM3wW04q-X251T29n5vVbNuNkAKEPG4ZY5qxVkhaLGs7tkS293k31zPYss3ywvLYtMoY1ddegEhi0eoZgPMUeV-lLjNhfU_2WBL1og2yn5GECGDFXc8NlJ17LQX5bcRQ7UdMZ4WgXjG2E4RDaamk", "key_ops": [ "sign" ], "ext": true }, "publicJwk": { "kid": "Zpq8J7aPOTs", "kty": "RSA", "alg": "RS384", "n": "tsTU-3wVZKChqec6RFKyqfHTuaVO2k4i6V3d2pB8Pei94kIRe6SllvUwvXsHyQfYyenbtOkvgxoCyyrhvqhTeUgCYU_KLpgxTeoe5WfjkHJg9O-2fXIcWX-tC33jUzf0snc5GGRRNfQcB915o1J_vB6yI-0t6zD4IHJyAhm8_7T5DYsb9o5n_f6F6FMxSWN27UESf_v3Nbfd_6x8S1yDiAjp3-JBZ56TMyprnMxoCNDHpxTL0cQ5yUOdpJbiM4BB3_HK5VIHIi8BNKIG3lC4US5zQP8KWrXtYJ1JfyHTGVNNgRHnRvngbDNi05safHMjsnrQkM3NY1Oaut0V2K8Dyw", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } }, "RS512": { "privateJwk": { "kid": "x3dswUUSN0U", "kty": "RSA", "alg": "RS512", "n": "tNQQr15whsDdse6g3TWpQxeQgUfDbhisHhirKbFvwa_fQzQ_TpV0Z1z2djHdxCRsKLm0WBeIPW222o_UjeDhgtYjLvknI92KWyK2xbwFTy6mBjUqwWrLcRBEzSSI0_u_u0VmjNUq6iHPW7e4fxXf8R198lEEZQiScLCZ6nPggoqUp4JvZze_Y3CEVt8OsO10DvQnjNLYzl5Iz_jajnPtGHnuLoqnv5IltbjrvFT9ZdBiYk4fejlyuwW5DYlcXOmsPml-6o9k-qKZIqIjtOv7kAFVSFJ-Cm6NdFtRjmWIsqjQQ3AZRvSGqXkL1qXMY4N7zwo68TaBiaHm79vVzKdwnw", "e": "AQAB", "d": "WQFw3QD9HjbcaHJul7OMBswYgqnSFeTrAz1dMn515b4thKS47HNAAoiNf4v3_k13N2yAykN6-dGBCX92PgeMuJuyYbKdg1fOfCFQGW_8pQbvDWsqCl4ImTRKyTbMapsluoXkfjmGkAvoRtrdYyBfRA0iKx2ZrilT-0fR2scCoP3E93Dy4GRJcfH5HTsSYi-fE6OYL0t0yLuiHvxlAgrgH1BG76rsg-85Cs58nfv1awzhlzsj8aOeE3YJpm0_7CdOWksjJy_3dpkXW8l6eBeLPLbDDTY2mqhyhBwd9DYgeqcEOnHizx6nSrp_EvVpodszmiLJaYrEzI-ODnpLA5Ga0Q", "p": "5jDw3E7FFg-uOeuRR8uEdSvrxu7oo_xVMF8rbcxEQOwv4D_4mUzuaCXNFvAPM8tUwxMJ0kb3MndSobmQ4IidrLhac80PAU3UtzIfwLp6G26P7BEaWgORGyxJ7wCqLWDSeFzJU5HZuqNZhWMZNOnA3yVvCZwd8_G9S818KBWqT7U", "q": "yRpKEY2fMxxGYp1Mmv_-6bAL4vNvrHJKyHTgoUqfhX3J-yPOcrRiNoXQgRG5W0TZ1WaPpxPNzNmwerL5NgdhEnphiOYFFxqWMB4-8cApoZ_hLwdvN9gU8QwI7LeR5LvJfvNRAXk5SKwA4Lxdv6dOasgwRDOez9k4LucStAhwa4M", "dp": "sQKZdRAthhVwyKFZCnh51xB7MnKs-s0jRRsp7jjRk4sMFcmbhWcfPX80Apt_VTjaaVfOiaCAnpIbPLnRqpfjh9oL1FivXTVYibdFq5K6KzYN_l34gZeUkeywvdUpB9QYhO308wqI_3h1cpkN3R6TbgT_KcifT-NoyU2uV7SxTrE", "dq": "ZAG12drS06j4-6t74Az0aKQKlTc4Tsqbk-pDlxI0GD-AxWp-DR7VRIXxNW5p_hBUP8J8J7s6eee70pjUQ-ERjg3cGSdCWsX28EDqTnCgg2atpiSNU3kamPDCM23qmJQmVQKNQy1UpmYbHaeu-1MpNQMOtavyCyzbur9g1rTnqx8", "qi": "LXw_6zmc8WRUspO8Wgit7t5s52VfFuW1btK2EQcgUtlfuOISSwLdmVNVnjosKx6_Arm7G8uZL3JZ3Jj0Q_D3H4wjkyqrylLPET8IoIZcJtPkZu0V4eycQiy2Y3wgnEwIqkZmrihzFlhB31maZOmTlVE__049ijTeEAt70FS0xkM", "key_ops": [ "sign" ], "ext": true }, "publicJwk": { "kid": "ohnoGTjONV4", "kty": "RSA", "alg": "RS512", "n": "tNQQr15whsDdse6g3TWpQxeQgUfDbhisHhirKbFvwa_fQzQ_TpV0Z1z2djHdxCRsKLm0WBeIPW222o_UjeDhgtYjLvknI92KWyK2xbwFTy6mBjUqwWrLcRBEzSSI0_u_u0VmjNUq6iHPW7e4fxXf8R198lEEZQiScLCZ6nPggoqUp4JvZze_Y3CEVt8OsO10DvQnjNLYzl5Iz_jajnPtGHnuLoqnv5IltbjrvFT9ZdBiYk4fejlyuwW5DYlcXOmsPml-6o9k-qKZIqIjtOv7kAFVSFJ-Cm6NdFtRjmWIsqjQQ3AZRvSGqXkL1qXMY4N7zwo68TaBiaHm79vVzKdwnw", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } } }, "encryption": {} }, "userinfo": { "encryption": {} }, "register": { "signing": { "RS256": { "privateJwk": { "kid": "nvgfF8szNnE", "kty": "RSA", "alg": "RS256", "n": "wrK3PPcXAHsv9M4U_emCKo1DAfrGn3lJOizbmti16xPjrFEnwpXIVOkU7KLDMnN2TDkqW_dpaYxXf270ezDTACj3r7QKuE-3X_jAcS-tCDZxjFnDPkvPz8vhkR8LaR4GVWDEosUTYnNp9rpAWTyJs4s39HKOu5AlRKFUcjYZCE0U_WJu50MkYIQ9qiQVEjTpWlRIe-ieIEEAHg5KZYs7DeNXvOTpSjuBZxjaCgaXFx_x-mJwK1is_7ct5iomyxVgXjxWzz-FKO0c85OhtJNYJLOXADcVY-Te-CppkHFRUkBX5kjt4C5oaLy8zxBRmKUBn71sWfdY1D5gSmEaHrMAPQ", "e": "AQAB", "d": "IJAlZ3PoC_OOfMIu7pysYZOIvn8D005Eug8gl5BhNLT54iscnEXS4KivEDjUrs5y3aK9--2wWq_XPn__Mc9jquYvLtrM5DCo-csv3H5d0T7R1Oh6aCPj2Nw_mLb5gJY99u-dU5_kcxXchiB2fLZMTZQeQqsaRjKAmS7mYce8fg-i_ORJ2ps4t3kgPNTooAheTbxGmaAJpdNtIpmOR8RTWHjGnb4V0hBYUjRSVpx20kkZD3_sSqfkp5_KVPifOZAXbv2DgaDV9IyB5hywumS2fUaviui0QGQ5O26ao_Bu4QPtVRg3wDBWxXmLkNm-x7dBtiOubsxQRvjVLW4MyAU-PQ", "p": "_pnS1trk67PKMOCwQrvihUKTKtK9E4tyyNBSTmh2wF6cUrAg_XDfBvHv9Zw_rKfKuqm25Ow-A-niPklsIF3bn2G0nAiXXgFHJ-5Lf8JgyHF5X3DOXHqOByXuIzlsP5eUKSTTfip4UwOVPomH-CluSQ2IdQ264hQBw12tg2hpCwM", "q": "w8Seu_i5as2Fh7VFE5gLYd8gg-_gMKRtvDgpOJC4Mur4qOxsxMWTQu3AsMqSurmy4xTDdwHQnb7qWqIFjvtuF_vgoewibcvfooOxBqo0mJNR1W4BQOiUrIcyrIfyq1n2PCNOOd2lqmJqDjnTAJRHMzOnjuklA14wC1HxRAgmQ78", "dp": "ZX7Q7FIK3GOqAN4xpgjcfp9Rp8qnEXmP4roZdaRzYrDUxKnuHNq4Lj6YmGMXhy7ExLEPdicvecPtK0N91VFVUi5TH7jVDuaXhTWb52yHxKBn8EI6IefBYtweGPC1VIyKxfQmU69-lCljf7C4AZgSnRDGC1PT4nhzU3yuxSoEZh0", "dq": "WByR-TacdaalGAyNV6FSVi2wG9IGTL5lKZb5bp0sWraJTraCyhnHCxOpa6jxW5ujGTPKOjEV7Tn1ju9noxAlYCQc2aRHZrWhJSsyrTZET_vWlEJbzBQifninZSIXJepqumyFk-Pf4Y1EAL0j7BnQ5gm0u8zRvO9SnnW2KpGkb08", "qi": "7DZsPWOilFcXDbFLGFoX52GfJINkPzq31uRtSYZEw8rIgSGS6svMpbhWb4eGmqsRP31eckAZTgFEV42dxTZG77sC6tJjGuVPB1fr8JXYWsSyPzyLArr6h0wHSHlPqaM40p_DZ4bIiWNDaPBIxpwBm_HP6R8esXOPwatU9KmopM4", "key_ops": [ "sign" ], "ext": true }, "publicJwk": { "kid": "0wSgJc5P4VU", "kty": "RSA", "alg": "RS256", "n": "wrK3PPcXAHsv9M4U_emCKo1DAfrGn3lJOizbmti16xPjrFEnwpXIVOkU7KLDMnN2TDkqW_dpaYxXf270ezDTACj3r7QKuE-3X_jAcS-tCDZxjFnDPkvPz8vhkR8LaR4GVWDEosUTYnNp9rpAWTyJs4s39HKOu5AlRKFUcjYZCE0U_WJu50MkYIQ9qiQVEjTpWlRIe-ieIEEAHg5KZYs7DeNXvOTpSjuBZxjaCgaXFx_x-mJwK1is_7ct5iomyxVgXjxWzz-FKO0c85OhtJNYJLOXADcVY-Te-CppkHFRUkBX5kjt4C5oaLy8zxBRmKUBn71sWfdY1D5gSmEaHrMAPQ", "e": "AQAB", "key_ops": [ "verify" ], "ext": true } } } } } }