blob: 8ffc22916518e9ce550486747f3648fcb0d95dc4 [file] [log] [blame]
// Copyright 2021 Google LLC
//
// Licensed 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.
//
///////////////////////////////////////////////////////////////////////////////
// A command-line utility for generating JSON Web Token (JWT) keys, and creating
// and verifying JWTs.
//
// The first argument is the operation and it should be one of the following:
// gen-private-key get-public-key sign verify.
// Additional arguments depend on the operation.
//
// gen-private-key
// Generates a new private keyset using JwtRs256_2048_F4_Template.
// It requires 1 additional argument:
// output-file: name of the file for the resulting output
//
// get-public-key
// Extracts a public keyset associated with the given private keyset.
// It requires 2 additional arguments:
// private-keyset-file: name of the file with the private keyset
// output-file: name of the file for the resulting output
//
// sign
// Generates and signs a token using the given private keyset.
// It requires 3 additional arguments:
// private-keyset-file: name of the file with the private keyset
// audience: audience claim to be put in the token.
// output-file: name of the file for the resulting output
//
// verify
// Verifies a token using the given public keyset.
// It requires 4 additional arguments:
// public-keyset-file: name of the file with the public keyset
// audience: expected audience in the token
// token-file: name of the file with the token
// output-file: name of the file for the resulting output (valid/invalid)
#include <iostream>
#include <string>
#include "tink/jwt/jwt_key_templates.h"
#include "tink/jwt/jwt_public_key_sign.h"
#include "tink/jwt/jwt_public_key_verify.h"
#include "tink/jwt/jwt_signature_config.h"
#include "tink/jwt/jwt_validator.h"
#include "tink/jwt/raw_jwt.h"
#include "tink/jwt/verified_jwt.h"
#include "jwt/util.h"
// Prints usage info.
using crypto::tink::JwtPublicKeySign;
using crypto::tink::JwtPublicKeyVerify;
using crypto::tink::JwtValidator;
using crypto::tink::KeysetHandle;
using crypto::tink::RawJwt;
using crypto::tink::RawJwtBuilder;
void PrintUsageInfo() {
std::clog << "Usage: operation arguments\n"
<< "where operation is one of the following:\n"
<< " gen-private-key get-public-key sign verify\n"
<< "and, depending on the operation, arguments are:\n"
<< " gen-private-key: output-file\n"
<< " get-public-key: private-keyset-file output-file\n"
<< " sign: private-keyset-file audience output-file\n"
<< " verify: public-keyset-file audience token-file "
<< "output-file" << std::endl;
}
// Generates a new private keyset using JwtRs256_2048_F4_Template and writes it
// to the output file.
void GeneratePrivateKey(absl::string_view output_filename) {
std::clog << "Generating a new private keyset.." << std::endl;
auto key_template = crypto::tink::JwtRs256_2048_F4_Template();
crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle =
crypto::tink::KeysetHandle::GenerateNew(key_template);
if (!keyset_handle.ok()) {
std::clog << "Generating new keyset failed: "
<< keyset_handle.status().message() << std::endl;
exit(1);
}
std::clog << "Writing the keyset to file " << output_filename
<< "..." << std::endl;
WriteKeyset(**keyset_handle, output_filename);
}
// Extracts a public keyset associated with the given private keyset
// and writes it to the output file.
void ExtractPublicKey(absl::string_view private_keyset_filename,
absl::string_view output_filename) {
std::clog << "Extracting a public keyset associated with the private "
<< "keyset from file " << private_keyset_filename << "..."
<< std::endl;
std::unique_ptr<crypto::tink::KeysetHandle> private_keyset_handle =
ReadKeyset(private_keyset_filename);
crypto::tink::util::StatusOr<std::unique_ptr<KeysetHandle>>
public_keyset_handle = private_keyset_handle->GetPublicKeysetHandle();
if (!public_keyset_handle.ok()) {
std::clog << "Getting the keyset failed: "
<< public_keyset_handle.status().message() << std::endl;
exit(1);
}
std::clog << "Writing the keyset to file " << output_filename
<< "..." << std::endl;
WriteKeyset(**public_keyset_handle, output_filename);
}
// Creates and signs a token with the given audience claim using the given
// private keyset and writes the signature to the output file.
void Sign(absl::string_view keyset_filename, absl::string_view audience,
absl::string_view output_filename) {
std::unique_ptr<crypto::tink::KeysetHandle> keyset_handle =
ReadKeyset(keyset_filename);
crypto::tink::util::StatusOr<std::unique_ptr<JwtPublicKeySign>>
jwt_public_key_sign = keyset_handle->GetPrimitive<JwtPublicKeySign>();
if (!jwt_public_key_sign.ok()) {
std::clog << "Getting JwtPublicKeySign-primitive from the factory failed: "
<< jwt_public_key_sign.status().message() << std::endl;
exit(1);
}
std::clog << "Generating a token with audience '" << audience
<< "' using private keyset from file " << keyset_filename << "..."
<< std::endl;
crypto::tink::util::StatusOr<RawJwt> raw_jwt =
RawJwtBuilder()
.AddAudience(audience)
.SetExpiration(absl::Now() + absl::Seconds(100))
.Build();
if (!raw_jwt.ok()) {
std::clog << "Building RawJwt failed: " << raw_jwt.status().message()
<< std::endl;
exit(1);
}
crypto::tink::util::StatusOr<std::string> token =
(*jwt_public_key_sign)->SignAndEncode(*raw_jwt);
if (!token.ok()) {
std::clog << "Error while generating the token: "
<< token.status().message() << std::endl;
exit(1);
}
std::clog << "Writing the resulting token to file " << output_filename
<< "..." << std::endl;
WriteFile(*token, output_filename);
}
// Verifies a token using the given public keyset and writes the result to the
// output file.
void Verify(absl::string_view keyset_filename,
absl::string_view expected_audience,
absl::string_view token_filename,
absl::string_view output_filename) {
std::unique_ptr<KeysetHandle> keyset_handle = ReadKeyset(keyset_filename);
crypto::tink::util::StatusOr<std::unique_ptr<JwtPublicKeyVerify>> verifier =
keyset_handle->GetPrimitive<crypto::tink::JwtPublicKeyVerify>();
if (!verifier.ok()) {
std::clog << "Getting JwtPublicKeyVerify-primitive from the factory "
<< "failed: " << verifier.status().message() << std::endl;
exit(1);
}
std::clog << "Verifying token from file " << token_filename
<< " with expected audience '" << expected_audience
<< "' using public keyset from file " << keyset_filename << "..."
<< std::endl;
std::string token = ReadFile(token_filename);
crypto::tink::util::StatusOr<JwtValidator> validator =
crypto::tink::JwtValidatorBuilder()
.ExpectAudience(expected_audience)
.Build();
if (!validator.ok()) {
std::clog << "Building validator failed: " << validator.status().message()
<< std::endl;
exit(1);
}
std::string result;
crypto::tink::util::StatusOr<crypto::tink::VerifiedJwt> verified_jwt =
(*verifier)->VerifyAndDecode(token, *validator);
if (!verified_jwt.ok()) {
std::clog << "Error while verifying the token: "
<< verified_jwt.status().message() << std::endl;
result = "invalid";
} else {
absl::Duration ttl = *verified_jwt->GetExpiration() - absl::Now();
std::clog << "Token is valid for " << ttl << "." << std::endl;
result = "valid";
}
std::clog << "Writing the result to file " << output_filename
<< "..." << std::endl;
WriteFile(result, output_filename);
}
int main(int argc, char** argv) {
if (argc == 1) {
PrintUsageInfo();
exit(1);
}
crypto::tink::util::Status status = crypto::tink::JwtSignatureRegister();
if (!status.ok()) {
std::clog << "JwtSignatureRegister() failed: " << status.message()
<< std::endl;
exit(1);
}
std::string operation = argv[1];
if (operation == "gen-private-key") {
if (argc != 3) {
PrintUsageInfo();
exit(1);
}
std::string output_filename = argv[2];
GeneratePrivateKey(output_filename);
} else if (operation == "get-public-key") {
if (argc != 4) {
PrintUsageInfo();
exit(1);
}
std::string private_keyset_filename = argv[2];
std::string output_filename = argv[3];
ExtractPublicKey(private_keyset_filename, output_filename);
} else if (operation == "sign") {
if (argc != 5) {
PrintUsageInfo();
exit(1);
}
std::string keyset_filename = argv[2];
std::string audience = argv[3];
std::string output_filename = argv[4];
Sign(keyset_filename, audience, output_filename);
} else if (operation == "verify") {
if (argc != 6) {
PrintUsageInfo();
exit(1);
}
std::string keyset_filename = argv[2];
std::string audience = argv[3];
std::string token_filename = argv[4];
std::string output_filename = argv[5];
Verify(keyset_filename, audience, token_filename, output_filename);
} else {
std::clog << "Unknown operation. Supported operations are: "
<< "gen-private-key get-public-key sign verify" << std::endl;
exit(1);
}
std::clog << "Done!" << std::endl;
return 0;
}