| /* | 
|  * Copyright 2016 jeasonlzy(廖子尧) | 
|  * | 
|  * Licensed under the Apache License, Version 2.0 (the "License"); | 
|  * you may not use this file except in compliance with the License. | 
|  * You may obtain a copy of the License at | 
|  * | 
|  *      http://www.apache.org/licenses/LICENSE-2.0 | 
|  * | 
|  * Unless required by applicable law or agreed to in writing, software | 
|  * distributed under the License is distributed on an "AS IS" BASIS, | 
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  * See the License for the specific language governing permissions and | 
|  * limitations under the License. | 
|  */ | 
| package com.doumee.lib_coremodel.http.utils; | 
|   | 
| import java.io.IOException; | 
| import java.io.InputStream; | 
| import java.security.KeyManagementException; | 
| import java.security.KeyStore; | 
| import java.security.NoSuchAlgorithmException; | 
| import java.security.cert.Certificate; | 
| import java.security.cert.CertificateException; | 
| import java.security.cert.CertificateFactory; | 
| import java.security.cert.X509Certificate; | 
|   | 
| import javax.net.ssl.HostnameVerifier; | 
| import javax.net.ssl.KeyManager; | 
| import javax.net.ssl.KeyManagerFactory; | 
| import javax.net.ssl.SSLContext; | 
| import javax.net.ssl.SSLSession; | 
| import javax.net.ssl.SSLSocketFactory; | 
| import javax.net.ssl.TrustManager; | 
| import javax.net.ssl.TrustManagerFactory; | 
| import javax.net.ssl.X509TrustManager; | 
|   | 
|   | 
| public class HttpsUtils { | 
|   | 
|     public static class SSLParams { | 
|         public SSLSocketFactory sSLSocketFactory; | 
|         public X509TrustManager trustManager; | 
|     } | 
|   | 
|     public static SSLParams getSslSocketFactory() { | 
|         return getSslSocketFactoryBase(null, null, null); | 
|     } | 
|   | 
|     /** | 
|      * https单向认证 | 
|      * 可以额外配置信任服务端的证书策略,否则默认是按CA证书去验证的,若不是CA可信任的证书,则无法通过验证 | 
|      */ | 
|     public static SSLParams getSslSocketFactory(X509TrustManager trustManager) { | 
|         return getSslSocketFactoryBase(trustManager, null, null); | 
|     } | 
|   | 
|     /** | 
|      * https单向认证 | 
|      * 用含有服务端公钥的证书校验服务端证书 | 
|      */ | 
|     public static SSLParams getSslSocketFactory(InputStream... certificates) { | 
|         return getSslSocketFactoryBase(null, null, null, certificates); | 
|     } | 
|   | 
|     /** | 
|      * https双向认证 | 
|      * bksFile 和 password -> 客户端使用bks证书校验服务端证书 | 
|      * certificates -> 用含有服务端公钥的证书校验服务端证书 | 
|      */ | 
|     public static SSLParams getSslSocketFactory(InputStream bksFile, String password, InputStream... certificates) { | 
|         return getSslSocketFactoryBase(null, bksFile, password, certificates); | 
|     } | 
|   | 
|     /** | 
|      * https双向认证 | 
|      * bksFile 和 password -> 客户端使用bks证书校验服务端证书 | 
|      * X509TrustManager -> 如果需要自己校验,那么可以自己实现相关校验,如果不需要自己校验,那么传null即可 | 
|      */ | 
|     public static SSLParams getSslSocketFactory(InputStream bksFile, String password, X509TrustManager trustManager) { | 
|         return getSslSocketFactoryBase(trustManager, bksFile, password); | 
|     } | 
|   | 
|     private static SSLParams getSslSocketFactoryBase(X509TrustManager trustManager, InputStream bksFile, String password, InputStream... certificates) { | 
|         SSLParams sslParams = new SSLParams(); | 
|         try { | 
|             KeyManager[] keyManagers = prepareKeyManager(bksFile, password); | 
|             TrustManager[] trustManagers = prepareTrustManager(certificates); | 
|             X509TrustManager manager; | 
|             if (trustManager != null) { | 
|                 //优先使用用户自定义的TrustManager | 
|                 manager = trustManager; | 
|             } else if (trustManagers != null) { | 
|                 //然后使用默认的TrustManager | 
|                 manager = chooseTrustManager(trustManagers); | 
|             } else { | 
|                 //否则使用不安全的TrustManager | 
|                 manager = UnSafeTrustManager; | 
|             } | 
|             // 创建TLS类型的SSLContext对象, that uses our TrustManager | 
|             SSLContext sslContext = SSLContext.getInstance("TLS"); | 
|             // 用上面得到的trustManagers初始化SSLContext,这样sslContext就会信任keyStore中的证书 | 
|             // 第一个参数是授权的密钥管理器,用来授权验证,比如授权自签名的证书验证。第二个是被授权的证书管理器,用来验证服务器端的证书 | 
|             sslContext.init(keyManagers, new TrustManager[]{manager}, null); | 
|             // 通过sslContext获取SSLSocketFactory对象 | 
|             sslParams.sSLSocketFactory = sslContext.getSocketFactory(); | 
|             sslParams.trustManager = manager; | 
|             return sslParams; | 
|         } catch (NoSuchAlgorithmException e) { | 
|             throw new AssertionError(e); | 
|         } catch (KeyManagementException e) { | 
|             throw new AssertionError(e); | 
|         } | 
|     } | 
|   | 
|     private static KeyManager[] prepareKeyManager(InputStream bksFile, String password) { | 
|         try { | 
|             if (bksFile == null || password == null) return null; | 
|             KeyStore clientKeyStore = KeyStore.getInstance("BKS"); | 
|             clientKeyStore.load(bksFile, password.toCharArray()); | 
|             KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); | 
|             kmf.init(clientKeyStore, password.toCharArray()); | 
|             return kmf.getKeyManagers(); | 
|         } catch (Exception e) { | 
|             e.printStackTrace(); | 
|         } | 
|         return null; | 
|     } | 
|   | 
|     private static TrustManager[] prepareTrustManager(InputStream... certificates) { | 
|         if (certificates == null || certificates.length <= 0) return null; | 
|         try { | 
|             CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); | 
|             // 创建一个默认类型的KeyStore,存储我们信任的证书 | 
|             KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); | 
|             keyStore.load(null); | 
|             int index = 0; | 
|             for (InputStream certStream : certificates) { | 
|                 String certificateAlias = Integer.toString(index++); | 
|                 // 证书工厂根据证书文件的流生成证书 cert | 
|                 Certificate cert = certificateFactory.generateCertificate(certStream); | 
|                 // 将 cert 作为可信证书放入到keyStore中 | 
|                 keyStore.setCertificateEntry(certificateAlias, cert); | 
|                 try { | 
|                     if (certStream != null) certStream.close(); | 
|                 } catch (IOException e) { | 
|                     e.printStackTrace(); | 
|                 } | 
|             } | 
|             //我们创建一个默认类型的TrustManagerFactory | 
|             TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); | 
|             //用我们之前的keyStore实例初始化TrustManagerFactory,这样tmf就会信任keyStore中的证书 | 
|             tmf.init(keyStore); | 
|             //通过tmf获取TrustManager数组,TrustManager也会信任keyStore中的证书 | 
|             return tmf.getTrustManagers(); | 
|         } catch (Exception e) { | 
|             e.printStackTrace(); | 
|         } | 
|         return null; | 
|     } | 
|   | 
|     private static X509TrustManager chooseTrustManager(TrustManager[] trustManagers) { | 
|         for (TrustManager trustManager : trustManagers) { | 
|             if (trustManager instanceof X509TrustManager) { | 
|                 return (X509TrustManager) trustManager; | 
|             } | 
|         } | 
|         return null; | 
|     } | 
|   | 
|     /** | 
|      * 为了解决客户端不信任服务器数字证书的问题,网络上大部分的解决方案都是让客户端不对证书做任何检查, | 
|      * 这是一种有很大安全漏洞的办法 | 
|      */ | 
|     public static X509TrustManager UnSafeTrustManager = new X509TrustManager() { | 
|         @Override | 
|         public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException { | 
|         } | 
|   | 
|         @Override | 
|         public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException { | 
|         } | 
|   | 
|         @Override | 
|         public X509Certificate[] getAcceptedIssuers() { | 
|             return new X509Certificate[]{}; | 
|         } | 
|     }; | 
|   | 
|     /** | 
|      * 此类是用于主机名验证的基接口。 在握手期间,如果 URL 的主机名和服务器的标识主机名不匹配, | 
|      * 则验证机制可以回调此接口的实现程序来确定是否应该允许此连接。策略可以是基于证书的或依赖于其他验证方案。 | 
|      * 当验证 URL 主机名使用的默认规则失败时使用这些回调。如果主机名是可接受的,则返回 true | 
|      */ | 
|     public static HostnameVerifier UnSafeHostnameVerifier = new HostnameVerifier() { | 
|         @Override | 
|         public boolean verify(String hostname, SSLSession session) { | 
|             return true; | 
|         } | 
|     }; | 
| } |