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 -->

[23648 byte] By [nightmask] at [2007-9-26 7:28:58]
# 1

I tried compiling the above program but came up with error in a particular line of code..

if (tmp.indexOf("http") > -1), line 68 in my case

with gt as being an unrecognizable variable and the semi-colon. I was not sure to the actual intent of the sentence so I temporarily substituted it with the stmt:

if (tmp.startsWith("http"))

I got over the compilation error but unsure if thats the way to proceed.

Also can anyone tell me if its programmatically possible to logon to a secure website by embedding username and password within the same java program and downloading automatically a csv file from that HTTPS site by clicking on the Download link provided on that page? All these without any human interaction at all and limited to none support from the website personnel? If so what are the relevant resources and path that i should follow...

Any help would be greatly appreciated..

mathewjdc at 2007-7-1 17:25:05 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 2

I tried the code and it works great with JRE 1.3 and JSSE 1.0.2.

However when I tried it with the JSSE that integrated with

JRE 1.4 beta, I cannot get it to work.

I got the following exception:

Exception in thread "main" java.util.NoSuchElementException

at java.util.StringTokenizer.nextToken(StringTokenizer.java:232)

at sun.net.www.protocol.http.HttpURLConnection.doTunneling(HttpURLConnec

tion.java:762)

at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect

(DashoA6275)

at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLCon

nection.java:556)

at com.sun.net.ssl.internal.www.protocol.https.HttpsURLConnectionOldImpl

.getInputStream(DashoA6275)

at URLTunnelReader.main(URLTunnelReader.java:34)

It seems like it is not running the code in the SSLTunnelSocketFactory.

Anyone tried to make this code work with JRE 1.4 beta? what are the changes needed?

thanks

snowman2002 at 2007-7-1 17:25:05 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 3

Keep in mind that JSSE and JDK 1.4 are using different methods. You have to re-write your code to make it compatible with JDK 1.4. If you open JDK 1.4 classes description, you will see "since JDK 1.4".

You have to open re-create all you SSLSocket's and SSLServerSocket's with new constructors.

spektr44 at 2007-7-1 17:25:05 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 4
I think you might want to take out thehttps.proxyPort/https.proxyHost, sothat the default "https" implementation doesn'ttry to do it's own proxy negotiation also?
wetmore at 2007-7-1 17:25:05 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 5

BTW, if you are using an implementation in which

the http/https implementation recognises

the java.net.Authenticator properly, you can use

that framework to do basic/digest authentication.

I think Sun's JDK 1.4 supports both basic

and digest for both proxies and the actual end

site you connect via http/https, but I haven't

tested it to be sure. I know it works

with http/basic at the end host.

Today's Ob hack:

import java.net.*;

import java.io.*;

class MyAuth extends Authenticator {

protected PasswordAuthentication getPasswordAuthentication() {

System.out.println("The realm '" + getRequestingPrompt() +

"' at '" + getRequestingHost() + ":" + getRequestingPort() +

"'\n" + "using " + getRequestingProtocol() + " is requesting " +

getRequestingScheme().toUpperCase() + " authentication.");

System.out.println("");

System.out.println("What should we send them? Let's send them ...");

System.out.println("");

return new PasswordAuthentication("username", "password".toCharArray());}

}

public class MyURL {

public static void main(String[] args) throws Exception {

// set to the authenticator you want to use.

Authenticator.setDefault(new myAuth());

URL url =

new URL("http://www.some.com/something_protected/index.htm");

BufferedReader in = new BufferedReader(

new InputStreamReader(

url.openStream()));

String inputLine;

while ((inputLine = in.readLine()) != null) {

System.out.println(inputLine);

}

in.close();

}

}

wetmore at 2007-7-1 17:25:05 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...
# 6

Hi all,

i am really thankful to nightmask for the HTTPS example, but i have one problem ,

i changed the connectionURL value to

"https://localhost:7002/example/Test.jsp", and in Test.jsp i am printing Authentication Type like below it is showing null

System.out.println(" HELLO TEST = "+ request.getAuthType());

what may be the problem

thanks in advance

nmvk1 at 2007-7-1 17:25:05 > top of Java-index,Security,Java Secure Socket Extension (JSSE)...