本記事では、Java Secure Socket Extention(JSSE) API を利用して、Play Framework で実装した Web アプリケーションでSSL/TLSを利用する方法を紹介します。
本記事の元ネタはこちらです。
https://github.com/wsargent/activator-play-tls-example
本記事執筆時点で最新の activator 1.2.12 に対応させたサンプルをこちらに置いています。動作確認の際はこちらを利用ください。
https://github.com/ogis-onishi/activator-play-tls-example
前提
- Java 8 をインストールしていること。
-
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
を利用するため、Unlimited Strength policy がインストールされている必要があります。 - Java downloads にアクセス.
- "Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files for JDK/JRE 8" で検索。
- ダウンロードしたポリシーファイルを $JAVA_HOME/lib/security/ にコピー
自己署名証明書の生成
(注意)パブリック認証局の証明書を使う場合は、証明書を自分で生成する必要はありません。
HTTPSを使うためには、X.509 証明書を用意する必要があります。証明書の生成は非常に面倒な作業のため、scripts ディレクトリに置いてある生成用のスクリプトを利用してください。証明書の生成に関する詳細は、Play WS SSL の Certificate Generation セクションを参照してください。
証明書を生成するには、以下を実行してください。
cd scripts/
./gencerts.sh
certs
ディレクトリを作成し、生成されたファイルを全て移動してください。
cd scripts
mkdir ../certs
mv client* ../certs
mv example* ../certs
mv password ../certs
ホスト名の設定 ( example.com -> localhost)
本サンプルでは、認証中のホスト名を example.com
にしています。SSLを利用するためにはホスト名を example.com
にする必要があります。本サンプルの動作確認中は、example.com
が localhost
を指すように/etc/hosts
を修正してください。
$ sudo vim /etc/hosts
127.0.0.1 example.com www.example.com
(注意)本番環境では正しいホスト名を設定した認証証明書を利用してください。
HTTPS 設定で Play を起動する
証明書を生成し、ホスト名の設定も行ったため、ようやく Play を起動できます。
本アプリケーションは、activator
ではなく ./play
を利用して起動してください。スクリプトでアプリケーションを実行するためのシステムプロパティを設定しています。
./play run
app/https/CustomSSLEngineProvider
が Play の HTTPS サーバに相当します。詳細については、 Configuring HTTPS を参照してください。
(任意)暗号スイートのリストをチェックする
(注意)以下は動作確認のための作業です。スキップしても設定上問題ありません。
SSLyze をダウンロードしてください。
https://github.com/iSECPartners/sslyze/releases
Play アプリケーションに対して、SSLyze を実行してください。
cd sslyze-0_9-osx64
python sslyze.py --regular www.example.com:9443
以下のような結果が出力されます。
REGISTERING AVAILABLE PLUGINS
-----------------------------
PluginOpenSSLCipherSuites
PluginCertInfo
PluginCompression
PluginHSTS
PluginHeartbleed
PluginSessionRenegotiation
PluginSessionResumption
CHECKING HOST(S) AVAILABILITY
-----------------------------
www.example.com:9443 => 127.0.0.1:9443
SCAN RESULTS FOR WWW.EXAMPLE.COM:9443 - 127.0.0.1:9443
------------------------------------------------------
* Session Renegotiation:
Client-initiated Renegotiations: Rejected
Secure Renegotiation: Supported
* Compression:
DEFLATE Compression: Disabled
* Heartbleed:
OpenSSL Heartbleed: NOT vulnerable
Unhandled exception when processing --certinfo:
exceptions.KeyError - 'exponent'
* Session Resumption:
With Session IDs: Not supported (0 successful, 5 failed, 0 errors, 5 total attempts).
With TLS Session Tickets: Not Supported - TLS ticket not assigned.
* SSLV2 Cipher Suites:
Server rejected all cipher suites.
* TLSV1_2 Cipher Suites:
Preferred:
ECDHE-ECDSA-AES256-SHA384 256 bits HTTP 200 OK
Accepted:
ECDHE-ECDSA-AES256-SHA384 256 bits HTTP 200 OK
ECDHE-ECDSA-AES256-SHA 256 bits HTTP 200 OK
ECDHE-ECDSA-AES256-GCM-SHA384 256 bits HTTP 200 OK
ECDHE-ECDSA-DES-CBC3-SHA 168 bits HTTP 200 OK
ECDHE-ECDSA-RC4-SHA 128 bits HTTP 200 OK
ECDHE-ECDSA-AES128-SHA256 128 bits HTTP 200 OK
ECDHE-ECDSA-AES128-SHA 128 bits HTTP 200 OK
ECDHE-ECDSA-AES128-GCM-SHA256 128 bits HTTP 200 OK
* TLSV1_1 Cipher Suites:
Preferred:
ECDHE-ECDSA-AES256-SHA 256 bits HTTP 200 OK
Accepted:
ECDHE-ECDSA-AES256-SHA 256 bits HTTP 200 OK
ECDHE-ECDSA-DES-CBC3-SHA 168 bits HTTP 200 OK
ECDHE-ECDSA-RC4-SHA 128 bits HTTP 200 OK
ECDHE-ECDSA-AES128-SHA 128 bits HTTP 200 OK
* TLSV1 Cipher Suites:
Preferred:
ECDHE-ECDSA-AES256-SHA 256 bits HTTP 200 OK
Accepted:
ECDHE-ECDSA-AES256-SHA 256 bits HTTP 200 OK
ECDHE-ECDSA-DES-CBC3-SHA 168 bits HTTP 200 OK
ECDHE-ECDSA-RC4-SHA 128 bits HTTP 200 OK
ECDHE-ECDSA-AES128-SHA 128 bits HTTP 200 OK
* SSLV3 Cipher Suites:
Preferred:
ECDHE-ECDSA-AES256-SHA 256 bits HTTP 200 OK
Accepted:
ECDHE-ECDSA-AES256-SHA 256 bits HTTP 200 OK
ECDHE-ECDSA-DES-CBC3-SHA 168 bits HTTP 200 OK
ECDHE-ECDSA-RC4-SHA 128 bits HTTP 200 OK
ECDHE-ECDSA-AES128-SHA 128 bits HTTP 200 OK
SCAN COMPLETED IN 9.51 S
------------------------
(任意)クライアント証明書による認証を有効にする
クライアント証明書によるクライアント認証を有効にしたい場合、./play
スクリプトの以下の設定をコメントアウトしてください。
JVM_OPTIONS="$JVM_OPTIONS -Dplay.ssl.needClientAuth=true"
サーバを再起動すると以下のような出力を確認できます。
ECDHE-ECDSA-RC4-SHA ClientCertificateRequested - Server requested a client certificate issued by one of the following CAs: '/C=US/ST=California/L=San Francisco/O=Example Company/OU=Example Org/CN=clientca'.
サーバがクライアント認証を要求するようになりました。クライアントは接続が確立する前に clientca
ルート証明書で署名されたクライアント証明書を提供しなければなりません。
(任意)Play WS (HTTP クライアントライブラリ)でサーバに接続する
本節では、Play WS という HTTP/HTTPS クライアントライブラリを利用して、TLS 上のクライアント認証 を行う方法を紹介します。
(注意)ブラウザでのみサーバにアクセスし、HTTP / HTTPS クライアントを作成しない場合は不要です。
HTTPS クライアントの設定ファイル ws.conf
は以下のようになっています。
ws.ssl {
protocol = "TLSv1.2"
enabledProtocols = [ "TLSv1.2" ]
enabledCiphers = [
"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"
]
ws.ssl.disabledSignatureAlgorithms = "MD2, MD4, MD5, SHA1, RSA"
ws.ssl.disabledKeyAlgorithms = "EC keySize < 384"
keyManager = {
stores = [
/* Note: app must be run from ./play, which loads the KEY_PASSWORD environment variable. */
{ type: "JKS", path: "certs/client.jks", password: ${?KEY_PASSWORD} },
]
}
trustManager = {
stores = [
{ type = "JKS", path = "certs/exampletrust.jks" }
]
}
}
TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
は Suite B Profile for Transport Layer Security に定義されている強力な暗号スートです。 (Bruce Schneier はECC 定数は NSA に操作されている と信じていますが、他のオプションはこの点について制限されています。)
前述のとおり、この暗号スイートを利用するには、JCE Unlimited Policy ファイルをインストールする必要があります。まだインストールしていない場合は、前提のセクションを再度確認してください。
Play WS スタンドアロンなアプリケーションで直接利用することができます。新しいシェルを立ち上げ、以下をタイプしてください。
$ ./play
> runMain Main
以下の結果が出力されます。
[info] Running Main
header = (Content-Length,Buffer(106))
header = (Content-Type,Buffer(text/html; charset=utf-8))
body =
<!DOCTYPE html>
<html>
<body>
<h1>Congratulations! You are reading the page!</h1>
</body>
</html>
クライアントキーでのみ動くことを検証するため、ws.conf
の keyManager セクションをコメントアウトし、Main
を再実行してください。クライアント認証が失敗します。
failure = java.net.ConnectException: Received fatal alert: bad_certificate to https://example.com:9443/
ブラウザでサーバに接続する
./play
を利用すると、以下の構成で Play アプリケーションが起動します。
- HTTP プロトコルは無効
- HTTPS プロトコルは 9443 ポートで有効
ブラウザで https://example.com:9443 にアクセスすると、サーバ証明書がパブリック認証局に署名されていないため、警告が出ます。
警告が出ないようにするためには、certs/example.com.crt
をブラウザにインポートしてください。以下の手順で証明書をインポートするツールを開くことができます。
- Chromeの場合、設定 → HTTPS/SSL → 証明書の管理 → (OSごとに異なるツールが開きます。CA証明書を追加してください。)
- Firefox の場合、設定 → 詳細 → 証明書 → 証明書の表示 → 認証局証明書 → 読み込む → certs/example.com.crt を選択
再度、アクセスすると今度は Web ページが正しく開きます。
おわりに
今回のアプリケーションは TLS 1.2 と ECDSA 証明書を使って動作しています。詳細については ./play
スクリプトと証明書生成スクリプトを確認してください。
TODO: 導入のための要変更事項
今回のサンプルを実際にアプリケーションに利用する際の注意事項を示します。