自堕落な技術者の日記

基本は喰ってるか飲んでるかですが、よく趣味でカラオケ・PKI・署名・認証・プログラミング・情報セキュリティをやっています。旅好き。テレビ好きで芸能通

W3C

W3C Web Cryptography APIとの果てしなき戦い(第3回 動くサンプル1)

前回はW3C Web Cryptography APIの鍵生成、署名などについて説明しましたが、実際に動く実例がないとAPIの有り難みもわかないのかなと思いまして、とりあえず3つのサンプルを作ってみました。サンプルはChromeやFirefoxでは動くようになっています。画像をクリックするとサンプルページが開くようになっています。

ハッシュ関数


wc-hash
ハッシュアルゴリズムを選んで、ハッシュ計算したい文字列(UTF-8だから日本語も可能)を入力して、「ハッシュ計算」のボタンを押すだけで、16進数でハッシュ計算結果が表示されます。

公開鍵ペアの生成


wc-genkey
RSA公開鍵ペアを生成します。W3C Web Cryptography APIでは、鍵生成やインポートの際、どのような用途で、どのようなハッシュ関数と一緒に使用するかを指定する必要があるようで、生成時に選択するようにしていますが、PKCS#8 で秘密鍵や公開鍵をエクスポートする際には、まぁ、関係のない話になってしまいます。PKCS#8のバイナリデータ(ArrayBufferView)をPEM形式に変換するのにjsrsasignライブラリを使っています。

RSA鍵による署名と検証


wc-sigver
RSA鍵でUTF-8の文字列に対してSHA256withRSA署名アルゴリズムで署名と署名検証ができます。鍵はW3C Web Cryptography APIで内部生成した鍵ペアで署名と署名検証することもできますし、PKCS#8 PEM形式やJSON Web Key(JWK)フォーマットの秘密鍵や公開鍵をインポートして署名や署名検証することもできます。

サンプルの動作環境

これらのサンプルページは新しいChromeのWindows、Android、Mac OS X、Linux版、新しいFirefoxのWindows、Mac OS X、Android版で動作します。Safariでは一部がMac OS X版、iOS8+などで動作します。Internet ExplorerではPromise APIをサポートしていないので動作しません。IEでも動作するようにコード書くのはちょっと面倒で、それらの相互運用性を持たせるような実装の仕方については別途紹介しようかと思っています。

おわりに

とりあえず、いろんなブラウザでW3C Cryptography APIの動作を試してみることができるので、遊んでやってください。ソースコードはgithubに置いてあるのでご覧ください。FirefoxとChromeだけでも相互運用性持たせるのに結構壁にぶちあたったりして、その上、SafariやIEなども一緒にサポートするコードを書くのはかなり骨が折れるんですよね。でも、AndroidのFirefoxでも動くのを見ると、ちょっと目がうるうるしてきたりしますw。今日はこのへんで。

関連記事

W3C Web Cryptography APIとの果てしなき戦い(第2回 RSA署名生成と検証)

前回は、 W3C Web Cryptography APIについて、一番簡単そうなハッシュの生成を紹介しながら、 幾つかの課題について紹介しました。

今回は、W3C Web Cryptography APIによる、 RSA公開鍵暗号によるデジタル署名の生成と検証を紹介していこうと思います。

署名と検証の例題って、よく一人の人というかプログラムが、 鍵ペアを生成して、署名して、検証するサンプルを出しますよね。( sjclの楕円暗号のテストコードとか・・・) 一人の人が同時に署名と検証を行うってユースケースとしてありえないと思うんですよね。 実際、私が見た当時、sjclでは鍵オブジェクトの秘密鍵と公開鍵を 分けてエクスポートができないようで、実際問題使えないことがありました。

そんなわけで、今回はまず、ちゃんとOpenSSLで生成した秘密鍵と公開鍵をインポートして、 署名の生成と検証をできるようにしたいと思います。 その後で、RSAの鍵ペアの生成をします。

データの準備

W3C Web Cryptoで鍵生成してもよいのですが、今回はOpenSSLで作ったRSA 2048bitの 秘密鍵で署名することにしたいと思います。PKCS#5 PEM形式の2048bit RSA秘密鍵を生成し、 それを平文のPKCS#8 DER形式に変換し、 bin2hexスクリプト で16進数文字列表現に変換します。

% openssl genrsa 2048 > k2048.p5p.pem % openssl pkcs8 -topk8 -nocrypt -in k2048.p5p.pem -outform DER -out k2048.p8p.der % bin2hex < k2048.p8p.der > k2048.p8p.hex #16進数形式のRSA秘密鍵
鍵ペアとなる公開鍵について、同様に16進数文字列形式のものを取得します。
% openssl rsa -in k2048.p5p.pem -pubout -out k2048.pub.pem # PEM形式の公開鍵 % openssl rsa -in k2048.p5p.pem -pubout -outform DER -out k2048.pub.der #DER形式の公開鍵 % bin2hex < k2048.pub.der > k2048.pub.hex # 16進数形式のRSA公開鍵
次に"aaa"という文字列に対して署名するとして、この文字列が含まれる署名対象 データファイルを作っておきます。
% echo -n aaa > aaa.txt
OpenSSLで前述の秘密鍵と署名対象データを使ってSHA1で署名すると結果のデータは以下のように 作られます。同様に16進数データを作っておきます。
% openssl dgst -sha -sign k2048.p5p.pem aaa.txt > k2048oaaa.sig.bin % bin2hex < k2048oaaa.sig.bin > k2048oaaa.sig.hex
OpenSSLで前述の公開鍵と署名対象データと生成された署名値データを使って検証するには 以下のように行います。
% openssl dgst -sha1 -verify k2048.pub.pem -signature k2048oaaa.sig.bin aaa.txt Verified OK
署名値は正しいことがわかります。

秘密鍵のインポートと署名の生成

秘密鍵の16進数値を使って文字列"aaa"に対してSHA1withRSAで署名する場合の、 W3C Web Cryptoのコードは以下のようになります。

var prvHex = "308204bd02010030..."; // 前節の16進数秘密鍵 var prvUint8a = hextouint8a(prvHex); // 秘密鍵のUint8Array var aaaUint8a = asciitouint8a("aaa"); // 署名対象aaaのUint8Array // PKCS8形式の秘密鍵を署名用にインポート window.crypto.subtle.importKey( "pkcs8", prvUint8a, { name: "RSASSA-PKCS1-v1_5", hash: {name: "SHA-1"} }, true, ["sign"] ).then( // 秘密鍵インポートに成功したら署名する function(prvKey) { console.log("**importKey** 成功"); console.log("prvkey=" + prvKey); return window.crypto.subtle.sign("RSASSA-PKCS1-v1_5", prvKey, aaaUint8a); }, function(e) { console.log("**importKey** エラー: " + e); } ).then( // 署名に成功したら署名値(ArrayBuffer)を16進数表示 function(sigVal) { console.log("**sign** 成功"); console.log("sigVal=" + abtohex(sigVal)); }, function(e) { console.log("**sign** エラー: " + e); } );
実装で注意しなければいけないポイントは以下の通りです。
  • OpenSSLの(平文の)PKCS#8秘密鍵を使うにはimportKeyで"pkcs8"を指定する。
  • 鍵使用目的で["sign"]を指定する。
コンソールに"sigVal="で表示された16進数の署名値は、 k2048oaaa.sig.hexファイルの値と同じになっていると思います。 hex2binスクリプト で署名値をバイナリデータに変換して、以下のようにOpenSSLで検証することが できます。うまく検証できたでしょうか。
署名値16進数をk2048waaa.sig.hexとして保存 % hex2bin < k2048waaa.sig.hex > k2048waaa.sig.bin openssl dgst -sha1 -verify k2048.pub.pem -signature k2048waaa.sig.bin aaa.txt Verified OK
hextouint8aについてはこちらをご覧ください。

公開鍵のインポートと署名の検証

前々節のOpenSSLで作った署名値でもよいですし、前節で生成された署名値でも よいですが、これをインポートした公開鍵で検証してみましょう。 以下のようなコードで検証することができます。

var pubHex = "30820122300d0609..."; // 前々節の16進公開鍵 var sigHex = "afd36b6f3f2af788..."; // 前節or前々節の16進署名値 var pubUint8a = hextouint8a(pubHex); // var sigUint8a = hextouint8a(sigHex); var aaaUint8a = asciitouint8a("aaa"); window.crypto.subtle.importKey( "spki", pubUint8a, { name: "RSASSA-PKCS1-v1_5", hash: {name: "SHA-1"} }, true, ["verify"] ).then( function(pubKey) { console.log("**importKey** 成功"); console.log("pubKey=" + pubKey); return window.crypto.subtle.verify("RSASSA-PKCS1-v1_5", pubKey, sigUint8a, aaaUint8a); }, function(e) { console.log("**importKey** エラー: " + e); } ).then( function(isValid) { console.log("**verify** 処理成功"); if (isValid == true) { console.log("**verify** 署名検証成功(一致)"); } else { console.log("**verify** 署名検証失敗(不一致)"); } }, function(e) { console.log("**verify** エラー: " + e); } );
実装で注意するポイントは以下の通りです。
  • OpenSSLの公開鍵をインポートする際には"spki"を指定する。
  • インポートする際の鍵使用目的に"verify"を指定する。
  • 署名検証の結果はブール値(isValid)で返されるのでこれに従う。 関数が呼ばれただけで安心して終わりにしない。

RSA鍵ペアの生成

上の例ではインポートした秘密鍵、公開鍵を使っていますが、 鍵ペアの生成だってできます。(現状RSAだけのようですが・・・) RSA 2048bitの鍵ペアは以下のように生成します。

var paramKeyGen = { name: "RSASSA-PKCS1-v1_5", // 現状ではRSA-PSS, RSA-OAEPは指定できなそう modulusLength: 2048, // 鍵長 publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 公開指数 65537 hash: { name: "SHA-256" } // 署名のハッシュアルゴリズム?別に利用外のを指定しても良い? }; window.crypto.subtle.generateKey( paramKeyGen, // 生成する鍵ペアのための鍵長等の各種パラメータ true, // エクスポート可能にするかどうかのフラグ ["sign", "verify"] // 鍵使用目的 ).then( function(key) { console.log("**generateKey** 成功"); console.log("秘密鍵:" + key.privateKey); console.log("公開鍵:" + key.publicKey); }, function(e) { console.log("**generateKey** エラー: " + e); } );

Web Crypto APIの困った所5:importKeyの融通の利かなさ

こうしてこのブログをすんなり見ていただくと、「W3C Web Cryptoって簡単じゃん」と 思われるかもしれませんが、このような動く例を見つけるまで、かなりの紆余曲折があり 時間がかかっています。importKeyの正しく動く例というのが、仕様に記載されておらず、 他の方のサンプルも動くものあり、動かないものあり、何が正しいのかよくわかりません。 importKeyの引数には、

  • format - 鍵データの形式 (pkcs8, spki, jwk, raw)
  • keyData - 鍵データ (ArrayBufferView(Uint8Array)かJSONデータ)
  • algorithm - アルゴリズムのJSONデータ
  • exportable - エクスポート可能かのブール値
  • keyUsage - 鍵使用目的の配列 ["sign", "verify"] 等
を指定しますが、仕様がイケてないと思うのは
  • 鍵のデータ形式など指定させる必要があるのか。公開鍵か秘密鍵のバイナリかはASN.1構造を見ればわかる話。JWKだって区別はできる。
  • アルゴリズムの指定も必要性がよくわからない。pkcs8、spkiであれば、鍵アルゴリズムが何であるかわかるし、鍵のインポートの際に署名アルゴリズムやハッシュアルゴリズムを指定させる 意味がわからない。"RSASSA-PKCS1-v1_5"にするか"RSA-PSS"にするかは、鍵インポート時に決める必要がない。署名のハッシュアルゴリズムについても同様に決める必要がないのに、"MD-5"などサポート外のアルゴリズムを指定するとエラーとなる。JWKデータも同様に鍵アルゴリズムの指定の必要がない。
  • keyUsageの指定の必要性もよくわからない。例えば、RSASSA-PKCS1-v1_5で秘密鍵をインポートしたらkeyUsageはsignに決まっており、省略できることの方が多い。
  • JSON等指定の自由度が非常に高い割に、値の指定を間違えるとすぐにエラーとなり融通が利かない。省略可能やデフォルト値を持つ引数、パラメータがあっても良さそうだが、そのようにはなっていない。
  • algorithmのhashのパラメータ値でMozillaのテストコードでは"SHA-1"となっているが、仕様上は{name: "SHA-1"}となるのが正しいようでこれならChromeでもFirefoxでも動作する。前述のように必要の無いパラメータの指定方法が原因で実装により動作するもの、しないものがあり、相互運用性の問題が生じている。
そのような意味では、jsrsasignのKEYUTIL.getKey()メソッドでは入力は極力自動解析し最小のパラメータで動作するようになっていて、秘密鍵であろうが、公開鍵であろうが、JSON形式であろうが、鍵が暗号化されていようが、鍵アルゴリズムが何であろうが、許容範囲の広い実装となっています。

Web Crypto APIの困った所6:アルゴリズムサポート状況の不明瞭

前回紹介したCan I Use CryptographyのページでChrome等いろんなブラウザが フルサポートかのような記述になっていますが、W3Cの勧告候補でも特にどのアルゴリズムは 実装必須となっているわけではないようで、ECDSA、RSA-PSSなど サポートしていないアルゴリズムはかなり多いです。(IE11+ではSHA-1未サポートのよう) この辺りについてスペックがどうなっているのかブラウザベンダーから正式な開示が 無いようですし、仕様上も何をサポートしているかを知る術が特になく、 動かしてみて動かなかったら未サポートのような状況になっています。 そもそも、Java JCEやOpenSSLなどに比べたら、 アルゴリズムの数が圧倒的に少ないのですから、全アルゴリズムを実装必須(MUST) としても良いぐらいではないかと思います。 また、Java JCEではどのようなアルゴリズムをサポートしているかを知るためのAPIがあります。 ブラウザ毎のアルゴリズムや機能のサポート状況の比較は、 そのうち表などで比較できればと思っています。

Web Crypto APIの困った所7:サポートアルゴリズムの狭さと変なバランス感

W3C Web Crypto APIでは、 MD5、RSA暗号(RSAES-PKCS1-v1_5)など現時点で脆弱とされているアルゴリズムは除外されており、 サポートされているアルゴリズムはかなり限定的で狭いものです。 後方互換性や相互運用性のために使いたいというケースもあるでしょうから、暗号ライブラリとしては、 サポートしても良いのかなと思ったりもします。 その割には、例えばFirefoxではRSA鍵の鍵長が256bit〜7168bitをサポートしており、むしろそちらの方を制限すべきなのでは?とも思ってしまいます。 また、FirefoxではECDSAをサポートしていないのに、ECDHはサポートしているなど、同じECCの鍵生成なのに、このあたりのバランス感や優先順位も奇妙に思います。

Web Crypto APIの困った所8:鍵生成のパラメータ

例えばRSA鍵生成のパラメータですが、RSA署名やRSA暗号化のアルゴリズムを 指定しなければならないのかわからず、公開指数の指定の仕方も面倒で、 何故ハッシュを指定しなければならないのかわかりません。 また、これらのパラメータは省略は一切許されていません。 間違えるとエラーとなり、どこが間違っているのかに関するエラーメッセージは どの実装も非常に不親切です。

var paramKeyGen = { name: "RSASSA-PKCS1-v1_5", // RSA-PSS, RSA-OAEPなど指定する必要なくRSAで十分では? modulusLength: 2048, publicExponent: new Uint8Array([0x01, 0x00, 0x01]), // 整数65537でいいのでは?Uint8Array面倒 hash: { name: "SHA-256" } // hashが必須な意味がわからない };

高い相互運用性のために

一連のシリーズでは、最も汎用性が高くなるように、 window.crypto、window.crypto.subtleを使っており、 FirefoxやChromeでそのまま動く動作コードになっています。 これらに加え、IEやSafariなどいろんなブラウザで動かそうとする場合には、 以下のようなコード先頭に入れ、

var WC = null; var WCS = null; if (window.msCrypto) WC = window.msCrypto; // IE11+ if (window.crypto) WC = window.crypto; // FF34+,CH37+ and others if (WC.subtle) WCS = WC.subtle; // IE11+,FF34+,CH37+ and others if (WC.webkitSubtle) WCS = WC.webkitSubtle; // Safari 7.1+
window.cryptoの代わりにWCを、 window.crypto.subtleの代わりにWCSを使えば、 どこでも動く可能性が高くなるかと思います。

おわりに

今回は、RSA鍵ペアの生成、署名生成、署名検証についてみてみました。 今日はこんなとこで。

関連記事

W3C Web Cryptography APIとの果てしなき戦い(第1回)

あけましておめでとうござます。 ウェブ関連技術の標準化を進めているW3Cから W3C Web Cryptography API という勧告候補が 出てまして、このAPIを使えば公開鍵暗号、共通鍵暗号、鍵交換、鍵生成、 暗号化、署名、ハッシュ関数、擬似乱数なんかが使えちゃうのだそう。 Twitterの私のリプライに「ほとんどのブラウザがサポートしてるから (jsrsasignでも) 使いなさいよ」と海外から何名かの方がコメントしてくださるので、 重い腰を上げて勉強してみたんですが、「ムキ〜〜っ!!わけわからん! 標準化って何なの?相互運用性著しく低いしっ。そもそも、 このAPIってJavaScriptを書くプログラマにちっとも優しくないよね!」 と怒り心頭なんですが、 年も明けたことですし、そのあたりのことを愚痴っぽく、 ぼちぼち書いてみたいと思います。

結論から言うと「今すぐ、これを使うとヤケドするので、 実装がこなれてきたり、様々なラッパーやユーティリティが整備される まで使うのは1、2年はお待ちなさい」という事かなと思います。

APIはどれくらい使えるの?

いろいろなウェブブラウザの機能のサポート状況を 公開してくれているサイトにcaniuse.comという サイトがあるのですが、そこで W3C Web Cryptography API の主要なウェブブラウザ毎のサポート状況 が公開されています。


fig1
(出典: Can I Use Web Cryptography)
グレーで囲まれているのが2015年1月時点で現行バージョンのブラウザで、 緑はフルサポート、黄緑は一部サポートを示しているんですが、 IE、Firefox、Chrome、Safari、Operaやスマホなどのほとんどブラウザで APIをサポートしていると言っています。 世界のブラウザシェアでは51.39%、日本では58.17%が APIをサポートしているのだそうです。 この図だけを見たら、問題なく簡単に使えそうな気がしちゃいますよね。

ハッシュ関数の例

それでは早速、ハッシュ関数の例を見ていきましょう。 文字列"aaa"のSHA-1ハッシュ値を計算するとします。 細かい関数は後で紹介していくとして、ハッシュ計算の流れは以下の例の ようになります。

var textBuf = asciitouint8a("aaa"); // ハッシュ対象文字列aaaをUint8Arrayに変換 window.crypto.subtle.digest({name: 'SHA-1'}, textBuf).then( // SHA1ハッシュを計算 function(hashValue) { // 成功した場合に呼び出される関数 console.log("sha1(aaa)=" + abtohex(hashValue)); // 結果ArrayBufferを16進数に }, function(error) { // 失敗した場合に呼び出される関数 console.log("sha1(aaa)エラー: " + error); } ); #文末にasciitouint8a, abtohexのコードはつけておきます。
APIのハッシュ関数、暗号化、鍵生成などの関数は、乱数生成を除いて 全て "window.crypto.subtle" の名前空間に入っており、 ハッシュ関数は "digest" となっています。

ハッシュの入力はHTML5で導入されたバイナリデータを扱うクラスUint8Arrayなど、 ハッシュの結果はArrayBufferクラスで帰ってきます。例では文字列から Uint8Array配列にしたり、ArrayBufferを16進数で表示できるようにユーティリティー関数を 使っています。

APIの殆ど全ての暗号機能は非同期実行APIになっており、 関数やメソッドが実行されても、すぐには結果が帰ってきません。 ウェブブラウザで、ある処理を行っている間、表示が固まったりしないように 非同期実行にしてあるのだそうです。(後日、突っ込みますw)

JavaScriptで非同期実行するには2通りの方法があるらしく、 windows.crypto.subtle.digest関数は非同期実行オブジェクトを返します。

  • Promiseパターンを使う方法(thenを使う。WebCryptでは主流?)
  • イベント(oncomplete,onerror)を使う方法(IE WebCryptで主流?)

Promiseパターンによる非同期実行

Promiseパターン、もしくはDeferredパターンと呼ばるデザインパターンは、 非同期処理は遅延実行を扱うパターンなのだそうです。 上の例ではdigest()関数により、非同期実行のためのPromiseオブジェクトが 生成され、処理が終わった際にどんな関数を実行するか、エラーが起きた際に どんな関数を実行するかをthen()メソッドで定義しておきます。

var promiseObj = new Promise(); promiseObj.then( 処理が終わった時に実行される関数, エラー発生時に実行される関数 );
Promiseパターンでは、非同期処理するオブジェクトをthenでチェーンすることにより、 処理1、処理2・・・のようにシーケンシャルに実行させることも可能です。
var promiseObj1 = new Promise(); promiseObj1 .then( function(result) { // 処理1が成功したら実行される関数 ... return new Promise(); // 新しい処理2の非同期処理オブジェクト } function(err) {...} ).then( function(result) { // 処理2が成功したら実行される関数 ... return new Promise(); // 新しい処理3の非同期処理オブジェクト } function(err) {...} ).then( // 処理3 );
Promiseパターンの方が、後述のイベントによる非同期実行よりも、 thenチェーンなど使えて綺麗に書けるので Web Crypto APIでは、 こちらの方が主流の使い方となるのだと思います。 Promiseパターンについて詳しく知りたい場合には、以下を参考に されると良いと思います。

イベントによる非同期実行

IEのWeb Crypto APIのサンプルでは、digest()で非同期実行オブジェクトを 生成するところまでは一緒ですが、これに処理が完了した場合に実行される 関数を参照するイベントプロパティ oncomplete と、エラーが発生した際に 実行される関数を参照するイベントプロパティ onerror を定義することにより ハッシュ計算を非同期実行しています。

var data = new Uint8Array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]); var digestValue; var crypto = window.msCrypto; if (crypto.subtle) { var op = crypto.subtle.digest({ name: "SHA-256" }, data); op.onerror = function (evt) { console.log("op.onerror event handler fired."); } op.oncomplete = function (evt) { digestValue = evt.target.result; console.log("op.oncomplete event handler fired, digest computation complete."); }; } else { console.log("Unable to create window.crypto object."); } (出典:MSDN: Web Cryptography: digest method)
IE 11+のW3C Web Cryptography APIでは、Promiseパターンではなく、 イベントによる方法しかサポートされていないのだと思います。

Web Crypto APIの困った所1:非同期実行

ここから、なんだか愚痴や文句っぽくなってきますorz W3C Web Cryptography APIの困ったなぁと思うのは、 (処理が大して重くないのに)非同期実行しかサポートされていないところです。 非同期実行は、それを使い始めてしまうと以降の全ての処理を非同期実行 しなければならず、途中の一部だけを非同期実行することができません。
fig2

非同期実行は鍵生成して、鍵を保管して、署名するといった一連の処理を まとめて非同期実行させて、表示が固まらないようにしたいのであり、 個々の暗号プリミティブレベルで非同期実行したいわけではないと思います。 暗号プリミティブを非同期実行するかはプログラマに任せてほしく、 暗号プリミティブは同期実行の方がよかったのではないかと思います。

IEと他のブラウザとで非同期実行の記述に一貫性が無いのも面倒だと思います。

Web Crypto APIの困った所2:名前空間が統一されてない

Can I useサイトやMSDNの例を見て「あれ?」と思った人もいると 思いますが、Web Crypto APIの名前空間って実装によって統一されている というわけではないんです。

  • window.crypto.subtle - Firefox、Chromeなど
  • window.msCrypt.subtle - IE11+
  • window.crypto.webkitSubtle - Safari, iOS Safari
実装の中身がブラウザによっていろいろ違うので、 名前で分かれていてもいいのかなとも思いますが、 そもそもW3Cで「標準化」してるんですよねぇ?と。 酷い話だなぁと思います。

Web Crypto APIの困った所3:ArrayBufferによるインタフェース

ハッシュや署名の入力はUint8ArrayといったArrayBufferViewのサブクラスでなければ いけなかったり、出力はArrayBufferになっていたりと、 文末のasciitouint8aやabtohexなど見てもらえれば判る通り、 処理するのが結構面倒くさいです。 ファイルなど大きなサイズのデータを扱う際にはわかりますが、 ハッシュの結果となるハッシュ値なんか大した大きさでないのに ArrayBufferで戻されます。

Web Crypto APIの困った所4:多態性のなさ

ハッシュ関数の入力に関して、文字列でも、整数は配列でも、Uint8Arrayでも 「ひとつよしなに」ハッシュを計算してくれてもよさそうなもんですが、 そうした 多態性(polymorphism)は無くて、 入力はUint8ArrayのようなArrayBufferViewでなければなりません。 多態性はJavaScriptの大きなメリットの一つだと思うんですが、 W3C Web Cryptography APIを使っていると、JavaScriptから Cで関数を呼び出ししているような気分になります。 (入力の融通のきかなさは次回他にも紹介します。)

Web Crypto APIの困った所5:ドキュメントやサンプルの少なさ

解説記事やサンプルを紹介しているサイトもあるのですが、そもそも、例が少ないですし、 それが希望のブラウザで動作するかわからず、コードの相互運用性に本当に苦労させられます。 また、ブラウザがどの機能をちゃんとサポートしているのか、よくわからず、公開ドキュメントも ないので、それが自分のコードのバグなのか仕様なのかよくわからない事も問題です。

おわりに

というわけで、今回は導入として、W3C Web Cryptography APIの雰囲気とハッシュ関数の例に ついて見てみました。 不勉強なため、認識違いをしている所もあると思いますが、その際にはコメント等でご指摘いただければ と思います。 次回以降、鍵生成、署名、共通鍵暗号等を紹介したり、 勉強になるサイトを紹介していこうと思います。 ではでは。

関連記事

(おまけ)例で使ったabtohex、asciitouint8aの定義

// ASCII文字列をUint8Arrayの配列に変換 function asciitouint8a(s) { var buf = new Uint8Array(s.length); for (var i = 0; i < s.length; i++) { buf[i] = s.charCodeAt(i); } return buf; } // ArrayBufferから16進数文字列に変換 function abtohex(arrayBuffer) { var s = ""; var dataView = new DataView(arrayBuffer); for (var i = 0; i < arrayBuffer.byteLength; i++) { var hex = dataView.getUint8(i).toString(16); hex = "00".substr(0, 2 - hex.length) + hex; s += hex; } return s; }

2007年のW3Cワークショップでの紹介

(application/pdf オブジェクト)
W3C Workshop Position Paper:The importance of incorporating XAdES extensions into ongoing
XML-Sig workより

Today XAdES has gained a high degree of acknowledgment in Europe and is also penetrating in
Japan. Several interoperability test events have been organized by ETSI in Europe and by ECOM in Japan. Some European countries mandate signing certain electronic documents with XAdES signatures (e-Invoices, accounting documents, etc).


W3Cの一昨年行われたワークショップ Next Steps for XML Signature and XML Encryption (25/26 September 2007 -- Mountain View, California hosted by VeriSign) で、Juan Carlos Cruellas教授が"The importance of incorporating XAdES extensions into ongoing XML-Sig work"というタイトルでプレゼンをして、日本のECOMの話も触れて頂いていたようです、、、、ありがとうございますm(_ _)m

今日はちょっと早くでないといけません、、、、眠い、、、、


W3C Test Cases for C14N 1.1 and XMLDSig Interoperability

先日、XML署名の改訂の話を書きましたがあわせて

W3C Test Cases for C14N 1.1 and XMLDSig Interoperability
http://www.w3.org/TR/2008/NOTE-xmldsig2ed-tests-20080610/

というドキュメントも出ています。STF351でもお世話になっている
Juan Carlos Cruellas先生とKonrad Lanzさんがまとめています。

これも見ておかないといけません。

XAdESのテストケースの設計で少し議論になったのですが、
XAdESの仕様はXML署名やタイムスタンプやX.509証明書、属性証明書等の
仕様も含んでいるでのテストテストしてこれらを包括した
テストをやりたいと申し入れたんですが、これがなかなか理解してもらえませんでした。

この記事に書かれているようなXML署名やX.509(NIST PKITS)などがあるので
個別にやればよいし、ワークも限られているので現時点では余分なものを入れるべきでない
という結論になりました。

個人的にはそうは思っていなくて、XAdESのあまり使われもしない
プロパティについて細かくテストするよりは、XAdESのそもそもの目的は
証拠情報として信頼するに足りる署名を提供することなので

・XML署名をきちんと検証できること(Enveloped,Enveloping,Detached署名,XPath)
・時間の順序関係を正しく扱えていること
・与えられた証明書の検証情報を用いて適切な時点での証明書有効性を検証すること
・将来的な暗号アルゴリズムの危殆化に備えた移行をスムースにできるようなテストを提供すること
・タイムスタンプ自体の検証

は、とても重要だと思っていますし、ECOMで設計したテストケースでは
そのように配慮していますが、これがなかなか理解してもらえない、、、、(ノд・。)

結合テストは上位レイヤーであるXAdESでしかテストできないんですけどね、、、、、

どうも、XML署名、XAdES関連の方と話をしていると、もちろん詳しい方もいらっしゃいますが、一部では他に気にしなければならないことがたくさんあるためか証明書やタイムスタンプの検証について重要視されない傾向があるような気がしています、、、、

W3C XML Signatureの改訂

2007年9月に開催されたW3CのXML署名に関するワークショップでの議論の結果を受けて、XML署名の仕様のを行いましょうということになったそうでW3Cから幾つかニュースリリースが出ています。ワークショップでの改定ポイントの議論の結果はこんな感じになったそうです。

とりあえず、ということで2008年6月10日「XML Signature Syntax and Processing (Second Edition)」ということでW3C勧告が改訂されたようです。

2008.03.26 Call for Review: XML Signature Syntax and Processing (Second Edition) Proposed Edited Recommendation
2008.05.21 XML Security Working Group to Take Next Steps on XML Signature, Encryption
2008.06.10 XML Signature Syntax and Processing (Second Edition) Is a W3C Recommendation

さらに今後の方向性として2008年6月に、XML署名とXML暗号化およびそれらの関連仕様の改訂に向けた新しいワーキンググループXML Security Workingが発足し、第一回目のキックオフミーティングが来週2008年7月16日-17日にスペイン・バルセロナのカタルーニャ工科大で実施されホストをXAdESのエディタでもあるJuan Carlos Cruellasさんがつとめられます。

XML署名がトランザクションやSAMLなどの認証の用途に使われる場合には、まぁ、どんな風になっていても問題ないんですが、XAdESなどの長期署名の目的で利用する場合には仕様の後方互換性が非常に問題になると思っています。

その一つが名前空間の問題で、XML署名、XAdES署名の要素には必ず名前空間が付き、仕様のバージョンなどの情報を含んでいます。今回のSecond Editionではスキーマの改訂はされなかったため問題ありませんが、2〜3年毎に改訂されるXAdESのバージョン毎にスキーマと名前空間が異なるといった問題があります。

要素の生成・検証要件が変更になっていないのに全体の名前空間が変更になってしまうのはとても困った問題で、実際多くの実装が古いバージョンのXAdES署名を扱えないようで、この先10年、20年といった長いスパンで過去の署名の検証に対応するのが難しいと思っています。

今後のXML署名の動向は、きちんとウォッチしていかないといけません。

<追記・紹介記事>
http://mediajam.info/topic/517724
最新記事
Categories
Archives
Twitter
記事Google検索

本ブログ内をGoogle検索
Yahoo!アクセス解析
Travel Advisor
記事検索
QRコード
QRコード
  • ライブドアブログ