Skip to content

Instantly share code, notes, and snippets.

@bric3
Created February 10, 2020 07:17
Show Gist options
  • Save bric3/4ac8d5184fdc80c869c70444e591d3de to your computer and use it in GitHub Desktop.
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
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
Copy link

Is it possible to add support for ProxySettings as proxyUser, proxyPassword?

@bric3
Copy link
Author

bric3 commented Aug 31, 2022

@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