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/04/28 23:24:04 UTC

[kudu] branch master updated: [util] handle OpenSSL errors in JWT code

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 5a3d116f3 [util] handle OpenSSL errors in JWT code
5a3d116f3 is described below

commit 5a3d116f302bde07e86bf80c237f8a595d5003b4
Author: Alexey Serbin <al...@apache.org>
AuthorDate: Mon Apr 24 22:42:56 2023 -0700

    [util] handle OpenSSL errors in JWT code
    
    While looking at ~50% flakiness stats for the newly added JWT test,
    I found that the root cause of the issue was a non-handled error left
    at the OpenSSL's error stack.
    
    When looking at the code in jwt-util.cc, it turned out the usage of the
    OpenSSL API in ConvertJwkToPem() methods was a bit fragile: possible
    errors returned by OpenSSL functions were not properly handled.
    
    While this patch doesn't fix the flakiness in the JWT test, it addresses
    the issue with unhandled OpenSSL errors in the JWT wrapper code.  I also
    updated the code to enable larger RSA and EC keys to be converted into
    PEM format, and detect if the provided JWK keys are too big.
    
    Change-Id: Iac5142c1dcaeec5042217e1c90f8e8770b36a670
    Reviewed-on: http://gerrit.cloudera.org:8080/19799
    Tested-by: Kudu Jenkins
    Reviewed-by: Wenzhe Zhou <wz...@cloudera.com>
    Reviewed-by: Yuqi Du <sh...@gmail.com>
    Reviewed-by: Zoltan Chovan <zc...@cloudera.com>
    Reviewed-by: Abhishek Chennaka <ac...@cloudera.com>
---
 src/kudu/util/jwt-util-internal.h |  14 +--
 src/kudu/util/jwt-util.cc         | 182 +++++++++++++++++++++-----------------
 src/kudu/util/jwt_test_certs.cc   |  48 ++++++----
 3 files changed, 139 insertions(+), 105 deletions(-)

diff --git a/src/kudu/util/jwt-util-internal.h b/src/kudu/util/jwt-util-internal.h
index 01be925ba..189817e8f 100644
--- a/src/kudu/util/jwt-util-internal.h
+++ b/src/kudu/util/jwt-util-internal.h
@@ -217,9 +217,8 @@ class RSAJWTPublicKeyBuilder {
                                    std::unique_ptr<JWTPublicKey>* pub_key_out);
 
  private:
-  // Convert public key of RSA from JWK format to PEM encoded format by using OpenSSL
-  // APIs.
-  static bool ConvertJwkToPem(
+  // Convert JWK's RSA public key to PEM format using OpenSSL API.
+  static Status ConvertJwkToPem(
       const std::string& base64_n, const std::string& base64_e, std::string& pub_key);
 };
 
@@ -230,10 +229,11 @@ class ECJWTPublicKeyBuilder {
                                    std::unique_ptr<JWTPublicKey>* pub_key_out);
 
  private:
-  // Convert public key of EC from JWK format to PEM encoded format by using OpenSSL
-  // APIs.
-  static bool ConvertJwkToPem(int eccgrp, const std::string& base64_x,
-      const std::string& base64_y, std::string& pub_key);
+  // Convert JWK's EC public key to PEM format using OpenSSL API.
+  static Status ConvertJwkToPem(int eccgrp,
+                                const std::string& base64_x,
+                                const std::string& base64_y,
+                                std::string& pub_key);
 };
 
 // This class load the JWKS from file or URL, store keys in an internal maps for each
diff --git a/src/kudu/util/jwt-util.cc b/src/kudu/util/jwt-util.cc
index 5fd7c582b..209d01c7c 100644
--- a/src/kudu/util/jwt-util.cc
+++ b/src/kudu/util/jwt-util.cc
@@ -34,7 +34,6 @@
 #include <cstring>
 #include <exception>
 #include <functional>
-#include <iterator>
 #include <mutex>
 #include <ostream>
 #include <stdexcept>
@@ -66,13 +65,25 @@
 #include "kudu/util/jwt-util-internal.h"
 #include "kudu/util/monotime.h"
 #include "kudu/util/openssl_util.h"
+#include "kudu/util/openssl_util_bio.h"
 #include "kudu/util/promise.h"
+#include "kudu/util/status.h"
 #include "kudu/util/string_case.h"
 #include "kudu/util/thread.h"
 
+using kudu::security::DataFormat;
+using kudu::security::GetOpenSSLErrors;
+using kudu::security::ToString;
+using kudu::security::c_unique_ptr;
+using kudu::security::ssl_make_unique;
+using rapidjson::Document;
+using rapidjson::Value;
+using std::make_shared;
+using std::shared_ptr;
 using std::string;
 using std::unique_ptr;
 using strings::Substitute;
+using strings::WebSafeBase64Unescape;
 
 DEFINE_int32(jwks_update_frequency_s, 60,
     "The time in seconds to wait between downloading JWKS from the specified URL.");
@@ -92,8 +103,36 @@ DEFINE_validator(jwks_pulling_timeout_s, &ValidateBiggerThanZero);
 
 namespace kudu {
 
-using rapidjson::Document;
-using rapidjson::Value;
+namespace security {
+
+template<> struct SslTypeTraits<BIGNUM> {
+  static constexpr auto kFreeFunc = &BN_free;
+};
+
+// Need this function because of template instantiation, but it's never used.
+int WriteDerFuncNotImplementedEC(BIO* /*ununsed*/, EC_KEY* /*unused*/) {
+  LOG(DFATAL) << "this should never be called";
+  return -1;
+}
+template<> struct SslTypeTraits<EC_KEY> {
+  static constexpr auto kFreeFunc = &EC_KEY_free;
+  static constexpr auto kWritePemFunc = &PEM_write_bio_EC_PUBKEY;
+  static constexpr auto kWriteDerFunc = &WriteDerFuncNotImplementedEC;
+};
+
+// Need this function because of template instantiation, but it's never used.
+int WriteDerNotImplementedRSA(BIO* /*unused*/, RSA* /*unused*/) {
+  LOG(DFATAL) << "this should never be called";
+  return -1;
+}
+template<> struct SslTypeTraits<RSA> {
+  static constexpr auto kFreeFunc = &RSA_free;
+  static constexpr auto kWritePemFunc = &PEM_write_bio_RSA_PUBKEY;
+  static constexpr auto kWriteDerFunc = &WriteDerNotImplementedRSA;
+};
+
+} // namespace security
+
 
 // JWK Set (JSON Web Key Set) is JSON data structure that represents a set of JWKs.
 // This class parses JWKS file.
@@ -352,11 +391,9 @@ Status RSAJWTPublicKeyBuilder::CreateJWKPublicKey(
   }
   // Converts public key to PEM encoded form.
   string pub_key;
-  if (!ConvertJwkToPem(it_n->second, it_e->second, pub_key)) {
-    return Status::InvalidArgument(
-        Substitute("Invalid public key 'n':'$0', 'e':'$1'", it_n->second, it_e->second));
-  }
-
+  RETURN_NOT_OK_PREPEND(ConvertJwkToPem(it_n->second, it_e->second, pub_key),
+                        Substitute("invalid public key 'n':'$0', 'e':'$1'",
+                                   it_n->second, it_e->second));
   unique_ptr<JWTPublicKey> jwt_pub_key;
   try {
     if (algorithm == "rs256") {
@@ -385,45 +422,35 @@ Status RSAJWTPublicKeyBuilder::CreateJWKPublicKey(
   return Status::OK();
 }
 
-// Convert public key of RSA from JWK format to PEM encoded format by using OpenSSL APIs.
-bool RSAJWTPublicKeyBuilder::ConvertJwkToPem(
-    const std::string& base64_n, const std::string& base64_e, std::string& pub_key) {
-  pub_key.clear();
+// Convert JWK's RSA public key to PEM format using OpenSSL API.
+Status RSAJWTPublicKeyBuilder::ConvertJwkToPem(
+    const string& base64_n, const string& base64_e, string& pub_key) {
   string str_n;
+  if (!WebSafeBase64Unescape(base64_n, &str_n)) {
+    return Status::InvalidArgument("malformed 'n' key component");
+  }
   string str_e;
-  if (!strings::WebSafeBase64Unescape(base64_n, &str_n)) return false;
-  if (!strings::WebSafeBase64Unescape(base64_e, &str_e)) return false;
-  security::c_unique_ptr<BIGNUM> modul {
-    BN_bin2bn(reinterpret_cast<const unsigned char*>(str_n.c_str()),
-              static_cast<int>(str_n.size()),
-              nullptr),
-    &BN_free
-  };
-  security::c_unique_ptr<BIGNUM> expon {
-    BN_bin2bn(reinterpret_cast<const unsigned char*>(str_e.c_str()),
-              static_cast<int>(str_e.size()),
-              nullptr),
-    &BN_free
-  };
-
-  security::c_unique_ptr<RSA> rsa { RSA_new(), &RSA_free };
+  if (!WebSafeBase64Unescape(base64_e, &str_e)) {
+    return Status::InvalidArgument("malformed 'e' key component");
+  }
+  auto mod = ssl_make_unique(BN_bin2bn(
+      reinterpret_cast<const unsigned char*>(str_n.c_str()),
+      static_cast<int>(str_n.size()),
+      nullptr));
+  auto exp = ssl_make_unique(BN_bin2bn(
+      reinterpret_cast<const unsigned char*>(str_e.c_str()),
+      static_cast<int>(str_e.size()),
+      nullptr));
+  auto rsa = ssl_make_unique(RSA_new());
 #if OPENSSL_VERSION_NUMBER < 0x10100000L
-  rsa->n = modul.release();
-  rsa->e = expon.release();
+  rsa->n = mod.release();
+  rsa->e = exp.release();
 #else
   // RSA_set0_key is a new API introduced in OpenSSL version 1.1
-  RSA_set0_key(rsa.get(), modul.release(), expon.release(), nullptr);
+  OPENSSL_RET_NOT_OK(RSA_set0_key(
+      rsa.get(), mod.release(), exp.release(), nullptr), "failed to set RSA key");
 #endif
-
-  unsigned char desc[1024] = { 0 };
-  auto bio = security::ssl_make_unique(BIO_new(BIO_s_mem()));
-  PEM_write_bio_RSA_PUBKEY(bio.get(), rsa.get());
-  if (BIO_read(bio.get(), desc, std::size(desc) - 1) > 0) {
-    pub_key = reinterpret_cast<char*>(desc);
-    // Remove last '\n'.
-    if (pub_key.length() > 0 && pub_key[pub_key.length() - 1] == '\n') pub_key.pop_back();
-  }
-  return !pub_key.empty();
+  return ToString(&pub_key, DataFormat::PEM, rsa.get());
 }
 
 // Create a JWKPublicKey of EC (ES256, ES384 or ES512) from the JWK.
@@ -486,12 +513,11 @@ Status ECJWTPublicKeyBuilder::CreateJWKPublicKey(
   if (it_x->second.empty() || it_y->second.empty()) {
     return Status::InvalidArgument("'x' and 'y' properties must be a non-empty string");
   }
-  // Converts public key to PEM encoded form.
+  // Convert the public key into PEM format.
   string pub_key;
-  if (!ConvertJwkToPem(eccgrp, it_x->second, it_y->second, pub_key)) {
-    return Status::InvalidArgument(
-        Substitute("Invalid public key 'x':'$0', 'y':'$1'", it_x->second, it_y->second));
-  }
+  RETURN_NOT_OK_PREPEND(ConvertJwkToPem(eccgrp, it_x->second, it_y->second, pub_key),
+                        Substitute("invalid public key 'x':'$0', 'y':'$1'",
+                                   it_x->second, it_y->second));
 
   JWTPublicKey* jwt_pub_key = nullptr;
   try {
@@ -514,44 +540,34 @@ Status ECJWTPublicKeyBuilder::CreateJWKPublicKey(
   return Status::OK();
 }
 
-// Convert public key of EC from JWK format to PEM encoded format by using OpenSSL APIs.
-bool ECJWTPublicKeyBuilder::ConvertJwkToPem(int eccgrp, const std::string& base64_x,
-    const std::string& base64_y, std::string& pub_key) {
-  pub_key.clear();
+// Convert JWK's EC public key to PEM format using OpenSSL API.
+Status ECJWTPublicKeyBuilder::ConvertJwkToPem(int eccgrp,
+                                              const string& base64_x,
+                                              const string& base64_y,
+                                              string& pub_key) {
   string ascii_x;
+  if (!WebSafeBase64Unescape(base64_x, &ascii_x)) {
+    return Status::InvalidArgument("malformed 'x' key component");
+  }
   string ascii_y;
-  if (!strings::WebSafeBase64Unescape(base64_x, &ascii_x)) return false;
-  if (!strings::WebSafeBase64Unescape(base64_y, &ascii_y)) return false;
-  security::c_unique_ptr<BIGNUM> x {
-    BN_bin2bn(reinterpret_cast<const unsigned char*>(ascii_x.c_str()),
-              static_cast<int>(ascii_x.size()),
-              nullptr),
-    &BN_free
-  };
-  security::c_unique_ptr<BIGNUM> y {
-    BN_bin2bn(reinterpret_cast<const unsigned char*>(ascii_y.c_str()),
-              static_cast<int>(ascii_y.size()),
-              nullptr),
-    &BN_free
-  };
-
-  security::c_unique_ptr<EC_KEY> ecKey { EC_KEY_new_by_curve_name(eccgrp), &EC_KEY_free };
-  EC_KEY_set_asn1_flag(ecKey.get(), OPENSSL_EC_NAMED_CURVE);
-  if (EC_KEY_set_public_key_affine_coordinates(ecKey.get(), x.get(), y.get()) == 0) return false;
-
-  unsigned char desc[1024] = { 0 };
-  auto bio = security::ssl_make_unique(BIO_new(BIO_s_mem()));
-  if (PEM_write_bio_EC_PUBKEY(bio.get(), ecKey.get()) != 0) {
-    if (BIO_read(bio.get(), desc, std::size(desc) - 1) > 0) {
-      pub_key = reinterpret_cast<char*>(desc);
-      // Remove last '\n'.
-      if (pub_key.length() > 0 && pub_key[pub_key.length() - 1] == '\n') {
-        pub_key.pop_back();
-      }
-    }
+  if (!WebSafeBase64Unescape(base64_y, &ascii_y)) {
+    return Status::InvalidArgument("malformed 'y' key component");
   }
-
-  return !pub_key.empty();
+  auto x = ssl_make_unique(BN_bin2bn(
+      reinterpret_cast<const unsigned char*>(ascii_x.c_str()),
+      static_cast<int>(ascii_x.size()),
+      nullptr));
+  auto y = ssl_make_unique(BN_bin2bn(
+      reinterpret_cast<const unsigned char*>(ascii_y.c_str()),
+      static_cast<int>(ascii_y.size()),
+      nullptr));
+  auto ec_key = ssl_make_unique(EC_KEY_new_by_curve_name(eccgrp));
+  OPENSSL_RET_IF_NULL(ec_key, "failed to create EC key");
+  EC_KEY_set_asn1_flag(ec_key.get(), OPENSSL_EC_NAMED_CURVE);
+  OPENSSL_RET_NOT_OK(EC_KEY_set_public_key_affine_coordinates(
+      ec_key.get(), x.get(), y.get()), "failed to set public key");
+
+  return ToString(&pub_key, DataFormat::PEM, ec_key.get());
 }
 
 //
@@ -970,7 +986,9 @@ Status PerAccountKeyBasedJwtVerifier::JWTHelperForToken(const JWTHelper::JWTDeco
   // JWTHelper for it, use it.
   const auto& issuer = token.decoded_jwt_.get_issuer();
   std::vector<string> issuer_pieces = strings::Split(issuer, "/");
-  CHECK(!issuer_pieces.empty());
+  if (issuer_pieces.empty()) {
+    return Status::InvalidArgument("cannot parse 'issuer' field");
+  }
   const auto& account_id = issuer_pieces.back();
 
   {
diff --git a/src/kudu/util/jwt_test_certs.cc b/src/kudu/util/jwt_test_certs.cc
index b79a1a150..1991b7bab 100644
--- a/src/kudu/util/jwt_test_certs.cc
+++ b/src/kudu/util/jwt_test_certs.cc
@@ -59,7 +59,8 @@ kM3Nh6HWFhROptfc6BNusRh1kX/cspDplK5x8EpJ0QKBgQDWFg6S2je0KtbV5PYe
 RultUEe2C0jYMDQx+JYxbPmtcopvZQrFEur3WKVuLy5UAy7EBvwMnZwIG7OOohJb
 vkSpADK6VPn9lbqq7O8cTedEHttm6otmLt8ZyEl3hZMaL3hbuRj6ysjmoFKx6CrX
 rK0/Ikt5ybqUzKCMJZg2VKGTxg==
------END PRIVATE KEY-----)";
+-----END PRIVATE KEY-----
+)";
 const char* kRsaPubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuGbXWiK3dQTyCbX5xdE4
 yCuYp0AF2d15Qq1JSXT/lx8CEcXb9RbDddl8jGDv+spi5qPa8qEHiK7FwV2KpRE9
@@ -68,7 +69,8 @@ WXI9C+yjHztqyL2h8P6mlThPY9E9ue2fCqdgixfTFIF9Dm4SLHbphUS2iw7w1JgT
 69s7of9+I9l5lsJ9cozf1rxrXX4V1u/SotUuNB3Fp8oB4C1fLBEhSlMcUJirz1E8
 AziMCxS+VrRPDM+zfvpIJg3JljAh3PJHDiLu902v9w+Iplu1WyoB2aPfitxEhRN0
 YwIDAQAB
------END PUBLIC KEY-----)";
+-----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 char* kRsaPubKeyJwkN =
@@ -101,13 +103,15 @@ Su5rsCPb8acJo5RO26gGVrfAsDcIXKC+bQJAZZ2XIpsitLyPpuiMOvBbzPavd4gY
 fSSjAkLRi54PKJ8TFUeOP15h9sQzydI8zJU+upvDEKZsZc/UhT/SySDOxQ4G/523
 Y0sz/OZtSWcol/UMgQJALesy++GdvoIDLfJX5GBQpuFgFenRiRDabxrE9MNUZ2aP
 FaFp+DyAe+b4nDwuJaW2LURbr8AEZga7oQj0uYxcYw==
------END RSA PRIVATE KEY-----)";
+-----END RSA PRIVATE KEY-----
+)";
 const char* kRsa512PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDdlatRjRjogo3WojgGHFHYLugd
 UWAY9iR3fy4arWNA1KoS8kVw33cJibXr8bvwUAUparCwlvdbH6dvEOfou0/gCFQs
 HUfQrSDv+MuSUMAe8jzKE4qW+jK+xQU9a03GUnKHkkle+Q0pX/g6jXZ7r1/xAK5D
 o2kQ+X5xK9cipRgEKwIDAQAB
------END PUBLIC KEY-----)";
+-----END PUBLIC KEY-----
+)";
 const char* kRsa512PubKeyJwkN =
     "3ZWrUY0Y6IKN1qI4BhxR2C7oHVFgGPYkd38uGq1jQNSqEvJFcN93CYm16_G78FA"
     "FKWqwsJb3Wx-nbxDn6LtP4AhULB1H0K0g7_jLklDAHvI8yhOKlvoyvsUFPWtNxl"
@@ -135,13 +139,15 @@ T/DoEfdciDK0Ui9rzh7HB+eW6rkFJGsDUWwV6SRTCD3X64PcpuDUNpK6ZFCVAkEA
 oaBgAAiDH1UPpAvK6LfALl0P6E1pjLvWjvhOg/Z4xKvS21cJIJlF0ShGFSV2CTzx
 YQUiqLkHegkGxV353XRxVQJAZaW5O2BI5jKy2hK0EoAx3pSnp2X4CmkWrXsSeOgC
 Zz+jDkn8QzPbRwb8cyks/IHc2CBvaFStLFKO2VQj1THDhw==
------END RSA PRIVATE KEY-----)";
+-----END RSA PRIVATE KEY-----
+)";
 const char* kRsa1024PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDT+6sb2SvN69NB+6Zg78B7mdke
 0tC91CTfixzCSn7wS8JUvvZKAO1uMgnrCQdDr2TNeRYr6urawIOCDB1Ybz1+cBSN
 xouVdt/aT9+cw27kzVQE59NAPMpQyLtXaAOR6rD8xzyIgAV12QFmc1kHFl7Sjobw
 msu5ZWRqYTwdXvFXIQIDAQAB
------END PUBLIC KEY-----)";
+-----END PUBLIC KEY-----
+)";
 const char* kRsa1024PubKeyJwkN =
     "0_urG9krzevTQfumYO_Ae5nZHtLQvdQk34scwkp-8EvCVL72SgDtbjIJ6wkHQ69"
     "kzXkWK-rq2sCDggwdWG89fnAUjcaLlXbf2k_fnMNu5M1UBOfTQDzKUMi7V2gDke"
@@ -174,7 +180,8 @@ tHPukBzyzxfL3f3T81XcGqUC65tL6aM0djUOrKXtEc4pWBEasd5Q74NO6bD0PNTs
 jOODBXkCgYEAhaD3gZUCWU+ZA6QmxPotfe9L0tzjmUjsLo0QUgIHJa2VaoHzdnWC
 ClvP3tFFkv2dlD6UW+g0JJFTVWcv+HEiC9WUnD/C6dXK/qA3fRvBhRKy8FTwvOis
 zSVeYds6mvDJwFe+2mk0KQiKnxlx22B4PcYbbN7mZ2ClBFTFrp0+Id4=
------END RSA PRIVATE KEY-----)";
+-----END RSA PRIVATE KEY-----
+)";
 const char* kRsa2048PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0jCHomsNIaRYVlsemWg9
 yBx3od1B9Fd9RUslk9IVE7IU+QYZ+T4NvRVPAMjpzuurvPnN4uBVPREycXOgEcWH
@@ -183,7 +190,8 @@ R1SBD8qEMAxpiLMH1Q/Yap+etvIjD1r2zQkQke53An9LvVl7OKkM8KGOcE/0tJRm
 c7x8ZlLqogPczkXvW6T+YAkwA8XwginZw0xBzfpoOEnajqm4Yikck0gJ0HwdlYFI
 p72ih7uozne7PYLVGb9X97cL0H1XDiA/SXJiFKo1AKXihcOdIRiw49eo9rzsoWPy
 gQIDAQAB
------END PUBLIC KEY-----)";
+-----END PUBLIC KEY-----
+)";
 const char* kRsa2048PubKeyJwkN =
     "0jCHomsNIaRYVlsemWg9yBx3od1B9Fd9RUslk9IVE7IU-QYZ-T4NvRVPAMjpzuu"
     "rvPnN4uBVPREycXOgEcWHiJDDQEhlQD4F69W8MFE7SXpdcBihzcj5qPYtTFP_52"
@@ -243,7 +251,8 @@ eVY6Sn7JZC9qyE+oCrJMg+0hzc5Gw8+/H+e0Jgca8+76WVu8gGcsLdT+NjYNQwXH
 rzo7tuC/a+Da3nd2UnMheqf8ajt7oXaXgrqYjzK9Fx/QJcUel12ny+Nx+NADx4UU
 K43Js4kcyWyYG9ms7S643u1leDDO+hpeB6EN15U2v7zXi8rMrLqvNKrBi9bCRFDu
 3zsKSPS+qeqpNBsefGtx7oluHdiQocA6w20nQ1DzIW2mOo8Pn5nzt7fPPPA=
------END RSA PRIVATE KEY-----)";
+-----END RSA PRIVATE KEY-----
+)";
 const char* kRsa4096PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtxmYsvs6ZfhTCFKCHQBW
 /W3iRfh8wZN+/XPXaOiIx9SXYSFrb/WRaTn8UOvflYuRnPYMaRGr5gVTS6/WFVvt
@@ -257,7 +266,8 @@ XcgQWTY7dpIP1BInhepCzHlcLSEiHtmMoCXNC3+i9ZXxiJ1u8avYfGjH8RrJW8dv
 Vw7BS7zlH9s7rCn001VBJCJcXtkGaykw9Zd1E+Jh7IKQJn8gydsQ0enlMmtwsJO/
 tEvYBojFXbl4XecMWADTiExjXobX1y7u9ZTn0KRNkPpX9GTgY3oR0ei+rwOr4d+k
 2CrUdkMTGfjnfcDHKjHh3LMCAwEAAQ==
------END PUBLIC KEY-----)";
+-----END PUBLIC KEY-----
+)";
 const char* kRsa4096PubKeyJwkN =
     "txmYsvs6ZfhTCFKCHQBW_W3iRfh8wZN-_XPXaOiIx9SXYSFrb_WRaTn8UOvflYu"
     "RnPYMaRGr5gVTS6_WFVvtNuZVIDOQBgEOBt5MQ0BeM0yPiM6qacP15couRwxbJx"
@@ -278,13 +288,15 @@ h2Srn7o8+4j/jQpwHTTHZThy10u5jMjaR+mgBwYFK4EEACOhgYkDgYYABAFFah0k
 6m4ddp/tUN/ObrKKwSCp4QUZdiAMaC9eY1HyNBPuuEsH5qCfeY5lmeJwSUpzCosn
 rgW8M2hQ4Kr5V9OXrgHLA5WVtH6//sSkUY2/xYuqc7/Ln8gI5ddtr1qG64Xtgs05
 /CNajSjFZeLm76llakvYiBTTH/ii8hIfrwukW9IP7Q==
------END EC PRIVATE KEY-----)";
+-----END EC PRIVATE KEY-----
+)";
 const char* kEcdsa521PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBRWodJOpuHXaf7VDfzm6yisEgqeEF
 GXYgDGgvXmNR8jQT7rhLB+agn3mOZZnicElKcwqLJ64FvDNoUOCq+VfTl64BywOV
 lbR+v/7EpFGNv8WLqnO/y5/ICOXXba9ahuuF7YLNOfwjWo0oxWXi5u+pZWpL2IgU
 0x/4ovISH68LpFvSD+0=
------END PUBLIC KEY-----)";
+-----END PUBLIC KEY-----
+)";
 const char* kEcdsa521PubKeyJwkX =
     "AUVqHSTqbh12n-1Q385usorBIKnhBRl2IAxoL15jUfI0E-64SwfmoJ95jmWZ4nB"
     "JSnMKiyeuBbwzaFDgqvlX05eu";
@@ -297,12 +309,14 @@ MIGkAgEBBDCrPXJDgQDtNRpM0qNUW/zN1vrCvOVH1CsItVZ+1NeGB+w/2whnIXJQ
 K7U5C1ETPHagBwYFK4EEACKhZANiAAR0JjvVJXc3u1I/7vt5mxzPtAIi1VIqxCwN
 wgISZVySTYZQzyicW2GfhMlFCow28LzqTwH/eCymAvnTAmpK/P1hXhNcnxDBZNOU
 WMbMLFcQrg2wwpIb/k/IXobNwjNPRBo=
------END EC PRIVATE KEY-----)";
+-----END EC PRIVATE KEY-----
+)";
 const char* kEcdsa384PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEdCY71SV3N7tSP+77eZscz7QCItVSKsQs
 DcICEmVckk2GUM8onFthn4TJRQqMNvC86k8B/3gspgL50wJqSvz9YV4TXJ8QwWTT
 lFjGzCxXEK4NsMKSG/5PyF6GzcIzT0Qa
------END PUBLIC KEY-----)";
+-----END PUBLIC KEY-----
+)";
 const char* kEcdsa384PubKeyJwkX =
     "dCY71SV3N7tSP-77eZscz7QCItVSKsQsDcICEmVckk2GUM8onFthn4TJRQqMNvC8";
 const char* kEcdsa384PubKeyJwkY =
@@ -312,11 +326,13 @@ const char* kEcdsa256PrivKeyPem = R"(-----BEGIN PRIVATE KEY-----
 MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgPGJGAm4X1fvBuC1z
 SpO/4Izx6PXfNMaiKaS5RUkFqEGhRANCAARCBvmeksd3QGTrVs2eMrrfa7CYF+sX
 sjyGg+Bo5mPKGH4Gs8M7oIvoP9pb/I85tdebtKlmiCZHAZE5w4DfJSV6
------END PRIVATE KEY-----)";
+-----END PRIVATE KEY-----
+)";
 const char* kEcdsa256PubKeyPem = R"(-----BEGIN PUBLIC KEY-----
 MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEQgb5npLHd0Bk61bNnjK632uwmBfr
 F7I8hoPgaOZjyhh+BrPDO6CL6D/aW/yPObXXm7SpZogmRwGROcOA3yUleg==
------END PUBLIC KEY-----)";
+-----END PUBLIC KEY-----
+)";
 const char* kEcdsa256PubKeyJwkX = "Qgb5npLHd0Bk61bNnjK632uwmBfrF7I8hoPgaOZjyhg";
 const char* kEcdsa256PubKeyJwkY = "fgazwzugi-g_2lv8jzm115u0qWaIJkcBkTnDgN8lJXo";