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<? extends Certificate> 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 抓包配置

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352