自堕落な技術者の日記

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

RSA

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鍵ペアの生成、署名生成、署名検証についてみてみました。 今日はこんなとこで。

関連記事

(小ネタ)HeartBleed影響で証明書の再発行とそれに使うRSA鍵の事前チェック

OpenSSL HeartBleed問題で証明書の再発行やら説明やらなんやらに追われてたわけですが、再発行に際してちょっと鍵も事前チェックしてみるかなぁ、、、と。

Debianで生成するRSA鍵の乱数が固定の値になってしまい生成される全てのケースの秘密鍵がわかってブラックリスト化されたり[0]、因数分解の片方がわかっていれば秘密鍵は簡単に復元できちゃう問題とかあったり[1][2][3]で、鍵がそのような弱い鍵でないか調べるサイトとして米ミシガン大学のチームのfactorable.netというサイトがあり、PEM形式の証明書を貼れば調べることができます。

でも、これって発行済の(他のサイトの)証明書を調べるツールなわけで、自分の証明書を発行してもらってから鍵がダメだったってわかっても、再発行にはお金もかかるし、なんで公開鍵や証明書発行要求(CSR,PKCS#10)でチェックしてくれないのかなぁと思ったわけす。証明書発行要求時点でわかれば鍵ペア生成し直せばいいだけですから。

factorable.netの管理している方へ「証明書発行要求に対応してくんない?事前にチェックできた方が便利だと思うんだよねぇ。」みたいなメールを送ってもみたんですが「ナイスな改善提案ありがと。でも他にもToDoがあっていつ直せるかっていうのは約束できない。対応したら連絡するね。」という旨のメールが来てやんわり断られました。とほほ。

OpenSSLで鍵生成したならこんな感じで秘密鍵から自己署名証明書をとりあえず作ってチェックすればいいんだけど、

% openssl req -new -key aaa.prv -x509 -subj /C=JP/O=TEST1 -set_serial 1 -days 3652 -out aaa.cer
#できたaaa.cerをfactorable.netでチェックすればよいです。
WindowsやHSMなんか使っている場合には、やはり証明書発行要求で事前チェックをするのが王道かなと。

仕方ないので簡単なツールを作りました。

jsrsasign: CSR to certificate converter:
http://kjur.github.io/jsrsasign/tool_forfact.html
jsrsasignを使って証明書発行要求から公開鍵をひっこ抜いて、適当な公開鍵証明書の形にでっちあげて、証明書の署名値はいいかげんな値に設定したニセの証明書を作ってしまえば、factorable.net で公開鍵が弱くないかチェックできるようになります。

これから発行してもらおうとしている証明書発行要求をツールに貼付けて「Convert」ボタンを押せばニセ証明書ができます。 (署名値は0x0102030405060708です。適当すぎますか?(^^;)
CSR-Self Cert Converter
この生成されたニセ証明書をfactorable.netのチェッカーにかければ、証明書発行要求とそれに紐づく鍵が弱い鍵でないかチェックすることができます。めでたしめでたし。
factorable.net key checker

factorable.net keychecker result

というわけでひっそりとjsrsasign 4.2.2を昨晩リリースしました。AuthorityKeyIdentifier証明書拡張に対応してくんない?というリクエストもあったので追加しときました。

今日はこの辺で

(小ネタ)JNSA電子署名WGの勉強会でお話してきました

JNSA電子署名ワーキンググループのスキルアップタスクフォースの勉強会で 拙作のJavaScript暗号ライブラリjsrsasignとJOSE JWS JWTライブラリ jsjwsのお話をさせて頂く機会を頂きました。スライドを公開しました。 これらのライブラリで日本語版の資料は作ったことがなかったので、 よかったら参考にしてください。

(続)RSAとECDSA、署名生成と署名検証どっちが速い?

前回の記事では、署名生成と署名検証とか、RSAとECDSAとかどっちが速いのかOpenSSLやJava JCEを使って速度の比較をしました。一度作った署名を何回か検証に使うというケースもあるので、検証にかかる時間はとても重要だと思います。今日は、前回の比較をさらに掘り下げてみたいと思います。

・署名検証の速度は(今我々が普通に使う鍵長では)RSAの方が断然速い
・しかしながらECCは鍵長が長くても遅くならないという特徴があるのでいつか逆転するはず

鍵長が長いとRSA不利、ECDSA有利になってくるのでいつか速度の逆転が起きるのだろうと思います。では、それが鍵長としていつなのかを調べたいと思います。

NISTの暗号強度の比較表を再度引用します。

共通鍵暗号
相当
RSAECDSA
80 1024 160-223
1122048 224-255
1283072 256-383
1927680 384-511
25615360512-

まずは、同じ暗号強度のECC、RSAの鍵長に対して秒間署名検証回数をプロットしてみましょう。
cmp6-verify2
ご覧の通り共通鍵暗号で200bit相当、RSAなら9000bit、ECCなら200bit程度の所で署名検証の のスピードが逆転しているように見えます。我々が現在利用することの多い2048〜4096bit程度の RSAの鍵ならまだまだ十分高速であることは言えるのではないかと思います。

上記のグラフが指数関数的なので今度は秒間署名回数の対数を取ってプロットしてみましょう。
cmp7-verify3log
グラフから、暗号強度に対してはほぼリニアに秒間署名速度の対数が推移し、 共通鍵暗号相当の暗号強度の軸であるx座標が212.5423729(bit)の時に、 RSAとECCの署名検証の速度が逆転しています。

それでは、共通鍵暗号の強度で213bitということはRSAやECCではどの程度の鍵長に 相当するのでしょうか。 共通鍵暗号暗号強度と同等のECC、RSAの鍵長は最初のNISTの表から これも指数関数的なのでRSAの鍵長と、ECCの鍵長の対数を使ってプロットしてみます。
cmp8-strength
すると、ご覧のように取った対数に対してほぼリニアに推移することがわかります。 共通鍵暗号の暗号強度で213bitとする点はRSAだとy軸が3.965、ECCだと2.612となり、 これらは指数に戻して

RSAとECDSAと同じ暗号強度で署名検証速度が逆転するのは
・RSAだと9234bit のとき
・ECCだと409bitのとき
ということのようです。この値に近づいてるようならECDSAへの移行を考えた方が よいということなんでしょうね。

今晩はこの辺で

あ、そうそう。これ書いている途中でizuさんのとてもためになる関連記事を発見してしまいました。杜撰な研究者の日記「RSA暗号の強度 (2009.11.19)」

RSAとECDSA、署名生成と署名検証どっちが速い?

2013年9月4日に開催されたOpenID Tech Night Vol.10 に参加してて、

大きなプロバイダではRSA署名の検証は結構大変です。 RSAは署名よりも検証の方が計算コストがかかるので...

みたいな話をされ、「んん?逆じゃないの。RSA署名の検証は署名生成よりも圧倒的に計算コスト低いよね。」とか 思ってたわけで「がが〜〜ん」と。 「まぁ、ちょっと調べてみるべぇ。」と手持ちのノートPCで調べてみました。

検証内容

以下のことを検証してみたいと思います。

  • 署名の生成と検証ではどちらが速いのか。RSAとECC(ECDSA)では違いがあるのか。
  • 同程度の暗号強度のRSA署名とECDSA署名ではどれくらい速度差があるのか。
  • RSA署名では鍵長が変わったとき、どれくらい速度差があるのか。
  • ECDSA署名では楕円曲線や鍵長が変わったとき、どれくらい速度差があるのか。
  • Ruby+OpenSSLとJava JCEではどれくらい速度差があるのか。
なんか、楕円「マンセー」みたいな人もおられますが、 「楕円は鍵長が短いから圧倒的に速い」みたいな事を言い出す人もいて 「ホンマかいな?」と調べてみたかったわけです。 楕円って期待するほどそんなに速くないですよね? むしろ遅いですよね。

検証方法・検証環境

言語の偏りがあるのも良くないのでOpenSSLベースのものとJava JCEベースのものと調べます。 いちいちOpenSSLをCで書くのも面倒なので。Rubyを使いました。 処理時間の測定方法については、Ruby+OpenSSL、Java JCEで次のような観点で測定しています。

共通
  • 鍵や証明書のロードの時間は処理時間に含めない。
  • 測定条件統一のためハッシュ計算の時間は処理時間に含める。
  • 同一のマシンで測定する。
  • 再利用しない同一の鍵で2000回の署名生成、署名検証の時間を計測。
  • 公開鍵暗号のパフォーマンスを知りたいだけなので署名対象は"aaa"の短い文字列。
  • SHA1withRSAもしくはSHA1withECDSAで比較する。
Ruby+OpenSSL
  • Ruby標準の'benchmark'モジュールを用い、リハーサルも行う。benchmarkのrealの時間を用いる。
Java JCE
  • イテレーションループの前後でのSystem.currentTimeMillis()の値の差を処理時間とする。
細かい検証環境情報は以下の通りとなります。
検証環境
マシンLenovo X201s
CPUIntel Core i7 L620 2.00GHz
メモリ8GB
OSMicrosoft Windows 7 Professional 32bit SP1
Java
バージョンOracle Java 1.7.0 build 1.7.0-b147
RSA署名JCEプロバイダSunRsaSign 1.7 Provider
ECDSA署名JCEプロバイダSunEC 1.7 Provider
Ruby (+ OpenSSL)
バージョンcygwin C Ruby 1.9.3p194
OpenSSLOpenSSL 1.0.1c

Ruby + OpenSSLで署名

Ruby + OpenSSLでRSAやECDSA署名するには、OpenSSLコマンドで普通に PKCS#5の秘密鍵と公開鍵を準備してこんな感じで署名生成、署名検証すればヨロシ。

# ECDSAの署名生成
prvKey = OpenSSL::PKey::EC.new(File.read(PKCS#5秘密鍵PEM))
hashed = OpenSSL::Digest::SHA1.digest(署名対象メッセージ)
sigVal = prvKey.dsa_sign_asn1(hashed)

# ECDSAの署名検証
pubKey = OpenSSL::PKey::EC.new(File.read(PKCS#5公開鍵PEM))
hashed = OpenSSL::Digest::SHA1.digest(data)
isValid = pubKey.dsa_verify_asn1(hashed, sigVal)

# RSAの署名生成
prvKey = OpenSSL::PKey::RSA.new(File.read(PKCS#5秘密鍵PEM))
sigVal = prvKey.sign("sha1", data)   

# RSAの署名検証
pubKey = OpenSSL::PKey::RSA.new(File.read(PKCS#5公開鍵PEM))
isValid = pubKey.verify("sha1", sigVal, data)   
ECDSAの時はハッシュを自分で計算するのと、 ECDSAの署名値をASN.1構造で表現することに気をつければ問題ないかと思います。

Java JCEで署名

最近、キーストア使って楽してたので普通に鍵ファイルを読みたい場合には、 PKCS#8 DERじゃないといけないんだよなとか探してみると 自分の記事 「OpenSSLで鍵生成した秘密鍵をJavaで使う」が見つかって助かりました。 秘密鍵ファイルはPKCS#8にしといて、公開鍵もPKCS#8にしようとしたら 「読めな〜〜〜い!!」鍵はパラメータの数値(BigInteger)を指定するか 証明書じゃないといけないそうだ。仕方ないから無理やり自己署名証明書を作りました。

ECDSAの署名生成はこんな感じ。

KeySpec keySpec = new PKCS8EncodedKeySpec(PKCS#8秘密鍵DERのデータbyte配列);
KeyFactory kf = KeyFactory.getInstance("EC");
PrivateKey prvKey = kf.generatePrivate(keySpec);
Signature sig = Signature.getInstance("SHA1withECDSA");
sig.initSign(prvKey);
sig.update(署名対象データaaa);
sigVal = sig.sign();

RSAの署名生成はこんな感じ。

KeySpec keySpec = new PKCS8EncodedKeySpec(PKCS#8秘密鍵DERのデータbyte配列);
KeyFactory kf = KeyFactory.getInstance("RSA");
PrivateKey prvKey = kf.generatePrivate(keySpec);
Signature sig = Signature.getInstance("SHA1withRSA");
sig.initSign(prvKey);
sig.update(署名対象データaaa);
sigVal = sig.sign();

RSAやECDSAの署名検証はこんな感じ。

CertificateFactory cf = CertificateFactory.getInstance("X.509");
X509Certificate cer = (X509Certificate)cf.generateCertificate(new FileInputStream(公開鍵証明書));
pubKey = cer.getPublicKey();
Signature sig = Signature.getInstance("SHA1withECDSA"); // RSAならSHA1withRSA
sig.initVerify(pubKey);
sig.update(署名対象データ);
isValid = sig.verify(sigVal);

最初、BouncyCastle使えばいいかとも思ったんですが、 Java SE 7から楕円用のプロバイダSunECが標準提供されるようになったので、 Java SE 7の標準バンドルされたプロバイダを使うことにしました。 サポートしている楕円曲線はどうだっけと思ったら 前に自分で調べてあったのでそれを参考にしました。 ( 祝 Java SE 7 リリース記念「JCEはどうなってんの?」)

RSAとECCの暗号強度対応

一般に、

  • ECC 160bit は RSA 1024bitに相当する、とか
  • ECC 256bit は RSA 3072bitに相当する、とか
言われていますが、調べて見ると、 NIST SP800-57 Recommendation for Key Management - Part1: Generalの 5.6.1節 Comparable Algorithm Strengthで対象暗号、RSA、DSA、ECC(ECDSA)の鍵長と 暗号強度の対応の表があり、 RFC 5656 Elliptic Curve Algorithm Integration in the Secure Shell Transport Layerでも引用してます。(こっちの方が見やすい)

表を引用しておきましよう。

共通鍵暗号DSARSAECDSA
80 L=1024,N=160 1024 160-223
112L=2048,N=256 2048 224-255
128L=3072,N=256 3072 256-383
192L=7680,N=384 7680 384-511
256L=15360,N=51215360512-

(結果1)RSAの署名生成と署名検証はどっちが速いか

まずはRSA鍵での署名と検証がどれくらい違うのか見てみましょう。


cmp1-rsa-sign-verify

署名と生成では、署名の方が圧倒的に時間がかかることがわかります。 また、鍵長が長くなるほど指数関数的に時間がかかることがわかります。 署名生成に関しては特にJava JCEの遅さが顕著です。

(結果2)ECDSAの署名生成と署名検証はどっちが速いか

グラフからECDSAではRSAとは逆に署名検証より署名生成の方が わずかながら速いですが、あまり変わらないということがわかります。 また、同じ鍵長のsecp160r1, secp160r2, secp160k1とでは ほとんど処理速度には違いはなく、鍵長が長くなると処理時間は増えますが、 RSAが非常に鍵長の影響を受けるのに対し、ECDSAでは あまり鍵長が長くなっても処理時間が長くはならない事がわかります。


cmp2-ecdsa-sign-verify

(結果3)同じ暗号強度ではRSAとECDSAとどちらが速いか

ECC 160bit とRSA 1024bitはほぼ同等の暗号強度です。 ECDSAと比較して、RSAは署名生成はとても遅いが、署名検証は とても速いことがわかります。


cmp3-ecc160rsa

鍵長が長いケース、ECC 256bit と同等なRSA 3072bit を比較してみると、 順序関係は変わりませんが、鍵長が長くなった分、 非常にRSA署名の生成時間が長くなっています。 これに対し、ECDSAではRSAほどは鍵長が長くなった影響を受けていません。


cmp4-ecc256rsa

まとめ

簡単にまとめると時間がかかる順に

  • RSA署名の生成には非常に時間がかかる
  • ECDSAの署名検証は署名生成よりほんの少し長く時間がかかる
  • ECDSAの署名生成は普通に時間がかかる
  • RSA署名の検証は非常に短時間であるECDSAの署名生成は普通に時間がかかる
  • RSAでは鍵長が長くなるほどその傾向が顕著になる。
  • ECDSAはRSAほどは鍵長が長くなる影響を受けない。
といった感じでしょうか。認識してたとおりで良かったなぁと思いました。今日はこの辺で。

RSAサイトの攻撃によるワンタイムパスワードSecurIDの影響を想像してみる

セキュリティ関係に興味がある方は御存じかと思いますがRSA SecurIDは世界で最も使用されていると思う(The Registerの記事では4000万ユーザとか?)ワンタイムパスワードトークンという道具で、大きな企業のダイアルアップやVPNなどのログインの際に、IDパスワードの他に60秒おきに表示される6桁の数字(ワンタイムパスワード)を入力することによりリモートログインのセキュリティ強度を強化するものです。つまりワンタイムパスワードを生成する道具をもってないと結局ログインができない事になります。

ここ数日Twitterのタイムラインなどで「RSAのウェブサイトが攻撃されSecurIDが危うい」、「SecurID 40万(?)アカウントが盗まれた」「RSA SecurIDがAPT攻撃によって脆弱になった」みたいなつぶやきが流れたりブログで紹介されたりしました。かな〜り誤解してるんじゃないなかなぁと想像される節もあるので、ブログで書いてみたいと思います。

RSA SecurIDの仕組み

近年このようなワンタイムパスワード認証の仕組みを オープン標準化しようという動きがあり Initiative for Open Authentication(OATH)という認証ベンダーによる 業界団体で標準の策定がなされておりInformational RFCにもなっています。

RSAもそのメンバーになっています。仕様にはサンプルコードもついていますしアルゴリズムはすぐわかると思います。私も以前にOATH仕様のワンタイムパスワード生成アプリをiアプリ、.NET、Javaで作ってみました。

RSA SecurIDのワンタイムパスワード生成アルゴリズムはRSA社独自のプロプライエタリなものですが、OATHの仕様を見てみれば大枠は同じ仕組みだと思うので説明してみたいと思います。

ワンタイムパスワードの仕組みは認証サーバーと利用者に配るたくさんのトークンで構成されます。認証するためには双方が連携する必要がありますけど、物理的に分離されておりケーブルで繋いだり無線で通信するわけでもないので連携の工夫が必要です。その工夫の方式に大きく2種類あります。

  • 時刻同期方式:認証サーバーと利用者に配るトークンがそれぞれ持っている時刻により同期する方式
  • イベント同期方式:認証サーバーと利用者に配るトークンがそれぞれ持っているボタンを押した回数(イベント)により同期する方式

otp1

御存じの通りRSA SecurID はボタンを押す必要のない便利なもので、1分おきにワンタイムパスワードとなる6桁の数字の表示が自動的に切り替わる時刻同期方式によるものです。トークンが持っている時刻などを元に独自のアルゴリズムでワンタイムパスワードの値を計算し表示します。
otp2

あるアルゴリズムで利用者が所持するトークン毎に想定されるワンタイムパスワードの値を計算するわけですが、トークンを区別し、また偽造されることの無いように同期用の値(時刻またはイベント)と共通鍵(Shared Secret)を元にワンタイムパスワードを生成していると思います。

今回うけた攻撃は何なのか?

RSAのプレジデントのアートコビエロ氏のニュースリリースで攻撃を受けた事実をアナウンスしています。 RSAのサイトが受けた攻撃はAPT(Advanced Persistent Threat)攻撃だと書いていますがMcAfee Blogの解説IT用語辞典に にある通り、特別な攻撃手法ではなく「RSAに対象を特定して執拗なスパイ行為、攻撃行為」をしたと 言っているにすぎません。

RSA SecurID用の認証サーバー(RSA Authentication Manager)が攻撃を受けて、何らかの情報が漏えいした といった話ではないです。

RSAサイトの攻撃により漏えいしたものはRSA社の機密情報全般なんだと思いますが、RSA SecurIDに関して 漏えいして困るものは以下の2種類だと思います。

  • [情報1] RSA SecurID用のトークンと認証サーバー(RSA Authentication Manager/旧ACE Server)の設計情報、ソースコード、独自のワンタイムパスワード生成アルゴリズム、時刻同期の方式、認証サーバーに登録するトークンリスト情報(トークン毎の共通鍵を含む)の処理方法などの設計情報一般
  • [情報2] 世界中に導入されたお客様の認証サーバーにインポートするRSA SecurIDの認証サーバーに登録するトークンリスト情報(トークン毎の共通鍵を含む)

RSA SecurIDに関する情報が漏えいしたとして影響はどうか

漏えいした情報が[情報1]か[情報2]なのかによって影響は異なります。

漏えいしたのが[情報1]の設計情報であったとした場合、顧客が持っているSecurIDによる認証がすぐ脆弱になるということは無いと思います。顧客システムの管理者が持っているトークンリスト情報から別の偽トークンや偽サーバを作られという可能性は全くないわけではありませんが、コストはかかっても新しいトークンの発行により回避できますし、他のOATHベースのオープン仕様のトークンでも状況は変わらず、インポート用のパスワードやリストの仕組み知っていれば偽トークンを作れる可能性はあります。漏えいにより製品設計上の欠陥は見つかるかもしれませんが、システムのバージョンアップにより解決できる問題だと思いSecurIDそのものが危機的な状態にあるとは思えません。

RSA SecurIDのワンタイムパスワードの生成アルゴリズムはプロプライエタリで3DESベースのものだったと 記憶していますが、OATHではアルゴリズムあオープンですし、それ自体が漏えいし公開されてしまった事で SecurIDが脆弱になることは無いと思います。むしろ、初期セットアップ時の共通鍵を共有する仕組みの方が 重要でしょう。共通鍵共有の仕組みが漏れたとしても共通鍵が漏れないかぎり、またアルゴリズムの欠陥が見つからない限り 使われた共通鍵暗号やHMACと同等の暗号強度は失われることはありません。

ただ、方式が時刻同期方式であるために、偽トークンを作った場合に初回同期がしやすいという仕様上の欠点はあると思います。イベント同期方式ならば認証サーバーで保持しているトークン毎のカウンタ値は知りようもないので、数回の認証の失敗の後アカウントがロックされます。時刻同期の場合には現在の時刻をもとにサーバーで持っている時刻値を想定できますから、その意味では問題があります。

漏えいした情報が[情報2]の場合、これは顧客のための機密情報が漏えいしたことになるので、社会的責任は重大ですが、[情報1]、[情報2]とセットで流出していたとしても、RSA SecurIDは基本は二要素認証製品なので、一要素目の認証であるIDパスワードで保護されていますし、どのRSA SecurIDのトークンをどのIDのユーザが 使っているのかがわからなければ、認証に使うことはできないので影響は限定的なものになるでしょう。

コビエロ氏のニュースリリースは何が漏えいしたのか、影響範囲はどうなのか何も説明するものではない ため利用者や導入を検討している人、私のようなニュースリリースを見てブログを書いている人は 混乱していると思います。

幾つか関連したブログやニュース読んだ中では The RegisterのRSA breach leaks data for hacking SecurID tokens の記事が比較的正確に詳細に事実を述べており、RSAの情報公開が十分でないことを 非難するような内容となっています。

RSA SecurIDの利用顧客側の管理担当者には事態の説明のための ビジネスレターが届いてるんでしょうか。 セキュリティ技術者やRSA SecurIDの末端の利用者も気になるところだと思うので 早急に情報公開と対策がされると良いなぁと思っています。

このブログの記事はRSAの社員でも関係者でも利用者でもない私が書いています。 事実に間違いなどあればご指摘いただければありがたいです。

う〜む、天気の良いあたたかい土曜の朝に何やってんだろ、、、オレ、、、、
ではでは

図説RSA署名の巻

RSA鍵による署名って、フツーはJava JCEでもCryptoAPIでも.NETでもOpenSSLでも、一つのオペレーションになっていて、中でどう処理されているから知る必要もないんですが、やろうと思えばハッシュとRSA鍵による暗号化・復号の暗号プリミティブで実装することはできます。

今回はRSA署名の中身を図説したものって、なかなか良い物が無かったので、ちょっと書いてみて、関連した相互運用上の問題なんかを紹介します。

RSA署名とは何?よ〜し父さん図説しちゃうぞ



RSA署名って、

文書のハッシュ取って秘密鍵で暗号化するんだよね


って簡単に解説しているものがあったりしますが、その説明って、なんか合っているようで合っていないというか、うそ臭いというか、「本当はどうなの?」っていうはなしが抜けているようで、これまでスッキリしませんでした。多分、それはパディングの話が無いからなんじゃないかと思うんです。

RSA署名の方式はPKCS#1 v2.1の中で定められているんですが、一般的な公開鍵証明書やCMS署名なんかは、その中で定められた "RSASSA-PKCS1-v1_5" というアルゴリズムを使っています。

RSASSA-PKCS1_v1_5アルゴリズムにかなり忠実に図説してみたのがコレ:

図1



左から右(→)が「署名の生成」で、右から左(←)が「署名の検証」です。

RSASSA-PKCS1-v1_5 のオペレーションは大きく、

・ハッシュの計算
・パディング処理
・公開鍵暗号

の3つに分かれます。DigestInfoを作るとこも含めてパディング処理と読んだりすることもあり、この図の方が間違っているかもしれませんが、一般的なプログラミング上のパディング処理を知っている方なら、DigestInfoを作った後からをパディング処理と呼んだ方がスッキリするようなが気がします。

DigestInfoと相互運用性



署名では、署名対象データのハッシュ値とハッシュアルゴリズムを格納するためにDigestInfoというASN.1構造を使います。

RSA PKCS#1 v2.1より
DigestInfo ::= SEQUENCE {
digestAlgorithm DigestAlgorithm,
digest OCTET STRING
}

DigestAlgorithm ::= AlgorithmIdentifier { {PKCS1-v1-5DigestAlgorithms} }

PKCS1-v1-5DigestAlgorithms ALGORITHM-IDENTIFIER ::= {
{ OID id-md2 PARAMETERS NULL }|
{ OID id-md5 PARAMETERS NULL }|
{ OID id-sha1 PARAMETERS NULL }|
{ OID id-sha256 PARAMETERS NULL }|
{ OID id-sha384 PARAMETERS NULL }|
{ OID id-sha512 PARAMETERS NULL }
}


署名生成時のアルゴリズムパラメータNULL



DigestInfoでハッシュアルゴリズムを指定する際に、アルゴリズムパラメータを指定できるんですが、SHA1、SHA2シリーズの場合にはNULLを指定します。このNULLを入れる入れないで、相互運用上の問題が起きたりします。

この辺りは昔、RSAとNISTの間で紆余曲折あったそうなんですが、解決のためにRSAはv2.1の訂正(PKCS #1 v2.1 Errat)を2005年12月に出しています。結論から言うと

・署名生成時:NULLを含めるものとする(SHALL)
・署名検証時:NULLがあってもなくても検証できるものとする(SHALL)

生成については、これまで自分が触ることができた10ぐらいの署名実装で一つを除き問題なくNULLが含まれていましたし、手当たり次第調べた証明書、CRLなんかも全てNULLが含まれた形になっています。残りの一つについても、パッチが出ているそうなので早く修正されるといいなぁ、、、と思います。(とある製品が国内2つの実サービスで使われているんですが、もう長いことパッチ未適用になってます。残念。)

検証については、目にした殆どの実装がNULLがあっても無くても検証できるようになっているんですが、入手できたうちの20%ぐらいの実装はNULLが無いと検証失敗します。

この辺りは、ハッシュとRSA暗号のプリミティブを使ってNULLの入ってない署名を作ったり、生の署名値、証明書、CMS署名、XML署名などで署名値のDigestInfoにNULLが入っているのかどうかをチェックするツールを作って調べました。

なんか、今回も細かい話ですみませんね。ではでは。

RSA BSAFE Share for Java 1.1のリリース

フリーのJavaの暗号ライブラリRSA BSAFE Shareがバージョンアップされたそうです。

変更点は以下のようです。

- EC DRBGアルゴリズムの乱数生成器
- 乱数生成器の場合にHSMが使える
- JCE APIを使ったCSRの生成 (PKCS#10, Certificate Request Message Format)
- JCE APIを使ったCertificate Management Protocolの利用
- 外部セッションキャッシュにおけるセッション情報保持のサポート
- RFC 5430に準拠したSuite-Bのサポート

うぅ〜〜む、PKCS#10ぐらいですかね?

あまり、注目するところは他には無いかと、、、、

RSA BSAFE Shareを試してみました(その6)

忘れた頃にやってくるBSAFE Shareの連載ですが、前回に引き続き証明書のパス検証関連で、今回は「BSAFE Shareの事前生成されたOCSPを使ったパス検証機能」を試してみたいと思います。

Java JCEのパス検証に対応したプロバイダ



Java JCE の証明書のパス構築(CertPathBuilder)、パス検証(CertPathValidator)をサポートしている一般に入手やすそうなJCEプロバイダとして

・SUN (Sun Java J2SE 1.4以降に含まれる)
・RSA BSAFE Share
・BouncyCastle
・IBM Java

の3つがあります。(IAIKやEntrust Toolkitはパス構築・検証についてJCEとは別のAPIを提供しています。)

RSA BSAFE Shareには、フツーのPKIXパス検証のオプションとして、OCSPを使った検証にも対応しているんですが、他のプロバイダには無い機能として

事前に取得していたOCSPレスポンスのデータを使ってパス検証ができる


っていうのがあります。Windows Vista以降はやっていますがOCSPレスポンスをキャッシュしておき再利用して検証するなんていう芸当がJavaでもできるっていうわけです。

これは、特にCAdESやXAdES長期署名をやっている人は食付いてきますよね(−−;CAdESやXAdESでは署名者証明書の検証情報(ルートまでの証明書のチェーン、CRL、OCSPレスポンス)を一緒にいれておいて将来検証に使ったりするのでデータとして渡されたOCSPレスポンスを使ってパス検証する必要があるわけです。ところが、SUNのプロバイダはオンライン取得しかサポートしていないのでこれができなかったわけです。

OCSPに対応したCAdESやXAdESの実装をやってらっしゃる方は、OCSPレスポンスのデータを使ってパス検証する仕組みを(どれくらい真面目にやっているかは別にして)実装されているんじゃないかと思います。(私もそうです)

OCSP応答ファイルを使った検証サンプル



BSAFE Shareのサンプル(sample/src/jce/certpath/ValidatePathUsingOCSPResponse.java)というのがあります。サンプルでは

・サブCA証明書(CAphoenix.crt)
・EE証明書(janet.crt)
・EE証明書検証用のOCSPレスポンス(ocspResponse.rsp)

が使われています。例によって(Javaで書くよりも余計な飾りが無い分全体の動きが見やすいと思うので)Jythonでサックリ書いてみると、、、、

# (0) BSAFE Share JCEプロバイダ設定して...
Security.insertProviderAt(JsafeJCE(), 1)
# (1) ファイル読んで...
cf = CertificateFactory.getInstance("X.509", "JsafeJCE")
caCert = cf.generateCertificate(FileInputStream("CAphoenix.crt"))
eeCert = cf.generateCertificate(FileInputStream("janet.crt"))
ocspBytes = 何らかの方法でOCSP応答"ocspResponse.rsp"をbyte配列で読んで
# (2) CertPath作って...
chain = ArrayList()
chain.add(eeCert)
certPath = cf.generateCertPath(chain)
# (3) CertPathParameter作って...
# (3.1) トラストアンカ作って...
ta = TrustAnchor(caCert, None)
trustAnchors = HashSet()
trustAnchors.add(ta)
pkixParams = PKIXParameters(trustAnchors)
# (3.2) OCSPパラメータ作って..
ocspParams = OCSPWithResponseParameters(ocspBytes, caCert)
# (3.3) CertPathパラメータ作って...
certPathParams = CertPathWithOCSPParameters(pkixParams, ocspParams)
# (4) 検証っと、、、、
certPathValidator = CertPathValidator.getInstance("PKIX", "JsafeJCE")
print certPathValidator.validate(certPath, certPathParams)


こんな流れになります。

(1) 証明書やOCSPを読み込んで
(2) CertPathオブジェクトを生成
(3) 検証用のPKIXParameterとOCSPパラメータを生成
(4) 検証

検証に使うOCSPレスポンス



OCSPのデータを使って検証する場合に、OCSPResponseを使うのか、BasicOCSPResponseなのかというのは気になるところです。詳しくはRFC 2560を見てもらうとしてざっくりこんな違いがあります。

・OCSPResponse
 ・BSAFE OCSPWithResponseParameters ではこれを用いる
 ・OCSPで返されるメッセージの全体
 ・中に成功したかどうかという状態情報(responseStatus)を含む
 ・通常は中にBasicOCSPResponseを含む
 ・XAdESではOCSP検証情報としてこれを用いる
 ・OpenSSLでもファイルとして検証できる
・BasicOCSPResponse
 ・単数または複数の証明書の有効状態
 ・OCSPResponseに含まれる
 ・CAdESではOCSP検証情報としてこれを用いる
 ・OpenSSLではファイルとして検証できない

このように、書いてしまった通り、BSAFE Share で検証させる場合にはOCSPResponseを使わなければなりません。CAdESの検証に使う場合にはBasicOCSPResponseからresponseStatus: successful をくっつけてラップしてOCSPResponseを作ってやらないといけません。

OCSPResponseかBasicOCSPResponseかを見分けるには dumpasn1 で表示させたときに先頭の辺にENUMERATED 0とあればOCSPResponse

SEQUENCE { ← OCSPResponseの始まり
ENUMERATED 0 ← responseStatus: successful
[0] {
SEQUENCE {
OBJECT IDENTIFIER ocspBasic (1 3 6 1 5 5 7 48 1 1)
OCTET STRING, encapsulates {
SEQUENCE { ← BasicOCSPResponseの始まり
SEQUENCE {
[1] { ← ResponderID.ByName
SEQUENCE {
SET {
SEQUENCE {
OBJECT IDENTIFIER countryName (2 5 4 6)
PrintableString 'FR'
  :以下略


無ければ BasicOCSPResponse

SEQUENCE { ← BasicOCSPResponseの始まり
SEQUENCE {
[1] { ← ResponderID.ByName
SEQUENCE {
SET {
SEQUENCE {
OBJECT IDENTIFIER countryName (2 5 4 6)
PrintableString 'FR'
}
  :以下略


ということになります。

ちょっと残念なところ



最初サンプルコードを見たときちょっと怪しいなぁ、、と思ったんですが、サンプルでは検証対象が

・SubCAがトラストアンカ
・SubCA→EEの一段のみ
・OCSPレスポンダ証明書はSubCA証明書と同じ

となっているんです。サンプルを見てみると

ocspParams = OCSPWithResponseParameters(ocspBytes, caCert)


つまり、パス中に入れられるOCSPレスポンスは高々一つだけって事です。Root→SubCA1→SubCA2→EEみたいな多段の証明書チェーンの場合にSubCA1、SubCA2の検証にはOCSPは使えないってことを意味しますし、OCSPレスポンダ証明書が事前にわかっているケースでしか使えないってことがわかります。

どうやら、証明書とOCSPレスポンスをごそっと投げて「検証お願いっっ!(はぁと)」というわけにはいかないようです。

途中までCRLでいいや、、、って場合にはPKIXParametersにCollectionStoreを追加してCRLをぶちこんでやれば検証できると思います。

まとめると

・OCSPレスポンダ証明書のパス検証はしない
・事前にOCSPレスポンダ証明書がわかっていなければいけない
・多段のOCSPは使えない

っていうことなんで前処理をしっかりやらないと汎用的なパス検証に使うのは面倒くさい感じですよね。

今、自前でもっているパス検証のやつは

・OCSPレスポンダ証明書を事前に知っておく必要はない
・OCSPレスポンダ証明書のトラストアンカからのパス検証を行う
・コレクションストアに入れていれば適切なOCSPレスポンスを選ぶ
・中間CAのOCSPレスポンスデータによる検証にも対応する
・OCSPのモデル(直接、委譲等)は問わない

というようになっているので、う〜〜〜む、最初はよさげと思ったんですが、結局当面は自前のを使うことになりそう、、、

NIST PKITSと似たやつでOCSPを含むパス検証でまともなテストケースが欲しいなぁ、、、、、時間を見つけて作るかなぁ、、、、
最新記事
Categories
Archives
Twitter
記事Google検索

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