JSON Web Signatureの紹介

こんばんは、ritouです。
TLで13日の金曜だからJSONの・・・みたいなのを見つけたのでちょっと書きます。
って思ったらもう土曜日でしたorz

JSON Web Signatureとは?

draft-jones-json-web-signature-02 - JSON Web Signature (JWS)
JWSは、HTTP Authorization HeaderやURIクエリパラメータなど制限がある環境でも使えるようなコンパクトな署名形式です。ってあります。
ざっくり説明すると、次の3つから構成されます。

これらを連結すると、署名付きJSONデータをやりとりできますね。
署名の作成アルゴリズムですが、これらが利用可能です。

  • HS256 : HMAC using SHA-256 hash
  • HS384 : HMAC using SHA-384 hash
  • HS512 : HMAC using SHA-512 hash
  • RS256 : RSA using SHA-256 hash
  • RS384 : RSA using SHA-384 hash
  • RS512 : RSA using SHA-512 hash
  • ES256 : ECDSA using P-256 curve and SHA-256 hash
  • ES384 : ECDSA using P-384 curve and SHA-384 hash
  • ES512 : ECDSA using P-521 curve and SHA-512 hash

実際はJWS Header Inputのパラメータなどもっと細かいことがあるのですが、気になる方は仕様をみてください。

つかいどころ、使われどころ

OpenID ABCでは、OAuth 2.0のAccess Tokenと同じタイミングでOpenIDのClaimsがRPに渡されます。
その際に、このJWSを用いた署名つきのJSONデータがやりとりされます。
まぁ、構造化されたデータをやりとりするしくみとして他にもいろんなとこで使えそうですね。

気になってきた方は、このあたりを見てください。
A Map for OpenID Connect | OpenID
JWT,JWS,JWK,JWE・・・JSONなんとかがいっぱいいますよ!

ここでいうBase64 URLエンコードとは?

仕様はここにあります。
RFC 4648 - The Base16, Base32, and Base64 Data Encodings

JWSの仕様のAppendix Cにも説明がありますが、Encodeの流れはこんな感じですね。

  • Base64 encode
  • paddingの"="をとっぱらう
  • "+" → "-", "/" → "_"

サンプルは、3 236 255 224 193っていうバイトコードを使っていますが、phpとかで単純にbase64_encodeとかすると、
"A+z/4ME="ってなるので、上記の手順を踏むと"A-z_4ME"になるわけですね。

<?php

$base64sample_bcodearr = array(3,236,255,224,193);
$base64sample_str = barr2str($base64sample_bcodearr);
print $base64sample_str;
$base64sample_encoded = base64_urlencode($base64sample_str);
print $base64sample_encoded;

// バイトコードから文字列に変換
function barr2str($byte_arr) {
    $str = implode("", array_map("chr", $byte_arr));
    return $str;
}

// Base64 URLエンコード
function base64_urlencode($str) {
    $enc = base64_encode($str);
    $enc = rtrim($enc, "=");
    $enc = strtr($enc, "+/", "-_");
    return $enc;
}

話を戻しますよ。

HMAC SHA-256を用いたJWS作成方法

仕様にのっているサンプルを実際に試してみます。
draft-jones-json-web-signature-02 - JSON Web Signature (JWS)

JWS Header Input,JWS Payload Inputはこうなっています。

JWS Header Input
{"typ":"JWT",
"alg":"HS256"}

JWS Payload Input
{"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}

PHPのサンプルはこちらです。

<?php 

// JWS Header Inputを用意
$jws_header_input = <<< SAMPLE
{"typ":"JWT",\r\n "alg":"HS256"}
SAMPLE;
print "jws_header_input : \n" . $jws_header_input . "\r\n\r\n";


// Base64 URL Encodeする
$jws_header_input_encoded = base64_urlencode($jws_header_input);
print "jws_header_input_encoded : " . $jws_header_input_encoded . "\r\n\r\n";


// JWS Payload Inputを用意
$jws_payload_input = <<< SAMPLE
{"iss":"joe",\r\n "exp":1300819380,\r\n "http://example.com/is_root":true}
SAMPLE;
print "jws_payload_input : \n" . $jws_payload_input . "\r\n\r\n";


// Base64 URL Encode
$jws_payload_input_encoded = base64_urlencode($jws_payload_input);
print "jws_payload_input_encoded : " . $jws_payload_input_encoded . "\r\n\r\n";


// ピリオドで連結して、JWS Signing Inputを作成
$jws_signing_input_encoded = $jws_header_input_encoded . "." . $jws_payload_input_encoded;


// サンプルにあるバイトコードをHMACのKeyに利用
$jws_signing_key_bcodearr = array(3, 35, 53, 75, 43, 15, 165, 188, 131, 126, 6, 101, 119, 123, 166, 143, 90, 179, 40, 230, 240, 84, 201, 40, 169, 15, 132, 178, 210, 80, 46, 191, 211, 251, 90, 146, 210, 6, 71, 239, 150, 138, 180, 195, 119, 98, 61, 34, 61, 46, 33, 114, 5, 46, 79, 8, 192, 205, 154, 245, 103, 208, 128, 163);
$jws_signing_key = barr2str($jws_signing_key_bcodearr);


// hash_hmac関数で署名作成。第3引数にtrueが必要
$jws_crypto_output = base64_urlencode(hash_hmac("sha256", $jws_signing_input_encoded, $jws_signing_key, true));
print "jws_crypto_output : ". $jws_crypto_output . "\r\n\r\n";


// ピリオドで結合して終わり
$jws = $jws_signing_input_encoded . "." . $jws_crypto_output;
print "jws : " . $jws;


// バイトコードから文字列に変換
function barr2str($byte_arr) {
    $str = implode("", array_map("chr", $byte_arr));
    return $str;
}

// Base64 URLエンコード
function base64_urlencode($str) {
    $enc = base64_encode($str);
    $enc = rtrim($enc, "=");
    $enc = strtr($enc, "+/", "-_");
    return $enc;
}

実行結果はこうなります。

jws_header_input :
{"typ":"JWT",
"alg":"HS256"}

jws_header_input_encoded : eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9

jws_payload_input :
{"iss":"joe",
"exp":1300819380,
"http://example.com/is_root":true}

jws_payload_input_encoded : eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ

jws_crypto_output : dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

jws : eyJ0eXAiOiJKV1QiLA0KICJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk

どうでしょうか。
JSON系の仕様は、今後も追っていきたいと思います。

ではまた。