溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android如何使用OkHttp請求自簽名的https網(wǎng)站

發(fā)布時間:2021-07-26 13:50:19 來源:億速云 閱讀:247 作者:小新 欄目:移動開發(fā)

這篇文章將為大家詳細講解有關Android如何使用OkHttp請求自簽名的https網(wǎng)站,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。

很多公司考慮到安全問題,項目中都采用https加密協(xié)議進行數(shù)據(jù)傳輸。但是一些公司又不想花一筆錢去CA申請證書,所以就采用自簽名的證書。

OkHttp默認是可以訪問通過CA認證的HTTPS鏈接,例如百度首頁也是https鏈接(https://www.baidu.com/)。但是如果是你們公司自簽名(即自己用keytool生成的證書,而不是采用通過CA認證的證書)的服務器,OkHttp是無法訪問的,例如訪問12306網(wǎng)站(https://kyfw.12306.cn/otn/),會報如下錯誤:

Android如何使用OkHttp請求自簽名的https網(wǎng)站

HTTPS的工作原理

HTTPS在傳輸數(shù)據(jù)之前需要客戶端(瀏覽器)與服務端(網(wǎng)站)之間進行一次握手,在握手過程中將確立雙方加密傳輸數(shù)據(jù)的密碼信息。握手過程的簡單描述如下:

  1. 瀏覽器將自己支持的一套加密算法、HASH算法發(fā)送給網(wǎng)站。

  2. 網(wǎng)站從中選出一組加密算法與HASH算法,并將自己的身份信息以證書的形式發(fā)回給瀏覽器。證書里面包含了網(wǎng)站地址,加密公鑰,以及證書的頒發(fā)機構(gòu)等信息。

  3. 瀏覽器獲得網(wǎng)站證書之后,開始驗證證書的合法性,如果證書信任,則生成一串隨機數(shù)字作為通訊過程中對稱加密的秘鑰。然后取出證書中的公鑰,將這串數(shù)字以及HASH的結(jié)果進行加密,然后發(fā)給網(wǎng)站。

  4. 網(wǎng)站接收瀏覽器發(fā)來的數(shù)據(jù)之后,通過私鑰進行解密,然后HASH校驗,如果一致,則使用瀏覽器發(fā)來的數(shù)字串使加密一段握手消息發(fā)給瀏覽器。

  5. 瀏覽器解密,并HASH校驗,沒有問題,則握手結(jié)束。接下來的傳輸過程將由之前瀏覽器生成的隨機密碼并利用對稱加密算法進行加密。

握手過程中如果有任何錯誤,都會使加密連接斷開,從而阻止了隱私信息的傳輸。

使用OKHTTP請求自簽名的https服務器數(shù)據(jù)

以下我們使用12306網(wǎng)站為例

1. 首先去12306網(wǎng)站首頁下載證書 http://www.12306.cn/

Android如何使用OkHttp請求自簽名的https網(wǎng)站

2. 將下載的證書srca.cer放到工程的assets文件夾下。

Android如何使用OkHttp請求自簽名的https網(wǎng)站

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>
   * <h4>Warning: Customizing Trusted Certificates is Dangerous!</h4>
   * <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. 最后能打印出這些信息就說明請求成功啦!

Android如何使用OkHttp請求自簽名的https網(wǎng)站

注意:別忘了加權(quán)限和依賴okhttp庫

關于“Android如何使用OkHttp請求自簽名的https網(wǎng)站”這篇文章就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI