HTTPS
防止内容被第三方冒充或者篡改
SSL/TLS协议及握手过程
(4)服务器使用私钥得到key,并用key对数据加密,在相同的输入参数下能得到跟客户端传来的一样的数据。
这个异常就是HTTPS握手的过程中出现了问题。
数字证书
通过这种方式,黑客就不能简单的修改证书中的公钥了,因为公钥有了CA的数字签名,由CA证明公钥的有效性,不能被轻易篡改。
12306的证书问题
同样,操作系统也保存了一份可信的证书列表,可以运行certmgr.msc打开证书管理器查看,这些证书实际上是存储在Windows的注册表中,一般在\SOFTWARE\Microsoft\SystemCertificates\下。
(5)利用SSLContext产生的SSLSocket和SSLServerSocket进行通信
对于非CA机构颁发的证书和自签名证书,我们是无法直接访问到服务器的,直接访问通常会抛出异常:
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertPathValidatorException:
Trust anchor forcertification path not found.
有两种方法可以通过忽略证书直接调用:
方法一:自定义TrustManager绕过证书检查
@Test public void basicHttpsGetIgnoreCertificateValidation() throws Exception { String url = "https://kyfw.12306.cn/otn/"; // Create a trust manager that does not validate certificate chains TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() { public X509Certificate[] getAcceptedIssuers() { return null; } public void checkClientTrusted(X509Certificate[] certs, String authType) { // don't check } public void checkServerTrusted(X509Certificate[] certs, String authType) { // don't check } } }; SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, trustAllCerts, null); LayeredConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(ctx); CloseableHttpClient httpclient = HttpClients.custom() .setSSLSocketFactory(sslSocketFactory) .build(); HttpGet request = new HttpGet(url); request.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) ..."); CloseableHttpResponse response = httpclient.execute(request); String responseBody = readResponseBody(response); System.out.println(responseBody); }
方法二:信任所有证书
@Test public void basicHttpsGetIgnoreCertificateValidation() throws Exception { SSLContextBuilder builder = new SSLContextBuilder(); //JAVA8支持的新语法 //TrustStrategy acceptingTrustStrategy = ((X509Certificate[] certificate,String type) -> true); //builder.loadTrustMaterial(null, acceptingTrustStrategy); //上面这两步可以这么写: builder.loadTrustMaterial(new TrustStrategy(){ @Override public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { return true; } }); SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build()); CloseableHttpClient httpclient = HttpClients.custom().setSSLSocketFactory( sslsf).build(); HttpGet httpGet = new HttpGet("https://some-server"); CloseableHttpResponse response = httpclient.execute(httpGet); try { System.out.println(response.getStatusLine()); HttpEntity entity = response.getEntity(); EntityUtils.consume(entity); } finally { response.close(); } }
方法二虽然解决了SSHHandshakeException异常,但是由于客户端信任所有的证书,反而存在更大的隐患。
单向验证且服务器证书可信
package com.ydd.study.hello.httpclient; /** * @COPYRIGHT (C) 2018 Schenker AG * <p/> * All rights reserved */ 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; import javax.net.ssl.SSLContext; 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; public class oneWayAuthTest { 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(); } }