| /* Copyright (C) 2024-2026 maClara, LLC <info@maclara-llc.com> |
| This file is part of the JWT C Library |
| |
| SPDX-License-Identifier: MPL-2.0 |
| This Source Code Form is subject to the terms of the Mozilla Public |
| License, v. 2.0. If a copy of the MPL was not distributed with this |
| file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <time.h> |
| #include <getopt.h> |
| #include <unistd.h> |
| |
| #include <jwt.h> |
| |
| #include "jwt-util.h" |
| |
| /* The PEM/DER/HMAC -> JWK conversion now lives in libjwt as a public API |
| * (jwks_load_fromkey* and jwks_export), so this tool only uses the public |
| * interface and links the shared library like every other tool. */ |
| |
| static int with_kid = 1; |
| static int assume_hmac = 1; |
| |
| _Noreturn static void usage(const char *error, int exit_state) |
| { |
| if (error) |
| fprintf(stderr, "ERROR: %s\n\n", error); |
| |
| fprintf(stderr, "\ |
| Usage: %s [OPTIONS] <FILE> [FILE]...\n\ |
| \n\ |
| Parse PEM/DER file(s) into JSON Web Key format.\n\ |
| \n\ |
| -h, --help This help information\n\ |
| -q, --quiet No output other than JWKS file\n\ |
| -l, --list List supported algorithms and exit\n\ |
| -k, --disable-kid Disable generating \"kid\" attribute\n\ |
| -m, --disable-hmac Disable fallback to HMAC\n\ |
| -o, --output=FILE File to write JWKS to\n\ |
| \n\ |
| This program will parse PEM/DER key files (public and private) into JSON Web\n\ |
| Keys and output a JWK Set. Note that HMAC keys are \"guessed\" based on them\n\ |
| not being parsed as a PEM/DER key. This may cause some issues. You can disable\n\ |
| this with the -m option.\n\ |
| \n\ |
| You can use '-' as the argument to the -o option to write to stdout.\n\ |
| \n\ |
| RSA keys will not have an algorithm set as they are valid for RS256, RS384,\n\ |
| and RS512. RSA keys must be at least 1024 bits.\n\ |
| \n\ |
| RSA-PSS keys will be set to PS256, otherwise they will look no different\n\ |
| than an RSA key.\n\ |
| \n\ |
| All keys will get a generated randomized uuidv4 \"kid\" attribute unless you\n\ |
| use the -k option..\n", get_progname()); |
| |
| exit(exit_state); |
| } |
| |
| /* Print the JWKS with a small libjwt.io: provenance header injected after the |
| * opening brace of the jwks_export() output. */ |
| static void write_jwks(FILE *outfp, const char *keys_json) |
| { |
| char comment[256]; |
| const char *brace; |
| time_t now; |
| char *time_str; |
| |
| /* jwks_export() always produces an indented object starting with "{\n". */ |
| brace = strchr(keys_json, '{'); |
| if (brace == NULL || brace[1] != '\n') { |
| /* Should not happen, but degrade gracefully. */ |
| fprintf(outfp, "%s\n", keys_json); // LCOV_EXCL_LINE |
| return; // LCOV_EXCL_LINE |
| } |
| |
| /* Emit the opening brace and newline, then our metadata members. */ |
| fputs("{\n", outfp); |
| |
| fprintf(outfp, " \"libjwt.io:comment\": \"%s\",\n", |
| "Generated by LibJWT"); |
| |
| now = time(NULL); |
| time_str = ctime(&now); |
| time_str[strlen(time_str) - 1] = '\0'; |
| fprintf(outfp, " \"libjwt.io:date\": \"%s\",\n", time_str); |
| |
| #ifdef _WIN32 |
| DWORD hostnamesize = sizeof(comment); |
| GetComputerNameA(comment, &hostnamesize); |
| #else |
| gethostname(comment, sizeof(comment)); |
| #endif |
| comment[sizeof(comment) - 1] = '\0'; |
| fprintf(outfp, " \"libjwt.io:hostname\": \"%s\",\n", comment); |
| |
| /* The remainder of the export, starting after the "{\n" we replaced: |
| * the "keys" member and closing brace. */ |
| fprintf(outfp, "%s\n", brace + 2); |
| } |
| |
| int main(int argc, char **argv) |
| { |
| jwk_set_auto_t *jwk_set = NULL; |
| jwt_alg_t alg; |
| int quiet = 0; |
| char *jwk_str = NULL; |
| FILE *outfp = NULL; |
| FILE *msg = stdout; |
| unsigned int flags; |
| int i, oc; |
| |
| char *optstr = "hlqo:km"; |
| struct option opttbl[] = { |
| { "help", no_argument, NULL, 'h' }, |
| { "list", no_argument, NULL, 'l' }, |
| { "quiet", no_argument, NULL, 'q' }, |
| { "output", required_argument, NULL, 'o' }, |
| { "disable-kid", no_argument, NULL, 'k' }, |
| { "disable-hmac", no_argument, NULL, 'm' }, |
| { NULL, 0, 0, 0 }, |
| }; |
| |
| while ((oc = getopt_long(argc, argv, optstr, opttbl, NULL)) != -1) { |
| switch (oc) { |
| case 'h': |
| usage(NULL, EXIT_SUCCESS); |
| |
| case 'l': |
| printf("Algorithms supported:\n"); |
| for (alg = JWT_ALG_NONE; alg < JWT_ALG_INVAL; alg++) { |
| const char *name = jwt_alg_str(alg); |
| /* An alg compiled out of this build (e.g. |
| * ML-DSA without WITH_ML_DSA) has no name. */ |
| if (name != NULL) |
| printf(" %s\n", name); |
| } |
| exit(EXIT_SUCCESS); |
| break; |
| |
| case 'q': |
| quiet = 1; |
| break; |
| |
| case 'k': |
| with_kid = 0; |
| break; |
| |
| case 'm': |
| assume_hmac = 0; |
| break; |
| |
| case 'o': |
| if (optarg[0] == '-') { |
| outfp = stdout; |
| msg = stderr; |
| } else { |
| outfp = fopen(optarg, "wx"); |
| if (outfp == NULL) |
| perror(optarg); |
| } |
| break; |
| default: /* '?' */ |
| usage("Unknown option", EXIT_FAILURE); |
| break; |
| } |
| } |
| |
| argc -= optind; |
| argv += optind; |
| |
| if (argc == 0) |
| usage("No key(s) given", EXIT_FAILURE); |
| |
| if (outfp == NULL) |
| usage("The --output argument is required", EXIT_FAILURE); |
| |
| flags = JWK_KEY_NONE; |
| if (with_kid) |
| flags |= JWK_KEY_GEN_KID; |
| if (assume_hmac) |
| flags |= JWK_KEY_TRY_HMAC; |
| |
| if (!quiet) |
| fprintf(msg, "Parsing %d files (", argc); |
| |
| for (i = 0; i < argc; i++) { |
| jwk_set = jwks_load_fromkey_file(jwk_set, argv[i], flags); |
| if (jwk_set == NULL) { |
| fprintf(stderr, "Error parsing key file: %s\n", argv[i]); |
| exit(EXIT_FAILURE); |
| } |
| if (jwks_error(jwk_set)) { |
| fprintf(stderr, "%s: %s\n", argv[i], |
| jwks_error_msg(jwk_set)); |
| exit(EXIT_FAILURE); |
| } |
| if (!quiet) |
| fprintf(msg, "."); |
| } |
| if (!quiet) { |
| fprintf(msg, ") done\n"); |
| fprintf(msg, "Generating JWKS...\n"); |
| } |
| |
| jwk_str = jwks_export(jwk_set, 1); |
| if (jwk_str == NULL) { |
| fprintf(stderr, "Error generating JWKS output\n"); // LCOV_EXCL_LINE |
| exit(EXIT_FAILURE); // LCOV_EXCL_LINE |
| } |
| |
| write_jwks(outfp, jwk_str); |
| |
| free(jwk_str); |
| |
| exit(EXIT_SUCCESS); |
| } |