第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で発行した証明書を使うことができるのですが、自己署名した証明書を使うこともできます。必要事項は次のとおりです。

  1. (自己署名証明書を使わない場合)サーバーコンポーネントに、サーバーの秘密鍵を含むキーストア、対応する証明書、およびCAの証明書チェーンを指定しなければなりません。
  2. クライアントコンポーネントに、信頼されたルート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アペンダーやレシーバー(SSLServerSocketReceiverSSLServerSocketAppenderSimpleSSLSocketServer)を使うときは、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プロパティでサーバーの証明書を含んだキーストアを指定します。

Groovyとして表示
<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プロパティを使うことができます。

Groovyとして表示
<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

KeyStoreの設定を指定します。キーストアには、少なくとも1つのX.509証明書(秘密鍵と対応する証明書、あるいはCA証明書チェーン)が含まれていなければなりません。この証明書がSSLのリモートピアに提示されます。

SSLクライアント(SSLSocketAppenderなど)の設定にkeyStoreプロパティが必要になるのは、リモートピアへの接続にクライアント認証が必要な場合だけです。

SSLサーバー(SimpleSSLSocketServerなど)のkeyStoreプロパティには、サーバー証明書を格納したキーストアを指定します。このプロパティが設定されていない場合は、JSSEのシステムプロパティjavax.net.ssl.keyStoreに、サーバーのキーストアの場所を指定しておかなければなりません。詳細はJSSEリファレンスガイドJSSEのカスタマイズを参照してください。

キーストアの設定については後述します。

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のリモートピアの同一性を検証するために使うKeyStoreの設定を指定します。キーストアには少なくとも1つ以上のトラストアンカーが含まれていなければなりません。"信頼できる"と印の付けられた自己署名証明書のことです。一般的に、トラストストアには自己署名CA証明書が含まれています。

このプロパティで指定したトラストストアは、JSSEのjava.net.ssl.trustStoreシステムプロパティで指定された全てのトラストストアを上書きします。詳細はJSSEリファレンスガイド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_expiredcertificate_revokedが現れているときは、新しいサーバー証明書が必要です。サーバーの設定で指定したキーストアの場所に、新しいサーバー証明書と対応する秘密鍵を配置してください。自己署名サーバー証明書を使っているときは、クライアントのアペンダーの設定に指定されているトラストストアにも新しい証明書を配置しなければなりません。

サーバーはクライアントを信頼できません

注: この問題が生じるのは、サーバーがクライアント証明書を要求するように明示的に設定している場合だけです(needClientAuthwantClientAuthプロパティを指定したときです)。

クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。

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

一方のピアで除外したプロトコルが、もう一方のピアでは除外されていない場合に発生します。

ソリューション

サーバーとクライアントの両方で、excludedProtocolsincludedProtocolsに指定した値を確認してください。

クライアントとサーバー間で暗号スイートを合意できません

注: この問題が生じるのは、明示的に暗号スイートを除外したり指定している場合だけです

クライアントがサーバに接続しようとするとき、次のような例外メッセージがログに現れます。

javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

サーバーのログに現れるメッセージのほうがわかりやすいです。

javax.net.ssl.SSLHandshakeException: no cipher suites in common

クライアントとサーバーそれぞれの暗号スイート一覧について、1つも一致するものが無い場合に発生します。

ソリューション

サーバーとクライアントの両方で、excludedCipherSuitesincludedCipherSuitesに指定した値を確認してください。