/* 
 | 
 * 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; 
 | 
        } 
 | 
    }; 
 | 
} 
 |