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可以利用JWSJWE

RFC 7516 - JSON Web Encryption(JWE)

序列化格式

JWE 定义了两种序列化形式,分别是JWE Compact SerializationJWE 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 标记的信息。 需要两个密钥:algenc
  • 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保护头,其中encalg头是必需的参数。

以下是一些代表性的头参数:

  • 必需头

    • enc头(加密算法)

      • 指定用于加密内容的算法。除了RFC 7518 JWA的5.1章节中定义的算法标识符之外,还可以指定具有冲突防止前缀的未注册算法。算法必须是使用指定长度的密钥的AEAD(带认证的加密)算法。后面会详细介绍可用的算法。
    • alg头(算法)

  • 指定密钥的头

    • 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
    • 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生成和②内容加密。

image.png

① 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密钥包装

在每种密钥管理模式下,按照下表的步骤生成密钥。

image.png

② 内容加密

通过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。

3.创建 JWE 令牌

以下五个部分用句号". 来完成 JWE 令牌。

  1. 以 UTF-8 + Base64Url 编码的 JWE 受保护标头
  2. 在 ① 中获取的 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。

总结

您觉得如何?我们讨论了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オブジェクトの文字列。algenc の2キーが必須。
  • 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)

  • 鍵を指定するヘッダ

    • 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 が使用できます。
    • 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ステップで構成されます。

image.png

① 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による鍵ラップ

各鍵管理モードでは、次の表な手順で鍵を生成します。

image.png

② コンテンツ暗号化

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として任意のパラメータを指定できます。

3. JWE トークンを構築する

以下の5コンポーネントをピリオド'.'で連結し、JWEトークンの完成です。

  1. JWE Protected HeaderをUTF-8エンコード+Base64Urlしたもの
  2. ① で求めたJWE Encryption Key をBase64Urlしたもの
  3. ② でランダムに生成したJWE Initial Vector を Base64Urlしたもの
  4. ② で暗号化した出力であるJWE Ciphertext を Base64Urlしたもの
  5. ② で暗号化した出力である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を提案しています。

まとめ

いかがでしたか?コンテンツの暗号表現であるJavascript Web Encription の仕様、作成方法について解説しました。 PASETO や Branca などの代替規格を検討してもいいかもしれませんね。お疲れ様でした。


阿东
201 声望54 粉丝

引用和评论

0 条评论