HttpClient4實現SSL雙向認證的客戶端(二)

在上篇文章中寫到了如何實現服務端程序,主要是netty實現的。還有如何生成證書和密鑰庫。java

這篇文章主要講客戶端如何實現:apache

 

httpclient實現鏈接池並進行ssl通訊

HttpClientUtils2.java安全

package https;

import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.client.methods.RequestBuilder;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;

import javax.net.ssl.SSLContext;
import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.SecureRandom;
import java.util.*;

/**
 * ssl通訊的client
 */
public class HttpClientUtils2 {

    private static PoolingHttpClientConnectionManager secureConnectionManager;
    private static HttpClientBuilder secureHttpBulder = null;
    private static RequestConfig requestConfig = null;
    private static int MAXCONNECTION = 10;
    private static int DEFAULTMAXCONNECTION = 5;

    private static String CLIENT_KEY_STORE = "E:\\https\\client.keystore";
    private static String CLIENT_TRUST_KEY_STORE = "E:\\https\\client.truststore";
    private static String CLIENT_KEY_STORE_PASSWORD = "123456";
    private static String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";
    private static String CLIENT_KEY_PASS = "123456";

    /**
     * 進行安全通訊的主機和端口
     */
    private static String HOST = "127.0.0.1";
    private static int PORT = 8888;

    static {
        //設置http的狀態參數
        requestConfig = RequestConfig.custom()
                .setSocketTimeout(5000)
                .setConnectTimeout(5000)
                .setConnectionRequestTimeout(5000)
                .build();

        try {
            KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream trustStoreInput = new FileInputStream(new File(CLIENT_TRUST_KEY_STORE));
            trustStore.load(trustStoreInput, CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());
            KeyStore clientKeyStore = KeyStore.getInstance(KeyStore.getDefaultType());
            FileInputStream clientKeyStoreInput = new FileInputStream(new File(CLIENT_KEY_STORE));
            clientKeyStore.load(clientKeyStoreInput, CLIENT_KEY_STORE_PASSWORD.toCharArray());

            SSLContext sslContext = SSLContexts.custom()
                    .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
                    .loadKeyMaterial(clientKeyStore, CLIENT_KEY_PASS.toCharArray())
                    .setSecureRandom(new SecureRandom())
                    .useSSL()
                    .build();


            ConnectionSocketFactory plainSocketFactory = new PlainConnectionSocketFactory();
            SSLConnectionSocketFactory sslSocketFactoy = new SSLConnectionSocketFactory(
                    sslContext, new String[]{"SSLv3"}, null,
                    SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);

            Registry<ConnectionSocketFactory> r = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", plainSocketFactory)
                    .register("https", sslSocketFactoy)
                    .build();

            secureConnectionManager = new PoolingHttpClientConnectionManager(r);
            HttpHost target = new HttpHost(HOST, PORT, "https");
            secureConnectionManager.setMaxTotal(MAXCONNECTION);
            //設置每一個Route的鏈接最大數
            secureConnectionManager.setDefaultMaxPerRoute(DEFAULTMAXCONNECTION);
            //設置指定域的鏈接最大數
            secureConnectionManager.setMaxPerRoute(new HttpRoute(target), 20);
            secureHttpBulder = HttpClients.custom().setConnectionManager(secureConnectionManager);
        } catch (Exception e) {
            throw new Error("Failed to initialize the server-side SSLContext", e);
        }
    }

    public static CloseableHttpClient getSecureConnection() throws Exception {
        return secureHttpBulder.build();
    }


    public static HttpUriRequest getRequestMethod(Map<String, String> map, String url, String method) {
        List<NameValuePair> params = new ArrayList<NameValuePair>();
        Set<Map.Entry<String, String>> entrySet = map.entrySet();
        for (Map.Entry<String, String> e : entrySet) {
            String name = e.getKey();
            String value = e.getValue();
            NameValuePair pair = new BasicNameValuePair(name, value);
            params.add(pair);
        }
        HttpUriRequest reqMethod = null;
        if ("post".equals(method)) {
            reqMethod = RequestBuilder.post().setUri(url)
                    .addParameters(params.toArray(new BasicNameValuePair[params.size()]))
                    .setConfig(requestConfig).build();
        } else if ("get".equals(method)) {
            reqMethod = RequestBuilder.get().setUri(url)
                    .addParameters(params.toArray(new BasicNameValuePair[params.size()]))
                    .setConfig(requestConfig).build();
        }
        return reqMethod;
    }


    public static void main(String args[]) throws Exception {
        Map<String, String> map = new HashMap<String, String>();
        map.put("account", "sdsdsd");
        map.put("password", "98765");

        HttpClient client = getSecureConnection(); //使用ssl通訊
        HttpUriRequest post = getRequestMethod(map, "https://127.0.0.1:8888/", "post");
        HttpResponse response = client.execute(post);

        if (response.getStatusLine().getStatusCode() == 200) {
            HttpEntity entity = response.getEntity();
            String message = EntityUtils.toString(entity, "utf-8");
            System.out.println(message);
        } else {
            System.out.println("請求失敗");
        }
    }
}

上面的httpclient實現了鏈接池,並能夠進行ssl雙向認證的通訊過程。其實也能夠進行不加密的http通訊。服務器

運行結果:session

服務器端app

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>dom

Your session is protected by TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA cipher suite.socket

 

VERSION: HTTP/1.1ide

 

REQUEST_URI: /post

 

HEADER: Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1

 

HEADER: Host=127.0.0.1:8888

 

HEADER: Connection=Keep-Alive

 

HEADER: User-Agent=Apache-HttpClient/4.3.3 (java 1.5)

 

HEADER: Accept-Encoding=gzip,deflate

 

HEADER: Content-Length=29

 

七月 10, 2014 11:55:07 上午 https.HttpDemoServerHandler exceptionCaught

警告: 

java.io.IOException: 遠程主機強迫關閉了一個現有的鏈接。

有異常,這不是主要的,是由於沒有關閉鏈接。

客戶端

WELCOME TO THE WILD WILD WEB SERVER

===================================

VERSION: HTTP/1.1

REQUEST_URI: /

 

HEADER: Content-Type=application/x-www-form-urlencoded; charset=ISO-8859-1

HEADER: Host=127.0.0.1:8888

HEADER: Connection=Keep-Alive

HEADER: User-Agent=Apache-HttpClient/4.3.3 (java 1.5)

HEADER: Accept-Encoding=gzip,deflate

HEADER: Content-Length=29

 

Is Chunked: false

IsMultipart: false

 

BODY Attribute: Attribute:Mixed: password=98765

 

BODY Attribute: Attribute:Mixed: account=sdsdsd

 

END OF POST CONTENT

 

Process finished with exit code 0

 

httpclien客戶端二

httpclient還有一種方式能夠進行ssl通訊。下面看這段代碼:

ClientCustomSSL.java

package https;

import java.io.File;
import java.io.FileInputStream;
import java.security.KeyStore;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLContexts;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;

/**
 * This example demonstrates how to create secure connections with a custom SSL
 * context.
 */
public class ClientCustomSSL {

    private static String CLIENT_KEY_STORE = "E:\\https\\client.keystore";
    private static String CLIENT_TRUST_KEY_STORE = "E:\\https\\client.truststore";
    private static String CLIENT_KEY_STORE_PASSWORD = "123456";
    private static String CLIENT_TRUST_KEY_STORE_PASSWORD = "123456";
    private static String CLIENT_KEY_PASS = "123456";


    public final static void main(String[] args) throws Exception {


        KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream instream = new FileInputStream(new File(CLIENT_TRUST_KEY_STORE));
        try {
            trustStore.load(instream, CLIENT_TRUST_KEY_STORE_PASSWORD.toCharArray());
        } finally {
            instream.close();
        }

        KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
        FileInputStream keyStoreInput = new FileInputStream(new File(CLIENT_KEY_STORE));
        try {
            keyStore.load(keyStoreInput, CLIENT_KEY_STORE_PASSWORD.toCharArray());
        } finally {
            keyStoreInput.close();
        }

        // Trust own CA and all self-signed certs
        SSLContext sslcontext = SSLContexts.custom()
                .loadTrustMaterial(trustStore, new TrustSelfSignedStrategy())
                .loadKeyMaterial(keyStore, CLIENT_KEY_PASS.toCharArray())
                .build();
        // Allow TLSv1 protocol only
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
                sslcontext,
                new String[]{"SSLv3"},
                null,
                SSLConnectionSocketFactory.BROWSER_COMPATIBLE_HOSTNAME_VERIFIER);
        CloseableHttpClient httpclient = HttpClients.custom()
                .setSSLSocketFactory(sslsf)
                .build();
        try {

            HttpPost httpPost = new HttpPost("https://127.0.0.1:8888/");

            System.out.println("executing request" + httpPost.getRequestLine());

            CloseableHttpResponse response = httpclient.execute(httpPost);
            try {
                HttpEntity entity = response.getEntity();

                System.out.println("----------------------------------------");
                System.out.println(response.getStatusLine());
                if (entity != null) {
                    System.out.println("Response content length: " + entity.getContentLength());
                }
                EntityUtils.consume(entity);
            } finally {
                response.close();
            }
        } finally {
            httpclient.close();
        }
    }
}

上面這段代碼沒有用到鏈接池,比較簡單的實現了雙向認證的ssl通訊過程。

運行結果:

----------------------------------------

HTTP/1.1 200 OK

Response content length: -1

 

Process finished with exit code 0

==========END==========

相關文章
相關標籤/搜索