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 をサポートするために内部で使用されます。
依存関係
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-jwt-build</artifactId>
</dependency>
implementation("io.quarkus:quarkus-smallrye-jwt-build")
Note you can use SmallRye JWT Build API without having to create MicroProfile JWT endpoints supported by quarkus-smallrye-jwt
.
It can also be excluded from quarkus-smallrye-jwt
if MP JWT endpoints do not need to generate JWT tokens.
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)の秘密鍵のほか、対称性のある秘密鍵も使用できます。 ES256
と HS256
は、それぞれ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-ES
と A256KW
は、それぞれ 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-algorithm
と smallrye.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.location
や smallrye.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.location=privateKey.pem
smallrye.jwt.encrypt.key.location=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.vault
と public.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
を使用して必要な長さの秘密鍵を生成することを検討してください。
たとえば、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 キーのペアを生成することもできます。これらのキーは、 JWK
、 JWK Set
、または PEM
形式を使用して保存できます。
SmallRye JWT Builder の設定
SmallRye JWT は、クレームの署名や暗号化の方法をカスタマイズするために使用できる次のプロパティーをサポートしています。
プロパティ名 | デフォルト | 説明 |
---|---|---|
|
|
引数なし |
|
|
Key value which will be used to sign the claims when either a no-argument |
|
|
JWK キーを使用する場合にのみチェックされる署名キー識別子。 |
|
|
引数なし |
|
|
署名キーの検証を緩和します。 |
|
|
引数なし |
|
|
JWK キーを使用する場合にのみチェックされる暗号化キー識別子。 |
|
|
暗号化キーの検証を緩和します。 |
|
|
署名アルゴリズム。このプロパティーは、JWT 署名ビルダーが署名アルゴリズムをまだ設定していない場合にチェックされます。 |
|
|
キー暗号化アルゴリズム。このプロパティーは、JWT 暗号化ビルダーがキー暗号化アルゴリズムをまだ設定していない場合にチェックされます。 |
|
|
コンテンツ暗号化アルゴリズム。このプロパティーは、JWT 暗号化ビルダーがコンテンツ暗号化アルゴリズムをまだ設定していない場合にチェックされます。 |
|
|
exp (expiry) クレームがまだ設定されていない場合、このクレーム値を計算するために使用される トークン生存期間の秒数。 |
|
|
|
|
|
|
|
|
既に初期化されている |
|
|
このプロパティは、 |
|
このプロパティは、 |
|
|
キーストアのパスワード。 |
|
|
このプロパティは、 |
|
|
このプロパティは、 |
|
|
このプロパティは、 |