在 SOCKS 之上为 Java TLS 使用目标站点的身份
先通过 SOCKS 建连,再叠加一个携带网站主机名并显式启用 HTTPS 端点校验的 SSLSocket。
SOCKS 端点把你带到网站,而这条连接内部的 TLS 对端正是网站本身,因此要把目标主机名传给分层套接字工厂:
String destination = "example.com";
int destinationPort = 443;
Proxy proxy = new Proxy(
Proxy.Type.SOCKS,
new InetSocketAddress("127.0.0.1", 1080)
);
try (Socket transport = new Socket(proxy)) {
transport.connect(
InetSocketAddress.createUnresolved(destination, destinationPort),
5000
);
SSLSocketFactory factory =
(SSLSocketFactory) SSLSocketFactory.getDefault();
try (SSLSocket tls = (SSLSocket) factory.createSocket(
transport, destination, destinationPort, true)) {
SSLParameters parameters = tls.getSSLParameters();
parameters.setEndpointIdentificationAlgorithm("HTTPS");
tls.setSSLParameters(parameters);
tls.setSoTimeout(15000);
tls.startHandshake();
// Send the application's HTTPS request through tls.
}
}这段代码假定已导入标准的 Java 网络与 TLS 类,存在默认信任配置,且 SOCKS 连接不需要代理凭据。它完成握手;应用仍需按服务器期望的格式发送 HTTP 请求。对于允许的公共路由上的公共 HTTPS 目标,SOCKS 代理列表提供了候选端点;请让服务器版本与主机名支持匹配这条 Java 套接字路径。
读懂示例中的两个身份
127.0.0.1:1080 是代理端点,可从当前 Java 进程访问。destination 与 destinationPort 标识网站。createUnresolved 会为 SOCKS 路径保留该主机名,而不是在客户端提前解析。
分层的 createSocket 接收目标身份。setEndpointIdentificationAlgorithm("HTTPS") 显式请求其主机名校验。原始 SSLSocket 的证书链信任本身并不能证明主机名验证已启用。为了压住不匹配而替换成代理名或 loopback,可能认证到错误的端点。
让信任与清理各自留在所属阶段
默认工厂使用 JVM 已配置的信任方案。有私有要求的应用可以改用其正确初始化的 SSLContext 工厂。这个选择与 SOCKS 路由相互独立;接受所有证书的信任管理器并不能修正错误的对端名称。
SOCKS 拒绝发生在 TLS 之前;不受信任的证书或错误的主机名会在 TLS 阶段失败;之后才可能出现 HTTP 错误。代理登录授权的是隧道请求,而不是认证隧道内部的网站。
在启用自动关闭的情况下关闭分层套接字,会同时关闭它承载的传输。如果包装或握手在正常完成之前失败,外层资源作用域也会关闭该传输。