JavaアプリからSSLサイトに通信する必要があって、開発環境で嵌ったので備忘メモ。
環境:
- Java 1.6
- Apache 2.1
ものすごく「え、えぇ?イマドキ??」という環境だけど、まぁ世の中いろいろある。
で、本番環境なら問題なかったんだけど、IPアドレスベースのVirtualHostでApache運用してる開発環境で嵌った。
Apache に登録してる証明書を Java の truststore に登録すれば良いんでしょ?
# keytool -importcert -v -trustcacerts -file server.crt -keystore cacerts -alias www
と登録して、通信させてみると、
javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No subject alternative names present
と言われてびびる。
ぐぐってみると、Atlassian のサイトに解決方法らしき話が載ってた。さすがあとらしあん!
No Subject Alternative Names
ほうほう、CN が IPアドレスのとき、Subject Alternative Names を見るようになったのね…ってそれはNameベースのSSLで使う拡張じゃないのか?
とにかくそいつを有効にすれば良いのね。
openssl.cnf を編集して、
- [ req ] セクションに
req_extensions = v3_req
って追加 - [ v3_req ] セクションに
subjectAltName = @alt_names
って追加 - [ alt_names ] セクション追加して
IP.1 = <YYY.YYY.YYY.YYY>
って追加
それを使って証明書を再作成。
# openssl x509 -in www.csr -days 365 -req -signkey server.key -sha256 -out www.crt -extensions v3_req -extfile openssl.cnf
その証明書をもっかい truststore に登録。
# keytool -delete -alias www -keystore cacerts # 元のやつを一旦削除
# keytool -importcert -v -trustcacerts -file server.crt -keystore cacerts -alias www
さぁこれでいけるか、と通信させてみると、
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
えええええ。
それ証明書を truststore に登録してないときと同じ例外やん。
つまり証明書としてマッチしてないわけですかJava的には。
しばし考えて、これはつまり Apache から送ってる証明書情報に拡張の情報(Subject Altenative Names)が乗っかってないってこと?と思い当たって、httpd.conf に記述を追加してみる。
<VirtualHost YYY.YYY.YYY.YYY:443>
+ ServerName YYY.YYY.YYY.YYY
……
IPアドレスベースの VirtualHost なので、ServerName は設定してなかったんだけど、Nameベースの拡張使うんなら、設定もNameベースに準拠しないといけないのかなと。
そもそもIPアドレスベースでServerName
使えなかったらどうしようかと思ったけど、apachectl -t
でチェックした限りでは問題ないみたい。
で、もう一度通信させてみると、無事通信できた!
……もしかして Apache の設定だけでいける?と思ったけど、未確認です。