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 오류가 올 수 있습니다. 프록시 로그인은 터널 요청을 승인할 뿐 그 안의 웹사이트를 인증하지는 않습니다.

자동 닫기를 활성화한 상태에서 덧씌운 소켓을 닫으면 그 전송이 닫힙니다. 바깥 자원 범위는 정상 완료 전에 감싸기나 핸드셰이크가 실패해도 그 전송을 닫습니다.

관련 글