ファイヤープロジェクト
HttpClientでSSL通信(HttpClient3.0-rc3)
2005-08-09T02:40+09:00   matsu
HttpClientでは特に独自の設定やコーディングをする必要なく,SSL通信ができるらしい.JavaでのSSL通信に関しては何ら調査したことがないので,それも兼ねて確認してみた.
本家には,SSL通信に関していろいろ記述があるが,本頁では基本的な事柄,すなわちSSLでサーバ認証して通信するレベルの確認にとどめる. 本家では,SSL通信にはJSSEのインストールが必要とあるが,JRE 1.4以降をしている場合は,これは必要ないと思うが,念のため,以下でjarを確認し,なければJSSEをインストールする.
$> ls $JAVA_HOME/jre/lib/jsse.jar
これができていれば,あとは,信頼するサーバの証明書を導入すれば,そのサーバとのSSL通信が可能となる. 特にコーディングの必要はないので,ここでは前頁のサンプルを使用して動作検証する.
信頼するサーバの証明書の導入なしにいきなりSSL通信使用とすると,以下のようになる.
$> java -jar httpclientsample.jar 0 1 https://thor/~matsu/ssltest/                                                                       cookiePolicy = compatibility
2005-08-09 01:59:25,598 DEBUG [main] header Wire 69 - >> "GET /~matsu/ssltest/ HTTP/1.1[\r][\n]"
2005-08-09 01:59:25,630 DEBUG [main] header Wire 69 - >> "User-Agent: Jakarta Commons-HttpClient/3.0-rc3[\r][\n]"
2005-08-09 01:59:25,633 DEBUG [main] header Wire 69 - >> "Host: thor[\r][\n]"
2005-08-09 01:59:25,634 DEBUG [main] header Wire 69 - >> "[\r][\n]"
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: No trusted certificate found
        at com.sun.net.ssl.internal.ssl.BaseSSLSocketImpl.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SunJSSE_az.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SunJSSE_az.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SunJSSE_ax.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.j(DashoA12275)
        at com.sun.net.ssl.internal.ssl.SSLSocketImpl.a(DashoA12275)
        at com.sun.net.ssl.internal.ssl.AppOutputStream.write(DashoA12275)
        at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:66)
        at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:124)
        at org.apache.commons.httpclient.HttpConnection.flushRequestOutputStream(HttpConnection.java:825)
        at org.apache.commons.httpclient.HttpMethodBase.writeRequest(HttpMethodBase.java:1975)
        at org.apache.commons.httpclient.HttpMethodBase.execute(HttpMethodBase.java:993)
        at org.apache.commons.httpclient.HttpMethodDirector.executeWithRetry(HttpMethodDirector.java:393)
        at org.apache.commons.httpclient.HttpMethodDirector.executeMethod(HttpMethodDirector.java:168)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:396)
        at org.apache.commons.httpclient.HttpClient.executeMethod(HttpClient.java:324)
        at org.fireproject.httpclientsample.HelloHttpClient.processGet(HelloHttpClient.java:75)
        at org.fireproject.httpclientsample.HelloHttpClient.main(HelloHttpClient.java:55)
Caused by: sun.security.validator.ValidatorException: No trusted certificate found
        at sun.security.validator.SimpleValidator.buildTrustedChain(SimpleValidator.java:304)
        at sun.security.validator.SimpleValidator.engineValidate(SimpleValidator.java:107)
        at sun.security.validator.Validator.validate(Validator.java:202)
        at com.sun.net.ssl.internal.ssl.X509TrustManagerImpl.checkServerTrusted(DashoA12275)
        at com.sun.net.ssl.internal.ssl.JsseX509TrustManager.checkServerTrusted(DashoA12275)
        ... 18 more
メッセージの通り,サーバから送信される証明書が信頼できないので,エラーとなっている. これを解決するには,サーバ証明書をJava実行環境に渡す必要がある. Java実行環境は,デフォルトでは,
$JAVA_HOME/jre/lib/security/cacerts
というファイルをキーストアとして使用する. このファイルに信頼できるサーバの証明書が格納されている. このファイルにはインストール時点で,既に著名なルートCAが登録されている. キーストアの管理にはkeytoolというコマンドを使用する. キーストアファイルに格納されている証明書のリストは-listオプションを指定すると閲覧できる.
$> keytool -v -list -keystore  $JAVA_HOME/jre/lib/security/cacerts
キーストアのパスワードを入力してください:  changeit

キーストアのタイプ: jks
キーストアのプロバイダ: SUN

キーストアには 31 エントリが含まれます。

別名: verisignclass1g3ca
作成日: 2004/06/16
エントリのタイプ: trustedCertEntry

所有者: CN=VeriSign Class 1 Public Primary Certification Authority
  - G3, OU="(c) 1999 VeriSign, Inc. - For authorized use only",
        OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
実行者: CN=VeriSign Class 1 Public Primary Certification Authority
  - G3, OU="(c) 1999 VeriSign, Inc. - For authorized use only",
        OU=VeriSign Trust Network, O="VeriSign, Inc.", C=US
シリアル番号: 8b5b75568454850b00cfaf3848ceb1a4
有効日: Fri Oct 01 09:00:00 JST 1999 有効期限: Thu Jul 17 08:59:59 JST 2036
証明書のフィンガープリント:
         MD5:  B1:47:BC:18:57:D1:18:A0:78:2D:EC:71:E8:2A:95:73
         SHA1: 20:42:85:DC:F7:EB:76:41:95:57:8E:13:6B:D4:B7:D1:E9:8E:46:A5


*******************************************
*******************************************


別名: equifaxsecureebusinessca1
作成日: 2003/07/24
エントリのタイプ: trustedCertEntry

所有者: CN=Equifax Secure eBusiness CA-1, O=Equifax Secure Inc., C=US
実行者: CN=Equifax Secure eBusiness CA-1, O=Equifax Secure Inc., C=US
シリアル番号: 4
...省略...
キーストアには,管理用パスワードがあり,$JAVA_HOME/jre/lib/security/cacertsのデフォルトパスワードは
changeit
である. keytoolsは,-keystoreオプションでキーストアファイルを指定しない場合,$HOME/.keystoreファイルをキーストアファイルとして使用する(なければ作成する). 一方,Javaアプリケーションは,デフォルトで$JAVA_HOME/jre/lib/security/cacertsをキーストアファイルとして使用する(※). Javaアプリケーションに任意のキーストアファイルを指定するには,システムプロパティを指定する.
javax.net.ssl.trustStore
トラストストアファイル
javax.net.ssl.trustStorePassword
トラストストアファイルのパスワード
javax.net.ssl.keyStore
キーストアファイル
javax.net.ssl.keyStorePassword
キーストアファイルのパスワード
どうも気持が悪いが,トラストストアは信頼できるサーバ証明書を格納したキーストアであり,SSL通信のためにはtrustStore,trustStorePasswordを指定する. trustStorePasswordは指定しなくても動作可能で,指定した場合はトラストストアファイルの整合性を検証できるらしい. 上記により,以下のように実行すると,$HOME/.keystoreをトラストストアファイルとして使用してSSL通信が可能となる.
$> java -Djavax.net.ssl.trustStore=/home/matsu/.keystore \
  -jar httpclientsample.jar 0 1 https://thor/~matsu/ssltest/
$> java -Djavax.net.ssl.trustStore=/home/matsu/.keystore \
  -Djavax.net.ssl.trustStorePassword=foovar \
  -jar httpclientsample.jar 0 1 https://thor/~matsu/ssltest/
※ より厳密な動作はサンのサイトに記述されている.
最後にキーストアにサーバ証明書を登録する. 今回は自己署名したCA証明書と,そのCAで署名したサーバ証明書を登録する. まず,CA証明書の登録.
$> keytool -import -trustcacerts -file ca.cert  -alias ca
-trustcacertsオプションとともに指定する. -aliasオプションでは,鍵管理用のための,証明書の別名を指定する. 上記コマンドを実行するとパスワードが聞かれるので,キーストアファイルのパスワードを入力する. まだない場合は,新規パスワードを入力する. 次にサーバ証明書を登録する.
$> keytool -import -file server.cert -alias server
上記コマンドを実行するとやはりパスワードが聞かれるので,キーストアファイルのパスワードを入力する. キーストアファイルを指定しなかったので,$HOME/.keystoreファイルが作成,更新されている. では,確認までにSSL通信してみる.
$> java -Djavax.net.ssl.trustStore=/home/matsu/.keystore -jar httpclientsample.jar 0 1 https://thor/~matsu/ssltest/
cookiePolicy = compatibility
2005-08-09 02:28:15,973 DEBUG [main] header Wire 69 - >> "GET /~matsu/ssltest/ HTTP/1.1[\r][\n]"
2005-08-09 02:28:16,006 DEBUG [main] header Wire 69 - >> "User-Agent: Jakarta Commons-HttpClient/3.0-rc3[\r][\n]"
2005-08-09 02:28:16,009 DEBUG [main] header Wire 69 - >> "Host: thor[\r][\n]"
2005-08-09 02:28:16,010 DEBUG [main] header Wire 69 - >> "[\r][\n]"
2005-08-09 02:28:16,118 DEBUG [main] header Wire 69 - << "HTTP/1.1 200 OK[\r][\n]"
2005-08-09 02:28:16,125 DEBUG [main] header Wire 69 - << "Date: Mon, 08 Aug 2005 17:28:15 GMT[\r][\n]"
2005-08-09 02:28:16,129 DEBUG [main] header Wire 69
  - << "Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7d mod_jk/1.2.5[\r][\n]"
2005-08-09 02:28:16,136 DEBUG [main] header Wire 69 - << "Last-Modified: Mon, 08 Aug 2005 16:49:17 GMT[\r][\n]"
2005-08-09 02:28:16,137 DEBUG [main] header Wire 69 - << "ETag: "19f44d-2c-42f78d0d"[\r][\n]"
2005-08-09 02:28:16,138 DEBUG [main] header Wire 69 - << "Accept-Ranges: bytes[\r][\n]"
2005-08-09 02:28:16,138 DEBUG [main] header Wire 69 - << "Content-Length: 44[\r][\n]"
2005-08-09 02:28:16,139 DEBUG [main] header Wire 69 - << "Content-Type: text/html[\r][\n]"
===== 0 Cookies =====
<html>
<body>
PAGE SSLTEST
</body>
</html>
ログの見ためではよくわからない. ところで,上記作業では,CA証明書とサーバ証明書の両方を登録したが,これはいずれかでよい. CA証明書のみを登録した場合,サーバ証明書の署名元のCAがキーストアにあり信頼できるので,そのサーバ証明書が信頼できる. サーバ証明書のみを登録した場合,サーバ証明書そのものがキーストアにあるので信頼できる. 以下にサーバ証明書を削除し,CA証明書だけでもSSL通信できることを確認してみる.
$%gt; keytool -delete -alias thor

$> keytool -list -alias server
キーストアのパスワードを入力してください:  foovar
keytool エラー: java.lang.Exception: 別名 <server> は存在しません。

$> keytool -list -alias ca
キーストアのパスワードを入力してください:  foovar
ca, 2005/08/09, trustedCertEntry,
証明書のフィンガープリント (MD5): D6:9B:85:D3:C7:97:0F:F0:5E:60:28:B9:6C:75:73:7F

$> java -Djavax.net.ssl.trustStore=/home/matsu/.keystore -jar httpclientsample.jar 0 1 https://thor/~matsu/ssltest/
cookiePolicy = compatibility
2005-08-09 02:37:36,852 DEBUG [main] header Wire 69 - >> "GET /~matsu/ssltest/ HTTP/1.1[\r][\n]"
2005-08-09 02:37:36,886 DEBUG [main] header Wire 69 - >> "User-Agent: Jakarta Commons-HttpClient/3.0-rc3[\r][\n]"
2005-08-09 02:37:36,890 DEBUG [main] header Wire 69 - >> "Host: thor[\r][\n]"
2005-08-09 02:37:36,891 DEBUG [main] header Wire 69 - >> "[\r][\n]"
2005-08-09 02:37:37,019 DEBUG [main] header Wire 69 - << "HTTP/1.1 200 OK[\r][\n]"
2005-08-09 02:37:37,026 DEBUG [main] header Wire 69 - << "Date: Mon, 08 Aug 2005 17:37:36 GMT[\r][\n]"
2005-08-09 02:37:37,033 DEBUG [main] header Wire 69
  - << "Server: Apache/1.3.33 (Debian GNU/Linux) mod_ssl/2.8.22 OpenSSL/0.9.7d mod_jk/1.2.5[\r][\n]"
2005-08-09 02:37:37,034 DEBUG [main] header Wire 69 - << "Last-Modified: Mon, 08 Aug 2005 16:49:17 GMT[\r][\n]"
2005-08-09 02:37:37,035 DEBUG [main] header Wire 69 - << "ETag: "19f44d-2c-42f78d0d"[\r][\n]"
2005-08-09 02:37:37,035 DEBUG [main] header Wire 69 - << "Accept-Ranges: bytes[\r][\n]"
2005-08-09 02:37:37,036 DEBUG [main] header Wire 69 - << "Content-Length: 44[\r][\n]"
2005-08-09 02:37:37,036 DEBUG [main] header Wire 69 - << "Content-Type: text/html[\r][\n]"
...省略...
やはりCA証明書だけをキーストアに登録しても,SSL通信できる. また,上記のような証明書のaliasを指定したlistや証明書のdelete方法を覚えておくと便利かもしれない.
matsu(C)
Since 2002
Mail to matsu