美文网首页
Okhttp3 Https请求配置自签名证书

Okhttp3 Https请求配置自签名证书

作者: 王魔王 | 来源:发表于2019-11-16 09:08 被阅读0次

    有关Https请求的原理本篇帖子不做过多赘述,今天要做的就是带领大家配置Https请求中自签名证书的问题

    先贴一段OkHttp初始化的代码,这段代码地球人都知道

      okHttpClient = new OkHttpClient.Builder()
                        .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                        .readTimeout(5, TimeUnit.SECONDS)
                        .connectTimeout(5,TimeUnit.SECONDS)
                        .writeTimeout(5,TimeUnit.SECONDS)
                    `.hostnameVerifier()`//校验主机名,用于域名验证
                        `.sslSocketFactory()`//校验SSL证书
                        .build();
    

    上面代码中,绿色部分的代码就是跟Https相关的代码了

    先写一个简单的,即校验主机名的部分

    这里需要一个HostnameVerifier对象,这个对象直接new出来即可

    new HostnameVerifier() {
                            @Override
                            public boolean verify(String hostname, SSLSession session) {
                              //hostname:本次请求的域名,如果域名是我们认可的域名,那么返回true,本次请求继续执行,返回false会中断本次请求
                             return    hostname.equals("xxxxxxxxx");
          //return true;如果不进行校验,直接return true,代表信任所有请求
                            }
                        }
    

    那么代码就成了下面的样子👇

     okHttpClient = new OkHttpClient.Builder()
                        .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                        .readTimeout(5, TimeUnit.SECONDS)
                        .connectTimeout(5,TimeUnit.SECONDS)
                        .writeTimeout(5,TimeUnit.SECONDS)
                    .hostnameVerifier(new HostnameVerifier() {
                            @Override
                            public boolean verify(String hostname, SSLSession session) {
                              //hostname:本次请求的域名,如果域名是我们认可的域名,那么返回true,本次请求继续执行,返回false会中断本次请求
                             return    hostname.equals("xxxxxxxxx");
          //return true;如果不进行校验,直接return true,代表信任所有请求
                            }
                        })`//校验主机名,用于域名验证
                        .sslSocketFactory()`//校验SSL证书
                        .build();
    

    接下来是校验SSL证书部分的代码,这部分代码有点繁琐,大家照着步骤来即可

    .sslSocketFactory()这里需要两个参数,一个是SSLSocketFactory,一个是X509TrustManager,大家先不要关注这两个对象的作用,只需要关注这两个对象的初始化步骤即可
    1、将下载的证书放到assets目录中
    然后复制下面的代码,用于生成证书对象

    /**
         * 根据asset下证书的名字取出证书,然后变成流,在变成证书对象
         */
        private static X509Certificate readCert(Context context, String assetName) {
            InputStream inputStream = null;
            try {
                inputStream = context.getAssets().open(assetName);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
            X509Certificate cert = null;
            try {
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                cert = (X509Certificate) cf.generateCertificate(inputStream);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (Throwable ex) {
                }
            }
            return cert;
        }
    

    2、自定义MyTrustManager类,类结构如下

    /**
         * 实现了 X509TrustManager
         * 通过此类中的 checkServerTrusted 方法来确认服务器证书是否正确
         */
        private static final class MyTrustManager implements X509TrustManager {
            X509Certificate cert;
    
            MyTrustManager(X509Certificate cert) {
                this.cert = cert;
            }
    
            @Override// 我们在客户端只做服务器端证书校验。
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
    
            /**
             * @param chain 服务端返回的证书数组,因为服务器可能有多个https证书,我们在这里的
             *              逻辑就是拿到第一个证书,然后和本地证书判断,如果不一致,异常!!!
             */
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    //             确认服务器端证书和代码中 hard code 的 CRT 证书相同。
    //            这里因为我们服务器只有一个证书,没有遍历,如果有多个,这里是for循环取出挨个判断
                if (chain[0].equals(this.cert)) {
                    return;
                }
                throw new CertificateException("checkServerTrusted No trusted server cert found!");
            }
    
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
        }
    

    3、定义初始化SslSocketFactory的方法

    //通过下载的证书文件、自定义的MyTrustManager对象来初始化SslSocketFactory
     private  void initSslSocketFactory(){
            try {
                sslContext = SSLContext.getInstance("TLS");
                //从assets文件夹下根据证书名字读取证书,变成一个可用的证书对象
                x509Certificate = readCert(App.context, CERTIFICATE_NAME);
                //校验服务端和本地证书是否一致
                mTrustManager = new MyTrustManager(x509Certificate);
                //初始化必要的对象,固定格式直接使用即可
                sslContext.init(null, new TrustManager[]{
                        mTrustManager
                }, new java.security.SecureRandom());
                mSslSocketFactory = sslContext.getSocketFactory();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    

    到此,有关SslSocketFactory的构建工作就完成了,我们只需要在OkHttp的初始化代码中使用有关对象即可
    为了方便使用,我们应该把上面的代码都统一封装到一个工具类中
    于是代码就成了现在的样子

    /**
     * @Author:AbnerMing
     * @Description:
     * @Date:2019/11/7 8:53
     */
    public class SSLSocketFactoryUtils {
        private static final String HOST_NAME = "xxxxxx";//请求服务器的主机名称
        private static String CERTIFICATE_NAME = "xxxx.crt";//下载的证书的文件名称
        //证书
        private X509Certificate x509Certificate;
        //需要配置给ok的SSLSocketFactory
        private SSLSocketFactory mSslSocketFactory;
        private SSLContext sslContext;
    
        //证书管理者
        private MyTrustManager mTrustManager;
    
        private static  SSLSocketFactoryUtils instance;
    
        private SSLSocketFactoryUtils() {
          initSslSocketFactory();
        }
    
        private  void initSslSocketFactory(){
            try {
                sslContext = SSLContext.getInstance("TLS");
                //从assets文件夹下根据证书名字读取证书,变成一个可用的证书对象
                x509Certificate = readCert(App.context, CERTIFICATE_NAME);
                //校验服务端和本地证书是否一致
                mTrustManager = new MyTrustManager(x509Certificate);
                //初始化必要的对象,固定格式直接使用即可
                sslContext.init(null, new TrustManager[]{
                        mTrustManager
                }, new java.security.SecureRandom());
                mSslSocketFactory = sslContext.getSocketFactory();
    
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
    
        public static SSLSocketFactoryUtils getInstance() {
            if (instance == null) {
                instance=new SSLSocketFactoryUtils();
            }
            return instance;
        }
    
        /**
         * 根据asset下证书的名字取出证书,然后变成流,在变成证书对象
         */
        private static X509Certificate readCert(Context context, String assetName) {
            InputStream inputStream = null;
            try {
                inputStream = context.getAssets().open(assetName);
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
            X509Certificate cert = null;
            try {
                CertificateFactory cf = CertificateFactory.getInstance("X.509");
                cert = (X509Certificate) cf.generateCertificate(inputStream);
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (inputStream != null) {
                        inputStream.close();
                    }
                } catch (Throwable ex) {
                }
            }
            return cert;
        }
    
        public  HostnameVerifier getHostnameVerifier() {
            return hostnameVerifier;
        }
    
        public SSLSocketFactory getmSslSocketFactory() {
            return mSslSocketFactory;
        }
    
        public MyTrustManager getmTrustManager() {
            return mTrustManager;
        }
    
        /**
         * 实现了 X509TrustManager
         * 通过此类中的 checkServerTrusted 方法来确认服务器证书是否正确
         */
        private static final class MyTrustManager implements X509TrustManager {
            X509Certificate cert;
    
            MyTrustManager(X509Certificate cert) {
                this.cert = cert;
            }
    
            @Override// 我们在客户端只做服务器端证书校验。
            public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
            }
    
            /**
             * @param chain 服务端返回的证书数组,因为服务器可能有多个https证书,我们在这里的
             *              逻辑就是拿到第一个证书,然后和本地证书判断,如果不一致,异常!!!
             */
            @Override
            public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
    //             确认服务器端证书和代码中 hard code 的 CRT 证书相同。
    //            这里因为我们服务器只有一个证书,没有遍历,如果有多个,这里是for循环取出挨个判断
                if (chain[0].equals(this.cert)) {
                    return;
                }
                throw new CertificateException("checkServerTrusted No trusted server cert found!");
            }
    
            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new java.security.cert.X509Certificate[]{};
            }
        }
    
        /**
         * 服务器域名验证,拿到请求接口的域名和本地配的域名进行比较,如果一样返回True
         */
        private  final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
            @Override
            public boolean verify(String hostname, SSLSession session) {
                return hostname.equals(HOST_NAME);
              //return true;
            }
        };
    }
    

    最后使用

     okHttpClient = new OkHttpClient.Builder()
                        .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                        .readTimeout(5, TimeUnit.SECONDS)
                        .connectTimeout(5,TimeUnit.SECONDS)
                        .writeTimeout(5,TimeUnit.SECONDS)
                        .hostnameVerifier(SSLSocketFactoryUtils.getInstance().getHostnameVerifier())
                        .sslSocketFactory(SSLSocketFactoryUtils.getInstance().getmSslSocketFactory(),
                                SSLSocketFactoryUtils.getInstance().getmTrustManager())
                        .build();
    

    相关文章

      网友评论

          本文标题:Okhttp3 Https请求配置自签名证书

          本文链接:https://www.haomeiwen.com/subject/whusictx.html