You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by al...@apache.org on 2023/01/19 20:30:19 UTC

[kudu] branch master updated: plumb JWT authentication into clients

This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/master by this push:
     new fa1b1837a plumb JWT authentication into clients
fa1b1837a is described below

commit fa1b1837ab02b326f8f7e7e3126f72d4929a950e
Author: Zoltan Chovan <zc...@cloudera.com>
AuthorDate: Tue Jan 17 15:07:31 2023 +0100

    plumb JWT authentication into clients
    
    This change plumbs the JWT authentication into the client and into the
    client negotiation (the JWTVerifier is set when building the Messenger).
    
    There following new flags are added:
    * enable_jwt_token_auth
    * jwt_validate_signature (unsafe)
    * jwt_allow_without_tls (unsafe)
    * jwks_file_path
    * jwks_url
    
    If 'enable_jwt_token_auth' is set to true, then either 'jwks_file_path'
    or 'jwks_url' has to be set, also both cannot be set at the same time.
    
    Co-authored-by: Andrew Wong <aw...@apache.org>
    
    Change-Id: Ibdfc2640c047a2e3bb5ea03aea4439cea2532e23
    Reviewed-on: http://gerrit.cloudera.org:8080/18471
    Tested-by: Kudu Jenkins
    Reviewed-by: Alexey Serbin <al...@apache.org>
---
 src/kudu/client/client.cc          |   7 +
 src/kudu/rpc/client_negotiation.cc |   9 +-
 src/kudu/security/tls_handshake.cc |   5 +-
 src/kudu/security/tls_handshake.h  |   1 -
 src/kudu/server/server_base.cc     |  65 ++++++
 src/kudu/util/jwt-util-test.cc     | 407 +++++--------------------------------
 src/kudu/util/jwt_test_certs.h     | 332 ++++++++++++++++++++++++++++++
 7 files changed, 460 insertions(+), 366 deletions(-)

diff --git a/src/kudu/client/client.cc b/src/kudu/client/client.cc
index 818bb27e3..1d767cf22 100644
--- a/src/kudu/client/client.cc
+++ b/src/kudu/client/client.cc
@@ -350,6 +350,9 @@ Status ImportAuthnCreds(const string& authn_creds,
     }
     messenger->set_authn_token(tok);
   }
+  if (pb.has_jwt()) {
+    messenger->set_jwt(pb.jwt());
+  }
   if (pb.has_real_user()) {
     user_credentials->set_real_user(pb.real_user());
   }
@@ -774,6 +777,10 @@ Status KuduClient::ExportAuthenticationCredentials(string* authn_creds) const {
   if (auto tok = data_->messenger_->authn_token(); tok) {
     pb.mutable_authn_token()->CopyFrom(*tok);
   }
+  auto jwt = data_->messenger_->jwt();
+  if (jwt) {
+    pb.mutable_jwt()->CopyFrom(*jwt);
+  }
   pb.set_real_user(data_->user_credentials_.real_user());
 
   vector<string> cert_ders;
diff --git a/src/kudu/rpc/client_negotiation.cc b/src/kudu/rpc/client_negotiation.cc
index 8fecbecba..7fc9f48a8 100644
--- a/src/kudu/rpc/client_negotiation.cc
+++ b/src/kudu/rpc/client_negotiation.cc
@@ -195,8 +195,9 @@ Status ClientNegotiation::Negotiate(unique_ptr<ErrorStatusPB>* rpc_error) {
       ContainsKey(server_features_, TLS)) {
     RETURN_NOT_OK(tls_context_->InitiateHandshake(&tls_handshake_));
 
-    if (negotiated_authn_ == AuthenticationType::SASL) {
-      // When using SASL authentication, verifying the server's certificate is
+    if (negotiated_authn_ == AuthenticationType::SASL ||
+        negotiated_authn_ == AuthenticationType::JWT) {
+      // When using SASL or JWT authentication, verifying the server's certificate is
       // not necessary. This allows the client to still use TLS encryption for
       // connections to servers which only have a self-signed certificate.
       tls_handshake_.set_verification_mode(security::TlsVerificationMode::VERIFY_NONE);
@@ -411,6 +412,10 @@ Status ClientNegotiation::HandleNegotiate(const NegotiatePB& response) {
         negotiated_authn_ = AuthenticationType::TOKEN;
         return Status::OK();
       case AuthenticationTypePB::kJwt:
+        if (!jwt_) {
+          return Status::RuntimeError(
+              "server chose JWT authentication, but client has no JWT");
+        }
         negotiated_authn_ = AuthenticationType::JWT;
         return Status::OK();
       case AuthenticationTypePB::kCertificate:
diff --git a/src/kudu/security/tls_handshake.cc b/src/kudu/security/tls_handshake.cc
index 82e22e073..f3d2e4503 100644
--- a/src/kudu/security/tls_handshake.cc
+++ b/src/kudu/security/tls_handshake.cc
@@ -22,9 +22,10 @@
 #include <openssl/x509.h>
 
 #include <cstdint>
+#include <functional>
 #include <memory>
-#include <string>
 #include <ostream>
+#include <string>
 #include <utility>
 
 #include "kudu/gutil/strings/strip.h"
@@ -179,7 +180,7 @@ bool TlsHandshake::NeedsExtraStep(const Status& continue_status,
                                   const string& token) const {
   DCHECK(has_started_);
   DCHECK(ssl_);
-  DCHECK(continue_status.ok() || continue_status.IsIncomplete());
+  DCHECK(continue_status.ok() || continue_status.IsIncomplete()) << continue_status.ToString();
 
   if (continue_status.IsIncomplete()) {
     return true;
diff --git a/src/kudu/security/tls_handshake.h b/src/kudu/security/tls_handshake.h
index d36e2bfbe..69f1c3ea7 100644
--- a/src/kudu/security/tls_handshake.h
+++ b/src/kudu/security/tls_handshake.h
@@ -17,7 +17,6 @@
 
 #pragma once
 
-#include <functional>
 #include <memory>
 #include <string>
 
diff --git a/src/kudu/server/server_base.cc b/src/kudu/server/server_base.cc
index 8ca83ab88..a83207a1a 100644
--- a/src/kudu/server/server_base.cc
+++ b/src/kudu/server/server_base.cc
@@ -76,6 +76,7 @@
 #include "kudu/util/flag_validators.h"
 #include "kudu/util/flags.h"
 #include "kudu/util/jsonwriter.h"
+#include "kudu/util/jwt-util.h"
 #include "kudu/util/logging.h"
 #include "kudu/util/mem_tracker.h"
 #include "kudu/util/metrics.h"
@@ -96,6 +97,10 @@
 #include "kudu/util/user.h"
 #include "kudu/util/version_info.h"
 
+namespace kudu {
+class JwtVerifier;
+}  // namespace kudu
+
 DEFINE_int32(num_reactor_threads, 4, "Number of libev reactor threads to start.");
 TAG_FLAG(num_reactor_threads, advanced);
 
@@ -240,6 +245,33 @@ DEFINE_uint64(server_max_open_files, 0,
               "automatically calculate this value. This is a soft limit");
 TAG_FLAG(server_max_open_files, advanced);
 
+DEFINE_bool(enable_jwt_token_auth, false,
+    "This enables JWT authentication, meaning that the server expects a valid "
+    "JWT to be sent by the client which will be verified when the connection is "
+    "being established. When true, read the JWT token out of the RPC and extract "
+    "user name from the token payload.");
+TAG_FLAG(enable_jwt_token_auth, experimental);
+
+DEFINE_bool(jwt_validate_signature, true,
+    "When true, validate the signature of JWT token with pre-installed JWKS.");
+TAG_FLAG(jwt_validate_signature, experimental);
+TAG_FLAG(jwt_validate_signature, unsafe);
+
+DEFINE_bool(jwt_allow_without_tls, false,
+    "When this configuration is set to true, Kudu allows JWT authentication on "
+    "unsecure channel. This should be only enabled for testing, or development "
+    "for which TLS is handled by proxy.");
+TAG_FLAG(jwt_allow_without_tls, experimental);
+TAG_FLAG(jwt_allow_without_tls, unsafe);
+
+DEFINE_string(jwks_file_path, "",
+    "File path of the pre-installed JSON Web Key Set (JWKS) for JWT verification.");
+TAG_FLAG(jwks_file_path, experimental);
+
+DEFINE_string(jwks_url, "",
+    "URL of the JSON Web Key Set (JWKS) for JWT verification.");
+TAG_FLAG(jwks_url, experimental);
+
 DECLARE_bool(use_hybrid_clock);
 DECLARE_int32(dns_resolver_max_threads_num);
 DECLARE_uint32(dns_resolver_cache_capacity_mb);
@@ -344,6 +376,27 @@ bool ValidateKeytabPermissions() {
 }
 GROUP_FLAG_VALIDATOR(keytab_permissions, &ValidateKeytabPermissions);
 
+bool ValidateJWKSNotEmpty() {
+  if (FLAGS_enable_jwt_token_auth && (FLAGS_jwks_file_path.empty() && FLAGS_jwks_url.empty())) {
+    LOG(ERROR) << "'jwt_token_auth' is enabled, but 'jwks_filepath' and 'jwks_url' are both empty";
+    return false;
+  }
+
+  return true;
+}
+
+GROUP_FLAG_VALIDATOR(jwk_not_empty_validator, &ValidateJWKSNotEmpty);
+
+bool ValidateEitherJWKSFilePathOrUrlSet() {
+  if (!FLAGS_jwks_url.empty() && !FLAGS_jwks_file_path.empty()) {
+    LOG(ERROR) << "only set either 'jwks_url' or 'jwks_file_path' but not both";
+    return false;
+  }
+  return true;
+}
+
+GROUP_FLAG_VALIDATOR(jwks_file_or_url_set_validator, &ValidateEitherJWKSFilePathOrUrlSet);
+
 } // namespace
 
 static bool ValidateRpcAuthentication(const char* flag_name, const string& flag_value) {
@@ -644,6 +697,17 @@ Status ServerBase::Init() {
 
   // Create the Messenger.
   rpc::MessengerBuilder builder(name_);
+  std::shared_ptr<JwtVerifier> jwt_verifier;
+  if (FLAGS_enable_jwt_token_auth) {
+    if (!FLAGS_jwks_url.empty()) {
+      jwt_verifier = std::make_shared<KeyBasedJwtVerifier>(FLAGS_jwks_url, false);
+    } else if (!FLAGS_jwks_file_path.empty()) {
+      jwt_verifier = std::make_shared<KeyBasedJwtVerifier>(FLAGS_jwks_file_path, true);
+    } else {
+      LOG(WARNING) << Substitute("JWT authentication enabled, but neither 'jwks_url' or "
+          "'jwks_file_path' are set!");
+    }
+  }
   builder.set_num_reactors(FLAGS_num_reactor_threads)
          .set_min_negotiation_threads(FLAGS_min_negotiation_threads)
          .set_max_negotiation_threads(FLAGS_max_negotiation_threads)
@@ -659,6 +723,7 @@ Status ServerBase::Init() {
          .set_epki_cert_key_files(FLAGS_rpc_certificate_file, FLAGS_rpc_private_key_file)
          .set_epki_certificate_authority_file(FLAGS_rpc_ca_certificate_file)
          .set_epki_private_password_key_cmd(FLAGS_rpc_private_key_password_cmd)
+         .set_jwt_verifier(std::move(jwt_verifier))
          .set_keytab_file(FLAGS_keytab_file)
          .enable_inbound_tls();
 
diff --git a/src/kudu/util/jwt-util-test.cc b/src/kudu/util/jwt-util-test.cc
index f9b4534f5..ff3b9fddb 100644
--- a/src/kudu/util/jwt-util-test.cc
+++ b/src/kudu/util/jwt-util-test.cc
@@ -37,6 +37,7 @@
 #include "kudu/server/webserver_options.h"
 #include "kudu/util/env.h"
 #include "kudu/util/jwt-util-internal.h"
+#include "kudu/util/jwt_test_certs.h"
 #include "kudu/util/net/sockaddr.h"
 #include "kudu/util/slice.h"
 #include "kudu/util/status.h"
@@ -50,319 +51,6 @@ using std::unique_ptr;
 using std::vector;
 using strings::Substitute;
 
-const std::string kRsaPrivKeyPem = R"(-----BEGIN PRIVATE KEY-----
-MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4ZtdaIrd1BPIJ
-tfnF0TjIK5inQAXZ3XlCrUlJdP+XHwIRxdv1FsN12XyMYO/6ymLmo9ryoQeIrsXB
-XYqlET3zfAY+diwCb0HEsVvhisthwMU4gZQu6TYW2s9LnXZB5rVtcBK69hcSlA2k
-ZudMZWxZcj0L7KMfO2rIvaHw/qaVOE9j0T257Z8Kp2CLF9MUgX0ObhIsdumFRLaL
-DvDUmBPr2zuh/34j2XmWwn1yjN/WvGtdfhXW79Ki1S40HcWnygHgLV8sESFKUxxQ
-mKvPUTwDOIwLFL5WtE8Mz7N++kgmDcmWMCHc8kcOIu73Ta/3D4imW7VbKgHZo9+K
-3ESFE3RjAgMBAAECggEBAJTEIyjMqUT24G2FKiS1TiHvShBkTlQdoR5xvpZMlYbN
-tVWxUmrAGqCQ/TIjYnfpnzCDMLhdwT48Ab6mQJw69MfiXwc1PvwX1e9hRscGul36
-ryGPKIVQEBsQG/zc4/L2tZe8ut+qeaK7XuYrPp8bk/X1e9qK5m7j+JpKosNSLgJj
-NIbYsBkG2Mlq671irKYj2hVZeaBQmWmZxK4fw0Istz2WfN5nUKUeJhTwpR+JLUg4
-ELYYoB7EO0Cej9UBG30hbgu4RyXA+VbptJ+H042K5QJROUbtnLWuuWosZ5ATldwO
-u03dIXL0SH0ao5NcWBzxU4F2sBXZRGP2x/jiSLHcqoECgYEA4qD7mXQpu1b8XO8U
-6abpKloJCatSAHzjgdR2eRDRx5PMvloipfwqA77pnbjTUFajqWQgOXsDTCjcdQui
-wf5XAaWu+TeAVTytLQbSiTsBhrnoqVrr3RoyDQmdnwHT8aCMouOgcC5thP9vQ8Us
-rVdjvRRbnJpg3BeSNimH+u9AHgsCgYEA0EzcbOltCWPHRAY7B3Ge/AKBjBQr86Kv
-TdpTlxePBDVIlH+BM6oct2gaSZZoHbqPjbq5v7yf0fKVcXE4bSVgqfDJ/sZQu9Lp
-PTeV7wkk0OsAMKk7QukEpPno5q6tOTNnFecpUhVLLlqbfqkB2baYYwLJR3IRzboJ
-FQbLY93E8gkCgYB+zlC5VlQbbNqcLXJoImqItgQkkuW5PCgYdwcrSov2ve5r/Acz
-FNt1aRdSlx4176R3nXyibQA1Vw+ztiUFowiP9WLoM3PtPZwwe4bGHmwGNHPIfwVG
-m+exf9XgKKespYbLhc45tuC08DATnXoYK7O1EnUINSFJRS8cezSI5eHcbQKBgQDC
-PgqHXZ2aVftqCc1eAaxaIRQhRmY+CgUjumaczRFGwVFveP9I6Gdi+Kca3DE3F9Pq
-PKgejo0SwP5vDT+rOGHN14bmGJUMsX9i4MTmZUZ5s8s3lXh3ysfT+GAhTd6nKrIE
-kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe
-RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb
-vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX
-rK0/Ikt5ybqUzKCMJZg2VKGTxg==
------END PRIVATE KEY-----)";
-const std::string kRsaPubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4
-yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9
-83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs
-WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT
-69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8
-AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0
-YwIDAQAB
------END PUBLIC KEY-----)";
-// The public keys in JWK format were converted from PEM formatted crypto keys with
-// pem-to-jwk tool at https://hub.docker.com/r/danedmunds/pem-to-jwk/
-const std::string kRsaPubKeyJwkN =
-    "uGbXWiK3dQTyCbX5xdE4yCuYp0AF2d15Qq1JSXT_lx8CEcXb9RbDddl8jGDv-sp"
-    "i5qPa8qEHiK7FwV2KpRE983wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qe"
-    "a1bXASuvYXEpQNpGbnTGVsWXI9C-yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTF"
-    "IF9Dm4SLHbphUS2iw7w1JgT69s7of9-I9l5lsJ9cozf1rxrXX4V1u_SotUuNB3F"
-    "p8oB4C1fLBEhSlMcUJirz1E8AziMCxS-VrRPDM-zfvpIJg3JljAh3PJHDiLu902"
-    "v9w-Iplu1WyoB2aPfitxEhRN0Yw";
-const std::string kRsaPubKeyJwkE = "AQAB";
-const std::string kRsaInvalidPubKeyJwkN =
-    "xzYuc22QSst_dS7geYYK5l5kLxU0tayNdixkEQ17ix-CUcUbKIsnyftZxaCYT46"
-    "rQtXgCaYRdJcbB3hmyrOavkhTpX79xJZnQmfuamMbZBqitvscxW9zRR9tBUL6vd"
-    "i_0rpoUwPMEh8-Bw7CgYR0FK0DhWYBNDfe9HKcyZEv3max8Cdq18htxjEsdYO0i"
-    "wzhtKRXomBWTdhD5ykd_fACVTr4-KEY-IeLvubHVmLUhbE5NgWXxrRpGasDqzKh"
-    "CTmsa2Ysf712rl57SlH0Wz_Mr3F7aM9YpErzeYLrl0GhQr9BVJxOvXcVd4kmY-X"
-    "kiCcrkyS1cnghnllh-LCwQu1sYw";
-
-const std::string kRsa512PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
-MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw
-33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW
-+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB
-AoGAD+onAtVye4ic7VR7V50DF9bOnwRwNXrARcDhq9LWNRrRGElESYYTQ6EbatXS
-3MCyjjX2eMhu/aF5YhXBwkppwxg+EOmXeh+MzL7Zh284OuPbkglAaGhV9bb6/5Cp
-uGb1esyPbYW+Ty2PC0GSZfIXkXs76jXAu9TOBvD0ybc2YlkCQQDywg2R/7t3Q2OE
-2+yo382CLJdrlSLVROWKwb4tb2PjhY4XAwV8d1vy0RenxTB+K5Mu57uVSTHtrMK0
-GAtFr833AkEA6avx20OHo61Yela/4k5kQDtjEf1N0LfI+BcWZtxsS3jDM3i1Hp0K
-Su5rsCPb8acJo5RO26gGVrfAsDcIXKC+bQJAZZ2XIpsitLyPpuiMOvBbzPavd4gY
-6Z8KWrfYzJoI/Q9FuBo6rKwl4BFoToD7WIUS+hpkagwWiz+6zLoX1dbOZwJACmH5
-fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523
-Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP
-FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==
------END RSA PRIVATE KEY-----)";
-const std::string kRsa512PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
-UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
-HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D
-o2kQ+X5xK9cipRgEKwIDAQAB
------END PUBLIC KEY-----)";
-const std::string kRsa512PubKeyJwkN =
-    "3ZWrUY0Y6IKN1qI4BhxR2C7oHVFgGPYkd38uGq1jQNSqEvJFcN93CYm16_G78FA"
-    "FKWqwsJb3Wx-nbxDn6LtP4AhULB1H0K0g7_jLklDAHvI8yhOKlvoyvsUFPWtNxl"
-    "Jyh5JJXvkNKV_4Oo12e69f8QCuQ6NpEPl-cSvXIqUYBCs";
-const std::string kRsa512PubKeyJwkE = "AQAB";
-const std::string kRsa512InvalidPubKeyJwkN =
-    "xzYuc22QSst_dS7geYYK5l5kLxU0tayNdixkEQ17ix-CUcUbKIsnyftZxaCYT46"
-    "rQtXgCaYRdJcbB3hmyrOavkhTpX79xJZnQmfuamMbZBqitvscxW9zRR9tBUL6vd"
-    "i_0rpoUwPMEh8-Bw7CgYR0FK0DhWYBNDfe9HKcyZEv3max8Cdq18htxjEsdYO0i"
-    "wzhtKRXomBWTdhD5ykd_fACVTr4-KEY-IeLvubHVmLUhbE5NgWXxrRpGasDqzKh"
-    "CTmsa2Ysf712rl57SlH0Wz_Mr3F7aM9YpErzeYLrl0GhQr9BVJxOvXcVd4kmY-X"
-    "kiCcrkyS1cnghnllh-LCwQu1sYw";
-
-const std::string kRsa1024PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
-MIICXgIBAAKBgQDT+6sb2SvN69NB+6Zg78B7mdke0tC91CTfixzCSn7wS8JUvvZK
-AO1uMgnrCQdDr2TNeRYr6urawIOCDB1Ybz1+cBSNxouVdt/aT9+cw27kzVQE59NA
-PMpQyLtXaAOR6rD8xzyIgAV12QFmc1kHFl7Sjobwmsu5ZWRqYTwdXvFXIQIDAQAB
-AoGBAJKDLxBgWVZJ2AmS1LvK+U50VwxmyL9rENEwZQAkXPfYZMgN9EvRuEihbRl1
-c//kCde6CQjxpMDsrfgER4QH3odypQWT9A5uXKcdfu/z+xKNtB813rSrew3Q9pXe
-wlOb0q7EcS7XHMrcPxj4gvn2yKqB40vF3TIY6oiSeZbFLUvBAkEA9NaTrGB1+FZj
-+3lIAs7UtYbxNggX53OEcXlstDbqhG3O9SzAHiccMbGu2lDBcAAghmtg9poT0Uo6
-V3VCJcnfNwJBAN2lppZFVWAXOLD2k8OMCp4jc9pRHIUtPU6kWoflU8O6kuDNNamD
-AeNMhdHX+Ed/Js3ig75eAGxsd9q+CFp/uGcCQQDFfGb0/YFqZFSVPMhm62oLWeMq
-T/DoEfdciDK0Ui9rzh7HB+eW6rkFJGsDUWwV6SRTCD3X64PcpuDUNpK6ZFCVAkEA
-oaBgAAiDH1UPpAvK6LfALl0P6E1pjLvWjvhOg/Z4xKvS21cJIJlF0ShGFSV2CTzx
-YQUiqLkHegkGxV353XRxVQJAZaW5O2BI5jKy2hK0EoAx3pSnp2X4CmkWrXsSeOgC
-Zz+jDkn8QzPbRwb8cyks/IHc2CBvaFStLFKO2VQj1THDhw==
------END RSA PRIVATE KEY-----)";
-const std::string kRsa1024PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDT+6sb2SvN69NB+6Zg78B7mdke
-0tC91CTfixzCSn7wS8JUvvZKAO1uMgnrCQdDr2TNeRYr6urawIOCDB1Ybz1+cBSN
-xouVdt/aT9+cw27kzVQE59NAPMpQyLtXaAOR6rD8xzyIgAV12QFmc1kHFl7Sjobw
-msu5ZWRqYTwdXvFXIQIDAQAB
------END PUBLIC KEY-----)";
-const std::string kRsa1024PubKeyJwkN =
-    "0_urG9krzevTQfumYO_Ae5nZHtLQvdQk34scwkp-8EvCVL72SgDtbjIJ6wkHQ69"
-    "kzXkWK-rq2sCDggwdWG89fnAUjcaLlXbf2k_fnMNu5M1UBOfTQDzKUMi7V2gDke"
-    "qw_Mc8iIAFddkBZnNZBxZe0o6G8JrLuWVkamE8HV7xVyE";
-const std::string kRsa1024PubKeyJwkE = "AQAB";
-
-const std::string kRsa2048PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
-MIIEpQIBAAKCAQEA0jCHomsNIaRYVlsemWg9yBx3od1B9Fd9RUslk9IVE7IU+QYZ
-+T4NvRVPAMjpzuurvPnN4uBVPREycXOgEcWHiJDDQEhlQD4F69W8MFE7SXpdcBih
-zcj5qPYtTFP/52s6Vg7Y8SAUkBDyr0B442ONR1SBD8qEMAxpiLMH1Q/Yap+etvIj
-D1r2zQkQke53An9LvVl7OKkM8KGOcE/0tJRmc7x8ZlLqogPczkXvW6T+YAkwA8Xw
-ginZw0xBzfpoOEnajqm4Yikck0gJ0HwdlYFIp72ih7uozne7PYLVGb9X97cL0H1X
-DiA/SXJiFKo1AKXihcOdIRiw49eo9rzsoWPygQIDAQABAoIBAADe2BT1XgojYNqc
-s9P9UUeEof80Mst6WEe4RQknb9RozVBEX55Ut4sEAqjVbC3MnpBgtXhTfFmNem4W
-BUCa7DyFzZ/fcjc8T9sh7mQB1h3FXraHN5ZUrH9auPsjBuvfBGW/rSjUfJlQefzS
-psgu950Rwxtnt+PuDTrWc6QaKx0ylvESKPIaVoticc11Kcts5Fe/RQ2Az2epDDM7
-ptZamvtzptozPPq5YUIvpSnKCJfzOczAQT4omVewJV/7nbo/MdCALExrqHcIqXFp
-2uMpHV1QhqZ160Bzf1O+iDRCxT3rd4OZ5Y68x/fYV8dRqrqPA5BFep6ukf17cnWM
-svDqsaUCgYEA+Z5RbadUKteAM3v1Deu9RG7TucnxyoNSofpEuwMoVxo3+z+dS44v
-UpC7/MJhx1FBf15yKSPIgtjt5o/LanApcJEZVyghucsNvqy11db027P63NkIL/ic
-AgB04odLvxpgLHNv/qEWy7zHBLHhcazajzDHW+a/xBXrtJa3i2G+poUCgYEA15Ap
-OJPafAx/BPMbrYthpd5pVX5AMExXTur7rMIPi4/wh0O0vqGtulwgX3FiS0X4bAzK
-tNJ23/V2RR0F16IAIVZQqt16pIvmhx52iC55EPp3bZWkGhZ33/8Dxzkbe+rlwECa
-wRK4dOyA9hwsnlRuEb8OHva6sr+EusOxmeN6Us0CgYEAg4O/QTe057GM0RNRJFl8
-6a4+jRdx9hHEmqTCS4m5WlLtBcoZdLJgCm9JLD25yIruKE45daVtwkrK5PwD33ti
-yfUY1cvGIR5zim9yikzry0mDNZJ/ds7UW1WkP6mq5e/elezoJ871tLgsXzPdJMg+
-iszXbHshtA0cl5QE9kG0cgUCgYEAzZf3WLjbxzh75RKhMVIgnfyU5i91tRr6opBH
-3atw/CEavUf8GV1GvtmjHqSbpUNk/ljs9K1PJ6eLV7uomNMv4JvccDqxAENWaUTK
-tHPukBzyzxfL3f3T81XcGqUC65tL6aM0djUOrKXtEc4pWBEasd5Q74NO6bD0PNTs
-jOODBXkCgYEAhaD3gZUCWU+ZA6QmxPotfe9L0tzjmUjsLo0QUgIHJa2VaoHzdnWC
-ClvP3tFFkv2dlD6UW+g0JJFTVWcv+HEiC9WUnD/C6dXK/qA3fRvBhRKy8FTwvOis
-zSVeYds6mvDJwFe+2mk0KQiKnxlx22B4PcYbbN7mZ2ClBFTFrp0+Id4=
------END RSA PRIVATE KEY-----)";
-const std::string kRsa2048PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0jCHomsNIaRYVlsemWg9
-yBx3od1B9Fd9RUslk9IVE7IU+QYZ+T4NvRVPAMjpzuurvPnN4uBVPREycXOgEcWH
-iJDDQEhlQD4F69W8MFE7SXpdcBihzcj5qPYtTFP/52s6Vg7Y8SAUkBDyr0B442ON
-R1SBD8qEMAxpiLMH1Q/Yap+etvIjD1r2zQkQke53An9LvVl7OKkM8KGOcE/0tJRm
-c7x8ZlLqogPczkXvW6T+YAkwA8XwginZw0xBzfpoOEnajqm4Yikck0gJ0HwdlYFI
-p72ih7uozne7PYLVGb9X97cL0H1XDiA/SXJiFKo1AKXihcOdIRiw49eo9rzsoWPy
-gQIDAQAB
------END PUBLIC KEY-----)";
-const std::string kRsa2048PubKeyJwkN =
-    "0jCHomsNIaRYVlsemWg9yBx3od1B9Fd9RUslk9IVE7IU-QYZ-T4NvRVPAMjpzuu"
-    "rvPnN4uBVPREycXOgEcWHiJDDQEhlQD4F69W8MFE7SXpdcBihzcj5qPYtTFP_52"
-    "s6Vg7Y8SAUkBDyr0B442ONR1SBD8qEMAxpiLMH1Q_Yap-etvIjD1r2zQkQke53A"
-    "n9LvVl7OKkM8KGOcE_0tJRmc7x8ZlLqogPczkXvW6T-YAkwA8XwginZw0xBzfpo"
-    "OEnajqm4Yikck0gJ0HwdlYFIp72ih7uozne7PYLVGb9X97cL0H1XDiA_SXJiFKo"
-    "1AKXihcOdIRiw49eo9rzsoWPygQ";
-const std::string kRsa2048PubKeyJwkE = "AQAB";
-
-const std::string kRsa4096PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
-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
------END RSA PRIVATE KEY-----)";
-const std::string kRsa4096PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtxmYsvs6ZfhTCFKCHQBW
-/W3iRfh8wZN+/XPXaOiIx9SXYSFrb/WRaTn8UOvflYuRnPYMaRGr5gVTS6/WFVvt
-NuZVIDOQBgEOBt5MQ0BeM0yPiM6qacP15couRwxbJx45ODQNyh5jNF4SdzqThNFT
-CFHtWakL1qrkGNSKdowIMaM59dm58liMHxp9h9yTmqM4ZgiZkoF6Vy4KYrg9ChVq
-UZze4KMiyow8Xv6ESM7Eg2ncTbRevuedbtYv7OVlotyozt1geFkWm/8ZUA6Z68lf
-tYMySq0/yjAmjql0DXP1+vPL9k5sKGr5lpIUlB7a9JWbXdepNjvy1vslFuLjcM50
-9d1E8e70C5VF61XthKlk3rRIBWK80XupWR7o6clJsMYKxeF+ImBbzDbWrIkMxe0v
-TbikS6S4CLPVlYx4sMWAWu4UBpxZquw4cVxyiKoZ2j5yTMAD1xiHI/b2/psFzW3q
-XcgQWTY7dpIP1BInhepCzHlcLSEiHtmMoCXNC3+i9ZXxiJ1u8avYfGjH8RrJW8dv
-Vw7BS7zlH9s7rCn001VBJCJcXtkGaykw9Zd1E+Jh7IKQJn8gydsQ0enlMmtwsJO/
-tEvYBojFXbl4XecMWADTiExjXobX1y7u9ZTn0KRNkPpX9GTgY3oR0ei+rwOr4d+k
-2CrUdkMTGfjnfcDHKjHh3LMCAwEAAQ==
------END PUBLIC KEY-----)";
-const std::string kRsa4096PubKeyJwkN =
-    "txmYsvs6ZfhTCFKCHQBW_W3iRfh8wZN-_XPXaOiIx9SXYSFrb_WRaTn8UOvflYu"
-    "RnPYMaRGr5gVTS6_WFVvtNuZVIDOQBgEOBt5MQ0BeM0yPiM6qacP15couRwxbJx"
-    "45ODQNyh5jNF4SdzqThNFTCFHtWakL1qrkGNSKdowIMaM59dm58liMHxp9h9yTm"
-    "qM4ZgiZkoF6Vy4KYrg9ChVqUZze4KMiyow8Xv6ESM7Eg2ncTbRevuedbtYv7OVl"
-    "otyozt1geFkWm_8ZUA6Z68lftYMySq0_yjAmjql0DXP1-vPL9k5sKGr5lpIUlB7"
-    "a9JWbXdepNjvy1vslFuLjcM509d1E8e70C5VF61XthKlk3rRIBWK80XupWR7o6c"
-    "lJsMYKxeF-ImBbzDbWrIkMxe0vTbikS6S4CLPVlYx4sMWAWu4UBpxZquw4cVxyi"
-    "KoZ2j5yTMAD1xiHI_b2_psFzW3qXcgQWTY7dpIP1BInhepCzHlcLSEiHtmMoCXN"
-    "C3-i9ZXxiJ1u8avYfGjH8RrJW8dvVw7BS7zlH9s7rCn001VBJCJcXtkGaykw9Zd"
-    "1E-Jh7IKQJn8gydsQ0enlMmtwsJO_tEvYBojFXbl4XecMWADTiExjXobX1y7u9Z"
-    "Tn0KRNkPpX9GTgY3oR0ei-rwOr4d-k2CrUdkMTGfjnfcDHKjHh3LM";
-const std::string kRsa4096PubKeyJwkE = "AQAB";
-
-const std::string kEcdsa521PrivKeyPem = R"(-----BEGIN EC PRIVATE KEY-----
-MIHcAgEBBEIAuZxTZjLIZM5hxgZX+JRrqt5FKpAEg/meZ7m9aSE3XbRITqtfz1Uy
-h2Srn7o8+4j/jQpwHTTHZThy10u5jMjaR+mgBwYFK4EEACOhgYkDgYYABAFFah0k
-6m4ddp/tUN/ObrKKwSCp4QUZdiAMaC9eY1HyNBPuuEsH5qCfeY5lmeJwSUpzCosn
-rgW8M2hQ4Kr5V9OXrgHLA5WVtH6//sSkUY2/xYuqc7/Ln8gI5ddtr1qG64Xtgs05
-/CNajSjFZeLm76llakvYiBTTH/ii8hIfrwukW9IP7Q==
------END EC PRIVATE KEY-----)";
-const std::string kEcdsa521PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBRWodJOpuHXaf7VDfzm6yisEgqeEF
-GXYgDGgvXmNR8jQT7rhLB+agn3mOZZnicElKcwqLJ64FvDNoUOCq+VfTl64BywOV
-lbR+v/7EpFGNv8WLqnO/y5/ICOXXba9ahuuF7YLNOfwjWo0oxWXi5u+pZWpL2IgU
-0x/4ovISH68LpFvSD+0=
------END PUBLIC KEY-----)";
-const std::string kEcdsa521PubKeyJwkX =
-    "AUVqHSTqbh12n-1Q385usorBIKnhBRl2IAxoL15jUfI0E-64SwfmoJ95jmWZ4nB"
-    "JSnMKiyeuBbwzaFDgqvlX05eu";
-const std::string kEcdsa521PubKeyJwkY =
-    "AcsDlZW0fr_-xKRRjb_Fi6pzv8ufyAjl122vWobrhe2CzTn8I1qNKMVl4ubvqWV"
-    "qS9iIFNMf-KLyEh-vC6Rb0g_t";
-
-const std::string kEcdsa384PrivKeyPem = R"(-----BEGIN EC PRIVATE KEY-----
-MIGkAgEBBDCrPXJDgQDtNRpM0qNUW/zN1vrCvOVH1CsItVZ+1NeGB+w/2whnIXJQ
-K7U5C1ETPHagBwYFK4EEACKhZANiAAR0JjvVJXc3u1I/7vt5mxzPtAIi1VIqxCwN
-wgISZVySTYZQzyicW2GfhMlFCow28LzqTwH/eCymAvnTAmpK/P1hXhNcnxDBZNOU
-WMbMLFcQrg2wwpIb/k/IXobNwjNPRBo=
------END EC PRIVATE KEY-----)";
-const std::string kEcdsa384PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdCY71SV3N7tSP+77eZscz7QCItVSKsQs
-DcICEmVckk2GUM8onFthn4TJRQqMNvC86k8B/3gspgL50wJqSvz9YV4TXJ8QwWTT
-lFjGzCxXEK4NsMKSG/5PyF6GzcIzT0Qa
------END PUBLIC KEY-----)";
-const std::string kEcdsa384PubKeyJwkX =
-    "dCY71SV3N7tSP-77eZscz7QCItVSKsQsDcICEmVckk2GUM8onFthn4TJRQqMNvC8";
-const std::string kEcdsa384PubKeyJwkY =
-    "6k8B_3gspgL50wJqSvz9YV4TXJ8QwWTTlFjGzCxXEK4NsMKSG_5PyF6GzcIzT0Qa";
-
-const std::string kEcdsa256PrivKeyPem = R"(-----BEGIN PRIVATE KEY-----
-MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPGJGAm4X1fvBuC1z
-SpO/4Izx6PXfNMaiKaS5RUkFqEGhRANCAARCBvmeksd3QGTrVs2eMrrfa7CYF+sX
-sjyGg+Bo5mPKGH4Gs8M7oIvoP9pb/I85tdebtKlmiCZHAZE5w4DfJSV6
------END PRIVATE KEY-----)";
-const std::string kEcdsa256PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQgb5npLHd0Bk61bNnjK632uwmBfr
-F7I8hoPgaOZjyhh+BrPDO6CL6D/aW/yPObXXm7SpZogmRwGROcOA3yUleg==
------END PUBLIC KEY-----)";
-const std::string kEcdsa256PubKeyJwkX = "Qgb5npLHd0Bk61bNnjK632uwmBfrF7I8hoPgaOZjyhg";
-const std::string kEcdsa256PubKeyJwkY = "fgazwzugi-g_2lv8jzm115u0qWaIJkcBkTnDgN8lJXo";
-
-const std::string kKid1 = "public:c424b67b-fe28-45d7-b015-f79da50b5b21";
-const std::string kKid2 = "public:9b9d0b47-b9ed-4ba6-9180-52fc5b161a3a";
-
-const std::string kJwksHsFileFormat = R"(
-{
-  "keys": [
-    { "kty": "oct", "kid": "$0", "alg": "$1", "k": "$2" }
-  ]
-})";
-
-const std::string kJwksRsaFileFormat = R"(
-{
-  "keys": [
-    { "kty": "RSA", "kid": "$0", "alg": "$1", "n": "$2", "e": "$3" },
-    { "kty": "RSA", "kid": "$4", "alg": "$5", "n": "$6", "e": "$7" }
-  ]
-})";
-
-const std::string kJwksEcFileFormat = R"(
-{
-  "keys": [
-    { "kty": "EC", "kid": "$0", "crv": "$1", "x": "$2", "y": "$3" }
-  ]
-})";
-
 /// Utility class for creating a file that will be automatically deleted upon test
 /// completion.
 class TempTestDataFile {
@@ -503,7 +191,7 @@ TEST(JwtUtilTest, VerifyJwtHS256) {
                    .set_type("JWS")
                    .set_algorithm("HS256")
                    .set_key_id(kKid1)
-                   .set_payload_claim("username", picojson::value("impala"))
+                   .set_payload_claim("username", picojson::value("kudu"))
                    .sign(jwt::algorithm::hs256(shared_secret));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -516,7 +204,7 @@ TEST(JwtUtilTest, VerifyJwtHS256) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtHS384) {
@@ -542,7 +230,7 @@ TEST(JwtUtilTest, VerifyJwtHS384) {
                    .set_type("JWS")
                    .set_algorithm("HS384")
                    .set_key_id(kKid1)
-                   .set_payload_claim("username", picojson::value("impala"))
+                   .set_payload_claim("username", picojson::value("kudu"))
                    .sign(jwt::algorithm::hs384(shared_secret));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -555,7 +243,7 @@ TEST(JwtUtilTest, VerifyJwtHS384) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtHS512) {
@@ -582,7 +270,7 @@ TEST(JwtUtilTest, VerifyJwtHS512) {
                    .set_type("JWS")
                    .set_algorithm("HS512")
                    .set_key_id(kKid1)
-                   .set_payload_claim("username", picojson::value("impala"))
+                   .set_payload_claim("username", picojson::value("kudu"))
                    .sign(jwt::algorithm::hs512(shared_secret));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -595,7 +283,7 @@ TEST(JwtUtilTest, VerifyJwtHS512) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtRS256) {
@@ -620,16 +308,15 @@ TEST(JwtUtilTest, VerifyJwtRS256) {
           .set_type("JWS")
           .set_algorithm("RS256")
           .set_key_id(kKid1)
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::rs256(kRsaPubKeyPem, kRsaPrivKeyPem, "", ""));
   ASSERT_EQ(
-      "eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpjNDI0YjY3Yi1mZTI4LTQ1ZDctYjAxNS1mNzlkYTUwYj"
-      "ViMjEiLCJ0eXAiOiJKV1MifQ.eyJpc3MiOiJhdXRoMCIsInVzZXJuYW1lIjoiaW1wYWxhIn0.OW5H2SClL"
-      "lsotsCarTHYEbqlbRh43LFwOyo9WubpNTwE7hTuJDsnFoVrvHiWI02W69TZNat7DYcC86A_ogLMfNXagHj"
-      "lMFJaRnvG5Ekag8NRuZNJmHVqfX-qr6x7_8mpOdU554kc200pqbpYLhhuK4Qf7oT7y9mOrtNrUKGDCZ0Q2"
-      "y_mizlbY6SMg4RWqSz0RQwJbRgXIWSgcbZd0GbD_MQQ8x7WRE4nluU-5Fl4N2Wo8T9fNTuxALPiuVeIczO"
-      "25b5n4fryfKasSgaZfmk0CoOJzqbtmQxqiK9QNSJAiH2kaqMwLNgAdgn8fbd-lB1RAEGeyPH8Px8ipqcKs"
-      "Pk0bg",
+      "eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpjNDI0YjY3Yi1mZTI4LTQ1ZDctYjAxNS1mNzlkYTUwYjViMjEiLCJ0"
+      "eXAiOiJKV1MifQ.eyJpc3MiOiJhdXRoMCIsInVzZXJuYW1lIjoia3VkdSJ9.OcAO1KFnYMyVyMXWqeWKlvZIDIYmeQYZ"
+      "hMYcjOuGi5KKuBF5J7IlSl14EM6EhswQJ54pP8EPhMyHHNqncUOPt-QN9foS39aA5XpNPqOOWShQvZLQweEsogjfab66"
+      "gWPO7baXGg8npqMBxpnvM5mjz4TIg6kwT4R_9p1NBGYmU5DQS6_jb7OfpMxY8bezKwL_iJB9yPgTlgZA5IJ0DPkIydcQ"
+      "ejz3ycLy-75G8GWK78WgtOq2ejwpCsrPo3QlaqQH1reDPBit_2xme8ypwgGztc3Nss1ZF8g5U69WTdhP2Dy5k7iFXKua"
+      "PHD5HBAFJiP1KVWMpuGX_POewU_ibt7v8g",
       token);
 
   // Verify the JWT token with jwt-cpp APIs directly.
@@ -649,7 +336,7 @@ TEST(JwtUtilTest, VerifyJwtRS256) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtRS384) {
@@ -674,7 +361,7 @@ TEST(JwtUtilTest, VerifyJwtRS384) {
           .set_type("JWS")
           .set_algorithm("RS384")
           .set_key_id(kKid1)
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::rs384(kRsaPubKeyPem, kRsaPrivKeyPem, "", ""));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -687,7 +374,7 @@ TEST(JwtUtilTest, VerifyJwtRS384) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtRS512) {
@@ -712,7 +399,7 @@ TEST(JwtUtilTest, VerifyJwtRS512) {
           .set_type("JWS")
           .set_algorithm("RS512")
           .set_key_id(kKid1)
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::rs512(kRsa512PubKeyPem, kRsa512PrivKeyPem, "", ""));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -725,7 +412,7 @@ TEST(JwtUtilTest, VerifyJwtRS512) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtPS256) {
@@ -750,7 +437,7 @@ TEST(JwtUtilTest, VerifyJwtPS256) {
           .set_type("JWS")
           .set_algorithm("PS256")
           .set_key_id(kKid1)
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::ps256(kRsa1024PubKeyPem, kRsa1024PrivKeyPem, "", ""));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -763,7 +450,7 @@ TEST(JwtUtilTest, VerifyJwtPS256) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtPS384) {
@@ -788,7 +475,7 @@ TEST(JwtUtilTest, VerifyJwtPS384) {
           .set_type("JWS")
           .set_algorithm("PS384")
           .set_key_id(kKid1)
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::ps384(kRsa2048PubKeyPem, kRsa2048PrivKeyPem, "", ""));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -801,7 +488,7 @@ TEST(JwtUtilTest, VerifyJwtPS384) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtPS512) {
@@ -826,7 +513,7 @@ TEST(JwtUtilTest, VerifyJwtPS512) {
           .set_type("JWS")
           .set_algorithm("PS512")
           .set_key_id(kKid1)
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::ps512(kRsa4096PubKeyPem, kRsa4096PrivKeyPem, "", ""));
 
   // Verify the JWT token with our wrapper class which use public key retrieved from JWKS,
@@ -839,7 +526,7 @@ TEST(JwtUtilTest, VerifyJwtPS512) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtES256) {
@@ -862,7 +549,7 @@ TEST(JwtUtilTest, VerifyJwtES256) {
                    .set_type("JWS")
                    .set_algorithm("ES256")
                    .set_key_id(kKid1)
-                   .set_payload_claim("username", picojson::value("impala"))
+                   .set_payload_claim("username", picojson::value("kudu"))
                    .sign(jwt::algorithm::es256(
                        kEcdsa256PubKeyPem, kEcdsa256PrivKeyPem, "", ""));
 
@@ -884,7 +571,7 @@ TEST(JwtUtilTest, VerifyJwtES256) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtES384) {
@@ -907,7 +594,7 @@ TEST(JwtUtilTest, VerifyJwtES384) {
                    .set_type("JWS")
                    .set_algorithm("ES384")
                    .set_key_id(kKid1)
-                   .set_payload_claim("username", picojson::value("impala"))
+                   .set_payload_claim("username", picojson::value("kudu"))
                    .sign(jwt::algorithm::es384(
                        kEcdsa384PubKeyPem, kEcdsa384PrivKeyPem, "", ""));
 
@@ -921,7 +608,7 @@ TEST(JwtUtilTest, VerifyJwtES384) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtES512) {
@@ -944,7 +631,7 @@ TEST(JwtUtilTest, VerifyJwtES512) {
                    .set_type("JWS")
                    .set_algorithm("ES512")
                    .set_key_id(kKid1)
-                   .set_payload_claim("username", picojson::value("impala"))
+                   .set_payload_claim("username", picojson::value("kudu"))
                    .sign(jwt::algorithm::es512(
                        kEcdsa521PubKeyPem, kEcdsa521PrivKeyPem, "", ""));
 
@@ -958,7 +645,7 @@ TEST(JwtUtilTest, VerifyJwtES512) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtNotVerifySignature) {
@@ -968,7 +655,7 @@ TEST(JwtUtilTest, VerifyJwtNotVerifySignature) {
           .set_issuer("auth0")
           .set_type("JWS")
           .set_algorithm("RS256")
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::rs256(kRsaPubKeyPem, kRsaPrivKeyPem, "", ""));
 
   // Do not verify signature.
@@ -978,7 +665,7 @@ TEST(JwtUtilTest, VerifyJwtNotVerifySignature) {
   string username;
   status = JWTHelper::GetCustomClaimUsername(decoded_token.get(), "username", username);
   EXPECT_OK(status);
-  ASSERT_EQ("impala", username);
+  ASSERT_EQ("kudu", username);
 }
 
 TEST(JwtUtilTest, VerifyJwtFailMismatchingAlgorithms) {
@@ -1125,7 +812,7 @@ TEST(JwtUtilTest, VerifyJwtFailExpiredToken) {
           .set_key_id(kKid1)
           .set_issued_at(std::chrono::system_clock::now())
           .set_expires_at(std::chrono::system_clock::now() - std::chrono::seconds{10})
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_payload_claim("username", picojson::value("kudu"))
           .sign(jwt::algorithm::rs256(kRsaPubKeyPem, kRsaPrivKeyPem, "", ""));
 
   // Verify the token, including expiring time.
@@ -1180,28 +867,26 @@ TEST(JwtUtilTest, VerifyJWKSUrl) {
   JWKSMockServer jwks_server;
   ASSERT_OK(jwks_server.Start());
 
-  JWTHelper jwt_helper;
-  ASSERT_OK(jwt_helper.Init(jwks_server.url(), false));
   auto encoded_token =
       jwt::create()
           .set_issuer("auth0")
-          .set_type("JWS")
+          .set_type("JWT")
           .set_algorithm("RS256")
           .set_key_id(kKid1)
-          .set_payload_claim("username", picojson::value("impala"))
+          .set_subject("kudu")
           .sign(jwt::algorithm::rs256(kRsaPubKeyPem, kRsaPrivKeyPem, "", ""));
   ASSERT_EQ(
-      "eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpjNDI0YjY3Yi1mZTI4LTQ1ZDctYjAxNS1mNzlkYTUwYj"
-      "ViMjEiLCJ0eXAiOiJKV1MifQ.eyJpc3MiOiJhdXRoMCIsInVzZXJuYW1lIjoiaW1wYWxhIn0.OW5H2SClL"
-      "lsotsCarTHYEbqlbRh43LFwOyo9WubpNTwE7hTuJDsnFoVrvHiWI02W69TZNat7DYcC86A_ogLMfNXagHj"
-      "lMFJaRnvG5Ekag8NRuZNJmHVqfX-qr6x7_8mpOdU554kc200pqbpYLhhuK4Qf7oT7y9mOrtNrUKGDCZ0Q2"
-      "y_mizlbY6SMg4RWqSz0RQwJbRgXIWSgcbZd0GbD_MQQ8x7WRE4nluU-5Fl4N2Wo8T9fNTuxALPiuVeIczO"
-      "25b5n4fryfKasSgaZfmk0CoOJzqbtmQxqiK9QNSJAiH2kaqMwLNgAdgn8fbd-lB1RAEGeyPH8Px8ipqcKs"
-      "Pk0bg",
+      "eyJhbGciOiJSUzI1NiIsImtpZCI6InB1YmxpYzpjNDI0YjY3Yi1mZTI4LTQ1ZDctYjAxNS1mNzlkYTUwYjViMjEiLCJ0"
+      "eXAiOiJKV1QifQ.eyJpc3MiOiJhdXRoMCIsInN1YiI6Imt1ZHUifQ.VENjfXICRV1lr2M-jBElI_qaBZNFILjkXr1Amg"
+      "poH8xlI41EFN8RVkihuJtFijUOEFxJ537LCEonDBHsouO9iQlrxh0AobIjB1QraqG1BoQLnKWF78E-rhPN2K1aueGed7"
+      "A86lkIEB4s7VU9dSDtR3bwbP5RFaf3XRZ6TyVh0h5sdMo91YKpS6nLCvYh2OSIbsUJCSNu4BoCmDz97Wq1xLiDoRfAJh"
+      "BZiHQeHO38ydRMWIeto78pV2s9sf1CdwVwycuJOfnKY_-M5-fl1hW_25kSTNt33L57a5BgbGZ1sabWP3AD__-HYD2muR"
+      "klbfyYn_ghqjL7ihY2ECaZzZ0Utw",
       encoded_token);
-  JWTHelper::UniqueJWTDecodedToken decoded_token;
-  ASSERT_OK(jwt_helper.Decode(encoded_token, decoded_token));
-  ASSERT_OK(jwt_helper.Verify(decoded_token.get()));
+  KeyBasedJwtVerifier jwt_verifier(jwks_server.url(), /*is_local_file*/false);
+  string subject;
+  ASSERT_OK(jwt_verifier.VerifyToken(encoded_token, &subject));
+  ASSERT_EQ("kudu", subject);
 }
 
 } // namespace kudu
diff --git a/src/kudu/util/jwt_test_certs.h b/src/kudu/util/jwt_test_certs.h
new file mode 100644
index 000000000..e30d85461
--- /dev/null
+++ b/src/kudu/util/jwt_test_certs.h
@@ -0,0 +1,332 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+#pragma once
+
+#include <string>
+
+const std::string kRsaPrivKeyPem = R"(-----BEGIN PRIVATE KEY-----
+MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC4ZtdaIrd1BPIJ
+tfnF0TjIK5inQAXZ3XlCrUlJdP+XHwIRxdv1FsN12XyMYO/6ymLmo9ryoQeIrsXB
+XYqlET3zfAY+diwCb0HEsVvhisthwMU4gZQu6TYW2s9LnXZB5rVtcBK69hcSlA2k
+ZudMZWxZcj0L7KMfO2rIvaHw/qaVOE9j0T257Z8Kp2CLF9MUgX0ObhIsdumFRLaL
+DvDUmBPr2zuh/34j2XmWwn1yjN/WvGtdfhXW79Ki1S40HcWnygHgLV8sESFKUxxQ
+mKvPUTwDOIwLFL5WtE8Mz7N++kgmDcmWMCHc8kcOIu73Ta/3D4imW7VbKgHZo9+K
+3ESFE3RjAgMBAAECggEBAJTEIyjMqUT24G2FKiS1TiHvShBkTlQdoR5xvpZMlYbN
+tVWxUmrAGqCQ/TIjYnfpnzCDMLhdwT48Ab6mQJw69MfiXwc1PvwX1e9hRscGul36
+ryGPKIVQEBsQG/zc4/L2tZe8ut+qeaK7XuYrPp8bk/X1e9qK5m7j+JpKosNSLgJj
+NIbYsBkG2Mlq671irKYj2hVZeaBQmWmZxK4fw0Istz2WfN5nUKUeJhTwpR+JLUg4
+ELYYoB7EO0Cej9UBG30hbgu4RyXA+VbptJ+H042K5QJROUbtnLWuuWosZ5ATldwO
+u03dIXL0SH0ao5NcWBzxU4F2sBXZRGP2x/jiSLHcqoECgYEA4qD7mXQpu1b8XO8U
+6abpKloJCatSAHzjgdR2eRDRx5PMvloipfwqA77pnbjTUFajqWQgOXsDTCjcdQui
+wf5XAaWu+TeAVTytLQbSiTsBhrnoqVrr3RoyDQmdnwHT8aCMouOgcC5thP9vQ8Us
+rVdjvRRbnJpg3BeSNimH+u9AHgsCgYEA0EzcbOltCWPHRAY7B3Ge/AKBjBQr86Kv
+TdpTlxePBDVIlH+BM6oct2gaSZZoHbqPjbq5v7yf0fKVcXE4bSVgqfDJ/sZQu9Lp
+PTeV7wkk0OsAMKk7QukEpPno5q6tOTNnFecpUhVLLlqbfqkB2baYYwLJR3IRzboJ
+FQbLY93E8gkCgYB+zlC5VlQbbNqcLXJoImqItgQkkuW5PCgYdwcrSov2ve5r/Acz
+FNt1aRdSlx4176R3nXyibQA1Vw+ztiUFowiP9WLoM3PtPZwwe4bGHmwGNHPIfwVG
+m+exf9XgKKespYbLhc45tuC08DATnXoYK7O1EnUINSFJRS8cezSI5eHcbQKBgQDC
+PgqHXZ2aVftqCc1eAaxaIRQhRmY+CgUjumaczRFGwVFveP9I6Gdi+Kca3DE3F9Pq
+PKgejo0SwP5vDT+rOGHN14bmGJUMsX9i4MTmZUZ5s8s3lXh3ysfT+GAhTd6nKrIE
+kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe
+RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb
+vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX
+rK0/Ikt5ybqUzKCMJZg2VKGTxg==
+-----END PRIVATE KEY-----)";
+const std::string kRsaPubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4
+yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9
+83wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qea1bXASuvYXEpQNpGbnTGVs
+WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT
+69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8
+AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0
+YwIDAQAB
+-----END PUBLIC KEY-----)";
+// The public keys in JWK format were converted from PEM formatted crypto keys with
+// pem-to-jwk tool at https://hub.docker.com/r/danedmunds/pem-to-jwk/
+const std::string kRsaPubKeyJwkN =
+    "uGbXWiK3dQTyCbX5xdE4yCuYp0AF2d15Qq1JSXT_lx8CEcXb9RbDddl8jGDv-sp"
+    "i5qPa8qEHiK7FwV2KpRE983wGPnYsAm9BxLFb4YrLYcDFOIGULuk2FtrPS512Qe"
+    "a1bXASuvYXEpQNpGbnTGVsWXI9C-yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTF"
+    "IF9Dm4SLHbphUS2iw7w1JgT69s7of9-I9l5lsJ9cozf1rxrXX4V1u_SotUuNB3F"
+    "p8oB4C1fLBEhSlMcUJirz1E8AziMCxS-VrRPDM-zfvpIJg3JljAh3PJHDiLu902"
+    "v9w-Iplu1WyoB2aPfitxEhRN0Yw";
+const std::string kRsaPubKeyJwkE = "AQAB";
+const std::string kRsaInvalidPubKeyJwkN =
+    "xzYuc22QSst_dS7geYYK5l5kLxU0tayNdixkEQ17ix-CUcUbKIsnyftZxaCYT46"
+    "rQtXgCaYRdJcbB3hmyrOavkhTpX79xJZnQmfuamMbZBqitvscxW9zRR9tBUL6vd"
+    "i_0rpoUwPMEh8-Bw7CgYR0FK0DhWYBNDfe9HKcyZEv3max8Cdq18htxjEsdYO0i"
+    "wzhtKRXomBWTdhD5ykd_fACVTr4-KEY-IeLvubHVmLUhbE5NgWXxrRpGasDqzKh"
+    "CTmsa2Ysf712rl57SlH0Wz_Mr3F7aM9YpErzeYLrl0GhQr9BVJxOvXcVd4kmY-X"
+    "kiCcrkyS1cnghnllh-LCwQu1sYw";
+
+const std::string kRsa512PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQDdlatRjRjogo3WojgGHFHYLugdUWAY9iR3fy4arWNA1KoS8kVw
+33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQsHUfQrSDv+MuSUMAe8jzKE4qW
++jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5Do2kQ+X5xK9cipRgEKwIDAQAB
+AoGAD+onAtVye4ic7VR7V50DF9bOnwRwNXrARcDhq9LWNRrRGElESYYTQ6EbatXS
+3MCyjjX2eMhu/aF5YhXBwkppwxg+EOmXeh+MzL7Zh284OuPbkglAaGhV9bb6/5Cp
+uGb1esyPbYW+Ty2PC0GSZfIXkXs76jXAu9TOBvD0ybc2YlkCQQDywg2R/7t3Q2OE
+2+yo382CLJdrlSLVROWKwb4tb2PjhY4XAwV8d1vy0RenxTB+K5Mu57uVSTHtrMK0
+GAtFr833AkEA6avx20OHo61Yela/4k5kQDtjEf1N0LfI+BcWZtxsS3jDM3i1Hp0K
+Su5rsCPb8acJo5RO26gGVrfAsDcIXKC+bQJAZZ2XIpsitLyPpuiMOvBbzPavd4gY
+6Z8KWrfYzJoI/Q9FuBo6rKwl4BFoToD7WIUS+hpkagwWiz+6zLoX1dbOZwJACmH5
+fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523
+Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP
+FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==
+-----END RSA PRIVATE KEY-----)";
+const std::string kRsa512PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
+UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
+HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D
+o2kQ+X5xK9cipRgEKwIDAQAB
+-----END PUBLIC KEY-----)";
+const std::string kRsa512PubKeyJwkN =
+    "3ZWrUY0Y6IKN1qI4BhxR2C7oHVFgGPYkd38uGq1jQNSqEvJFcN93CYm16_G78FA"
+    "FKWqwsJb3Wx-nbxDn6LtP4AhULB1H0K0g7_jLklDAHvI8yhOKlvoyvsUFPWtNxl"
+    "Jyh5JJXvkNKV_4Oo12e69f8QCuQ6NpEPl-cSvXIqUYBCs";
+const std::string kRsa512PubKeyJwkE = "AQAB";
+const std::string kRsa512InvalidPubKeyJwkN =
+    "xzYuc22QSst_dS7geYYK5l5kLxU0tayNdixkEQ17ix-CUcUbKIsnyftZxaCYT46"
+    "rQtXgCaYRdJcbB3hmyrOavkhTpX79xJZnQmfuamMbZBqitvscxW9zRR9tBUL6vd"
+    "i_0rpoUwPMEh8-Bw7CgYR0FK0DhWYBNDfe9HKcyZEv3max8Cdq18htxjEsdYO0i"
+    "wzhtKRXomBWTdhD5ykd_fACVTr4-KEY-IeLvubHVmLUhbE5NgWXxrRpGasDqzKh"
+    "CTmsa2Ysf712rl57SlH0Wz_Mr3F7aM9YpErzeYLrl0GhQr9BVJxOvXcVd4kmY-X"
+    "kiCcrkyS1cnghnllh-LCwQu1sYw";
+
+const std::string kRsa1024PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDT+6sb2SvN69NB+6Zg78B7mdke0tC91CTfixzCSn7wS8JUvvZK
+AO1uMgnrCQdDr2TNeRYr6urawIOCDB1Ybz1+cBSNxouVdt/aT9+cw27kzVQE59NA
+PMpQyLtXaAOR6rD8xzyIgAV12QFmc1kHFl7Sjobwmsu5ZWRqYTwdXvFXIQIDAQAB
+AoGBAJKDLxBgWVZJ2AmS1LvK+U50VwxmyL9rENEwZQAkXPfYZMgN9EvRuEihbRl1
+c//kCde6CQjxpMDsrfgER4QH3odypQWT9A5uXKcdfu/z+xKNtB813rSrew3Q9pXe
+wlOb0q7EcS7XHMrcPxj4gvn2yKqB40vF3TIY6oiSeZbFLUvBAkEA9NaTrGB1+FZj
++3lIAs7UtYbxNggX53OEcXlstDbqhG3O9SzAHiccMbGu2lDBcAAghmtg9poT0Uo6
+V3VCJcnfNwJBAN2lppZFVWAXOLD2k8OMCp4jc9pRHIUtPU6kWoflU8O6kuDNNamD
+AeNMhdHX+Ed/Js3ig75eAGxsd9q+CFp/uGcCQQDFfGb0/YFqZFSVPMhm62oLWeMq
+T/DoEfdciDK0Ui9rzh7HB+eW6rkFJGsDUWwV6SRTCD3X64PcpuDUNpK6ZFCVAkEA
+oaBgAAiDH1UPpAvK6LfALl0P6E1pjLvWjvhOg/Z4xKvS21cJIJlF0ShGFSV2CTzx
+YQUiqLkHegkGxV353XRxVQJAZaW5O2BI5jKy2hK0EoAx3pSnp2X4CmkWrXsSeOgC
+Zz+jDkn8QzPbRwb8cyks/IHc2CBvaFStLFKO2VQj1THDhw==
+-----END RSA PRIVATE KEY-----)";
+const std::string kRsa1024PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDT+6sb2SvN69NB+6Zg78B7mdke
+0tC91CTfixzCSn7wS8JUvvZKAO1uMgnrCQdDr2TNeRYr6urawIOCDB1Ybz1+cBSN
+xouVdt/aT9+cw27kzVQE59NAPMpQyLtXaAOR6rD8xzyIgAV12QFmc1kHFl7Sjobw
+msu5ZWRqYTwdXvFXIQIDAQAB
+-----END PUBLIC KEY-----)";
+const std::string kRsa1024PubKeyJwkN =
+    "0_urG9krzevTQfumYO_Ae5nZHtLQvdQk34scwkp-8EvCVL72SgDtbjIJ6wkHQ69"
+    "kzXkWK-rq2sCDggwdWG89fnAUjcaLlXbf2k_fnMNu5M1UBOfTQDzKUMi7V2gDke"
+    "qw_Mc8iIAFddkBZnNZBxZe0o6G8JrLuWVkamE8HV7xVyE";
+const std::string kRsa1024PubKeyJwkE = "AQAB";
+
+const std::string kRsa2048PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
+MIIEpQIBAAKCAQEA0jCHomsNIaRYVlsemWg9yBx3od1B9Fd9RUslk9IVE7IU+QYZ
++T4NvRVPAMjpzuurvPnN4uBVPREycXOgEcWHiJDDQEhlQD4F69W8MFE7SXpdcBih
+zcj5qPYtTFP/52s6Vg7Y8SAUkBDyr0B442ONR1SBD8qEMAxpiLMH1Q/Yap+etvIj
+D1r2zQkQke53An9LvVl7OKkM8KGOcE/0tJRmc7x8ZlLqogPczkXvW6T+YAkwA8Xw
+ginZw0xBzfpoOEnajqm4Yikck0gJ0HwdlYFIp72ih7uozne7PYLVGb9X97cL0H1X
+DiA/SXJiFKo1AKXihcOdIRiw49eo9rzsoWPygQIDAQABAoIBAADe2BT1XgojYNqc
+s9P9UUeEof80Mst6WEe4RQknb9RozVBEX55Ut4sEAqjVbC3MnpBgtXhTfFmNem4W
+BUCa7DyFzZ/fcjc8T9sh7mQB1h3FXraHN5ZUrH9auPsjBuvfBGW/rSjUfJlQefzS
+psgu950Rwxtnt+PuDTrWc6QaKx0ylvESKPIaVoticc11Kcts5Fe/RQ2Az2epDDM7
+ptZamvtzptozPPq5YUIvpSnKCJfzOczAQT4omVewJV/7nbo/MdCALExrqHcIqXFp
+2uMpHV1QhqZ160Bzf1O+iDRCxT3rd4OZ5Y68x/fYV8dRqrqPA5BFep6ukf17cnWM
+svDqsaUCgYEA+Z5RbadUKteAM3v1Deu9RG7TucnxyoNSofpEuwMoVxo3+z+dS44v
+UpC7/MJhx1FBf15yKSPIgtjt5o/LanApcJEZVyghucsNvqy11db027P63NkIL/ic
+AgB04odLvxpgLHNv/qEWy7zHBLHhcazajzDHW+a/xBXrtJa3i2G+poUCgYEA15Ap
+OJPafAx/BPMbrYthpd5pVX5AMExXTur7rMIPi4/wh0O0vqGtulwgX3FiS0X4bAzK
+tNJ23/V2RR0F16IAIVZQqt16pIvmhx52iC55EPp3bZWkGhZ33/8Dxzkbe+rlwECa
+wRK4dOyA9hwsnlRuEb8OHva6sr+EusOxmeN6Us0CgYEAg4O/QTe057GM0RNRJFl8
+6a4+jRdx9hHEmqTCS4m5WlLtBcoZdLJgCm9JLD25yIruKE45daVtwkrK5PwD33ti
+yfUY1cvGIR5zim9yikzry0mDNZJ/ds7UW1WkP6mq5e/elezoJ871tLgsXzPdJMg+
+iszXbHshtA0cl5QE9kG0cgUCgYEAzZf3WLjbxzh75RKhMVIgnfyU5i91tRr6opBH
+3atw/CEavUf8GV1GvtmjHqSbpUNk/ljs9K1PJ6eLV7uomNMv4JvccDqxAENWaUTK
+tHPukBzyzxfL3f3T81XcGqUC65tL6aM0djUOrKXtEc4pWBEasd5Q74NO6bD0PNTs
+jOODBXkCgYEAhaD3gZUCWU+ZA6QmxPotfe9L0tzjmUjsLo0QUgIHJa2VaoHzdnWC
+ClvP3tFFkv2dlD6UW+g0JJFTVWcv+HEiC9WUnD/C6dXK/qA3fRvBhRKy8FTwvOis
+zSVeYds6mvDJwFe+2mk0KQiKnxlx22B4PcYbbN7mZ2ClBFTFrp0+Id4=
+-----END RSA PRIVATE KEY-----)";
+const std::string kRsa2048PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0jCHomsNIaRYVlsemWg9
+yBx3od1B9Fd9RUslk9IVE7IU+QYZ+T4NvRVPAMjpzuurvPnN4uBVPREycXOgEcWH
+iJDDQEhlQD4F69W8MFE7SXpdcBihzcj5qPYtTFP/52s6Vg7Y8SAUkBDyr0B442ON
+R1SBD8qEMAxpiLMH1Q/Yap+etvIjD1r2zQkQke53An9LvVl7OKkM8KGOcE/0tJRm
+c7x8ZlLqogPczkXvW6T+YAkwA8XwginZw0xBzfpoOEnajqm4Yikck0gJ0HwdlYFI
+p72ih7uozne7PYLVGb9X97cL0H1XDiA/SXJiFKo1AKXihcOdIRiw49eo9rzsoWPy
+gQIDAQAB
+-----END PUBLIC KEY-----)";
+const std::string kRsa2048PubKeyJwkN =
+    "0jCHomsNIaRYVlsemWg9yBx3od1B9Fd9RUslk9IVE7IU-QYZ-T4NvRVPAMjpzuu"
+    "rvPnN4uBVPREycXOgEcWHiJDDQEhlQD4F69W8MFE7SXpdcBihzcj5qPYtTFP_52"
+    "s6Vg7Y8SAUkBDyr0B442ONR1SBD8qEMAxpiLMH1Q_Yap-etvIjD1r2zQkQke53A"
+    "n9LvVl7OKkM8KGOcE_0tJRmc7x8ZlLqogPczkXvW6T-YAkwA8XwginZw0xBzfpo"
+    "OEnajqm4Yikck0gJ0HwdlYFIp72ih7uozne7PYLVGb9X97cL0H1XDiA_SXJiFKo"
+    "1AKXihcOdIRiw49eo9rzsoWPygQ";
+const std::string kRsa2048PubKeyJwkE = "AQAB";
+
+const std::string kRsa4096PrivKeyPem = R"(-----BEGIN RSA PRIVATE KEY-----
+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
+-----END RSA PRIVATE KEY-----)";
+const std::string kRsa4096PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtxmYsvs6ZfhTCFKCHQBW
+/W3iRfh8wZN+/XPXaOiIx9SXYSFrb/WRaTn8UOvflYuRnPYMaRGr5gVTS6/WFVvt
+NuZVIDOQBgEOBt5MQ0BeM0yPiM6qacP15couRwxbJx45ODQNyh5jNF4SdzqThNFT
+CFHtWakL1qrkGNSKdowIMaM59dm58liMHxp9h9yTmqM4ZgiZkoF6Vy4KYrg9ChVq
+UZze4KMiyow8Xv6ESM7Eg2ncTbRevuedbtYv7OVlotyozt1geFkWm/8ZUA6Z68lf
+tYMySq0/yjAmjql0DXP1+vPL9k5sKGr5lpIUlB7a9JWbXdepNjvy1vslFuLjcM50
+9d1E8e70C5VF61XthKlk3rRIBWK80XupWR7o6clJsMYKxeF+ImBbzDbWrIkMxe0v
+TbikS6S4CLPVlYx4sMWAWu4UBpxZquw4cVxyiKoZ2j5yTMAD1xiHI/b2/psFzW3q
+XcgQWTY7dpIP1BInhepCzHlcLSEiHtmMoCXNC3+i9ZXxiJ1u8avYfGjH8RrJW8dv
+Vw7BS7zlH9s7rCn001VBJCJcXtkGaykw9Zd1E+Jh7IKQJn8gydsQ0enlMmtwsJO/
+tEvYBojFXbl4XecMWADTiExjXobX1y7u9ZTn0KRNkPpX9GTgY3oR0ei+rwOr4d+k
+2CrUdkMTGfjnfcDHKjHh3LMCAwEAAQ==
+-----END PUBLIC KEY-----)";
+const std::string kRsa4096PubKeyJwkN =
+    "txmYsvs6ZfhTCFKCHQBW_W3iRfh8wZN-_XPXaOiIx9SXYSFrb_WRaTn8UOvflYu"
+    "RnPYMaRGr5gVTS6_WFVvtNuZVIDOQBgEOBt5MQ0BeM0yPiM6qacP15couRwxbJx"
+    "45ODQNyh5jNF4SdzqThNFTCFHtWakL1qrkGNSKdowIMaM59dm58liMHxp9h9yTm"
+    "qM4ZgiZkoF6Vy4KYrg9ChVqUZze4KMiyow8Xv6ESM7Eg2ncTbRevuedbtYv7OVl"
+    "otyozt1geFkWm_8ZUA6Z68lftYMySq0_yjAmjql0DXP1-vPL9k5sKGr5lpIUlB7"
+    "a9JWbXdepNjvy1vslFuLjcM509d1E8e70C5VF61XthKlk3rRIBWK80XupWR7o6c"
+    "lJsMYKxeF-ImBbzDbWrIkMxe0vTbikS6S4CLPVlYx4sMWAWu4UBpxZquw4cVxyi"
+    "KoZ2j5yTMAD1xiHI_b2_psFzW3qXcgQWTY7dpIP1BInhepCzHlcLSEiHtmMoCXN"
+    "C3-i9ZXxiJ1u8avYfGjH8RrJW8dvVw7BS7zlH9s7rCn001VBJCJcXtkGaykw9Zd"
+    "1E-Jh7IKQJn8gydsQ0enlMmtwsJO_tEvYBojFXbl4XecMWADTiExjXobX1y7u9Z"
+    "Tn0KRNkPpX9GTgY3oR0ei-rwOr4d-k2CrUdkMTGfjnfcDHKjHh3LM";
+const std::string kRsa4096PubKeyJwkE = "AQAB";
+
+const std::string kEcdsa521PrivKeyPem = R"(-----BEGIN EC PRIVATE KEY-----
+MIHcAgEBBEIAuZxTZjLIZM5hxgZX+JRrqt5FKpAEg/meZ7m9aSE3XbRITqtfz1Uy
+h2Srn7o8+4j/jQpwHTTHZThy10u5jMjaR+mgBwYFK4EEACOhgYkDgYYABAFFah0k
+6m4ddp/tUN/ObrKKwSCp4QUZdiAMaC9eY1HyNBPuuEsH5qCfeY5lmeJwSUpzCosn
+rgW8M2hQ4Kr5V9OXrgHLA5WVtH6//sSkUY2/xYuqc7/Ln8gI5ddtr1qG64Xtgs05
+/CNajSjFZeLm76llakvYiBTTH/ii8hIfrwukW9IP7Q==
+-----END EC PRIVATE KEY-----)";
+const std::string kEcdsa521PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBRWodJOpuHXaf7VDfzm6yisEgqeEF
+GXYgDGgvXmNR8jQT7rhLB+agn3mOZZnicElKcwqLJ64FvDNoUOCq+VfTl64BywOV
+lbR+v/7EpFGNv8WLqnO/y5/ICOXXba9ahuuF7YLNOfwjWo0oxWXi5u+pZWpL2IgU
+0x/4ovISH68LpFvSD+0=
+-----END PUBLIC KEY-----)";
+const std::string kEcdsa521PubKeyJwkX =
+    "AUVqHSTqbh12n-1Q385usorBIKnhBRl2IAxoL15jUfI0E-64SwfmoJ95jmWZ4nB"
+    "JSnMKiyeuBbwzaFDgqvlX05eu";
+const std::string kEcdsa521PubKeyJwkY =
+    "AcsDlZW0fr_-xKRRjb_Fi6pzv8ufyAjl122vWobrhe2CzTn8I1qNKMVl4ubvqWV"
+    "qS9iIFNMf-KLyEh-vC6Rb0g_t";
+
+const std::string kEcdsa384PrivKeyPem = R"(-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDCrPXJDgQDtNRpM0qNUW/zN1vrCvOVH1CsItVZ+1NeGB+w/2whnIXJQ
+K7U5C1ETPHagBwYFK4EEACKhZANiAAR0JjvVJXc3u1I/7vt5mxzPtAIi1VIqxCwN
+wgISZVySTYZQzyicW2GfhMlFCow28LzqTwH/eCymAvnTAmpK/P1hXhNcnxDBZNOU
+WMbMLFcQrg2wwpIb/k/IXobNwjNPRBo=
+-----END EC PRIVATE KEY-----)";
+const std::string kEcdsa384PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdCY71SV3N7tSP+77eZscz7QCItVSKsQs
+DcICEmVckk2GUM8onFthn4TJRQqMNvC86k8B/3gspgL50wJqSvz9YV4TXJ8QwWTT
+lFjGzCxXEK4NsMKSG/5PyF6GzcIzT0Qa
+-----END PUBLIC KEY-----)";
+const std::string kEcdsa384PubKeyJwkX =
+    "dCY71SV3N7tSP-77eZscz7QCItVSKsQsDcICEmVckk2GUM8onFthn4TJRQqMNvC8";
+const std::string kEcdsa384PubKeyJwkY =
+    "6k8B_3gspgL50wJqSvz9YV4TXJ8QwWTTlFjGzCxXEK4NsMKSG_5PyF6GzcIzT0Qa";
+
+const std::string kEcdsa256PrivKeyPem = R"(-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPGJGAm4X1fvBuC1z
+SpO/4Izx6PXfNMaiKaS5RUkFqEGhRANCAARCBvmeksd3QGTrVs2eMrrfa7CYF+sX
+sjyGg+Bo5mPKGH4Gs8M7oIvoP9pb/I85tdebtKlmiCZHAZE5w4DfJSV6
+-----END PRIVATE KEY-----)";
+const std::string kEcdsa256PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQgb5npLHd0Bk61bNnjK632uwmBfr
+F7I8hoPgaOZjyhh+BrPDO6CL6D/aW/yPObXXm7SpZogmRwGROcOA3yUleg==
+-----END PUBLIC KEY-----)";
+const std::string kEcdsa256PubKeyJwkX = "Qgb5npLHd0Bk61bNnjK632uwmBfrF7I8hoPgaOZjyhg";
+const std::string kEcdsa256PubKeyJwkY = "fgazwzugi-g_2lv8jzm115u0qWaIJkcBkTnDgN8lJXo";
+
+const std::string kKid1 = "public:c424b67b-fe28-45d7-b015-f79da50b5b21";
+const std::string kKid2 = "public:9b9d0b47-b9ed-4ba6-9180-52fc5b161a3a";
+
+const std::string kJwksHsFileFormat = R"(
+{
+  "keys": [
+    { "kty": "oct", "kid": "$0", "alg": "$1", "k": "$2" }
+  ]
+})";
+
+const std::string kJwksRsaFileFormat = R"(
+{
+  "keys": [
+    { "kty": "RSA", "kid": "$0", "alg": "$1", "n": "$2", "e": "$3" },
+    { "kty": "RSA", "kid": "$4", "alg": "$5", "n": "$6", "e": "$7" }
+  ]
+})";
+
+const std::string kJwksEcFileFormat = R"(
+{
+  "keys": [
+    { "kty": "EC", "kid": "$0", "crv": "$1", "x": "$2", "y": "$3" }
+  ]
+})";