3 * Copyright 2015 gRPC authors.
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
9 * http://www.apache.org/licenses/LICENSE-2.0
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
19 #include <grpc/support/port_platform.h>
21 #include "src/core/lib/iomgr/error.h"
22 #include "src/core/lib/security/credentials/jwt/json_token.h"
26 #include <grpc/grpc_security.h>
27 #include <grpc/support/alloc.h>
28 #include <grpc/support/log.h>
29 #include <grpc/support/string_util.h>
30 #include <grpc/support/time.h>
32 #include "src/core/lib/gpr/string.h"
33 #include "src/core/lib/security/util/json_util.h"
34 #include "src/core/lib/slice/b64.h"
37 #include <openssl/bio.h>
38 #include <openssl/evp.h>
39 #include <openssl/pem.h>
42 using grpc_core::Json;
44 /* --- Constants. --- */
47 gpr_timespec grpc_max_auth_token_lifetime() {
51 out.clock_type = GPR_TIMESPAN;
55 #define GRPC_JWT_RSA_SHA256_ALGORITHM "RS256"
56 #define GRPC_JWT_TYPE "JWT"
58 /* --- Override for testing. --- */
60 static grpc_jwt_encode_and_sign_override g_jwt_encode_and_sign_override =
63 /* --- grpc_auth_json_key. --- */
65 int grpc_auth_json_key_is_valid(const grpc_auth_json_key* json_key) {
66 return (json_key != nullptr) &&
67 strcmp(json_key->type, GRPC_AUTH_JSON_TYPE_INVALID) != 0;
70 grpc_auth_json_key grpc_auth_json_key_create_from_json(const Json& json) {
71 grpc_auth_json_key result;
73 const char* prop_value;
75 grpc_error* error = GRPC_ERROR_NONE;
77 memset(&result, 0, sizeof(grpc_auth_json_key));
78 result.type = GRPC_AUTH_JSON_TYPE_INVALID;
79 if (json.type() == Json::Type::JSON_NULL) {
80 gpr_log(GPR_ERROR, "Invalid json.");
84 prop_value = grpc_json_get_string_property(json, "type", &error);
85 GRPC_LOG_IF_ERROR("JSON key parsing", error);
86 if (prop_value == nullptr ||
87 strcmp(prop_value, GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT) != 0) {
90 result.type = GRPC_AUTH_JSON_TYPE_SERVICE_ACCOUNT;
92 if (!grpc_copy_json_string_property(json, "private_key_id",
93 &result.private_key_id) ||
94 !grpc_copy_json_string_property(json, "client_id", &result.client_id) ||
95 !grpc_copy_json_string_property(json, "client_email",
96 &result.client_email)) {
100 prop_value = grpc_json_get_string_property(json, "private_key", &error);
101 GRPC_LOG_IF_ERROR("JSON key parsing", error);
102 if (prop_value == nullptr) {
105 bio = BIO_new(BIO_s_mem());
106 success = BIO_puts(bio, prop_value);
107 if ((success < 0) || (static_cast<size_t>(success) != strlen(prop_value))) {
108 gpr_log(GPR_ERROR, "Could not write into openssl BIO.");
112 PEM_read_bio_RSAPrivateKey(bio, nullptr, nullptr, const_cast<char*>(""));
113 if (result.private_key == nullptr) {
114 gpr_log(GPR_ERROR, "Could not deserialize private key.");
120 if (bio != nullptr) BIO_free(bio);
121 if (!success) grpc_auth_json_key_destruct(&result);
125 grpc_auth_json_key grpc_auth_json_key_create_from_string(
126 const char* json_string) {
127 grpc_error* error = GRPC_ERROR_NONE;
128 Json json = Json::Parse(json_string, &error);
129 GRPC_LOG_IF_ERROR("JSON key parsing", error);
130 return grpc_auth_json_key_create_from_json(json);
133 void grpc_auth_json_key_destruct(grpc_auth_json_key* json_key) {
134 if (json_key == nullptr) return;
135 json_key->type = GRPC_AUTH_JSON_TYPE_INVALID;
136 if (json_key->client_id != nullptr) {
137 gpr_free(json_key->client_id);
138 json_key->client_id = nullptr;
140 if (json_key->private_key_id != nullptr) {
141 gpr_free(json_key->private_key_id);
142 json_key->private_key_id = nullptr;
144 if (json_key->client_email != nullptr) {
145 gpr_free(json_key->client_email);
146 json_key->client_email = nullptr;
148 if (json_key->private_key != nullptr) {
149 RSA_free(json_key->private_key);
150 json_key->private_key = nullptr;
154 /* --- jwt encoding and signature. --- */
156 static char* encoded_jwt_header(const char* key_id, const char* algorithm) {
157 Json json = Json::Object{
159 {"typ", GRPC_JWT_TYPE},
162 std::string json_str = json.Dump();
163 return grpc_base64_encode(json_str.c_str(), json_str.size(), 1, 0);
166 static char* encoded_jwt_claim(const grpc_auth_json_key* json_key,
167 const char* audience,
168 gpr_timespec token_lifetime, const char* scope) {
169 gpr_timespec now = gpr_now(GPR_CLOCK_REALTIME);
170 gpr_timespec expiration = gpr_time_add(now, token_lifetime);
171 if (gpr_time_cmp(token_lifetime, grpc_max_auth_token_lifetime()) > 0) {
172 gpr_log(GPR_INFO, "Cropping token lifetime to maximum allowed value.");
173 expiration = gpr_time_add(now, grpc_max_auth_token_lifetime());
176 Json::Object object = {
177 {"iss", json_key->client_email},
180 {"exp", expiration.tv_sec},
182 if (scope != nullptr) {
183 object["scope"] = scope;
185 /* Unscoped JWTs need a sub field. */
186 object["sub"] = json_key->client_email;
190 std::string json_str = json.Dump();
191 return grpc_base64_encode(json_str.c_str(), json_str.size(), 1, 0);
194 static char* dot_concat_and_free_strings(char* str1, char* str2) {
195 size_t str1_len = strlen(str1);
196 size_t str2_len = strlen(str2);
197 size_t result_len = str1_len + 1 /* dot */ + str2_len;
199 static_cast<char*>(gpr_malloc(result_len + 1 /* NULL terminated */));
200 char* current = result;
201 memcpy(current, str1, str1_len);
204 memcpy(current, str2, str2_len);
206 GPR_ASSERT(current >= result);
207 GPR_ASSERT((uintptr_t)(current - result) == result_len);
214 const EVP_MD* openssl_digest_from_algorithm(const char* algorithm) {
215 if (strcmp(algorithm, GRPC_JWT_RSA_SHA256_ALGORITHM) == 0) {
218 gpr_log(GPR_ERROR, "Unknown algorithm %s.", algorithm);
223 char* compute_and_encode_signature(const grpc_auth_json_key* json_key,
224 const char* signature_algorithm,
225 const char* to_sign) {
226 const EVP_MD* md = openssl_digest_from_algorithm(signature_algorithm);
227 EVP_MD_CTX* md_ctx = nullptr;
228 EVP_PKEY* key = EVP_PKEY_new();
230 unsigned char* sig = nullptr;
231 char* result = nullptr;
232 if (md == nullptr) return nullptr;
233 md_ctx = EVP_MD_CTX_create();
234 if (md_ctx == nullptr) {
235 gpr_log(GPR_ERROR, "Could not create MD_CTX");
238 EVP_PKEY_set1_RSA(key, json_key->private_key);
239 if (EVP_DigestSignInit(md_ctx, nullptr, md, nullptr, key) != 1) {
240 gpr_log(GPR_ERROR, "DigestInit failed.");
243 if (EVP_DigestSignUpdate(md_ctx, to_sign, strlen(to_sign)) != 1) {
244 gpr_log(GPR_ERROR, "DigestUpdate failed.");
247 if (EVP_DigestSignFinal(md_ctx, nullptr, &sig_len) != 1) {
248 gpr_log(GPR_ERROR, "DigestFinal (get signature length) failed.");
251 sig = static_cast<unsigned char*>(gpr_malloc(sig_len));
252 if (EVP_DigestSignFinal(md_ctx, sig, &sig_len) != 1) {
253 gpr_log(GPR_ERROR, "DigestFinal (signature compute) failed.");
256 result = grpc_base64_encode(sig, sig_len, 1, 0);
259 if (key != nullptr) EVP_PKEY_free(key);
260 if (md_ctx != nullptr) EVP_MD_CTX_destroy(md_ctx);
261 if (sig != nullptr) gpr_free(sig);
265 char* grpc_jwt_encode_and_sign(const grpc_auth_json_key* json_key,
266 const char* audience,
267 gpr_timespec token_lifetime, const char* scope) {
268 if (g_jwt_encode_and_sign_override != nullptr) {
269 return g_jwt_encode_and_sign_override(json_key, audience, token_lifetime,
272 const char* sig_algo = GRPC_JWT_RSA_SHA256_ALGORITHM;
273 char* to_sign = dot_concat_and_free_strings(
274 encoded_jwt_header(json_key->private_key_id, sig_algo),
275 encoded_jwt_claim(json_key, audience, token_lifetime, scope));
276 char* sig = compute_and_encode_signature(json_key, sig_algo, to_sign);
277 if (sig == nullptr) {
281 return dot_concat_and_free_strings(to_sign, sig);
285 void grpc_jwt_encode_and_sign_set_override(
286 grpc_jwt_encode_and_sign_override func) {
287 g_jwt_encode_and_sign_override = func;