SSl:Secure Sockets Layer 安全套接層
TLS:Transport Layer Security傳輸層安全
是為網絡通信提供安全及數據完整性的一種安全協議。TLS與SSL在傳輸層對網絡連接進行加密。(見百度)
場景描述:將公司請求第三方公司的接口協議由http改成https后,出現了請求套接字異常的情況,第三方公司也收不到具體的請求,具體異常如下,
javax.ws.rs.ProcessingException: java.net.SocketException: Connection reset at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:287) at org.glassfish.jersey.client.ClientRuntime.invoke(ClientRuntime.java:252) at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:684) at org.glassfish.jersey.client.JerseyInvocation$1.call(JerseyInvocation.java:681) at org.glassfish.jersey.internal.Errors.process(Errors.java:315) at org.glassfish.jersey.internal.Errors.process(Errors.java:297) at org.glassfish.jersey.internal.Errors.process(Errors.java:228) at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:444) at org.glassfish.jersey.client.JerseyInvocation.invoke(JerseyInvocation.java:681) at org.glassfish.jersey.client.JerseyInvocation$Builder.method(JerseyInvocation.java:411) at org.glassfish.jersey.client.JerseyInvocation$Builder.get(JerseyInvocation.java:311) at com.baoxian.payment.UnionPayPayment.request(UnionPayPayment.java:323)
...
Caused by: java.net.SocketException: Connection reset
at java.net.SocketInputStream.read(SocketInputStream.java:209)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.security.ssl.InputRecord.readFully(InputRecord.java:465)
at sun.security.ssl.InputRecord.read(InputRecord.java:503)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:973)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1375)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1403)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1387)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1513)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1441)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:338)
at org.glassfish.jersey.client.internal.HttpUrlConnector._apply(HttpUrlConnector.java:394)
at org.glassfish.jersey.client.internal.HttpUrlConnector.apply(HttpUrlConnector.java:285)
... 66 more
實現http協議的代碼為:
1 import java.security.SecureRandom; 2 import java.security.cert.X509Certificate; 3 4 import javax.net.ssl.HostnameVerifier; 5 import javax.net.ssl.SSLContext; 6 import javax.net.ssl.SSLSession; 7 import javax.net.ssl.TrustManager; 8 import javax.net.ssl.X509TrustManager; 9 import javax.ws.rs.client.Client; 10 import javax.ws.rs.client.ClientBuilder; 11 12 import org.apache.commons.logging.Log; 13 import org.apache.commons.logging.LogFactory; 14 15 public class ClientUtil { 16 private static Log log = LogFactory.getLog(ClientUtil.class); 17 private static SSLContext sslContext = null; 18 private static HostnameVerifier hv = null; 19 public static Client sslClient = null; 20 public static Client client = null; 21 static{ 22 client = ClientBuilder.newClient(); 23 try { 24 sslContext = SSLContext.getInstance("SSLv3"); 25 sslContext.init(null, new TrustManager[] { new X509TrustManager() { 26 public X509Certificate[] getAcceptedIssuers() { 27 return new X509Certificate[0]; 28 } 29 30 public void checkClientTrusted(X509Certificate[] certs, String authType) { 31 } 32 33 public void checkServerTrusted(X509Certificate[] certs, String authType) { 34 } 35 } }, new SecureRandom()); 36 } catch (Exception e) { 37 log.error("SSL失敗", e); 38 } 39 hv = new HostnameVerifier() { 40 public boolean verify( String arg0, SSLSession arg1 ) { return true; } 41 }; 42 sslClient = ClientBuilder.newBuilder().hostnameVerifier(hv).sslContext(sslContext).build(); 43 } 44 }
調用ClientUtil類的代碼
1 url = url + "?data=" + URLEncoder.encode(jsonObject.toJSONString(), "UTF-8"); 2 log.info("銀聯請求: type: " + transType + ", URL:" + url); 3 4 5 Response response = ClientUtil.client.
.target(url) 7 .request() 8 .get();
這里的代碼寫死了只能用SSLv3安全協議,一運行的時候就報連接錯誤。可是,同樣的請求放到google瀏覽器上請求就可以通過。
把請求復制到google瀏覽器請求欄,按F12,點擊enter鍵,查看Security菜單欄輸出的網頁內容,發現這個請求接受TLS1.2安全協議
為了不影響其它類使用SSL協議,對這個類進行重寫。重寫后類,新增了獲取制定安全協議的方法,支持指定安全協議的請求。
1 import java.security.SecureRandom; 2 import java.security.cert.X509Certificate; 3 4 import javax.net.ssl.HostnameVerifier; 5 import javax.net.ssl.SSLContext; 6 import javax.net.ssl.SSLSession; 7 import javax.net.ssl.TrustManager; 8 import javax.net.ssl.X509TrustManager; 9 import javax.ws.rs.client.Client; 10 import javax.ws.rs.client.ClientBuilder; 11 12 import org.apache.commons.logging.Log; 13 import org.apache.commons.logging.LogFactory; 14 15 public class ClientUtil { 16 private static Log log = LogFactory.getLog(ClientUtil.class); 17 private static SSLContext sslContext = null; 18 private static HostnameVerifier hv = null; 19 public static Client sslClient = null; 20 public static Client client = null; 21 private static TrustManager simpleTrust=null; 22 static{ 23 client = ClientBuilder.newClient(); 24 try { 25 sslContext = SSLContext.getInstance("SSLv3"); 26 simpleTrust=new X509TrustManager() { 27 public X509Certificate[] getAcceptedIssuers() { 28 return new X509Certificate[0]; 29 } 30 31 public void checkClientTrusted(X509Certificate[] certs, String authType) { 32 } 33 34 public void checkServerTrusted(X509Certificate[] certs, String authType) { 35 } 36 }; 37 sslContext.init(null, new TrustManager[] { simpleTrust}, new SecureRandom()); 38 } catch (Exception e) { 39 log.error("SSL失敗", e); 40 } 41 hv = new HostnameVerifier() { 42 public boolean verify( String arg0, SSLSession arg1 ) { return true; } 43 }; 44 sslClient = ClientBuilder.newBuilder().hostnameVerifier(hv).sslContext(sslContext).build(); 45 } 46 47 public static Client getSslClient(String protocol) 48 { 49 try { 50 SSLContext sslContextTmp= SSLContext.getInstance(protocol); 51 sslContextTmp.init(null, new TrustManager[] { simpleTrust}, new SecureRandom()); 52 return ClientBuilder.newBuilder().hostnameVerifier(hv).sslContext(sslContextTmp).build(); 53 } 54 catch (Exception ex) 55 { 56 return ClientBuilder.newBuilder().hostnameVerifier(hv).sslContext(sslContext).build(); 57 } 58 } 59 }
改正后的調用方法
1 url = url + "?data=" + URLEncoder.encode(jsonObject.toJSONString(), "UTF-8"); 2 3 Response response = ClientUtil.getSslClient("TLSv1.2") 4 .target(url) 5 .request() 6 .get();
說明:不同第三方公司支持https協議的時候可以用不同安全協議,對於不同的情況要予以考慮。