OKHTTP通信使用(三)HTTPS


HTTPS与HTTP的区别:

HTTPS和HTTP的区别主要为以下四点:
一、https协议需要到ca申请证书,一般免费证书很少,需要交费。
二、http是 超文本传输协议,信息是明文传输,https 则是具有 安全性的 ssl加密传输协议。
三、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
四、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的 网络协议,比http协议安全。

OKHTTP默认是支持通过CA认证的HTTPS请求的,例如利用get请求去获得Https://www.baidu.com/的信息是可以直接拿到的。

但是对于没有通过CA认证的网站,OKHTTP是无法进行HTTPS请求获取数据的,例如获取12306网站的数据:https://kyfw.12306.cn/otn/


所以需要进行证书信任才能正常的进行访问,在OKHTTP的WIKI中就已经介绍了如何使用HTTPS进行通信。


HTTPS通信过程:


HTTPS在传输数据之前需要客户端(浏览器)与服务端(网站)之间进行一次握手,在握手过程中将确立双方加密传输数据的密码信息。握手过程的简单描述如下:

  1. 浏览器将自己支持的一套加密算法、HASH算法发送给网站。
  2. 网站从中选出一组加密算法与HASH算法,并将自己的身份信息以证书的形式发回给浏览器。证书里面包含了网站地址,加密公钥,以及证书的颁发机构等信息。
  3. 浏览器获得网站证书之后,开始验证证书的合法性,如果证书信任,则生成一串随机数字作为通讯过程中对称加密的秘钥。然后取出证书中的公钥,将这串数字以及HASH的结果进行加密,然后发给网站。
  4. 网站接收浏览器发来的数据之后,通过私钥进行解密,然后HASH校验,如果一致,则使用浏览器发来的数字串使加密一段握手消息发给浏览器。
  5. 浏览器解密,并HASH校验,没有问题,则握手结束。接下来的传输过程将由之前浏览器生成的随机密码并利用对称加密算法进行加密。

握手过程中如果有任何错误,都会使加密连接断开,从而阻止了隐私信息的传输。

以下就是使用OKHTTP进行HTTPS进行通信的实例:

第一步:在12306官网上下载根证书:

srca.cer,并放在Assets文件夹中。

第二步:添加HTTPSUtils:

public final class HTTPSUtils {
    private  OkHttpClient client;

    public Context mContext;


    /**
     * 初始化HTTPS,添加信任证书
     * @param context
     */
    public HTTPSUtils(Context context) {
        mContext = context;
        X509TrustManager trustManager;
        SSLSocketFactory sslSocketFactory;
        final InputStream inputStream;
        try {
            inputStream = mContext.getAssets().open("srca.cer"); // 得到证书的输入流
            try {

                trustManager = trustManagerForCertificates(inputStream);//以流的方式读入证书
                SSLContext sslContext = SSLContext.getInstance("TLS");
                sslContext.init(null, new TrustManager[]{trustManager}, null);
                sslSocketFactory = sslContext.getSocketFactory();

            } catch (GeneralSecurityException e) {
                throw new RuntimeException(e);
            }

            client = new OkHttpClient.Builder()
                    .sslSocketFactory(sslSocketFactory, trustManager)
                    .build();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 测试代码
     * @throws Exception
     */
    public void run() throws Exception {
        Request request = new Request.Builder()
               .url("https://kyfw.12306.cn/otn/")
                .build();


        client.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

                if (!response.isSuccessful()) throw new IOException("Unexpected code " + response);

                Headers responseHeaders = response.headers();
                for (int i = 0; i < responseHeaders.size(); i++) {
                    System.out.println(responseHeaders.name(i) + ": " + responseHeaders.value(i));
                }

                System.out.println(response.body().string());
            }
        });
    }


    /**
     * 以流的方式添加信任证书
     */
    /**
     * Returns a trust manager that trusts {@code certificates} and none other. HTTPS services whose
     * certificates have not been signed by these certificates will fail with a {@code
     * SSLHandshakeException}.
     * 

*

This can be used to replace the host platform's built-in trusted certificates with a custom * set. This is useful in development where certificate authority-trusted certificates aren't * available. Or in production, to avoid reliance on third-party certificate authorities. *

*

*

Warning: Customizing Trusted Certificates is Dangerous!

*

*

Relying on your own trusted certificates limits your server team's ability to update their * TLS certificates. By installing a specific set of trusted certificates, you take on additional * operational complexity and limit your ability to migrate between certificate authorities. Do * not use custom trusted certificates in production without the blessing of your server's TLS * administrator. */ private X509TrustManager trustManagerForCertificates(InputStream in) throws GeneralSecurityException { CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509"); Collection 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); } } }


第三步:在onCreate调用:

 HTTPSUtils customTrust = new HTTPSUtils(this);

        try {
            customTrust.run();


        } catch (Exception e) {
            e.printStackTrace();
        }


最后输出的信息:

06-15 08:54:01.728 5913-5926/? I/System.out: Date: Wed, 15 Jun 2016 12:54:03 GMT
06-15 08:54:01.728 5913-5926/? I/System.out: Server: Apache-Coyote/1.1
06-15 08:54:01.728 5913-5926/? I/System.out: X-Powered-By: Servlet 2.5; JBoss-5.0/JBossWeb-2.1
06-15 08:54:01.728 5913-5926/? I/System.out: Set-Cookie: JSESSIONID=0A01D727FCF585CB10CC82BB7CAB47A3A505745087; Path=/otn
06-15 08:54:01.728 5913-5926/? I/System.out: ct: c1_39
06-15 08:54:01.728 5913-5926/? I/System.out: Content-Type: text/html;charset=utf-8
06-15 08:54:01.728 5913-5926/? I/System.out: Content-Language: zh-CN
06-15 08:54:01.728 5913-5926/? I/System.out: Transfer-Encoding: chunked
06-15 08:54:01.728 5913-5926/? I/System.out: Set-Cookie: BIGipServerotn=668401930.64545.0000; path=/
06-15 08:54:01.728 5913-5926/? I/System.out: X-Via: 1.1 hljshwt42:9 (Cdn Cache Server V2.0)
06-15 08:54:01.728 5913-5926/? I/System.out: Connection: keep-alive
06-15 08:54:01.728 5913-5926/? I/System.out: X-Cdn-Src-Port: 58011
06-15 08:54:01.776 5913-5926/? I/System.out: 
06-15 08:54:01.788 5913-5926/? I/System.out: 
06-15 08:54:01.788 5913-5926/? I/System.out: 
06-15 08:54:01.788 5913-5926/? I/System.out: 
06-15 08:54:01.788 5913-5926/? I/System.out: 
06-15 08:54:01.788 5913-5926/? I/System.out: 
06-15 08:54:01.800 5913-5926/? I/System.out: 
                    
                    

你可能感兴趣的:(Android实用进阶)