کتابخانه JWT Tink اجازه ایجاد و تأیید توکن های وب JSON (JWT) را می دهد.
JWT ها را ایجاد کنید
ما برای اکثر موارد استفاده، موارد اولیه JwtPublicKeySign را با نوع کلید JWT_ES256 توصیه می کنیم.
مثال های زیر نحوه ایجاد JWT و نحوه تبدیل مجموعه کلید عمومی به فرمت مجموعه 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; }
برو
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 }
جاوا
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() {} }
پایتون
"""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)
یک روش رایج برای اشتراک گذاری مجموعه کلید عمومی، فرمت مجموعه JWK است. مثال زیر نحوه تبدیل مجموعه کلید عمومی به فرمت مجموعه 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; }
برو
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"}]} }
جاوا
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() {} }
پایتون
"""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)
JWT ها را تأیید کنید
ما برای اکثر موارد استفاده، موارد اولیه JwtPublicKeyVerify را با نوع کلید JWT_ES256 توصیه می کنیم.
مثالهای زیر نشان میدهند که چگونه میتوان JWTها را با استفاده از یک مجموعه کلید عمومی در قالب مجموعه JWK، که یک فرمت رایج برای اشتراکگذاری مجموعه کلیدهای عمومی با سایر طرفها است، تأیید کرد.
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; }
برو
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 }
جاوا
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() {} }
پایتون
"""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)
JSON Web Tokens (JWTs)
Tink از تولید و تأیید JWT ها پشتیبانی می کند، که یک استاندارد پرکاربرد در وب هستند. پیادهسازی JWT Tink زیرمجموعهای از استاندارد JWT تعریف شده در RFC 7519 را ارائه میکند که تیم Tink استفاده از آن را ایمن میداند و به خوبی در کتابخانه Tink قرار میگیرد.
Tink از بخشهایی از استاندارد که به ندرت استفاده میشوند یا استفاده صحیح از آنها دشوار است، پشتیبانی نمیکند. اینها محدودیت ها هستند:
- Tink فقط از فرمت JWS Compact Serialization پشتیبانی می کند. JWS JSON Serialization و JWE پشتیبانی نمی شوند.
- Tink از مقدار
None
در هدرalg
پشتیبانی نمی کند. - Tink فقط از هدرهای
typ
,alg
وkid
پشتیبانی می کند . همه سرصفحه های دیگر پشتیبانی نمی شوند. - Tink اجازه نمی دهد توکن ها قبل از تأیید امضا یا MAC تجزیه شوند.
امضاهای JWT
اگر توکنها توسط موجودیتهای مختلف تولید و تأیید میشوند، باید از کلیدهای نامتقارن با کلیدهای اولیه JwtPublicKeySign
و JwtPublicKeyVerify
استفاده کنید. کلید خصوصی برای تولید توکن ها و کلید عمومی برای تایید توکن ها استفاده می شود. الگوریتم های پشتیبانی شده توسط این الگوریتم های اولیه عبارتند از: ES256
، ES384
، ES512
، RS256
، RS384
، RS512
، PS256
، PS384
و PS512
.
یک نوع کلید را انتخاب کنید
امضاهای JWT از انواع کلیدهای متفاوتی نسبت به امضای دیجیتال معمولی در Tink استفاده می کنند. این مورد نیاز است زیرا برخی از ابرداده ها (مانند alg
و kid
) باید با کلید ذخیره شوند.
ما استفاده از JWT_ES256
را برای بیشتر موارد استفاده توصیه می کنیم. توکنهایی که با این نوع کلید تولید میشوند، همیشه یک هدر kid
دارند. اگر توکنهای کمی کوتاهتر را بدون هدر kid
ترجیح میدهید، نوع کلید JWT_ES256_RAW
را انتخاب کنید. برای همه انواع کلیدهای پشتیبانی شده، به انواع کلیدهای پشتیبانی شده مراجعه کنید.
توزیع مجموعه کلید عمومی
Tink به مجموعه کلیدهای عمومی اجازه می دهد تا به فرمت JWK Sets تعریف شده در RFC 7517 تبدیل شوند و اکثر کتابخانه های JWT آن را درک می کنند.
Tink از صادرات کلیدهای عمومی JWT در هر قالب دیگری پشتیبانی نمی کند. دلیل این امر این است که سایر قالبها حاوی متادیتای alg
و kid
برای استفاده در تأیید نیستند، که استفاده از آنها را مستعد خطا میکند و ممکن است چرخش کلیدها را دشوارتر کند.
ترجیحاً نه تنها یک بار مجموعه کلیدهای عمومی را به اشتراک بگذارید، بلکه راهی برای به روز رسانی خودکار مجموعه کلیدهای عمومی ارائه دهید. (اگر نه، چرخش به یک کلید جدید بسیار سخت است.) این اغلب با انتشار مجموعه کلید عمومی در یک URL مطمئن و ایمن انجام می شود. سروری که توکن ها را تأیید می کند، باید به طور دوره ای مجموعه کلیدهای عمومی را از آن URL، مثلاً یک بار در روز، دوباره واکشی کند. برای چرخاندن کلید، کلید عمومی جدید باید حداقل یک روز قبل از استفاده برای امضای نشانه ها به مجموعه کلیدهای عمومی اضافه شود. در غیر این صورت، توکنهای جدید امضا شده با کلید خصوصی جدید توسط سرورهایی که هنوز از مجموعه کلید عمومی قدیمی استفاده میکنند رد میشوند.
JWT MAC
Tink همچنین از JWT با کلیدهای متقارن با JwtMac
اولیه پشتیبانی می کند. فقط در صورتی از این اولیه استفاده کنید که توکن ها توسط یک موجودیت تولید و تأیید شوند. الگوریتم های پشتیبانی شده توسط این اولیه HS256
، HS384
و HS512
هستند.
یک نوع کلید را انتخاب کنید
انواع کلید MAC JWT با انواع کلیدهای MAC معمولی متفاوت است. ما استفاده از JWT_HS256
را برای بیشتر موارد استفاده توصیه می کنیم.