Java Web サービス: Metro での WS-Security

Metro Web サービス・フレームワークで WS-Security に対処する方法を学ぶ

Metro Web サービス・スタックは JAXB 2.x および JAX-WS 2.x Java™ 標準のリファレンス実装をベースとしていますが、WS-* SOAP 拡張技術全般に対するサポートも組み込まれています。Dennis Sosnoski の連載「Java Web サービス」の今回の記事では、Metro での WS-Security の構成とその使用方法について説明します。

Dennis Sosnoski, Architecture Consultant and Trainer, Sosnoski Software Associates Ltd

Author photoDennis Sosnoski は Java ベースの XML および Web サービスを専門とするコンサルタント兼トレーナーです。専門家としてのソフトウェア開発経験は 30 年以上に渡り、この 10 年間はサーバー・サイドの XML 技術や Java 技術に注力しています。オープンソースのJiBX XML Data Binding フレームワークや、それに関連した JiBX/WS Web サービス・フレームワークの開発リーダーを務め、さらに Apache Axis2 Web サービス・フレームワークのコミッターでもあります。彼は JAX-WS 2.0 および JAXB 2.0 仕様のエキスパート・グループの一員でもありました。



2009年 12月 01日

Metro の紹介」で説明したように、Metro Web サービス・フレームワークのコアとなっているのは、JAXB 2.x データ・バインディングおよび JAX-WS 2.x Web サービス標準のリファレンス実装です。しかし、JAXB と JAX-WS だけでは基本的な Web サービスのサポートしか提供できません。SOAP がエンタープライズ環境で機能するようにアップグレードする WS-* 技術は JAX-WS の対象外です。そのため、WS-* 技術のサポートを追加するには他のソフトウェア・コンポーネントが必要となります。

この連載について

Web サービスは、エンタープライズ・コンピューティングにおいて Java 技術が担う重大な役割の一部です。この連載では、XML および Web サービスのコンサルタントである Dennis Sosnoski が、Web サービスを使用する Java 開発者にとって重要になる主要なフレームワークと技術について説明します。この連載から、現場での最新の開発情報を入手して、それらを皆さんのプログラミング・プロジェクトにどのように利用できるかを知っておいてください。

Metro の主要な追加コンポーネントは、WSIT (Web Services Interoperability Technologies) です。WSIT は、当初 Project Tango として知られていたサービス・スタックの現在の形で、この Sun の取り組みでは、セキュリティーと信頼性の高いメッセージングを含めた WS-* 機能の相互運用性を、Microsoft .NET プラットフォームで確保することを目的としています。そのような背景を持つ WSIT が Metro に WS-SecurityPolicy、WS-Trust、WS-SecureConversation、WS-ReliableMessaging などのサポートを提供し、別の追加コンポーネントである XWSS (XML and WebServices Security Project) が WS-Security の実際のランタイム処理を実装します。

この記事では、Metro を (Glassfish サーバー外部の) スタンドアロンの Web アプリケーションとして使用した場合の WS-Security の使用方法と構成方法を説明します。記事に記載するサンプルの完全なソース・コードは「ダウンロード」から入手してください。このサンプル・アプリケーションで実装するのは、この連載で前にも使用した単純なライブラリー管理サービスです。

WSIT の基本

WSIT の役割は、Metro ランタイムをサービスの WS-Policy 仕様 (WS-SecurityPolicy などの WS-Policy の拡張も含む) に合わせて構成することです。標準的な WS-Policy 拡張の他、Metro はセキュリティーの処理に必要なユーザー情報 (キーストアの場所やパスワードなど) を構成するために、ポリシー文書に含まれるカスタム拡張も使用します。

WSIT はポリシー情報を WSDL (Web Services Description Language) のサービス記述から取得します。クライアント・サイドでは、このことが混乱の種になりかねません。WSIT 構成に使用する WSDL は、JAX-WS のサービス定義に使用する WSDL とは別だからです。「Metro の紹介」で説明したように、JAX-WS クライアントを構成するための WSDL は、サービスから直接取得されるか、あるいは JAX-WS コードを生成するときに指定した場所から取得されます。WSIT が使用する WSDL のファイル名は決められており (ただし、このファイルは <wsdl:import> を使用して、完全な WSDL が記述された別のファイルを参照することもできます)、この WSDL には常にクラス・パスからアクセスします。

サーバー・サイドでは、WSIT が必要とする WSDL が WEB-INF/sun-jaxws.xml 構成ファイル (「Metro の紹介」で説明) で指定された場所に提供されていなければなりません。この WSDL には、WSIT 用にユーザー情報を構成するために使用するカスタム拡張が含まれている必要があります。ただし、これらのカスタム拡張は、サービス・エンドポイントへの HTTP GET リクエストに対するレスポンスで提供される WSDL からは削除されます。

WSIT ユーザー情報の構成に使用されるカスタム拡張は、クライアント・サイドとサーバー・サイドで同じように見えますが、拡張要素に使用される XML 名前空間は異なります。この名前空間は、クライアントでは http://schemas.sun.com/2006/03/wss/client、サーバーでは http://schemas.sun.com/2006/03/wss/server となります。


Metro での UsernameToken

Axis2 WS-Security の基本」では、UsernameToken の単純な例を用いて Axis2/Rampart での WS-Security を紹介しました。UsernameToken は、WS-Security でユーザー名とパスワードのペアを表す標準の手段です。パスワード情報は平文として送信することも、ハッシュ値として送信することもできます (一般に、本番環境では TLS (Transport Layer Security) または WS-Security 暗号化を組み合わせない限り、平文としてパスワード情報を送信することはしませんが、テストには平文を使用したほうが便利です)。

Metro で単純な平文の UsernameToken の例を実装するには、適切な WS-Policy/WS-SecurityPolicy 構成が組み込まれた WSDL サービス定義を使用する必要があります。リスト 1 は、「Metro の紹介」で使用した基本的な WSDL サービス定義を編集したバージョンです。今回は、ポリシー情報が組み込まれており、クライアントからサーバーへリクエストを送信する際に UsernameToken が必要となります。太字で記載されているのが、<wsdl:binding> 内のポリシー参照、およびポリシーそのものです。

リスト 1. 平文による UsernameToken WSDL
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
    xmlns:wns="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
    ...
  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>
  ...

  <wsdl:portType name="Library">
    <wsdl:operation name="getBook">
      <wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>
    ...
  </wsdl:portType>

  <wsdl:binding name="LibrarySoapBinding" type="wns:Library">
  
    <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        URI="#UsernameToken"/>

    <wsdlsoap:binding style="document"
        transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getBook">
      <wsdlsoap:operation soapAction="urn:getBook"/>
      <wsdl:input name="getBookRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getBookResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    ...
  </wsdl:binding>

  <wsdl:service name="MetroLibrary">
    <wsdl:port binding="wns:LibrarySoapBinding" name="library">
      <wsdlsoap:address location="http://localhost:8080/metro-library-username"/>
    </wsdl:port>
  </wsdl:service>
  
  <!-- Policy for Username Token with plaintext password, sent from client to
    server only -->
  <wsp:Policy wsu:Id="UsernameToken" xmlns:wsu=
     "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
     xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
    <wsp:ExactlyOne>
      <wsp:All>
        <sp:SupportingTokens
            xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <wsp:Policy>
            <sp:UsernameToken sp:IncludeToken=".../IncludeToken/AlwaysToRecipient"/>
          </wsp:Policy>
        </sp:SupportingTokens>
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>

</wsdl:definitions>

リスト 1 の WSDL は、サービスへのアクセスを要求する全員に、セキュリティーの処理に関して行わなければならない内容を伝えるものです。このように WSDL サービス定義を用意したら、次に必要となる作業は、クライアント・サイドとサーバー・サイド両方のポリシー情報に WSIT カスタム拡張を追加することです。カスタム拡張にユーザー構成の詳細を含めることで、セキュリティー処理がどのように実装されるかを指定します。これらのカスタム拡張は、WSDL の <wsp:Policy> コンポーネントに組み込まれます。次のセクションでは、クライアント・サイドとサーバー・サイドそれぞれの場合でのカスタム拡張の例を紹介します。

クライアント・サイドの使用方法

クライアント・サイドで WSIT の構成に使用されるのは、wsit-client.xml という (固定の) 名前が付いたファイルです。このファイルは、クラス・パス上のルート・ディレクトリー (パッケージの外部)、またはクラス・パスに含まれるディレクトリーの META-INF サブディレクトリーに置かれていなければなりません。さらに、wsit-client.xml は WSDL 文書として、完全なサービス WSDL を直接提供するか、あるいは <wsdl:import> を使用して別の WSDL サービス定義を参照する必要があります。いずれにしても、WSDL には完全な WS-Policy/WS-SecurityPolicy 要件および WSIT 構成拡張の両方が組み込まれていなければなりません。

リスト 1 に記載した WSDL のポリシー・セクションに、クライアント・サイドの UsernameToken サポートを構成する WSIT カスタム拡張を追加したコードをリスト 2 に示します。太字で示されている <wssc:CallbackHandlerConfiguration> 要素とその子要素が、カスタム拡張です。2 つの子要素 <wssc:CallbackHandler> のうち、最初の子要素はユーザー名のコールバック・クラス (name="usernameHandler")、2 番目の子要素はパスワードのコールバック・クラス (name="passwordHandler") を定義します。ここで指定されたクラスは、javax.security.auth.callback.CallbackHandler インターフェースを実装する必要があります。

リスト 2. クライアント・サイドの WSIT 拡張を追加した UsernameToken ポリシー
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    wsu:Id="UsernameToken">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken=".../IncludeToken/AlwaysToRecipient"/>
        </wsp:Policy>
      </sp:SupportingTokens>
      <wssc:CallbackHandlerConfiguration wspp:visibility="private"
          xmlns:wssc="http://schemas.sun.com/2006/03/wss/client"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy">
        <wssc:CallbackHandler name="usernameHandler"
            classname="com.sosnoski.ws.library.metro.UserPassCallbackHandler"/>
        <wssc:CallbackHandler name="passwordHandler"
            classname="com.sosnoski.ws.library.metro.UserPassCallbackHandler"/>
      </wssc:CallbackHandlerConfiguration>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

リスト 2 では、両方のコールバックが同じクラスを使用しています。このコールバック・クラスのコードは、リスト 3 に記載するとおり、各コールバック・リクエストのタイプをチェックして該当する値を設定するだけにすぎません。

リスト 3. クライアントのコールバックのコード
public class UserPassCallbackHandler implements CallbackHandler
{
    public void handle(Callback[] callbacks) throws UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof NameCallback) {
                ((NameCallback)callbacks[i]).setName("libuser");
            } else if (callbacks[i] instanceof PasswordCallback) {
                ((PasswordCallback)callbacks[i]).setPassword("books".toCharArray());
            } else {
                throw new UnsupportedCallbackException(callbacks[i],
                    "Unsupported callback type");
            }
        }
    }
}

ユーザー名とパスワードのいずれにしても、コールバックを使用しなければならないということはありません。この 2 つに固定値を使用している場合は、代わりに classname="xxx" 属性を default="yyy" 属性に置き換えて (属性の値は実際のユーザー名またはパスワードとなります)、ユーザー名とパスワードそれぞれに対応する <wssc:CallbackHandler> 要素に直接その値を設定することもできます。

サーバー・サイドの使用方法

サーバー・サイドでは、WSIT 構成情報は WSDL サービス定義に含まれていなければなりません。「Metro の紹介」でも説明しましたが、サービス WSDL の場所は、サービス WAR ファイル内の WEB-INF/sun-jaxws.xml にパラメーターとして指定することができます。WSIT 機能を使用していなければ、この WSDL はオプションです。その場合、WSDL は実行時に自動的に生成されることになります。WSIT 機能を使用している場合には WSDL が必須であり、この WSDL ファイルに、サービスで使用する機能の WSIT を構成する上で必要なすべてのカスタム要素が組み込まれている必要があります。リスト 4 に記載するのはリスト 1 の WSDL のポリシー・セクションに、今度はサーバー・サイドの UsernameToken サポートを構成する WSIT カスタム拡張 (太字で記載) を追加したコードです。

リスト 4. サーバー・サイドの WSIT 拡張を追加した UsernameToken ポリシー
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
    "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
    wsu:Id="UsernameToken">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:SupportingTokens
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          <sp:UsernameToken sp:IncludeToken=".../IncludeToken/AlwaysToRecipient"/>
        </wsp:Policy>
      </sp:SupportingTokens>
      <wsss:ValidatorConfiguration wspp:visibility="private"
          xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy">
        <wsss:Validator name="usernameValidator"
            classname="com.sosnoski.ws.library.metro.PasswordValidator"/>
      </wsss:ValidatorConfiguration>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

リスト 4 のサーバー・サイドの WSIT 拡張は、<wsss:ValidatorConfiguration> 要素とその子要素 <wsss:Validator> という形をとり、バリデーター・コールバックとして使用されるクラスを指定しています。このクラスのコードはリスト 5 に記載するとおりで、com.sun.xml.wss.impl.callback.PasswordValidationCallback.PasswordValidator インターフェースを実装する必要があります。この例のバリデーターは、提供されたユーザー名とパスワードを固定値と照らし合わせてチェックしているだけですが、データベース・ルックアップやその他のメカニズムも簡単に使用することができます。

リスト 5. サーバーのコールバックのコード
public class PasswordValidator implements PasswordValidationCallback.PasswordValidator
{
    public boolean validate(Request request) throws PasswordValidationException {
        PasswordValidationCallback.PlainTextPasswordRequest ptreq 
            = (PasswordValidationCallback.PlainTextPasswordRequest)request;
        return "libuser".equals(ptreq.getUsername()) &&
            "books".equals(ptreq.getPassword());
    }
}

Metro のポリシー・ツール

Metro/WSIT を使用する場合、Axis2/Rampart と同じく、独自の WSDL に自分で構成情報を追加しなければなりません。この連載で以前 Axis2/Rampart を取り上げた記事では、ビルド・プロセス中に必要に応じて変更した WSDL を生成する特殊なポリシー・ツールを使用しました。この記事のサンプル・コードのダウンロードに、Metro/WSIT のニーズに合わせて設計された同じようなツールが含まれています。

これに該当するツールは、サンプル・コードの mergetool ディレクトリーにある com.sosnoski.ws.MergeTool アプリケーションです。MergeTool はデータをターゲット XML 文書にマージし、ネストされた XML 要素を突き合わせてマージ対象のデータを見つけ、ターゲット文書内でのマージ・ポイントを決定します。サンプル・アプリケーションの build.xml はこの MergeTool を使って、クライアントまたはサーバーの WSIT 構成情報をサービスの WSDL に追加しています。MergeTool は皆さん独自のアプリケーションでも使用することができます。mergetool/readme.txt ファイルには基本的な使用方法に関する情報が記載されている他、付属のビルドでどのように使用されているかも確認することができます。

<wsss:ValidatorConfiguration> が提供されていない場合、Metro は Web アプリケーション・コンテナー (サーブレット・サポートを提供する Web サーバー) が提供する許可メカニズムを使用します。

サンプル・コードのビルドおよび実行

サンプル・コードを試すには、最新バージョンの Metro (サンプル・コードは Metro 1.5 リリースでテストしました) をダウンロードしてシステムにインストールする必要があります (「参考文献」を参照)。さらに、サンプル・コードのダウンロードを解凍することで作成される root ディレクトリー内にある build.properties ファイルを編集し、metro-home プロパティーの値を Metro システムへのパスに変更してください。異なるシステムのサーバーや、サーバーの異なるポートでテストする予定であれば、host-namehost-port も変更する必要があります。

提供されている Ant build.xml を使用してサンプル・アプリケーションをビルドするには、コンソールでダウンロード・コードの root ディレクトリーを開き、ant と入力します。これにより、最初に JAX-WS wsimport ツール (Metro ディストリビューションに含まれています) が起動された後、クライアントとサーバーがコンパイルされ、最後にサーバー・コードが WAR としてパッケージ化されます (このプロセスで、クライアントおよびサーバーの WSIT 構成情報が含まれるサービス WSDL の別バージョンが生成されます)。Metro 1.5 に含まれている wsimport バージョンでは、名前「tns:BookInformation」を「型定義」コンポーネントに解決できないことを知らせる src-resolve: Cannot resolve the name 'tns:BookInformation' to a(n) 'type definition' component という警告メッセージが出されることに注意してください (これは、WSDL に組み込まれたスキーマ処理ツールの特異な振る舞いによるものです)。

生成されたこの metro-library.war ファイルをテスト・サーバーにデプロイし、コンソールで ant run と入力すれば、サンプル・クライアントを実行してみることができます。サンプル・クライアントはサーバーに対して一連のリクエストを行い、それぞれのリクエストごとに簡潔な結果を出力します。


Metro での署名と暗号化

UsernameToken はその単純さから、WS-Security の使い方を学ぶにはもってこいですが、これは一般的な WS-Security の使用方法ではありません。大抵は、署名か暗号化、あるいはその両方を使用することになります。リスト 6 は、署名と暗号化の両方を使用して編集した WSDL の例です (この例のベースとなっているのは、「Axis2 WS-Security による署名および暗号化」で使用した WSDL です。一般的な WS-Security の署名と暗号化についての詳細は、この以前の記事を参照してください)。WSDL のポリシーの部分は太字で示されています。

リスト 6. WSDL の署名/暗号化
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://ws.sosnoski.com/library/wsdl"
    xmlns:wns="http://ws.sosnoski.com/library/wsdl"
    xmlns:tns="http://ws.sosnoski.com/library/types"
    xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
    xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/">
  <wsdl:types>
    ...
  </wsdl:types>

  <wsdl:message name="getBookRequest">
    <wsdl:part element="wns:getBook" name="parameters"/>
  </wsdl:message>
  ...

  <wsdl:portType name="Library">
    <wsdl:operation name="getBook">
      <wsdl:input message="wns:getBookRequest" name="getBookRequest"/>
      <wsdl:output message="wns:getBookResponse" name="getBookResponse"/>
    </wsdl:operation>
    ...
  </wsdl:portType>

  <wsdl:binding name="LibrarySoapBinding" type="wns:Library">
  
    <wsp:PolicyReference xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy"
        URI="#SignEncr"/>

    <wsdlsoap:binding style="document"
        transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="getBook">
      <wsdlsoap:operation soapAction="urn:getBook"/>
      <wsdl:input name="getBookRequest">
        <wsdlsoap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="getBookResponse">
        <wsdlsoap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    ...
  </wsdl:binding>

  <wsdl:service name="MetroLibrary">
    <wsdl:port binding="wns:LibrarySoapBinding" name="library">
      <wsdlsoap:address location="http://localhost:8080/metro-library-username"/>
    </wsdl:port>
  </wsdl:service>
  
  <!-- Policy for first signing and then encrypting all messages, with the certificate
   included in the message from client to server but only a thumbprint on messages from
   the server to the client. -->
  <wsp:Policy wsu:Id="SignEncr" xmlns:wsu=
     "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
     xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy">
    <wsp:ExactlyOne>
      <wsp:All>
        <sp:AsymmetricBinding
            xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <wsp:Policy>
            <sp:InitiatorToken>
              <wsp:Policy>
                <sp:X509Token sp:IncludeToken=".../IncludeToken/AlwaysToRecipient">
                  <!-- Added this policy component so Metro would work with the same
                   certificates (and key stores) used in the Axis2/Rampart example. -->
                  <wsp:Policy>
                    <sp:RequireThumbprintReference/>
                  </wsp:Policy>
                </sp:X509Token>
              </wsp:Policy>
            </sp:InitiatorToken>
            <sp:RecipientToken>
              <wsp:Policy>
                <sp:X509Token sp:IncludeToken=".../IncludeToken/Never">
                  <wsp:Policy>
                    <sp:RequireThumbprintReference/>
                  </wsp:Policy>
                </sp:X509Token>
              </wsp:Policy>
            </sp:RecipientToken>
            <sp:AlgorithmSuite>
              <wsp:Policy>
                <sp:TripleDesRsa15/>
              </wsp:Policy>
            </sp:AlgorithmSuite>
            <sp:Layout>
              <wsp:Policy>
                <sp:Strict/>
              </wsp:Policy>
            </sp:Layout>
            <sp:IncludeTimestamp/>
            <sp:OnlySignEntireHeadersAndBody/>
          </wsp:Policy>
        </sp:AsymmetricBinding>

        <sp:SignedParts
            xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <sp:Body/>
        </sp:SignedParts>
        <sp:EncryptedParts
            xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
          <sp:Body/>
        </sp:EncryptedParts>
      </wsp:All>
    </wsp:ExactlyOne>
  </wsp:Policy>

</wsdl:definitions>

リスト 6 の WSDL が、前の Axis2/Rampart の例で使用した WSDL と顕著に異なっている点は唯一、ポリシーが <sp:InitiatorToken> コンポーネントに追加され、X.509 証明書がメッセージに含まれていない場合には、拇印の参照を使用することを要件としていることだけです。この追加は、Metro と Axis2ではデフォルトでの参照の処理方法が異なることから必要となっています。

クライアント (WS-SecurityPolicy で言うイニシエーター) がメッセージを送信するときには、クライアントの X.509 証明書がメッセージの一部として送信されます (<sp:InitiatorToken/wsp:Policy/sp:X509Token> 要素の sp:IncludeToken=".../IncludeToken/AlwaysToRecipient" 属性で指定されているため)。サーバーはこの証明書を使用して署名を検証します。サーバーがクライアントに応答する際には、暗号化処理で使用されたクライアントの証明書と同一の証明書を参照する必要があります。他の手段が指定されてない場合、Axis2/Rampart は証明書の識別にデフォルトで拇印の参照を使用するように設定します。一方 Metro/WSIT でのデフォルト設定では、SKI (Subject Key Identifier) という別の手段が使用されます。Axis2/Rampart の例で使用した証明書は SKI をサポートしないフォームであったため、これらの証明書はデフォルトでは Metro/WSIT で機能しません。そこで、<sp:RequireThumbprintReference/> 要素をポリシーに追加して、Metro/WSIT に証明書ではなく拇印の参照を使用するように指示しているというわけです。

ポリシーをこのように変更することによって、前の Axis2/Rampart の例で使用したものと同じ証明書とキーストアをこの例でも使用できるようになります。つまり、相互運用性を確認する便利な方法として、Axis2/Rampart クライアントの例を Metro/WSIT サーバーで使用することも、その逆も可能であるということです。それぞれの場合にテスト対象のクライアントに渡すターゲット・パスを変更することによってこの方法を試してみると、ほとんどのメッセージは問題なく交換されるはずですが、動作には以下の「相互運用性の問題」セクションで説明する 1 つの違いがあります。

UsernameToken の例と同じく、WSIT にはクライアント・サイドとサーバー・サイドの両方で、追加の構成詳細を指定するポリシー情報のカスタム拡張が必要です。

クライアント・サイドの使用方法

リスト 7 に、この例でのクライアント・サイドの処理を構成するために WSDL ポリシーに追加する WSIT カスタム拡張を記載します。これらの太字で示されたカスタム拡張は、署名と暗号化に必要なキーストア (クライアントの秘密鍵とそれに対応する証明書が含まれます) とトラスト・ストア (サーバーの証明書が含まれます) を構成します。

リスト 7. クライアント・サイドの WSIT 拡張を追加した署名および暗号化ポリシー
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsu:Id="SignEncr">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:AsymmetricBinding xmlns:sp=
      "http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          ...
        </wsp:Policy>
      </sp:AsymmetricBinding>
      <sp:SignedParts
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <sp:Body/>
      </sp:SignedParts>
      <sp:EncryptedParts
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <sp:Body/>
      </sp:EncryptedParts>
    
      <wssc:KeyStore alias="clientkey" keypass="clientpass"
          location="client.keystore" storepass="nosecret"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy" wspp:visibility="private"
          xmlns:wssc="http://schemas.sun.com/2006/03/wss/client"/>
      <wssc:TrustStore location="client.keystore" peeralias="serverkey"
          storepass="nosecret" xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy"
          wspp:visibility="private"
          xmlns:wssc="http://schemas.sun.com/2006/03/wss/client"/>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

リスト 7 の WSIT カスタム拡張は、キーストアとトラスト・ストア (この例の場合、この 2 つは同じファイルです) にアクセスするために必要なすべてのパラメーターを指定します。これにはクライアントの秘密鍵にアクセスするためのパスワードも含まれます (<wssc:KeyStore> 要素の keypass="clientpass" 属性)。また、次のセクションで説明するように、パスワード情報にコールバックを使用することもできます。

名前付きキーストアとトラスト・ストアは、クラス・パスに含まれるディレクトリーの META-INFサブディレクトリー内に置かれていなければなりません。これらのファイルを指定するには、単なるファイル名ではなく絶対ファイル・パスを使用することも可能です。その場合、ファイルシステム上の任意の固定ロケーションにファイルを配置することができます (クライアントの場合、WSIT カスタム拡張が含まれる WSDL には wsit-client.xml という固定の名前を使用し、その配置場所はクラス・パスのルート・ディレクトリーか、クラス・パスのルート・ディレクトリーの META-INF サブディレクトリーでなければならないことを思い出してください)。

サーバー・サイドの使用方法

WSDL に追加するサーバー・サイドの WSIT カスタム拡張はリスト 8 のとおりです (同じく太字で示されています)。この場合、<wsss:KeyStore>keypass 属性では (リスト 7 のクライアント・サイドの例のように) 実際のパスワードの値を指定するのではなく、クラス名を指定しています。この手法を使用すると、参照されるクラスは javax.security.auth.callback.CallbackHandler インターフェースを実装することになるため、WSIT コードが秘密鍵のパスワードにアクセスしなければならないときに WSIT コードから呼び出されます。これと同じ手法を使用して、storepass の値としてパスワードではなくクラス名を指定することもできます。

リスト 8. サーバー・サイドの WSIT 拡張を追加した署名および暗号化ポリシー
<wsp:Policy xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsu=
"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" 
wsu:Id="SignEncr">
  <wsp:ExactlyOne>
    <wsp:All>
      <sp:AsymmetricBinding xmlns:sp=
      "http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <wsp:Policy>
          ...
        </wsp:Policy>
      </sp:AsymmetricBinding>
      <sp:SignedParts
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <sp:Body/>
      </sp:SignedParts>
      <sp:EncryptedParts
          xmlns:sp="http://docs.oasis-open.org/ws-sx/ws-securitypolicy/200702">
        <sp:Body/>
      </sp:EncryptedParts>
    
      <wsss:KeyStore alias="serverkey"
          keypass="com.sosnoski.ws.library.metro.KeystoreAccess"
          location="server.keystore" storepass="nosecret"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy" wspp:visibility="private"
          xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"/>
      <wsss:TrustStore location="server.keystore" storepass="nosecret"
          xmlns:wspp="http://java.sun.com/xml/ns/wsit/policy" wspp:visibility="private"
          xmlns:wsss="http://schemas.sun.com/2006/03/wss/server"/>
    </wsp:All>
  </wsp:ExactlyOne>
</wsp:Policy>

リスト 9 に、この例に使用する CallbackHandler インターフェースの実装を記載します。

リスト 9. サーバーのキーストア・パスワード・コールバックのコード
public class KeystoreAccess implements CallbackHandler
{
    public void handle(
       Callback[] callbacks) throws IOException, UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            Callback callback = callbacks[i];
            if (callback instanceof PasswordCallback) {
                ((PasswordCallback)callback).setPassword("serverpass".toCharArray());
            } else {
                throw new UnsupportedCallbackException(callback, "unknown callback");
            }
        }
    }
}

サンプル・コードのビルドおよび実行

署名および暗号化の例で使用するビルド・ステップは、UsernameToken の例と同じです。ただし、build.properties ファイルについては、variant-name=signencr を使用するように変更してください (UsernameToken の例では、この値は username となっています)。

相互運用性の問題

Axis2/Rampart クライアントと Metro/WSIT サーバーを使用しているときに (またはその逆)、ライブラリーに既に存在する本と同じ ISBN (International Standard Book Number) を持つ本をクライアントが追加しようとすると問題が発生することがあります。この場合、サービスは通常の SOAP レスポンス・メッセージではなく Fault を返します。このような試行が行われた場合、Axis2/Rampart 1.5.x リリースでは WSDL に必要な通常の署名および暗号化の処理を正常に行いますが、Metro/WSIT 1.5 リリースではこの処理を行いません。そのため、クライアント上では失敗という結果になります。これは WSIT コードのエラーなので、次回の Metro リリースでは修正されるはずです。

Rampart にも Rampart 1.5 リリースまでは同じバグがあったため、以前の Axis2/Rampart バージョンを使ってテストを実行すれば問題は起こらないはずです。


次回の Metro の記事

WS-SecurityPolicy に対する Metro の WSIT サポートによって、(キーストアと秘密鍵のパスワードを含め) ユーザー名とパスワードなどのパラメーターを直接構成することも、コールバックを使用して必要に応じてこれらの値を取得することも可能になります。また、サーブレット・コンテナーで許可を処理するか、または独自のコールバックによってサーバー上でユーザー名とパスワードの組み合わせを確認するかを選択することも可能です。この柔軟性により、Metro は各種のアプリケーションに伴うニーズに簡単に対応することができます。WSIT/XWSS WS-Security サポートは Axis2 および Rampart では別個のコンポーネントとなっていますが (コンポーネント独自のリリース・サイクルがあり、通常はコア・コンポーネントのバージョン間の互換性はありません)、Metro では便利なことに統合コンポーネントとして付属しています。

欠点としては、NetBeans IDE と Glassfish アプリケーション・サーバーとを組み合わせて使用する場合に比べ、Metro/WSIT をスタンドアロンとして使用し、直接構成する際にはその方法についての情報が少ないことです。必要なオプションのほとんどは、ブログの投稿や E メールの交換でしか文書化されていません (「参考文献」を参照)。

連載「Java Web サービス」の次回の記事では、引き続き Metroを取り上げます。次回の焦点となるのは、そのパフォーマンスです。単純なメッセージ交換の場合、そして WS-Security を使用した場合の Metro のパフォーマンスを、Axis2 と比べてみます。


ダウンロード

内容ファイル名サイズ
Source code for this articlej-jws10.zip38KB

参考文献

学ぶために

  • Metro: Metro サイトにアクセスして、資料およびダウンロードを入手してください。
  • WSIT (Web Services Interoperability Technologies): WSIT は Metro のコンポーネントで、WS-SecurityPolicy とその他いくつかのセキュリティー関連の技術を処理します。
  • XWSS (XML and WebServices Security Project): XWSS は Metro のランタイム・セキュリティー処理を実装します。
  • Security Token Configuration in Metro」(Kumar Jayanti 著、java.net、2009年6月): セキュリティー処理に対応した Metro/WSIT の構成については、このブログ・エントリーが多少なりとも包括的な唯一の資料となります (ただし適用対象は、現行の 1.5 リリースではなく、現在予定されている 2.0 リリースです)。
  • JAXB リファレンス実装: JAXB リファレンス実装のホーム・ページです。
  • JAX-WS リファレンス実装: JAX-WS リファレンス実装のホーム・ページです。
  • Technology bookstore で、この記事で取り上げた技術やその他の技術に関する本を探してください。
  • developerWorks Java technology ゾーン: Java プログラミングのあらゆる側面を網羅した記事が豊富に用意されています。

製品や技術を入手するために

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Java technology, SOA and web services
ArticleID=460706
ArticleTitle=Java Web サービス: Metro での WS-Security
publish-date=12012009