2019-07-16 Charles 在安卓7.0以上系统抓包辅助

本文仅真对OkhttpClient进行抓包适配,参考文章:原文

1.在创建OkHttpClient的时候,调用OkHttpClient.Builder的build()方法前调用 以下代码
CharlesUtils.getInstance().setCharlesSSL(builder,
CharlesUtils.getInstance().getCharlesInputStream(context,"charles.pem"))
2.辅助工具类
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Environment;
import android.util.Log;
import java.io.*;
import java.security.*;
import java.security.cert.*;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.Collection;
import javax.net.ssl.*;
import okhttp3.OkHttpClient;

/**
 * 参考:https://www.jianshu.com/p/cc7ae2f96b64
 * app抓包辅助,抓包条件:
 * 1.需要在sd卡下面保存charles的pem文件,文件名称为charles.pem
 * 2.需要SD卡读取权限
 * 3.尽量只对测试渠道的设置抓包
 * 4.如果OkHttpClient是单利,启动APP后才设置代理,则需要杀掉APP后重新打开
 * 5.如果打开了代理,又不能正确读取设置代理,则会出现无法访问的情况
 * Author:caiyoufei
 * Date:19-7-16
 * Time:下午4:49
 */
public class CharlesUtils {
  private static class SingleTonHolder {
    private static final CharlesUtils INSTANCE = new CharlesUtils();
  }

  public static CharlesUtils getInstance() {
    return SingleTonHolder.INSTANCE;
  }

  private CharlesUtils() {
  }

  /**
   * 获取chales的pem文件流
   *
   * @param context 上下文
   * @param sdRootPemName sd卡下面的pem文件名称
   * @return pem文件流
   */
  public FileInputStream getCharlesInputStream(Context context, String sdRootPemName) {
    if (!isWifiProxy(context)) {
      Log.e("CharlesUtils", "没有使用代理,不用抓包");
      return null;
    }
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M &&
        context.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
            == PackageManager.PERMISSION_GRANTED
    ) {
      try {
        File pemFile = new File(Environment.getExternalStorageDirectory(), sdRootPemName);
        if (pemFile.exists()) {
          return new FileInputStream(pemFile);
        } else {
          Log.e("CharlesUtils", "SD卡没有pem文件");
          return null;
        }
      } catch (FileNotFoundException e) {
        Log.e("CharlesUtils", "读取SD卡pem文件失败");
        e.printStackTrace();
        return null;
      }
    } else {
      Log.e("CharlesUtils", "没有SD卡权限读取pem文件");
      return null;
    }
  }

  /**
   * 是否使用了代理
   */
  private boolean isWifiProxy(Context context) {
    final boolean IS_ICS_OR_LATER = Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH;
    String proxyAddress;
    int proxyPort;
    if (IS_ICS_OR_LATER) {
      proxyAddress = System.getProperty("http.proxyHost");
      String portStr = System.getProperty("http.proxyPort");
      proxyPort = Integer.parseInt((portStr != null ? portStr : "-1"));
    } else {
      proxyAddress = android.net.Proxy.getHost(context);
      proxyPort = android.net.Proxy.getPort(context);
    }
    return (!TextUtils.isEmpty(proxyAddress)) && (proxyPort != -1);
  }

  /**
   * 为okhttp客户端设置抓包验证
   *
   * @param builder okhttp客户端builder
   * @param certificate 自签名证书的输入流
   */
  public void setCharlesSSL(OkHttpClient.Builder builder, InputStream certificate) {
    try {
      if (builder == null || certificate == null) return;
      X509TrustManager trustManager = trustManagerForCertificates(certificate);
      if (trustManager != null) {
        SSLContext sslContext = SSLContext.getInstance("TLS");
        //使用构建出的trustManger初始化SSLContext对象
        sslContext.init(null, new TrustManager[] { trustManager }, null);
        //获得sslSocketFactory对象
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
        builder.sslSocketFactory(sslSocketFactory, trustManager);
        Log.e("CharlesUtils", "开启抓包");
      } else {
        Log.e("CharlesUtils", "验证pem文件失败");
      }
    } catch (Exception e) {
      e.printStackTrace();
      Log.e("CharlesUtils", "验证pem文件失败:" + e.getMessage());
    }
  }

  /**
   * 获去信任自签证书的trustManager
   *
   * @param input 自签证书输入流
   * @return 信任自签证书的trustManager
   */
  private X509TrustManager trustManagerForCertificates(InputStream input) {
    try {
      CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
      //通过证书工厂得到自签证书对象集合
      Collection certificates =
          certificateFactory.generateCertificates(input);
      if (certificates.isEmpty()) {
        throw new IllegalArgumentException("expected non-empty set of trusted certificates");
      }
      //为证书设置一个keyStore
      char[] password = "password".toCharArray(); // Any password will work.
      KeyStore keyStore = newEmptyKeyStore(password);
      if (keyStore == null) return null;
      int index = 0;
      //将证书放入keystore中
      for (Certificate certificate : certificates) {
        String certificateAlias = Integer.toString(index++);
        keyStore.setCertificateEntry(certificateAlias, certificate);
      }
      // Use it to build an X509 trust manager.
      //使用包含自签证书信息的keyStore去构建一个X509TrustManager
      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];
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }

  private KeyStore newEmptyKeyStore(char[] password) {
    try {
      KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      keyStore.load(null, password);
      return keyStore;
    } catch (Exception e) {
      e.printStackTrace();
      return null;
    }
  }
}

附:Charles 抓包配置

你可能感兴趣的:(2019-07-16 Charles 在安卓7.0以上系统抓包辅助)