【Charles】弱网测试

599 阅读4分钟

「这是我参与2022首次更文挑战的第30天,活动详情查看:2022首次更文挑战

一、前言

下载地址

(1)Ubuntu 安装

下载后:

# 1. 解压
$ sudo tar zxvf charles-proxy-4.2.8_amd64.tar.gz

# 2. 移动
$ sudo mv charles /opt/charles

# 3. 使其可以命令启动
$ sudo vi /etc/profile
# 在最后一行加入
export PATH=$PATH:/opt/charles/bin

# 4. 使当前终端生效,可启动
$ sudo source /etc/profile

**激活码生成:**www.zzzmode.com/mytools/cha…

打开 charles -> Help -> Register Charles

# 例如
Registered Name:	ddd
License Key:	fa1b27097ca4845676

证书

  1. 下载 CharlesSSL 根证书:Help -> SSL Proxying -> Install Charles Root Certificate
# 1. 在 home 下 .charles/ca/ 能找到这两个文件
$ ~/.charles/ca$ ll
-rw-r--r-- 1 donald donald 1344 Feb 15 16:03 charles-proxy-ssl-proxying-certificate.cer
-rw-r--r-- 1 donald donald 1893 Feb 15 16:03 charles-proxy-ssl-proxying-certificate.pem


# 2. 再执行执行格式转换
openssl x509 -outform der -in charles-proxy-ssl-proxying-certificate.pem -out charles-proxy-ssl-proxying-certificate.crt

# 发现多出一个文件 crt
$ ll
total 24
drwxr-xr-x 2 donald donald 4096 Feb 15 16:12 ./
drwxr-xr-x 6 donald donald 4096 Feb 16 04:00 ../
-rw-r--r-- 1 donald donald 1344 Feb 15 16:03 charles-proxy-ssl-proxying-certificate.cer
-rw-r--r-- 1 donald donald 1344 Feb 15 16:12 charles-proxy-ssl-proxying-certificate.crt
-rw-r--r-- 1 donald donald 1893 Feb 15 16:03 charles-proxy-ssl-proxying-certificate.pem
  1. /usr/share/ca-certificates 文件夹下新建一个目录 charles,复制刚才转化的证书
$ cd /usr/share/ca-certificates
$ sudo mkdir charles
$ sudo cp ~/.charles/ca/charles-proxy-ssl-proxying-certificate.crt /usr/share/ca-certificates/charles/

# 查看
/usr/share/ca-certificates/charles$ ll
-rw-r--r-- 1 root root 1344 Feb 16 10:32 charles-proxy-ssl-proxying-certificate.crt
  1. /etc/ca-certificates.conf 这个配置文件的最后追加 charles/charles-proxy-ssl-proxying-certificate.crt
$ sudo vi /etc/ca-certificates.conf
  1. 更新证书
$ sudo update-ca-certificates

$ sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
rehash: skipping charles-proxy-ssl-proxying-certificate.pem,it does not contain exactly one certificate or CRL
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...

Adding debian:charles-proxy-ssl-proxying-certificate.pem
done.
Updating Mono key store
Linux Cert Store Sync - version 4.6.2.0
Synchronize local certs with certs from local Linux trust store.
Copyright 2002, 2003 Motus Technologies. Copyright 2004-2008 Novell. BSD licensed.

I already trust 133, your new list has 133
Import process completed.
Done
done.


# 查看
/etc/ssl/certs$ ll | grep "charles"
lrwxrwxrwx 1 root root     77 Feb 16 10:42 charles-proxy-ssl-proxying-certificate.pem -> /usr/share/ca-certificates/charles/charles-proxy-ssl-proxying-certificate.crt

(2)移动端

(1) 连接配置

  1. 手机需要与 PC / 服务器在同一个网络。
  2. 启动 Charles ,设置一个监听端口,默认为 8888
  3. 手机一般使用 Wifi 接入,因此在 Wifi 接入配置时候,在高级配置中设置,服务器地址,配置为 Charles 所在机器的 IP 地址,端口为 Charles 监听端口。
  4. 此时在 Charles 上可以看到抓取到的手机网络包。

(2)HTTPS 配置

Charles 可以抓到所有的网络包,但是只能解开 HTTP/HTTPS 的包。对于 HTTPS 协议,需要配置 Charles 证书。

  1. PC / 服务器,安装 CharlesCA 证书,到操作系统的信任证书链中:Help->SSL Proxying->Install Charls root certificate

  2. 在手机端,安装 CharlesCA 证书,到操作系统的信任证书链中: 打开浏览器,输入 chls.pro/ssl,下载证书,并安装证书。


(3)Java 程序

官网:www.charlesproxy.com/documentati…

Charles 根证书添加到 Java 中的根证书信任库中,然后所有 Java 应用程序都将信任 Charles 颁发的证书:

# 在 Charles 中,转到 Help 菜单并选择 SSL Proxying > Save Charles Root Certificate
# 将根证书作为 Base 64 编码证书 (.pem) 保存到 home
$ sudo keytool -import -alias charles -file ~/charles-ssl-proxying-certificate.pem -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit
# 然后输入:yes

# 查询
$ keytool -list -keystore $JAVA_HOME/jre/lib/security/cacerts -storepass changeit | grep "charles"

有三种方法:

  1. 在代码里加上:
System.setProperty("http.proxyHost", "127.0.0.1");
System.setProperty("http.proxyPort", "8888");
  1. IDEA 中加入参数
# VM options 中加入:
-DproxySet=true -DproxyHost=127.0.0.1 -DproxyPort=8888

2022-02-1602-27-07.png

  1. IDEA 里开启

2022-02-1602-30-15.png


(4)Java 证书

没有配置证书的时候,报的异常:

sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 
  1. 问题的根本是:

缺少安全证书时出现的异常

  1. 解决问题方法:

安全认证证书导入到客户端即可

执行步骤:

  1. javac InstallCert.java
  2. java InstallCert baidu.com
  3. 输入1然后回车,然后当前目录下就会生成名为:jssecacerts 的证书
Enter certificate to add to trusted keystore or 'q' to quit: [1] 
1
  1. 将刚才生成的证书 jssecacerts 放置到 $JAVA_HOME/jre/lib/security 目录下,必须确认该 JDKjre 是项目所用的 JDK
$ sudo cp jssecacerts $JAVA_HOME/jre/lib/security/
  1. 重启应用

InstallCert.java 如下,通过以下程序获取安全证书::

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.security.KeyStore;
import java.security.MessageDigest;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLException;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;


public class InstallCert {

    public static void main(String[] args) throws Exception {
        String host  ;
        int port  ;
        char[] passphrase;
        if ((args.length == 1) || (args.length == 2)) {
            String[] c = args[0].split(":");
            host = c[0];
            port = (c.length == 1) ? 443 : Integer.parseInt(c[1]);
            String p = (args.length == 1) ? "changeit" : args[1];
            passphrase = p.toCharArray();
        } else {
            System.out.println("Usage: java InstallCert <host>[:port] [passphrase]");
            return;
        }
        File file = new File("jssecacerts");
        if (file.isFile() == false) {
            char SEP = File.separatorChar;
            File dir = new File(System.getProperty("java.home") + SEP + "lib"
                    + SEP + "security");
            file = new File(dir, "jssecacerts");
            if (file.isFile() == false) {
                file = new File(dir, "cacerts");
            }
        }
        System.out.println("Loading KeyStore " + file + "...");
        InputStream in = new FileInputStream(file);
        KeyStore ks = KeyStore.getInstance(KeyStore.getDefaultType());
        ks.load(in, passphrase);
        in.close();
        SSLContext context = SSLContext.getInstance("TLS");
        TrustManagerFactory tmf = TrustManagerFactory
                .getInstance(TrustManagerFactory.getDefaultAlgorithm());
        tmf.init(ks);
        X509TrustManager defaultTrustManager = (X509TrustManager) tmf
                .getTrustManagers()[0];
        SavingTrustManager tm = new SavingTrustManager(defaultTrustManager);
        context.init(null, new TrustManager[] { tm }, null);
        SSLSocketFactory factory = context.getSocketFactory();
        System.out
                .println("Opening connection to " + host + ":" + port + "...");
        SSLSocket socket = (SSLSocket) factory.createSocket(host, port);
        socket.setSoTimeout(10000);
        try {
            System.out.println("Starting SSL handshake...");
            socket.startHandshake();
            socket.close();
            System.out.println();
            System.out.println("No errors, certificate is already trusted");
        } catch (SSLException e) {
            System.out.println();
            e.printStackTrace(System.out);
        }
        X509Certificate[] chain = tm.chain;
        if (chain == null) {
            System.out.println("Could not obtain server certificate chain");
            return;
        }
        BufferedReader reader = new BufferedReader(new InputStreamReader(
                System.in));
        System.out.println();
        System.out.println("Server sent " + chain.length + " certificate(s):");
        System.out.println();
        MessageDigest sha1 = MessageDigest.getInstance("SHA1");
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        for (int i = 0; i < chain.length; i++) {
            X509Certificate cert = chain[i];
            System.out.println(" " + (i + 1) + " Subject "
                    + cert.getSubjectDN());
            System.out.println("   Issuer  " + cert.getIssuerDN());
            sha1.update(cert.getEncoded());
            System.out.println("   sha1    " + toHexString(sha1.digest()));
            md5.update(cert.getEncoded());
            System.out.println("   md5     " + toHexString(md5.digest()));
            System.out.println();
        }
        System.out
                .println("Enter certificate to add to trusted keystore or 'q' to quit: [1]");
        String line = reader.readLine().trim();
        int k;
        try {
            k = (line.length() == 0) ? 0 : Integer.parseInt(line) - 1;
        } catch (NumberFormatException e) {
            System.out.println("KeyStore not changed");
            return;
        }
        X509Certificate cert = chain[k];
        String alias = host + "-" + (k + 1);
        ks.setCertificateEntry(alias, cert);
        OutputStream out = new FileOutputStream("jssecacerts");
        ks.store(out, passphrase);
        out.close();
        System.out.println();
        System.out.println(cert);
        System.out.println();
        System.out
                .println("Added certificate to keystore 'jssecacerts' using alias '"
                        + alias + "'");
    }
    private static final char[] HEXDIGITS = "0123456789abcdef".toCharArray();
    private static String toHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder(bytes.length * 3);
        for (int b : bytes) {
            b &= 0xff;
            sb.append(HEXDIGITS[b >> 4]);
            sb.append(HEXDIGITS[b & 15]);
            sb.append(' ');
        }
        return sb.toString();
    }
    private static class SavingTrustManager implements X509TrustManager {
        private final X509TrustManager tm;
        private X509Certificate[] chain;
        SavingTrustManager(X509TrustManager tm) {
            this.tm = tm;
        }
        public X509Certificate[] getAcceptedIssuers() {
            throw new UnsupportedOperationException();
        }
        public void checkClientTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            throw new UnsupportedOperationException();
        }
        public void checkServerTrusted(X509Certificate[] chain, String authType)
                throws CertificateException {
            this.chain = chain;
            tm.checkServerTrusted(chain, authType);
        }
    }
}



二、弱网测试

一些概念:

  • Bandwidth(带宽)
  • Utilistation(利用百分比)
  • Round-trip(往返延迟)
  • MTU(最大传输单元)

网络情况:

  • 3G:300k-2Mbps左右
  • 2.5G(GPRS)一般在100kbps
  • 2G(GSM)一般在5-9kbps
网络上行下行
弱网1030
2G1550
3G3842800

参数参考:数据来源

IDEA 里执行程序,发出网络连接:

2022-02-1701-04-56.png