LoginSignup
1

More than 1 year has passed since last update.

Compact じゃない JWS Serialization

Last updated at Posted at 2021-12-15

JWSとは、RFC 7515(対訳版)JSON Web Signatureのことですね。ID系ならRFC 7519(対訳版)JWT(JSON Web Token)でも使われているのでまあ常識?RFC 7515に載っているJWSの例が以下です。ちょっと見やすくするために色分けと強調をしています。

JWSの例
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

ID厨の人なら「ピリオド "." で3つで区切られているBase64文字列で、最初の2つが "eyJ" の3文字で始まっているからJWS(JWT)だね。ふふん。」と言うことでしょうw そう一般にはこの形式がJWSです。JWSで検索するとこの形式のJWS(JWT)説明ページが沢山見つかると思います。ところでRFC 7515の目次を見ると7章 シリアライズ(直列化)が2つに分かれています。シリアライズとは複数の要素を一列に並べる処理です。

      :
7. Serializations  
 7.1. JWS Compact Serialization  
 7.2. JWS JSON Serialization  
      :

実は先に示した例は「7.1. JWS Compact Serialization」の形式です。じゃあ Compact じゃない「7.2. JWS JSON Serialization」って何?と言うのが今日のお話です。つまり「Compact じゃない JWS Serialization」とは JWS JSON Serialization と言うことになります。

JWS Compact Serialization(復習)

とは言え違いを理解するためにも JWS Compact Serialization を復習しておきましょう。もう一度先ほどのサンプルを見てみます。

JWS Compact Serialization の例
eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

先ほど書いた通りピリオドで分けられた3つのパートが連結されています。これを仕様書では以下と説明しています。BASE64URLはBase64のURLエンコードで、UTF8は文字コード指定ですね。

BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload) || '.' || BASE64URL(JWS Signature) 

と言うことで3つのパートは、保護ヘッダー部・ペイロード部・署名部となります。

名称 概要
JWS Protected Header 保護ヘッダー部。このJWSのタイプ(JWT等)と署名アルゴリズム等がを記載。Protected(保護)とは署名対象に含まれている(署名に守られている)ヘッダーとの意味。
JWS Payload ペイロード部。署名対象のJSON情報を記載。実はどのような情報でも良いがJWT等ではJSONで書かれた属性情報をBase64化した情報となるので最初3文字が"eyJ"となる。
JWS Signature 署名部。Compactの場合には「保護ヘッダー部+ペイロード部」に対してデジタル署名した署名値。バイナリ情報なのでBase64デコードしても読めない。

署名部はバイナリデータですので除外して、例のヘッダー部とペイロード部をBase64URLでデコードして見て行きましょう。

例のヘッダー部
Base64 eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9
decode { "typ":"JWT", "alg":"HS256" }

デコードすると2つのパラメーター "typ" と "alg" が出てきました。"typ" が種別を示しますが判別できれば基本何でも良いか IANA に登録します。ここでは "JWT" なので JSON Web Token であることを示しています。"alg" が署名アルゴリズムを示します。"HS256" は HMAC SHA-256 で署名していることが分かります。つまり署名部のバイナリ情報は HMAC SHA-256 の公開鍵を使って検証できることになります。"alg" で使える暗号アルゴリズムは RFC 7518 - JSON Web Algorithms (JWA) に記載されていますが、JWSで使える暗号アルゴリズムは以下とあります。

"alg" Param Value Digital Signature or MAC Algorithm Implementation Requirements
"HS256" HMAC using SHA-256 Required
"HS384" HMAC using SHA-384 Optional
"HS512" HMAC using SHA-512 Optional
"RS256" RSASSA-PKCS1-v1_5 using SHA-256 Recommended
"RS384" RSASSA-PKCS1-v1_5 using SHA-384 Optional
"RS512" RSASSA-PKCS1-v1_5 using SHA-512 Optional
"ES256" ECDSA using P-256 and SHA-256 Recommended+
"ES384" ECDSA using P-384 and SHA-384 Optional
"ES512" ECDSA using P-521 and SHA-512 Optional
"PS256" RSASSA-PSS using SHA-256 and MGF1 with SHA-256 Optional
"PS384" RSASSA-PSS using SHA-384 and MGF1 with SHA-384 Optional
"PS512" RSASSA-PSS using SHA-512 and MGF1 with SHA-512 Optional
"none" No digital signature or MAC performed Optional

電子署名屋だと "RS256" と "ES256" 辺りは使いそうです。その他の署名・暗号系のパラメーターは IANA の JSON Object Signing and Encryption (JOSE) に登録します。

次にペイロード部は基本何でも好きに使ってよいのですが、例では "iss" や "exp" とID認証のトークンで使われているパラメーターがセットされていました。

例のペイロード部
Base64 eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ
decode {"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}

最後に署名部はこの例では HMAC SHA-256 を署名アルゴリズムとして利用し、署名対象は以下のようにヘッダー部とペイロード部をピリオドで連結したASCIIデータとなります。つまり最初の2つの部分ですね。

ASCII(BASE64URL(UTF8(JWS Protected Header)) || '.' || BASE64URL(JWS Payload))

以上が JWS Compact Serialization の概要です。シリアライズされた内容はJSON形式ですが、それをBase64化して連結することでコンパクトになっており、JWT のようなトークンとして利用できる仕様となっていました。

JWS JSON Serialization(本題)

閑話休題。さて本題の JWS JSON Serialization です。JSONシリアライズではCompactシリアライズに加えてオプションですが JWS Unprotected Header(非保護ヘッダー部)が追加され、以下の4パートとなります。

  • "payload", with the value BASE64URL(JWS Payload)
  • "protected", with the value BASE64URL(UTF8(JWS Protected Header))
  • "header", with the value JWS Unprotected Header
  • "signature", with the value BASE64URL(JWS Signature)

署名対象が保護ヘッダー部+ペイロード部である点はCompactシリアライズと同じです。それでは JWS JSON Serialization のサンプルを見てみましょう。後述しますがこのサンプルはフラット化(Flattened)された形式です。

{
  "payload":"eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ",
  "protected":"eyJhbGciOiJFUzI1NiJ9",
  "header": {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},
  "signature":"DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8ISlSApmWQxfKTUJqPP3-Kg6NU1Q"
}

保護ヘッダ部・ペイロード部・署名部がBase64化されているのはCompactシリアライズと同じですが、JSONシリアライズではパラメーター名が付いてJSON形式になっています。なお署名対象ではない非保護ヘッダ部 "header" だけはBase64化されずJSON形式のまま記述されています。

保護ヘッダ部とペイロード部も中はJSONなのだからBase64化しなくても…と思われるかもしれませんが、デジタル署名では署名対象が1バイト違っても値が異なってしまいます。その為にBase64化することでバイナリとして固定して扱えるようにしています。これで表記ゆれや改行コード追加等による問題を回避しています。XML署名だとBase64化せずにC14N正規化(XML Canonicalization)により解決していますが、正直C14N正規化は面倒なので個人的にはBase64化しているJWSの方が実装が楽で良いと思います。

一応サンプルの中も見ておきましょう。保護ヘッダ部とペイロード部はBase64URLデコードしてあります。

パート 内容
保護ヘッダ部 {"alg":"ES256"}
非保護ヘッダ部 {"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"}
ペイロード部 {"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}

保護ヘッダ部の "alg" が "ES256" なので、署名アルゴリズムは ECDSA using P-256 and SHA-256 の楕円曲線暗号を使っていることが分かります。非保護ヘッダ部の "kid"(keyid)は鍵ヒントです。この部分は署名に守られていないので改ざん可能ですがkidが改ざんされても正しい鍵を取得しないと署名検証に失敗するので問題ありません。以上からフラット化された JWS JSON Serialization は以下の形式になっていることが分かりました。

{
  "payload":"<payload contents>",
  "protected":"<integrity-protected header contents>",
  "header":<non-integrity-protected header contents>,
  "signature":"<signature contents>"
}

さてフラット化(Flattened)とはどう言う意味かと言うと電子署名が1つだけの形式と言う意味になります。完全な(Complete)JWS JSON Serialization は複数の電子署名を付与することが可能となっています。signature部が配列となる以下の形式になります。

{
  "payload":"<payload contents>",
  "signatures":[
    {
     "protected":"<integrity-protected header 1 contents>",
     "header":<non-integrity-protected header 1 contents>,
     "signature":"<signature 1 contents>"
    },
    ...
    {
     "protected":"<integrity-protected header N contents>",
     "header":<non-integrity-protected header N contents>,
     "signature":"<signature N contents>"
    }
  ]
}

完全な(Complete)JWS JSON Serialization では署名は保護ヘッダ部・非保護ヘッダ部・署名部の3つがセットとなって signatures パラメーターの配列として複数指定することが可能となります。並列署名(Prallel Signatures)と呼ばれる形式であり、1つの署名対象に互いに独立に複数の署名を付与することが可能です。下の図の左上のパターンですね。
image.png
それでは完全な(Complete)JWS JSON Serialization のサンプルを見てみましょう。

{
  "payload":
    "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtc
     GxlLmNvbS9pc19yb290Ijp0cnVlfQ",
  "signatures":[
    {
      "protected":"eyJhbGciOiJSUzI1NiJ9",
      "header":{"kid":"2010-12-29"},
      "signature":
        "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ
         mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb
         KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl
         b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES
         c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX
         LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"
    },
    {
      "protected":"eyJhbGciOiJFUzI1NiJ9",
      "header":{"kid":"e9bc097a-ce51-4036-9562-d2ade882db0d"},
      "signature":
        "DtEhU3ljbEg8L38VWAfUAqOyKAM6-Xx-F4GawxaepmXFCgfTjDxw5djxLa8IS
         lSApmWQxfKTUJqPP3-Kg6NU1Q"
    }
  ]
}

このサンプルでは1つの palyload に対して、2つの signature があります。最初の署名の保護ヘッダ部をBase64デコードすると {"alg":"RS256"} となりますので RSASSA-PKCS1-v1_5 using SHA-256 方式による署名となります。さすがにRSA暗号だと署名値のサイズも大きいですね。

JWSのPKI/証明書関連パラメーター

さてここまではデジタル署名のアルゴリズム的な話でしたが、電子署名としてはPKI(公開鍵基盤)の話も必要となります。RFC 7515 の目次を見ると4章のJOSE(Javascript Object Signing and Encryption)ヘッダーにある、"x5u" / "x5c" / "x5t" / "x5t#S256" 辺りが証明書関連のようですので見てみましょう。

        :
4. JOSE Header
  4.1. Registered Header Parameter Names
        :
    4.1.5. "x5u" (X.509 URL) Header Parameter
    4.1.6. "x5c" (X.509 Certificate Chain) Header Parameter
    4.1.7. "x5t" (X.509 Certificate SHA-1 Thumbprint) Header Parameter
    4.1.8. "x5t#S256" (X.509 Certificate SHA-256 Thumbprint) Header Parameter
        :
"x5u" (X.509 URL) Header Parameter

"x5u" はX.509電子証明書の場所をURL(URI)で指定するパラメーターです。外部に置いたX.509電子証明書の参照時に利用します。URLの先に置かれるX.509電子証明書はPEM形式である必要があり、複数の証明書を含むことが可能です。複数の場合の区切り方は RFC 4945 - The Internet IP Security PKI Profile of IKEv1/ISAKMP, IKEv2, and PKIX の6.1章に記載されています。PEM形式の証明書はテキストで以下のようになっています。OpenSSLのコマンド等でバイナリのDER形式との相互変換が可能です。

-----BEGIN CERTIFICATE-----
MIID1jCCAr6gAwIBAgIHA41+pMaAAzANBgkqhkiG9w0BAQsFADBNMQswCQYDVQQG
EwJKUDERMA8GA1UEChMITGFuZ0VkZ2UxDTALBgNVBAsTBGRlbW8xHDAaBgNVBAMT
  :
Nm0GbXwL0Dd1/XkjeI/0e0Fy+XAMspt02Cq6d82h+ZMm5XUBrG9P23Mkmy1VhzJM
m/Lm+3FBCd1Zrh50xlkvMfY/NEm1Ijx0YY8=
-----END CERTIFICATE-----
"x5c" (X.509 Certificate Chain) Header Parameter

"x5c" はX.509電子証明書を直接指定するパラメーターです。配列となっているので複数のX.509証明書を格納できます。格納するX.509証明書はバイナリのDER形式のデータをBase64化(注Base64URLではない)した文字列で指定します。例えば以下のように指定が可能です。これ例では3証明書となります。PKI厨は最初の3文字 "MII" を見るとDERデータだと分かったりしますw

"x5c":["MIIE3j...(cert1)...WVU+4=",
       "MIIE+z...(cert2)...9VZw==",
       "MIIC5z...(cert3)...ViH0Pd"]
"x5t" (X.509 Certificate Thumbprint) Header Parameter

"x5t" はX.509電子証明書のDER形式データに関するハッシュ値です。このハッシュ値は証明書の拇印(Certificate Thumbprint)と呼ばれます。"x5t" だと SHA-1 の、"x5t#S256" であれば SHA-256 のハッシュ値をBase64URLエンコード化して指定します。

あれ?CRLやOCSPレスポンスの情報は?

長期署名厨であれば「あれ証明書だけなの?CRLやOCSPレスポンスの情報は入れられないの?」と思うことでしょう。残念ながらJWS標準ではCRLやOCSPレスポンス等の有効性を確認する情報を含むことは出来ないようです。まあこの辺りはXML署名(XmlDsig)と同じと考えて良さそうです。つまりXML署名に対してXAdES長期署名があるように、JSON署名(JWS)に対してJAdES長期署名が必要と言うことになります。

JAdES(長期署名)

JAdES は欧州ETSIの規格です。長期署名に詳しくない人は Re:ゼロから始める長期署名 を先に読んでください。

長期署名にする為に必要な物は検証情報とタイムスタンプトークン(RFC 3161)の2つです。これらに対応するパラメーター類が JAdES 規格の ETSI TS 119 182-1 V1.1.1 (2021-03) で追加されています。では詳しく…と言いたいところですが今回は JWS JSON Serialization の説明回ですので、JAdES に関してはまたいずれ…今日のところはJAdESの構造例を載せて終わりにします。

{
  "payload":
    "eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtc
     GxlLmNvbS9pc19yb290Ijp0cnVlfQ",
  "signatures": [
    {
      "protected": "eyJhbGciOiJSUzI1NiIsImN0...(略)...bInNpZ1QiXX0",
      "header": {
        "etsiU": ["eyJjU2lnIjoiZXlK...(略)...RFBJVGl0bzZwQXQ5cVEifQ"]
      },
      "signature": 
        "cC4hiUPoj9Eetdgtv3hF80EGrhuB__dzERat0XF9g2VtQgr9PJbu3XOiZj5RZ
         mh7AAuHIm4Bh-0Qc_lF5YKt_O8W2Fp5jujGbds9uJdbF9CUAr7t1dnZcAcQjb
         KBYNX4BAynRFdiuB--f_nZLgrnbyTyWzO75vRK5h6xBArLIARNPvkSjtQBMHl
         b1L07Qe7K0GarZRmB_eSN9383LcOLn6_dO--xi12jzDwusC-eOkHWEsqtFZES
         c6BfI7noOPqvhJ1phCnvWh6IeYI2w9QOYEUipUTI8np6LbgGY9Fs98rqVt5AX
         LIhWkWywlVmtVrBp0igcN_IoypGlUPQGe77Rw"
    }
  ]
}

ぱっと見て気が付くところは、"header" に "etsiU" と言うパラメーターがあるところでしょうか。私もまだちゃんと見ていないのですが、XAdESで言うところの、SignedProperties(署名パラメーター群)下の属性が "protected" パラメーターの中にあり、UnsignedProperties(非署名パラメーター群)下の属性が "etsiU" パラメーターの中にあるようです。以下の長期署名構造例で言えば「署名値="signature"」「署名属性="protected"」「署名タイムスタンプ・検証属性・アーカイブタイムスタンプ="etsiU"」となります。
image.png
つまり署名タイムスタンプ "sigTst"・検証情報(証明書群 "xVals"・有効性情報群 "rVals")・アーカイブタイムスタンプ "arcTst" は非署名パラメーターなので、"etsiU" の中にあります。

なお JWS Compact Serialization を使ったJAdESもありますが、基本的に署名のみの JAdES-B のみとなります。まあ "header" が使えないので JAdES-T ~ JAdES-LTA は無理と言うことです。

JAdESの勉強をするにはXAdESを知っていると理解が早そうです。JAdES仕様書の Annex C に、JAdESとXAdESのタグ比較があります。う~んまだまだ勉強が必要ですね。

まとめ

と言うところで簡単にまとめます。RFC 7515 - JSON Web Signature (JWS) には2つの Serialization があります。1つは RFC 7519 - JSON Web Token (JWT) に使われている JWS Compact Serialization で、もう1つが ETSI TS 119 182-1 - JAdES digital signatures に使われている JWS JSON Serialization です。これらの関係をまとめた図が以下となります。
image.png
おそらくID厨が注目するのが JWT に使われている JWS Compact Serialization で、PKI厨が注目するのが JAdES に使われている JWS JSON Serialization でしょうか。また勉強して JAdES について書けると良いなと考えています。では今日はここまで!

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
What you can do with signing up
1