5.4. Web サービスクライアントのセキュリティー保護
概要
基本的な Camel CXF プロキシーのデモンストレーションでは、Web サービスクライアントは実際には src/test
ディレクトリーに JUnit テストとして実装されます。そのため、クライアントは Maven コマンド mvn test
を使用して簡単に実行できます。クライアントで SSL/TLS セキュリティーを有効にするため、テストクライアントの Java 実装が完全に置き換えられ、SSL/TLS 設定を含む Spring ファイルが src/test/resources/META-INF/spring
ディレクトリーに追加されます。クライアントを設定するために実行する必要のある手順を説明する前に、このセクションでクライアントの Java コードと Spring 設定の詳細について説明します。
暗黙的な設定
エンドポイントアドレスの URL スキームを https:
に変更する以外に、クライアントプロキシーで SSL/TLS セキュリティーを有効にする設定のほとんどは、Spring 設定の http:conduit
要素に含まれます。ただし、この設定をクライアントプロキシーに適用する方法は、混乱を招く可能性があります。それは、http:conduit
要素によってクライアントプロキシーが明示的に参照されず、クライアントプロキシーは http:conduit
要素を明示的に参照しないためです。http:conduit
要素とクライアントプロキシー間の接続は暗黙的に確立され、http:conduit で暗黙的に設定されたクライアントプロキシー が示すように、いずれも同じ WSDL ポートを参照します。
http:conduit で暗黙的に設定されたクライアントプロキシー
Element
以下のように、クライアントプロキシーと http:conduit
要素間の接続が確立されます。
-
クライアントは
http:conduit
要素が含まれる Spring 設定ファイルをロードおよび解析します。 -
http:conduit
Bean が作成されると、対応するエントリーがレジストリーに作成されます。これは、指定の WSDL ポート名の下に Bean への参照を保存します (名前は QName 形式で保存されます)。 -
JAX-WS クライアントプロキシーが作成されると、レジストリーをスキャンし、プロキシーの WSDL ポート名に関連付けられた
http:conduit
Bean を見つけられるかどうかを確認します。そのような Bean が見つかると、設定の詳細がプロキシーに自動的に挿入されます。
クライアント側で必要な証明書
クライアントは、src/main/resources/certs
ディレクトリーからの以下の clientKeystore.jks
キーストアファイルで設定されます。このキーストアには、次の 2 つのエントリーが含まれています。
- 信頼できる証明書エントリー
- サーバー証明書とクライアント証明書の両方を発行して署名した CA 証明書を含む信頼できる証明書エントリー。
- 秘密鍵の入力
- クライアント自身の X.509 証明書と秘密鍵を含む秘密鍵エントリー。実際、サーバーでは TLS ハンドシェイク中にクライアントによる証明書の送信が必要ないため、現在の例を実行するためにこの証明書が必ず必要なわけではありません (例5.2「SSL/TLS が有効になっている httpj:engine-factory 要素」 を参照)。
Spring 定義をクライアントにロード
サンプルクライアントは Spring コンテナーに直接デプロイされていませんが、セキュアな HTTP コンジットを定義するためにいくつかの Spring 定義が必要です。では、Spring コンテナーなしで Spring 定義を作成するにはどうすればよいでしょうか。org.apache.cxf.bus.spring.SpringBusFactory
クラスを使用すると、Spring 定義を Java ベースのクライアントに簡単に読み取ることができます。
以下のコードは、META-INF/spring/cxf-client.xml
ファイルから Spring 定義を読み取り、これらの定義を取り入れた Apache CXF Bus オブジェクトを作成する方法を示しています。
// Java import org.apache.cxf.bus.spring.SpringBusFactory; ... protected void startCxfBus() throws Exception { bf = new SpringBusFactory(); Bus bus = bf.createBus("META-INF/spring/cxf-client.xml"); bf.setDefaultBus(bus); }
クライアントプロキシーの作成
原則として、WSDL プロキシーの作成にはいくつかの方法があります。JAX-WS API を使用して WSDL ファイルの内容に基づいてプロキシーを作成したり、WS-WS API を使用して WSDL ファイルなしでプロキシーを作成できます。また、Apache CXF 固有のクラス JaxWsProxyFactoryBean
を使用してプロキシーを作成できます。
この SSL/TLS クライアントの場合、次の Java サンプルに示すように、最も便利なアプローチは、JAX-WS API を使用して WSDL ファイルを使用せずにプロキシーを作成することです。
// Java import javax.xml.ws.Service; import org.apache.camel.example.reportincident.ReportIncidentEndpoint; ... // create the webservice client and send the request Service s = Service.create(SERVICE_NAME); s.addPort( PORT_NAME, "http://schemas.xmlsoap.org/soap/", ADDRESS_URL ); ReportIncidentEndpoint client = s.getPort(PORT_NAME, ReportIncidentEndpoint.class);
この例では、JaxWsProxyFactoryBean
のアプローチを使用してプロキシーを作成できません。これは、このように作成されたプロキシーは Spring 設定ファイルで指定された HTTP コンジット設定を見つけることができないためです。
SERVICE_NAME
および PORT_NAME
定数は、例5.1「ReportIncidentEndpointService WSDL サービス」 で定義されているように、それぞれ WSDL サービスおよび WSDL ポートの QNames です。ADDRESS_URL
文字列には、プロキシー Web サービスアドレスと同じ値があり、以下のように定義されます。
private static final String ADDRESS_URL = "https://localhost:9080/camel-example-cxf-proxy/webservices/incident";
特に、このアドレスは URL スキーム https
で定義し、SSL/TLS 経由の HTTP を選択する必要があることに注意してください。
クライアントへの SSL/TLS セキュリティー追加手順
SSL/TLS セキュリティーが有効になっている JAX-WS クライアントを定義するには、次の手順を実行します。
テストケースとして Java クライアントを作成
例5.3「ReportIncidentRoutesTest Java クライアント」 は、JUnit テストケースとして実装された Java クライアントの完全なコードを示しています。このクライアントは、examples/camel-example-cxf-proxy
デモンストレーションの src/test/java/org/apache/camel/example/reportincident
サブディレクトリーに、既存のテストである ReportIncidentRoutesTest.java
を置き換えます。
クライアントを CamelInstallDir/examples/camel-example-cxf-proxy
デモに追加するには、src/test/java/org/apache/camel/example/reportincident
サブディレクトリーに移動し、既存の ReportIncidentRoutesTest.java
ファイルをバックアップの場所に移動し、新しい ReportIncidentRoutesTest.java
ファイルを作成して、例5.3「ReportIncidentRoutesTest Java クライアント」 のコードをこのファイルに貼り付けます。
例5.3 ReportIncidentRoutesTest Java クライアント
// Java package org.apache.camel.example.reportincident; import org.apache.camel.spring.Main; import org.apache.cxf.jaxws.JaxWsProxyFactoryBean; import org.junit.Test; import java.net.URL; import javax.xml.namespace.QName; import javax.xml.ws.Service; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBusFactory; import org.apache.camel.example.reportincident.ReportIncidentEndpoint; import org.apache.camel.example.reportincident.ReportIncidentEndpointService; import static org.junit.Assert.assertEquals; /** * Unit test of our routes */ public class ReportIncidentRoutesTest { private static final QName SERVICE_NAME = new QName("http://reportincident.example.camel.apache.org", "ReportIncidentEndpointService"); private static final QName PORT_NAME = new QName("http://reportincident.example.camel.apache.org", "ReportIncidentEndpoint"); private static final String WSDL_URL = "file:src/main/resources/etc/report_incident.wsdl"; // should be the same address as we have in our route private static final String ADDRESS_URL = "https://localhost:9080/camel-example-cxf-proxy/webservices/incident"; protected SpringBusFactory bf; protected void startCxfBus() throws Exception { bf = new SpringBusFactory(); Bus bus = bf.createBus("META-INF/spring/cxf-client.xml"); bf.setDefaultBus(bus); } @Test public void testRendportIncident() throws Exception { startCxfBus(); runTest(); } protected void runTest() throws Exception { // create input parameter InputReportIncident input = new InputReportIncident(); input.setIncidentId("123"); input.setIncidentDate("2008-08-18"); input.setGivenName("Claus"); input.setFamilyName("Ibsen"); input.setSummary("Bla"); input.setDetails("Bla bla"); input.setEmail("davsclaus@apache.org"); input.setPhone("0045 2962 7576"); // create the webservice client and send the request Service s = Service.create(SERVICE_NAME); s.addPort(PORT_NAME, "http://schemas.xmlsoap.org/soap/", ADDRESS_URL); ReportIncidentEndpoint client = s.getPort(PORT_NAME, ReportIncidentEndpoint.class); OutputReportIncident out = client.reportIncident(input); // assert we got a OK back assertEquals("OK;456", out.getCode()); } }
Spring 設定への http:conduit 要素の追加
例5.4「SSL/TLS が有効になっている http:conduit 要素」 は、ReportIncidentEndpoint
WSDL ポートの http:conduit
要素を定義する Spring 設定を示しています。http:conduit
要素は、指定の WSDL ポートを使用するクライアントプロキシーの SSL/TLS セキュリティーを有効にするように設定されます。
クライアントテストケースに Spring 設定を追加するには、src/test/resources/META-INF/spring
サブディレクトリーを作成し、お気に入りのテキストエディターを使用してファイル cxf-client.xml
を作成し、例5.4「SSL/TLS が有効になっている http:conduit 要素」 の内容をそのファイルに貼り付けます。
例5.4 SSL/TLS が有効になっている http:conduit 要素
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://camel.apache.org/schema/cxf" xmlns:sec="http://cxf.apache.org/configuration/security" xmlns:http="http://cxf.apache.org/transports/http/configuration" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://camel.apache.org/schema/cxf http://camel.apache.org/schema/cxf/camel-cxf.xsd http://cxf.apache.org/configuration/security http://cxf.apache.org/schemas/configuration/security.xsd http://cxf.apache.org/transports/http/configuration http://cxf.apache.org/schemas/configuration/http-conf.xsd "> <http:conduit name="{http://reportincident.example.camel.apache.org}ReportIncidentEndpoint.http-conduit"> <http:tlsClientParameters disableCNCheck="true" secureSocketProtocol="TLSv1"> <sec:keyManagers keyPassword="ckpass"> <sec:keyStore password="cspass" type="JKS" resource="certs/clientKeystore.jks" /> </sec:keyManagers> <sec:trustManagers> <sec:keyStore password="cspass" type="JKS" resource="certs/clientKeystore.jks" /> </sec:trustManagers> <sec:cipherSuitesFilter> <sec:include>.*_WITH_3DES_.*</sec:include> <sec:include>.*_WITH_DES_.*</sec:include> <sec:exclude>.*WITH_NULL.</sec:exclude>* <sec:exclude>.*DH_anon.</sec:exclude>* </sec:cipherSuitesFilter> </http:tlsClientParameters> </http:conduit> </beans>
上記の設定については、次の点に注意してください。
-
http:conduit
要素を定義するのに、http:
およびsec:
名前空間の接頭辞が必要です。xsi:schemaLocation
要素では、対応するhttp://cxf.apache.org/configuration/security
およびhttp://cxf.apache.org/transports/http/configuration
名前空間の場所を指定する必要もあります。 http:tlsClientParameters
要素のdisableCNCheck
属性はtrue
に設定されます。これは、サーバーの X.509 証明書のコモンネームがサーバーのホスト名と一致するかどうかをチェック しない ことを意味します。詳細は 付録A 証明書の管理 を参照してください。重要実稼働環境では、CN チェックを無効にすることはお勧め しません。
sec:keystore
要素では、クラスパス上の証明書を見つけるresource
属性を使用して証明書の場所が指定されます。Maven がテストを実行すると、証明書がsrc/main/resources/certs
ディレクトリーから読み取りできるように、クラスパスでsrc/main/resources
の内容を自動的に利用できるようにします。注記また、ファイルシステム内を検索する
file
属性を使用して、証明書の場所を指定するオプションもあります。ただし、resource
属性は、バンドルにパッケージ化されたアプリケーションでの使用に適しています。sec:cipherSuitesFilter
要素は、.*WITH_NULL.\*
および.*DH_anon.\*
に一致する暗号スイートを除外するように設定されています。これらの暗号化スイートは事実上不完全であり、通常の使用を目的としたものでは ありません。重要.*WITH_NULL.\*
および.*DH_anon.\*
に一致する暗号を常に除外することが推奨されます。-
secureSocketProtocol
属性は、サーバーのプロトコルと一致するように TLSv1 に設定し、SSLv3 プロトコルが使用されないようにする必要があります (POODLE セキュリティー脆弱性 (CVE-2014-3566))。
クライアントの実行
クライアントはテストケースとして定義されているため、標準の Maven テスト目標を使用してクライアントを実行できます。クライアントを実行するには、新しいコマンドウィンドウを開き、CamelInstallDir/examples/camel-example-cxf-proxy
ディレクトリーに移動し、以下の Maven コマンドを入力します。
mvn test
テストが正常に実行されると、OSGi コンソールウィンドウに次の出力が表示されます。
Incident was 123, changed to 456 Invoked real web service: id=456 by Claus Ibsen