Source
JSON Web Encryption (JWE) の解説 #JWT - Qiita
-------------- GPT 翻譯 --------------
JSON Web Encryption(JWE)解釋
我之前在工作中研究了一下JWE,現在將整理的資料修改後發佈到 Qiita 上。
JWE是什麼
JSON Web Encryption(JWE)是一種將加密數據與解密所需的元數據一起打包成 JSON 格式的數據表示形式。此外,它還具有通過 AEAD 提供的完整性(防篡改)保證功能。該規範已被 IETF 制定為 RFC 標準。
JWE 是 JSON Web Signature(JWS)用於簽名和打包消息以確保防篡改性,以及用於在各方之間交換認證令牌的 JSON Web Token(JWT)等規範中的一部分。JWT可以利用JWS或JWE。
RFC 7516 - JSON Web Encryption(JWE)。
序列化格式
JWE 定義了兩種序列化形式,分別是JWE Compact Serialization和JWE JSON Serialization。
JWE Compact Serialization
JWE Compact Serialization是一種通過.(點)連接5個經過 BASE64URL 編碼的組件的序列化方式。由於是 Web 安全的,因此可以在 URL 或 HTTP 頭值中使用。
以下是JWE Compact Serialization的示例。換行僅為了方便顯示。
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ
.
AAG-Mxcoy3qHgYNZTGC8IzLhTkQOv7Iku5JI_gGm1Ev1GaqlbwdWT5x0rtvNSIxbvGOA
KFoTnPygByBcvXVkD0SnkPotkjXqKXocozG9zT9vaHdDUtth4ySKMs9huVXWqglZTkSA
QvZSpxSU6Gu0ZUB5yuUO2XZEeIwuRiG9F0hlAuQDokNvqWS69NfDxoiRN6Qp7Ud67LF8
FH75JA_eSuJsrW1JWWcB8bHQgrbB0WU-z5IVbyN7VPGNm1GGdENSh963iRTb0DlvADF-
KbU5yIHRomvJxEjOQDS2LYyswy6zAv7rrJeTO4dzgWYo7lcf07F19U8mGFfiimFXD5Lw
.
HtGmVwOnLfsVZmp1
.
cHUwotN3V05o4sHRhA
.
NGmNkNr5QyuVYGh93zknlA
上面的 JWE 令牌由以下組件組成。
base64url(JWE Header)
.
base64url(JWE Encrypted)
.
base64url(JWE Initialization Vector)
.
base64url(JWE Ciphertext)
.
base64url(JWE Authentication Tag)
每個組件的作用如下
-
JWE 標頭。
- JOSE 標頭,一串 JSON 對象,包含用於解釋 JWE 標記的信息。 需要兩個密鑰:
alg和enc。
- JOSE 標頭,一串 JSON 對象,包含用於解釋 JWE 標記的信息。 需要兩個密鑰:
-
JWE 加密密鑰。
- 用於加密內容的密鑰(CEK),用另一個密鑰加密。
-
JWE 初始化向量。
- 用於加密內容的初始化向量。
-
JWE 密碼文本
- 加密後的內容正文。
-
JWE驗證標籤
- 保證 JWE 令牌完整性的驗證標籤,用於驗證 JWE 令牌是否被篡改。
JWE JSON序列化
JWE JSON序列化 是一種將整個數據表示為單個JSON對象的序列化方式,其中每個組件都以JSON鍵值對的形式表示。可以指定多個接收者(Recipient),並且可以為每個接收者指定不同的算法和加密密鑰。
以下是JWE緊湊序列化的示例。換行僅為了顯示方便。
{
"protected": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ",
"encrypted_key": "KJpOWsV4gN-3tPaEoEd_Ae0UE6KI4gvWdt6Vuc_mOUmeWX2L9QYvi_CeCNZaiWoQoAT-pE4oiewGufT-7xgkILILvSoomrFD0xJlmWkJcGUoSZSUZr7nmOJa6V0XHbA--g3F1B35Jk-qUd7tgAxb9uDJKkfr96LTe1_Zt6ADUkaPwnB_0mqSqWXVl14W1LuwPYHrE9K4tPXtn6O3uoOAACLR8X_oGr5x8uVicgOBYpitDsB30k-0o-T6a8kgpD3MHF1iYYIKrZb-QEbeTYyT9wDbMsS4FsmRqKHu6kQ94ha4wFBEJ_fUvWvXnbX5WZ67zrCg8MtqEKAibUfRKv5Zeg",
"iv": "7ObH5owmUWQdldiQ",
"ciphertext": "DW2iCLUFevSLloS4mg",
"tag": "UoZ1Ljz_a7R4QhFoWwNRlQ"
}
由於這種格式並不常用,因此本文將基於JWE緊湊序列化進行解釋。
JOSE頭
JOSE頭是一個JavaScript對象,其中包含了處理JWE令牌所需的元數據。JOSE頭受到篡改的保護。在RFC中定義的參數稱為JWE保護頭,其中enc和alg頭是必需的參數。
以下是一些代表性的頭參數:
-
必需頭
-
enc頭(加密算法)
- 指定用於加密內容的算法。除了RFC 7518 JWA的5.1章節中定義的算法標識符之外,還可以指定具有衝突防止前綴的未註冊算法。算法必須是使用指定長度的密鑰的AEAD(帶認證的加密)算法。後面會詳細介紹可用的算法。
-
alg頭(算法)
- 指定加密密鑰(CEK)的管理方法。可用的算法在RFC 7518 - JSON Web Algorithms (JWA) section 4.1中定義。詳見下文。
-
-
指定密鑰的頭
-
jku頭(JWK集合URL)
- 引用JSON編碼的公鑰集合(其中之一對應於加密JWE的密鑰)的URI。已使用JWK Set格式進行編碼。
-
jwk頭(JSON Web Key)
- 對應於加密JWE的密鑰的公鑰。此密鑰以JSON Web Key [JWK]格式表示。
-
kid頭(密鑰ID)
- 指示用於加密JWE的哪個密鑰的提示信息。KID的結構未定義。如果與JWK一起使用,則用於匹配JWK "kid"參數值。
-
x5u頭(X.509 URL)
- 引用與加密JWE對應的PEM格式的.509公鑰證書或證書鏈資源的URI。可用於標識複合JWE所需的秘密密鑰。
-
x5c頭(X.509證書鏈)
- 用於加密JWE的X.509公鑰證書或證書鏈。這是一個證書字符串數組。可用於標識複合JWE所需的秘密密鑰。
-
x5t頭(X.509證書SHA-1 Thumbprint)
- 對應於加密JWE的密鑰的X.509 DER編碼SHA-1摘要的base64url編碼。可用於標識複合JWE所需的秘密密鑰。
-
x5t#S256頭(X.509證書SHA-256 Thumbprint)
- 對應於加密JWE的密鑰的X.509 DER編碼SHA-256摘要的base64url編碼。可用於標識複合JWE所需的秘密密鑰。
-
-
其他已註冊頭參數
-
zip頭(壓縮算法)
- 在加密內容之前,可以使用zip指定的算法對Plaintext進行壓縮。可以使用表示DEFLATE壓縮的
DEF。
- 在加密內容之前,可以使用zip指定的算法對Plaintext進行壓縮。可以使用表示DEFLATE壓縮的
-
typ頭(類型)
- 指示JWE對象的類型。如果需要處理不同類型的對象,則應用程序將使用JOSE對象來區分它們。
-
可用的值
- JOSE:使用JWS Compact Serialization的JWS或JWE
- JOSE+JSON:使用JWS JSON Serialization的JWS或JWE
- 其他值:應用程序可以使用其他值。(無法處理的值將被忽略)
-
cty頭(內容類型)
- 可選頭,用於指示受保護的內容(有效載荷)的類型。取值為IANA Media Type。如果JWS/JWE具有多個有效載荷,則用於區分它們的類型。
-
crit頭(關鍵)
- 枚舉擴展規範和/或JWA頭名稱的數組,其中接收者必須理解和處理JOSE頭。如果接收者未理解並執行列出的任何頭,則該JWS/JWE無效。
-
JWE生成步驟
下圖展示了JWE的生成步驟。輸入值為用橙色表示的密鑰、頭部參數和明文,輸出為底部的深灰色方框中的JWE。該過程由兩個步驟組成,即大致為①CEK生成和②內容加密。
① CEK生成
由alg頭部參數指定的算法各自具有相應的密鑰管理模式,但是通過該密鑰管理模式定義的方式,將生成或準備好用於加密內容的內容加密密鑰(CEK)。共定義了以下5種密鑰管理模式。
alg 參數值和密鑰管理模式
-
直接加密
- 使用的內容加密密鑰是在參與方之間共享的秘密對稱密鑰值的密鑰管理模式。共享的CEK本身作為參數提供。
-
支持的算法
- dir
-
直接密鑰協商
- 使用密鑰協商算法就內容加密密鑰達成一致的密鑰管理模式。
-
支持的算法
-
ECDH-ES
- 橢圓曲線迪菲-赫爾曼短暫靜態密鑰協商
-
-
密鑰協商與密鑰包裝
- 使用對稱密鑰包裝算法將內容加密密鑰值加密的對稱密鑰用於共享密鑰的密鑰管理算法的密鑰管理模式。
-
支持的算法
-
ECDH-ES+A128KW, ECDS-ES+A192KW, ECDS-ES+A256KW
- 使用Concat KDF的ECDH-ES和各個密鑰長度的CEK包裝
-
-
密鑰加密
- 使用非對稱密鑰加密算法(公鑰加密)對內容加密密鑰值進行加密的密鑰管理模式。
- 用於加密CEK的非對稱密鑰作為外部參數提供。
-
支持的算法
-
RSA1_5
- RSAES-PKCS1-v1_5
-
RSA-OAEP
- 使用默認參數的RSAES OAEP
-
RSA-OAEP-256
- 使用SHA-256的RSAES OAEP和基於SHA256的MGF1
-
-
密鑰包裝
- 使用對稱密鑰包裝算法(共享密鑰加密)對內容加密密鑰值進行加密的密鑰管理模式。 注意:密鑰包裝:一種設計用於封裝(加密)加密密鑰的對稱加密算法。參考:Key Wrap - Wikipedia
- 用於包裝CEK的目標密鑰作為外部參數提供。
-
支持的算法
-
A128KW, A192KW, A256KW
- 使用128、192、256位密鑰的默認初始值的AES密鑰包裝
-
PBES2-HS256+A128KW, PBES2-HS284+A192KW, PBES2-HS512+A256KW
- 使用HMAC SHA-256、284、512的PBES2和A128KW、A192KW、A256KW密鑰包裝
-
A128GCMKW, A192GCMKW, A256GCMKW
- 使用128、192、256位密鑰的AES GCM密鑰包裝
-
在每種密鑰管理模式下,按照下表的步驟生成密鑰。
② 內容加密
通過enc標頭參數指定的算法(見下表)對內容進行加密。
-
A128CBC-HS256
- AES_128_CBC_HMAC_SHA256 帶認證的加密算法。
-
A192CBC-HS384
- AES_192_CBC_HMAC_SHA384 帶認證的加密算法。
-
A256CBC-HS512
- AES_256_CBC_HMAC_SHA512 帶認證的加密算法。
-
A128GCM
- 使用128位密鑰的 AES GCM
-
A192GCM
- 使用192位密鑰的 AES GCM
-
A256GCM
- 使用256位密鑰的 AES GCM
來源:RFC 7518 - JSON Web Algorithms (JWA)。
加密處理的輸入包括明文、內容加密密鑰、初始化向量(IV)、附加認證數據(AAD),輸出為密文和AEAD的認證標籤(Authentication Tag)。
-
輸入
-
內容加密密鑰(CEK: Content Encryption Key):
- 用於加密內容的密鑰
-
初始化向量(IV: Initial Vector):
- 在CBC模式下,使用相同密鑰對相同明文進行加密會生成相同的密文,從而泄露信息。為了防止這種情況發生,每次加密都會準備一個唯一的字節序列,稱為初始化向量。初始化向量本身並不需要保密,因此在JWE中會在加密時隨機生成,並直接包含在最終的JWE中。
-
附加認證數據(AAD: Additional Authentication Data):
- 由於JWE中指定的加密算法為AEAD(帶認證的加密算法),可以使用附加認證數據(AAD)來生成認證標籤。通過這個標籤,如果JWE的AAD部分或密文部分被篡改,解密時會出現錯誤,從而可以檢測到篡改,確保JWE的完整性。在
JWE Compact Serialization中,JWE Protected Header被用作AAD。在JWE JSON Serialization中,可以指定任意參數作為AAD。
- 由於JWE中指定的加密算法為AEAD(帶認證的加密算法),可以使用附加認證數據(AAD)來生成認證標籤。通過這個標籤,如果JWE的AAD部分或密文部分被篡改,解密時會出現錯誤,從而可以檢測到篡改,確保JWE的完整性。在
-
3.創建 JWE 令牌
以下五個部分用句號". 來完成 JWE 令牌。
- 以 UTF-8 + Base64Url 編碼的 JWE 受保護標頭
- 在 ① 中獲取的 JWE 加密密鑰的 Base64Url。
在②中隨機生成的 JWE 初始向量的 Base64Url。
在②中加密的 JWE 密文的 Base64Url。
JWE 驗證標籤的 Base64Url,即在②中加密的輸出。
實現
在Go語言中,可以使用square/go-jose庫來處理JWE。讓我們使用go-jose.v2來創建一個JWE令牌。
package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"log"
jose "gopkg.in/square/go-jose.v2"
)
func main() {
var plaintext = []byte("Lorem ipsum dolor sit amet")
fmt.Println("Plaintext:", string(plaintext))
//
// RSA-OAEP で使うためのRSA鍵を生成
//
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Fatal(err)
}
//
// enc: AES128-GCM, alg: RSA-OAEP で暗號化し、JWE Compact Serializationで出力
//
encrypter, err := jose.NewEncrypter(jose.A128GCM, jose.Recipient{
Algorithm: jose.RSA_OAEP,
Key: privateKey.PublicKey,
}, nil)
if err != nil {
log.Fatal(err)
}
object, err := encrypter.Encrypt(plaintext)
if err != nil {
log.Fatal(err)
}
token, err := object.CompactSerialize()
if err != nil {
log.Fatal(err)
}
fmt.Println("JWE Token:", token)
//
// 復號化
//
object, err = jose.ParseEncrypted(token)
if err != nil {
log.Fatal(err)
}
decrypted, err := object.Decrypt(privateKey)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted: %s\n", decrypted)
}
執行結果
Plaintext: Lorem ipsum dolor sit amet
JWE Token: eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.YOnRGwtYnd4C4w_OEtxlHhFzJT3qUH6APt7jDXTn3ln5yt-kS1RxM29uM3NEBnop5ZcQhtqNmZ90VeyN3AqsMGDp1KkEvHOb1O405Iwk1taaI5XZfGI6dxAhnH6YonbUBpQkqgHcNQhOYxgXuOkhecCUCyKKK-RcsukQSDNWB4rjf0QRwvcjtC4uAsnplsP2gwKNof4QgV_yMoVLBYFg6YVyDeYdIVSLIeZxO0SKIDbugF698Mufrj5gjsd_ydm-kvFlLO29Bti5eaDnOetlw1QzCzHa8fjUHoRyS6L5kcKjPqs6HzIxZv53OxrAsWq2IWdmnkMfBSXIMHCCIPeBVw.RZ9qri36pOoV_5C-.bQCU4nVQ9lrEH4EgvExzTjziJ8iWTzW7BKI.iNIOEhEt9eglEX6rGXxXpw
Decrypted: Lorem ipsum dolor sit amet
JWE的批評
JWE(或JOSE)受到了安全性方面的批評。特別是Paragon的工程師指出,在許多實現中,JWS存在攻擊可能性,而JWE中定義的公鑰加密算法很多都是脆弱的,開發人員可能會自掘墳墓。他們認為Fernet更優秀,提出瞭解決JOSE問題的PASETO。
- JOSE是一個絕對應該避免的糟糕標準 | POSTD
- No Way, JOSE! Javascript Object Signing and Encryption is a Bad Standard That Everyone Should Avoid - Paragon Initiative Enterprises Blog
-
帶PKCS #1v1.5填充的RSA
- "帶PKCS #1v1.5填充的RSA容易受到一種稱為填充預言的選擇密文攻擊的影響"
-
帶OAEP填充的RSA
- 如果將RSA視為安全的話,那就是安全的,但是"安全專家建議從RSA遷移"
-
ECDH
- 在JWT(JOSE?)中只允許使用基於橢圓曲線迪菲-赫爾曼的ECDH,但該曲線存在Invalid-Curve攻擊的脆弱性。由於AES-GCM上述公鑰加密模式存在疑問,應該使用預共享密鑰密碼模式(?)
總結
您覺得如何?我們討論了Javascript Web Encryption的規範和創建方法。或許可以考慮使用PASETO或Branca等替代標準。辛苦了。
-------------- 原文 --------------
JSON Web Encryption (JWE) の解説
ちょっと前に業務で JWE について調べ物をしたので、その際の資料を改稿して Qiita に放流します。
JWE とは
JSON Web Encryption (JWE) とは、暗號化したデータを、復號に必要なメタデータとあわせて JSON 形式でパッケージするデータ表現形式です。また、 AEAD による完全性(改竄耐性)保証の機能も持ちます。仕様は IETF により RFC 化されています。
JWE は、メッセージを署名とパッケージして改竄耐性を保証する JSON Web Signature (JWS) や、パーティ間で認証トークンを交換するための JSON Web Token (JWT) などを含む Javascript Object Signing and Encryption (JOSE) と呼ばれる規格羣のひとつです。 JWT は、 JWS または JWE を利用することができます。
RFC 7516 - JSON Web Encryption (JWE)。
シリアライゼーション形式
JWE には、JWE Compact Serialization と JWE JSON Serialization の二種類のシリアライズ形式が定義されています。
JWE Compact Serialization
JWE Compact Serialization は、 BASE64URL エンコードされた5つのコンポーネントを、.(ピリオド)で結合するシリアライゼーション方式です。Web セーフなため、URL や HTTP ヘッダ値のなかで使用できます。
以下に JWE Compact Serialization の例を示します。改行は表示の都合です。
eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ
.
AAG-Mxcoy3qHgYNZTGC8IzLhTkQOv7Iku5JI_gGm1Ev1GaqlbwdWT5x0rtvNSIxbvGOA
KFoTnPygByBcvXVkD0SnkPotkjXqKXocozG9zT9vaHdDUtth4ySKMs9huVXWqglZTkSA
QvZSpxSU6Gu0ZUB5yuUO2XZEeIwuRiG9F0hlAuQDokNvqWS69NfDxoiRN6Qp7Ud67LF8
FH75JA_eSuJsrW1JWWcB8bHQgrbB0WU-z5IVbyN7VPGNm1GGdENSh963iRTb0DlvADF-
KbU5yIHRomvJxEjOQDS2LYyswy6zAv7rrJeTO4dzgWYo7lcf07F19U8mGFfiimFXD5Lw
.
HtGmVwOnLfsVZmp1
.
cHUwotN3V05o4sHRhA
.
NGmNkNr5QyuVYGh93zknlA
上の JWE トークンは、以下のようなコンポーネントで構成されています。
base64url(JWE Header)
.
base64url(JWE Encrypted)
.
base64url(JWE Initialization Vector)
.
base64url(JWE Ciphertext)
.
base64url(JWE Authentication Tag)
それぞれのコンポーネントは、以下の役割を持ちます。
-
JWE Header
- JOSEヘッダ。JWEトークンを解釈するための情報が格納されているJSONオブジェクトの文字列。
algとencの2キーが必須。
- JOSEヘッダ。JWEトークンを解釈するための情報が格納されているJSONオブジェクトの文字列。
-
JWE Encrypted Key
- コンテンツの暗號化に使われた鍵(CEK)が、別の鍵で暗號化されたもの。
-
JWE Initialization Vector
- コンテンツの暗號化に使われた初期化ベクトル。
-
JWE Ciphertext
- 暗號化されたコンテンツ本體。
-
JWE Authentication Tag
- JWE トークンの完全性を保証する認証タグ。JWE トークンの改竄の有無を検証するために使われる。
JWE JSON Serialization
一方の JWE JSON Serialization は、全體がひとつの JSON であるシリアライゼーション方式で、各コンポーネントが JSON のキー・バリューの形で表現されています。複數の Recipient (受信者)を指定でき、 Recipient ごとに異なるアルゴリズムや Encrypted Key を指定できます。
以下に JWE Compact Serialization の例を示します。改行は表示の都合です。
{
"protected": "eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ",
"encrypted_key": "KJpOWsV4gN-3tPaEoEd_Ae0UE6KI4gvWdt6Vuc_mOUmeWX2L9QYvi_CeCNZaiWoQoAT-pE4oiewGufT-7xgkILILvSoomrFD0xJlmWkJcGUoSZSUZr7nmOJa6V0XHbA--g3F1B35Jk-qUd7tgAxb9uDJKkfr96LTe1_Zt6ADUkaPwnB_0mqSqWXVl14W1LuwPYHrE9K4tPXtn6O3uoOAACLR8X_oGr5x8uVicgOBYpitDsB30k-0o-T6a8kgpD3MHF1iYYIKrZb-QEbeTYyT9wDbMsS4FsmRqKHu6kQ94ha4wFBEJ_fUvWvXnbX5WZ67zrCg8MtqEKAibUfRKv5Zeg",
"iv": "7ObH5owmUWQdldiQ",
"ciphertext": "DW2iCLUFevSLloS4mg",
"tag": "UoZ1Ljz_a7R4QhFoWwNRlQ"
}
この形式はあまり使われないため、本稿では JWE Compact Serialization を前提に解説を進めます。
JOSEヘッダ
JOSEヘッダ は、JWEトークンを適切に扱うためのメタ情報が格納された JavaScript オブジェクトです。 JOSEヘッダ は改竄に対して保護されます。JOSEヘッダ のうち、RFCで定義されたパラメータは JWE Protected Header と定義され、そのうち、enc と alg ヘッダは必須パラメータです。
以下に、代表的なヘッダパラメータを紹介します。
-
必須ヘッダ
-
enc ヘッダ (Encryption Algorithm)
- コンテンツを暗號するためのアルゴリズム を指定します。RFC 7518 JWAの5.1章で定義されたアルゴリズム識別子 のほか、衝突防止プレフィックスをもつ未登録アルゴリズムを指定できます。アルゴリズムは、指定された長さのキーを使うAEAD(認証付き暗號化)アルゴリズムである必要がります。使用可能なアルゴリズムについては後述します。
-
alg ヘッダ (algorithm)
- コンテンツを暗號化した鍵(CEK)の管理方法 を指定します。使用可能なアルゴリズムは、RFC 7518 - JSON Web Algorithms (JWA) section 4.1 に定義されています。詳細は後述。
-
-
鍵を指定するヘッダ
-
jku ヘッダ (JWK Set URL)
- JSONエンコードされた公開鍵のセット(そのうちひとつはJWEを暗號化した鍵に相當する)を參照するURI。JWK Set形式でエンコードされています。
-
jwk ヘッダ (JSON Web Key)
- JWEを暗號化した鍵に対応する公開鍵。このキーはJSON Web Key[JWK]の形式で表現されます。
-
kid ヘッダ (Key ID)
- JWEを暗號化するためにどの鍵が使用されたかを示すヒント情報。KIDの構造は定義されていない。JWKとともに使われた場合、JWK "kid" パラメータ値を照合するために用いられます。
-
x5u ヘッダ (X.509 URL)
- JWEを暗號化した鍵に対応する、PEM形式の.509公開鍵証明書もしくは証明書チェーンリソースを參照するURI. JWEの複合に必要な秘密鍵を特定するために使うことができます。
-
x5c ヘッダ (X.509 Certificate Chain)
- JWEを暗號化するために用いるX.509公開鍵証明書または証明書チェーン。証明書文字列の配列です。JWEの複合に必要な秘密鍵を特定するために使うことができます。
-
x5t ヘッダ (X.509 Certificate SHA-1 Thumbprint)
- JWEを暗號化した鍵に相當するX.509のDERエンコードのSHA-1サムプリント(ダイジェスト)をbase64urlエンコードしたもの。JWEの復號に必要な秘密鍵を特定するために使うことができます。
-
x5t#S256 ヘッダ (X.509 Certificate SHA-256 Thumbprint)
- JWEを暗號化した鍵に相當するX.509のDERエンコードのSHA-256サムプリント(ダイジェスト)をbase64urlエンコードしたもの。JWEの復號に必要な秘密鍵を特定するために使うことができます。
-
-
その他の Registered Header Parameters
-
zip ヘッダ (Compression Algorithm)
- コンテンツを暗號化する前に、zipで指定したアルゴリズムでPlaintextを圧縮できます。DEFLATE圧縮を示す
DEFが使用できます。
- コンテンツを暗號化する前に、zipで指定したアルゴリズムでPlaintextを圧縮できます。DEFLATE圧縮を示す
-
typ ヘッダ (Type)
- JWEオブジェクトの種類を示します。異なる種類のオブジェクトも扱う必要がある場合に、アプリケーションがJOSEオブジェクトを他のものと區別するために使うためのもの。
-
使用可能な値
- JOSE: JWS Compact Serialization を用いたJWSまたはJWE
- JOSE+JSON: JWS JSON Serialization を用いたJWSまたはJWE
- その他の値: アプリケーションはその他の値を使っても良い。(扱えない値は無視される)
-
cty ヘッダ (Content Type)
- 保護されたコンテンツ(ペイロード)のタイプを示すために、アプリケーションにより使われるオプショナルヘッダ。IANA Media Typeの値をとります。JWS/JWEにペイロードが複數ある場合、種類を區別するために使うためのもの。
-
crit ヘッダ (Critical)
- JOSEヘッダのうち、受信者によって理解して処理されなければいけない拡張仕様および/またはJWAヘッダ名を列挙した配列。もしも列挙されたいずれかのヘッダが受信者によって理解され実行されなかった場合、そのJWS/JWEは不正です。
-
JWE生成の手順
JWEの作成手順を図に示します。オレンジ色で示した 鍵, ヘッダパラメータ と Plaintext が入力値で、下部の濃い灰色のボックスが出力されるJWEです。手順は大きく ①CEK生成 と ②コンテンツ暗號化 の2ステップで構成されます。
① CEK生成
alg ヘッダパラメータによって指定されるアルゴリズムは、それぞれ対応する 鍵管理モード を持ちますが、この鍵管理モードにより定義される方法で、コンテンツを暗號化する コンテンツ暗號化キー(CEK) が生成または用意されます。鍵管理モードとして、以下の5種類のモードが定義されています。
alg パラメータ値と鍵管理モード
-
Direct Encryption
- 使用さているコンテンツ暗號化キーがパーティ間で共有されている秘密の対稱的なキー値であるキー管理モード。共有されたCEKそのものが外部からパラメータとして與えられます。
-
対応アルゴリズム
- dir
-
Direct key Agreement
- 鍵共有(Key Agreement) アルゴリズムを使ってコンテンツ暗號化キーについて同意するキー管理モード。
-
対応アルゴリズム
-
ECDH-ES
- Elliptic Curve Diffie-Hellman Ephemeral Static 鍵共有
-
-
Key Agreement with Key Wrapping
- 対稱的な鍵ラップアルゴリズムを使ってコンテンツ暗號化キー値を暗號化するのに使われている対稱的キーを共有するためにキーマネジメントアルゴリズムが使われているキー管理モード。
-
対応アルゴリズム
-
ECDH-ES+A128KW, ECDS-ES+A192KW, ECDS-ES+A256KW
- Concat KDFを使ったECDH-ES と 各キー長でのCEKラップ
-
-
Key Encryption
- コンテンツ暗號化キーの値が非対稱鍵を使った鍵暗號化アルゴリズム(公開鍵暗號)を用いて暗號化されるキー管理モード。
- CEKを暗號化するための非対稱鍵が外部からパラメータとして與えられます。
-
対応アルゴリズム
-
RSA1_5
- RSAES-PKCS1-v1_5
-
RSA-OAEP
- デフォルトパラメータを使ったRSAES OAEP
-
RSA-OAEP-256
- SHA-256 を使った RSAES OAEP と SHA256 による MGF1
-
-
Key Wrapping
- コンテンツ暗號化キーの値が対稱鍵を使った鍵ラップアルゴリズム(共通鍵暗號)を用いて暗號化されるキー管理モード。 NOTE: 鍵ラップ:暗號鍵をカプセル化(暗號化)するためにデザインされた対稱的暗號アルゴリズムの種類。Ref: Key Wrap - Wikipedia
- CEKをラップするための対象鍵が外部からパラメータとして與えられる
-
対応アルゴリズム
-
A128KW, A192KW, A256KW
- 128, 192, 256ビットキーを使ったデフォルト初期値による AES 鍵ラップ
-
PBES2-HS256+A128KW, PBES2-HS284+A192KW, PBES2-HS512+A256KW
- HMAC SHA-256, 284, 512 による PBES2 と A128KW, A192KW, A256KW 鍵ラップ
-
A128GCMKW, A192GCMKW, A256GCMKW
- 128, 192, 256キーを使ったAES GCMによる鍵ラップ
-
各鍵管理モードでは、次の表な手順で鍵を生成します。
② コンテンツ暗號化
enc ヘッダパラメータにより指定されたアルゴリズム(下表)でコンテンツを暗號化します。
-
A128CBC-HS256
- AES_128_CBC_HMAC_SHA256 認証付き暗號化アルゴリズム.
-
A192CBC-HS384
- AES_192_CBC_HMAC_SHA384 認証付き暗號化アルゴリズム.
-
A256CBC-HS512
- AES_256_CBC_HMAC_SHA512 認証付き暗號化アルゴリズム.
-
A128GCM
- 128ビットキーを使った AES GCM
-
A192GCM
- 192ビットキーを使った AES GCM
-
A256GCM
- 256ビットキーを使った AES GCM
RFC 7518 - JSON Web Algorithms (JWA) より。
暗號処理の入力は 平文, コンテンツ暗號化キー, 初期化ベクトル(Initial Vector), 追加暗號化データ(AAD) で、出力は Ciphertext とAEADの認証タグ(Authentication Tag) です。
-
入力
-
コンテンツ暗號化キー(CEK: Content Encryption Key):
- コンテンツを暗號化するためのキー
-
初期化ベクトル(IV: Initial Vector):
- CBCモードで同じ鍵を使って同じ平文を暗號化すると、同じ暗號文が生成され、同じ內容であることがわかってしまう。これを防ぐため、暗號化ごとに用意されるユニークなバイト列のこと。暗號化した出力をランダム化するために用いられます。IV自體は秘匿すべき情報ではないため、JWEでは暗號化の際にランダムに生成され、最終的なJWEにそのまま表現されます。
-
追加認証データ(AAD: Additional Authentication Data):
- JWEで指定可能な暗號化アルゴリズムはAEAD(認証付き暗號化アルゴリズム)なので、追加の認証データ(AAD)を使い、 Authentication Tag を生成することができます。このタグにより、JWEのAAD部とCiphertext部のどちらかが改竄されると復號時にエラーになり、改竄されたことがわかるため、JWEは完全性が得られます。
JWE Compact Serializationでは、AADとしてJWE Protected HeaderがAADとして使われます。JWE JSON Serializationでは、AAEとして任意のパラメータを指定できます。
- JWEで指定可能な暗號化アルゴリズムはAEAD(認証付き暗號化アルゴリズム)なので、追加の認証データ(AAD)を使い、 Authentication Tag を生成することができます。このタグにより、JWEのAAD部とCiphertext部のどちらかが改竄されると復號時にエラーになり、改竄されたことがわかるため、JWEは完全性が得られます。
-
3. JWE トークンを構築する
以下の5コンポーネントをピリオド'.'で連結し、JWEトークンの完成です。
- JWE Protected HeaderをUTF-8エンコード+Base64Urlしたもの
- ① で求めたJWE Encryption Key をBase64Urlしたもの
- ② でランダムに生成したJWE Initial Vector を Base64Urlしたもの
- ② で暗號化した出力であるJWE Ciphertext を Base64Urlしたもの
- ② で暗號化した出力であるJWE Authentication Tag をBase64Urlしたもの
実裝
GoでJWEを使用するライブラリには、square/go-jose が挙げられます。go-jose.v2 を使ってJWEトークンを作成してみましょう。
package main
import (
"crypto/rand"
"crypto/rsa"
"fmt"
"log"
jose "gopkg.in/square/go-jose.v2"
)
func main() {
var plaintext = []byte("Lorem ipsum dolor sit amet")
fmt.Println("Plaintext:", string(plaintext))
//
// RSA-OAEP で使うためのRSA鍵を生成
//
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Fatal(err)
}
//
// enc: AES128-GCM, alg: RSA-OAEP で暗號化し、JWE Compact Serializationで出力
//
encrypter, err := jose.NewEncrypter(jose.A128GCM, jose.Recipient{
Algorithm: jose.RSA_OAEP,
Key: privateKey.PublicKey,
}, nil)
if err != nil {
log.Fatal(err)
}
object, err := encrypter.Encrypt(plaintext)
if err != nil {
log.Fatal(err)
}
token, err := object.CompactSerialize()
if err != nil {
log.Fatal(err)
}
fmt.Println("JWE Token:", token)
//
// 復號化
//
object, err = jose.ParseEncrypted(token)
if err != nil {
log.Fatal(err)
}
decrypted, err := object.Decrypt(privateKey)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Decrypted: %s\n", decrypted)
}
実行結果
Plaintext: Lorem ipsum dolor sit amet
JWE Token: eyJhbGciOiJSU0EtT0FFUCIsImVuYyI6IkExMjhHQ00ifQ.YOnRGwtYnd4C4w_OEtxlHhFzJT3qUH6APt7jDXTn3ln5yt-kS1RxM29uM3NEBnop5ZcQhtqNmZ90VeyN3AqsMGDp1KkEvHOb1O405Iwk1taaI5XZfGI6dxAhnH6YonbUBpQkqgHcNQhOYxgXuOkhecCUCyKKK-RcsukQSDNWB4rjf0QRwvcjtC4uAsnplsP2gwKNof4QgV_yMoVLBYFg6YVyDeYdIVSLIeZxO0SKIDbugF698Mufrj5gjsd_ydm-kvFlLO29Bti5eaDnOetlw1QzCzHa8fjUHoRyS6L5kcKjPqs6HzIxZv53OxrAsWq2IWdmnkMfBSXIMHCCIPeBVw.RZ9qri36pOoV_5C-.bQCU4nVQ9lrEH4EgvExzTjziJ8iWTzW7BKI.iNIOEhEt9eglEX6rGXxXpw
Decrypted: Lorem ipsum dolor sit amet
JWEへの批判
JWE(またはJOSE)はセキュリティ上の批判に曬されていいます。特にParagonのエンジニアは、JWSでは多くの実裝で攻撃が可能で、JWEは定義されている公開鍵暗號アルゴリズムは脆弱なものが多く、開発者が自分の足を撃つリスクが高いと主張しています。Fernetの方が優れており、さらにJOSEの問題を解決したPASETOを提案しています。
- JOSEは、絶対に避けるべき悪い標準規格である | POSTD
- No Way, JOSE! Javascript Object Signing and Encryption is a Bad Standard That Everyone Should Avoid - Paragon Initiative Enterprises Blog
-
PKCS #1v1.5パティング付きRSA
- "PKCS #1v1.5パディング付きRSAは、パディングオラクルと呼ばれる一種の選択暗號文攻撃に対して脆弱"
-
OAEPパディング付きRSA
- RSAを安全だとみなすなら安全だが、"セキュリティの専門家はRSAから移行するように勧めている"
-
ECDH
- JWT(JOSE?)では楕円曲線ディフィー・ヘルマンを使ったECDHのみ許されているが、この曲線は Invalid-Curve攻撃 の脆弱性があります。 AES-GCM 上記の公開鍵暗號モードは疑わしいので、事前に鍵を共有する共有鍵暗號モードをつかうべき(?)
まとめ
いかがでしたか?コンテンツの暗號表現であるJavascript Web Encription の仕様、作成方法について解説しました。 PASETO や Branca などの代替規格を検討してもいいかもしれませんね。お疲れ様でした。