基於httpclient4.5.6實現ssl雙向訪問、單向訪問

1.引入包

<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.6</version>
</dependency>
 <dependency>
     <groupId>org.apache.httpcomponents</groupId>
     <artifactId>httpmime</artifactId>
     <version>4.3.2</version>
    </dependency> 

2.對於單向https,客戶端通過配置truststore來驗證服務器證書合法性即可,而服務器則無需驗證客戶端身份。

對於雙向https則服務器端需要驗證客戶端身份,如此客戶端需要配置對應keystore,其中keystore存儲着客戶端密鑰、公鑰證書等信息。

實際完整代碼如下:

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.io.InterruptedIOException;
import java.net.URL;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.Enumeration;
import java.util.concurrent.TimeUnit;
import java.util.zip.GZIPInputStream;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.CookieStore;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.config.SocketConfig;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
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.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.json.JSONObject;
public class ClientUtil {
    private static PoolingHttpClientConnectionManager cm;
    private CloseableHttpClient httpClient;
    private HttpClientContext context;
    public boolean ishttps;
    private String prefix;  
    //雙向驗證ssl、https
    public  ClientUtil(String prefix,String CLIENT_CERT_FILE,String CLIENT_PWD,String TRUST_STRORE_FILE,String TRUST_STORE_PWD) throws Exception {
        ishttps = "https".equals(new URL(prefix).getProtocol());
        if(!ishttps){
        throw new Exception("請選擇http初始化類");    
        }         
        // 初始化密鑰庫
        KeyManagerFactory keyManagerFactory = KeyManagerFactory
                .getInstance("SunX509");
        KeyStore keyStore = getKeyStore(CLIENT_CERT_FILE, CLIENT_PWD, "PKCS12");             
        keyManagerFactory.init(keyStore, CLIENT_PWD.toCharArray());
        //獲取一些證書信息,添加到請求頭中
        /*Enumeration<String> aliases = keyStore.aliases();
        String keyAlias = null;
        while(aliases.hasMoreElements())
        {
            keyAlias = aliases.nextElement();
            if(keyStore.isKeyEntry(keyAlias))
                break;
        }
        PrivateKey signKey = (PrivateKey) keyStore.getKey(keyAlias, "1".toCharArray());
        X509Certificate signCert = (X509Certificate) keyStore.getCertificate(keyAlias);
        System.out.println("私鑰是:"+Base64.encode(signKey.getEncoded()));
        System.out.println("公鑰證書是:"+Base64.encode(signCert.getEncoded()));
        System.out.println("公鑰證書序列號是:"+signCert.getSerialNumber().toString(16).toUpperCase());
        System.out.println("頒發者是:"+signCert.getIssuerDN().getName());  */            
        // 初始化信任庫
        TrustManagerFactory trustManagerFactory = TrustManagerFactory
                .getInstance("SunX509");
        KeyStore trustkeyStore = getKeyStore(TRUST_STRORE_FILE, TRUST_STORE_PWD,"JKS");
        trustManagerFactory.init(trustkeyStore);
        // 初始化SSL上下文
        SSLContext sslcontext = SSLContext.getInstance("SSL");
        sslcontext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory
                .getTrustManagers(), null);              
       //設置規則
        SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(sslcontext,
                                 new String[]{"TLSv1","TLSv1.1","TLSv1.2"},null,
                                 new HttpsHostnameVerifier(prefix));
        //new AllowAllHostnameVerifier()不驗證服務器證書與服務器ip之間關聯
      //註冊
        Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
                .register("http", PlainConnectionSocketFactory.INSTANCE)
                .register("https", ssf).build();      
        if (cm == null) {
            cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            cm.setMaxTotal(200);
            cm.setDefaultMaxPerRoute(5);
            cm.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).build());
            cm.closeExpiredConnections();
            cm.closeIdleConnections(60, TimeUnit.SECONDS);
        }
        CookieStore cookieStore = new BasicCookieStore();
        context = HttpClientContext.create();
        context.setCookieStore(cookieStore);
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(10000)
                .setConnectTimeout(10000).setSocketTimeout(30000).build();
        this.prefix = prefix;
        httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig)
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()).build();        
    }
    //單向驗證https
    public  ClientUtil(String prefix,String TRUST_STRORE_FILE,String TRUST_STORE_PWD) throws Exception {
        ishttps = "https".equals(new URL(prefix).getProtocol());
        if(!ishttps){
        throw new Exception("請選擇http初始化該類");    
        }        

      // 初始化信任庫
      TrustManagerFactory trustManagerFactory = TrustManagerFactory
               .getInstance("SunX509");
       KeyStore trustkeyStore = getKeyStore(TRUST_STRORE_FILE, TRUST_STORE_PWD,"JKS");
       trustManagerFactory.init(trustkeyStore);
       SSLContext sslcontext = SSLContexts.custom().loadTrustMaterial(trustkeyStore, new  TrustSelfSignedStrategy()).build();
      //設置規則
       SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(sslcontext,
                                new String[]{"TLSv1","TLSv1.1","TLSv1.2"},null,
                             new AllowAllHostnameVerifier());
     //註冊
       Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create()
               .register("http", PlainConnectionSocketFactory.INSTANCE)
               .register("https", ssf).build();      
       if (cm == null) {
            cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
            cm.setMaxTotal(200);
            cm.setDefaultMaxPerRoute(5);
            cm.setDefaultSocketConfig(SocketConfig.custom().setTcpNoDelay(true).build());
            cm.closeExpiredConnections();
            cm.closeIdleConnections(60, TimeUnit.SECONDS);
        }
        CookieStore cookieStore = new BasicCookieStore();
        context = HttpClientContext.create();
        context.setCookieStore(cookieStore);
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(10000)
                .setConnectTimeout(10000).setSocketTimeout(30000).build();
        this.prefix = prefix;
        httpClient = HttpClients.custom().setConnectionManager(cm).setDefaultRequestConfig(requestConfig)
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy()).build();        
    }
    
    public String Post(String urlEnd, String param) {
        HttpPost httpPost = new HttpPost(prefix + urlEnd);
        try {
            httpPost.setHeader("Connection", "keep-alive");
            if (param != null) {
                httpPost.setEntity(new StringEntity(param, ContentType.APPLICATION_JSON));
            }
            HttpResponse httpResponse = null;
            while (httpResponse == null) {
                try {
                    httpResponse = httpClient.execute(httpPost, context);
                } catch (InterruptedIOException ex) {
                    if (!ex.getMessage().equals("Connection has been shut down")) {
                        throw ex;
                    }
                }
            }
            int code = httpResponse.getStatusLine().getStatusCode();
            switch (code)
            {
            case 200:
                {
                    HttpEntity entity = httpResponse.getEntity();
                    byte[] bs = EntityUtils.toByteArray(entity);
                    String jsonStr;
                    if (bs[0] != '{') {
                        if (bs[0] == 0x1F && bs[1] == 0x8B && bs[2] == 0x08) {
                            GZIPInputStream gzin = new GZIPInputStream(new ByteArrayInputStream(bs));
                            InputStreamReader isr = new InputStreamReader(gzin, "utf-8");
                            BufferedReader br = new BufferedReader(isr);
                            StringBuilder sb = new StringBuilder();
                            String tempbf;
                            while ((tempbf = br.readLine()) != null) {
                                sb.append(tempbf);
                                sb.append("\r\n");
                            }
                            isr.close();
                            gzin.close();
                            jsonStr = sb.toString();
                        } else {
                            jsonStr = new String(bs);
                            //System.out.println(jsonStr);
                            //jsonStr = "{\"data\":\"" + jsonStr + "\"}";
                        }
                    } else {
                        jsonStr = new String(bs, "utf-8");
                    }                    
                     return jsonStr;
                }
            default:
                return "http code is "+code;
            }
        } catch (Exception ex) {
            return ex.toString();
        } finally {
            httpPost.releaseConnection();
        }
    }
    
    /**
     * 獲得KeyStore
     *
     * @param keyStorePath
     * @param password
     * @return

     * @throws Exception
     */
    private static KeyStore getKeyStore(String keyStorePath, String password,String type)
            throws Exception {
        FileInputStream is = new FileInputStream(keyStorePath);
        KeyStore ks = KeyStore.getInstance(type);
        ks.load(is, password.toCharArray());
        is.close();
        return ks;
    }
    public static void main(String[] args) throws Exception {
        String prefix="https://192.168.3.111:443";
        String CLIENT_CERT_FILE="C:/rsa1.pfx";// 客戶端證書路徑,用了本地絕對路徑,需要修改
        String CLIENT_PWD="1";// 客戶端證書密碼
        String TRUST_STRORE_FILE="C:/trustStore";// 信任庫路徑
        String TRUST_STORE_PWD="changeit";// 信任庫密碼
      ClientUtil client=new ClientUtil(prefix,CLIENT_CERT_FILE,CLIENT_PWD,TRUST_STRORE_FILE,TRUST_STORE_PWD);
        //ClientUtil client1=new ClientUtil(prefix,TRUST_STRORE_FILE,TRUST_STORE_PWD);
        String url = "/test_args";
        String url1="/redis";
        String httpOrgCreateTestRtn = client.Post(url, null);
        System.out.println(httpOrgCreateTestRtn);
    }
}

這裏CLIENT_CERT_FILE採用pdf證書,這裏使用標準ca頒發證書,trustStore生成可以參照keytool配置ssl策略。

其中new HttpsHostnameVerifier(prefix)、new AllowAllHostnameVerifier()分別爲手動編寫的主機域名驗證策略與採取不驗證允許所有。這裏HttpsHostnameVerifier主要防止重定向。

import java.security.cert.X509Certificate;

import java.util.List;

import javax.net.ssl.HostnameVerifier;

import javax.net.ssl.SSLException;

import javax.net.ssl.SSLSession;

import org.apache.http.conn.util.PublicSuffixMatcher;

public class HttpsHostnameVerifier implements HostnameVerifier {

private String host;

public HttpsHostnameVerifier(String host){

String hostString=host.replace("https://", "");

this.host = hostString.substring(0, hostString.indexOf(":"));

}

/**

* 驗證對方主機名稱 ,防止服務器證書上的Hostname和實際的URL不匹配,這個目前證書不具備和ip主題相同

* 防止鏈接被重定向到其他的不安全的地址

*/

@Override

public boolean verify(String hostname, SSLSession session) {

System.out.println("hostname = [" + hostname + "],session = [" + session.getPeerHost() + "]");

if (hostname.equals(host) || session.equals(host))

return true;

else

return false;

}

}

經驗證,設置成雙向時只能初始化爲雙向進行訪問,但當nginx設置成單向時,使用雙向亦可訪問。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章