Java

JAX-WS Client で HTTPS


あー、ハマったよ。3日間...いや、もっとかな?
記憶に残ってるのは3日くらいなんですがね...。


えー、WCFでのHTTPS連携はやったけど、今回はそのJava版で。

使うのはApacheCXFです。
例によって J2SEのkeyToolで証明書を作成し、WebコンテナでHTTPS通信させます。

[ keytool.bat ]
-----
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -genkey -alias server -keyalg RSA -keypass password -storepass password -keystore server.keystore
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -certreq -alias server -keystore server.keystore -storepass password -file server.csr
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -export -alias server -keystore server.keystore -storepass password -file server.cer
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -import -alias server -storepass password -file server.cer
"C:\Program Files\Java\jdk1.6.0_18\bin\"keytool -list -storepass password
[ EOF ]
-----

サービス側のデプロイはコ コを参照してもらうとして、またクライアント側のみ提示しておきます。

サーバー側に配置した証明書と同じものを、クライアントとなるプロジェクト内に配置します。
プロジェクト名は [ ws-sample-client ] とし、認証鍵は [ ws-sample-client/keys/ ] 以下に保存するものとします。
server.cer
server.csr
server.keystore
の3つですか。まぁ使っているのは server.keystoreだけなんですがね。多分keystoreだけでイイハズ。

あとはコードを提示しておきます。
注意点は、サービス側で提供しているInterfaceをクライアント側のプロジェクトにも配置しておくこと...でしょうか。

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


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

@WebService
public interface IService {

    @WebMethod
    public String hello();
   
    @WebMethod
    public String sayHello(String message);
}
[ EOF ]
----------------


[ com.chocbanana.ws.client.Client ]
----------------
package com.chocbanana.ws.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;
import org.apache.cxf.transport.http.HTTPConduit;

public class Client {

    private static void configureSSLOnTheClient(Object c) {
        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(c);
        HTTPConduit httpConduit = (HTTPConduit) client.getConduit();

        try {
            TLSClientParameters tlsParams = new TLSClientParameters();
            tlsParams.setDisableCNCheck(true);

            KeyStore keyStore = KeyStore.getInstance("JKS");
            String trustpass = "password";

            String contextPath = "";
            try {
                contextPath = new java.io.File("keys").getAbsolutePath();
                // Main.class.getResource(".").toString();
                System.out.println(contextPath);
            } catch (Exception e) {
                e.printStackTrace();
            }

            File truststore = new File(contextPath + "/server.keystore");
            // System.out.println(truststore.getAbsolutePath());

            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            TrustManagerFactory trustFactory = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustFactory.init(keyStore);
            TrustManager[] tm = trustFactory.getTrustManagers();
            tlsParams.setTrustManagers(tm);
            tlsParams.setDisableCNCheck(true);

            KeyManager[] myKeyManagers = getKeyManagers(keyStore, trustpass);
            tlsParams.setKeyManagers(myKeyManagers);

            truststore = new File(contextPath + "/server.keystore");
            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            KeyManagerFactory keyFactory = KeyManagerFactory
                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyFactory.init(keyStore, trustpass.toCharArray());
            KeyManager[] km = keyFactory.getKeyManagers();
            tlsParams.setKeyManagers(km);
            tlsParams.setDisableCNCheck(true);

            TrustManager[] myTrustStoreKeyManagers = getTrustManagers(keyStore);
            tlsParams.setTrustManagers(myTrustStoreKeyManagers);

            FiltersType filter = new FiltersType();
            filter.getInclude().add(".*_EXPORT_.*");
            filter.getInclude().add(".*_EXPORT1024_.*");
            filter.getInclude().add(".*_WITH_DES_.*");
            filter.getInclude().add(".*_WITH_NULL_.*");
            filter.getExclude().add(".*_DH_anon_.*");
            tlsParams.setCipherSuitesFilter(filter);

            httpConduit.setTlsClientParameters(tlsParams);
        } catch (KeyStoreException kse) {
            System.out
                    .println("Security configuration failed with the following: "
                            + kse.getCause());
        } catch (NoSuchAlgorithmException nsa) {
            System.out
                    .println("Security configuration failed with the following: "
                            + nsa.getCause());
        } catch (FileNotFoundException fnfe) {
            System.out
                    .println("Security configuration failed with the following: "
                            + fnfe.getCause());
        } catch (UnrecoverableKeyException uke) {
            System.out
                    .println("Security configuration failed with the following: "
                            + uke.getCause());
            // } catch (CertificateException ce) {
            // System.out.println("Security configuration failed with the
            // following: " + ce.getCause());
        } catch (GeneralSecurityException gse) {
            System.out
                    .println("Security configuration failed with the following: "
                            + gse.getCause());
        } catch (IOException ioe) {
            System.out
                    .println("Security configuration failed with the following: "
                            + ioe.getCause());
        }
    }

    private static TrustManager[] getTrustManagers(KeyStore trustStore)
            throws NoSuchAlgorithmException, KeyStoreException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
        fac.init(trustStore);
        return fac.getTrustManagers();
    }

    private static KeyManager[] getKeyManagers(KeyStore keyStore,
            String keyPassword) throws GeneralSecurityException, IOException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        char[] keyPass = keyPassword != null ? keyPassword.toCharArray() : null;
        KeyManagerFactory fac = KeyManagerFactory.getInstance(alg);
        fac.init(keyStore, keyPass);
        return fac.getKeyManagers();
    }

    public static void main(String args[]) {
        // System.out.println("The client's security configuration will be done
        // programatically.");
        // System.out.println();
        String address = "https://127.0.0.1:8443/ws-sample/services/Service";
        JaxWsProxyFactoryBean proxyFactory = new JaxWsProxyFactoryBean();
        proxyFactory.setServiceClass(com.chocbanana.ws.IService.class);
        proxyFactory.setAddress(address);

        com.chocbanana.ws.IService client = (com.chocbanana.ws.IService) proxyFactory
                .create();
        configureSSLOnTheClient(client);

        System.out.println("Invoking greetMe...");
        try {
            String resp = client.sayHello("hoge"); // greetMe(System.getProperty("user.name"));
            System.out.println("Server responded with: " + resp);
            System.out.println();

        } catch (Exception e) {
            System.out.println("Invocation failed with the following: "
                    + e.getCause());
            System.out.println();
        }
    }
}
[ EOF ]
----------------

このコードは時間掛かったね。
あー、これでやっと終わった!

nonHTTPSなら話は簡単なのよね、でもセキュリティがどうとか、暗号化されてないとどうとか昨今五月蝿いじゃない?
これなら文句ねーだろ...。


- 応用編 SpringFramework使用 -

[ Client.java ]
---------------
package com.chocbanana.ws.client;

import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Client {

    public static void main(String args[]) {
        try {
            com.chocbanana.ws.IService servicePort =
                (com.chocbanana.ws.IService)new ClassPathXmlApplicationContext("/com/chocbanana/ws/client/bean.xml").getBean("service");
   
            ConfigureSSLOnTheClient.configureSSLOnTheClient(servicePort);
   
            System.out.println("Invoking greetMe...");
            String resp = servicePort.sayHello("hoge");
            System.out.println("Server responded with: " + resp);
            System.out.println();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
[ EOF ]
---------------


[ bean.xml ]
---------------
<?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:jaxws="http://cxf.apache.org/jaxws"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
        http://cxf.apache.org/jaxws http://cxf.apache.org/schema/jaxws.xsd">

    <bean id="proxyFactory"
        class="org.apache.cxf.jaxws.JaxWsProxyFactoryBean">
        <property name="serviceClass"
            value="com.chocbanana.ws.IService" />
        <property name="address"
            value="https://127.0.0.1:8443/ws-sample/services/Service" />
    </bean>

    <bean id="service" class="com.chocbanana.ws.IService"
        factory-bean="proxyFactory" factory-method="create" />

</beans>
[ EOF ]
---------------


[ ConfigureSSLOnTheClient.java ]
---------------
package com.chocbanana.ws.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;

import javax.net.ssl.KeyManager;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;

import org.apache.cxf.configuration.jsse.TLSClientParameters;
import org.apache.cxf.configuration.security.FiltersType;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.HTTPConduit;

public class ConfigureSSLOnTheClient {
   
    public static void configureSSLOnTheClient(Object c) {
        org.apache.cxf.endpoint.Client client = ClientProxy.getClient(c);
        HTTPConduit httpConduit = (HTTPConduit) client.getConduit();

        try {
            TLSClientParameters tlsParams = new TLSClientParameters();
            tlsParams.setDisableCNCheck(true);

            KeyStore keyStore = KeyStore.getInstance("JKS");
            String trustpass = "password";

            String contextPath = "";
            contextPath = new java.io.File("keys").getAbsolutePath();

            File truststore = new File(contextPath + "/server.keystore");

            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            TrustManagerFactory trustFactory = TrustManagerFactory
                    .getInstance(TrustManagerFactory.getDefaultAlgorithm());
            trustFactory.init(keyStore);
            TrustManager[] tm = trustFactory.getTrustManagers();
            tlsParams.setTrustManagers(tm);
            tlsParams.setDisableCNCheck(true);

            KeyManager[] myKeyManagers = getKeyManagers(keyStore, trustpass);
            tlsParams.setKeyManagers(myKeyManagers);

            truststore = new File(contextPath + "/server.keystore");
            keyStore.load(new FileInputStream(truststore), trustpass
                    .toCharArray());
            KeyManagerFactory keyFactory = KeyManagerFactory
                    .getInstance(KeyManagerFactory.getDefaultAlgorithm());
            keyFactory.init(keyStore, trustpass.toCharArray());
            KeyManager[] km = keyFactory.getKeyManagers();
            tlsParams.setKeyManagers(km);
            tlsParams.setDisableCNCheck(true);

            TrustManager[] myTrustStoreKeyManagers = getTrustManagers(keyStore);
            tlsParams.setTrustManagers(myTrustStoreKeyManagers);

            FiltersType filter = new FiltersType();
            filter.getInclude().add(".*_EXPORT_.*");
            filter.getInclude().add(".*_EXPORT1024_.*");
            filter.getInclude().add(".*_WITH_DES_.*");
            filter.getInclude().add(".*_WITH_NULL_.*");
            filter.getExclude().add(".*_DH_anon_.*");
            tlsParams.setCipherSuitesFilter(filter);

            httpConduit.setTlsClientParameters(tlsParams);
        } catch(Exception e){
            e.printStackTrace();
        }
    }

    private static TrustManager[] getTrustManagers(KeyStore trustStore) throws NoSuchAlgorithmException, KeyStoreException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        TrustManagerFactory fac = TrustManagerFactory.getInstance(alg);
        fac.init(trustStore);
        return fac.getTrustManagers();
    }

    private static KeyManager[] getKeyManagers(KeyStore keyStore, String keyPassword) throws GeneralSecurityException, IOException {
        String alg = KeyManagerFactory.getDefaultAlgorithm();
        char[] keyPass = keyPassword != null ? keyPassword.toCharArray() : null;
        KeyManagerFactory fac = KeyManagerFactory.getInstance(alg);
        fac.init(keyStore, keyPass);
        return fac.getKeyManagers();
    }
}
[  EOF ]
---------------

このページの先頭へ