Created
February 10, 2020 07:17
-
-
Save bric3/4ac8d5184fdc80c869c70444e591d3de to your computer and use it in GitHub Desktop.
The famous SSLPoke from Atlassian : establish a TLS connection but support http proxy and updated to Java 11
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import javax.net.ssl.SSLParameters; | |
import javax.net.ssl.SSLSocket; | |
import javax.net.ssl.SSLSocketFactory; | |
import java.io.BufferedReader; | |
import java.io.BufferedWriter; | |
import java.io.IOException; | |
import java.io.InputStreamReader; | |
import java.io.OutputStreamWriter; | |
import java.net.Socket; | |
/** | |
* Establish a SSL connection to a host and port, writes a byte and | |
* prints the response. | |
* | |
* Based on the famous SSLPoke from Atlassian, but support http proxy and | |
* updated to Java 11. | |
* | |
* See http://confluence.atlassian.com/display/JIRA/Connecting+to+SSL+services | |
* | |
* Run {@code java -Dhttps.proxyHost=proxy-out -Dhttps.proxyPort=13128 SSLPoke.java google.com 443} | |
*/ | |
public class SSLPoke { | |
public static void main(String[] args) { | |
if (args.length != 2) { | |
System.out.println("Usage: " + SSLPoke.class.getName() + " <host> <port>"); | |
System.exit(1); | |
} | |
var tunnelHost = System.getProperty("https.proxyHost", null); | |
var tunnelPort = Integer.getInteger("https.proxyPort", 0); | |
var sslSocketFactory = (SSLSocketFactory) SSLSocketFactory.getDefault(); | |
try (SSLSocket sslSocket = connectTlsSocket( | |
sslSocketFactory, | |
tunnelHost, | |
tunnelPort, | |
args[0], | |
Integer.parseInt(args[1]))) { | |
var sslParams = new SSLParameters(); | |
sslParams.setEndpointIdentificationAlgorithm("HTTPS"); | |
sslSocket.setSSLParameters(sslParams); | |
var in = sslSocket.getInputStream(); | |
var out = sslSocket.getOutputStream(); | |
out.write(1); // Write a test byte to get a reaction :) | |
while (in.available() > 0) { | |
System.out.print(in.read()); | |
} | |
System.out.println("Successfully connected"); | |
} catch (Exception exception) { | |
exception.printStackTrace(); | |
System.exit(1); | |
} | |
} | |
private static SSLSocket connectTlsSocket(SSLSocketFactory sslSocketFactory, String tunnelHost, int tunnelPort, String host, int port) | |
throws IOException { | |
if (tunnelHost != null || tunnelPort != 0) { | |
var tunnel = new Socket(tunnelHost, tunnelPort); | |
connectTunnel(tunnel, host, port); | |
return (SSLSocket) sslSocketFactory.createSocket(tunnel, host, port, true); | |
} | |
return (SSLSocket) sslSocketFactory.createSocket(host, port); | |
} | |
private static void connectTunnel(Socket proxySocket, String proxyHost, int proxyPort) | |
throws IOException { | |
var bw = new BufferedWriter(new OutputStreamWriter(proxySocket.getOutputStream(), "ASCII7")); | |
bw.write("CONNECT " + proxyHost + ":" + proxyPort + " HTTP/1.1\n" | |
+ "Proxy-Connection: Keep-Alive" | |
+ "User-Agent: " + SSLPoke.class.getName() | |
+ "\r\n\r\n"); | |
bw.flush(); | |
var br = new BufferedReader(new InputStreamReader(proxySocket.getInputStream(), "ASCII7")); | |
var statusLine = br.lines() | |
.findFirst() | |
.orElseThrow(() -> new IOException("Unexpected EOF from proxy")); | |
if (!statusLine.startsWith("HTTP/1.1 200")) { | |
throw new IOException("Unable to tunnel through " | |
+ proxySocket.getInetAddress().getHostAddress() + ":" + proxySocket.getPort() | |
+ ". Proxy returns \"" + statusLine + "\""); | |
} | |
} | |
} |
@albepere that makes sense, but I don't have a proxy anymore to test that.
Also there's the question of authentication schemes: when a proxy ask an authentication scheme (a request has to be made first, proxy replies with 407 and Proxy-Authenticate
then the client send a new request to the proxy with the Proxy-Authorization
. The simplest scheme is the Basic base 64, but other schemes exist, So I can eventually come up with this simple thing.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Is it possible to add support for ProxySettings as proxyUser, proxyPassword?