https單雙向驗證環境的搭建參見:http://www.cnblogs.com/YDDMAX/p/5368404.htmlhtml
示例程序:java
package com.ydd.study.hello.httpclient; import java.io.File; import java.io.IOException; import java.security.KeyManagementException; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import javax.net.ssl.SSLContext; import org.apache.http.HttpHost; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.HttpClient; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; 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.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; public class OneTLSPool { public static CloseableHttpClient httpclient; // 得到池化得HttpClient static { // 設置truststore SSLContext sslcontext = null; try { sslcontext = SSLContexts .custom() .loadTrustMaterial( new File("D://https//ca//cl.jks"), "123456".toCharArray(), new TrustSelfSignedStrategy()).build(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } // 客戶端支持TLSV1,TLSV2,TLSV3這三個版本 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory( sslcontext, new String[] { "TLSv1", "TLSv2", "TLSv3" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());// 客戶端驗證服務器身份的策略 // Create a registry of custom connection socket factories for supported // protocol schemes. Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder .<ConnectionSocketFactory> create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new SSLConnectionSocketFactory(sslcontext)) .build(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager( socketFactoryRegistry); // Configure total max or per route limits for persistent connections // that can be kept in the pool or leased by the connection manager. connManager.setMaxTotal(100); connManager.setDefaultMaxPerRoute(10); // 個性化設置某個url的鏈接 connManager.setMaxPerRoute(new HttpRoute(new HttpHost("www.y.com", 80)), 20); httpclient = HttpClients.custom().setConnectionManager(connManager) .build(); } /** * 單向驗證且服務端的證書可信 * @throws IOException * @throws ClientProtocolException */ public static void oneWayAuthorizationAccepted() throws ClientProtocolException, IOException { // Execution context can be customized locally. HttpClientContext context = HttpClientContext.create(); HttpGet httpget = new HttpGet("https://www.yunzhu.com:8443"); // 設置請求的配置 RequestConfig requestConfig = RequestConfig.custom() .setSocketTimeout(5000).setConnectTimeout(5000) .setConnectionRequestTimeout(5000).build(); httpget.setConfig(requestConfig); System.out.println("executing request " + httpget.getURI()); CloseableHttpResponse response = httpclient.execute(httpget, context); try { System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); System.out.println(EntityUtils.toString(response.getEntity())); System.out.println("----------------------------------------"); // Once the request has been executed the local context can // be used to examine updated state and various objects affected // by the request execution. // Last executed request context.getRequest(); // Execution route context.getHttpRoute(); // Target auth state context.getTargetAuthState(); // Proxy auth state context.getTargetAuthState(); // Cookie origin context.getCookieOrigin(); // Cookie spec used context.getCookieSpec(); // User security token context.getUserToken(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] a) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { oneWayAuthorizationAccepted(); } }
一、用eclipse運行的時候報NoSuchAlgorithmException的錯。將eclipse的JRE刪除再從新導入本地的JRE就解決了。應該是缺失一些JDK的jar致使。apache
executing request https://www.yunzhu.com:8443 Exception in thread "main" javax.net.ssl.SSLKeyException: RSA premaster secret error at sun.security.ssl.RSAClientKeyExchange.<init>(Unknown Source) at sun.security.ssl.ClientHandshaker.serverHelloDone(Unknown Source) at sun.security.ssl.ClientHandshaker.processMessage(Unknown Source) at sun.security.ssl.Handshaker.processLoop(Unknown Source) at sun.security.ssl.Handshaker.process_record(Unknown Source) at sun.security.ssl.SSLSocketImpl.readRecord(Unknown Source) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(Unknown Source) at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) at sun.security.ssl.SSLSocketImpl.startHandshake(Unknown Source) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:141) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) at com.ydd.study.hello.httpclient.OneTLSPool.oneWayAuthorizationAccepted(OneTLSPool.java:138) at com.ydd.study.hello.httpclient.OneTLSPool.main(OneTLSPool.java:172) Caused by: java.security.NoSuchAlgorithmException: SunTlsRsaPremasterSecret KeyGenerator not available at javax.crypto.KeyGenerator.<init>(KeyGenerator.java:158) at javax.crypto.KeyGenerator.getInstance(KeyGenerator.java:207) at sun.security.ssl.JsseJce.getKeyGenerator(Unknown Source) ... 22 more
上面的程序使用JDK7將致使本身簽名的證書驗證失敗,報的錯誤和下面的請求百度報的錯相同。使用JDK6成功。這是JDK7的一個bug引發的:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7018897 tomcat
針對於JDK7的這個bug須要使用下面的代碼:服務器
package com.ydd.study.hello.httpclient; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import javax.net.ssl.X509TrustManager; import org.apache.http.HttpHost; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; 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.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; public class OneTLSPool { public static CloseableHttpClient httpclient; public static final String KEY_STORE_TRUST_PATH = "D://https//ca//cl.jks"; // truststore的路徑 public static final String KEY_STORE_TYPE_JKS = "jks"; // truststore的類型 private static final String KEY_STORE_TRUST_PASSWORD = "123456"; // truststore的密碼 // 得到池化得HttpClient static { SSLContext sslcontext = null; try { // 設置truststore KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS); InputStream tsIn = new FileInputStream(new File(KEY_STORE_TRUST_PATH)); try { trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray()); } finally { try { tsIn.close(); } catch (Exception ignore) { } } sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).build(); //解決jdk7的ssl的自簽名會有問題的bug,若是不是jdk7,則下面的代碼能夠沒有 //bug地址:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7018897 X509TrustManager xtm = new X509TrustManager(){ //建立TrustManager public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public X509Certificate[] getAcceptedIssuers() { return null; //return new java.security.cert.X509Certificate[0]; } }; sslcontext.init(null, new TrustManager[]{xtm}, null); //解決bug結束 } catch (Exception e) { e.printStackTrace(); } // 客戶端支持TLSV1,TLSV2,TLSV3這三個版本 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1", "TLSv2", "TLSv3" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());// 客戶端驗證服務器身份的策略 // Create a registry of custom connection socket factories for supported // protocol schemes. Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new SSLConnectionSocketFactory(sslcontext)).build(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); // Configure total max or per route limits for persistent connections // that can be kept in the pool or leased by the connection manager. connManager.setMaxTotal(100); connManager.setDefaultMaxPerRoute(10); // 個性化設置某個url的鏈接 connManager.setMaxPerRoute(new HttpRoute(new HttpHost("www.y.com", 80)), 20); httpclient = HttpClients.custom().setConnectionManager(connManager).build(); } /** * 單向驗證且服務端的證書可信 * * @throws IOException * @throws ClientProtocolException */ public static void oneWayAuthorizationAccepted() throws ClientProtocolException, IOException { // Execution context can be customized locally. HttpClientContext context = HttpClientContext.create(); HttpGet httpget = new HttpGet("https://www.yunzhu.com:8443"); // 設置請求的配置 RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000) .setConnectionRequestTimeout(5000).build(); httpget.setConfig(requestConfig); System.out.println("executing request " + httpget.getURI()); CloseableHttpResponse response = httpclient.execute(httpget, context); try { System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); System.out.println(EntityUtils.toString(response.getEntity())); System.out.println("----------------------------------------"); // Once the request has been executed the local context can // be used to examine updated state and various objects affected // by the request execution. // Last executed request context.getRequest(); // Execution route context.getHttpRoute(); // Target auth state context.getTargetAuthState(); // Proxy auth state context.getTargetAuthState(); // Cookie origin context.getCookieOrigin(); // Cookie spec used context.getCookieSpec(); // User security token context.getUserToken(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] a) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { oneWayAuthorizationAccepted(); } }
下面是請求百度時由於client端沒有信任百度的CA證書,因此單向不能驗證成功app
executing request https://www.baidu.com Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.ssl.Alerts.getSSLException(Alerts.java:192) at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1884) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:276) at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:270) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1439) at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:209) at sun.security.ssl.Handshaker.processLoop(Handshaker.java:878) at sun.security.ssl.Handshaker.process_record(Handshaker.java:814) at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1016) at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1312) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1339) at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1323) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.createLayeredSocket(SSLConnectionSocketFactory.java:394) at org.apache.http.conn.ssl.SSLConnectionSocketFactory.connectSocket(SSLConnectionSocketFactory.java:353) at org.apache.http.impl.conn.DefaultHttpClientConnectionOperator.connect(DefaultHttpClientConnectionOperator.java:141) at org.apache.http.impl.conn.PoolingHttpClientConnectionManager.connect(PoolingHttpClientConnectionManager.java:353) at org.apache.http.impl.execchain.MainClientExec.establishRoute(MainClientExec.java:380) at org.apache.http.impl.execchain.MainClientExec.execute(MainClientExec.java:236) at org.apache.http.impl.execchain.ProtocolExec.execute(ProtocolExec.java:184) at org.apache.http.impl.execchain.RetryExec.execute(RetryExec.java:88) at org.apache.http.impl.execchain.RedirectExec.execute(RedirectExec.java:110) at org.apache.http.impl.client.InternalHttpClient.doExecute(InternalHttpClient.java:184) at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:82) at com.ydd.study.hello.httpclient.OneTLSPool.oneWayAuthorizationDenied(OneTLSPool.java:91) at com.ydd.study.hello.httpclient.OneTLSPool.main(OneTLSPool.java:173) Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:385) at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292) at sun.security.validator.Validator.validate(Validator.java:260) at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:326) at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:231) at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:107) at org.apache.http.ssl.SSLContextBuilder$TrustManagerDelegate.checkServerTrusted(SSLContextBuilder.java:298) at sun.security.ssl.AbstractTrustManagerWrapper.checkServerTrusted(SSLContextImpl.java:813) at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1421) ... 20 more Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:196) at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:268) at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:380) ... 28 more
client信任了www.yunzhu.com:8443的CA證書,單項驗證成功eclipse
executing request https://www.yunzhu.com:8443 ---------------------------------------- HTTP/1.1 200 OK <!DOCTYPE html> <html lang="en"> (tomcat主頁的html內容) </html> ----------------------------------------
2、雙向握手socket
示例代碼:ide
package com.ydd.study.hello.httpclient; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.security.cert.CertificateException; import javax.net.ssl.SSLContext; import org.apache.http.HttpHost; import org.apache.http.client.ClientProtocolException; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.protocol.HttpClientContext; 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.TrustSelfSignedStrategy; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.ssl.SSLContexts; import org.apache.http.util.EntityUtils; public class DoubleWayTlsPool { public static CloseableHttpClient httpclient; public static final String KEY_STORE_TRUST_PATH = "D://https//ca//cl.jks"; // truststore的路徑 public static final String KEY_STORE_TYPE_JKS = "jks"; // truststore的類型 private static final String KEY_STORE_TRUST_PASSWORD = "123456"; // truststore的密碼 public static final String KEY_STORE_CLIENT_PATH="D://https//client//client.p12"; public static final String KEY_STORE_TYPE_P12="PKCS12"; private static final String KEY_STORE_PASSWORD="123456"; // 得到池化得HttpClient static { SSLContext sslcontext = null; try { // 設置truststore KeyStore trustStore = KeyStore.getInstance(KEY_STORE_TYPE_JKS); KeyStore keyStore = KeyStore.getInstance(KEY_STORE_TYPE_P12); InputStream ksIn = new FileInputStream(KEY_STORE_CLIENT_PATH); InputStream tsIn = new FileInputStream(new File(KEY_STORE_TRUST_PATH)); try { keyStore.load(ksIn, KEY_STORE_PASSWORD.toCharArray()); trustStore.load(tsIn, KEY_STORE_TRUST_PASSWORD.toCharArray()); } finally { try { ksIn.close(); tsIn.close(); } catch (Exception e) { e.printStackTrace(); } } sslcontext = SSLContexts.custom().loadTrustMaterial(trustStore, new TrustSelfSignedStrategy()).loadKeyMaterial(keyStore, KEY_STORE_PASSWORD.toCharArray()).build(); //下面的代碼能夠動態的設置握手驗證證書的策略,能夠不用手工導入證書,而只要程序控制便可 //bug地址:http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7018897 /* X509TrustManager xtm = new X509TrustManager(){ //建立TrustManager public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {} public X509Certificate[] getAcceptedIssuers() { return null; //return new java.security.cert.X509Certificate[0]; } }; sslcontext.init(null, new TrustManager[]{xtm}, null);*/ //解決bug結束 } catch (Exception e) { e.printStackTrace(); } // 客戶端支持TLSV1,TLSV2,TLSV3這三個版本 SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslcontext, new String[] { "TLSv1", "TLSv2", "TLSv3" }, null, SSLConnectionSocketFactory.getDefaultHostnameVerifier());// 客戶端驗證服務器身份的策略 // Create a registry of custom connection socket factories for supported // protocol schemes. Registry<ConnectionSocketFactory> socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() .register("http", PlainConnectionSocketFactory.INSTANCE) .register("https", new SSLConnectionSocketFactory(sslcontext)).build(); PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); // Configure total max or per route limits for persistent connections // that can be kept in the pool or leased by the connection manager. connManager.setMaxTotal(100); connManager.setDefaultMaxPerRoute(10); // 個性化設置某個url的鏈接 connManager.setMaxPerRoute(new HttpRoute(new HttpHost("www.y.com", 80)), 20); httpclient = HttpClients.custom().setConnectionManager(connManager).build(); } /** * 單向驗證且服務端的證書可信 * * @throws IOException * @throws ClientProtocolException */ public static void doubleWayAuthorizationAccepted() throws ClientProtocolException, IOException { // Execution context can be customized locally. HttpClientContext context = HttpClientContext.create(); HttpGet httpget = new HttpGet("https://www.yunzhu.com:8443"); // 設置請求的配置 RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000) .setConnectionRequestTimeout(5000).build(); httpget.setConfig(requestConfig); System.out.println("executing request " + httpget.getURI()); CloseableHttpResponse response = httpclient.execute(httpget, context); try { System.out.println("----------------------------------------"); System.out.println(response.getStatusLine()); System.out.println(EntityUtils.toString(response.getEntity())); System.out.println("----------------------------------------"); // Once the request has been executed the local context can // be used to examine updated state and various objects affected // by the request execution. // Last executed request context.getRequest(); // Execution route context.getHttpRoute(); // Target auth state context.getTargetAuthState(); // Proxy auth state context.getTargetAuthState(); // Cookie origin context.getCookieOrigin(); // Cookie spec used context.getCookieSpec(); // User security token context.getUserToken(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] a) throws KeyManagementException, NoSuchAlgorithmException, KeyStoreException, CertificateException, IOException { doubleWayAuthorizationAccepted(); } }
該程序在JDK6運行成功。oop
使用X509TrustManager能夠動態的改變握手時驗證證書的行爲。能夠利用這點來動態的導入證書,而不是須要手動的導入證書。 具體的用法參見下面的博客:
http://www.cnblogs.com/devinzhang/archive/2012/02/28/2371631.html