Windows

WCF と Apache CXF でWS-Security の実装...にはなってないか。


Visual Studio 2008 C# Express Edition の WCF を使用して Apache CXF と連携させます。
んで、ついでにhttpsで経路の暗号化。

動作環境は.NET Framework3.5以上なので、WindowsXP以降になります。
svcutil.exeにて作成したスタブをVS2005のProjectにインポートしてコンパイルすれば、.NET Framework2.0でも動作可能です。
従って、.NET Framework2.0が動作するOSであればOKかと。
コンパイルは通ったけど、Win2kで実行したらエラーになりますた。orz

サーバー側はJavaが動けば何でも良いです。


サーバー側(Java)

WebコンテナはCaucho Resin 3.0.27 J2EE Server + Apache CXF

あらかじめSSLの証明書を作成しておきます。
場所は /usr/local/resin-3.0.27/keys/server.key に保存して下さい。

J2SE6.0 に付属している keytool.exeで作成します。

[ keys.bat ]
----------------
echo on
"C:\Program Files\Java\jdk1.6.0_13\bin\keytool.exe" -genkey -keyalg RSA -keystore keys/server.key
----------------
 
さらに、Resinの設定ファイルである resin.conf( /usr/local/resin-3.0.27/conf/resin.conf ) を書き換えます。

[ resin.conf ]
--------------------
    <!--
       - SSL port configuration:
       -
       - <http port="8443">
       -   <openssl>
       -     <certificate-file>keys/gryffindor.crt</certificate-file>
       -     <certificate-key-file>keys/gryffindor.key</certificate-key-file>
       -     <password>test123</password>
       -   </openssl>
       - </http>
      -->
       <http port="8443">
            <jsse-ssl>
                <key-store-type>jks</key-store-type>
                <key-store-file>keys/server.key</key-store-file>
                <password>password</password>
            </jsse-ssl>
       </http>
--------------------

これで httpsで経路を暗号化して通信することが出来るようになりました。
今回はWebコンテナレベルでHTTPS実装を行いましたが、アプリケーションレベルまで落としても良いと思います。

で、Java(サーバー)側のサービスメソッド。

[ com.chocbanana.ws.Service ]
------------------------------
package com.chocbanana.ws;

import javax.jws.WebService;
import javax.jws.WebMethod;

@WebService
public class Service implements IService{
    public Service(){

    }

    @WebMethod
    public String hello(){
        return "Hello";
    }
}
------------------------------


[ com/chocbanana/ws/cxf.xml ]
------------------------------
<beans xmlns="http://www.springframework.org/schema/beans"
      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xmlns:jaxws="http://cxf.apache.org/jaxws"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
                     http://www.springframework.org/schema/beans/spring-beans.xsd
                     http://cxf.apache.org/jaxws
                     http://cxf.apache.org/schemas/jaxws.xsd">

    <import resource="classpath:META-INF/cxf/cxf.xml" />
    <import resource="classpath:META-INF/cxf/cxf-extension-soap.xml"/>
    <import resource="classpath:META-INF/cxf/cxf-servlet.xml" />

    <jaxws:endpoint id="service" implementor="com.chocbanana.ws.Service" address="/Service" />
</beans>
------------------------------


[ web.xml ]
------------------------------
<?xml version="1.0"?>
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
    <display-name>Service</display-name>
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:/com/chocbanana/ws/cxf.xml</param-value>
    </context-param>

    <servlet>
        <servlet-name>context</servlet-name>
        <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <servlet>
        <servlet-name>CXFServlet</servlet-name>
        <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
        <load-on-startup>2</load-on-startup>
    </servlet>

    <servlet-mapping>
        <servlet-name>CXFServlet</servlet-name>
        <url-pattern>/services/*</url-pattern>
    </servlet-mapping>

    <mime-mapping>
        <extension>wsdl</extension>
        <mime-type>text/xml</mime-type>
    </mime-mapping>
</web-app>
------------------------------




クライアント(WCFホスト)

.NET Framework3.5に付属している svcutil.exeでスタブを作成します。

[ svcutil.bat ]
------------------
echo on

"C:\Program Files\Microsoft SDKs\Windows\v6.0A\bin\SvcUtil.exe" /l:cs /n:*,ims http://127.0.0.1:8080/services/Service?wsdl /out:Service.cs
------------------
上記は URIがhttpになっていますが、実際には下記に示すコードでhttpsにて通信します。
スタブを作成するだけなので、svcutil.exeを使用する際はhttpでOK。


[ -C#- app.config ]
--------------------
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
            <section name="ca2.Properties.Settings" type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
        </sectionGroup>
    </configSections>
    <system.serviceModel>
        <services />
        <bindings>
            <basicHttpBinding>
                <binding name="BasicHttpBinding_Service">
                    <security mode="Transport" />
                </binding>
            </basicHttpBinding>
            <wsHttpBinding>
                <binding name="wsHttpBinding_Service">
                    <security mode="TransportWithMessageCredential">
                        <transport clientCredentialType="None" />
                        <message clientCredentialType="UserName" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="https://127.0.0.1:8443/services/Service" binding="basicHttpBinding"
                bindingConfiguration="BasicHttpBinding_Service" contract="ims.IService"
                name="BasicHttpBinding_Service" />
            <endpoint address="https://127.0.0.1:8443/services/Service" binding="wsHttpBinding"
                bindingConfiguration="wsHttpBinding_Service" contract="ims.IService"
                name="wsHttpBinding_Service" />
        </client>
    </system.serviceModel>
    <applicationSettings>
        <ca2.Properties.Settings>
            <setting name="URL" serializeAs="String">
                <value>https://192.168.102.101:443/services/Service</value>
            </setting>
        </ca2.Properties.Settings>
    </applicationSettings>
</configuration>
--------------------
こんなapp.configファイル書けるか!ってな人には、.NET Frameworkに付属しているSvcConfigEditor.exeというツールがありますので、そちらでapp.configファイルを編集してください。Visual Studioにツール登録をしておくと便利です。

詳しくは
http://handcraft.blogsite.org/ComponentGeek/ShowArticle/24.aspx
http://handcraft.blogsite.org/ComponentGeek/ShowArticle/25.aspx

を参照してください。とても参考にさせて頂きました。

SvcConfigEditor.PNG


[ -C#- Program.cs ]
--------------------
using System;
using System.Collections.Generic;
using System.Text;

namespace ca2 {
    class Program {
        static void Main(string[] args) {
            ConfirmCertificatePolicy p = new ConfirmCertificatePolicy(); // 証明書続行確認用
            System.Console.WriteLine("WCFサービスホストを起動したら、キーを入力して下さい。");
            System.Console.ReadLine();
            try {
                ims.ServiceClient proxy = new ims.ServiceClient("BasicHttpBinding_Service",
                    new System.ServiceModel.EndpointAddress(ca2.Properties.Settings.Default.URL));
                Console.WriteLine(proxy.hello());
                proxy.Close();
            } catch (Exception e) {
                Console.WriteLine(e.StackTrace);
            }
            Console.WriteLine("終了するにはなにかキーを押してください。");
            Console.ReadLine();
        }
    }
}
--------------------


[ -C#- ConfirmCertificatePolicy.cs ]
--------------------
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Net.Security;
using System.Security.Cryptography.X509Certificates;

namespace ca2
{
    class ConfirmCertificatePolicy
    {
        public ConfirmCertificatePolicy()
        {
            ServicePointManager.ServerCertificateValidationCallback += RemoteCertValidation;
        }
        public bool RemoteCertValidation(object sender, X509Certificate cert, X509Chain chain, SslPolicyErrors error)
        {
            if (error != SslPolicyErrors.None)
            {
                Console.WriteLine("証明書に問題があります。理由:" + chain.ChainStatus[0].StatusInformation);
                Console.Write("処理を続行しますか? y:続行:");
                string input = Console.ReadLine();
                if (input.ToLower() == "y")
                    return true;
                else
                    return false;
            }
            return true;
        }
    }
}
--------------------

サンプルとしてはこんな感じかな?

このページの先頭へ