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