Android使用OkHttp请求自签名的https网站的示例

 更新时间:2017年09月29日 10:47:26   作者:wildma_  
本篇文章主要介绍了Android使用OkHttp请求自签名的https网站的示例,非常具有实用价值,需要的朋友可以参考下、

前言

很多公司考虑到安全问题,项目中都采用https加密协议进行数据传输。但是一些公司又不想花一笔钱去CA申请证书,所以就采用自签名的证书。

OkHttp默认是可以访问通过CA认证的HTTPS链接,例如百度首页也是https链接(https://www.baidu.com/)。但是如果是你们公司自签名(即自己用keytool生成的证书,而不是采用通过CA认证的证书)的服务器,OkHttp是无法访问的,例如访问12306网站(https://kyfw.12306.cn/otn/),会报如下错误:

这里写图片描述

HTTPS的工作原理

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

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

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

使用OKHTTP请求自签名的https服务器数据

以下我们使用12306网站为例

1. 首先去12306网站首页下载证书 http://www.12306.cn/

这里写图片描述

2. 将下载的证书srca.cer放到工程的assets文件夹下。

这里写图片描述

3. 添加HTTPS工具类

package com.alpha58.okhttp;

import android.content.Context;

import java.io.IOException;
import java.io.InputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.util.Arrays;
import java.util.Collection;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;

import okhttp3.OkHttpClient;

/**
 * Created by admin on 2017/03/12.
 */
public final class HTTPSUtils {
  private OkHttpClient client;

  public Context mContext;


  /**
   * 获取OkHttpClient实例
   * @return
   */
  public OkHttpClient getInstance()
  {
    return client;
  }

  /**
   * 初始化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();
    }
  }

  /**
   * 以流的方式添加信任证书
   */
  /**
   * 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}.
   * <p>
   * <p>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.
   * <p>
   * <p>
   * <h3>Warning: Customizing Trusted Certificates is Dangerous!</h3>
   * <p>
   * <p>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<? 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);
    }
  }


}

4.代码中请求

public void getHttpsHtml(View view) {
    Request request = new Request.Builder()
        .url("https://kyfw.12306.cn/otn/")
        .build();
    HTTPSUtils httpsUtils = new HTTPSUtils(this);
    httpsUtils.getInstance().newCall(request).enqueue(new Callback() {
      @Override
      public void onFailure(Call call, IOException e) {
        System.out.println("--------------onFailure--------------" + e.toString());
      }

      @Override
      public void onResponse(Call call, Response response) throws IOException {
        System.out.println("--------------onResponse--------------" + response.body().string());
      }
    });
  }
 

5. 最后能打印出这些信息就说明请求成功啦!

这里写图片描述

注意:别忘了加权限和依赖okhttp库

Demo地址:https://github.com/Alpha58/okhttps

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • android中暂停背景音乐

    android中暂停背景音乐

    本文给大家分享的是在Android中暂停背景音乐的方法的示例,以及度娘上其他网友的实现方法,感觉非常不错,这里全部推荐给大家,有需要的小伙伴可以参考下。
    2015-05-05
  • Android实现遮罩层(蒙板)效果

    Android实现遮罩层(蒙板)效果

    这篇文章主要为大家详细介绍了Android实现遮罩层效果,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-10-10
  • Android开发使用Messenger及Handler进行通信的方法示例

    Android开发使用Messenger及Handler进行通信的方法示例

    这篇文章主要介绍了Android开发使用Messenger及Handler进行通信的方法,结合实例形式分析了Android使用Messenger及Handler定义客户端与服务器端实现通信的相关操作技巧,需要的朋友可以参考下
    2017-12-12
  • 利用kotlin实现一个饼图实例代码

    利用kotlin实现一个饼图实例代码

    饼状图是以不同颜色的圆的切片表示的值。下面这篇文章主要给大家介绍了关于利用kotlin实现一个饼图的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧。
    2017-12-12
  • Flutter Widget 之package mason实现详解

    Flutter Widget 之package mason实现详解

    这篇文章主要为大家介绍了Flutter Widget 之package: mason实现详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-11-11
  • Android中的build.gradle文件深入讲解

    Android中的build.gradle文件深入讲解

    这篇文章主要给大家介绍了关于Android中build.gradle文件的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-10-10
  • Android编程实现popupwindow定时消失的方法

    Android编程实现popupwindow定时消失的方法

    这篇文章主要介绍了Android编程实现popupwindow定时消失的方法,结合实例形式分析了Android使用定时器实现popupwindow定时消失的相关操作技巧,需要的朋友可以参考下
    2018-01-01
  • Android工程:引用另一个Android工程的方法详解

    Android工程:引用另一个Android工程的方法详解

    本篇文章是对在Android中引用另一个Android工程的方法进行了详细的分析介绍。需要的朋友参考下
    2013-05-05
  • Android Recyclerview实现上拉加载更多功能

    Android Recyclerview实现上拉加载更多功能

    在项目中使用列表的下拉刷新和上拉加载更多是很常见的功能。下文给大家带来了Android Recyclerview上拉加载更多功能,需要的朋友参考下吧
    2017-05-05
  • Android中封装RecyclerView实现添加头部和底部示例代码

    Android中封装RecyclerView实现添加头部和底部示例代码

    这篇文章主要给大家介绍了关于Android中封装RecyclerView实现添加头部和底部的相关资料,网上这方面的资料很多,但都不是自己需要的,索性自己写一个分享出来供大家参考学习,需要的朋友们下面随着小编一起来学习学习吧。
    2017-08-08

最新评论