Java-Tipp 96: Verwenden Sie HTTPS in Ihrem Java-Clientcode

Wenn Sie jemals versucht haben, eine sichere Kommunikation zwischen einem Java-Client und einem HTTPS-Server (HyperText Transfer Protocol Secure) zu implementieren, haben Sie wahrscheinlich festgestellt, dass die Standardklasse java.net.URLdas HTTPS-Protokoll nicht unterstützt. Die serverseitige Implementierung dieser Gleichung ist ziemlich einfach. Nahezu jeder heute verfügbare Webserver bietet einen Mechanismus zum Anfordern von Daten mithilfe von HTTPS. Sobald Sie Ihren Webserver eingerichtet haben, kann jeder Browser sichere Informationen von Ihrem Server anfordern, indem Sie einfach HTTPS als Protokoll für die URL angeben. Wenn Sie noch keinen HTTPS-Server eingerichtet haben, können Sie Ihren Client-Code mit fast jeder HTTPS-Webseite im Internet testen. Der Abschnitt Ressourcen enthält eine kurze Liste von Kandidaten, die Sie für diesen Zweck verwenden können.

Aus Client-Sicht täuscht jedoch die Einfachheit des S am Ende des bekannten HTTP. Der Browser führt tatsächlich eine beträchtliche Menge Arbeit hinter den Kulissen durch, um sicherzustellen, dass niemand die von Ihnen angeforderten Informationen manipuliert oder überwacht hat. Wie sich herausstellt, ist der Algorithmus zur Verschlüsselung von HTTPS von RSA Security patentiert (für mindestens einige weitere Monate). Die Verwendung dieses Algorithmus wurde von Browserherstellern lizenziert, jedoch nicht von Sun Microsystems für die Aufnahme in die Standardimplementierung der Java- URLKlasse lizenziert . Wenn Sie versuchen, ein URLObjekt mit einer Zeichenfolge zu MalformedURLExceptionerstellen, die HTTPS als Protokoll angibt, wird daher a ausgelöst.

Um diese Einschränkung zu berücksichtigen, bietet die Java-Spezifikation die Möglichkeit, einen alternativen Stream-Handler für die URLKlasse auszuwählen . Die zur Implementierung erforderliche Technik ist jedoch je nach verwendeter virtueller Maschine (VM) unterschiedlich. Für die JDK 1.1-kompatible VM von Microsoft, JView, hat Microsoft den Algorithmus lizenziert und einen HTTPS-Stream-Handler als Teil seines wininetPakets bereitgestellt. Auf der anderen Seite hat Sun kürzlich die Java Secure Sockets Extension (JSSE) für JDK 1.2-kompatible VMs veröffentlicht, für die Sun auch einen HTTPS-Stream-Handler lizenziert und bereitgestellt hat. In diesem Artikel wird gezeigt, wie die Verwendung eines HTTPS-fähigen Stream-Handlers mithilfe von JSSE und Microsoft wininetimplementiert wird.

JDK 1.2-kompatible virtuelle Maschinen

Die Technik zur Verwendung von JDK 1.2-kompatiblen VMs basiert hauptsächlich auf der Java Secure Sockets Extension (JSSE) 1.0.1. Bevor diese Technik funktioniert, müssen Sie die JSSE installieren und dem Klassenpfad der betreffenden Client-VM hinzufügen.

Nachdem Sie JSSE installiert haben, müssen Sie eine Systemeigenschaft festlegen und dem SecurityKlassenobjekt einen neuen Sicherheitsanbieter hinzufügen . Es gibt verschiedene Möglichkeiten, diese beiden Dinge zu tun. Für die Zwecke dieses Artikels wird jedoch die programmatische Methode gezeigt:

System.setProperty ("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol"); Security.addProvider (neuer com.sun.net.ssl.internal.ssl.Provider ());

Nach den beiden vorherigen Methodenaufrufen MalformedURLExceptionwird der nicht mehr durch Aufrufen des folgenden Codes ausgelöst:

 URL url = neue URL ("// [Ihr Server]"); 

Wenn Sie eine Verbindung zum Standard-SSL-Port 443 herstellen, haben Sie die Möglichkeit, die Portnummer an die URL-Zeichenfolge anzuhängen. Wenn Ihr Webserver jedoch einen nicht standardmäßigen Port für den SSL-Verkehr verwendet, müssen Sie die Portnummer wie folgt an Ihre URL-Zeichenfolge anhängen:

 URL url = neue URL ("// [Ihr Server]: 7002"); 

Eine Einschränkung dieser Technik betrifft eine URL, die auf einen Server verweist, der über ein nicht signiertes oder ungültiges SSL-Zertifikat verfügt. In diesem Fall wird beim Versuch, den Eingabe- oder Ausgabestream vom Verbindungsobjekt der URL SSLExceptionabzurufen, die Meldung "Nicht vertrauenswürdige Serverzertifizierungskette" ausgegeben. Wenn der Server über ein gültiges, signiertes Zertifikat verfügt, wird keine Ausnahme ausgelöst.

URL url = neue URL ("// [Ihr Server]"); URLConnection con = URL.openConnection (); // SSLException wird hier ausgelöst, wenn das Serverzertifikat ungültig ist con.getInputStream ();

Die offensichtliche Lösung für dieses Problem besteht darin, signierte Zertifikate für Ihren Server zu erhalten. Eine der folgenden URLs bietet jedoch möglicherweise auch eine Lösung: "Änderungen an Java Secure Socket Extension 1.0.2" (Sun Microsystems) oder das Java Developer Connection-Forum von Sun.

Microsoft JView

Zum Teil aufgrund des anhaltenden Streits zwischen Microsoft und Sun über die Lizenzierung von Java für die Verwendung auf Windows-Plattformen ist die Microsoft JView VM derzeit nur JDK 1.1-kompatibel. Daher funktioniert die oben beschriebene Technik nicht für Clients, die in JView ausgeführt werden, da für JSSE mindestens eine 1.2.2-kompatible VM erforderlich ist. Praktischerweise bietet Microsoft jedoch einen HTTPS-fähigen Stream-Handler als Teil des com.ms.net.wininetPakets an.

Sie können den Stream-Handler in einer JView-Umgebung festlegen, indem Sie eine einzelne statische Methode für die URLKlasse aufrufen :

 URL.setURLStreamHandlerFactory (neue com.ms.net.wininet.WininetStreamHandlerFactory ()); 

Nach dem vorherigen Methodenaufruf wird die

MalformedURLException

wird nicht mehr durch Aufrufen des folgenden Codes ausgelöst:

 URL url = neue URL ("// [Ihr Server]"); 

Mit dieser Technik sind zwei Einschränkungen verbunden. Erstens kann die setURLStreamHandlerFactoryMethode gemäß der JDK-Dokumentation in einer bestimmten VM höchstens einmal aufgerufen werden. Nachfolgende Versuche, diese Methode aufzurufen, lösen eine aus Error. Zweitens müssen Sie wie bei der 1.2 VM-Lösung vorsichtig sein, wenn Sie eine URL verwenden, die auf einen Server mit einem nicht signierten oder ungültigen SSL-Zertifikat verweist. Wie im vorherigen Fall treten Probleme auf, wenn versucht wird, den Eingabe- oder Ausgabestream vom Verbindungsobjekt der URL abzurufen. Anstatt ein zu werfen SSLException, löst der Microsoft-Stream-Handler jedoch einen Standard aus IOException.

URL url = neue URL ("// [Ihr Server]"); URLConnection con = url.openConnection (); // IOException wird hier ausgelöst, wenn das Serverzertifikat ungültig ist con.getInputStream ();

Die offensichtliche Lösung für dieses Problem besteht wiederum darin, die HTTPS-Kommunikation nur mit Servern zu versuchen, die über ein signiertes, gültiges Zertifikat verfügen. JView bietet jedoch eine weitere Option. Unmittelbar vor dem Abrufen des Eingabe- oder Ausgabestreams vom Verbindungsobjekt der URL können Sie das Verbindungsobjekt aufrufen setAllowUserInteraction(true). Dies führt dazu, dass JView eine Meldung anzeigt, die den Benutzer warnt, dass die Zertifikate des Servers ungültig sind, ihm jedoch die Möglichkeit gibt, trotzdem fortzufahren. Beachten Sie jedoch, dass solche Nachrichten für eine Desktop-Anwendung möglicherweise sinnvoll sind. Es ist jedoch wahrscheinlich nicht akzeptabel, dass auf Ihrem Server Dialogfelder angezeigt werden, die nicht zu Debugging-Zwecken dienen.

Hinweis: Sie können die setAllowUserInteraction()Methode auch in JDK 1.2-kompatiblen VMs aufrufen . Bei Verwendung der 1.2-VM von Sun (mit der dieser Code getestet wurde) werden jedoch keine Dialogfelder angezeigt, selbst wenn diese Eigenschaft auf true festgelegt ist.

URL url = neue URL ("// [Ihr Server]"); URLConnection con = url.openConnection (); // bewirkt, dass die VM ein Dialogfeld anzeigt, wenn // eine Verbindung zu nicht vertrauenswürdigen Servern hergestellt wird con.setAllowUserInteraction (true); con.getInputStream ();

Das com.ms.net.wininetPaket scheint standardmäßig auf Windows NT 4.0-, Windows 2000- und Windows 9x-Systemen installiert und im Systemklassenpfad abgelegt zu sein. Gemäß der Microsoft JDK-Dokumentation WinInetStreamHandlerFactoryist "... derselbe Handler, der standardmäßig beim Ausführen von Applets installiert wird".

Plattformunabhängigkeit

Obwohl beide von mir beschriebenen Techniken die meisten Plattformen abdecken, auf denen Ihr Java-Client ausgeführt werden kann, muss Ihr Java-Client möglicherweise sowohl auf JDK 1.1- als auch auf JDK 1.2-kompatiblen VMs ausgeführt werden. "Einmal schreiben, irgendwohin laufen", erinnerst du dich? Wie sich herausstellt, ist es ziemlich einfach, diese beiden Techniken so zu kombinieren, dass der entsprechende Handler abhängig von der VM geladen wird. Der folgende Code zeigt einen Weg, um dies zu erreichen:

 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("//[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

Die Verwendung des HTTPS-Protokolls zwischen Anwendungen kann eine schnelle und effektive Möglichkeit sein, ein angemessenes Maß an Sicherheit in Ihrer Kommunikation zu erreichen. Leider scheinen die Gründe, warum es nicht als Teil der Standard-Java-Spezifikation unterstützt wird, eher legal als technisch zu sein. Mit dem Aufkommen von JSSE und der Verwendung des Microsoft- com.ms.net.winintPakets ist jedoch eine sichere Kommunikation von den meisten Plattformen mit nur wenigen Codezeilen möglich.

Matt Towers, ein selbst beschriebener eBozo, hat kürzlich seine Entwicklungsposition bei Visio verlassen. Seitdem ist er bei einem Internet-Startup, PredictPoint.com, in Seattle, Washington, tätig, wo er als Vollzeit-Java-Entwickler arbeitet.

Erfahren Sie mehr über dieses Thema

  • The source code zip file for this article contains the platform-independent code shown above implemented in a class called HttpsMessage. HttpsMessage is intended as a subclass to the HttpMessage class written by Jason Hunter, author of Java Servlet Programming (O'Reilly & Associates). Look for HttpsMessage in the upcoming second edition of his book. If you wish to use that class as intended, you'll need to download and install the com.oreilly.servlets package. The com.oreilly.servlets package and corresponding source code can be found on Hunter's Website

    //www.servlets.com

  • You can also download the source zip file

    //images.techhive.com/downloads/idge/imported/article/jvw/2000/06/httpsmessage.zip

  • Here are a few good Webpages for testing HTTPS communication:
  • //www.verisign.com/
  • //happiness.dhs.org/
  • //www.microsoft.com
  • //www.sun.com
  • //www.ftc.gov
  • More information on the JSSE as well as the downloadable bits and installation instructions can be found on Sun's Website

    //java.sun.com/products/jsse/.

  • A description of how to use some JSSE services, including the technique described above, can be found in "Secure Networking in Java" by Jonathan Knudsen on the O'Reilly Website

    //java.oreilly.com/bite-size/java_1099.html

  • More information on WininetStreamHandlerFactory class can be found in the Microsoft JSDK documentation

    //www.microsoft.com/java/sdk/. In addition, the Microsoft knowledge base also publishes "PRBAllowing the URL class to access HTTPS in Applications"

    //support.microsoft.com/support/kb/articles/Q191/1/20.ASP

  • For more information on using HTTPS with the Java 2 plug-in, see "How HTTPS Works in Java Plug-In" on Sun's Website

    //java.sun.com/products/plugin/1.2/docs/https.html

Diese Geschichte "Java-Tipp 96: Verwenden Sie HTTPS in Ihrem Java-Client-Code" wurde ursprünglich von JavaWorld veröffentlicht.