android 使用https请求

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/JerryWu145/article/details/78467424

今年年初就已经吵吵着要把大部分的服务端由http转成https了,但是由于很多公司还是比较懒,而且有的公司可能不想再多掏一些钱去对自己的网址加入CA认证,所以这件事就一直拖下来了,但是随着用户数据越来越多暴露在一些不法分子眼前,所以信息安全越来越被用户重视,一些金融、贷款公司已经开始使用这种技术了,今天就来讲解一下android上面的通过https对服务器进行请求。


首先先来解答一下疑惑,我们在做测试的时候经常会对https://www.baidu.com或者https://www.github.com进行一个网络工具类的测试,但是不知道大家有没有注意到,这些网址链接之前全都是https://的,为什么我们在访问的时候完全没有报错,也能够正常的访问呢。ok,其实这两个大网站已经被CA认证过了,而CA证书已经被android设备默认支持了,所以这些CA认证的网站可以直接通过HttpUrlConnection进行连接,当然也可以通过HttpsUrlConnection进行连接。


但是除了被CA认证的网址就不能够正常的连接了,比如我们访问神奇的网站12306时,在他的注册页面其实已经升级成了Https协议的了,而且这个网站的证书是没有被CA认证过的。我们来分别对比下百度的 和github 还有我们神奇的网站12306的 签名文件~~~


见图:


第一张是百度网站的 证书文件信息




这张是github的证书信息




这张当然就是12306的喽




大家可以自己打开电脑浏览器,打开开发者工具(或者F12),然后点击security就能看到这个信息了,如果对证书感兴趣,还可以点击上边的“view certificate”去查看证书的信息

我们通过上面的三个图,不难分析出baidu 和github使用的是CA的认证的证书,而12306并没有,12306用的是自己证书

这就导致了12306需要我们特别去写一下网络请求,而百度和github则不需要。


那我们怎么去正常的访问12306呢,这里我封装了一个网络请求类,核心代码在这里:


try {
                    URL url = new URL(path);
                    //1.改成s
                    HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
                    //2.SSLContext 初始化
                    SSLContext tls = SSLContext.getInstance("TLS");
                    MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context));

                    TrustManager[] trustManagers={myX509TrustManager};
                    tls.init(null,trustManagers,new SecureRandom());
                    //3.ssl工厂
                    SSLSocketFactory factory = tls.getSocketFactory();
                    //4.添加一个主机名称校验器
                    conn.setHostnameVerifier(new HostnameVerifier() {
                        @Override
                        public boolean verify(String hostname, SSLSession session) {
                                if (hostname.equals("kyfw.12306.cn")) {
                                return true;
                            }else{
                                return false;
                                }

                        }
                    });
                    conn.setSSLSocketFactory(factory);
                    conn.setRequestMethod("GET");
                    conn.setReadTimeout(5000);
                    conn.setConnectTimeout(5000);
                    conn.connect();
                    InputStream inputStream = conn.getInputStream();

                    StringBuilder sb=new StringBuilder();
                    int flag;
                    byte[] buf=new byte[1024];
                    while((flag=inputStream.read(buf))!=-1){
                        sb.append(new String(buf,0,flag));
                    }
                    String s = sb.toString();
                    //调用对方传入callback完成回调操作
                    callBack.onSuccess(s);
                } catch (Exception e) {
                    e.printStackTrace();
                    callBack.onFail(e);
                }

主要的思路是:

1.先将我们常用的httpurlconnection变成HttpsConnection

2.初始化我们的SSLContext 这个类是ssl的一个帮助类,能够帮助我们去验证我们的证书文件

这个环节中其实稍微多一些操作,比如获取SSLContext的实例

SSLContext tls = SSLContext.getInstance("TLS");
还有创建SSLContext的初始化参数,这个MyX509TrustManager是我自己封装的类

MyX509TrustManager myX509TrustManager = new MyX509TrustManager(getX509Certificate(context));


封装的类如下

package com.guaju.httpsrequesttest.http;

import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.SignatureException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.X509TrustManager;

/**
 * Created by guaju on 2017/11/7.
 */

public class MyX509TrustManager implements X509TrustManager {
    //如果需要对证书进行校验,需要这里去实现,如果不实现的话是不安全 
    X509Certificate mX509Certificate;


    public MyX509TrustManager(X509Certificate mX509Certificate) {
        this.mX509Certificate = mX509Certificate;
    }

    @Override
    public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

    }

    @Override
    public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
       for (X509Certificate certificate:chain){
           //检查证书是否有效
           certificate.checkValidity();
           try {
               certificate.verify(mX509Certificate.getPublicKey());
           } catch (NoSuchAlgorithmException e) {
               e.printStackTrace();
           } catch (InvalidKeyException e) {
               e.printStackTrace();
           } catch (NoSuchProviderException e) {
               e.printStackTrace();
           } catch (SignatureException e) {
               e.printStackTrace();
           }

       }


    }

    @Override
    public X509Certificate[] getAcceptedIssuers() {
        return new X509Certificate[0];
    }
}


而上个类中传入的构造方法中的证书文件

X509Certificate mX509Certificate;


这个证书文件需要通过我们下载的证书去获得,12306的证书在官网首页,而我们如果开发自己的公司的网站的话,后台应该会给我们一份,我们拿着这个证书放到assets目录下(或者后台的服务器上)就可以转变成我们自己的签名了,下午是12306下载证书的地方:



下方是提供的将assets中的证书文件流转化成证书的方法,很简单:

//拿到自己的证书
   X509Certificate getX509Certificate(Context context) throws IOException, CertificateException {
       InputStream in = context.getAssets().open("srca.cer");
       CertificateFactory instance = CertificateFactory.getInstance("X.509");
       X509Certificate certificate = (X509Certificate) instance.generateCertificate(in);
       return certificate;
   }


创建TrustManager数组,将上方的MyX509TrustManager放进数组中去

TrustManager[] trustManagers={myX509TrustManager};
然后调用SSLContext的初始化方法

tls.init(null,trustManagers,new SecureRandom());
3.上方的sslcontext初始化好之后,我们就可以做SSLSocketFactory的创建了

SSLSocketFactory factory = tls.getSocketFactory();
然后把这个工厂为我们的连接conn设置上

conn.setSSLSocketFactory(factory);
4.添加主机名称校验器,记住这里的主机名称地址需要查看我们的证书的配置,不能瞎写


证书配置如下图


下方的名称及上方的常用名称

conn.setHostnameVerifier(new HostnameVerifier() {
    @Override
    public boolean verify(String hostname, SSLSession session) {
            if (hostname.equals("kyfw.12306.cn")) {
            return true;
        }else{
            return false;
            }

    }
});
这样的话就可以访问12306的注册页面了。

ok,为了方便大家使用,我已经提交到github了,如果大家觉得不错,记得star 或者fork哦 ,多谢~~~

github:https://github.com/guaju/HttpsRequestTest



展开阅读全文

没有更多推荐了,返回首页