La bibliothèque JWT de Tink permet de créer et de valider des jetons Web JSON (JWT).
Créer des jetons JWT
Nous recommandons les primitives JwtPublicKeySign avec le type de clé JWT_ES256 pour la plupart des cas d'utilisation.
Les exemples suivants montrent comment créer des jetons JWT et comment convertir le keyset public au format JWK.
C++
// An example for signing JSON Web Tokens (JWT). #include <iostream> #include <memory> #include <ostream> #include <string> #include <utility> #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" #include "absl/strings/string_view.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "tink/config/global_registry.h" #include "util/util.h" #include "tink/jwt/jwt_public_key_sign.h" #include "tink/jwt/jwt_signature_config.h" #include "tink/jwt/raw_jwt.h" #include "tink/keyset_handle.h" #include "tink/util/status.h" ABSL_FLAG(std::string, keyset_filename, "", "Keyset file in JSON format"); ABSL_FLAG(std::string, audience, "", "Expected audience in the token"); ABSL_FLAG(std::string, token_filename, "", "Path to the token file"); namespace { using ::crypto::tink::JwtPublicKeySign; using ::crypto::tink::KeysetHandle; using ::crypto::tink::RawJwt; using ::crypto::tink::RawJwtBuilder; using ::crypto::tink::util::Status; using ::crypto::tink::util::StatusOr; void ValidateParams() { // ... } } // namespace namespace tink_cc_examples { // JWT sign example CLI implementation. Status JwtSign(const std::string& keyset_filename, absl::string_view audience, const std::string& token_filename) { Status result = crypto::tink::JwtSignatureRegister(); if (!result.ok()) return result; // Read the keyset from file. StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = ReadJsonCleartextKeyset(keyset_filename); if (!keyset_handle.ok()) return keyset_handle.status(); StatusOr<RawJwt> raw_jwt = RawJwtBuilder() .AddAudience(audience) .SetExpiration(absl::Now() + absl::Seconds(100)) .Build(); if (!raw_jwt.ok()) return raw_jwt.status(); StatusOr<std::unique_ptr<JwtPublicKeySign>> jwt_signer = (*keyset_handle) ->GetPrimitive<crypto::tink::JwtPublicKeySign>( crypto::tink::ConfigGlobalRegistry()); if (!jwt_signer.ok()) return jwt_signer.status(); StatusOr<std::string> token = (*jwt_signer)->SignAndEncode(*raw_jwt); if (!token.ok()) return token.status(); return WriteToFile(*token, token_filename); } } // namespace tink_cc_examples int main(int argc, char** argv) { absl::ParseCommandLine(argc, argv); ValidateParams(); std::string keyset_filename = absl::GetFlag(FLAGS_keyset_filename); std::string audience = absl::GetFlag(FLAGS_audience); std::string token_filename = absl::GetFlag(FLAGS_token_filename); std::clog << "Using keyset in " << keyset_filename << " to "; std::clog << " generate and sign a token using audience '" << audience << "'; the resulting signature is written to " << token_filename << '\n'; CHECK_OK( tink_cc_examples::JwtSign(keyset_filename, audience, token_filename)); return 0; }
Go
func Example_signAndVerify() { // A private keyset created with // "tinkey create-keyset --key-template=JWT_ES256 --out private_keyset.cfg". // Note that this keyset has the secret key information in cleartext. privateJSONKeyset := `{ "primaryKeyId": 1742360595, "key": [ { "keyData": { "typeUrl": "type.googleapis.com/google.crypto.tink.JwtEcdsaPrivateKey", "value": "GiBgVYdAPg3Fa2FVFymGDYrI1trHMzVjhVNEMpIxG7t0HRJGIiBeoDMF9LS5BDCh6YgqE3DjHwWwnEKEI3WpPf8izEx1rRogbjQTXrTcw/1HKiiZm2Hqv41w7Vd44M9koyY/+VsP+SAQAQ==", "keyMaterialType": "ASYMMETRIC_PRIVATE" }, "status": "ENABLED", "keyId": 1742360595, "outputPrefixType": "TINK" } ] }` // The corresponding public keyset created with // "tinkey create-public-keyset --in private_keyset.cfg" publicJSONKeyset := `{ "primaryKeyId": 1742360595, "key": [ { "keyData": { "typeUrl": "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey", "value": "EAEaIG40E1603MP9RyoomZth6r+NcO1XeODPZKMmP/lbD/kgIiBeoDMF9LS5BDCh6YgqE3DjHwWwnEKEI3WpPf8izEx1rQ==", "keyMaterialType": "ASYMMETRIC_PUBLIC" }, "status": "ENABLED", "keyId": 1742360595, "outputPrefixType": "TINK" } ] }` // Create a keyset handle from the cleartext private keyset in the previous // step. The keyset handle provides abstract access to the underlying keyset to // limit the access of the raw key material. WARNING: In practice, // it is unlikely you will want to use a insecurecleartextkeyset, as it implies // that your key material is passed in cleartext, which is a security risk. // Consider encrypting it with a remote key in Cloud KMS, AWS KMS or HashiCorp Vault. // See https://github.com/google/tink/blob/master/docs/GOLANG-HOWTO.md#storing-and-loading-existing-keysets. privateKeysetHandle, err := insecurecleartextkeyset.Read( keyset.NewJSONReader(bytes.NewBufferString(privateJSONKeyset))) if err != nil { log.Fatal(err) } // Retrieve the JWT Signer primitive from privateKeysetHandle. signer, err := jwt.NewSigner(privateKeysetHandle) if err != nil { log.Fatal(err) } // Use the primitive to create and sign a token. In this case, the primary key of the // keyset will be used (which is also the only key in this example). expiresAt := time.Now().Add(time.Hour) audience := "example audience" subject := "example subject" rawJWT, err := jwt.NewRawJWT(&jwt.RawJWTOptions{ Audience: &audience, Subject: &subject, ExpiresAt: &expiresAt, }) if err != nil { log.Fatal(err) } token, err := signer.SignAndEncode(rawJWT) if err != nil { log.Fatal(err) } // Create a keyset handle from the keyset containing the public key. Because the // public keyset does not contain any secrets, we can use [keyset.ReadWithNoSecrets]. publicKeysetHandle, err := keyset.ReadWithNoSecrets( keyset.NewJSONReader(bytes.NewBufferString(publicJSONKeyset))) if err != nil { log.Fatal(err) } // Retrieve the Verifier primitive from publicKeysetHandle. verifier, err := jwt.NewVerifier(publicKeysetHandle) if err != nil { log.Fatal(err) } // Verify the signed token. validator, err := jwt.NewValidator(&jwt.ValidatorOpts{ExpectedAudience: &audience}) if err != nil { log.Fatal(err) } verifiedJWT, err := verifier.VerifyAndDecode(token, validator) if err != nil { log.Fatal(err) } // Extract subject claim from the token. if !verifiedJWT.HasSubject() { log.Fatal(err) } extractedSubject, err := verifiedJWT.Subject() if err != nil { log.Fatal(err) } fmt.Println(extractedSubject) // Output: example subject }
Java
package jwt; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.crypto.tink.InsecureSecretKeyAccess; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.RegistryConfiguration; import com.google.crypto.tink.TinkJsonProtoKeysetFormat; import com.google.crypto.tink.jwt.JwtPublicKeySign; import com.google.crypto.tink.jwt.JwtSignatureConfig; import com.google.crypto.tink.jwt.RawJwt; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; /** * A command-line utility for signing JSON Web Tokens (JWTs). * * <p>It loads cleartext private keys from disk - this is not recommended! * * <p>It requires the following arguments: * * <ul> * <li>private-keyset-file: Name of the input file containing the private keyset. * <li>audience: The audience claim to be used in the token * <li>token-file: name of the output file containing the signed JWT. */ public final class JwtSign { public static void main(String[] args) throws Exception { if (args.length != 3) { System.err.printf("Expected 3 parameters, got %d\n", args.length); System.err.println("Usage: java JwtSign private-keyset-file audience token-file"); System.exit(1); } Path privateKeysetFile = Paths.get(args[0]); String audience = args[1]; Path tokenFile = Paths.get(args[2]); // Register all JWT signature key types with the Tink runtime. JwtSignatureConfig.register(); // Read the private keyset into a KeysetHandle. KeysetHandle privateKeysetHandle = TinkJsonProtoKeysetFormat.parseKeyset( new String(Files.readAllBytes(privateKeysetFile), UTF_8), InsecureSecretKeyAccess.get()); // Get the primitive. JwtPublicKeySign signer = privateKeysetHandle.getPrimitive(RegistryConfiguration.get(), JwtPublicKeySign.class); // Use the primitive to sign a token that expires in 100 seconds. RawJwt rawJwt = RawJwt.newBuilder() .addAudience(audience) .setExpiration(Instant.now().plusSeconds(100)) .build(); String signedToken = signer.signAndEncode(rawJwt); Files.write(tokenFile, signedToken.getBytes(UTF_8)); } private JwtSign() {} }
Python
"""A utility for creating and signing JSON Web Tokens (JWT). It loads cleartext keys from disk - this is not recommended! """ import datetime from absl import app from absl import flags from absl import logging import tink from tink import jwt from tink import secret_key_access _PRIVATE_KEYSET_PATH = flags.DEFINE_string( 'private_keyset_path', None, 'Path to the keyset used for the JWT signature operation.') _AUDIENCE = flags.DEFINE_string('audience', None, 'Audience to be used in the token') _TOKEN_PATH = flags.DEFINE_string('token_path', None, 'Path to the token file.') def main(argv): del argv # Unused. # Initialise Tink jwt.register_jwt_signature() # Read the keyset into a KeysetHandle with open(_PRIVATE_KEYSET_PATH.value, 'rt') as keyset_file: try: text = keyset_file.read() keyset_handle = tink.json_proto_keyset_format.parse( text, secret_key_access.TOKEN ) except tink.TinkError as e: logging.exception('Error reading keyset: %s', e) return 1 now = datetime.datetime.now(tz=datetime.timezone.utc) # Get the JwtPublicKeySign primitive try: jwt_sign = keyset_handle.primitive(jwt.JwtPublicKeySign) except tink.TinkError as e: logging.exception('Error creating JwtPublicKeySign: %s', e) return 1 # Create token raw_jwt = jwt.new_raw_jwt( audiences=[_AUDIENCE.value], expiration=now + datetime.timedelta(seconds=100)) token = jwt_sign.sign_and_encode(raw_jwt) with open(_TOKEN_PATH.value, 'wt') as token_file: token_file.write(token) logging.info('Token has been written to %s', _TOKEN_PATH.value) if __name__ == '__main__': flags.mark_flags_as_required( ['private_keyset_path', 'audience', 'token_path']) app.run(main)
Le format JWK Set est un moyen courant de partager le keyset public. L'exemple suivant montre comment convertir le keyset public au format JWK.
C++
// An example for converting a Tink keyset with public keys into a JWK set. #include <iostream> #include <memory> #include <ostream> #include <string> #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" #include "util/util.h" #include "tink/jwt/jwk_set_converter.h" #include "tink/jwt/jwt_signature_config.h" #include "tink/keyset_handle.h" #include "tink/util/status.h" ABSL_FLAG(std::string, public_keyset_filename, "", "Public keyset file in Tink's JSON format"); ABSL_FLAG(std::string, public_jwk_set_filename, "", "Path to the output public JWK set file"); namespace { using ::crypto::tink::JwkSetFromPublicKeysetHandle; using ::crypto::tink::KeysetHandle; using ::crypto::tink::util::Status; using ::crypto::tink::util::StatusOr; void ValidateParams() { // ... } } // namespace namespace tink_cc_examples { Status JwtGeneratePublicJwkSet(const std::string& public_keyset_filename, const std::string& public_jwk_set_filename) { Status result = crypto::tink::JwtSignatureRegister(); if (!result.ok()) return result; StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = ReadJsonCleartextKeyset(public_keyset_filename); if (!keyset_handle.ok()) return keyset_handle.status(); StatusOr<std::string> public_jwk_set = JwkSetFromPublicKeysetHandle(**keyset_handle); if (!public_jwk_set.ok()) return keyset_handle.status(); return WriteToFile(*public_jwk_set, public_jwk_set_filename); } } // namespace tink_cc_examples int main(int argc, char** argv) { absl::ParseCommandLine(argc, argv); ValidateParams(); std::string public_keyset_filename = absl::GetFlag(FLAGS_public_keyset_filename); std::string public_jwk_set_filename = absl::GetFlag(FLAGS_public_jwk_set_filename); std::clog << "Convert public keyset in " << public_keyset_filename << " to "; std::clog << " to JWK set format; the result is written to " << public_jwk_set_filename << '\n'; CHECK_OK(tink_cc_examples::JwtGeneratePublicJwkSet(public_keyset_filename, public_jwk_set_filename)); return 0; }
Go
func Example_generateJWKS() { // A Tink keyset in JSON format with one JWT public key. publicJSONKeyset := `{ "primaryKeyId": 1742360595, "key": [ { "keyData": { "typeUrl": "type.googleapis.com/google.crypto.tink.JwtEcdsaPublicKey", "value": "EAEaIG40E1603MP9RyoomZth6r+NcO1XeODPZKMmP/lbD/kgIiBeoDMF9LS5BDCh6YgqE3DjHwWwnEKEI3WpPf8izEx1rQ==", "keyMaterialType": "ASYMMETRIC_PUBLIC" }, "status": "ENABLED", "keyId": 1742360595, "outputPrefixType": "TINK" } ] }` // Create a keyset handle from the keyset containing the public key. Because the // public keyset does not contain any secrets, we can use [keyset.ReadWithNoSecrets]. publicKeysetHandle, err := keyset.ReadWithNoSecrets( keyset.NewJSONReader(bytes.NewBufferString(publicJSONKeyset))) if err != nil { log.Fatal(err) } // Create a publicJWKset from publicKeysetHandle. publicJWKset, err := jwt.JWKSetFromPublicKeysetHandle(publicKeysetHandle) if err != nil { log.Fatal(err) } // Remove whitespace so that we can compare it to the expected string. compactPublicJWKset := &bytes.Buffer{} err = json.Compact(compactPublicJWKset, publicJWKset) if err != nil { log.Fatal(err) } fmt.Println(compactPublicJWKset.String()) // Output: // {"keys":[{"alg":"ES256","crv":"P-256","key_ops":["verify"],"kid":"Z9pQEw","kty":"EC","use":"sig","x":"bjQTXrTcw_1HKiiZm2Hqv41w7Vd44M9koyY_-VsP-SA","y":"XqAzBfS0uQQwoemIKhNw4x8FsJxChCN1qT3_IsxMda0"}]} }
Java
package jwt; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.crypto.tink.InsecureSecretKeyAccess; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.TinkJsonProtoKeysetFormat; import com.google.crypto.tink.jwt.JwkSetConverter; import com.google.crypto.tink.jwt.JwtSignatureConfig; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; /** * A command-line example for generating the public JWT keyset in JWK set format. * * <p>It loads cleartext private keys from disk - this is not recommended! * * <p>It requires the following arguments: * * <ul> * <li>private-keyset-file: Name of the input file containing the private keyset. * <li>public-jwkset-file: Name of the output file containing the public key in JWK set format. */ public final class JwtGeneratePublicJwkSet { public static void main(String[] args) throws Exception { if (args.length != 2) { System.err.printf("Expected 2 parameters, got %d\n", args.length); System.err.println( "Usage: java JwtGeneratePublicJwkSet private-keyset-file public-jwk-set-file"); System.exit(1); } Path privateKeysetFile = Paths.get(args[0]); Path publicJwkSetFile = Paths.get(args[1]); // Register all JWT signature key types with the Tink runtime. JwtSignatureConfig.register(); // Read the keyset into a KeysetHandle. KeysetHandle privateKeysetHandle = TinkJsonProtoKeysetFormat.parseKeyset( new String(Files.readAllBytes(privateKeysetFile), UTF_8), InsecureSecretKeyAccess.get()); // Export the public keyset as JWK set. String publicJwkSet = JwkSetConverter.fromPublicKeysetHandle(privateKeysetHandle.getPublicKeysetHandle()); Files.write(publicJwkSetFile, publicJwkSet.getBytes(UTF_8)); } private JwtGeneratePublicJwkSet() {} }
Python
"""A utility for generating the public JWK set from the public keyset. """ from absl import app from absl import flags from absl import logging import tink from tink import jwt _PUBLIC_KEYSET_PATH = flags.DEFINE_string( 'public_keyset_path', None, 'Path to the public keyset in Tink JSON format.') _PUBLIC_JWK_SET_PATH = flags.DEFINE_string( 'public_jwk_set_path', None, 'Path to public keyset in JWK format.') def main(argv): del argv # Unused. # Initialise Tink jwt.register_jwt_signature() # Read the keyset into a KeysetHandle with open(_PUBLIC_KEYSET_PATH.value, 'rt') as keyset_file: try: text = keyset_file.read() public_keyset_handle = tink.json_proto_keyset_format.parse_without_secret( text ) except tink.TinkError as e: logging.exception('Error reading keyset: %s', e) return 1 # Export Public Keyset as JWK set public_jwk_set = jwt.jwk_set_from_public_keyset_handle(public_keyset_handle) with open(_PUBLIC_JWK_SET_PATH.value, 'wt') as public_jwk_set_file: public_jwk_set_file.write(public_jwk_set) logging.info('The public JWK set has been written to %s', _PUBLIC_JWK_SET_PATH.value) if __name__ == '__main__': flags.mark_flags_as_required(['public_keyset_path', 'public_jwk_set_path']) app.run(main)
Vérifier les jetons JWT
Nous recommandons les primitives JwtPublicKeyVerify avec le type de clé JWT_ES256 pour la plupart des cas d'utilisation.
Les exemples suivants montrent comment valider des jetons JWT à l'aide d'un ensemble de clés publiques au format JWKset, qui est un format couramment utilisé pour partager des ensembles de clés publiques avec d'autres parties.
C++
// A utility for creating, signing and verifying JSON Web Tokens (JWT). #include <iostream> #include <memory> #include <ostream> #include <string> #include <utility> #include "absl/flags/flag.h" #include "absl/flags/parse.h" #include "absl/log/check.h" #include "absl/strings/string_view.h" #include "tink/config/global_registry.h" #include "util/util.h" #include "tink/jwt/jwk_set_converter.h" #include "tink/jwt/jwt_public_key_verify.h" #include "tink/jwt/jwt_signature_config.h" #include "tink/jwt/jwt_validator.h" #include "tink/keyset_handle.h" #include "tink/util/status.h" ABSL_FLAG(std::string, jwk_set_filename, "", "Path to the JWK set file"); ABSL_FLAG(std::string, audience, "", "Expected audience in the token"); ABSL_FLAG(std::string, token_filename, "", "Path to the token file"); namespace { using ::crypto::tink::JwkSetToPublicKeysetHandle; using ::crypto::tink::JwtPublicKeyVerify; using ::crypto::tink::JwtValidator; using ::crypto::tink::KeysetHandle; using ::crypto::tink::util::Status; using ::crypto::tink::util::StatusOr; void ValidateParams() { // ... } } // namespace namespace tink_cc_examples { // JWT verify example CLI implementation. Status JwtVerify(const std::string& jwk_set_filename, absl::string_view audience, const std::string& token_filename) { Status result = crypto::tink::JwtSignatureRegister(); if (!result.ok()) return result; // Read the JWK set from file and convert it. StatusOr<std::string> jwk_set = ReadFile(jwk_set_filename); if (!jwk_set.ok()) return jwk_set.status(); StatusOr<std::unique_ptr<KeysetHandle>> keyset_handle = JwkSetToPublicKeysetHandle(*jwk_set); // Read the token. StatusOr<std::string> token = ReadFile(token_filename); if (!token.ok()) return token.status(); StatusOr<JwtValidator> validator = crypto::tink::JwtValidatorBuilder().ExpectAudience(audience).Build(); if (!validator.ok()) return validator.status(); StatusOr<std::unique_ptr<JwtPublicKeyVerify>> jwt_verifier = (*keyset_handle) ->GetPrimitive<crypto::tink::JwtPublicKeyVerify>( crypto::tink::ConfigGlobalRegistry()); if (!jwt_verifier.ok()) return jwt_verifier.status(); return (*jwt_verifier)->VerifyAndDecode(*token, *validator).status(); } } // namespace tink_cc_examples int main(int argc, char** argv) { absl::ParseCommandLine(argc, argv); ValidateParams(); std::string jwk_set_filename = absl::GetFlag(FLAGS_jwk_set_filename); std::string audience = absl::GetFlag(FLAGS_audience); std::string token_filename = absl::GetFlag(FLAGS_token_filename); std::clog << "Using keyset in " << jwk_set_filename << " to "; std::clog << " verify a token with expected audience '" << audience << '\n'; CHECK_OK( tink_cc_examples::JwtVerify(jwk_set_filename, audience, token_filename)); return 0; }
Go
func Example_verifyWithJWKS() { // A signed token with the subject 'example subject', audience 'example audience'. // and expiration on 2023-03-23. token := `eyJhbGciOiJFUzI1NiIsICJraWQiOiJaOXBRRXcifQ.eyJhdWQiOiJleGFtcGxlIGF1ZGllbmNlIiwgImV4cCI6MTY3OTUzMzIwMCwgInN1YiI6ImV4YW1wbGUgc3ViamVjdCJ9.ZvI0T84fJ1aouiB7n62kHOmbm0Hpfiz0JtYs15XVDT8KyoVYZ8hu_DGJUN47BqZIbuOI-rdu9TxJvutj8uF3Ow` // A public keyset in the JWK set format. publicJWKset := `{ "keys":[ { "alg":"ES256", "crv":"P-256", "key_ops":["verify"], "kid":"Z9pQEw", "kty":"EC", "use":"sig", "x":"bjQTXrTcw_1HKiiZm2Hqv41w7Vd44M9koyY_-VsP-SA", "y":"XqAzBfS0uQQwoemIKhNw4x8FsJxChCN1qT3_IsxMda0" } ] }` // Create a keyset handle from publicJWKset. publicKeysetHandle, err := jwt.JWKSetToPublicKeysetHandle([]byte(publicJWKset)) if err != nil { log.Fatal(err) } // Retrieve the Verifier primitive from publicKeysetHandle. verifier, err := jwt.NewVerifier(publicKeysetHandle) if err != nil { log.Fatal(err) } // Verify the signed token. For this example, we use a fixed date. Usually, you would // either not set FixedNow, or set it to the current time. audience := "example audience" validator, err := jwt.NewValidator(&jwt.ValidatorOpts{ ExpectedAudience: &audience, FixedNow: time.Date(2023, 3, 23, 0, 0, 0, 0, time.UTC), }) if err != nil { log.Fatal(err) } verifiedJWT, err := verifier.VerifyAndDecode(token, validator) if err != nil { log.Fatal(err) } // Extract subject claim from the token. if !verifiedJWT.HasSubject() { log.Fatal(err) } extractedSubject, err := verifiedJWT.Subject() if err != nil { log.Fatal(err) } fmt.Println(extractedSubject) // Output: example subject }
Java
package jwt; import static java.nio.charset.StandardCharsets.UTF_8; import com.google.crypto.tink.KeysetHandle; import com.google.crypto.tink.RegistryConfiguration; import com.google.crypto.tink.jwt.JwkSetConverter; import com.google.crypto.tink.jwt.JwtPublicKeyVerify; import com.google.crypto.tink.jwt.JwtSignatureConfig; import com.google.crypto.tink.jwt.JwtValidator; import com.google.crypto.tink.jwt.VerifiedJwt; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.List; /** * A command-line utility for verifying JSON Web Tokens (JWTs). * * <p>It requires the following arguments: * * <ul> * <li>public-jwkset-file: Name of the input file containing the public keyset in JWK set format. * <li>audience: The audience claim to be used in the token * <li>token-file: name of the input file containing the signed JWT. */ public final class JwtVerify { public static void main(String[] args) throws Exception { if (args.length != 3) { System.err.printf("Expected 3 parameters, got %d\n", args.length); System.err.println( "Usage: java JwtVerify public-jwk-set-file audience token-file"); System.exit(1); } Path publicJwkSetFile = Paths.get(args[0]); String audience = args[1]; Path tokenFile = Paths.get(args[2]); // Register all JWT signature key types with the Tink runtime. JwtSignatureConfig.register(); // Read the public keyset in JWK set format into a KeysetHandle. KeysetHandle publicKeysetHandle = JwkSetConverter.toPublicKeysetHandle( new String(Files.readAllBytes(publicJwkSetFile), UTF_8)); List<String> lines = Files.readAllLines(tokenFile, UTF_8); if (lines.size() != 1) { System.err.printf("The signature file should contain only one line, got %d", lines.size()); System.exit(1); } String signedToken = lines.get(0).trim(); // Get the primitive. JwtPublicKeyVerify verifier = publicKeysetHandle.getPrimitive(RegistryConfiguration.get(), JwtPublicKeyVerify.class); // Use the primitive to verify a token. JwtValidator validator = JwtValidator.newBuilder().expectAudience(audience).build(); VerifiedJwt verifiedJwt = verifier.verifyAndDecode(signedToken, validator); long seconds = ChronoUnit.SECONDS.between(Instant.now(), verifiedJwt.getExpiration()); System.out.println("Token is valid and expires in " + seconds + " seconds."); } private JwtVerify() {} }
Python
"""A utility for verifying Json Web Tokens (JWT).""" import datetime from absl import app from absl import flags from absl import logging import tink from tink import jwt FLAGS = flags.FLAGS _PUBLIC_JWK_SET_PATH = flags.DEFINE_string( 'public_jwk_set_path', None, 'Path to public keyset in JWK set format.') _AUDIENCE = flags.DEFINE_string('audience', None, 'Audience to be used in the token') _TOKEN_PATH = flags.DEFINE_string('token_path', None, 'Path to the signature file.') def main(argv): del argv # Unused. # Initialise Tink jwt.register_jwt_signature() with open(_PUBLIC_JWK_SET_PATH.value, 'rt') as public_jwk_set_file: try: text = public_jwk_set_file.read() keyset_handle = jwt.jwk_set_to_public_keyset_handle(text) except tink.TinkError as e: logging.exception('Error reading public JWK set: %s', e) return 1 now = datetime.datetime.now(tz=datetime.timezone.utc) try: jwt_verify = keyset_handle.primitive(jwt.JwtPublicKeyVerify) except tink.TinkError as e: logging.exception('Error creating JwtPublicKeyVerify: %s', e) return 1 # Verify token with open(_TOKEN_PATH.value, 'rt') as token_file: token = token_file.read() validator = jwt.new_validator(expected_audience=_AUDIENCE.value) try: verified_jwt = jwt_verify.verify_and_decode(token, validator) expires_in = verified_jwt.expiration() - now logging.info('Token is valid and expires in %s seconds', expires_in.seconds) return 0 except tink.TinkError as e: logging.info('JWT verification failed: %s', e) return 1 if __name__ == '__main__': flags.mark_flags_as_required(['audience', 'token_path']) app.run(main)
Jetons Web JSON (JWT)
Tink permet de générer et de valider des JWT, qui sont une norme largement utilisée sur le Web. L'implémentation JWT de Tink fournit un sous-ensemble de la norme JWT définie dans la RFC 7519 que l'équipe Tink considère comme sûre et qui s'intègre bien à la bibliothèque Tink.
Tink ne prend pas en charge les parties de la norme qui sont rarement utilisées ou difficiles à utiliser correctement. Voici les limites :
- Tink n'est compatible qu'avec le format JWS Compact Serialization. JWS JSON Serialization et JWE ne sont pas acceptés.
- Tink n'est pas compatible avec la valeur
None
dans l'en-têtealg
. - Tink n'accepte que les en-têtes
typ
,alg
etkid
. Les autres en-têtes ne sont pas acceptés. - Tink n'autorise pas l'analyse des jetons avant la vérification de la signature ou du MAC.
Signatures JWT
Si les jetons sont générés et validés par différentes entités, vous devez utiliser des clés asymétriques avec les primitives JwtPublicKeySign
et JwtPublicKeyVerify
.
La clé privée sert à générer des jetons, tandis que la clé publique permet de les valider. Les algorithmes compatibles avec ces primitives sont les suivants : ES256
, ES384
, ES512
, RS256
, RS384
, RS512
, PS256
, PS384
et PS512
.
Choisir un type de clé
Les signatures JWT utilisent des types de clés différents de la signature numérique normale dans Tink. Cela est nécessaire, car certaines métadonnées (telles que alg
et kid
) doivent être stockées avec la clé.
Nous vous recommandons d'utiliser JWT_ES256
pour la plupart des cas d'utilisation. Les jetons générés avec ce type de clé comportent toujours un en-tête kid
. Si vous préférez des jetons légèrement plus courts sans en-tête kid
, choisissez le type de clé JWT_ES256_RAW
. Pour connaître tous les types de clés acceptés, consultez Types de clés acceptés.
Distribution de clés publiques
Tink permet de convertir les ensembles de clés publiques au format JWK Sets défini dans la RFC 7517, que la plupart des bibliothèques JWT comprennent.
Tink n'est pas compatible avec l'exportation de clés JWT publiques dans d'autres formats. En effet, les autres formats ne contiennent pas les métadonnées alg
et kid
à utiliser pour la validation, ce qui les rend plus sujets aux erreurs et peut rendre la rotation des clés plus difficile.
Il est préférable de ne pas se contenter de partager le keyset public une seule fois, mais de fournir un moyen de mettre à jour le keyset public automatiquement. (Sinon, la rotation vers une nouvelle clé est très difficile.) Cela se fait souvent en publiant le keyset public sur une URL fiable et sécurisée. Un serveur qui valide les jetons doit ensuite récupérer régulièrement l'ensemble de clés publiques à partir de cette URL (par exemple, une fois par jour). Pour alterner la clé, la nouvelle clé publique doit être ajoutée au keyset public au moins un jour avant son utilisation pour signer les jetons. Sinon, les nouveaux jetons signés avec la nouvelle clé privée seront refusés par les serveurs qui utilisent encore l'ancienne collection de clés publiques.
JWT MAC
Tink est également compatible avec les JWT avec clés symétriques avec le type primitif JwtMac
. N'utilisez cette primitive que si les jetons sont générés et validés par la même entité. Les algorithmes compatibles avec cette primitive sont HS256
, HS384
et HS512
.
Choisir un type de clé
Les types de clés MAC JWT sont différents des types de clés MAC standards. Nous vous recommandons d'utiliser JWT_HS256
pour la plupart des cas d'utilisation.