The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

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

RFC7519 によると、JSON Web Token (JWT) は、使用される JSON オブジェクトとしてエンコードされたクレームを表すコンパクトで URL セーフな手段です。JSON Web Signature (JWS) 構造のペイロードとして、または JSON Web Encryption (JWE) 構造のプレーンテキストとして、クレームにデジタル署名したり、メッセージ認証コード (MAC) で完全に保護したり、暗号化したりできます。

クレームへの署名は、クレームを保護するために最も頻繁に使用されます。今日において JWT トークンとして知られているものは、通常、JSON Web 署名 の仕様で説明されている手順を使用して、JSON 形式でクレームに署名することによって生成されます。

ただし、クレームが機密である場合は、JSON Web 暗号化 の仕様に記載されている手順に従って暗号化されたクレームを含む JWT を生成することにより、機密性を保証できます。

さらに、クレームに署名してからネストされた JWT トークンを暗号化することにより、クレームの機密性と整合性の両方を強化できます。

SmallRye JWT Build は、これらすべてのオプションを使用して JWT クレームを保護するための API を提供します。Jose4J は、この API をサポートするために内部で使用されます。

依存関係

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

quarkus-smallrye-jwt でサポートされている MicroProfile JWT エンドポイントを作成しなくても、Smallrye JWT Build API を使用できることに留意してください。MP JWT エンドポイントが JWT トークンを生成する必要がない場合は、quarkus-smallrye-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");
// Or start typing the claims immediately:
// JwtClaimsBuilder builder1 = Jwt.upn("Alice");

// Builder created from the existing claims
JwtClaimsBuilder builder2 = Jwt.claims("/tokenClaims.json");

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

// Builder created from 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);

// Builder created from JsonWebToken
@Inject JsonWebToken token;
JwtClaimsBuilder builder5 = Jwt.claims(token);

これは fluent API であるため、ビルダーの初期化は fluent API シーケンスの一部として実行できます。

ビルダーは、iat (発行日) を現在の時刻に設定し、exp (有効期限) を現在の時刻から 5 分後に設定し (smallrye.jwt.new-token.lifespan プロパティーでカスタマイズ可能)、未設定の場合は jti (一意のトークン識別子) クレームも設定します。

smallrye.jwt.new-token.issuer プロパティーと smallrye.jwt.new-token.audience プロパティーを設定し、ビルダー API を使用した発行者とオーディエンスの直接設定を省略することもできます。

次のステップでは、クレームの保護方法を決定します。

クレームへの署名

クレームは、すぐに署名するか、 JSON Web Signature ヘッダーが設定された後に署名できます。

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.
// No 'jws()' transition is necessary. Default algorithm is RS256.
String jwt1 = Jwt.claims("/tokenClaims.json").sign();

// Set the headers and sign the claims with an RSA private key loaded in the code (the implementation of this method is omitted).
// Note a 'jws()' transition to a 'JwtSignatureBuilder', Default algorithm is RS256.
String jwt2 = Jwt.claims("/tokenClaims.json").jws().keyId("kid1").header("custom-header", "custom-value").sign(getPrivateKey());

alg (アルゴリズム) ヘッダーはデフォルトで RS256 に設定されていることに注意してください。 kid プロパティーを含む単一の JSON Web Key (JWK) を使用する場合、署名キー識別子 ( kid ヘッダー) を設定する必要はありません。

クレームの署名には、RSAや楕円曲線(EC)の秘密鍵のほか、対称性のある秘密鍵も使用できます。 ES256HS256 は、それぞれEC秘密鍵と対称鍵アルゴリズムのためのdefautアルゴリズムです。

署名アルゴリズムはカスタマイズできます。以下はその例です。

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. Algorithm is PS256.
String jwt = Jwt.upn("Alice").jws().algorithm(SignatureAlgorithm.PS256).sign();

または、 smallrye.jwt.new-token.signature-algorithm プロパティーを使用することもできます:

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. Algorithm is PS256.
String jwt = Jwt.upn("Alice").sign();

Note the sign step can be combined with the encrypt step to produce inner-signed and encrypted tokens, see Sign the claims and encrypt the nested JWT token section.

クレームの暗号化

クレームは、すぐに暗号化することも、 JSON Web Encryption ヘッダーを署名するのと同じ方法で設定した後に暗号化することもできます。唯一の小さな違いは、API がクレームの署名と内部署名をサポートするように最適化されている場合、クレームの暗号化には常に jwe() JwtEncryptionBuilder 遷移が必要になることです。

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. Default key encryption algorithm is RSA-OAEP.
String jwt1 = Jwt.claims("/tokenClaims.json").jwe().encrypt();

// Set the headers and encrypt the claims with an RSA public key loaded in the code (the implementation of this method is omitted).  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 および Elliptic Curve (EC) 公開鍵、および対称秘密鍵を使用して、クレームを暗号化できます。ECDH-ESA256KW は、それぞれ EC 公開鍵と対称鍵の暗号化アルゴリズムのデフォルトアルゴリズムです。

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

1) 生成されたコンテンツ暗号化キーは、 RSA-OAEP などのキー暗号化アルゴリズムを使用して、API で提供されたキーによって暗号化されます。 2) クレームは、 A256GCM などのコンテンツ暗号化アルゴリズムを使用して、生成されたコンテンツ暗号化キーによって暗号化されます。

キーとコンテンツの暗号化アルゴリズムはカスタマイズできます。以下はその例です。

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, 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-algorithmsmallrye.jwt.new-token.content-encryption-algorithm のプロパティでキーとコンテンツの暗号化アルゴリズムをカスタマイズすることが可能です。

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 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, content encryption algorithm is A256CBC-HS512.
String jwt = Jwt.subject("Bob").encrypt();

トークンが公開 RSA または EC キーによって直接暗号化されている場合、トークンの送信元は確認できないことに注意してください。したがって、トークンを直接暗号化するには秘密鍵を使用する必要があります。たとえば、JWT を Cookie として使用する場合、秘密鍵は Quarkus エンドポイントによって管理され、このエンドポイントのみが暗号化されたトークンのプロデューサーとコンシューマーになります。

If you would like to use RSA or EC public keys to encrypt the token then it is recommended to sign the token first if the signing key is available, see the next Sign the claims and encrypt the nested JWT token section.

クレームへの署名とネストされた 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 set with the 'smallrye.jwt.sign.key.location' and 'smallrye.jwt.encrypt.key.location' properties respectively. Signature algorithm is RS256, key encryption algorithm is RSA-OAEP-256.
String jwt = Jwt.claims("/tokenClaims.json").innerSign().encrypt();

JWT の迅速な生成

smallrye.jwt.sign.key.locationsmallrye.jwt.encrypt.key.location プロパティーが設定されている場合、既存のクレーム (リソース、マップ、JsonObjects) を 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=privateKey.pem
smallrye.jwt.encrypt.key=publicKey.pem

MicroProfile ConfigSource を使用して、HashiCorp Vault や他のシークレットマネージャーなどの外部サービスからキーを取得し、代わりに smallrye.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.keysmallrye.jwt.encrypt.key には、Base64 でエンコードされた秘密鍵または公開鍵の値のみ含めることができます。

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

キーは、JWT ビルド API に提供される、トークンをビルドするコードによってロードすることもできます。

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

たとえば、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 プロパティーを使用してそれを参照する方法もあります。以下はその例です。

{
 "kty":"oct",
 "kid":"secretKey",
 "k":"Fdh9u8rINxfivbrianbbVT1u232VQBZYKx1HGAGPt2I"
}

または

{
 "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

none

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

smallrye.jwt.sign.key

none

Key value which will be used to sign the claims when either a no-argument sign() or innerSign() method is called.

smallrye.jwt.sign.key.id

none

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

smallrye.jwt.encrypt.key.location

none

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

smallrye.jwt.sign.relax-key-validation

false

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

smallrye.jwt.encrypt.key

none

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

smallrye.jwt.encrypt.key.id

none

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 (expiry) クレームがまだ設定されていない場合、このクレーム値を計算するために使用される トークン生存期間の秒数。

smallrye.jwt.new-token.issuer

none

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

smallrye.jwt.new-token.audience

none

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 と異なる場合に設定される場合があります。

関連コンテンツ