Java 如何通过HTTP代理连接SSL套接字?

Java 如何通过HTTP代理连接SSL套接字?,java,android,ssl,Java,Android,Ssl,我正在尝试使用Java(Android)通过SSL套接字连接到服务器。请注意,这不是HTTP数据。这是一个混合了文本和二进制数据的专有协议 我想通过HTTP代理来中继SSL连接,但我面临着很多问题。现在,我使用的场景以及我的浏览器似乎与squid代理一起使用的场景如下 [客户端]->[http连接]->[代理]->[ssl连接]->[服务器] 这适用于浏览器,因为在代理建立ssl连接后,TLS协商立即发生。然而,我的代码似乎没有做到这一点 final TrustManager[] trustMa

我正在尝试使用Java(Android)通过SSL套接字连接到服务器。请注意,这不是HTTP数据。这是一个混合了文本和二进制数据的专有协议

我想通过HTTP代理来中继SSL连接,但我面临着很多问题。现在,我使用的场景以及我的浏览器似乎与squid代理一起使用的场景如下

[客户端]->[http连接]->[代理]->[ssl连接]->[服务器]

这适用于浏览器,因为在代理建立ssl连接后,TLS协商立即发生。然而,我的代码似乎没有做到这一点

final TrustManager[] trustManager = new TrustManager[] { new MyX509TrustManager() };
final SSLContext context = SSLContext.getInstance("TLS");
context.init(null, trustManager, null);
SSLSocketFactory factory = context.getSocketFactory();
Socket s = factory.createSocket(new Socket(proxy_ip, 3128), hostName, port, true);
我的问题是createSocket永远不会返回。通过代理机器的wireshark转储,我可以看到代理和服务器之间发生了tcp握手。对于来自web会话的转储,我可以看到客户端通常在此时启动SSL握手,这在我的场景中不会发生

这不是信任管理器的问题,因为证书永远不会返回给我,也永远不会被验证

编辑:

经过讨论,这是我试图运行的代码的更完整版本。上面这个以simple(newsocket(…)作为参数的版本是我后来尝试过的

我试图调试的代码的原始版本抛出
java.net.ConnectException:无法连接到/192.168.1.100(端口443):连接失败:ETIMEDOUT(连接超时)

顺序如下(再次稍微简化):

然后用以下代码读取响应:

    final InputStreamReader isr = new InputStreamReader(proxySocket.getInputStream());
    final BufferedReader br = new BufferedReader(isr);
    final String statusLine = br.readLine();

    boolean proxyConnectSuccess = false;
    // readLine consumed the CRLF
    final Pattern statusLinePattern = Pattern.compile("^HTTP/\\d+\\.\\d+ (\\d\\d\\d) .*");
    final Matcher statusLineMatcher = statusLinePattern.matcher(statusLine);
    if (statusLineMatcher.matches())
    {
        final String statusCode = statusLineMatcher.group(1);
        if (null != statusCode && 0 < statusCode.length() && '2' == statusCode.charAt(0))
        {
            proxyConnectSuccess = true;
        }
    }

    // Consume rest of proxy response
    String line;
    while ( "".equals((line = br.readLine())) == false )
    {
    }
final InputStreamReader isr=新的InputStreamReader(proxySocket.getInputStream());
最终BufferedReader br=新的BufferedReader(isr);
最终字符串statusLine=br.readLine();
布尔proxyConnectSuccess=false;
//readLine消耗了CRLF
最终模式状态linepattern=Pattern.compile(“^HTTP/\\d+\.\\d+(\\d\\d\\d)。*”;
最终匹配器statusLineMatcher=statusLinePattern.Matcher(statusLine);
if(statusLineMatcher.matches())
{
最终字符串statusCode=statusLineMatcher.group(1);
if(null!=statusCode&&0

我可以说,这段代码之所以有效,是因为它在没有SSL的情况下工作。此处创建的套接字,
proxySocket
是传递给createSocket函数的套接字,而不是像我的原始示例中那样动态创建一个新的套接字。

java.net.Proxy
,或
https.proxyHost/proxyPort
属性,仅支持通过
HttpURLConnection进行HTTP代理,
不通过
插座。

要使其适用于您自己的
SSLSocket
,只需创建一个纯文本套接字,对其发出
httpconnect
命令,检查200的响应,然后将其包装在
SSLSocket中。


编辑发送CONNECT命令时,当然不能关闭套接字;阅读回复时,不得使用
BufferedReader,
,否则会丢失数据;手动读取该行或使用
DataInputStream.readLine(),
,尽管该行已被弃用。您还需要完全遵循RFC2616。

我现在没有时间测试/编写目标解决方案,但是似乎涵盖了基本问题


在您的情况下,您需要连接到代理,发出代理连接,然后升级连接-实际上,您的连接取代了参考问题中的STARTTLS,并且不需要检查“670”。

您必须使用javax.net lib。您可以使用javax.net.ssl.*将文件归档到目标

我认为您可以使用oracle文档获得解决方案。这里是链接


结合MacDaddy的回答和Viktor Mukhachev的评论,在
套接字上使用
SSLSocket
,在
代理上使用

代码:

导入javax.net.ssl.SSLSocket;
导入javax.net.ssl.SSLSocketFactory;
导入java.io.IOException;
导入java.io.InputStream;
导入java.net.InetSocketAddress;
导入java.net.Proxy;
导入java.net.Socket;
导入java.nio.charset.StandardCharset;
公共类SSR代理{
公共静态void main(字符串[]args)引发IOException{
final String REQUEST=“GET/HTTP/1.1\r\n”+
“主机:github.com\r\n”+
“连接:关闭\r\n”+
“\r\n”;
Proxy Proxy=newproxy(Proxy.Type.HTTP,new InetSocketAddress(“您的代理主机”,8080));
套接字=新套接字(代理);
InetSocketAddress=newinetsocketaddress(“github.com”,443);
socket.connect(地址);
SSLSocketFactory SSLSocketFactory=(SSLSocketFactory)SSLSocketFactory.getDefault();
SSLSocket SSLSocket=(SSLSocket)sslSocketFactory.createSocket(socket,address.getHostName(),address.getPort(),true);
sslSocket.startHandshake();
sslSocket.getOutputStream().write(REQUEST.getBytes());
InputStream InputStream=sslSocket.getInputStream();
byte[]bytes=inputStream.readAllBytes();
System.out.println(新字符串(字节,StandardCharsets.UTF_8));
sslSocket.close();
}
}

java.net.Proxy在HTTP下不工作。这似乎需要一个代理。我已经为sun os找到了一个补丁,但没有为android找到任何补丁。这一点对于HTTP非常重要,因为如果没有第三方应用程序,Android无法提供HTTPS/SOCKS支持(默认安装在2015年1月没有提供足够的配置)。如果您通过HTTP代理连接到SSL套接字,我建议尽可能考虑WebSocket,尤其是
    final InputStreamReader isr = new InputStreamReader(proxySocket.getInputStream());
    final BufferedReader br = new BufferedReader(isr);
    final String statusLine = br.readLine();

    boolean proxyConnectSuccess = false;
    // readLine consumed the CRLF
    final Pattern statusLinePattern = Pattern.compile("^HTTP/\\d+\\.\\d+ (\\d\\d\\d) .*");
    final Matcher statusLineMatcher = statusLinePattern.matcher(statusLine);
    if (statusLineMatcher.matches())
    {
        final String statusCode = statusLineMatcher.group(1);
        if (null != statusCode && 0 < statusCode.length() && '2' == statusCode.charAt(0))
        {
            proxyConnectSuccess = true;
        }
    }

    // Consume rest of proxy response
    String line;
    while ( "".equals((line = br.readLine())) == false )
    {
    }