說明:本文主要是在平時接口對接開發中遇到的為保證傳輸安全的情況特要求使用https進行交互的情況下,使用httpClient4.5版本對HTTPS的雙向驗證的 功能的實現
首先,老生常談,文章將按照哲學三部曲來解答什么是https,為什么要使用https,httpClient怎么實現https及雙向驗證。
問題1:什么是https?
https:安全的超文本傳輸協議;其實質是
加密+認證+完整性保護的HTTP,
也就是說是http的安全版本,https由http進行通信,但利用SSL/TLS協議來加密數據包,並提供對網站服務器的身份認證,保護交換數據的隱私與完整性 。
問題2:為什么要使用https協議?
沒有任何一個東西是完美的,總所周知,https出現以前,哪怕是直到現在我們見到更多的還是http協議,那么為什么我們要使用https協議呢?一般來說https是安全的http協議,我們更多的用於支付場景中,https協議主要針對解決http協議以下不足:
1.http使用明文(不加密)通信,內容可能會被竊聽
2.不對通信方身份進行認證,可能遭遇偽裝攻擊
3.無法證明報文的完整性(即准確性),報文可能已被篡改
同樣的雖然https解決了HTTP存在的一些問題,但是https的本質是將http的通信接口加入了SSL/TLS協議進行加密處理,加密通信會消耗更多的cup以及內存資源。同樣在三次握手事,相對效率會變低,除此之外,要進行https通信,證書是必不可少的。而是用證書必須向認證機構(CA)購買。
問題3:httpClient怎么實現https及雙向驗證?
對接的服務端發送過來的證書是pfx格式的證書,正常來講pfx格式證書是包含了公鑰/私鑰/證書信息等的PKCS12證書,然而直接使用pfx證書作為keyStore卻一直報錯no trusted certificate error,因此在網上查詢了一天,終於把這個問題解決了,解決方案如下:
先將.pfx文件轉為.cer文件,再將.cer文件使用keytool工具導入$JAVA_HOME/jre/lib/security/cacerts中,cacerts證書庫的默認密碼為changeit;
1.pfx文件轉為cer文件的方法:
1.使用IE瀏覽器
2.將.cer文件使用keytool工具導入cacerts證書庫
1.keytool -import -alias testCer -keystore cacerts -file C:\Users\admin\Desktop\bcTest.cer
3.java代碼中的文件配置
初始化keyStore:為pfx證書路徑與密碼,策略為PKCS12
初始化trustStore:為cacerts證書庫地址,策略為JKS,cacerts證書庫默認密碼為changeit
Java代碼實現:
1 package com.test.sendhttps; 2 3 import java.io.File; 4 import java.io.FileInputStream; 5 import java.io.FileNotFoundException; 6 import java.io.IOException; 7 import java.security.KeyManagementException; 8 import java.security.KeyStore; 9 import java.security.KeyStoreException; 10 import java.security.NoSuchAlgorithmException; 11 import java.security.UnrecoverableKeyException; 12 import java.security.cert.CertificateException; 13 import java.util.Iterator; 14 import java.util.Map; 15 import java.util.Set; 16 import javax.net.ssl.KeyManager; 17 import javax.net.ssl.KeyManagerFactory; 18 import javax.net.ssl.SSLContext; 19 import javax.net.ssl.TrustManager; 20 import javax.net.ssl.TrustManagerFactory; 21 import org.apache.commons.lang3.ObjectUtils; 22 import org.apache.http.Header; 23 import org.apache.http.HttpEntity; 24 import org.apache.http.HttpStatus; 25 import org.apache.http.client.methods.CloseableHttpResponse; 26 import org.apache.http.client.methods.HttpPost; 27 import org.apache.http.config.Registry; 28 import org.apache.http.config.RegistryBuilder; 29 import org.apache.http.conn.socket.ConnectionSocketFactory; 30 import org.apache.http.conn.socket.PlainConnectionSocketFactory; 31 import org.apache.http.conn.ssl.SSLConnectionSocketFactory; 32 import org.apache.http.entity.StringEntity; 33 import org.apache.http.impl.client.CloseableHttpClient; 34 import org.apache.http.impl.client.HttpClients; 35 import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; 36 import org.apache.http.util.EntityUtils; 37 import org.apache.log4j.Logger; 38 import com.test.util.JsonUtils; 39 import com.test.util.PropertiesUtil; 40 41 public class SendHttps { 42 private static Logger log = Logger.getLogger(SendHttps.class); 43 static String keyStorePath ; 44 static String keyStorePassword ; 45 static String keyStoreType ; 46 static String trustStorePath ; 47 static String trustStorePassword ; 48 static String trustStoreType; 49 static{ 50 PropertiesUtil proUtil; 51 52 //配置文件讀取 53 try { 54 proUtil = new PropertiesUtil(); 55 keyStorePath = proUtil.readValue("sslKeyStorePath"); 56 keyStorePassword = proUtil.readValue("sslKeyStorePassword"); 57 keyStoreType = proUtil.readValue("sslKeyStoreType"); 58 trustStorePath = proUtil.readValue("sslTrustStore"); 59 trustStorePassword = proUtil.readValue("sslTrustStorePassword"); 60 trustStoreType = proUtil.readValue("sslTrustStoreType"); 61 } catch (IOException e) { 62 // TODO Auto-generated catch block 63 log.error("配置文件讀取異常"); 64 e.printStackTrace(); 65 } 66 } 67 68 public static String sendToHttps(String reqMsg, String url, Map<String, String> headMap) { 69 log.info("keyFactory"); 70 //初始化KeyManager 71 KeyManagerFactory keyFactory = null; 72 try { 73 keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 74 KeyStore keystore = KeyStore.getInstance(keyStoreType); 75 keystore.load(new FileInputStream(new File(keyStorePath)), null); 76 keyFactory.init(keystore, keyStorePassword.toCharArray()); 77 78 } catch (NoSuchAlgorithmException e) { 79 // TODO Auto-generated catch block 80 e.printStackTrace(); 81 } catch (KeyStoreException e) { 82 // TODO Auto-generated catch block 83 e.printStackTrace(); 84 } catch (CertificateException e) { 85 // TODO Auto-generated catch block 86 e.printStackTrace(); 87 } catch (FileNotFoundException e) { 88 // TODO Auto-generated catch block 89 e.printStackTrace(); 90 } catch (IOException e) { 91 // TODO Auto-generated catch block 92 e.printStackTrace(); 93 } catch (UnrecoverableKeyException e) { 94 // TODO Auto-generated catch block 95 e.printStackTrace(); 96 } 97 KeyManager[] keyManagers = keyFactory.getKeyManagers(); 98 //初始化Trust Manager 99 log.info("keyFactory ="+keyFactory); 100 TrustManagerFactory trustFactory = null; 101 102 try { 103 trustFactory = TrustManagerFactory.getInstance("SunX509"); 104 } catch (NoSuchAlgorithmException e1) { 105 // TODO Auto-generated catch block 106 e1.printStackTrace(); 107 } 108 109 KeyStore tsstore; 110 try { 111 tsstore = KeyStore.getInstance(trustStoreType); 112 tsstore.load(new FileInputStream(new File(trustStorePath)), trustStorePassword.toCharArray()); 113 trustFactory.init(tsstore); 114 log.info("tsstore ="+tsstore+" || trustFactory = "+trustFactory); 115 } catch (KeyStoreException e) { 116 // TODO Auto-generated catch block 117 e.printStackTrace(); 118 } catch (NoSuchAlgorithmException e) { 119 // TODO Auto-generated catch block 120 e.printStackTrace(); 121 } catch (CertificateException e) { 122 // TODO Auto-generated catch block 123 e.printStackTrace(); 124 } catch (FileNotFoundException e) { 125 // TODO Auto-generated catch block 126 e.printStackTrace(); 127 } catch (IOException e) { 128 // TODO Auto-generated catch block 129 e.printStackTrace(); 130 } 131 132 TrustManager[] trustManagers = trustFactory.getTrustManagers(); 133 log.info("trustManagers ="+trustManagers); 134 //注冊HtpClient 135 SSLContext sslContext = null; 136 try { 137 sslContext = SSLContext.getInstance("TLS"); 138 sslContext.init(keyManagers, trustManagers, null); 139 } catch (NoSuchAlgorithmException e) { 140 // TODO Auto-generated catch block 141 e.printStackTrace(); 142 } catch (KeyManagementException e) { 143 // TODO Auto-generated catch block 144 e.printStackTrace(); 145 } 146 log.info("sslContext ="+sslContext); 147 148 //設置規則限制 149 SSLConnectionSocketFactory ssf = new SSLConnectionSocketFactory(sslContext, 150 new String[]{"TLSv1","TLSv1.1","TLSv1.2"},null, 151 new HttpsHostnameVerifier()); 152 //注冊 153 Registry<ConnectionSocketFactory> socketFactoryRegistry = null; 154 socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory> create() 155 .register("http", PlainConnectionSocketFactory.INSTANCE) 156 .register("https", ssf).build(); 157 //池化管理 158 PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry); 159 //創建httpClient 160 CloseableHttpClient httpClient; 161 httpClient = HttpClients.custom().setConnectionManager(connManager).build(); 162 163 //設置httpPost 164 HttpPost httpPost = new HttpPost(url); 165 if ((!headMap.isEmpty()) && (headMap.size() > 0)) { 166 Set<String> keys = headMap.keySet(); 167 for (Iterator<String> i = keys.iterator(); i.hasNext(); ) { 168 String key = ObjectUtils.toString(i.next()); 169 if("host".equals(key)){ 170 continue; 171 }else{ 172 log.info("key="+key+",value="+(String)headMap.get(key)); 173 httpPost.addHeader(key, (String)headMap.get(key)); 174 } 175 } 176 } 177 StringEntity reqEntity = new StringEntity(reqMsg, "UTF-8"); 178 179 Header[] types = httpPost.getHeaders("Content-Type"); 180 if ((types == null) || (types.length < 1)) { 181 httpPost.addHeader("Content-Type", "application/json;charset=utf-8"); 182 } 183 184 httpPost.setEntity(reqEntity); 185 CloseableHttpResponse response; 186 try { 187 response = httpClient.execute(httpPost); 188 } catch (Exception e) { 189 e.printStackTrace(); 190 return null; 191 } 192 int statusCode = response.getStatusLine().getStatusCode(); 193 if (statusCode != HttpStatus.SC_OK) { 194 httpPost.abort(); 195 return JsonUtils.setError("Fail to connect . response code = " + statusCode + ". error."); 196 } 197 198 HttpEntity entity = response.getEntity(); 199 String result = null; 200 try { 201 if (entity != null) { 202 result = EntityUtils.toString(entity, "utf-8"); 203 } 204 EntityUtils.consume(entity); 205 response.close(); 206 } catch (Exception e) { 207 log.error("Change charset to utf-8 error."); 208 return JsonUtils.setError("Change charset to utf-8 error."); 209 } 210 return result; 211 } 212 } 213
https設置主機名校驗
1 package com.test.sendhttps; 2 3 import javax.net.ssl.HostnameVerifier; 4 import javax.net.ssl.SSLSession; 5 6 public class HttpsHostnameVerifier implements HostnameVerifier { 7 8 /** 9 * 驗證對方主機名稱 ,防止服務器證書上的Hostname和實際的URL不匹配 10 * 防止鏈接被重定向到其他的不安全的地址 11 */ 12 @Override 13 public boolean verify(String hostname, SSLSession session) { 14 System.out.println("hostname = [" + hostname + "],session = [" + session.getPeerHost() + "]"); 15 if (hostname.equals("地址") || session.equals("地址")) 16 return true; 17 else 18 return false; 19 } 20 }
配置信息 config.propertities:
sslKeyStorePath=pfx文件路徑 sslKeyStorePassword=pfx文件密碼 sslKeyStoreType=PKCS12 #jdk中的cacerts庫地址 sslTrustStore=C:/Program Files/Java/jre7/lib/security/cacerts #cacerts庫密碼 sslTrustStorePassword=changeit #cacerts庫默認類型 sslTrustStoreType=JKS
后續更換證書操作
查看cacerts證書庫中的所有證書
keytool -list -keystore cacerts
刪除證書庫中別名為testCer的證書
keytool -delete -alias testCer -keystore cacerts
再次導入證書
keytool -import -alias testCer -keystore cacerts -file C:\Users\admin\Desktop\testCer.cer
以上即是所有內容,如有不正確之處,歡迎各位大神批評,斧正,謝謝