The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.
このページを編集

JSON Web トークン (JWT) のビルド、署名、暗号化

JSON Web Token (JWT) は、RFC 7519 仕様で定義されており、コンパクトで URL セーフな形式でクレームを表現する手段です。 クレームは JSON オブジェクトとしてエンコードされ、JSON Web Signature (JWS) 構造のペイロードとして使用することも、JSON Web Encryption (JWE) 構造の平文として使用することもできます。 この仕組みにより、クレームはデジタル署名されるか、メッセージ認証コード (MAC) によって整合性が保護され、さらに暗号化することが可能になります。

クレームの署名は、クレームを保護する最も一般的な方法です。 通常、JWT トークンは JSON 形式のクレームに署名することで生成されますが、これは JSON Web Signature (JWS) 仕様に基づいた手順で行われます。

クレームに機密情報が含まれる場合、JSON Web Encryption (JWE) 仕様を使用することで、その機密性を確保できます。 この方法では、クレームが暗号化された JWT が生成されます。

セキュリティーを強化するために、両方の方法を組み合わせることができます。 まずクレームに署名し、その後、作成された JWT をネスト化し、暗号化します。 このプロセスにより、クレームの機密性と整合性の両方が確保されます。

SmallRye JWT Build API は、これらすべてのオプションをサポートすることで、JWT クレームの保護を簡素化します。 この機能を提供するために、内部で Jose4J ライブラリーを使用しています。

依存関係

SmallRye JWT ビルド API を使用するには、プロジェクトに次の依存関係を追加します。

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-smallrye-jwt-build</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-smallrye-jwt-build")

SmallRye JWT Build API は、 quarkus-smallrye-jwt エクステンションがサポートする MicroProfile JWT エンドポイントを作成せずに、単独で使用できます。

JwtClaimsBuilder の作成とクレームの設定

最初のステップとして、以下のオプションの 1 つを使用して JwtClaimsBuilder を初期化し、それにいくつかのクレームを追加します。

import java.util.Collections;
import jakarta.json.Json;
import jakarta.json.JsonObject;
import io.smallrye.jwt.build.Jwt;
import io.smallrye.jwt.build.JwtClaimsBuilder;
import org.eclipse.microprofile.jwt.JsonWebToken;
...
// Create an empty builder and add some claims
JwtClaimsBuilder builder1 = Jwt.claims();
builder1.claim("customClaim", "custom-value").issuer("https://issuer.org");
// Alternatively, start with claims directly:
// JwtClaimsBuilder builder1 = Jwt.upn("Alice");

// Create a builder from an existing claims file
JwtClaimsBuilder builder2 = Jwt.claims("/tokenClaims.json");

// Create a builder from a map of claims
JwtClaimsBuilder builder3 = Jwt.claims(Collections.singletonMap("customClaim", "custom-value"));

// Create a builder from a JsonObject
JsonObject userName = Json.createObjectBuilder().add("username", "Alice").build();
JsonObject userAddress = Json.createObjectBuilder().add("city", "someCity").add("street", "someStreet").build();
JsonObject json = Json.createObjectBuilder(userName).add("address", userAddress).build();
JwtClaimsBuilder builder4 = Jwt.claims(json);

// Create a builder from a JsonWebToken
@Inject JsonWebToken token;
JwtClaimsBuilder builder5 = Jwt.claims(token);

API は Fluent であるため、Fluent Sequence の一部としてビルダーを初期化できます。

明示的に設定されていない場合、ビルダーは次のクレームを自動的に設定します。

  • iat (発行時刻): 現在の時刻

  • exp (有効期限): 現在の時刻から 5 分後 (smallrye.jwt.new-token.lifespan プロパティーでカスタマイズ可能)

  • jti (一意のトークン識別子)

ビルダーで直接設定しなくても済むように、次のプロパティーをグローバルに設定できます。

  • smallrye.jwt.new-token.issuer: デフォルトの発行者を指定します。

  • smallrye.jwt.new-token.audience: デフォルトのオーディエンスを指定します。

クレームを初期化して設定した後、次のステップとして、クレームを保護する方法を決定します。

クレームへの署名

クレームは、すぐに署名することも、 JSON Web Signature (JWS) ヘッダーを設定した後に署名することもできます。

import io.smallrye.jwt.build.Jwt;
...

// Sign the claims using an RSA private key loaded from the location specified by the 'smallrye.jwt.sign.key.location' property.
// No 'jws()' transition is required. The default algorithm is RS256.
String jwt1 = Jwt.claims("/tokenClaims.json").sign();

// Set the headers and sign the claims by using an RSA private key loaded in the code (the implementation of this method is omitted).
// Includes a 'jws()' transition to a 'JwtSignatureBuilder'. The default algorithm is RS256.

String jwt2 = Jwt.claims("/tokenClaims.json")
                 .jws()
                 .keyId("kid1")
                 .header("custom-header", "custom-value")
                 .sign(getPrivateKey());

デフォルトの動作:

  • alg (algorithm) ヘッダーはデフォルトで RS256 に設定されていることに注意してください。

  • kid プロパティーを含む単一の JSON Web Key (JWK) が使用される場合、署名鍵の識別子 (kid ヘッダー) を設定する必要はありません。

サポートされているキーとアルゴリズム:

  • クレームに署名するには、RSA 秘密鍵、楕円曲線 (EC) 秘密鍵、対称シークレットキーを使用できます。

  • RS256 はデフォルトの RSA 秘密鍵署名アルゴリズムです。

  • ES256 はデフォルトの EC 秘密鍵署名アルゴリズムです。

  • HS256 はデフォルトの対称鍵署名アルゴリズムです。

署名アルゴリズムをカスタマイズするには、 JwtSignatureBuilder API を使用します。例:

import io.smallrye.jwt.SignatureAlgorithm;
import io.smallrye.jwt.build.Jwt;

// Sign the claims using an RSA private key loaded from the location set with a 'smallrye.jwt.sign.key.location' property. The algorithm is PS256.
String jwt = Jwt.upn("Alice").jws().algorithm(SignatureAlgorithm.PS256).sign();

または、次のプロパティーを使用して署名アルゴリズムをグローバルに設定することもできます。

smallrye.jwt.new-token.signature-algorithm=PS256

このアプローチにより、よりシンプルな API シーケンスが実現します。

import io.smallrye.jwt.build.Jwt;

// Sign the claims using an RSA private key loaded from the location set with a 'smallrye.jwt.sign.key.location' property. The algorithm is PS256.
String jwt = Jwt.upn("Alice").sign();

sign のステップと encrypt のステップを組み合わせることで、 内側が署名され、暗号化された トークンを作成できます。 詳細は、Sign the claims and encrypt the nested JWT token セクションを参照してください。

クレームの暗号化

クレームは、署名方法と同様に、すぐに暗号化することも、 JSON Web Encryption (JWE) ヘッダーを設定した後に暗号化することもできます。 ただし、API は署名および内部署名操作をサポートするように最適化されているため、クレームを暗号化するには、常に jwe() から JwtEncryptionBuilder への遷移が必要です。

import io.smallrye.jwt.build.Jwt;
...

// Encrypt the claims using an RSA public key loaded from the location specified by the 'smallrye.jwt.encrypt.key.location' property.
// The default key encryption algorithm is RSA-OAEP.

String jwt1 = Jwt.claims("/tokenClaims.json").jwe().encrypt();

// Set the headers and encrypt the claims by using an RSA public key loaded in the code (the implementation of this method is omitted).
// The default key encryption algorithm is A256KW.
String jwt2 = Jwt.claims("/tokenClaims.json").jwe().header("custom-header", "custom-value").encrypt(getSecretKey());

デフォルトの動作:

  • alg (キー管理アルゴリズム) ヘッダーのデフォルトは RSA-OAEP です。

  • enc (コンテンツ暗号化) ヘッダーのデフォルトは A256GCM です。

サポートされているキーとアルゴリズム:

  • クレームを暗号化するには、RSA 公開鍵、楕円曲線 (EC) 公開鍵、対称シークレットキーを使用できます。

  • RSA-OAEP はデフォルトの RSA 公開鍵暗号化アルゴリズムです。

  • ECDH-ES はデフォルトの EC 公開鍵暗号化アルゴリズムです。

  • A256KW はデフォルトの対称鍵暗号化アルゴリズムです。

暗号化されたトークンを作成する際には、2つの暗号化処理が行われることに注意してください。

  1. 生成されたコンテンツ暗号鍵は、提供されたキーと RSA-OAEP などのキー暗号化アルゴリズムを使用して暗号化されます。

  2. クレームは、コンテンツ暗号鍵と A256GCM などのコンテンツ暗号化アルゴリズムを使用して暗号化されます。

JwtEncryptionBuilder API を使用して、キーとコンテンツの暗号化アルゴリズムをカスタマイズできます。例:

import io.smallrye.jwt.KeyEncryptionAlgorithm;
import io.smallrye.jwt.ContentEncryptionAlgorithm;
import io.smallrye.jwt.build.Jwt;

// Encrypt the claims using an RSA public key loaded from the location set with a 'smallrye.jwt.encrypt.key.location' property.
// Key encryption algorithm is RSA-OAEP-256. The content encryption algorithm is A256CBC-HS512.

String jwt = Jwt.subject("Bob").jwe()
    .keyAlgorithm(KeyEncryptionAlgorithm.RSA_OAEP_256)
    .contentAlgorithm(ContentEncryptionAlgorithm.A256CBC_HS512)
    .encrypt();

または、次のプロパティーを使用してアルゴリズムをグローバルに設定することもできます。

smallrye.jwt.new-token.key-encryption-algorithm=RSA-OAEP-256
smallrye.jwt.new-token.content-encryption-algorithm=A256CBC-HS512

この設定により、API シーケンスが簡素化されます。

import io.smallrye.jwt.build.Jwt;

// Encrypt the claims by using an RSA public key loaded from the location set with a 'smallrye.jwt.encrypt.key.location' property.
// Key encryption algorithm is RSA-OAEP-256. The content encryption algorithm is A256CBC-HS512.
String jwt = Jwt.subject("Bob").encrypt();

安全なトークン暗号化に関する推奨事項:

  • トークンが公開 RSA または EC キーで直接暗号化されると、どの当事者がトークンを送信したかを確認できません。 これに対処するには、特に Quarkus エンドポイントによってのみ管理される Cookie として JWT を使用する場合、直接暗号化には対称秘密鍵が推奨されます。

  • RSA または EC 公開鍵でトークンを暗号化する場合、署名鍵が利用可能な場合は、まずトークンに署名することを推奨します。詳細は、Sign the claims and encrypt the nested JWT token セクションを参照してください。

クレームへの署名とネストされた JWT トークンの暗号化

クレームに署名し、署名と暗号化のステップを組み合わせてネストされた JWT トークンを暗号化できます。

import io.smallrye.jwt.build.Jwt;
...

// Sign the claims and encrypt the nested token using the private and public keys loaded from the locations
// specified by the 'smallrye.jwt.sign.key.location' and 'smallrye.jwt.encrypt.key.location' properties, respectively.
// The signature algorithm is RS256, and the key encryption algorithm is RSA-OAEP-256.
String jwt = Jwt.claims("/tokenClaims.json").innerSign().encrypt();

JWT の高速生成

smallrye.jwt.sign.key.location または smallrye.jwt.encrypt.key.location プロパティーが設定されている場合は、リソース、マップ、JsonObject などの既存のクレームを 1 回の呼び出しで保護できます。

// More compact than Jwt.claims("/claims.json").sign();
Jwt.sign("/claims.json");

// More compact than Jwt.claims("/claims.json").jwe().encrypt();
Jwt.encrypt("/claims.json");

// More compact than Jwt.claims("/claims.json").innerSign().encrypt();
Jwt.signAndEncrypt("/claims.json");

前述のように、 iat (発行時刻)、 exp (有効期限)、 jti (トークン識別子)、 iss (発行者)、および aud (対象者) などのクレームは、まだ設定されていない場合は自動的に追加されます。

キーの取り扱い

smallrye.jwt.sign.key.location プロパティーと smallrye.jwt.encrypt.key.location プロパティーを使用して、署名キーと暗号鍵の場所を指定できます。これらのキーは、ローカルファイルシステム、クラスパス、またはリモートエンドポイントから取得できます。キーは PEM または JSON Web Key (JWK) 形式に指定できます。例:

smallrye.jwt.sign.key.location=privateKey.pem
smallrye.jwt.encrypt.key.location=publicKey.pem

または、リンクなどの外部サービスからキーを取得することもできます。https://docs.quarkiverse.io/quarkus-vault/dev/index.html [HashiCorp Vault] またはその他のシークレットマネージャーでは、MicroProfile の ConfigSourcesmallrye.jwt.sign.key および smallrye.jwt.encrypt.key プロパティーを使用します。

smallrye.jwt.sign.key=${private.key.from.vault}
smallrye.jwt.encrypt.key=${public.key.from.vault}

この例では、 private.key.from.vaultpublic.key.from.vault は、カスタム ConfigSource によって提供される PEM または JWK 形式のキー値です。

smallrye.jwt.sign.key プロパティーと smallrye.jwt.encrypt.key プロパティーには、Base64 でエンコードされた秘密鍵または公開鍵の値を直接含めることもできます。

ただし、設定に秘密鍵を直接インライン化することは推奨されない点に注意してください。リモートシークレットマネージャーから署名キーの値を取得する必要がある場合にのみ、 smallrye.jwt.sign.key プロパティーを使用してください。

キーは、トークンを構築するコードによって読み込まれ、トークン作成のために JWT ビルド API に渡すこともできます。

対称秘密鍵を使用してトークンに署名または暗号化する必要がある場合は、 io.smallrye.jwt.util.KeyUtils を使用して必要な長さの SecretKey を生成することを検討してください。

たとえば、 HS512 アルゴリズム (512/8) を使用してトークンに署名するには 64 バイトのキーが、 A256KW アルゴリズム (256/8) を使用してコンテンツ暗号鍵を暗号化するには 32 バイトのキーが必要です。

import javax.crypto.SecretKey;
import io.smallrye.jwt.KeyEncryptionAlgorithm;
import io.smallrye.jwt.SignatureAlgorithm;
import io.smallrye.jwt.build.Jwt;
import io.smallrye.jwt.util.KeyUtils;

SecretKey signingKey = KeyUtils.generateSecretKey(SignatureAlgorithm.HS512);
SecretKey encryptionKey = KeyUtils.generateSecretKey(KeyEncryptionAlgorithm.A256KW);
String jwt = Jwt.claim("sensitiveClaim", getSensitiveClaim()).innerSign(signingKey).encrypt(encryptionKey);

また、JSON Web Key (JWK) または JSON Web Key Set (JWK Set) 形式を使用して、秘密鍵を安全なファイルシステムに保存することも検討できます。 smallrye.jwt.sign.key.location または smallrye.jwt.encrypt.key.location プロパティーを使用して、キーを参照できます。

JWK の例
{
 "kty":"oct",
 "kid":"secretKey",
 "k":"Fdh9u8rINxfivbrianbbVT1u232VQBZYKx1HGAGPt2I"
}
JWK セットの例
{
 "keys": [
   {
     "kty":"oct",
     "kid":"secretKey1",
     "k":"Fdh9u8rINxfivbrianbbVT1u232VQBZYKx1HGAGPt2I"
   },
   {
     "kty":"oct",
     "kid":"secretKey2",
     "k":"AyM1SysPpbyDfgZld3umj1qzKObwVMkoqQ-EstJQLr_T-1qS0gZH75aKtMN3Yj0iPS4hcgUuTwjAzZr1Z9CAow"
   }
 ]
}

io.smallrye.jwt.util.KeyUtils を使用して、非対称 RSA または EC キーのペアを生成することもできます。これらのキーは、 JWKJWK Set 、または PEM 形式を使用して保存できます。

SmallRye JWT Builder の設定

SmallRye JWT は、クレームの署名または暗号化の方法をカスタマイズするために使用できる次のプロパティーをサポートしています。

プロパティ名 デフォルト 説明

smallrye.jwt.sign.key.location

なし

引数なしの sign() または innerSign() メソッドが呼び出されたときにクレームに署名するために使用される秘密鍵の場所です。

smallrye.jwt.sign.key

なし

引数なしの sign() または innerSign() メソッドが呼び出されたときにクレームに署名するために使用されるキー値。

smallrye.jwt.sign.key.id

なし

署名キー識別子。JWK キーが使用される場合にのみチェックされます。

smallrye.jwt.encrypt.key.location

なし

引数なしの encrypt() メソッドが呼び出されたときに、クレームまたは内部 JWT を暗号化するために使用される公開鍵の場所です。

smallrye.jwt.sign.relax-key-validation

false

署名キーの検証を緩和します。

smallrye.jwt.encrypt.key

なし

引数なしの encrypt() メソッドが呼び出されたときにクレームまたは内部 JWT を暗号化するために使用されるキー値。

smallrye.jwt.encrypt.key.id

なし

暗号鍵識別子。JWK キーが使用される場合にのみチェックされます。

smallrye.jwt.encrypt.relax-key-validation

false

暗号化キーの検証を緩和します。

smallrye.jwt.new-token.signature-algorithm

RS256

署名アルゴリズム。JWT 署名ビルダーが署名アルゴリズムをまだ設定していないかどうかを確認します。

smallrye.jwt.new-token.key-encryption-algorithm

RSA-OAEP

キー暗号化アルゴリズム。JWT 暗号化ビルダーがキー暗号化アルゴリズムをまだ設定していないかどうかを確認します。

smallrye.jwt.new-token.content-encryption-algorithm

A256GCM

コンテンツ暗号化アルゴリズム。JWT 暗号化ビルダーがコンテンツ暗号化アルゴリズムをまだ設定していないかどうかを確認します。

smallrye.jwt.new-token.lifespan

300

このクレームがまだ設定されていない場合に、 exp (有効期限) クレーム値を計算するために使用されるトークンの有効期間 (秒単位)。

smallrye.jwt.new-token.issuer

なし

このクレームがまだ設定されていない場合に、 iss (発行者) クレーム値を設定するために使用されるトークン発行者。

smallrye.jwt.new-token.audience

なし

このクレームがまだ設定されていない場合に、 aud (オーディエンス) クレーム値を設定するために使用されるトークンオーディエンス。

smallrye.jwt.new-token.override-matching-claims

false

既に初期化されている iss (発行者)と aud (視聴者)のクレームを smallrye.jwt.new-token.issuersmallrye.jwt.new-token.audience の値で上書きするために、 このプロパティを true に設定します。

smallrye.jwt.keystore.type

JKS

このプロパティは、 smallrye.jwt.sign.key.location または smallrye.jwt.encrypt.key.location のいずれか、あるいはこれらのプロパティの両方が KeyStore ファイルを指している場合に、キーストアタイプをカスタマイズするために使用することができます。このプロパティが設定されていない場合、ファイル名をチェックしてキーストアの種類を決定し、デフォルトで JKS に設定されます。

smallrye.jwt.keystore.provider

このプロパティは、 smallrye.jwt.sign.key.location または smallrye.jwt.encrypt.key.locationKeyStore ファイルを指している場合に、 KeyStore プロバイダをカスタマイズするために使用できます。

smallrye.jwt.keystore.password

キーストアのパスワード。 smallrye.jwt.sign.key.location または smallrye.jwt.encrypt.key.locationKeyStore ファイルを指している場合は、このプロパティーを設定する必要があります。

smallrye.jwt.keystore.encrypt.key.alias

smallrye.jwt.encrypt.key.locationKeyStore ファイルを指している場合、一致する証明書の KeyStore から抽出された公開暗号鍵を識別するには、このプロパティーを設定する必要があります。

smallrye.jwt.keystore.sign.key.alias

このプロパティは、 smallrye.jwt.sign.key.locationKeyStore ファイルを指している場合、秘密署名鍵を特定するために設定する必要があります。

smallrye.jwt.keystore.sign.key.password

このプロパティーは、 smallrye.jwt.sign.key.locationKeyStore ファイルを指しているときに、 KeyStore 内の秘密署名鍵のパスワードが smallrye.jwt.keystore.password と異なる場合に設定できます。

関連コンテンツ