From cde5b8b0b3fa586fbda243238dd81f613c4d1876 Mon Sep 17 00:00:00 2001 From: Paul Toffoloni <69189821+ptoffy@users.noreply.github.com> Date: Sun, 3 Nov 2024 21:50:14 +0100 Subject: [PATCH] Add Swift script to update x5c test certs when expired (#211) * Add Swift script to update x5c test certs when expired * Update script * Address lint error --- .gitignore | 1 + Tests/JWTKitTests/X5CTests.swift | 72 +++++++------- scripts/create-certs.sh | 54 ----------- scripts/create-jwt.py | 25 ----- scripts/create-jwt.sh | 29 ------ scripts/generate-certificates.sh | 65 +++++++++++++ scripts/generateTokens.swift | 162 +++++++++++++++++++++++++++++++ 7 files changed, 264 insertions(+), 144 deletions(-) delete mode 100644 scripts/create-certs.sh delete mode 100644 scripts/create-jwt.py delete mode 100755 scripts/create-jwt.sh create mode 100644 scripts/generate-certificates.sh create mode 100644 scripts/generateTokens.swift diff --git a/.gitignore b/.gitignore index 546e2e90..f724f106 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ DerivedData Tests/LinuxMain.swift .benchmarkBaselines/ Benchmarks/.benchmarkBaselines/ +x5c_test_certs diff --git a/Tests/JWTKitTests/X5CTests.swift b/Tests/JWTKitTests/X5CTests.swift index 1c8611db..305e7296 100644 --- a/Tests/JWTKitTests/X5CTests.swift +++ b/Tests/JWTKitTests/X5CTests.swift @@ -25,23 +25,28 @@ import X509 /// Only tokens with an x5c chain that starts with "Leaf" /// and ends in either "Intermediate" or "Root" should /// successfully be verified. +/// +/// Note: if the certificates are expired and need updating, see the `scripts/generateTokens.swift` file. @Suite("X5CTests") struct X5CTests { let verifier = try! X5CVerifier(rootCertificates: [ // Trusted root: """ -----BEGIN CERTIFICATE----- - MIIB4TCCAYegAwIBAgIUDoOefefCNq/TGWriIsYHvz0LpNIwCgYIKoZIzj0EAwIw - RjELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUxDjAMBgNVBAoMBVZh - cG9yMRIwEAYDVQQDDAlyb290LWNlcnQwHhcNMjMxMDI0MTIwODI3WhcNMzMxMDIx - MTIwODI3WjBGMQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEOMAwG - A1UECgwFVmFwb3IxEjAQBgNVBAMMCXJvb3QtY2VydDBZMBMGByqGSM49AgEGCCqG - SM49AwEHA0IABNpv+HG52jOT1W+r1k13bJo2k9DyRyFbycBpPsWQKft9nxwEHvzD - j1ivoMfajxlL+n/FLBnOnY63mFWmzaoZvH+jUzBRMB0GA1UdDgQWBBRjXfPURcaf - 1QF+mCl9T21Bu8xFCDAfBgNVHSMEGDAWgBRjXfPURcaf1QF+mCl9T21Bu8xFCDAP - BgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA0gAMEUCIGIpX9lliU135V8+LY6/ - cjBmGrKKNlYWLLoZ6DiauzdJAiEA9GSAIGhfM9kbWlkcjMs6lA4pwf4RfUEFeghY - pZKbqFo= + MIICiTCCAi+gAwIBAgIUd4xyZnMUgAiH19R8ifnuJ4/Cg1UwCgYIKoZIzj0EAwIw + gZkxCzAJBgNVBAYTAlVTMREwDwYDVQQIDAhOZXcgWW9yazERMA8GA1UEBwwITmV3 + IFlvcmsxDjAMBgNVBAoMBVZhcG9yMRQwEgYDVQQLDAtFbmdpbmVlcmluZzEWMBQG + A1UEAwwNVmFwb3IgUm9vdCBDQTEmMCQGCSqGSIb3DQEJARYXYWRtaW5AdmFwb3Iu + ZXhhbXBsZS5jb20wHhcNMjQxMDI4MTcyOTIxWhcNMzQxMDI2MTcyOTIxWjCBmTEL + MAkGA1UEBhMCVVMxETAPBgNVBAgMCE5ldyBZb3JrMREwDwYDVQQHDAhOZXcgWW9y + azEOMAwGA1UECgwFVmFwb3IxFDASBgNVBAsMC0VuZ2luZWVyaW5nMRYwFAYDVQQD + DA1WYXBvciBSb290IENBMSYwJAYJKoZIhvcNAQkBFhdhZG1pbkB2YXBvci5leGFt + cGxlLmNvbTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABHMhyEOaGoJi63JOVR1V + k1sLm3vjGEGncZgfWAOfID6Ei7e51Cw5hsknoEnAvMrfG7C3F20Qyuyp0oR5Vn1D + jqejUzBRMB0GA1UdDgQWBBQfnW9qHrIYYZneCPOSCkjp0y+PUDAfBgNVHSMEGDAW + gBQfnW9qHrIYYZneCPOSCkjp0y+PUDAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49 + BAMCA0gAMEUCIQDWgv4V5odzuwKGq5ZDfDLagDbyiaf9OvcsUyj4LhkEGgIgOH2h + 7lJI5vYpa5KTTVxVzcor7Zms4whkmmMNcMfBjKk= -----END CERTIFICATE----- """ ]) @@ -240,7 +245,8 @@ struct X5CTests { certificates: [leaf, intermediate], policy: { RFC5280Policy(validationTime: Date(timeIntervalSince1970: TimeInterval(1_681_312_846))) - }) + } + ) switch result { case .couldNotValidate(let failures): @@ -258,7 +264,8 @@ struct X5CTests { certificates: [leaf, intermediate], policy: { RFC5280Policy(validationTime: Date(timeIntervalSince1970: TimeInterval(2_280_946_846))) - }) + } + ) switch result { case .couldNotValidate: @@ -342,36 +349,29 @@ struct X5CTests { } } -let validToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCanpDQ0FUVUNGSDhFWFBJbDBRbDQwYzdKOEhkK2R6QWgwY3dVTUFvR0NDcUdTTTQ5QkFNQ01FNHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1RNHdEQVlEVlFRS0RBVldZWEJ2Y2pFYU1CZ0dBMVVFQXd3UmFXNTBaWEp0WldScFlYUmxMV05sY25Rd0hoY05Nak14TURJME1UTTBNalU1V2hjTk1qUXhNREl6TVRNME1qVTVXakJHTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhFakFRQmdOVkJBTU1DV3hsWVdZdFkyVnlkREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRU85VjZLQWhSR0l4QWx2Ukl6U3BtSVRmVVZiWHl5ZGMvZWdlbHpLLzZ3NDEySmc4RDJlSVRkOHVDRmxnVmh4WUlkR1pNN1hYUWhaNmhOZnE2S3JyUG93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUxJaXhudE93V3o4NlFHa0g0SGNnT09malBiczlBUVpXYm1HYkpKRjRWRWZBaUI4ZUVLRi9WQllvWVhRREQzVHpLNUlEMkdzbXplMzhpNk56ek9ndHRMam9nPT0iLCJNSUlCNXpDQ0FZeWdBd0lCQWdJVUw2ZnJzdHdldjdZTWtPWVNuUHVlSlQyR3g1UXdDZ1lJS29aSXpqMEVBd0l3UmpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01DbE52YldVdFUzUmhkR1V4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUkl3RUFZRFZRUUREQWx5YjI5MExXTmxjblF3SGhjTk1qTXhNREkwTVRNME1qTTFXaGNOTWpneE1ESXlNVE0wTWpNMVdqQk9NUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1UyOXRaUzFUZEdGMFpURU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEdqQVlCZ05WQkFNTUVXbHVkR1Z5YldWa2FXRjBaUzFqWlhKME1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWt6WGFFQTlDSVpyUkVmQ0Mra05tM0pxZDdmR0ZFT05Ia2p5TFJ4NS83SUFYeHB4TGZ0WWNoOTU1K1VRNVhHOHdUZ2tNQ0NaNG9LRjhNMXg3Zkw3cXU2TlFNRTR3REFZRFZSMFRCQVV3QXdFQi96QWRCZ05WSFE0RUZnUVVyRU53VW02VDBSbmUxTW9MY3lWQ2NhVTVvTTB3SHdZRFZSMGpCQmd3Rm9BVVkxM3oxRVhHbjlVQmZwZ3BmVTl0UWJ2TVJRZ3dDZ1lJS29aSXpqMEVBd0lEU1FBd1JnSWhBSW04bStLb0RFbktBdUgrZUZROGJWSDJkc3p2NlcveCtwNE9zZERzd0VrNUFpRUF5bWd1SmdxQUpZU3NDdzdYM0pDVVBNY29LdGFRRzZNamhRdThrWlpCQUNJPSIsIk1JSUI0VENDQVllZ0F3SUJBZ0lVRG9PZWZlZkNOcS9UR1dyaUlzWUh2ejBMcE5Jd0NnWUlLb1pJemowRUF3SXdSakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNsTnZiV1V0VTNSaGRHVXhEakFNQmdOVkJBb01CVlpoY0c5eU1SSXdFQVlEVlFRRERBbHliMjkwTFdObGNuUXdIaGNOTWpNeE1ESTBNVEl3T0RJM1doY05Nek14TURJeE1USXdPREkzV2pCR01Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLVTI5dFpTMVRkR0YwWlRFT01Bd0dBMVVFQ2d3RlZtRndiM0l4RWpBUUJnTlZCQU1NQ1hKdmIzUXRZMlZ5ZERCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk5wditIRzUyak9UMVcrcjFrMTNiSm8yazlEeVJ5RmJ5Y0JwUHNXUUtmdDlueHdFSHZ6RGoxaXZvTWZhanhsTCtuL0ZMQm5Pblk2M21GV216YW9adkgralV6QlJNQjBHQTFVZERnUVdCQlJqWGZQVVJjYWYxUUYrbUNsOVQyMUJ1OHhGQ0RBZkJnTlZIU01FR0RBV2dCUmpYZlBVUmNhZjFRRittQ2w5VDIxQnU4eEZDREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJR0lwWDlsbGlVMTM1VjgrTFk2L2NqQm1HcktLTmxZV0xMb1o2RGlhdXpkSkFpRUE5R1NBSUdoZk05a2JXbGtjak1zNmxBNHB3ZjRSZlVFRmVnaFlwWkticUZvPSJdfQ.eyJjb29sIjp0cnVlfQ.bqzLnIVtK4rU9eXhQnrWMpXPdWvxIcodDNI5BQsC-u_pAdaiO8ckbUs840c1WtWdGB7Zv7w7z7bwNWGAwM7WIQ +let missingLeafAndIntermediateToken = """ + eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlDaVRDQ0FpK2dBd0lCQWdJVWQ0eHlabk1VZ0FpSDE5UjhpZm51SjRcL0NnMVV3Q2dZSUtvWkl6ajBFQXdJd2daa3hDekFKQmdOVkJBWVRBbFZUTVJFd0R3WURWUVFJREFoT1pYY2dXVzl5YXpFUk1BOEdBMVVFQnd3SVRtVjNJRmx2Y21zeERqQU1CZ05WQkFvTUJWWmhjRzl5TVJRd0VnWURWUVFMREF0RmJtZHBibVZsY21sdVp6RVdNQlFHQTFVRUF3d05WbUZ3YjNJZ1VtOXZkQ0JEUVRFbU1DUUdDU3FHU0liM0RRRUpBUllYWVdSdGFXNUFkbUZ3YjNJdVpYaGhiWEJzWlM1amIyMHdIaGNOTWpReE1ESTRNVGN5T1RJeFdoY05NelF4TURJMk1UY3lPVEl4V2pDQm1URUxNQWtHQTFVRUJoTUNWVk14RVRBUEJnTlZCQWdNQ0U1bGR5QlpiM0pyTVJFd0R3WURWUVFIREFoT1pYY2dXVzl5YXpFT01Bd0dBMVVFQ2d3RlZtRndiM0l4RkRBU0JnTlZCQXNNQzBWdVoybHVaV1Z5YVc1bk1SWXdGQVlEVlFRRERBMVdZWEJ2Y2lCU2IyOTBJRU5CTVNZd0pBWUpLb1pJaHZjTkFRa0JGaGRoWkcxcGJrQjJZWEJ2Y2k1bGVHRnRjR3hsTG1OdmJUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJITWh5RU9hR29KaTYzSk9WUjFWazFzTG0zdmpHRUduY1pnZldBT2ZJRDZFaTdlNTFDdzVoc2tub0VuQXZNcmZHN0MzRjIwUXl1eXAwb1I1Vm4xRGpxZWpVekJSTUIwR0ExVWREZ1FXQkJRZm5XOXFIcklZWVpuZUNQT1NDa2pwMHkrUFVEQWZCZ05WSFNNRUdEQVdnQlFmblc5cUhySVlZWm5lQ1BPU0NranAweStQVURBUEJnTlZIUk1CQWY4RUJUQURBUUhcL01Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lRRFdndjRWNW9kenV3S0dxNVpEZkRMYWdEYnlpYWY5T3Zjc1V5ajRMaGtFR2dJZ09IMmg3bEpJNXZZcGE1S1RUVnhWemNvcjdabXM0d2hrbW1NTmNNZkJqS2s9Il19.eyJjb29sIjp0cnVlfQ.3p61wCJofz02WzEhxNihI0MxXMQ3MuKqNrFwfmTg7tpzbWIzcBTmM8mc5NkEcubGYUQ2Q59x8V5YH7KLhaIIGg """ - let missingIntermediateToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCanpDQ0FUVUNGSDhFWFBJbDBRbDQwYzdKOEhkK2R6QWgwY3dVTUFvR0NDcUdTTTQ5QkFNQ01FNHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1RNHdEQVlEVlFRS0RBVldZWEJ2Y2pFYU1CZ0dBMVVFQXd3UmFXNTBaWEp0WldScFlYUmxMV05sY25Rd0hoY05Nak14TURJME1UTTBNalU1V2hjTk1qUXhNREl6TVRNME1qVTVXakJHTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhFakFRQmdOVkJBTU1DV3hsWVdZdFkyVnlkREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRU85VjZLQWhSR0l4QWx2Ukl6U3BtSVRmVVZiWHl5ZGMvZWdlbHpLLzZ3NDEySmc4RDJlSVRkOHVDRmxnVmh4WUlkR1pNN1hYUWhaNmhOZnE2S3JyUG93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUxJaXhudE93V3o4NlFHa0g0SGNnT09malBiczlBUVpXYm1HYkpKRjRWRWZBaUI4ZUVLRi9WQllvWVhRREQzVHpLNUlEMkdzbXplMzhpNk56ek9ndHRMam9nPT0iLCJNSUlCNFRDQ0FZZWdBd0lCQWdJVURvT2VmZWZDTnEvVEdXcmlJc1lIdnowTHBOSXdDZ1lJS29aSXpqMEVBd0l3UmpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01DbE52YldVdFUzUmhkR1V4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUkl3RUFZRFZRUUREQWx5YjI5MExXTmxjblF3SGhjTk1qTXhNREkwTVRJd09ESTNXaGNOTXpNeE1ESXhNVEl3T0RJM1dqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1UyOXRaUzFUZEdGMFpURU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEVqQVFCZ05WQkFNTUNYSnZiM1F0WTJWeWREQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJOcHYrSEc1MmpPVDFXK3IxazEzYkpvMms5RHlSeUZieWNCcFBzV1FLZnQ5bnh3RUh2ekRqMWl2b01mYWp4bEwrbi9GTEJuT25ZNjNtRldtemFvWnZIK2pVekJSTUIwR0ExVWREZ1FXQkJSalhmUFVSY2FmMVFGK21DbDlUMjFCdTh4RkNEQWZCZ05WSFNNRUdEQVdnQlJqWGZQVVJjYWYxUUYrbUNsOVQyMUJ1OHhGQ0RBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUdJcFg5bGxpVTEzNVY4K0xZNi9jakJtR3JLS05sWVdMTG9aNkRpYXV6ZEpBaUVBOUdTQUlHaGZNOWtiV2xrY2pNczZsQTRwd2Y0UmZVRUZlZ2hZcFpLYnFGbz0iXX0.eyJjb29sIjp0cnVlfQ.mqHyjgbPc0vtcOtflV8TUTEeG7X7Wrb_gfsYicn8zKP3xmcTFn96V1-QQgOeyLSGPR3iMz11CELXIUuPoRRfbw - """ - -let missingRootToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCanpDQ0FUVUNGSDhFWFBJbDBRbDQwYzdKOEhkK2R6QWgwY3dVTUFvR0NDcUdTTTQ5QkFNQ01FNHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1RNHdEQVlEVlFRS0RBVldZWEJ2Y2pFYU1CZ0dBMVVFQXd3UmFXNTBaWEp0WldScFlYUmxMV05sY25Rd0hoY05Nak14TURJME1UTTBNalU1V2hjTk1qUXhNREl6TVRNME1qVTVXakJHTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhFakFRQmdOVkJBTU1DV3hsWVdZdFkyVnlkREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRU85VjZLQWhSR0l4QWx2Ukl6U3BtSVRmVVZiWHl5ZGMvZWdlbHpLLzZ3NDEySmc4RDJlSVRkOHVDRmxnVmh4WUlkR1pNN1hYUWhaNmhOZnE2S3JyUG93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUxJaXhudE93V3o4NlFHa0g0SGNnT09malBiczlBUVpXYm1HYkpKRjRWRWZBaUI4ZUVLRi9WQllvWVhRREQzVHpLNUlEMkdzbXplMzhpNk56ek9ndHRMam9nPT0iLCJNSUlCNXpDQ0FZeWdBd0lCQWdJVUw2ZnJzdHdldjdZTWtPWVNuUHVlSlQyR3g1UXdDZ1lJS29aSXpqMEVBd0l3UmpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01DbE52YldVdFUzUmhkR1V4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUkl3RUFZRFZRUUREQWx5YjI5MExXTmxjblF3SGhjTk1qTXhNREkwTVRNME1qTTFXaGNOTWpneE1ESXlNVE0wTWpNMVdqQk9NUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1UyOXRaUzFUZEdGMFpURU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEdqQVlCZ05WQkFNTUVXbHVkR1Z5YldWa2FXRjBaUzFqWlhKME1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWt6WGFFQTlDSVpyUkVmQ0Mra05tM0pxZDdmR0ZFT05Ia2p5TFJ4NS83SUFYeHB4TGZ0WWNoOTU1K1VRNVhHOHdUZ2tNQ0NaNG9LRjhNMXg3Zkw3cXU2TlFNRTR3REFZRFZSMFRCQVV3QXdFQi96QWRCZ05WSFE0RUZnUVVyRU53VW02VDBSbmUxTW9MY3lWQ2NhVTVvTTB3SHdZRFZSMGpCQmd3Rm9BVVkxM3oxRVhHbjlVQmZwZ3BmVTl0UWJ2TVJRZ3dDZ1lJS29aSXpqMEVBd0lEU1FBd1JnSWhBSW04bStLb0RFbktBdUgrZUZROGJWSDJkc3p2NlcveCtwNE9zZERzd0VrNUFpRUF5bWd1SmdxQUpZU3NDdzdYM0pDVVBNY29LdGFRRzZNamhRdThrWlpCQUNJPSJdfQ.eyJjb29sIjp0cnVlfQ.d7AmnXaDUu1eU5ufBQ5ruailzBbJVud3RtqAcDYApvP6fPC1SttYmDKbsjUHx4q-NEt14n6xZ0Np_zy-OtM6lw + eyJ4NWMiOlsiTUlJQ2ZEQ0NBaU9nQXdJQkFnSVVDSEswTGNjc2d4T1Buc0pOQXZsTXlKc2NQWGt3Q2dZSUtvWkl6ajBFQXdJd2dhRXhDekFKQmdOVkJBWVRBbFZUTVJFd0R3WURWUVFJREFoT1pYY2dXVzl5YXpFUk1BOEdBMVVFQnd3SVRtVjNJRmx2Y21zeERqQU1CZ05WQkFvTUJWWmhjRzl5TVJRd0VnWURWUVFMREF0RmJtZHBibVZsY21sdVp6RWVNQndHQTFVRUF3d1ZWbUZ3YjNJZ1NXNTBaWEp0WldScFlYUmxJRU5CTVNZd0pBWUpLb1pJaHZjTkFRa0JGaGRoWkcxcGJrQjJZWEJ2Y2k1bGVHRnRjR3hsTG1OdmJUQWVGdzB5TkRFd01qZ3hOekk1TWpKYUZ3MHlOVEV3TWpneE56STVNakphTUlHV01Rc3dDUVlEVlFRR0V3SlZVekVSTUE4R0ExVUVDQXdJVG1WM0lGbHZjbXN4RVRBUEJnTlZCQWNNQ0U1bGR5QlpiM0pyTVE0d0RBWURWUVFLREFWV1lYQnZjakVVTUJJR0ExVUVDd3dMUlc1bmFXNWxaWEpwYm1jeEV6QVJCZ05WQkFNTUNsWmhjRzl5SUV4bFlXWXhKakFrQmdrcWhraUc5dzBCQ1FFV0YyRmtiV2x1UUhaaGNHOXlMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFV3JWZ0hCays4TEwyVW5QUGUrWHdrbVBkU040a1pBd0N6VkRmZ2pSTzVGbzdjRCtkQVVLUlFBMkJpb0pWczJ5dE44SmJQNWhvcVZzOXNSdE1LVFI1WHFOQ01FQXdIUVlEVlIwT0JCWUVGRlRUTVpZWE1qamcyUEQ5VjdCdTc4STd2R2JcL01COEdBMVVkSXdRWU1CYUFGREdJRDIwdDNkRDRZMXZ6UytkaFM0clBmak9XTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUdkUllLZm04TTZyZjFCdnZldEsyWFltWmlrOGNLQ3ozRUNsb3dGMzA5VGxBaUFlRzFsdXVVRzh6OW5IeUIzZllmZCs0SnFUUTRLbkxITUE4WVJzdkl3eGpnPT0iLCJNSUlDaVRDQ0FpK2dBd0lCQWdJVWQ0eHlabk1VZ0FpSDE5UjhpZm51SjRcL0NnMVV3Q2dZSUtvWkl6ajBFQXdJd2daa3hDekFKQmdOVkJBWVRBbFZUTVJFd0R3WURWUVFJREFoT1pYY2dXVzl5YXpFUk1BOEdBMVVFQnd3SVRtVjNJRmx2Y21zeERqQU1CZ05WQkFvTUJWWmhjRzl5TVJRd0VnWURWUVFMREF0RmJtZHBibVZsY21sdVp6RVdNQlFHQTFVRUF3d05WbUZ3YjNJZ1VtOXZkQ0JEUVRFbU1DUUdDU3FHU0liM0RRRUpBUllYWVdSdGFXNUFkbUZ3YjNJdVpYaGhiWEJzWlM1amIyMHdIaGNOTWpReE1ESTRNVGN5T1RJeFdoY05NelF4TURJMk1UY3lPVEl4V2pDQm1URUxNQWtHQTFVRUJoTUNWVk14RVRBUEJnTlZCQWdNQ0U1bGR5QlpiM0pyTVJFd0R3WURWUVFIREFoT1pYY2dXVzl5YXpFT01Bd0dBMVVFQ2d3RlZtRndiM0l4RkRBU0JnTlZCQXNNQzBWdVoybHVaV1Z5YVc1bk1SWXdGQVlEVlFRRERBMVdZWEJ2Y2lCU2IyOTBJRU5CTVNZd0pBWUpLb1pJaHZjTkFRa0JGaGRoWkcxcGJrQjJZWEJ2Y2k1bGVHRnRjR3hsTG1OdmJUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJITWh5RU9hR29KaTYzSk9WUjFWazFzTG0zdmpHRUduY1pnZldBT2ZJRDZFaTdlNTFDdzVoc2tub0VuQXZNcmZHN0MzRjIwUXl1eXAwb1I1Vm4xRGpxZWpVekJSTUIwR0ExVWREZ1FXQkJRZm5XOXFIcklZWVpuZUNQT1NDa2pwMHkrUFVEQWZCZ05WSFNNRUdEQVdnQlFmblc5cUhySVlZWm5lQ1BPU0NranAweStQVURBUEJnTlZIUk1CQWY4RUJUQURBUUhcL01Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lRRFdndjRWNW9kenV3S0dxNVpEZkRMYWdEYnlpYWY5T3Zjc1V5ajRMaGtFR2dJZ09IMmg3bEpJNXZZcGE1S1RUVnhWemNvcjdabXM0d2hrbW1NTmNNZkJqS2s9Il0sInR5cCI6IkpXVCIsImFsZyI6IkVTMjU2In0.eyJjb29sIjp0cnVlfQ.sXEhgQHMMweIGECmI6v6Np-WbHHYe4cEf_TpyFxOxGDrI3qwvmopZNEGQQxProYYEZlHsY9-7VaXAxrW_svKJw """ - -let missingLeafToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCNXpDQ0FZeWdBd0lCQWdJVUw2ZnJzdHdldjdZTWtPWVNuUHVlSlQyR3g1UXdDZ1lJS29aSXpqMEVBd0l3UmpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01DbE52YldVdFUzUmhkR1V4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUkl3RUFZRFZRUUREQWx5YjI5MExXTmxjblF3SGhjTk1qTXhNREkwTVRNME1qTTFXaGNOTWpneE1ESXlNVE0wTWpNMVdqQk9NUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1UyOXRaUzFUZEdGMFpURU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEdqQVlCZ05WQkFNTUVXbHVkR1Z5YldWa2FXRjBaUzFqWlhKME1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWt6WGFFQTlDSVpyUkVmQ0Mra05tM0pxZDdmR0ZFT05Ia2p5TFJ4NS83SUFYeHB4TGZ0WWNoOTU1K1VRNVhHOHdUZ2tNQ0NaNG9LRjhNMXg3Zkw3cXU2TlFNRTR3REFZRFZSMFRCQVV3QXdFQi96QWRCZ05WSFE0RUZnUVVyRU53VW02VDBSbmUxTW9MY3lWQ2NhVTVvTTB3SHdZRFZSMGpCQmd3Rm9BVVkxM3oxRVhHbjlVQmZwZ3BmVTl0UWJ2TVJRZ3dDZ1lJS29aSXpqMEVBd0lEU1FBd1JnSWhBSW04bStLb0RFbktBdUgrZUZROGJWSDJkc3p2NlcveCtwNE9zZERzd0VrNUFpRUF5bWd1SmdxQUpZU3NDdzdYM0pDVVBNY29LdGFRRzZNamhRdThrWlpCQUNJPSIsIk1JSUI0VENDQVllZ0F3SUJBZ0lVRG9PZWZlZkNOcS9UR1dyaUlzWUh2ejBMcE5Jd0NnWUlLb1pJemowRUF3SXdSakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNsTnZiV1V0VTNSaGRHVXhEakFNQmdOVkJBb01CVlpoY0c5eU1SSXdFQVlEVlFRRERBbHliMjkwTFdObGNuUXdIaGNOTWpNeE1ESTBNVEl3T0RJM1doY05Nek14TURJeE1USXdPREkzV2pCR01Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLVTI5dFpTMVRkR0YwWlRFT01Bd0dBMVVFQ2d3RlZtRndiM0l4RWpBUUJnTlZCQU1NQ1hKdmIzUXRZMlZ5ZERCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk5wditIRzUyak9UMVcrcjFrMTNiSm8yazlEeVJ5RmJ5Y0JwUHNXUUtmdDlueHdFSHZ6RGoxaXZvTWZhanhsTCtuL0ZMQm5Pblk2M21GV216YW9adkgralV6QlJNQjBHQTFVZERnUVdCQlJqWGZQVVJjYWYxUUYrbUNsOVQyMUJ1OHhGQ0RBZkJnTlZIU01FR0RBV2dCUmpYZlBVUmNhZjFRRittQ2w5VDIxQnU4eEZDREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJR0lwWDlsbGlVMTM1VjgrTFk2L2NqQm1HcktLTmxZV0xMb1o2RGlhdXpkSkFpRUE5R1NBSUdoZk05a2JXbGtjak1zNmxBNHB3ZjRSZlVFRmVnaFlwWkticUZvPSJdfQ.eyJjb29sIjp0cnVlfQ.nky-7PNeo6TX1Vtr3ci5pAERNo20Dzcd41LBry3XzSDyFrz1I14836c8skKGT4M7GWn5rB_w0GAA3inZPusiAA +let expiredLeafToken = """ + eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDaFRDQ0FpdWdBd0lCQWdJVUNISzBMY2NzZ3hPUG5zSk5BdmxNeUpzY1BYb3dDZ1lJS29aSXpqMEVBd0l3Z2FFeEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUlEQWhPWlhjZ1dXOXlhekVSTUE4R0ExVUVCd3dJVG1WM0lGbHZjbXN4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFZU1Cd0dBMVVFQXd3VlZtRndiM0lnU1c1MFpYSnRaV1JwWVhSbElFTkJNU1l3SkFZSktvWklodmNOQVFrQkZoZGhaRzFwYmtCMllYQnZjaTVsZUdGdGNHeGxMbU52YlRBZUZ3MHlOREV3TWpneE56STVNakphRncweU5ERXdNamN4TnpJNU1qSmFNSUdlTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNBd0lUbVYzSUZsdmNtc3hFVEFQQmdOVkJBY01DRTVsZHlCWmIzSnJNUTR3REFZRFZRUUtEQVZXWVhCdmNqRVVNQklHQTFVRUN3d0xSVzVuYVc1bFpYSnBibWN4R3pBWkJnTlZCQU1NRWxaaGNHOXlJRVY0Y0dseVpXUWdUR1ZoWmpFbU1DUUdDU3FHU0liM0RRRUpBUllYWVdSdGFXNUFkbUZ3YjNJdVpYaGhiWEJzWlM1amIyMHdXVEFUQmdjcWhrak9QUUlCQmdncWhrak9QUU1CQndOQ0FBUThBbkQ0dmlMRnpRdktHTzRrYk5vQWtpM3pnTHNNN0FxOHdRaTNYTjhuNWxUenQwcHRqQ2VsaEhnblBYYkU0VEJETnY3T2hLamo1V1d6MzBRdzgxeGxvMEl3UURBZEJnTlZIUTRFRmdRVURyWWROdlhqVEVuSm9PMEcwaDN3K09sS0dnTXdId1lEVlIwakJCZ3dGb0FVTVlnUGJTM2QwUGhqV1wvTkw1MkZMaXM5K001WXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBS2N0ZFN1T20wcmVRRHBKdDlUVWtmTHc2OFwvcTVkWW5XZjlKZTFIWmhkWEFBaUJVcSs0OXJmaGllbTFlOGwwdnpieFQzNUVNSzB2aGEwOWxYTGphUjRlWjZRPT0iLCJNSUlDampDQ0FqU2dBd0lCQWdJVVREQTV0NVJvQjA1aWZOVnE3UlhoT1FERHc4c3dDZ1lJS29aSXpqMEVBd0l3Z1preEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUlEQWhPWlhjZ1dXOXlhekVSTUE4R0ExVUVCd3dJVG1WM0lGbHZjbXN4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFV01CUUdBMVVFQXd3TlZtRndiM0lnVW05dmRDQkRRVEVtTUNRR0NTcUdTSWIzRFFFSkFSWVhZV1J0YVc1QWRtRndiM0l1WlhoaGJYQnNaUzVqYjIwd0hoY05NalF4TURJNE1UY3lPVEl5V2hjTk1qa3hNREkzTVRjeU9USXlXakNCb1RFTE1Ba0dBMVVFQmhNQ1ZWTXhFVEFQQmdOVkJBZ01DRTVsZHlCWmIzSnJNUkV3RHdZRFZRUUhEQWhPWlhjZ1dXOXlhekVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhGREFTQmdOVkJBc01DMFZ1WjJsdVpXVnlhVzVuTVI0d0hBWURWUVFEREJWV1lYQnZjaUJKYm5SbGNtMWxaR2xoZEdVZ1EwRXhKakFrQmdrcWhraUc5dzBCQ1FFV0YyRmtiV2x1UUhaaGNHOXlMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcmVuSHJZVEtqalFFY0FmSXdiWktkWlwvSkxUNzQzUlNsWDA4ZDNTTGJtXC92SWFYQ1wvXC9tVEVaK2hablQ0TE0wNWh5c2VmXC9HZTVmN0wxNkV2SVhEeTNsS05RTUU0d0RBWURWUjBUQkFVd0F3RUJcL3pBZEJnTlZIUTRFRmdRVU1ZZ1BiUzNkMFBoaldcL05MNTJGTGlzOStNNVl3SHdZRFZSMGpCQmd3Rm9BVUg1MXZhaDZ5R0dHWjNnanprZ3BJNmRNdmoxQXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTnUxdmxIdlY3aERVSkQyd0tlczBObzRqVFR1ZmFYZlVZbThQR3JZdFR0QkFpQVlSaFl4WnlqQ3lBU1wvcFpaUDhxNkhja3E0ZU9cL0RjTGljSWRDUG1FRVFldz09IiwiTUlJQ2lUQ0NBaStnQXdJQkFnSVVkNHh5Wm5NVWdBaUgxOVI4aWZudUo0XC9DZzFVd0NnWUlLb1pJemowRUF3SXdnWmt4Q3pBSkJnTlZCQVlUQWxWVE1SRXdEd1lEVlFRSURBaE9aWGNnV1c5eWF6RVJNQThHQTFVRUJ3d0lUbVYzSUZsdmNtc3hEakFNQmdOVkJBb01CVlpoY0c5eU1SUXdFZ1lEVlFRTERBdEZibWRwYm1WbGNtbHVaekVXTUJRR0ExVUVBd3dOVm1Gd2IzSWdVbTl2ZENCRFFURW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBZG1Gd2IzSXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1qUXhNREk0TVRjeU9USXhXaGNOTXpReE1ESTJNVGN5T1RJeFdqQ0JtVEVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFnTUNFNWxkeUJaYjNKck1SRXdEd1lEVlFRSERBaE9aWGNnV1c5eWF6RU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEZEQVNCZ05WQkFzTUMwVnVaMmx1WldWeWFXNW5NUll3RkFZRFZRUUREQTFXWVhCdmNpQlNiMjkwSUVOQk1TWXdKQVlKS29aSWh2Y05BUWtCRmhkaFpHMXBia0IyWVhCdmNpNWxlR0Z0Y0d4bExtTnZiVEJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSE1oeUVPYUdvSmk2M0pPVlIxVmsxc0xtM3ZqR0VHbmNaZ2ZXQU9mSUQ2RWk3ZTUxQ3c1aHNrbm9FbkF2TXJmRzdDM0YyMFF5dXlwMG9SNVZuMURqcWVqVXpCUk1CMEdBMVVkRGdRV0JCUWZuVzlxSHJJWVlabmVDUE9TQ2tqcDB5K1BVREFmQmdOVkhTTUVHREFXZ0JRZm5XOXFIcklZWVpuZUNQT1NDa2pwMHkrUFVEQVBCZ05WSFJNQkFmOEVCVEFEQVFIXC9NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURXZ3Y0VjVvZHp1d0tHcTVaRGZETGFnRGJ5aWFmOU92Y3NVeWo0TGhrRUdnSWdPSDJoN2xKSTV2WXBhNUtUVFZ4Vnpjb3I3Wm1zNHdoa21tTU5jTWZCaktrPSJdfQ.eyJjb29sIjp0cnVlfQ.MUzu9FvvtrcRjWqPIyeziZIBA0Q6YI_UTOl1RYAM2EanjbCTIr5-VWvOATYEVq0Je_ZWtj71s_2cZwvzj2GW4w """ - -let missingLeafAndIntermediateToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCNFRDQ0FZZWdBd0lCQWdJVURvT2VmZWZDTnEvVEdXcmlJc1lIdnowTHBOSXdDZ1lJS29aSXpqMEVBd0l3UmpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01DbE52YldVdFUzUmhkR1V4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUkl3RUFZRFZRUUREQWx5YjI5MExXTmxjblF3SGhjTk1qTXhNREkwTVRJd09ESTNXaGNOTXpNeE1ESXhNVEl3T0RJM1dqQkdNUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1UyOXRaUzFUZEdGMFpURU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEVqQVFCZ05WQkFNTUNYSnZiM1F0WTJWeWREQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJOcHYrSEc1MmpPVDFXK3IxazEzYkpvMms5RHlSeUZieWNCcFBzV1FLZnQ5bnh3RUh2ekRqMWl2b01mYWp4bEwrbi9GTEJuT25ZNjNtRldtemFvWnZIK2pVekJSTUIwR0ExVWREZ1FXQkJSalhmUFVSY2FmMVFGK21DbDlUMjFCdTh4RkNEQWZCZ05WSFNNRUdEQVdnQlJqWGZQVVJjYWYxUUYrbUNsOVQyMUJ1OHhGQ0RBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUdJcFg5bGxpVTEzNVY4K0xZNi9jakJtR3JLS05sWVdMTG9aNkRpYXV6ZEpBaUVBOUdTQUlHaGZNOWtiV2xrY2pNczZsQTRwd2Y0UmZVRUZlZ2hZcFpLYnFGbz0iXX0.eyJjb29sIjp0cnVlfQ.9Dy7P8zp3GSXxw7QQgvhS0lLaLl0yIt1Mmo2-Nt7EJIr7lvVMSvlDHzDk7PmAnocm7MBWGKdfwo7b0GIdblgLA +let validButNotCoolToken = """ + eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlDZkRDQ0FpT2dBd0lCQWdJVUNISzBMY2NzZ3hPUG5zSk5BdmxNeUpzY1BYa3dDZ1lJS29aSXpqMEVBd0l3Z2FFeEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUlEQWhPWlhjZ1dXOXlhekVSTUE4R0ExVUVCd3dJVG1WM0lGbHZjbXN4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFZU1Cd0dBMVVFQXd3VlZtRndiM0lnU1c1MFpYSnRaV1JwWVhSbElFTkJNU1l3SkFZSktvWklodmNOQVFrQkZoZGhaRzFwYmtCMllYQnZjaTVsZUdGdGNHeGxMbU52YlRBZUZ3MHlOREV3TWpneE56STVNakphRncweU5URXdNamd4TnpJNU1qSmFNSUdXTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNBd0lUbVYzSUZsdmNtc3hFVEFQQmdOVkJBY01DRTVsZHlCWmIzSnJNUTR3REFZRFZRUUtEQVZXWVhCdmNqRVVNQklHQTFVRUN3d0xSVzVuYVc1bFpYSnBibWN4RXpBUkJnTlZCQU1NQ2xaaGNHOXlJRXhsWVdZeEpqQWtCZ2txaGtpRzl3MEJDUUVXRjJGa2JXbHVRSFpoY0c5eUxtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVXclZnSEJrKzhMTDJVblBQZStYd2ttUGRTTjRrWkF3Q3pWRGZnalJPNUZvN2NEK2RBVUtSUUEyQmlvSlZzMnl0TjhKYlA1aG9xVnM5c1J0TUtUUjVYcU5DTUVBd0hRWURWUjBPQkJZRUZGVFRNWllYTWpqZzJQRDlWN0J1NzhJN3ZHYlwvTUI4R0ExVWRJd1FZTUJhQUZER0lEMjB0M2RENFkxdnpTK2RoUzRyUGZqT1dNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJR2RSWUtmbThNNnJmMUJ2dmV0SzJYWW1aaWs4Y0tDejNFQ2xvd0YzMDlUbEFpQWVHMWx1dVVHOHo5bkh5QjNmWWZkKzRKcVRRNEtuTEhNQThZUnN2SXd4amc9PSIsIk1JSUNqakNDQWpTZ0F3SUJBZ0lVVERBNXQ1Um9CMDVpZk5WcTdSWGhPUUREdzhzd0NnWUlLb1pJemowRUF3SXdnWmt4Q3pBSkJnTlZCQVlUQWxWVE1SRXdEd1lEVlFRSURBaE9aWGNnV1c5eWF6RVJNQThHQTFVRUJ3d0lUbVYzSUZsdmNtc3hEakFNQmdOVkJBb01CVlpoY0c5eU1SUXdFZ1lEVlFRTERBdEZibWRwYm1WbGNtbHVaekVXTUJRR0ExVUVBd3dOVm1Gd2IzSWdVbTl2ZENCRFFURW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBZG1Gd2IzSXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1qUXhNREk0TVRjeU9USXlXaGNOTWpreE1ESTNNVGN5T1RJeVdqQ0JvVEVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFnTUNFNWxkeUJaYjNKck1SRXdEd1lEVlFRSERBaE9aWGNnV1c5eWF6RU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEZEQVNCZ05WQkFzTUMwVnVaMmx1WldWeWFXNW5NUjR3SEFZRFZRUUREQlZXWVhCdmNpQkpiblJsY20xbFpHbGhkR1VnUTBFeEpqQWtCZ2txaGtpRzl3MEJDUUVXRjJGa2JXbHVRSFpoY0c5eUxtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVyZW5IcllUS2pqUUVjQWZJd2JaS2RaXC9KTFQ3NDNSU2xYMDhkM1NMYm1cL3ZJYVhDXC9cL21URVoraFpuVDRMTTA1aHlzZWZcL0dlNWY3TDE2RXZJWER5M2xLTlFNRTR3REFZRFZSMFRCQVV3QXdFQlwvekFkQmdOVkhRNEVGZ1FVTVlnUGJTM2QwUGhqV1wvTkw1MkZMaXM5K001WXdId1lEVlIwakJCZ3dGb0FVSDUxdmFoNnlHR0daM2dqemtncEk2ZE12ajFBd0NnWUlLb1pJemowRUF3SURTQUF3UlFJaEFOdTF2bEh2VjdoRFVKRDJ3S2VzME5vNGpUVHVmYVhmVVltOFBHcll0VHRCQWlBWVJoWXhaeWpDeUFTXC9wWlpQOHE2SGNrcTRlT1wvRGNMaWNJZENQbUVFUWV3PT0iLCJNSUlDaVRDQ0FpK2dBd0lCQWdJVWQ0eHlabk1VZ0FpSDE5UjhpZm51SjRcL0NnMVV3Q2dZSUtvWkl6ajBFQXdJd2daa3hDekFKQmdOVkJBWVRBbFZUTVJFd0R3WURWUVFJREFoT1pYY2dXVzl5YXpFUk1BOEdBMVVFQnd3SVRtVjNJRmx2Y21zeERqQU1CZ05WQkFvTUJWWmhjRzl5TVJRd0VnWURWUVFMREF0RmJtZHBibVZsY21sdVp6RVdNQlFHQTFVRUF3d05WbUZ3YjNJZ1VtOXZkQ0JEUVRFbU1DUUdDU3FHU0liM0RRRUpBUllYWVdSdGFXNUFkbUZ3YjNJdVpYaGhiWEJzWlM1amIyMHdIaGNOTWpReE1ESTRNVGN5T1RJeFdoY05NelF4TURJMk1UY3lPVEl4V2pDQm1URUxNQWtHQTFVRUJoTUNWVk14RVRBUEJnTlZCQWdNQ0U1bGR5QlpiM0pyTVJFd0R3WURWUVFIREFoT1pYY2dXVzl5YXpFT01Bd0dBMVVFQ2d3RlZtRndiM0l4RkRBU0JnTlZCQXNNQzBWdVoybHVaV1Z5YVc1bk1SWXdGQVlEVlFRRERBMVdZWEJ2Y2lCU2IyOTBJRU5CTVNZd0pBWUpLb1pJaHZjTkFRa0JGaGRoWkcxcGJrQjJZWEJ2Y2k1bGVHRnRjR3hsTG1OdmJUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJITWh5RU9hR29KaTYzSk9WUjFWazFzTG0zdmpHRUduY1pnZldBT2ZJRDZFaTdlNTFDdzVoc2tub0VuQXZNcmZHN0MzRjIwUXl1eXAwb1I1Vm4xRGpxZWpVekJSTUIwR0ExVWREZ1FXQkJRZm5XOXFIcklZWVpuZUNQT1NDa2pwMHkrUFVEQWZCZ05WSFNNRUdEQVdnQlFmblc5cUhySVlZWm5lQ1BPU0NranAweStQVURBUEJnTlZIUk1CQWY4RUJUQURBUUhcL01Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lRRFdndjRWNW9kenV3S0dxNVpEZkRMYWdEYnlpYWY5T3Zjc1V5ajRMaGtFR2dJZ09IMmg3bEpJNXZZcGE1S1RUVnhWemNvcjdabXM0d2hrbW1NTmNNZkJqS2s9Il19.eyJjb29sIjpmYWxzZX0.DEljwQFgwQv39MVUxldFIf1xb1OkiiudjhwW2nZnOL3JMihAKsbWwXcy6a-BNoZOOh95Le3UVuNd64232oqnvg """ - let missingIntermediateAndRootToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCanpDQ0FUVUNGSDhFWFBJbDBRbDQwYzdKOEhkK2R6QWgwY3dVTUFvR0NDcUdTTTQ5QkFNQ01FNHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1RNHdEQVlEVlFRS0RBVldZWEJ2Y2pFYU1CZ0dBMVVFQXd3UmFXNTBaWEp0WldScFlYUmxMV05sY25Rd0hoY05Nak14TURJME1UTTBNalU1V2hjTk1qUXhNREl6TVRNME1qVTVXakJHTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhFakFRQmdOVkJBTU1DV3hsWVdZdFkyVnlkREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRU85VjZLQWhSR0l4QWx2Ukl6U3BtSVRmVVZiWHl5ZGMvZWdlbHpLLzZ3NDEySmc4RDJlSVRkOHVDRmxnVmh4WUlkR1pNN1hYUWhaNmhOZnE2S3JyUG93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUxJaXhudE93V3o4NlFHa0g0SGNnT09malBiczlBUVpXYm1HYkpKRjRWRWZBaUI4ZUVLRi9WQllvWVhRREQzVHpLNUlEMkdzbXplMzhpNk56ek9ndHRMam9nPT0iXX0.eyJjb29sIjp0cnVlfQ.kYaJBFl8CVD-wZwaPd_G3oIjCyawHW-8nAQcpP3gzM1AMMm0V7w83cKczNXCWvqnGd-jE8xLAcBpOJ6ZINqH2Q + eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlDZkRDQ0FpT2dBd0lCQWdJVUNISzBMY2NzZ3hPUG5zSk5BdmxNeUpzY1BYa3dDZ1lJS29aSXpqMEVBd0l3Z2FFeEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUlEQWhPWlhjZ1dXOXlhekVSTUE4R0ExVUVCd3dJVG1WM0lGbHZjbXN4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFZU1Cd0dBMVVFQXd3VlZtRndiM0lnU1c1MFpYSnRaV1JwWVhSbElFTkJNU1l3SkFZSktvWklodmNOQVFrQkZoZGhaRzFwYmtCMllYQnZjaTVsZUdGdGNHeGxMbU52YlRBZUZ3MHlOREV3TWpneE56STVNakphRncweU5URXdNamd4TnpJNU1qSmFNSUdXTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNBd0lUbVYzSUZsdmNtc3hFVEFQQmdOVkJBY01DRTVsZHlCWmIzSnJNUTR3REFZRFZRUUtEQVZXWVhCdmNqRVVNQklHQTFVRUN3d0xSVzVuYVc1bFpYSnBibWN4RXpBUkJnTlZCQU1NQ2xaaGNHOXlJRXhsWVdZeEpqQWtCZ2txaGtpRzl3MEJDUUVXRjJGa2JXbHVRSFpoY0c5eUxtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVXclZnSEJrKzhMTDJVblBQZStYd2ttUGRTTjRrWkF3Q3pWRGZnalJPNUZvN2NEK2RBVUtSUUEyQmlvSlZzMnl0TjhKYlA1aG9xVnM5c1J0TUtUUjVYcU5DTUVBd0hRWURWUjBPQkJZRUZGVFRNWllYTWpqZzJQRDlWN0J1NzhJN3ZHYlwvTUI4R0ExVWRJd1FZTUJhQUZER0lEMjB0M2RENFkxdnpTK2RoUzRyUGZqT1dNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJR2RSWUtmbThNNnJmMUJ2dmV0SzJYWW1aaWs4Y0tDejNFQ2xvd0YzMDlUbEFpQWVHMWx1dVVHOHo5bkh5QjNmWWZkKzRKcVRRNEtuTEhNQThZUnN2SXd4amc9PSJdfQ.eyJjb29sIjp0cnVlfQ.Q5B2BExQAxvBjK8S2IWMNahMPVYmHZbg1FI-8hP1y8kzwH7p2I_OUEANvjiH3elkY3g3TXhRxflLr-oYr7Bf3Q """ - -let expiredLeafToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCbHpDQ0FUMENGQmdtUFlSaE1nY0VQbnhkOE8xSjlyU09ML2psTUFvR0NDcUdTTTQ5QkFNQ01FNHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1RNHdEQVlEVlFRS0RBVldZWEJ2Y2pFYU1CZ0dBMVVFQXd3UmFXNTBaWEp0WldScFlYUmxMV05sY25Rd0hoY05Nak14TURJME1UTTFOVE0zV2hjTk1qTXhNREl6TVRNMU5UTTNXakJPTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhHakFZQmdOVkJBTU1FV1Y0Y0dseVpXUXRiR1ZoWmkxalpYSjBNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUV5V1o0b3J1VEpjTzIzakJoMlZDZys0KzI2d1QrYXNwaDhxbUVYakpuZVlwMW9LQlFmYjc2RENqN1lUWlgzeEk1Q2JDVUhzbndOT1B3ZllkOUZqRjdZVEFLQmdncWhrak9QUVFEQWdOSUFEQkZBaUVBdjUzV2V5RHFUSnczTFRGdGFiS05qanFaOEZOY1YvRUNoeGU3cHdaenlmOENJRUlsN20xRnZmQ3FFR1lMcDdsZ0RnckRXY1pMKy9XZ1BVdjhpck5hSVpCUiIsIk1JSUI1ekNDQVl5Z0F3SUJBZ0lVTDZmcnN0d2V2N1lNa09ZU25QdWVKVDJHeDVRd0NnWUlLb1pJemowRUF3SXdSakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNsTnZiV1V0VTNSaGRHVXhEakFNQmdOVkJBb01CVlpoY0c5eU1SSXdFQVlEVlFRRERBbHliMjkwTFdObGNuUXdIaGNOTWpNeE1ESTBNVE0wTWpNMVdoY05Namd4TURJeU1UTTBNak0xV2pCT01Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLVTI5dFpTMVRkR0YwWlRFT01Bd0dBMVVFQ2d3RlZtRndiM0l4R2pBWUJnTlZCQU1NRVdsdWRHVnliV1ZrYVdGMFpTMWpaWEowTUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFa3pYYUVBOUNJWnJSRWZDQytrTm0zSnFkN2ZHRkVPTkhranlMUng1LzdJQVh4cHhMZnRZY2g5NTUrVVE1WEc4d1Rna01DQ1o0b0tGOE0xeDdmTDdxdTZOUU1FNHdEQVlEVlIwVEJBVXdBd0VCL3pBZEJnTlZIUTRFRmdRVXJFTndVbTZUMFJuZTFNb0xjeVZDY2FVNW9NMHdId1lEVlIwakJCZ3dGb0FVWTEzejFFWEduOVVCZnBncGZVOXRRYnZNUlFnd0NnWUlLb1pJemowRUF3SURTUUF3UmdJaEFJbThtK0tvREVuS0F1SCtlRlE4YlZIMmRzenY2Vy94K3A0T3NkRHN3RWs1QWlFQXltZ3VKZ3FBSllTc0N3N1gzSkNVUE1jb0t0YVFHNk1qaFF1OGtaWkJBQ0k9IiwiTUlJQjRUQ0NBWWVnQXdJQkFnSVVEb09lZmVmQ05xL1RHV3JpSXNZSHZ6MExwTkl3Q2dZSUtvWkl6ajBFQXdJd1JqRUxNQWtHQTFVRUJoTUNWVk14RXpBUkJnTlZCQWdNQ2xOdmJXVXRVM1JoZEdVeERqQU1CZ05WQkFvTUJWWmhjRzl5TVJJd0VBWURWUVFEREFseWIyOTBMV05sY25Rd0hoY05Nak14TURJME1USXdPREkzV2hjTk16TXhNREl4TVRJd09ESTNXakJHTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhFakFRQmdOVkJBTU1DWEp2YjNRdFkyVnlkREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCTnB2K0hHNTJqT1QxVytyMWsxM2JKbzJrOUR5UnlGYnljQnBQc1dRS2Z0OW54d0VIdnpEajFpdm9NZmFqeGxMK24vRkxCbk9uWTYzbUZXbXphb1p2SCtqVXpCUk1CMEdBMVVkRGdRV0JCUmpYZlBVUmNhZjFRRittQ2w5VDIxQnU4eEZDREFmQmdOVkhTTUVHREFXZ0JSalhmUFVSY2FmMVFGK21DbDlUMjFCdTh4RkNEQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lHSXBYOWxsaVUxMzVWOCtMWTYvY2pCbUdyS0tObFlXTExvWjZEaWF1emRKQWlFQTlHU0FJR2hmTTlrYldsa2NqTXM2bEE0cHdmNFJmVUVGZWdoWXBaS2JxRm89Il19.eyJjb29sIjpmYWxzZX0.0J41d6x1AsJz7kVrBtJKqeQV8mSdb7tYDQXQjPCuHLktAz2b3m1WfrScQ3Vz4lz2Yzb_dKBrX9M9kQP16Nx1Bw +let validToken = """ + eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlDZkRDQ0FpT2dBd0lCQWdJVUNISzBMY2NzZ3hPUG5zSk5BdmxNeUpzY1BYa3dDZ1lJS29aSXpqMEVBd0l3Z2FFeEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUlEQWhPWlhjZ1dXOXlhekVSTUE4R0ExVUVCd3dJVG1WM0lGbHZjbXN4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFZU1Cd0dBMVVFQXd3VlZtRndiM0lnU1c1MFpYSnRaV1JwWVhSbElFTkJNU1l3SkFZSktvWklodmNOQVFrQkZoZGhaRzFwYmtCMllYQnZjaTVsZUdGdGNHeGxMbU52YlRBZUZ3MHlOREV3TWpneE56STVNakphRncweU5URXdNamd4TnpJNU1qSmFNSUdXTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNBd0lUbVYzSUZsdmNtc3hFVEFQQmdOVkJBY01DRTVsZHlCWmIzSnJNUTR3REFZRFZRUUtEQVZXWVhCdmNqRVVNQklHQTFVRUN3d0xSVzVuYVc1bFpYSnBibWN4RXpBUkJnTlZCQU1NQ2xaaGNHOXlJRXhsWVdZeEpqQWtCZ2txaGtpRzl3MEJDUUVXRjJGa2JXbHVRSFpoY0c5eUxtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVXclZnSEJrKzhMTDJVblBQZStYd2ttUGRTTjRrWkF3Q3pWRGZnalJPNUZvN2NEK2RBVUtSUUEyQmlvSlZzMnl0TjhKYlA1aG9xVnM5c1J0TUtUUjVYcU5DTUVBd0hRWURWUjBPQkJZRUZGVFRNWllYTWpqZzJQRDlWN0J1NzhJN3ZHYlwvTUI4R0ExVWRJd1FZTUJhQUZER0lEMjB0M2RENFkxdnpTK2RoUzRyUGZqT1dNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJR2RSWUtmbThNNnJmMUJ2dmV0SzJYWW1aaWs4Y0tDejNFQ2xvd0YzMDlUbEFpQWVHMWx1dVVHOHo5bkh5QjNmWWZkKzRKcVRRNEtuTEhNQThZUnN2SXd4amc9PSIsIk1JSUNqakNDQWpTZ0F3SUJBZ0lVVERBNXQ1Um9CMDVpZk5WcTdSWGhPUUREdzhzd0NnWUlLb1pJemowRUF3SXdnWmt4Q3pBSkJnTlZCQVlUQWxWVE1SRXdEd1lEVlFRSURBaE9aWGNnV1c5eWF6RVJNQThHQTFVRUJ3d0lUbVYzSUZsdmNtc3hEakFNQmdOVkJBb01CVlpoY0c5eU1SUXdFZ1lEVlFRTERBdEZibWRwYm1WbGNtbHVaekVXTUJRR0ExVUVBd3dOVm1Gd2IzSWdVbTl2ZENCRFFURW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBZG1Gd2IzSXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1qUXhNREk0TVRjeU9USXlXaGNOTWpreE1ESTNNVGN5T1RJeVdqQ0JvVEVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFnTUNFNWxkeUJaYjNKck1SRXdEd1lEVlFRSERBaE9aWGNnV1c5eWF6RU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEZEQVNCZ05WQkFzTUMwVnVaMmx1WldWeWFXNW5NUjR3SEFZRFZRUUREQlZXWVhCdmNpQkpiblJsY20xbFpHbGhkR1VnUTBFeEpqQWtCZ2txaGtpRzl3MEJDUUVXRjJGa2JXbHVRSFpoY0c5eUxtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVyZW5IcllUS2pqUUVjQWZJd2JaS2RaXC9KTFQ3NDNSU2xYMDhkM1NMYm1cL3ZJYVhDXC9cL21URVoraFpuVDRMTTA1aHlzZWZcL0dlNWY3TDE2RXZJWER5M2xLTlFNRTR3REFZRFZSMFRCQVV3QXdFQlwvekFkQmdOVkhRNEVGZ1FVTVlnUGJTM2QwUGhqV1wvTkw1MkZMaXM5K001WXdId1lEVlIwakJCZ3dGb0FVSDUxdmFoNnlHR0daM2dqemtncEk2ZE12ajFBd0NnWUlLb1pJemowRUF3SURTQUF3UlFJaEFOdTF2bEh2VjdoRFVKRDJ3S2VzME5vNGpUVHVmYVhmVVltOFBHcll0VHRCQWlBWVJoWXhaeWpDeUFTXC9wWlpQOHE2SGNrcTRlT1wvRGNMaWNJZENQbUVFUWV3PT0iLCJNSUlDaVRDQ0FpK2dBd0lCQWdJVWQ0eHlabk1VZ0FpSDE5UjhpZm51SjRcL0NnMVV3Q2dZSUtvWkl6ajBFQXdJd2daa3hDekFKQmdOVkJBWVRBbFZUTVJFd0R3WURWUVFJREFoT1pYY2dXVzl5YXpFUk1BOEdBMVVFQnd3SVRtVjNJRmx2Y21zeERqQU1CZ05WQkFvTUJWWmhjRzl5TVJRd0VnWURWUVFMREF0RmJtZHBibVZsY21sdVp6RVdNQlFHQTFVRUF3d05WbUZ3YjNJZ1VtOXZkQ0JEUVRFbU1DUUdDU3FHU0liM0RRRUpBUllYWVdSdGFXNUFkbUZ3YjNJdVpYaGhiWEJzWlM1amIyMHdIaGNOTWpReE1ESTRNVGN5T1RJeFdoY05NelF4TURJMk1UY3lPVEl4V2pDQm1URUxNQWtHQTFVRUJoTUNWVk14RVRBUEJnTlZCQWdNQ0U1bGR5QlpiM0pyTVJFd0R3WURWUVFIREFoT1pYY2dXVzl5YXpFT01Bd0dBMVVFQ2d3RlZtRndiM0l4RkRBU0JnTlZCQXNNQzBWdVoybHVaV1Z5YVc1bk1SWXdGQVlEVlFRRERBMVdZWEJ2Y2lCU2IyOTBJRU5CTVNZd0pBWUpLb1pJaHZjTkFRa0JGaGRoWkcxcGJrQjJZWEJ2Y2k1bGVHRnRjR3hsTG1OdmJUQlpNQk1HQnlxR1NNNDlBZ0VHQ0NxR1NNNDlBd0VIQTBJQUJITWh5RU9hR29KaTYzSk9WUjFWazFzTG0zdmpHRUduY1pnZldBT2ZJRDZFaTdlNTFDdzVoc2tub0VuQXZNcmZHN0MzRjIwUXl1eXAwb1I1Vm4xRGpxZWpVekJSTUIwR0ExVWREZ1FXQkJRZm5XOXFIcklZWVpuZUNQT1NDa2pwMHkrUFVEQWZCZ05WSFNNRUdEQVdnQlFmblc5cUhySVlZWm5lQ1BPU0NranAweStQVURBUEJnTlZIUk1CQWY4RUJUQURBUUhcL01Bb0dDQ3FHU000OUJBTUNBMGdBTUVVQ0lRRFdndjRWNW9kenV3S0dxNVpEZkRMYWdEYnlpYWY5T3Zjc1V5ajRMaGtFR2dJZ09IMmg3bEpJNXZZcGE1S1RUVnhWemNvcjdabXM0d2hrbW1NTmNNZkJqS2s9Il19.eyJjb29sIjp0cnVlfQ.LG86GQ-fMb2Q3Hf6fJKFJ6BSWmvD5F-y4Rk-CKzTd_WC0k12k_S8XYJpbaRpxa7hzqB3JnGzcLGA3jQ2hVxILw """ - -let validButNotCoolToken = """ - eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCIsIng1YyI6WyJNSUlCanpDQ0FUVUNGSDhFWFBJbDBRbDQwYzdKOEhkK2R6QWgwY3dVTUFvR0NDcUdTTTQ5QkFNQ01FNHhDekFKQmdOVkJBWVRBbFZUTVJNd0VRWURWUVFJREFwVGIyMWxMVk4wWVhSbE1RNHdEQVlEVlFRS0RBVldZWEJ2Y2pFYU1CZ0dBMVVFQXd3UmFXNTBaWEp0WldScFlYUmxMV05sY25Rd0hoY05Nak14TURJME1UTTBNalU1V2hjTk1qUXhNREl6TVRNME1qVTVXakJHTVFzd0NRWURWUVFHRXdKVlV6RVRNQkVHQTFVRUNBd0tVMjl0WlMxVGRHRjBaVEVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhFakFRQmdOVkJBTU1DV3hsWVdZdFkyVnlkREJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCRU85VjZLQWhSR0l4QWx2Ukl6U3BtSVRmVVZiWHl5ZGMvZWdlbHpLLzZ3NDEySmc4RDJlSVRkOHVDRmxnVmh4WUlkR1pNN1hYUWhaNmhOZnE2S3JyUG93Q2dZSUtvWkl6ajBFQXdJRFNBQXdSUUloQUxJaXhudE93V3o4NlFHa0g0SGNnT09malBiczlBUVpXYm1HYkpKRjRWRWZBaUI4ZUVLRi9WQllvWVhRREQzVHpLNUlEMkdzbXplMzhpNk56ek9ndHRMam9nPT0iLCJNSUlCNXpDQ0FZeWdBd0lCQWdJVUw2ZnJzdHdldjdZTWtPWVNuUHVlSlQyR3g1UXdDZ1lJS29aSXpqMEVBd0l3UmpFTE1Ba0dBMVVFQmhNQ1ZWTXhFekFSQmdOVkJBZ01DbE52YldVdFUzUmhkR1V4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUkl3RUFZRFZRUUREQWx5YjI5MExXTmxjblF3SGhjTk1qTXhNREkwTVRNME1qTTFXaGNOTWpneE1ESXlNVE0wTWpNMVdqQk9NUXN3Q1FZRFZRUUdFd0pWVXpFVE1CRUdBMVVFQ0F3S1UyOXRaUzFUZEdGMFpURU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEdqQVlCZ05WQkFNTUVXbHVkR1Z5YldWa2FXRjBaUzFqWlhKME1Ga3dFd1lIS29aSXpqMENBUVlJS29aSXpqMERBUWNEUWdBRWt6WGFFQTlDSVpyUkVmQ0Mra05tM0pxZDdmR0ZFT05Ia2p5TFJ4NS83SUFYeHB4TGZ0WWNoOTU1K1VRNVhHOHdUZ2tNQ0NaNG9LRjhNMXg3Zkw3cXU2TlFNRTR3REFZRFZSMFRCQVV3QXdFQi96QWRCZ05WSFE0RUZnUVVyRU53VW02VDBSbmUxTW9MY3lWQ2NhVTVvTTB3SHdZRFZSMGpCQmd3Rm9BVVkxM3oxRVhHbjlVQmZwZ3BmVTl0UWJ2TVJRZ3dDZ1lJS29aSXpqMEVBd0lEU1FBd1JnSWhBSW04bStLb0RFbktBdUgrZUZROGJWSDJkc3p2NlcveCtwNE9zZERzd0VrNUFpRUF5bWd1SmdxQUpZU3NDdzdYM0pDVVBNY29LdGFRRzZNamhRdThrWlpCQUNJPSIsIk1JSUI0VENDQVllZ0F3SUJBZ0lVRG9PZWZlZkNOcS9UR1dyaUlzWUh2ejBMcE5Jd0NnWUlLb1pJemowRUF3SXdSakVMTUFrR0ExVUVCaE1DVlZNeEV6QVJCZ05WQkFnTUNsTnZiV1V0VTNSaGRHVXhEakFNQmdOVkJBb01CVlpoY0c5eU1SSXdFQVlEVlFRRERBbHliMjkwTFdObGNuUXdIaGNOTWpNeE1ESTBNVEl3T0RJM1doY05Nek14TURJeE1USXdPREkzV2pCR01Rc3dDUVlEVlFRR0V3SlZVekVUTUJFR0ExVUVDQXdLVTI5dFpTMVRkR0YwWlRFT01Bd0dBMVVFQ2d3RlZtRndiM0l4RWpBUUJnTlZCQU1NQ1hKdmIzUXRZMlZ5ZERCWk1CTUdCeXFHU000OUFnRUdDQ3FHU000OUF3RUhBMElBQk5wditIRzUyak9UMVcrcjFrMTNiSm8yazlEeVJ5RmJ5Y0JwUHNXUUtmdDlueHdFSHZ6RGoxaXZvTWZhanhsTCtuL0ZMQm5Pblk2M21GV216YW9adkgralV6QlJNQjBHQTFVZERnUVdCQlJqWGZQVVJjYWYxUUYrbUNsOVQyMUJ1OHhGQ0RBZkJnTlZIU01FR0RBV2dCUmpYZlBVUmNhZjFRRittQ2w5VDIxQnU4eEZDREFQQmdOVkhSTUJBZjhFQlRBREFRSC9NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJR0lwWDlsbGlVMTM1VjgrTFk2L2NqQm1HcktLTmxZV0xMb1o2RGlhdXpkSkFpRUE5R1NBSUdoZk05a2JXbGtjak1zNmxBNHB3ZjRSZlVFRmVnaFlwWkticUZvPSJdfQ.eyJjb29sIjpmYWxzZX0.JtNl3uCSJ7rycwW__0o1xARr0y5XYsXUc2Ltx1W2IKmBmn66vAOEY2Eur9Xy40eX8qMr8GrxsGmzia5YEN3ugQ +let missingRootToken = """ + eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlDZkRDQ0FpT2dBd0lCQWdJVUNISzBMY2NzZ3hPUG5zSk5BdmxNeUpzY1BYa3dDZ1lJS29aSXpqMEVBd0l3Z2FFeEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUlEQWhPWlhjZ1dXOXlhekVSTUE4R0ExVUVCd3dJVG1WM0lGbHZjbXN4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFZU1Cd0dBMVVFQXd3VlZtRndiM0lnU1c1MFpYSnRaV1JwWVhSbElFTkJNU1l3SkFZSktvWklodmNOQVFrQkZoZGhaRzFwYmtCMllYQnZjaTVsZUdGdGNHeGxMbU52YlRBZUZ3MHlOREV3TWpneE56STVNakphRncweU5URXdNamd4TnpJNU1qSmFNSUdXTVFzd0NRWURWUVFHRXdKVlV6RVJNQThHQTFVRUNBd0lUbVYzSUZsdmNtc3hFVEFQQmdOVkJBY01DRTVsZHlCWmIzSnJNUTR3REFZRFZRUUtEQVZXWVhCdmNqRVVNQklHQTFVRUN3d0xSVzVuYVc1bFpYSnBibWN4RXpBUkJnTlZCQU1NQ2xaaGNHOXlJRXhsWVdZeEpqQWtCZ2txaGtpRzl3MEJDUUVXRjJGa2JXbHVRSFpoY0c5eUxtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVXclZnSEJrKzhMTDJVblBQZStYd2ttUGRTTjRrWkF3Q3pWRGZnalJPNUZvN2NEK2RBVUtSUUEyQmlvSlZzMnl0TjhKYlA1aG9xVnM5c1J0TUtUUjVYcU5DTUVBd0hRWURWUjBPQkJZRUZGVFRNWllYTWpqZzJQRDlWN0J1NzhJN3ZHYlwvTUI4R0ExVWRJd1FZTUJhQUZER0lEMjB0M2RENFkxdnpTK2RoUzRyUGZqT1dNQW9HQ0NxR1NNNDlCQU1DQTBjQU1FUUNJR2RSWUtmbThNNnJmMUJ2dmV0SzJYWW1aaWs4Y0tDejNFQ2xvd0YzMDlUbEFpQWVHMWx1dVVHOHo5bkh5QjNmWWZkKzRKcVRRNEtuTEhNQThZUnN2SXd4amc9PSIsIk1JSUNqakNDQWpTZ0F3SUJBZ0lVVERBNXQ1Um9CMDVpZk5WcTdSWGhPUUREdzhzd0NnWUlLb1pJemowRUF3SXdnWmt4Q3pBSkJnTlZCQVlUQWxWVE1SRXdEd1lEVlFRSURBaE9aWGNnV1c5eWF6RVJNQThHQTFVRUJ3d0lUbVYzSUZsdmNtc3hEakFNQmdOVkJBb01CVlpoY0c5eU1SUXdFZ1lEVlFRTERBdEZibWRwYm1WbGNtbHVaekVXTUJRR0ExVUVBd3dOVm1Gd2IzSWdVbTl2ZENCRFFURW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBZG1Gd2IzSXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1qUXhNREk0TVRjeU9USXlXaGNOTWpreE1ESTNNVGN5T1RJeVdqQ0JvVEVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFnTUNFNWxkeUJaYjNKck1SRXdEd1lEVlFRSERBaE9aWGNnV1c5eWF6RU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEZEQVNCZ05WQkFzTUMwVnVaMmx1WldWeWFXNW5NUjR3SEFZRFZRUUREQlZXWVhCdmNpQkpiblJsY20xbFpHbGhkR1VnUTBFeEpqQWtCZ2txaGtpRzl3MEJDUUVXRjJGa2JXbHVRSFpoY0c5eUxtVjRZVzF3YkdVdVkyOXRNRmt3RXdZSEtvWkl6ajBDQVFZSUtvWkl6ajBEQVFjRFFnQUVyZW5IcllUS2pqUUVjQWZJd2JaS2RaXC9KTFQ3NDNSU2xYMDhkM1NMYm1cL3ZJYVhDXC9cL21URVoraFpuVDRMTTA1aHlzZWZcL0dlNWY3TDE2RXZJWER5M2xLTlFNRTR3REFZRFZSMFRCQVV3QXdFQlwvekFkQmdOVkhRNEVGZ1FVTVlnUGJTM2QwUGhqV1wvTkw1MkZMaXM5K001WXdId1lEVlIwakJCZ3dGb0FVSDUxdmFoNnlHR0daM2dqemtncEk2ZE12ajFBd0NnWUlLb1pJemowRUF3SURTQUF3UlFJaEFOdTF2bEh2VjdoRFVKRDJ3S2VzME5vNGpUVHVmYVhmVVltOFBHcll0VHRCQWlBWVJoWXhaeWpDeUFTXC9wWlpQOHE2SGNrcTRlT1wvRGNMaWNJZENQbUVFUWV3PT0iXX0.eyJjb29sIjp0cnVlfQ.nenwxqFklsxogt4Cp2IzgcUcp7EFc2taJTc6vj49yOfKRZLm8bAtuOToa-lflk6ABHVGT1BeCeEb8Y5wFIZ9AQ + """ +let missingLeafToken = """ + eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NiIsIng1YyI6WyJNSUlDampDQ0FqU2dBd0lCQWdJVVREQTV0NVJvQjA1aWZOVnE3UlhoT1FERHc4c3dDZ1lJS29aSXpqMEVBd0l3Z1preEN6QUpCZ05WQkFZVEFsVlRNUkV3RHdZRFZRUUlEQWhPWlhjZ1dXOXlhekVSTUE4R0ExVUVCd3dJVG1WM0lGbHZjbXN4RGpBTUJnTlZCQW9NQlZaaGNHOXlNUlF3RWdZRFZRUUxEQXRGYm1kcGJtVmxjbWx1WnpFV01CUUdBMVVFQXd3TlZtRndiM0lnVW05dmRDQkRRVEVtTUNRR0NTcUdTSWIzRFFFSkFSWVhZV1J0YVc1QWRtRndiM0l1WlhoaGJYQnNaUzVqYjIwd0hoY05NalF4TURJNE1UY3lPVEl5V2hjTk1qa3hNREkzTVRjeU9USXlXakNCb1RFTE1Ba0dBMVVFQmhNQ1ZWTXhFVEFQQmdOVkJBZ01DRTVsZHlCWmIzSnJNUkV3RHdZRFZRUUhEQWhPWlhjZ1dXOXlhekVPTUF3R0ExVUVDZ3dGVm1Gd2IzSXhGREFTQmdOVkJBc01DMFZ1WjJsdVpXVnlhVzVuTVI0d0hBWURWUVFEREJWV1lYQnZjaUJKYm5SbGNtMWxaR2xoZEdVZ1EwRXhKakFrQmdrcWhraUc5dzBCQ1FFV0YyRmtiV2x1UUhaaGNHOXlMbVY0WVcxd2JHVXVZMjl0TUZrd0V3WUhLb1pJemowQ0FRWUlLb1pJemowREFRY0RRZ0FFcmVuSHJZVEtqalFFY0FmSXdiWktkWlwvSkxUNzQzUlNsWDA4ZDNTTGJtXC92SWFYQ1wvXC9tVEVaK2hablQ0TE0wNWh5c2VmXC9HZTVmN0wxNkV2SVhEeTNsS05RTUU0d0RBWURWUjBUQkFVd0F3RUJcL3pBZEJnTlZIUTRFRmdRVU1ZZ1BiUzNkMFBoaldcL05MNTJGTGlzOStNNVl3SHdZRFZSMGpCQmd3Rm9BVUg1MXZhaDZ5R0dHWjNnanprZ3BJNmRNdmoxQXdDZ1lJS29aSXpqMEVBd0lEU0FBd1JRSWhBTnUxdmxIdlY3aERVSkQyd0tlczBObzRqVFR1ZmFYZlVZbThQR3JZdFR0QkFpQVlSaFl4WnlqQ3lBU1wvcFpaUDhxNkhja3E0ZU9cL0RjTGljSWRDUG1FRVFldz09IiwiTUlJQ2lUQ0NBaStnQXdJQkFnSVVkNHh5Wm5NVWdBaUgxOVI4aWZudUo0XC9DZzFVd0NnWUlLb1pJemowRUF3SXdnWmt4Q3pBSkJnTlZCQVlUQWxWVE1SRXdEd1lEVlFRSURBaE9aWGNnV1c5eWF6RVJNQThHQTFVRUJ3d0lUbVYzSUZsdmNtc3hEakFNQmdOVkJBb01CVlpoY0c5eU1SUXdFZ1lEVlFRTERBdEZibWRwYm1WbGNtbHVaekVXTUJRR0ExVUVBd3dOVm1Gd2IzSWdVbTl2ZENCRFFURW1NQ1FHQ1NxR1NJYjNEUUVKQVJZWFlXUnRhVzVBZG1Gd2IzSXVaWGhoYlhCc1pTNWpiMjB3SGhjTk1qUXhNREk0TVRjeU9USXhXaGNOTXpReE1ESTJNVGN5T1RJeFdqQ0JtVEVMTUFrR0ExVUVCaE1DVlZNeEVUQVBCZ05WQkFnTUNFNWxkeUJaYjNKck1SRXdEd1lEVlFRSERBaE9aWGNnV1c5eWF6RU9NQXdHQTFVRUNnd0ZWbUZ3YjNJeEZEQVNCZ05WQkFzTUMwVnVaMmx1WldWeWFXNW5NUll3RkFZRFZRUUREQTFXWVhCdmNpQlNiMjkwSUVOQk1TWXdKQVlKS29aSWh2Y05BUWtCRmhkaFpHMXBia0IyWVhCdmNpNWxlR0Z0Y0d4bExtTnZiVEJaTUJNR0J5cUdTTTQ5QWdFR0NDcUdTTTQ5QXdFSEEwSUFCSE1oeUVPYUdvSmk2M0pPVlIxVmsxc0xtM3ZqR0VHbmNaZ2ZXQU9mSUQ2RWk3ZTUxQ3c1aHNrbm9FbkF2TXJmRzdDM0YyMFF5dXlwMG9SNVZuMURqcWVqVXpCUk1CMEdBMVVkRGdRV0JCUWZuVzlxSHJJWVlabmVDUE9TQ2tqcDB5K1BVREFmQmdOVkhTTUVHREFXZ0JRZm5XOXFIcklZWVpuZUNQT1NDa2pwMHkrUFVEQVBCZ05WSFJNQkFmOEVCVEFEQVFIXC9NQW9HQ0NxR1NNNDlCQU1DQTBnQU1FVUNJUURXZ3Y0VjVvZHp1d0tHcTVaRGZETGFnRGJ5aWFmOU92Y3NVeWo0TGhrRUdnSWdPSDJoN2xKSTV2WXBhNUtUVFZ4Vnpjb3I3Wm1zNHdoa21tTU5jTWZCaktrPSJdfQ.eyJjb29sIjp0cnVlfQ.NSXDHiFXy9QT6d3O4XE9htXUA7Tm8ZYehPPaMmlFJKSmUev07YOxVI9J3Em_wspMzUj--4VxnfN9D73AvevtMw """ let x5cCerts = [ @@ -457,7 +457,7 @@ private struct TokenPayload: JWTPayload { func verify(using _: some JWTAlgorithm) throws { if !cool.value { - throw JWTError.claimVerificationFailure(failedClaim: cool, reason: "not cool") + throw JWTError.claimVerificationFailure(failedClaim: self.cool, reason: "not cool") } } } diff --git a/scripts/create-certs.sh b/scripts/create-certs.sh deleted file mode 100644 index d29f2662..00000000 --- a/scripts/create-certs.sh +++ /dev/null @@ -1,54 +0,0 @@ -#!/usr/bin/env bash - -set -e -o pipefail - -mkdir jwt_x5c_certs -pushd jwt_x5c_certs - -# Create a Root Certificate (self-signed): - -# Generate a private key for the Root CA -openssl ecparam -name prime256v1 -genkey -noout -out root_key.pem - -# Create a self-signed Root certificate -openssl req -new -x509 -key root_key.pem -out root_cert.pem -days 3650 - -# Create an Intermediate CA Certificate (signed by the Root, and itself a CA): - -# Generate a private key for the Intermediate CA -openssl ecparam -name prime256v1 -genkey -noout -out intermediate_key.pem - -# Create a certificate signing request for the Intermediate CA -openssl req -new -key intermediate_key.pem -out intermediate_csr.pem - -# Sign the Intermediate CA certificate with the Root, specifying the basicConstraints extension -openssl x509 -req \ - -in intermediate_csr.pem \ - -CA root_cert.pem -CAkey root_key.pem \ - -out intermediate_cert.pem \ - -days 1825 \ - -extfile <(echo 'basicConstraints=CA:TRUE') - -# Create a Leaf Certificate (signed by the Intermediate): - -# Generate a private key for the Leaf certificate -openssl ecparam -name prime256v1 -genkey -noout -out leaf_key.pem - -# Create a certificate signing request (CSR) for the Leaf -openssl req -new -key leaf_key.pem -out leaf_csr.pem - -# Sign the Leaf certificate with the Intermediate -openssl x509 -req -in leaf_csr.pem -CA intermediate_cert.pem -CAkey intermediate_key.pem -out leaf_cert.pem -days 365 - -# Create a Leaf Expired Certificate - -# Generate a private key for the Leaf certificate -openssl ecparam -name prime256v1 -genkey -noout -out expired_leaf_key.pem - -# Create a certificate signing request (CSR) for the Leaf -openssl req -new -key expired_leaf_key.pem -out expired_leaf_csr.pem - -# Sign the Leaf certificate with the Intermediate -openssl x509 -req -in expired_leaf_csr.pem -CA intermediate_cert.pem -CAkey intermediate_key.pem -out expired_leaf_cert.pem -days -1 - -popd diff --git a/scripts/create-jwt.py b/scripts/create-jwt.py deleted file mode 100644 index dbcb240d..00000000 --- a/scripts/create-jwt.py +++ /dev/null @@ -1,25 +0,0 @@ -import jwt -import cryptography - -# This script creates a JWT token with an x5c array in the header. -# The certs are loaded from the filesystem using the commands which are in the create-certs.sh file. -# To load different certs into the x5c header, change the x5c array below. - -# Load base64-encoded certificates -with open('root_cert.pem', 'r') as f: - root_cert = f.read().strip() - root_cert = root_cert.replace("-----BEGIN CERTIFICATE-----", "").replace("-----END CERTIFICATE-----", "").replace("\n", "") -with open('intermediate_cert.pem', 'r') as f: - intermediate_cert = f.read().strip() - intermediate_cert = intermediate_cert.replace("-----BEGIN CERTIFICATE-----", "").replace("-----END CERTIFICATE-----", "").replace("\n", "") -with open('leaf_cert.pem', 'r') as f: - leaf_cert = f.read().strip() - leaf_cert = leaf_cert.replace("-----BEGIN CERTIFICATE-----", "").replace("-----END CERTIFICATE-----", "").replace("\n", "") - -# Create JWT token with x5c array -payload = {"cool": False} -key = open("leaf_key.pem").read() -x5c = [leaf_cert, intermediate_cert, root_cert] -token = jwt.encode(payload, algorithm="ES256", key=key, headers={"x5c": x5c}) - -print(token) diff --git a/scripts/create-jwt.sh b/scripts/create-jwt.sh deleted file mode 100755 index 10809fa5..00000000 --- a/scripts/create-jwt.sh +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bash - -# Function to convert a string to base64 URL encoding -function b64url() { - echo "${1}" | openssl base64 -a -A | tr '+' '-' | tr '/' '_' | tr -d '=' -} - -# Read and concatenate certificate files, remove PEM header/footer lines and newlines -certs=$(cat root_cert.pem intermediate_cert.pem leaf_cert.pem | - sed '/-----/d' | tr -d '\n') - -# Base64 URL encode the JWT Header with embedded certificate chain -hdr="$(b64url '{"alg":"ES256","typ":"JWT","x5c":["'"${certs}"'"]}')" - -# Base64 URL encode the JWT Payload -pld="$(b64url '{"cool":true}')" - -# Concatenate the encoded header and payload -msg="${hdr}.${pld}" - -# Sign the message using the private key and Base64 URL encode the signature -# Ensure that the message is hashed if necessary -sig=$(echo -n "${msg}" | openssl dgst -sha256 -sign leaf_key.pem | b64url) - -# Concatenate the header, payload, and signature to form the JWT -jwt="${msg}.${sig}" - -# Output the JWT -echo "${jwt}" diff --git a/scripts/generate-certificates.sh b/scripts/generate-certificates.sh new file mode 100644 index 00000000..a4611836 --- /dev/null +++ b/scripts/generate-certificates.sh @@ -0,0 +1,65 @@ +# This gets called by the `generateTokens.swift` script. Do not modify! + +#!/usr/bin/env bash +set -e -o pipefail + +# Configuration variables +COUNTRY="US" +STATE="New York" +CITY="New York" +ORGANIZATION="Vapor" +ORGANIZATIONAL_UNIT="Engineering" +EMAIL="admin@vapor.example.com" + +mkdir -p jwt_x5c_certs +pushd jwt_x5c_certs + +# Function to generate subject string +generate_subject() { + local prefix="$1" + echo "/C=$COUNTRY/ST=$STATE/L=$CITY/O=$ORGANIZATION/OU=$ORGANIZATIONAL_UNIT/CN=$ORGANIZATION $prefix/emailAddress=$EMAIL" +} + +# Create Root Certificate +openssl ecparam -name prime256v1 -genkey -noout -out root_key.pem +openssl req -new -x509 -key root_key.pem -out root_cert.pem -days 3650 \ + -subj "$(generate_subject 'Root CA')" \ + -nodes + +# Create Intermediate Certificate +openssl ecparam -name prime256v1 -genkey -noout -out intermediate_key.pem +openssl req -new -key intermediate_key.pem -out intermediate_csr.pem \ + -subj "$(generate_subject 'Intermediate CA')" \ + -nodes + +echo 'basicConstraints=CA:TRUE' > intermediate_ext.txt +openssl x509 -req \ + -in intermediate_csr.pem \ + -CA root_cert.pem -CAkey root_key.pem \ + -CAcreateserial \ + -out intermediate_cert.pem \ + -days 1825 \ + -extfile intermediate_ext.txt + +# Create Valid Leaf Certificate +openssl ecparam -name prime256v1 -genkey -noout -out leaf_key.pem +openssl req -new -key leaf_key.pem -out leaf_csr.pem \ + -subj "$(generate_subject 'Leaf')" \ + -nodes +openssl x509 -req -in leaf_csr.pem \ + -CA intermediate_cert.pem -CAkey intermediate_key.pem \ + -CAcreateserial \ + -out leaf_cert.pem -days 365 + +# Create Expired Leaf Certificate +openssl ecparam -name prime256v1 -genkey -noout -out expired_leaf_key.pem +openssl req -new -key expired_leaf_key.pem -out expired_leaf_csr.pem \ + -subj "$(generate_subject 'Expired Leaf')" \ + -nodes +openssl x509 -req -in expired_leaf_csr.pem \ + -CA intermediate_cert.pem -CAkey intermediate_key.pem \ + -CAcreateserial \ + -out expired_leaf_cert.pem -days -1 + +rm -f intermediate_ext.txt +popd diff --git a/scripts/generateTokens.swift b/scripts/generateTokens.swift new file mode 100644 index 00000000..d3754453 --- /dev/null +++ b/scripts/generateTokens.swift @@ -0,0 +1,162 @@ +/// This script can be used to regenerate the X5CTests tokens +/// when they don't verify anymore. If you're here, it likely means +/// the certificates are expired and should be updated. +/// This script does just that: generate new certificates and create +/// tokens with x5c chains based on those certs. +/// +/// To run the script, simply run `swift scripts/generateTokens.swift`. +/// The output will be +/// - the new tokens, printed in Swift, which means you +/// just need to copy and paste them replacing the old ones; +/// - the root certificate, which you have to replace too. +/// After creating the tokens, the script will delete certificates. +/// If you want to keep them for some reason, just add `--keep-certs` +/// to the script execution. They will be stored in the `x5c_test_certs`. +/// This directory is in the `.gitignore` so that it doesn't get committed. +import Foundation + +enum ScriptError: Error { + case certificateGenerationFailed(status: Int32) + case fileNotFound(at: String) + case invalidSignature(String) +} + +struct JWTGenerator { + let certificateDirectory = "x5c_test_certs" + let leafKeyFileName = "leaf_key.pem" + let leafCertFileName = "leaf_cert.pem" + let expiredLeafCertFileName = "expired_leaf_cert.pem" + let intermediateCertFileName = "intermediate_cert.pem" + let rootCertFileName = "root_cert.pem" + + func generateCertificates() throws { + let process = Process() + process.launchPath = "/bin/bash" + process.arguments = ["scripts/generate-certificates.sh"] + + try process.run() + process.waitUntilExit() + + if process.terminationStatus != 0 { + throw ScriptError.certificateGenerationFailed(status: process.terminationStatus) + } + + print("✅ Certificates generated successfully") + } + + func readCertificateData(from name: String, stripped: Bool = true) throws -> String { + let path = URL(filePath: certificateDirectory).appending(path: name) + let content = try String(contentsOf: path, encoding: .utf8) + let finalContent = + if stripped { + content + .replacing("-----BEGIN CERTIFICATE-----", with: "") + .replacing("-----END CERTIFICATE-----", with: "") + .replacing("\n", with: "") + } else { + content + } + return finalContent + } + + func generateToken(payload: [String: Any], certificates: [String], signingKeyPath: String) throws -> String { + func base64URLEncode(_ data: Data) -> String { + data.base64EncodedString() + .replacingOccurrences(of: "+", with: "-") + .replacingOccurrences(of: "/", with: "_") + .replacingOccurrences(of: "=", with: "") + } + + let x5cChain = try certificates.map { try readCertificateData(from: $0) } + + let header: [String: Any] = [ + "alg": "ES256", + "typ": "JWT", + "x5c": x5cChain, + ] + let encodedHeader = try base64URLEncode(JSONSerialization.data(withJSONObject: header)) + let encodedBody = try base64URLEncode(JSONSerialization.data(withJSONObject: payload)) + + let message = "\(encodedHeader).\(encodedBody)" + + let task = Process() + task.executableURL = URL(filePath: "/bin/bash") + let command = """ + echo -n "\(message)" | + openssl dgst -sha256 -sign "\(certificateDirectory)/\(signingKeyPath)" | + openssl asn1parse -inform DER | + perl -n -e '/INTEGER :([0-9A-Z]*)$/ && print $1' | + xxd -p -r + """ + task.arguments = ["-c", command] + + let outputPipe = Pipe() + task.standardOutput = outputPipe + + try task.run() + let signature = base64URLEncode(outputPipe.fileHandleForReading.readDataToEndOfFile()) + return "\(message).\(signature)" + } + + func generateTokens(keepingCertificates: Bool = false) throws { + let coolPayload = ["cool": true] + let tokens = try [ + "validToken": generateToken( + payload: coolPayload, certificates: [leafCertFileName, intermediateCertFileName, rootCertFileName], + signingKeyPath: leafKeyFileName + ), + "missingIntermediateToken": generateToken( + payload: coolPayload, certificates: [leafCertFileName, rootCertFileName], signingKeyPath: leafKeyFileName + ), + "missingRootToken": generateToken( + payload: coolPayload, certificates: [leafCertFileName, intermediateCertFileName], signingKeyPath: leafKeyFileName + ), + "missingLeafToken": generateToken( + payload: coolPayload, certificates: [intermediateCertFileName, rootCertFileName], signingKeyPath: leafKeyFileName + ), + "missingLeafAndIntermediateToken": generateToken( + payload: coolPayload, certificates: [rootCertFileName], signingKeyPath: leafKeyFileName + ), + "missingIntermediateAndRootToken": generateToken( + payload: coolPayload, certificates: [leafCertFileName], signingKeyPath: leafKeyFileName + ), + "expiredLeafToken": generateToken( + payload: coolPayload, certificates: [expiredLeafCertFileName, intermediateCertFileName, rootCertFileName], + signingKeyPath: leafKeyFileName + ), + "validButNotCoolToken": generateToken( + payload: ["cool": false], certificates: [leafCertFileName, intermediateCertFileName, rootCertFileName], + signingKeyPath: leafKeyFileName + ), + ] + print("Swift Token Declarations:") + for (name, token) in tokens { + print( + """ + let \(name) = \""" + \(token) + \""" + """) + } + try print(readCertificateData(from: rootCertFileName, stripped: false)) + + if !keepingCertificates { + try? FileManager.default.removeItem(atPath: certificateDirectory) + print("🧹 Certificates cleaned up") + } else { + if let absolutePath = FileManager.default.currentDirectoryPath as NSString? { + print("💾 Certificates saved in \(absolutePath.appendingPathComponent(certificateDirectory))") + } + } + } +} + +let generator = JWTGenerator() +let keepingCertificates = CommandLine.arguments.contains("--keep-certs") +do { + try generator.generateCertificates() + try generator.generateTokens(keepingCertificates: keepingCertificates) +} catch { + print("Error: \(error)") + exit(1) +}