メインコンテンツまでスキップ

RFC 7516: JSON Web Encryption(JWE)

RFC 7516 は、JSON ベースのデータを暗号化するための仕様です。ペイロードの機密性を保護する必要がある場合に使用します。


第1部: 概要編

JWE とは何か?

JWE(JSON Web Encryption)は、任意のデータを暗号化するための標準フォーマットです。JWS が「改ざん防止」なら、JWE は「盗聴防止」です。

JWS vs JWE:

JWS(署名):
ペイロードは見える(Base64URL)
改ざんを検知できる

JWE(暗号化):
ペイロードは見えない(暗号化済み)
機密性を保護できる

いつ JWE を使うか?

ユースケース説明
機密情報の送信PII、医療情報、金融データ
Request ObjectOIDC の暗号化リクエストオブジェクト
ID Token の暗号化クライアントのみが復号可能
Nested JWT署名後に暗号化(Sign-then-Encrypt)

JWE の構造

JWE Compact Serialization は 5 つのパートで構成されます。

eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkEyNTZHQ00ifQ.
OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCVJRgckCL9kiMT03JGe
ipsEdY3mx_etLbbWSrFr05kLzcSr4qKAq7YN7e9jwQRb23nfa6c9d-StnImGyFDb
Sv04uVuxIp5Zms1gNxKKK2Da14B8S4rzVRltdYwam_lDp5XnZAYpQdb76FdIKLaV
mqgfwX7XWRxv2322i-vDxRfqNzo_tETKzpVLzfiwQyeyPGLBIO56YJ7eObdv0je8
1860ppamavo35UgoRdbYaBcoh9QcfylQr66oc6vFWXRcZ_ZT2LawVCWTIy3brGPi
6UklfCpIMfIjf7iGdXKHzg.
48V1_ALb6US04U3b.
5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX_EFShS8iB7j6ji
SdiwkIr3ajwQzaBtQD_A.
XFBoMYUZodetZdvTiFvSkQ
└────────┬────────┘.└──────────────────┬───────────────────┘.└────┬────┘.└────────────────────┬────────────────────┘.└────────┬────────┘
Header Encrypted Key IV Ciphertext Tag
パート説明
Headerアルゴリズム情報(JOSE Header)
Encrypted Keyコンテンツ暗号化鍵(CEK)の暗号化版
IV初期化ベクトル
Ciphertext暗号化されたペイロード
Authentication Tag認証タグ(完全性保証)

第2部: 詳細編

暗号化の仕組み

JWE は 2 段階の暗号化を使用します。

1. コンテンツ暗号化(CEK でペイロードを暗号化)
┌─────────────┐ ┌─────────────┐
│ Plaintext │ ──► │ Ciphertext │
└─────────────┘ └─────────────┘
│ ▲
│ │
└── CEK (Content Encryption Key)

2. 鍵暗号化(受信者の公開鍵で CEK を暗号化)
┌─────────────┐ ┌─────────────────┐
│ CEK │ ──► │ Encrypted CEK │
└─────────────┘ └─────────────────┘
│ ▲
│ │
└── 受信者の公開鍵

この方式を「ハイブリッド暗号化」と呼びます。

JOSE Header

{
"alg": "RSA-OAEP",
"enc": "A256GCM",
"kid": "key-2024-01"
}
パラメータ必須説明
alg鍵暗号化アルゴリズム
encコンテンツ暗号化アルゴリズム
zip圧縮アルゴリズム(DEF = DEFLATE)
kid鍵 ID
jkuJWK Set URL
jwk公開鍵
typメディアタイプ
ctyコンテンツタイプ

鍵暗号化アルゴリズム(alg)

RSA 系

アルゴリズム説明推奨
RSA1_5RSAES-PKCS1-v1_5❌ 非推奨(パディング攻撃)
RSA-OAEPRSAES OAEP using SHA-1⚠️
RSA-OAEP-256RSAES OAEP using SHA-256✅ 推奨

AES Key Wrap 系

アルゴリズム説明
A128KWAES Key Wrap with 128-bit key
A192KWAES Key Wrap with 192-bit key
A256KWAES Key Wrap with 256-bit key

AES-GCM Key Wrap 系

アルゴリズム説明
A128GCMKWAES GCM Key Wrap with 128-bit key
A192GCMKWAES GCM Key Wrap with 192-bit key
A256GCMKWAES GCM Key Wrap with 256-bit key

直接暗号化

アルゴリズム説明
dirDirect use of shared symmetric key

CEK を暗号化せず、共有鍵を直接使用。

ECDH 系

アルゴリズム説明
ECDH-ESECDH Ephemeral Static
ECDH-ES+A128KWECDH-ES with AES-128 Key Wrap
ECDH-ES+A192KWECDH-ES with AES-192 Key Wrap
ECDH-ES+A256KWECDH-ES with AES-256 Key Wrap

パスワードベース

アルゴリズム説明
PBES2-HS256+A128KWPBES2 with HMAC SHA-256 and A128KW
PBES2-HS384+A192KWPBES2 with HMAC SHA-384 and A192KW
PBES2-HS512+A256KWPBES2 with HMAC SHA-512 and A256KW

コンテンツ暗号化アルゴリズム(enc)

AES-CBC + HMAC

アルゴリズム説明
A128CBC-HS256AES-128-CBC + HMAC-SHA-256
A192CBC-HS384AES-192-CBC + HMAC-SHA-384
A256CBC-HS512AES-256-CBC + HMAC-SHA-512

AES-GCM(推奨)

アルゴリズム説明推奨
A128GCMAES-128-GCM
A192GCMAES-192-GCM
A256GCMAES-256-GCM✅ 推奨

AES-GCM は暗号化と認証を同時に行う AEAD(Authenticated Encryption with Associated Data)です。

暗号化の手順

1. CEK(Content Encryption Key)を生成
CEK = random(256 bits) // enc が A256GCM の場合

2. CEK を受信者の公開鍵で暗号化
Encrypted_CEK = RSA-OAEP(CEK, recipient_public_key)

3. IV(初期化ベクトル)を生成
IV = random(96 bits) // AES-GCM の場合

4. AAD(Additional Authenticated Data)を構築
AAD = ASCII(BASE64URL(Header))

5. ペイロードを暗号化
(Ciphertext, Tag) = AES-GCM-Encrypt(
Plaintext,
CEK,
IV,
AAD
)

6. JWE Compact Serialization を構築
Header.Encrypted_CEK.IV.Ciphertext.Tag

復号の手順

1. JWE をパース
5 つのパートに分割

2. Header をデコードしてアルゴリズムを確認
alg, enc を検証

3. CEK を復号
CEK = RSA-OAEP-Decrypt(Encrypted_CEK, recipient_private_key)

4. AAD を再構築
AAD = ASCII(BASE64URL(Header))

5. ペイロードを復号・検証
Plaintext = AES-GCM-Decrypt(
Ciphertext,
CEK,
IV,
AAD,
Tag
)

Nested JWT(Sign-then-Encrypt)

署名付き JWT を暗号化する場合。

1. JWT を作成・署名
signed_jwt = JWS(claims, sender_private_key)

2. 署名済み JWT を暗号化
jwe = JWE(signed_jwt, recipient_public_key)

受信者:
1. JWE を復号
signed_jwt = Decrypt(jwe, recipient_private_key)

2. JWT の署名を検証
claims = Verify(signed_jwt, sender_public_key)

Nested JWT の場合、外側の JWE ヘッダーに cty: "JWT" を設定します。

{
"alg": "RSA-OAEP-256",
"enc": "A256GCM",
"cty": "JWT"
}

JSON Serialization

複数の受信者に対して暗号化する場合。

{
"protected": "eyJlbmMiOiJBMjU2R0NNIn0",
"iv": "48V1_ALb6US04U3b",
"ciphertext": "5eym8TW_c8SuK0ltJ3rpYIzOeDQz7TALvtu6UG9oMo4vpzs9tX...",
"tag": "XFBoMYUZodetZdvTiFvSkQ",
"recipients": [
{
"header": {
"alg": "RSA-OAEP-256",
"kid": "recipient-1"
},
"encrypted_key": "OKOawDo13gRp2ojaHV7LFpZcgV7T6DVZKTyKOMTYUmKoTCV..."
},
{
"header": {
"alg": "RSA-OAEP-256",
"kid": "recipient-2"
},
"encrypted_key": "a]G9HnWIgjXJe7UpEZ_R-4q..."
}
]
}

セキュリティ考慮事項

項目推奨事項
RSA1_5使用禁止(パディングオラクル攻撃)
鍵サイズRSA は 2048 ビット以上、AES は 256 ビット
AES-GCM推奨(AEAD)
IV の再利用絶対に禁止
圧縮CRIME 攻撃に注意(機密データでは避ける)

参考リンク