如何用小提琴手捕获https,在java中

2022-08-31 17:48:35

我正在Eclipse IDE中运行以下java程序:

import java.net.*;
import java.io.*;

public class HH
{
    public static void main(String[] args) throws Exception
    {
        //if i comment out the system properties, and don't set any jvm arguments, the program runs and prints out the html fine.
        System.setProperty("http.proxyHost", "localhost"); 
        System.setProperty("http.proxyPort", "8888"); 
        System.setProperty("https.proxyHost", "localhost"); 
        System.setProperty("https.proxyPort", "8888"); 

        URL x = new URL("https://www.google.com");
        HttpURLConnection hc = (HttpURLConnection)x.openConnection();

        hc.setRequestProperty("User-Agent","Mozilla/5.0 (Windows NT 6.0)
        AppleWebKit/535.2 (KHTML, like Gecko) Chrome/15.0.874.121 Safari/535.2");

        InputStream is = hc.getInputStream();

        int u = 0;
        byte[] kj = new byte[1024];
        while((u = is.read(kj)) != -1)
        {
            System.out.write(kj,0,u);
        }
        is.close();
    }
}

如果 fiddler 在捕获和未捕获时都在运行,这将产生以下异常:

Exception in thread "main" javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at com.sun.net.ssl.internal.ssl.Alerts.getSSLException(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.fatal(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.fatalSE(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.serverCertificate(Unknown Source)
    at com.sun.net.ssl.internal.ssl.ClientHandshaker.processMessage(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.processLoop(Unknown Source)
    at com.sun.net.ssl.internal.ssl.Handshaker.process_record(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.readRecord(Unknown Source)
    at com.sun.net.ssl.internal.ssl.SSLSocketImpl.performInitialHandshake(Unknown ...

如果我关闭fiddler,程序运行良好,没有任何例外,在我连接到的URL上生成html。

或者,如果我指定 ,而不是:,它运行并打印出所有html,无一例外,即使小提琴手处于打开状态,在捕获模式下,但仍然根本没有从小提琴手捕获。System.setProperty("https.proxyPort", "443");System.setProperty("https.proxyPort", "8888");

然后,如果我通过 eclipse 的 jvm 参数(如:)设置这些系统属性,则只要 fiddler 应用程序在捕获和非捕获模式下运行,就会再次发生相同的异常。如果我关闭小提琴手,程序将运行良好。-DproxySet=true -DproxyHost=127.0.0.1 -DproxyPort=8888

如果我使用:而不是:,它运行得很好,小提琴应用程序运行良好,既有大写/非捕获模式,也没有捕获的流量。System.setProperty("http.proxyHost", "127.0.0.1");System.setProperty("http.proxyHost", "localhost");

有没有人能够用小提琴手捕获自己的https流量,不是通过Web浏览器,而是通过Java程序?jvm参数是什么,你如何设置它来做到这一点?谢谢


答案 1

创建一个包含 Fiddler 证书的密钥库。将此密钥库与代理设置一起用作 JVM 的信任库。

操作方法如下:

  • 导出小提琴手的根证书

工具 -> Fiddler 选项... -> HTTPS ->导出根证书到桌面

  • 使用此证书创建密钥库

以管理员身份打开命令行(否则 keytool 不起作用)

<JDK_Home>\bin\keytool.exe -import -file C:\Users\<Username>\Desktop\FiddlerRoot.cer -keystore FiddlerKeystore -alias Fiddler

出现提示时输入密码。这应该创建一个名为FiddlerKeystore的文件。

  • 现在启动 JVM,其中 Fiddler 作为代理,此密钥库作为信任库。您将需要以下虚拟图像:

-DproxySet=true

-DproxyHost=127.0.0.1

-DproxyPort=8888

-Djavax.net.ssl.trustStore=<path\to\FiddlerKeystore>

-Djavax.net.ssl.trustStorePassword=<密钥库密码>

在 eclipse 运行配置中使用这些 vmargs,您应该可以放心使用。

我能够捕获从JVM发出的HTTPS请求,而此设置没有任何问题。


答案 2

您还可以将 Fiddler 密钥导入 Java 可信证书存储区(只要您知道这是不安全的,并且您在任何非开发环境中都不这样做):

  1. 从 Fiddler 中导出 Fiddler 的根证书:

工具→小提琴手选项...→ HTTPS →操作→将根证书导出到桌面

  1. 启动提升的命令提示符,并使用以下命令导入证书。将部件替换为相应的 JDK/JRE 版本。如果安装了多个 JDK/JRE,则需要对每个环境执行此操作。jdk1.7.0_79
"keytool.exe" -import -noprompt -trustcacerts -alias FiddlerRoot -file c:\work\FiddlerRoot.cer  -keystore "C:\Program Files\Java\jdk1.7.0_79\jre\lib\security\cacerts"  -storepass changeit

我还遇到了使用Google API客户端与Fiddler结合使用来解密HTTPS流量的问题。问题是,默认情况下,客户端使用自己的证书存储:

InputStream keyStoreStream = GoogleUtils.class.getResourceAsStream("google.jks");
SecurityUtils.loadKeyStore(certTrustStore, keyStoreStream, "notasecret");

这就是我如何解决这个问题:

HttpTransport transport = new NetHttpTransport() 
//instead of transport = GoogleNetHttpTransport.newTrustedTransport();