After making the previous two method calls, the MalformedURLException
will no longer be thrown by calling the following code
URL url = new URL("https://[your server]");
If you are connecting to the standard SSL port, 443, you have the option of appending the port number to the URL string. However, if your Web server is using a nonstandard port for SSL traffic, you'll need to append the port number to your URL string like this:
URL url = new URL("https://[your server]:7002");
One caveat of that technique concerns a URL that refers to a server that has an unsigned or invalid SSL certificate. In that case an attempt to retrieve the input or output stream from the URL's connection object will throw an SSLException
with the message "untrusted server cert chain." If the server has a valid, signed certificate, no exception will be thrown.
URL url = new URL("https://[your server]");
URLConnection con = URL.openConnection();
//SSLException thrown here if server certificate is invalid
con.getInputStream();
Microsoft JView
Due in part to the ongoing dispute between Microsoft and Sun over the licensing of Java for use on Windows platforms, the Microsoft JView VM is currently only JDK 1.1-compliant. Therefore, the technique described above will not work for clients running in JView, as the JSSE requires at least a 1.2.2-compatible VM. Conveniently enough, however, Microsoft provides an HTTPS-enabled stream handler as part of the com.ms.net.wininet
package.
You can set the stream handler in a JView environment by calling a single static method on the URL
class:
URL.setURLStreamHandlerFactory(new
com.ms.net.wininet.WininetStreamHandlerFactory());
After making the previous method call, the MalformedURLException
will no longer be thrown by calling the following code:
URL url = new URL("https://[your server]");
There are two caveats associated with that technique. First, according to the JDK documentation, thesetURLStreamHandlerFactory
method may be called at most once in a given VM. Subsequent attempts to call that method will throw an Error
. Second, as is the case with the 1.2 VM solution, you must be cautious when using a URL that refers to a server with an unsigned or invalid SSL certificate. As with the previous case, problems occur when an attempt is made to retrieve the input or output stream from the URL's connection object. However, instead of throwing an SSLException
, the Microsoft stream handler throws a standard IOException
.
URL url = new URL("https://[your server]");
URLConnection con = url.openConnection();
//IOException thrown here if server certificate is invalid
con.getInputStream();
Again, the obvious solution to that problem is to attempt HTTPS communication only with servers that have a signed, valid certificate. However, JView offers one other option. Immediately prior to retrieving the input or output stream from the URL's connection object, you can call setAllowUserInteraction(true)
on the connection object. That will cause JView to display a message warning the user that the server's certificates are invalid, but giving him or her the option to proceed anyway. Keep in mind, however, that such messages may be reasonable for a desktop application, but having dialog boxes appear on your server for anything other than debugging purposes is probably unacceptable.
Note: You can also call the setAllowUserInteraction()
method in JDK 1.2-compatible VMs. However, in using Sun's 1.2 VM (with which this code was tested), no dialogs are displayed even when that property is set to true.
URL url = new URL("https://[your server]");
URLConnection con = url.openConnection();
//causes the VM to display a dialog when connecting
//to untrusted servers
con.setAllowUserInteraction(true);
con.getInputStream();
The com.ms.net.wininet
package appears to be installed and placed on the system classpath by default on Windows NT 4.0, Windows 2000, and Windows 9x systems. Also, according to the Microsoft JDK documentation, WinInetStreamHandlerFactory
is "...the same handler that is installed by default when running applets."
Platform independence
Although both of those techniques I've described cover most of the platforms on which your Java client may run, your Java client may need to run on both JDK 1.1- and JDK 1.2-compliant VMs. "Write once, run anywhere," remember? As it turns out, combining those two techniques so that the appropriate handler is loaded depending on the VM, is fairly straightforward. The following code demonstrates one way to go about that:
String strVendor = System.getProperty("java.vendor");
String strVersion = System.getProperty("java.version");
//Assumes a system version string of the form:
//[major].[minor].[release] (eg. 1.2.2)
Double dVersion = new Double(strVersion.substring(0, 3));
//If we are running in a MS environment, use the MS stream handler.
if( -1 < strVendor.indexOf("Microsoft") )
{
try
{
Class clsFactory =
Class.forName("com.ms.net.wininet.WininetStreamHandlerFactory" );
if ( null != clsFactory )
URL.setURLStreamHandlerFactory(
(URLStreamHandlerFactory)clsFactory.newInstance());
}
catch( ClassNotFoundException cfe )
{
throw new Exception("Unable to load the Microsoft SSL " +
"stream handler. Check classpath." + cfe.toString());
}
//If the stream handler factory has
//already been successfully set
//make sure our flag is set and eat the error
catch( Error err ){m_bStreamHandlerSet = true;}
}
//If we are in a normal Java environment,
//try to use the JSSE handler.
//NOTE: JSSE requires 1.2 or better
else if( 1.2 <= dVersion.doubleValue() )
{
System.setProperty("java.protocol.handler.pkgs",
"com.sun.net.ssl.internal.www.protocol");
try
{
//if we have the JSSE provider available,
//and it has not already been
//set, add it as a new provide to the Security class.
Class clsFactory = Class.forName("com.sun.net.ssl.internal.ssl.Provider");
if( (null != clsFactory) && (null == Security.getProvider("SunJSSE")) )
Security.addProvider((Provider)clsFactory.newInstance());
}
catch( ClassNotFoundException cfe )
{
throw new Exception("Unable to load the JSSE SSL stream handler." +
"Check classpath." + cfe.toString());
}
}
What about applets?
Performing HTTPS-based communication from within an applet seems like a natural extension of scenarios described above. In reality, it's even easier in most cases. In 4.0 and later versions of Netscape Navigator and Internet Explorer, HTTPS is enabled by default for their respective VMs. Therefore, if you want to create an HTTPS connection from within your applet code, simply specify HTTPS as your protocol when creating an instance of the URL
class:
URL url = new URL("https://[your server]");
If the client browser is running Sun's Java 2 plug-in, then there are additional limitations to how you can use HTTPS. A full discussion on using HTTPS with the Java 2 plug-in can be found on Sun's Website (see
Resources).
Conclusion
Using the HTTPS protocol between applications can be a quick and effective way to gain a reasonable level of security in your communication. Unfortunately, the reasons that it is not supported as part of the standard Java specification appear to be more legal than technical. However, with the advent of the JSSE and the use of Microsoft's com.ms.net.winint
package, secure communication is possible from most platforms with only a few lines of code.