Pages

自己紹介

某IT技術情報誌に連載記事を持っていたテクニカルライター。またもや休刊してしまったよ。
Copyright © 2011-2016 Daregada. All rights reserved. Powered by Blogger.

Oracle製Javaを使うアプリでhttps(TLS)接続の際に例外が出る問題

2015-03-04

Oracle製のJavaでApache Antを利用してcss-validatorをビルドする際、Validator.nuからHTMLパーサーをHTTPS(TLS接続)でダウンロードすることに失敗するよ、という話

Abstract

理由
Oracle製Javaが利用するキーストアに、Validator.nuのサーバー証明書を発行しているStartCom認証局のルート証明書が登録されていないため、TLS接続を行なう際に例外(SSLHandshakeException)が発生してApache Antの実行が中断されてしまう
対策
StartCom認証局のルート証明書を入手し、キーストアに登録する(OSの管理者権限が必要)。あるいは、別の手段でダウンロードして、Apache Antによるダウンロードは行なわない。

理由

少し説明が長くなるので、(公開鍵の)証明書とは何であるかということや、ルート認証局と中間認証局の関係などを知っている人は、具体的な理由まで読み飛ばしてほしい。

前提となる知識

公開鍵証明書(public-key certificate、以下証明書と表記)とは、簡単に言えば「誰かの公開鍵と、その真正性(出所の正しさ)を確認するためのデジタル署名の組み合わせ」だ。より正確には、公開鍵に加えて、公開鍵の所有者の情報や、証明書の発行者である認証局(Certification Authority; CA)の情報、証明書の有効期間、公開鍵暗号やデジタル署名のアルゴリズムの情報などを含むデータ構造体に対して、認証局のデジタル署名が付けられている。

Javaでは、「キーストア」と呼ばれるデータベース(実体は単一のファイル)に、証明書を登録して利用する。キーストアを明示しない場合、「デフォルトキーストア」として、Javaのインストール先(たいていは環境変数JAVA_HOMEで示される位置)にある、jre/lib/security/cacertsが使われる。

あなたが使うWebブラウザーと同様に、cacertsにはベリサイン(シマンテック)やグローバルサインなど、いくつかの認証局のルート証明書が最初から登録されている。このことは、信頼の基点(トラストアンカー)となるルート認証局として、あなたがこれらの認証局を信頼することを意味する

実のところ、ルート認証局の信頼性は、ルート証明書からは判断できない(*1)。ルート認証局を運用している企業・団体の知名度や過去の実績(*2)、WebTrust-CA (WebTrust Program for Certification Authorities)に基づく監査を受けているかなどを考慮して、あなたが決めることだ。cacertsやWebブラウザーを初期設定のまま使うなら、それぞれのベンダーが信頼するとした判断をそのまま受け入れたことになる。もちろん、あなたが信頼しないルート認証局が含まれているなら、そのルート証明書を取り除けばいい。

*1 ルート証明書は、公開鍵の所有者であるルート認証局自身がデジタル署名を付けて発行した自己署名証明書であり、いわゆる「オレオレ証明書」と同じ形式だ。いくつかのルート証明書を信頼しつつ、オレオレ証明書を信頼しないという判断は、証明書の内容以外の基準に基づく。

*2 オランダのルート認証局DigiNotarは、認証局インフラに不正侵入されていたことが2011年に発覚。500以上の証明書を不正に発行され、実際にGmailに対する中間者攻撃の試みに使われた。Webブラウザーのベンダーは、ブラウザーが利用するルート証明書のリストからDigiNotarのルート証明書を削除する措置をとり、結果としてDigiNotarの事業は破綻に追い込まれた。

ルート認証局以外の認証局(中間認証局)は、ルート認証局を頂点とする階層構造を成し、自分より上位の認証局から発行された証明書によって、自身の公開鍵の真正性を確認される。たとえば、「ルート認証局A」の下に「中間認証局B」があるなら、Aのデジタル署名が付いたBの証明書が発行される。もし、Bの下に「中間認証局C」があれば、Bのデジタル署名が付いたCの証明書が発行される(以下同様)。

さて、サーバーの公開鍵に対する証明書(「サーバー証明書」などと呼ばれる)は、たいていは中間認証局(上の例ではBやC)が発行するので、サーバー証明書だけではサーバーの公開鍵の真正性を確認できない。そこで、TLSプロトコルによる接続の初期段階(ハンドシェイクプロトコル)において、サーバーはクライアント(WebブラウザーやApache Antなど)に、証明書チェーンと呼ばれる複数の証明書のリストを送る。このリストには、ルート証明書に到達するまでの中間証明書をすべて含む必要がある。

たとえば、先ほどの例の「中間認証局C」が「サーバーD」に対して、Cのデジタル署名が付いたサーバー証明書を発行したとしよう。Dがクライアントに対して送信する証明書チェーンには、Dの証明書に加えて、(Dの証明書を発行した)Cの証明書、(Cの証明書を発行した)Bの証明書が含まれる。Bの証明書を発行した「ルート認証局A」の証明書は、ルート証明書としてクライアント側にあるはずなので、リストには含めない。

クライアントの側では、信頼するAのルート証明書に証明書チェーンが到達したなら、一連の証明書のデジタル署名を検証する。信頼するAの公開鍵を使ってBの証明書のデジタル署名を検証し、成功したらBの公開鍵を使ってCの証明書のデジタル署名を検証し、成功したらCの証明書を使ってDの証明書のデジタル署名を検証する。これが成功して初めて、サーバー証明書に含まれるDの公開鍵の真正性が確認できたことになる。

もし、証明書チェーンが信頼するルート認証局のルート証明書に到達しなかったり、デジタル署名の検証に失敗した場合には、クライアントはサーバー証明書を信頼しない。Webブラウザーでは警告画面を表示してユーザーに確認を求めるし、Apache Antの場合はJavaの例外(SSLHandshakeException)が発生して実行を中断する。

具体的な理由

ルート認証局のひとつに、イスラエルの企業StartComが運営するStartCom認証局がある。サービス名称は「StartSSL」だ。なお、SSLはTLSの前身となったプロトコルで、脆弱性が発見されたために現在では排除・無効化されている。

StartCom認証局が発行する「Class 1」証明書は、ドメイン名とメールアドレスの確認のみで無料で取得できるため利用者が多い。また、「Class 2」証明書は、名前・住所などの実在確認が追加されるが、2年で59.90ドルと非常に安価に取得できる。今回問題が起きたValidator.nuのWebサイトは、StartCom認証局の「Class 2」証明書を利用している。

なお、IE/Chrome/Firefox/Safariなど、主要なWebブラウザーは、StartCom認証局のルート証明書を登録済みの状態で配布される。つまり、ブラウザーのベンダーはStartCom認証局を信頼する。通常、ブラウザーのユーザーはそれを変更せずStartCom認証局を信頼するので、ブラウザーでValidator.nuのWebサイトを閲覧し、そこで配布されているHTMLバリデーターやHTMLパーサーのファイルをダウンロードすることに問題は生じない。同様に、OpenJDKのデフォルトキーストアにもStartCom認証局のルート証明書が登録済みなので、OpenJDKを利用すればこの問題は起きない。

いっぽう、Oracle製のJavaでは、デフォルトキーストア(cacerts)にStartCom認証局のルート証明書が登録されていない。つまり、Oracle製のJavaはStartCom認証局を信頼しない。その結果、デフォルトキーストアを変更せずに、StartCom認証局が発行した証明書を利用してサーバーがJavaで書かれたクライアント(Apache Antなど)とTLS接続(HTTPSなど)を行なおうとすると、例外(SSLHandshakeException)が発生してクライアントの実行が中断されてしまう。

対策

理由がわかれば解決するのは簡単で、あなたがStartCom認証局を信頼することをJavaに伝えればいい。具体的には、StartCom認証局のルート証明書をデフォルトキーストア(cacerts)に登録するか、StartCom認証局のルート証明書を含む別のキーストアを作って、そちらを使うかのいずれかだ。

ここでは前者の方法を説明する。前提としては、Oracle製のJava JDKを使っていて、環境変数JAVA_HOMEを正しく設定していることぐらいだ。

  1. StartCom認証局のルート証明書を入手する

    Webブラウザーでhttps://www.startssl.com/certs/を開くと、StartCom認証局が提供する証明書が一覧表示される(前述のように、主要なWebブラウザーはStartCom認証局のルート証明書を登録済みなので、この接続に問題は生じない)。クライアント側で必要なのはルート証明書だけなので、ca.crtだけをダウンロードする。ダウンロード先は、あなたが場所を覚えていられれば、どこでも構わない。

    Windowsでは、ダウンロードしたファイルのプロパティを開き、ブロックの解除ボタンが表示されていれば、それを押しておこう。

  2. 管理者権限で操作する

    デフォルトキーストア(cacerts)は、Javaのインストール先(たいていは環境変数JAVA_HOMEで示される位置)の、jre/lib/securityに置かれる。WindowsのProgram Filesフォルダー以下など、一般ユーザーの書き換えを許さない場所にインストールした場合には、書き換えに管理者権限が必要だ。

    Windows 7以前を使っているなら、スタートメニューから「コマンドプロンプト」(あるいは「cmd」)を検索し、右クリックメニューの「管理者として実行」を選択する。Windows 8以降では、Windowsキーを押したままxキーを押して、メニューから「コマンドプロンプト(管理者)」を選択すればいい。

    Unix系OSを使っているなら、端末(のウィンドウ)を開き、それぞれのOSの作法に従って管理者権限を使う。具体的には、あらかじめsuで管理者(root)になっておくか、keytoolを実行するさいに、sudo keytool ...と先頭にsudoを付けて、管理者権限で実行する。

  3. StartComのルート証明書を登録する

    キーストアへの登録に使うツールkeytoolは、Java JDKに含まれている。注意すべき点としては、登録作業の最後に「この証明書を信頼しますか。[いいえ]:」と確認されるところで、日本語で「はい」と入力しないこと(登録が行なわれない)。「y」や「yes」と入力してEnterキーを押せばいい。また、最後に「アクセスが拒否されました」と表示された場合は、管理者権限でkeytoolを実行していないことが原因なので、「管理者権限で操作する」を読み直してほしい。

    Windowsを使っていて、環境変数PATHに「%HOME_JAVA%\bin」を追加しているなら、そのままkeytoolを実行できるはずだ。

    Unix系OSでは、環境変数JAVA_HOMEを使わず、javakeytoolにシンボリックリンクが張られていることもある。その場合は、シンボリックリンクをたどって、実際にOracle製Java JDKがインストールされているディレクトリを調べ、一時的にJAVA_HOMEを設定すればいいだろう。

0 件のコメント:

コメントを投稿