Here is example code for HTTPS Tunneling through proxy(400 Lines of code
Here is the source for Https Tunneling that I have gotten working. It is based on Pua Yeow Cheong's JavaWorld Tip 111. Thanks to David Lord for providing the final breakthrough that I needed.
I have posted it here for anyone who wishes to use it. If you find any bugs, or write any improvements, please tack them onto the end of this thread.
I have been trying to tackle this problem for quite some time, so I hope this helps a few of you out there.
Lots of Luck,
nightmask.
<-- Begin Copy and Paste -->
import java.net.*;
import java.io.*;
import java.security.*;
import sun.misc.BASE64Encoder;
import javax.net.*;
import javax.net.ssl.*;
/*
* This example is based on JavaWorld Tip 111. Thanks to Pua Yeow Cheong for writing it.
* It tunnels through a proxy using the Https protocol.
* Thanks go to David Lord in the java forums for figuring out the main problem with Tip 111
* PLEASE NOTE: You need to have the JSSE 1.0.2 jars installed for this to work
*/
/**
* Downloads contents of a URL, using Proxy Tunneling and Basic Authentication
*/
publicclass URLReader{
/**
* The main program for the URLReader class
*/
publicstaticvoid main(String[] args)throws Exception{
//set up strings for use in app. Change these to your own settings
String proxyPassword ="password";
String proxyUsername ="username";
String proxyHost ="myproxy.com";
String proxyPort ="3128";
String connectionURL ="https://www.verisign.com";
//set up system properties to indicate we are using a proxy
System.setProperty("https.proxyHost", proxyHost);
System.setProperty("https.proxyPort", proxyPort);
System.setProperty("proxyHost", proxyHost);
System.setProperty("proxyPort", proxyPort);
System.setProperty("proxySet","true");
System.setProperty("http.proxyHost", proxyHost);
System.setProperty("http.proxyPort", proxyPort);
System.setProperty("http.proxySet","true");
//set up handler for jsse
System.setProperty("java.protocol.handler.pkgs","com.sun.net.ssl.internal.www.protocol");
java.security.Provider prov =new com.sun.net.ssl.internal.ssl.Provider();
Security.addProvider(prov);
//create the connection
URL myURL =new URL(connectionURL);
URLConnection myConnection = myURL.openConnection();
if (myConnectioninstanceof com.sun.net.ssl.HttpsURLConnection){
((com.sun.net.ssl.HttpsURLConnection) myConnection).setSSLSocketFactory(new SSLTunnelSocketFactory(System.getProperty("proxyHost"), System.getProperty("proxyPort")));
}
myConnection.setDoInput(true);
myConnection.setDoOutput(true);
BufferedReader in;
try{
System.err.println("opening Input stream1");
in =new BufferedReader(
new InputStreamReader(
myConnection.getInputStream()));
String inputLine;
System.err.println("Input stream is Open1");
while ((inputLine = in.readLine()) !=null){
System.err.println(inputLine);
}
in.close();
System.err.println("Input stream is Closed1");
}catch (Exception e){
e.printStackTrace(System.err);
String tmp = e.getMessage().toLowerCase().trim();
System.err.println("tmp *" + tmp +"*");
if (tmp.indexOf("http") > -1){
//http error message to be parsed
tmp = tmp.substring(tmp.indexOf("http")).trim();
System.err.println("tmp *" + tmp +"*");
tmp = tmp.substring(8).trim();
System.err.println("tmp *" + tmp +"*");
if (tmp.startsWith("407")){
//proxy authentication required
myURL =new URL(connectionURL);
myConnection = myURL.openConnection();
if (myConnectioninstanceof com.sun.net.ssl.HttpsURLConnection){
((com.sun.net.ssl.HttpsURLConnection) myConnection).setSSLSocketFactory(new SSLTunnelSocketFactory(System.getProperty("proxyHost"), System.getProperty("proxyPort"), proxyUsername, proxyPassword));
}
myConnection.setDoInput(true);
myConnection.setDoOutput(true);
try{
System.err.println("opening Input stream 2");
in =new BufferedReader(
new InputStreamReader(
myConnection.getInputStream()));
String inputLine;
System.err.println("Input stream is Open 2");
while ((inputLine = in.readLine()) !=null){
System.out.println(inputLine);
}
in.close();
System.err.println("Input stream is closed 2");
}catch (Exception ex){
System.err.println(ex.getMessage());
ex.printStackTrace(System.err);
}
}
}
}
}
}
/**
* SSLSocket used to tunnel through a proxy
*/
class SSLTunnelSocketFactoryextends SSLSocketFactory{
private String tunnelHost;
privateint tunnelPort;
private SSLSocketFactory dfactory;
private String tunnelPassword;
private String tunnelUserName;
privateboolean socketConnected =false;
privateint falsecount = 0;
/**
* Constructor for the SSLTunnelSocketFactory object
*
*@param proxyHost The url of the proxy host
*@param proxyPort the port of the proxy
*/
public SSLTunnelSocketFactory(String proxyHost, String proxyPort){
System.err.println("creating Socket Factory");
tunnelHost = proxyHost;
tunnelPort = Integer.parseInt(proxyPort);
dfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
}
/**
* Constructor for the SSLTunnelSocketFactory object
*
*@param proxyHostThe url of the proxy host
*@param proxyPortthe port of the proxy
*@param proxyUserName username for authenticating with the proxy
*@param proxyPassword password for authenticating with the proxy
*/
public SSLTunnelSocketFactory(String proxyHost, String proxyPort, String proxyUserName, String proxyPassword){
System.err.println("creating Socket Factory with password/username");
tunnelHost = proxyHost;
tunnelPort = Integer.parseInt(proxyPort);
tunnelUserName = proxyUserName;
tunnelPassword = proxyPassword;
dfactory = (SSLSocketFactory) SSLSocketFactory.getDefault();
}
/**
* Sets the proxyUserName attribute of the SSLTunnelSocketFactory object
*
*@param proxyUserName The new proxyUserName value
*/
publicvoid setProxyUserName(String proxyUserName){
tunnelUserName = proxyUserName;
}
/**
* Sets the proxyPassword attribute of the SSLTunnelSocketFactory object
*
*@param proxyPassword The new proxyPassword value
*/
publicvoid setProxyPassword(String proxyPassword){
tunnelPassword = proxyPassword;
}
/**
* Gets the supportedCipherSuites attribute of the SSLTunnelSocketFactory
* object
*
*@returnThe supportedCipherSuites value
*/
public String[] getSupportedCipherSuites(){
return dfactory.getSupportedCipherSuites();
}
/**
* Gets the defaultCipherSuites attribute of the SSLTunnelSocketFactory
* object
*
*@returnThe defaultCipherSuites value
*/
public String[] getDefaultCipherSuites(){
return dfactory.getDefaultCipherSuites();
}
/**
* Gets the socketConnected attribute of the SSLTunnelSocketFactory object
*
*@returnThe socketConnected value
*/
publicsynchronizedboolean getSocketConnected(){
return socketConnected;
}
/**
* Creates a new SSL Tunneled Socket
*
*@param s Ignored
*@param host destination host
*@param port destination port
*@param autoClosewether to close the socket automaticly
*@returnproxy tunneled socket
*@exception IOExceptionraised by an IO error
*@exception UnknownHostException raised when the host is unknown
*/
public Socket createSocket(Socket s, String host,int port,boolean autoClose)
throws IOException, UnknownHostException{
Socket tunnel =new Socket(tunnelHost, tunnelPort);
doTunnelHandshake(tunnel, host, port);
SSLSocket result = (SSLSocket) dfactory.createSocket(tunnel, host, port, autoClose);
result.addHandshakeCompletedListener(
new HandshakeCompletedListener(){
publicvoid handshakeCompleted(HandshakeCompletedEvent event){
System.out.println("Handshake Finished!");
System.out.println("\t CipherSuite :" + event.getCipherSuite());
System.out.println("\t SessionId: " + event.getSession());
System.out.println("\t PeerHost: " + event.getSession().getPeerHost());
setSocketConnected(true);
}
});
// thanks to David Lord in the java forums for figuring out this line is the problem
// result.startHandshake(); //this line is the bug which stops Tip111 from working correctly
return result;
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host destination host
*@param port destination port
*@returntunneled SSL Socket
*@exception IOExceptionraised by IO error
*@exception UnknownHostException raised when the host is unknown
*/
public Socket createSocket(String host,int port)
throws IOException, UnknownHostException{
return createSocket(null, host, port,true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host Destination Host
*@param port Destination Port
*@param clientHostIgnored
*@param clientPortIgnored
*@returnSSL Tunneled Socket
*@exception IOExceptionRaised when IO error occurs
*@exception UnknownHostException Raised when the destination host is
*unknown
*/
public Socket createSocket(String host,int port, InetAddress clientHost,
int clientPort)
throws IOException, UnknownHostException{
return createSocket(null, host, port,true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param host destination host
*@param port destination port
*@returntunneled SSL Socket
*@exception IOException raised when IO error occurs
*/
public Socket createSocket(InetAddress host,int port)
throws IOException{
return createSocket(null, host.getHostName(), port,true);
}
/**
* Creates a new SSL Tunneled Socket
*
*@param address destination host
*@param port destination port
*@param clientAddressignored
*@param clientPortignored
*@returntunneled SSL Socket
*@exception IOException raised when IO exception occurs
*/
public Socket createSocket(InetAddress address,int port,
InetAddress clientAddress,int clientPort)
throws IOException{
return createSocket(null, address.getHostName(), port,true);
}
/**
* Sets the socketConnected attribute of the SSLTunnelSocketFactory object
*
*@param b The new socketConnected value
*/
privatesynchronizedvoid setSocketConnected(boolean b){
socketConnected = b;
}
/**
* Description of the Method
*
*@param tunneltunnel socket
*@param host destination host
*@param port destination port
*@exception IOException raised when an IO error occurs
*/
privatevoid doTunnelHandshake(Socket tunnel, String host,int port)throws IOException{
OutputStream out = tunnel.getOutputStream();
//generate connection string
String msg ="CONNECT " + host +":" + port +" HTTP/1.0\n"
+"User-Agent: "
+ sun.net.www.protocol.http.HttpURLConnection.userAgent;
if (tunnelUserName !=null && tunnelPassword !=null){
//add basic authentication header for the proxy
sun.misc.BASE64Encoder enc =new sun.misc.BASE64Encoder();
String encodedPassword = enc.encode((tunnelUserName +":" + tunnelPassword).getBytes());
msg = msg +"\nProxy-Authorization: Basic " + encodedPassword;
}
msg = msg +"\nContent-Length: 0";
msg = msg +"\nPragma: no-cache";
msg = msg +"\r\n\r\n";
System.err.println(msg);
byte b[];
try{
//we really do want ASCII7 as the http protocol doesnt change with locale
b = msg.getBytes("ASCII7");
}catch (UnsupportedEncodingException ignored){
//If ASCII7 isn't there, something is seriously wrong!
b = msg.getBytes();
}
out.write(b);
out.flush();
byte reply[] =newbyte[200];
int replyLen = 0;
int newlinesSeen = 0;
boolean headerDone =false;
InputStream in = tunnel.getInputStream();
boolean error =false;
while (newlinesSeen < 2){
int i = in.read();
if (i < 0){
thrownew IOException("Unexpected EOF from Proxy");
}
if (i =='\n'){
headerDone =true;
++newlinesSeen;
}else
if (i !='\r'){
newlinesSeen = 0;
if (!headerDone && replyLen < reply.length){
reply[replyLen++] = (byte) i;
}
}
}
//convert byte array to string
String replyStr;
try{
replyStr =new String(reply, 0, replyLen,"ASCII7");
}catch (UnsupportedEncodingException ignored){
replyStr =new String(reply, 0, replyLen);
}
//we check for connection established because our proxy returns http/1.1 instead of 1.0
if (replyStr.toLowerCase().indexOf("200 connection established") == -1){
System.err.println(replyStr);
thrownew IOException("Unable to tunnel through " + tunnelHost +":" + tunnelPort +". Proxy returns\"" + replyStr +"\"");
}
//tunneling hanshake was successful
}
}
<-- End Copy and Paste -->

