OKHttp实现Https请求

一.简介

1.HTTPS定义

HTTPS全称为Hyper Text Transfer Protocol over Secure Socket Layer或是Hypertext Transfer Protocol Secure


中文含义为“超文本传输安全协议” 。是以安全为目标的HTTP通道。在HTTP的基础上通过传输加密身份认证保证了传输过程的安全性 。HTTPS 在HTTP 的基础下加入SSL,HTTPS 的安全基础是 SSL,因此加密的详细内容就需要 SSL。 HTTPS 存在不同于 HTTP 的默认端口及一个加密/身份验证层(在 HTTP与 TCP 之间)。这个系统提供了身份验证与加密通讯方法。它被广泛用于万维网上安全敏感的通讯,例如交易支付等方面  。

2.HTTPS改进目标

HTTPS 协议是由 HTTP 加上 TLS/SSL 协议构建的可进行加密传输、身份认证的网络协议,主要通过数字证书加密算法非对称密钥等技术完成互联网数据传输加密,实现互联网传输安全保护。设计目标主要有三个。


  <1> 数据保密性:保证数据内容在传输的过程中不会被第三方查看。就像快递员传递包裹一样,都进行了封装,别人无法获知里面装了什么   。


  <2> 数据完整性:及时发现被第三方篡改的传输内容。就像快递员虽然不知道包裹里装了什么东西,但他有可能中途掉包,数据完整性就是指如果被掉包,我们能轻松发现并拒收 。


  <3> 身份校验安全性:保证数据到达用户期望的目的地。就像我们邮寄包裹时,虽然是一个封装好的未掉包的包裹,但必须确定这个包裹不会送错地方,通过身份校验来确保送对了地方。 

3.HTTPS的工作原理

<1> 双向的身份认证


客户端服务端传输数据之前,会通过基于X.509证书对双方进行身份认证 。

   (1) 客户端发起 SSL 握手消息给服务端要求连接。
 

   (2) 服务端将证书发送给客户端。


   (3) 客户端检查服务端证书,确认是否由自己信任的证书签发机构签发。 如果不是,将是否继续通讯的决定权交给用户选择 ( 注意,这里将是一个安全缺陷 )。如果检查无误或者用户选择继续,则客户端认可服务端的身份。


   (4) 服务端要求客户端发送证书,并检查是否通过验证。失败则关闭连接,认证成功则从客户端证书中获得客户端的公钥,一般为1024位或者 2048位。到此,服务器客户端双方的身份认证结束,双方确保身份都是真实可靠的。

<2> 数据传输的机密性

客户端和服务端在开始传输数据之前,会协商传输过程需要使用的加密算法。 客户端发送协商请求给服务端, 其中包含自己支持的非对称加密的密钥交换算法 ( 一般是RSA),,数据签名摘要算法 ( 一般是SHA或者MD5) ,加密传输数据的对称加密算法 ( 一般是DES),以及加密密钥的长度。 服务端接收到消息之后,选中安全性最高的算法,并将选中的算法发送给客户端,完成协商。

客户端生成随机的字符串,通过协商好的非对称加密算法,使用服务端的公钥对该字符串进行加密,发送给服务端。 服务端接收到之后,使用自己的私钥解密得到该字符串。在随后的数据传输当中,使用这个字符串作为密钥进行对称加密  。

二.代码实现

1.使用okhttp实现https请求

实现https我们需要一份ca证书,购买的证书,格式为.pfx,带有公钥和私钥,附带一个密码。还有一种格式为.cer的证书,这种证书是没有私钥的。 服务器会将证书配置到tomcat中,客户端则存放在本地,app启动的时候加载进去。

.pfx格式和.cer格式的区别:

<1> 带有私钥的证书 
  由Public Key Cryptography Standards #12,PKCS#12标准定义,包含了公钥和私钥的二进制格式的证书形式,以pfx作为证书文件后缀名。 
   

 

<2> 二进制编码的证书 
  证书中没有私钥,DER 编码二进制格式的证书文件,以cer作为证书文件后缀名。

<3> Base64编码的证书 
      证书中没有私钥,BASE64 编码格式的证书文件,也是以cer作为证书文件后缀名。

2.实现需要

OkHttp配置HTTPS访问。需要操作以下三个类


<1> SSLSocketFactory:ssl套接字工厂。

<2> X509TrustManager:证书信任器管理类。

<3> HostnameVerifier:验证主机名。

3.代码实现

OkHttp设置

mOkHttpClient=new OkHttpClient.Builder()
        .connectTimeout(DataConstant.nettimeout, TimeUnit.SECONDS)//连接时间
        .readTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//读时间
        .writeTimeout(DataConstant.nettimeout,TimeUnit.SECONDS)//写时间
        .retryOnConnectionFailure(true)//连接失败后是否重新连接
                    
        .sslSocketFactory(sslSocketFactory,trustManager)//Https证书
                    
        .build();

即:.sslSocketFactory(sslSocketFactory,trustManager)//Https证书

通过sslSocketFactory方法设置https证书

获取 SSLSocketFactory和 X509TrustManager 对象

下载相关证书放到asset目录下

代码

public class MainApplication extends Application {

    private static SSLSocketFactory sslSocketFactory;
    private static X509TrustManager x509TrustManager;

    @Override
    public void onCreate() {
        super.onCreate();
        initx509TrustManagerAndsslSocketFactory();
    }

    /**
     * 初始化
     * */

    private void initx509TrustManagerAndsslSocketFactory(){
        InputStream inputStream = null;
        try {
            inputStream = getApplicationContext().getAssets().open("srca.cer"); //得到证书的输入流
            try {
                if(null!=inputStream){
                    x509TrustManager = trustManagerForCertificates(inputStream);//以流的方式读入证书
                    if(null!=x509TrustManager){
                        SSLContext sslContext = SSLContext.getInstance("TLS");
                        sslContext.init(null, new TrustManager[]{x509TrustManager}, null);
                        sslSocketFactory = sslContext.getSocketFactory();
                    }
                }
            } catch (GeneralSecurityException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            if(null!=inputStream){
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        MainApplication.setSslSocketFactory(sslSocketFactory);
        MainApplication.setX509TrustManager(x509TrustManager);
    }

    /**
     * 以流的方式添加信任证书
     */

    private X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException {
        CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
        Collection<? extends Certificate> certificates = certificateFactory.generateCertificates(in);
        if (certificates.isEmpty()) {
            throw new IllegalArgumentException("expected non-empty set of trusted certificates");
        }

        // Put the certificates a key store.
        char[] password = "password".toCharArray(); // Any password will work.
        KeyStore keyStore = newEmptyKeyStore(password);
        int index = 0;
        for (Certificate certificate : certificates) {
            String certificateAlias = Integer.toString(index++);
            keyStore.setCertificateEntry(certificateAlias, certificate);
        }

        // Use it to build an X509 trust manager.
        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, password);
        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);
        TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
        if (trustManagers.length != 1 || !(trustManagers[0] instanceof X509TrustManager)) {
            throw new IllegalStateException("Unexpected default trust managers:" + Arrays.toString(trustManagers));
        }
        return (X509TrustManager) trustManagers[0];
    }

    /**
     * 添加password
     * @param password
     * @return
     * @throws GeneralSecurityException
     */

    private KeyStore newEmptyKeyStore(char[] password) throws GeneralSecurityException {
        try {
            KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); // 这里添加自定义的密码,默认
            InputStream in = null; // By convention, 'null' creates an empty key store.
            keyStore.load(in, password);
            return keyStore;
        } catch (IOException e) {
            throw new AssertionError(e);
        }
    }

    public static SSLSocketFactory getSslSocketFactory() {
        return sslSocketFactory;
    }

    public static void setSslSocketFactory(SSLSocketFactory sslSocketFactory) {
        MainApplication.sslSocketFactory = sslSocketFactory;
    }

    public static X509TrustManager getX509TrustManager() {
        return x509TrustManager;
    }

    public static void setX509TrustManager(X509TrustManager x509TrustManager) {
        MainApplication.x509TrustManager = x509TrustManager;
    }
}

HostnameVerifier验证

public class OkHttpHostnameVerifier implements HostnameVerifier {

    /**
     * OkHttp验证主机名
     *
     * @return false 不信任主机
     * 默认 false
     */

    @Override
    public boolean verify(String hostname, SSLSession session) {

        /**
         * 这里验证主机名是XXX.XXX.com就返回true。信任这个域名。也可以结合白名单等动态校验。
         * */

        if ("XXX.XXX.com".equals(hostname)) {
            return true;
        } else {
            HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
            return hv.verify(hostname, session);
        }
    }
}

添加HostnameVerifier验签

//创建OkHttpClient对象
mOkHttpClient = new OkHttpClient.Builder()
        .connectTimeout(20, TimeUnit.SECONDS)//连接时间
        .readTimeout(20, TimeUnit.SECONDS)//读时间
        .writeTimeout(20, TimeUnit.SECONDS)//写时间
        .retryOnConnectionFailure(true)//连接失败后是否重新连接
        .dns(new OkHttpDNS())//DNS域名解析
        .eventListener(new OkHttpEventListener())//EventListener监听 可以统计各种时间
                
        .hostnameVerifier(new OkHttpHostnameVerifier())//验签
                
        .build();

三.OkHttp防止抓包

1.描述

客户端请求接口数据时,可以使用一些apk完成抓包操作。会获取我们请求的参数和请求成功后的报文。常见的抓包工具有HttpCanary和Fiddler。那么我们在使用OkHttp时,如何防止被这些apk转包呢?其实OkHttp已经为我们提供了相应的方法。

2.原理

OkHttp使用ProxySelector来获取代理信息,在构造OkHttpClient时是可以设置的,其默认值是ProxySelector.getDefault(),该默认值会反应出系统的代理信息。 
那么我们就可以提供自己的ProxySelector实现来达到绕过系统代理的能力。

3.代码

//配置okHttp的参数
OkHttpClient okHttpClient = new OkHttpClient.Builder()
        .connectTimeout(nettimeout, TimeUnit.SECONDS)


        //proxySelector方法设置防止抓包
        .proxySelector(new ProxySelector() {
            @Override
            public List<Proxy> select(URI uri) {
                return Collections.singletonList(Proxy.NO_PROXY);
            }

            @Override
            public void connectFailed(URI uri, SocketAddress sa, IOException ioe) {

            }
        })


        .build();

注:这里只是测试,用的12306以前的证书。现在的12306已经不需要这个证书了,使用OkHttp不用配置sslSocketFactory也能访问。

完整代码: https://github.com/wujianning/CustomOkHttps

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值