第15章 SSLを使用する
建築物と創造物の間には越えられない壁があります。それは、建築物は作られた後でしか愛されることができないのに、創造物は存在する前から愛されていることです。
—CHARLES DICKENS
logbackはソケットベースのアペンダーから遠隔のレシーバーにログを配信するために、Secure Socket Layer(SSL)を使用することが出来ます。SSLに対応したアペンダーとレシーバーを使うと、シリアライズされたロギングイベントはセキュアなチャネルで配信されます。
SSLとそれを使うコンポーネントの役割
アペンダーやレシーバーはサーバーとしても振る舞うし、クライアントとしても振る舞うことができます。これはネットワーク接続を始める方向によるものです。サーバーとして振る舞うとき、logbackのコンポーネントは外部のクライアントコンポーネントからの接続を待ち受けます。逆に、クライアントとして振る舞うコンポーネントは、外部のサーバーコンポーネントに接続し始めます。たとえば、クライアントとして振る舞うアペンダーは、サーバーとして振る舞うレシーバーに接続するのです。あるいは、クライアントとして振る舞うレシーバーが、 サーバーとして振る舞うアペンダーに接続します。
コンポーネントの役割は、基本的にコンポーネントタイプによって決まります。たとえば、 SSLServerSocketAppender
はサーバーとして振る舞うアペンダーコンポーネントですし、SSLSocketAppender
はクライアントとして振る舞うアペンダーコンポーネントです。このように、開発者もアプリケーション管理者も、思った通りの方向でネットワーク接続を始められるようにlogbackのコンポーネントを設定することができます。
SSLのコンテキストにおいて接続を開始する方向は非常に重要です。なぜなら、サーバーコンポーネントは接続してくるクライアントに対してX.509証明書を使って自分のことを証明しなければならないからです。クライアントコンポーネントはサーバーに接続するとき、信頼できるかどうかをサーバーの証明書で検証します。開発者やアプリケーション管理者はlogbackのコンポーネントの役割をきちんと理解しておかなければなりません。つまり、サーバーならキーストア(サーバーのX.509証明書を配置します)を適切に構成し、クライアントならトラストストア(信頼できるサーバーかどうかを検証するときに使用する自己署名ルート証明書を配置します)を適切に構成しなければなりません。
SSLが相互認証するように設定されている場合、サーバーコンポーネントとクライアントコンポーネントの両方が、それぞれのピアから信頼性を検証された正当なX.509証明書を持っていなければなりません。相互認証はサーバーコンポーネントで設定するものなので、開発者もアプリケーション管理者も、コンポーネントがちゃんとサーバーとして振る舞っていることをきちんと把握しておかなければなりません。
本章では、サーバーとして振る舞うアペンダーやレシーバーのことを、単にサーバーコンポーネントあるいはサーバーと呼ぶことにします。そして、クライアントとして振る舞うコンポーネントはクライアントコンポーネントあるいはクライアントと呼ぶことにします。
SSLとX.509証明書
SSLに対応したlogbackコンポーネントを使うには、SSLサーバーとして動作するコンポーネントが自分のことを証明するのに、X.509証明書(秘密鍵とそれに対応する証明書、および、CAの証明書チェーン)が必要になります。相互認証を使いたいときは、SSLクライアントとして動作するコンポーネントの証明書も必要です。
民間の証明機関(CA)だけでなく、独自のCAで発行した証明書を使うことができるのですが、自己署名した証明書を使うこともできます。必要事項は次のとおりです。
- (自己署名証明書を使わない場合)サーバーコンポーネントに、サーバーの秘密鍵を含むキーストア、対応する証明書、およびCAの証明書チェーンを指定しなければなりません。
- クライアントコンポーネントに、信頼されたルートCA証明書(複数可)または、サーバの自己署名ルート証明書を含むトラストストアを指定しなければなりません。
SSL用のlogbackコンポーネントの設定
logbackがSSL対応するのに使っているJava Secure Sockets Extension(JSSE)とJava 暗号化アーキテクチャ(JCA)には設定可能な項目がたくさんあります。また、プラグイン可能なフレームワークなので、組み込みのSSLとプラットフォーム固有の暗号化機能は差し替え可能になっています。SSLに対応したlogbackのコンポーネントでは、SSLエンジンと暗号化プロバイダーに設定できることは全て設定できるようになっています。ですので、利用者によって固有のセキュリティ要件を満たすことができます。
JSSEのシステムプロパティを使った基本的なSSLの設定
SSLに対応したlogbackのコンポーネントでは、SSLの設定可能なプロパティのほとんどについて妥当な初期値が用意されています。したがって、ほとんどの場合いくつかJSEEのシステムプロパティを指定するだけで済むでしょう。
このセクションの残りの部分では、ほとんどの環境で必要になる特定のJSSEプロパティについて説明します。システムプロパティを使ってJSSEをカスタマイズする方法の詳細については、 JSSEのカスタマイズのJSSEリファレンスガイドを参照してください。
サーバーとして振る舞うSSL対応のlogbackアペンダーやレシーバー(SSLServerSocketReceiver
やSSLServerSocketAppender
やSimpleSSLSocketServer
)を使うときは、JSSEのシステムプロパティで秘密鍵と証明書を含むキーストアの場所と種類とパスワードを指定しなければなりません。
サーバー側でキーストアを指定するためのシステムプロパティ
プロパティ名 | 説明 |
---|---|
javax.net.ssl.keyStore |
サーバーコンポーネントの秘密鍵と証明書を含むファイルの、ファイルシステム上のパスを指定します。 |
javax.net.ssl.keyStoreType |
キーストアの種類を指定します。このプロパティが指定されていない場合、プラットフォームのデフォルト(JKS)になります。 |
javax.net.ssl.keyStorePassword |
キーストアにアクセスするためのパスワードを指定します。 |
こちらの例で、SSL対応のサーバーコンポーネントを実行する際にシステムプロパティを指定する方法を確認してください。
サーバーコンポーネントが民間の証明機関(CA)で署名された証明書を使うときは、おそらくクライアントコンポーネントでSSLの設定をする必要はありません。サーバー側では、JVMのシステムプロパティとしてキーストアを指定するだけでよいです。
自己署名したサーバー証明書か、Javaのデフォルトのトラストストアに含まれないルート証明書(社内用の証明局など)で署名したサーバー証明書を使うときは、JSEEシステムプロパティでトラストストアの場所、種類、パスワードを指定するか、サーバー証明書を署名した信頼できるルート証明書を指定しなければなりません。SSL対応のクライアントコンポーネントを利用するアプリケーションごとに、これらのシステムプロパティを設定しなければなりません 。
クライアント側でトラストストアを指定するためのシステムプロパティ
プロパティ名 | 説明 |
---|---|
javax.net.ssl.trustStore |
サーバーコンポーネントの証明書か、サーバー証明書に署名した認証局(CA)の信頼できるルート証明書のファイルシステム上のパスを指定します。 |
javax.net.ssl.trustStoreType |
トラストストアの種類を指定します。このプロパティが指定されていない場合、プラットフォームのデフォルト(JKS)になります。 |
javax.net.ssl.trustStorePassword |
トラストストアにアクセスするためのパスワードを指定します。 |
こちらの例で、SSL対応のクライアントコンポーネントを実行する際にシステムプロパティを指定する方法を確認してください。
高度なSSLの設定
JSSEシステムプロパティによる基本的なSSLの設定だけでは足りないことがあります。WebアプリケーションでSSLServerSocketReceiver
を使用しているとき、WebクライアントがWebサーバーを識別するための証明書と、ロギングクライアントがロギングサーバーを識別するための証明書では別のものを使いたいはずです。ロギングサーバーには、認証、および、認可されたリモートロガーだけが接続できるよう、SSLのクライアントを認証したいこともあるでしょう。あるいは、内部ネットワークではSSLプロトコルと暗号スイートを使わなければならない、というポリシーを強制する組織があるかもしれません。これらのいずれかのニーズを満たすためには、logbackの高度なSSL設定オプションを確かめておいたほういいでしょう。
logbackのコンポーネントでSSLを設定するときは、ssl
プロパティに指定します。
SSLServerSocketReceiver
を使うときは、keystoreプロパティでサーバーの証明書を含んだキーストアを指定します。
<configuration> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> </encoder> </appender> <root level="debug"> <appender-ref ref="CONSOLE" /> </root> <receiver class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver"> <ssl> <keyStore> <location>classpath:/logging-server-keystore.jks</location> <password>changeit</password> </keyStore> </ssl> </receiver> </configuration>
ここでは、キーストアの場所にアプリケーションのクラスパスのルートに配置されたlogging-server-keystore.jksが指定されています。もちろん、fileから始まるURLを指定することもできます。
アプリケーションでSSLSocketAppender
を使いたいけど、アプリケーション自体の(JSEEの)デフォルトのトラストストアを変更したくないときは、javax.net.ssl.trustStore
プロパティを使うことができます。
<configuration> <appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender"> <ssl> <trustStore> <location>classpath:/logging-server-truststore.jks</location> <password>changeit</password> </trustStore> </ssl> </appender> <root level="debug"> <appender-ref ref="SOCKET" /> </root> </configuration>
ここでは、トラストストアの場所にアプリケーションのクラスパスのルートに配置されたlogging-server-truststore.jksが指定されています。 もちろん、fileから始まるURLを指定することもできます。
SSLプロパティ
JSSEは設定可能なオプションを大量に公開しています。logbackのSSL対応では公開された設定のほとんどを設定ファイル中で指定できるようになっています。XML形式の設定ファイルでは、SSLの各種設定をssl要素で設定します。ssl要素に対応しているのはSSLConfiguration
クラスです。
SSLに対応したコンポーネントを設定するには、デフォルト値では困る時にこれらのプロパティを設定するだけでよいです。なんでもかんでも設定してしまうと、こんがらがってしまって問題を解析するのがとてもむずかしくなってしまいます。
SSLの設定におけるトップレベルのプロパティは次のとおりです。これらのプロパティの多くについて、さらに下位のプロパティが存在します。トップレベルなプロパティの一覧表の後に説明します。
プロパティ名 | Type | 説明 |
---|---|---|
keyManagerFactory |
KeyManagerFactoryFactoryBean
|
KeyManagerFactory の設定を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのファクトリが使われます。キーマネージャファクトリの設定を参照してください。
|
keyStore |
KeyStoreFactoryBean
|
SSLクライアント( SSLサーバー( キーストアの設定については後述します。 |
parameters |
SSLParametersConfiguration |
SSLセッションのネゴシエーションで使用するいろいろなパラメータを指定します。SSLパラメータの設定については後述します。 |
protocol | String |
SSLContext を作成するために使用するSSLプロトコルを指定します。JSSEリファレンスガイドに記載された標準名を指定してください。このプロパティが設定されていない場合は、JVMのデフォルトのプロトコル名が使用されます。
|
provider | String |
SSLContext を作成するために使用するJSSEプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダ名が使用されます。
|
secureRandom |
SecureRandomFactoryBean
|
SecureRandom (安全な乱数生成器)の設定を指定します。このプロパティが設定されていない場合は、JVMのデフォルトの乱数生成器が使用されます。安全な乱数生成器の設定については後述します。
|
trustManagerFactory |
TrustManagerFactoryFactoryBean
|
TrustManagerFactory の設定を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのファクトリーを使用します。トラストマネージャファクトリーについては後述します。
|
trustStore |
KeyStoreFactoryBean
|
SSLのリモートピアの同一性を検証するために使う このプロパティで指定したトラストストアは、JSSEの |
キーストアの設定
KeyStoreFactoryBean
は、X.509証明書を格納したKeyStore
を作成するために必要な設定を提供します。このファクトリーBeanのプロパティは、SSLの設定で紹介したkeyStoreおよびtrustStoreとして使うことができます。
プロパティ名 | 型 | 説明 |
---|---|---|
location | String |
キーストアの場所をURLで指定します。ファイルシステム上のキーストアを指定するときは、file: 形式のURLを指定します。classpath: 形式のURLを指定すれば、クラスパス上のリソースを指定することもできます。URLスキームがないときは、classpath: が指定されたものとして扱います。 |
password | String |
キーストアにアクセスするためのパスワードを指定します。 |
provider | String |
KeyStore を作成するために使用するJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのキーストアプロバイダーが使用されます。
|
type | String |
KeyStore の種類を指定します。指定できるのは、Java暗号化アーキテクチャに記載された仕様である標準名です。このプロパティが設定されていない場合は、JVMのデフォルトが使用されます。
|
キーマネージャファクトリーの設定
KeyManagerFactory
を作成するために必要な設定は、KeyManagerFactoryFactoryBean
によって提供されます。普通はキーマネージャーファクトリーの明示的な設定は不要です。ほぼ全ての場合にJVMのデフォルトのファクトリーで十分だからです。
プロパティ名 | 型 | 説明 |
---|---|---|
algorithm | String |
KeyManagerFactory が使うアルゴリズム名を指定します。指定できるのは、JSSEリファレンスガイドに記載された標準名だけです。このプロパティが設定されていない場合は、JVMのデフォルトのアルゴリズムが使用されます。
|
provider | String |
SecureRandom 生成器を生成するのに使われるJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダーが使用されます。
|
安全な乱数生成器の設定
SecureRandom
生成器を作成するために必要な設定は、SecureRandomFactoryBean
によって提供されます。JVMのデフォルトの乱数生成器で十分なことがほとんどなので、普通は明示的に設定する必要がありません。
プロパティ名 | 型 | 説明 |
---|---|---|
algorithm | String |
安全な乱数生成器 のアルゴリズム名を指定します。指定できるのはJava暗号化アーキテクチャのに記載された標準名だけです。このプロパティが設定されていない場合は、JVMのデフォルトの乱数生成アルゴリズムが使用されます。
|
provider | String |
安全な乱数生成器 を生成するために使われるJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダ名が使用されます。
|
SSLパラメータの設定
SSLParametersConfiguration
を使って、SSLプロトコル、暗号スイート、およびクライアント認証オプションをカスタマイズすることができます。
プロパティ名 | 型 | 説明 |
---|---|---|
excludedCipherSpecs | String |
セッションネゴシエーションをする際に除外するSSL暗号方式名をカンマ区切りリストで指定します。このプロパティに指定する値は、SSLエンジンがサポートしている暗号方式を限定するために使用されます。マッチした暗号方式はすべて無効になります。 カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。 指定できるのは、JSSEリファレンスガイドの標準名にあるものだけです。 |
includedCipherSpecs | String |
セッションネゴシエーションをする際に使用するSSL暗号方式名をカンマ区切りリストで指定します。 このプロパティに指定する値は、SSLエンジンがサポートしている暗号方式を特定するために使用されます。マッチした暗号方式はすべて有効になります。 カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。 指定できるのは、JSSEリファレンスガイドの標準名にあるものだけです。 |
excludedProtocols | String |
セッションネゴシエーションをする際に除外するSSLプロトコル名をカンマ区切りリストで指定します。このプロパティに指定する値は、SSLエンジンがサポートしているSSLプロトコルを限定するために使用されます。マッチしたSSLプロトコルはすべて無効になります。 カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。 指定できるのは、JSSEリファレンスガイドの標準名にあるものだけです。 |
includedProtocols | String |
セッションネゴシエーションをする際に使用するSSLプロトコル名をカンマ区切りリストで指定します。 このプロパティに指定する値は、SSLエンジンがサポートしているSSLプロトコルを特定するために使用されます。マッチしたSSLプロトコルはすべて有効になります。 カンマ区切りリストのそれぞれの項目には単純な文字列あるいは正規表現を指定することができます。 指定できるのは、JSSEリファレンスガイドの標準名にあるものだけです。 |
needClientAuth | boolean |
サーバー側でクライアント認証が必要なときは、このプロパティにtrue を指定します。クライアントコンポーネント(SSLSocketAppender など)の場合はこのプロパティは無視されます。
|
wantClientAuth | boolean |
サーバー側でクライアント認証が必要なときは、このプロパティにtrue を指定します。
クライアントコンポーネント(SSLSocketAppender など)の場合はこのプロパティは無視されます。
|
トラストマネージャファクトリーの設定
TrustManagerFactory
を作成するために必要な設定は、TrustManagerFactoryFactoryBean
が提供します。JVMのデフォルトのファクトリーで十分なことがほとんどなので、普通は明示的に設定する必要がありません。
プロパティ名 | 型 | 説明 |
---|---|---|
algorithm | String |
TrustManagerFactory のアルゴリズム名を指定します。指定できるのは、JSSEリファレンスガイドの標準名にあるものだけです。このプロパティが設定されていない場合は、JVMのデフォルトのキーマネージャのアルゴリズムが使用されます。
|
provider | String |
安全な乱数生成器 を生成するために使われるJCAプロバイダー名を指定します。このプロパティが設定されていない場合は、JVMのデフォルトのJSSEプロバイダが使用されます。
|
例
JSSEシステムプロパティを使用する
JSSEシステムプロパティは、サーバーのX.509証明書を含むキーストアの場所とパスワードを指定するために使われます。また、クライアントコンポーネントが信頼できるサーバーかどうかを検証するために使用する、自己署名ルートCA証明書を含むトラストストアの場所とパスワードを指定します。
サーバーのキーストアを指定する
サーバーコンポーネントを実行する場合、サーバーの証明書を含むキーストアの場所とパスワードを指定しなければなりません。そのために、JSSEシステムプロパティを使用することができます。logbackの配布物に含まれるSimpleSSLSocketServer
を実行してみましょう。
java -DkeyStore=/etc/logback-server-keystore.jks \ -DkeyStorePassword=changeit -DkeyStoreType=JKS \ ch.qos.logback.net.SimpleSSLSocketServer 6000 /etc/logback-server-config.xml
JSSEシステムプロパティのkeyStoreに、サーバーのキーストアへのパスを指定するのを忘れないようにしてください。そのパスをlogback.xmlで指定するときはURLで指定します。
この例ではlogbackの配布物に含まれているスタンドアローンのサーバーアプリケーションを実行しました。どんなアプリケーションでも、logbackのサーバーコンポーネントを使う場合は同じようにシステムプロパティを指定できます。
クライアントのトラストストアを指定する
クライアントコンポーネントを使用する場合、信頼できるサーバーかどうかを検証するために使用する、ルートCA証明書を含むトラストストアの場所とパスワードを指定しなければなりません。そのためにJSSEシステムプロパティを使用することができます。logbackのSSL対応のクライアントコンポーネントを複数使用するcom.example.MyLoggingApplication
を実行してみましょう。
java -DtrustStore=/etc/logback-client-truststore.jks \ -DtrustStorePassword=changeit -DtrustStoreType=JKS \ com.example.MyLoggingApplication
JSSEシステムプロパティのtrustStoreに、トラストストアへのパスを指定するのを忘れないようにしてください。そのパスをlogback.xmlで指定するときはURLで指定します。
サーバーコンポーネントの自己署名証明書を生成して利用する
自己署名証明書を生成するには、Javaランタイム環境(JRE)に同梱されているkeytoolユーティリティを使用します。以下の手順では、サーバーコンポーネントの自己署名X.509証明書をキーストアに配置してから、クライアントコンポーネントの使用するトラストストアを作成します。
サーバコンポーネントの証明書の生成
次のコマンドを実行すると、server.keystoreというファイル名の自己署名クライアント証明書を生成します。
keytool -genkey -alias server -dname "CN=my-logging-server" \ -keyalg RSA -validity 365 -keystore server.keystore Enter keystore password: <Enter password of your choosing> Re-enter new password: <Re-enter same password> Enter key password for <my-logging-server> (RETURN if same as keystore password): <Press RETURN>
dnameに指定したmy-logging-serverは、正当なものであればどんなものでも構いません。サーバーを実行するホストの完全修飾ドメイン名にするとよいでしょう。validityには証明書の有効期限が切れるまでの日数を指定します。
本番環境の設定で重要になるのが、サーバー証明書を配置するキーストアにとても強力なパスワードを指定することです。このパスワードによって、サーバーの秘密鍵にアクセスできるのが限られた関係者だけであることを保証します。後の手順でこのパスワードが必要になるので、サーバーの設定をするときに心の中にメモしておいてください。
クライアントコンポーネント用のトラストストアの作成
クライアントコンポーネントの設定に使用するため、前の手順で作成したサーバーの証明書をキーストアからエクスポートして、トラストストアにインポートしましょう。次のコマンドを実行すると、証明書をエクスポートしてserver.truststoreというファイル名のトラストストアにインポートします。
keytool -export -rfc -alias server -keystore server.keystore \ -file server.crt Enter keystore password: <Enter password you chose for in previous step> keytool -import -alias server -file server.crt -keystore server.truststore Enter keystore password: <Enter password of your choosing> Re-enter new password: <Re-enter same password> Owner: CN=my-logging-server Issuer: CN=my-logging-server Serial number: 6e7eea40 Valid from: Sun Mar 31 07:57:29 EDT 2013 until: Mon Mar 31 07:57:29 EDT 2014 ... Trust this certificate? [no]: <Enter "yes">
最初のコマンドは、キーストアからエクスポートしたサーバー証明書(サーバーの秘密鍵ではありません)をserver.crtというファイル名で保存します。二つ目のコマンドは、サーバー証明書を含んだserver.truststoreという新しいトラストストアを作成します。
本番環境の設定で重要になるのが、トラストストアにとても強力な、そして、サーバーのキーストアに使用したパスワードとは異なるパスワードを設定することです。ここで指定したパスワードは、クライアントのアペンダーの設定に必要になるので心の中にメモしておいてください。
サーバコンポーネントの設定
server.keystoreをアプリケーションアーカイブの内部に取り込まなければならないかもしれません。キーストアはアプリケーションのクラスパスリソースとして扱うこともできますし、単にサーバーを実行するホストのファイルシステム上に配置することもできます。設定ファイルからキーストアの場所を指定するときは、classpath:
から始まるURLか、file:
から始まるURLのいずれかを指定します。設定ファイルを見てみましょう。
例:サーバーコンポーネントの設定
<configuration debug="true"> <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern> </encoder> </appender> <root level="DEBUG"> <appender-ref ref="CONSOLE" /> </root> <server class="ch.qos.logback.classic.net.server.SSLServerSocketReceiver"> <ssl> <keyStore> <location>classpath:server.keystore</location> <password>${server.keystore.password}</password> </keyStore> </ssl> </server> </configuration>
この例では、キーストアがアプリケーションのクラスパスのルートに配置されていることを前提としています。
また、キーストアのパスワードにはserver.keystore.passwordという変数を指定しています。こうしておけば、どの設定ファイルにもパスワードを書いておかなくてもよくなります。たとえば、アプリケーションが起動した後、ロギングシステムを設定する前にコンソールでパスワード入力を促すプロンプトを出すようにして、システムプロパティserver.keystore.passwordをプログラム的に設定すればよいのです。
クライアントコンポーネントの設定
クライアントとして振る舞うSSL対応のコンポーネントを使用するアプリケーションそれぞれについて、server.truststoreをアプリケーションアーカイブの内部に取り込まなければならないかもしれません。トラストストアはアプリケーションのクラスパスリソースとして扱うこともできますし、単にクライアントを実行するホストのファイルシステム上に配置することもできます。
設定ファイルからトラストストアの場所を指定するときは、classpath:
から始まるURLか、file:
から始まるURLのいずれかを指定します。
アペンダーの設定を見てみましょう。
例:アペンダーの設定
<configuration debug="true"> <appender name="SOCKET" class="ch.qos.logback.classic.net.SSLSocketAppender"> <remoteHost>${host}</remoteHost> <ssl> <trustStore> <location>classpath:server.truststore</location> <password>${server.truststore.password}</password> </trustStore> </ssl> </appender> <root level="DEBUG"> <appender-ref ref="SOCKET" /> </root> </configuration>
この例では、トラストストアがアプリケーションのクラスパスのルートに配置されていることを前提としています。
また、トラストストアのパスワードにはserver.truststore.passwordという変数を指定しています。 こうしておけば、どの設定ファイルにもパスワードを書いておかなくてもよくなります。 たとえば、アプリケーションが起動した後、ロギングシステムを設定する前にコンソールでパスワード入力を促すプロンプトを出すようにして、システムプロパティserver.truststore.passwordをプログラム的に設定すればよいのです。
SSLの設定を監査する
安全な通信が必要とされる環境では、SSLを使うコンポーネントがローカルセキュリティポリシーを満たしていることを検証するため、設定内容を監査しなければならない。logbackでは、自身を初期化している間に行われるSSLの設定について詳細なロギング情報を提供することでこれに対応しています。設定中でdebug
プロパティを使うことで、監査ログを有効化することができます。
<configuration debug="true"> ... </configuration>
debugプロパティを有効にすると、ロギングシステムの初期化中に行われるSSLの設定に関するあらゆる情報が得られるようになります。SSL設定の監査ログとして出力されるのは次のようなものです。
例:SSL設定の監査ログ
06:46:31,941 |-INFO in SSLServerSocketReceiver@4ef18d37 - SSL protocol 'SSL' provider 'SunJSSE version 1.6' 06:46:31,967 |-INFO in SSLServerSocketReceiver@4ef18d37 - key store of type 'JKS' provider 'SUN version 1.6': file:src/main/java/chapters/appenders/socket/ssl/keystore.jks 06:46:31,967 |-INFO in SSLServerSocketReceiver@4ef18d37 - key manager algorithm 'SunX509' provider 'SunJSSE version 1.6' 06:46:31,973 |-INFO in SSLServerSocketReceiver@4ef18d37 - secure random algorithm 'SHA1PRNG' provider 'SUN version 1.6' 06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: SSLv2Hello 06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: SSLv3 06:46:32,755 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled protocol: TLSv1 06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: SSL_RSA_WITH_RC4_128_MD5 06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: SSL_RSA_WITH_RC4_128_SHA 06:46:32,756 |-INFO in SSLParametersConfiguration@4a6f19d5 - enabled cipher suite: TLS_RSA_WITH_AES_256_CBC_SHA
ここで示した出力は大幅にカットしてありますが、一般的には次のようなものが含まれています。
- 完全なプロトコルのリスト
- プロバイダー
- アルゴリズム
- 暗号スイート
- キーストアやトラストストアの場所
この監査ログに取り扱いに注意の必要な情報が含まれることはありませんが、セキュリティのベストプラクティスとしては、本番環境の設定の妥当性が確認されたなら、監査ログは無効化するべきです。debug
プロパティを消すか、false
を指定すれば監査ログは無効化されます。
SSLの設定誤りを解決する
SSLの設定が間違っていると、普通ならクライアントとサーバーのコンポーネントの間でセッションを確立できなくなります。これは、クライアントがサーバーに接続しようとしてそれができなかったときにそれぞれで例外をスローすることによって明らかになります。
例外メッセージの内容は、見ているログによって異なります。それは、セッションネゴシエーション中に報告されるエラーの原因は、主にプロトコルの制限によるものだからです。したがって、セッションネゴシエーションの問題を解決するには、クライアントとサーバーの両方のログを調査しなければなりません。
サーバの証明書が使用できません
サーバーの証明書が使用できない時、サーバコンポーネントを起動すると次のような例外メッセージがログに出力されます。
javax.net.ssl.SSLException: No available certificate or key corresponds to the SSL cipher suites which are enabled
ほとんどの場合、サーバーの秘密鍵に対応する証明書を含むキーストアの場所を設定し忘れていることが原因です。
ソリューション
システムプロパティのキーストアか、サーバーコンポーネントのsslの設定でkeyStoreプロパティに、サーバ証明書を含むキーストアの場所を指定してください。あと、キーストアのパスワードも指定してください。
クライアントはサーバーを信頼できません
クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。
javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed
これは、クライアントがサーバーの提示した証明書を信頼できないと判断した場合に発生します。よくある原因としては、サーバーが自己署名サーバー証明書(あるいは内部の認証局で署名したサーバー証明書)を使っている上で、クライアントの使用するトラストストアにサーバーの自己署名証明書が含まれていない(あるいはサーバー証明書に署名したCAの信頼できるルート証明書が含まれていない)ことです。
サーバー証明書が失効している場合も同じような現象が発生します。サーバー側のログを見ることが出来るなら、クライアントが接続しようとするたびに次のような例外メッセージが現れているのがわかると思います。
javax.net.ssl.SSLHandshakeException: Received fatal alert: ...
例外メッセージの後半部分には、クライアントがサーバー証明書を拒否した理由を表すコードが現れます。
コード | 説明 |
---|---|
certificate_unknown |
クライアントのトラストストアが正しく設定されていないことを示しています。 |
certificate_expired |
サーバー証明書が失効しているので更新が必要なことを示しています。 |
certificate_revoked |
サーバー証明書に署名した認証局(CA)が、そのサーバー証明書を無効にしたことを示しています。サーバー証明書を更新しなければなりません。 |
ソリューション
サーバー側のログにcertificate_unknown
が現れているときは、システムプロパティトラストストアか、アペンダーのssl設定にtrustStoreプロパティを指定してください。サーバーの自己署名証明書か、証明書に署名したCAのルート証明書を格納したトラストストアの場所と、アクセスするためのパスワードを指定しなければなりません。
サーバー側のログにcertificate_expired
かcertificate_revoked
が現れているときは、新しいサーバー証明書が必要です。サーバーの設定で指定したキーストアの場所に、新しいサーバー証明書と対応する秘密鍵を配置してください。自己署名サーバー証明書を使っているときは、クライアントのアペンダーの設定に指定されているトラストストアにも新しい証明書を配置しなければなりません。
サーバーはクライアントを信頼できません
注: この問題が生じるのは、サーバーがクライアント証明書を要求するように明示的に設定している場合だけです(needClientAuthかwantClientAuthプロパティを指定したときです)。
クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。
javax.net.ssl.SSLHandshakeException: Received fatal alert: ...
例外メッセージの後半部分には、サーバーがクライアントの証明書を拒否した理由を表すコードが現れます。
コード | 説明 |
---|---|
certificate_unknown |
サーバのトラストストアが正しく設定されていないことを示しています。 |
certificate_expired |
クライアントの証明書が失効しているので更新が必要なことを示しています。 |
certificate_revoked |
クライアントの証明書に署名した認証局(CA)が、その証明書を無効にしたことを示しています。証明書を更新しなければなりません。 |
ソリューション
クライアント側のログにbad_certificate
が現れているときは、システムプロパティトラストストアかサーバーコンポーネントのssl設定のtrustStoreプロパティに、トラストストアの場所とパスワードを指定しなければなりません。トラストストアにはクライアントの自己署名証明書か、証明書に署名したCAのルート証明書が含まれていなければなりません。
サーバー側のログにcertifacte_expired
あるいはcertificate_revoked
が現れているときは、新しい証明書が必要です。クライアントの設定で指定したキーストアの場所に、新しい証明書と対応する秘密鍵を配置してください。自己署名証明書を使っているときは、サーバーの設定に指定されているトラストストアにも新しい証明書を配置しなければなりません。
クライアントとサーバー間でSSLプロトコルを合意できません
注: この問題が生じるのは、明示的にSSLプロトコルを除外したり指定している場合だけです 。
クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
サーバーのログに現れるメッセージのほうがわかりやすいです。
javax.net.ssl.SSLHandshakeException: SSLv2Hello is disabled
一方のピアで除外したプロトコルが、もう一方のピアでは除外されていない場合に発生します。
ソリューション
サーバーとクライアントの両方で、excludedProtocolsとincludedProtocolsに指定した値を確認してください。
クライアントとサーバー間で暗号スイートを合意できません
注: この問題が生じるのは、明示的に暗号スイートを除外したり指定している場合だけです 。
クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。
javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure
サーバーのログに現れるメッセージのほうがわかりやすいです。
javax.net.ssl.SSLHandshakeException: no cipher suites in common
クライアントとサーバーそれぞれの暗号スイート一覧について、1つも一致するものが無い場合に発生します。
ソリューション
サーバーとクライアントの両方で、excludedCipherSuitesとincludedCipherSuitesに指定した値を確認してください。