Wskazówka Java 96: Użyj protokołu HTTPS w kodzie klienta Java

Jeśli kiedykolwiek próbowałeś zaimplementować bezpieczną komunikację między klientem Java a serwerem HTTPS (HyperText Transfer Protocol Secure), prawdopodobnie odkryłeś, że standardowa java.net.URLklasa nie obsługuje protokołu HTTPS. Implementacja tego równania po stronie serwera jest dość prosta. Prawie każdy dostępny obecnie serwer sieci Web zapewnia mechanizm żądania danych przy użyciu protokołu HTTPS. Po skonfigurowaniu serwera WWW każda przeglądarka może zażądać bezpiecznych informacji z serwera, po prostu określając HTTPS jako protokół adresu URL. Jeśli nie masz jeszcze skonfigurowanego serwera HTTPS, możesz przetestować kod klienta na prawie każdej stronie HTTPS w Internecie. Sekcja Zasoby zawiera krótką listę kandydatów, których możesz użyć w tym celu.

Jednak z punktu widzenia klienta prostota S na końcu znanego protokołu HTTP jest myląca. Przeglądarka w rzeczywistości wykonuje znaczną część pracy za kulisami, aby upewnić się, że nikt nie manipulował ani nie monitorował żądanych informacji. Jak się okazuje, algorytm szyfrujący dla HTTPS jest opatentowany przez RSA Security (na co najmniej kilka miesięcy). Na użycie tego algorytmu udzielono licencji producentów przeglądarek, ale firma Sun Microsystems nie udzieliła licencji na uwzględnienie go w standardowej URLimplementacji klasy Java . W rezultacie, jeśli spróbujesz skonstruować URLobiekt z ciągiem znaków określającym HTTPS jako protokół, MalformedURLExceptionzostanie wyrzucony a.

Na szczęście, aby uwzględnić to ograniczenie, specyfikacja języka Java umożliwia wybranie alternatywnej procedury obsługi strumienia dla URLklasy. Jednak technika wymagana do zaimplementowania jest różna w zależności od używanej maszyny wirtualnej (VM). W przypadku maszyny wirtualnej firmy Microsoft zgodnej z JDK 1.1, JView, firma Microsoft udzieliła licencji na algorytm i dostarczyła moduł obsługi strumienia HTTPS jako część wininetpakietu. Z drugiej strony firma Sun niedawno wydała rozszerzenie Java Secure Sockets Extension (JSSE) dla maszyn wirtualnych zgodnych z JDK 1.2, w przypadku których firma Sun również udzieliła licencji i dostarczyła obsługę strumieni HTTPS. W tym artykule pokazano, jak zaimplementować obsługę strumienia obsługującego protokół HTTPS przy użyciu JSSE i pakietu firmy Microsoft wininet.

Maszyny wirtualne zgodne z JDK 1.2

Technika korzystania z maszyn wirtualnych zgodnych z JDK 1.2 opiera się głównie na rozszerzeniu Java Secure Sockets Extension (JSSE) 1.0.1. Zanim ta technika zadziała, należy zainstalować JSSE i dodać go do ścieżki klasy danej maszyny wirtualnej klienta.

Po zainstalowaniu JSSE należy ustawić właściwość systemową i dodać nowego dostawcę zabezpieczeń do Securityobiektu klasy. Obie te czynności można wykonać na wiele różnych sposobów, ale na potrzeby tego artykułu przedstawiono metodę programową:

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

Po wykonaniu dwóch poprzednich wywołań metody MalformedURLExceptionnie będzie już generowany przez wywołanie następującego kodu:

 URL url = nowy adres URL ("// [twój serwer]"); 

Jeśli łączysz się ze standardowym portem SSL 443, masz możliwość dołączenia numeru portu do ciągu adresu URL. Jeśli jednak Twój serwer internetowy używa niestandardowego portu dla ruchu SSL, musisz dołączyć numer portu do ciągu adresu URL w następujący sposób:

 URL url = nowy adres URL ("// [twój serwer]: 7002"); 

Jedno zastrzeżenie tej techniki dotyczy adresu URL, który odnosi się do serwera, który ma niepodpisany lub nieprawidłowy certyfikat SSL. W takim przypadku próba pobrania strumienia wejściowego lub wyjściowego z obiektu połączenia adresu URL spowoduje SSLExceptionwyświetlenie komunikatu „niezaufany łańcuch certyfikatów serwera”. Jeśli serwer ma ważny, podpisany certyfikat, żaden wyjątek nie zostanie zgłoszony.

URL url = nowy adres URL ("// [twój serwer]"); URLConnection con = URL.openConnection (); // SSLException generowany w tym miejscu, jeśli certyfikat serwera jest nieprawidłowy con.getInputStream ();

Oczywistym rozwiązaniem tego problemu jest uzyskanie podpisanych certyfikatów dla serwera. Jednak jeden z poniższych adresów URL może również zawierać rozwiązanie: „Zmiany w rozszerzeniu Java Secure Socket 1.0.2” (Sun Microsystems) lub forum Java Developer Connection firmy Sun.

Microsoft JView

Częściowo z powodu trwającego sporu między Microsoft i Sun w sprawie licencjonowania oprogramowania Java do użytku na platformach Windows, maszyna wirtualna Microsoft JView jest obecnie zgodna tylko z JDK 1.1. Dlatego technika opisana powyżej nie będzie działać dla klientów działających w JView, ponieważ JSSE wymaga co najmniej maszyny wirtualnej zgodnej z 1.2.2. Jednak dość wygodnie, firma Microsoft udostępnia w pakiecie moduł obsługi strumienia obsługujący protokół HTTPS com.ms.net.wininet.

Możesz ustawić procedurę obsługi strumienia w środowisku JView, wywołując pojedynczą metodę statyczną w URLklasie:

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

Po wykonaniu poprzedniego wywołania metody

MalformedURLException

nie będą już generowane przez wywołanie następującego kodu:

 URL url = nowy adres URL ("// [twój serwer]"); 

Z tą techniką wiążą się dwa zastrzeżenia. Po pierwsze, zgodnie z dokumentacją JDK, setURLStreamHandlerFactorymetodę można wywołać najwyżej raz na danej maszynie wirtualnej. Kolejne próby wywołania tej metody spowodują zgłoszenie Error. Po drugie, podobnie jak w przypadku rozwiązania 1.2 VM, należy zachować ostrożność podczas korzystania z adresu URL, który odwołuje się do serwera z niepodpisanym lub nieprawidłowym certyfikatem SSL. Podobnie jak w poprzednim przypadku, przy próbie pobrania strumienia wejściowego lub wyjściowego z obiektu połączenia adresu URL występują problemy. Jednak zamiast rzucać SSLException, program obsługi strumienia firmy Microsoft zgłasza standard IOException.

URL url = nowy adres URL ("// [twój serwer]"); URLConnection con = url.openConnection (); // IOException generowany w tym miejscu, jeśli certyfikat serwera jest nieprawidłowy con.getInputStream ();

Ponownie, oczywistym rozwiązaniem tego problemu jest próba komunikacji HTTPS tylko z serwerami, które mają podpisany, ważny certyfikat. Jednak JView oferuje jeszcze jedną opcję. Bezpośrednio przed pobraniem strumienia wejściowego lub wyjściowego z obiektu połączenia adresu URL można wywołać setAllowUserInteraction(true)obiekt połączenia. Spowoduje to, że JView wyświetli komunikat ostrzegający użytkownika, że ​​certyfikaty serwera są nieważne, ale dając mu możliwość kontynuowania mimo wszystko. Należy jednak pamiętać, że takie komunikaty mogą być uzasadnione w przypadku aplikacji komputerowych, ale wyświetlanie okien dialogowych na serwerze w celach innych niż debugowanie jest prawdopodobnie niedopuszczalne.

Uwaga: możesz również wywołać tę setAllowUserInteraction()metodę na maszynach wirtualnych zgodnych z JDK 1.2. Jednak w przypadku korzystania z maszyny wirtualnej 1.2 firmy Sun (z którą testowano ten kod), żadne okna dialogowe nie są wyświetlane, nawet jeśli ta właściwość ma wartość true.

URL url = nowy adres URL ("// [twój serwer]"); URLConnection con = url.openConnection (); // powoduje, że maszyna wirtualna wyświetla okno dialogowe podczas łączenia // z niezaufanymi serwerami con.setAllowUserInteraction (true); con.getInputStream ();

com.ms.net.wininetPakiet wydaje się być zainstalowane i umieszczone na ścieżce klasy systemu domyślnie w systemie Windows NT 4.0, Windows 2000 oraz systemów Windows 9x. Ponadto, zgodnie z dokumentacją Microsoft JDK, WinInetStreamHandlerFactoryjest to „... ten sam program obsługi, który jest instalowany domyślnie podczas uruchamiania apletów”.

Niezależność od platformy

Chociaż obie te techniki, które opisałem, obejmują większość platform, na których może działać twój klient Java, może być konieczne uruchomienie klienta Java na maszynach wirtualnych zgodnych z JDK 1.1 i JDK 1.2. „Napisz raz, biegnij gdziekolwiek”, pamiętasz? Jak się okazuje, połączenie tych dwóch technik tak, aby w zależności od maszyny wirtualnej ładowany był odpowiedni program obsługi, jest dość proste. Poniższy kod ilustruje jeden ze sposobów, aby to zrobić:

String strVendor = System.getProperty ("java.vendor"); String strVersion = System.getProperty ("java.version"); // Zakłada ciąg znaków wersji systemu w postaci: //[major].[minor].[release] (np. 1.2.2) Double dVersion = new Double (strVersion.substring (0, 3)); // Jeśli pracujemy w środowisku MS, użyj programu obsługi strumienia MS. 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 ("Nie można załadować modułu obsługi strumienia Microsoft SSL" + ". Sprawdź ścieżkę klasy." + cfe.toString ());} // Jeśli fabryka obsługi strumienia została // już pomyślnie ustawiona // upewnij się, że nasza flaga jest ustawiona i zjedz błąd catch (błąd błędu) {m_bStreamHandlerSet = true;}} // Jeśli jesteśmy w normalnym środowisku Java, // spróbuj użyć programu obsługi JSSE. // UWAGA: JSSE wymaga wersji 1.2 lub nowszej if (1.2 <= dVersion.doubleValue ()) {System.setProperty ("java.protocol.handler.pkgs", "com.sun.net.ssl.internal.www.protocol "); try {// jeśli mamy dostępnego dostawcę JSSE, // i nie został on jeszcze // ustawiony, dodaj go jako nowe źródło do klasy Security. 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 ("Nie można załadować modułu obsługi strumienia JSSE SSL." + "Sprawdź ścieżkę klasy." + cfe.toString ()); }}

A co z apletami?

Wykonywanie komunikacji opartej na protokole HTTPS z poziomu apletu wydaje się naturalnym rozszerzeniem opisanych powyżej scenariuszy. W rzeczywistości w większości przypadków jest to jeszcze łatwiejsze. W 4.0 i nowszych wersjach przeglądarek Netscape Navigator i Internet Explorer protokół HTTPS jest domyślnie włączony dla odpowiednich maszyn wirtualnych. Dlatego jeśli chcesz utworzyć połączenie HTTPS z poziomu kodu apletu, po prostu określ HTTPS jako swój protokół podczas tworzenia instancji URLklasy:

 URL url = nowy adres URL ("// [twój serwer]"); 

Jeśli w przeglądarce klienta działa wtyczka Java 2 firmy Sun, istnieją dodatkowe ograniczenia dotyczące sposobu korzystania z protokołu HTTPS. Pełną dyskusję na temat korzystania z protokołu HTTPS z wtyczką Java 2 można znaleźć w witrynie internetowej firmy Sun (patrz Zasoby).

Wniosek

Korzystanie z protokołu HTTPS między aplikacjami może być szybkim i skutecznym sposobem na uzyskanie rozsądnego poziomu bezpieczeństwa komunikacji. Niestety, powody, dla których nie jest on obsługiwany jako część standardowej specyfikacji Java, wydają się być bardziej legalne niż techniczne. Jednak wraz z pojawieniem się JSSE i wykorzystaniem pakietu Microsoftu com.ms.net.winint, bezpieczna komunikacja jest możliwa z większości platform za pomocą zaledwie kilku linii kodu.

Matt Towers, samozwańczy eBozo, niedawno opuścił swoje stanowisko programistyczne w Visio. Od tego czasu dołączył do startupu internetowego PredictPoint.com w Seattle w stanie Waszyngton, gdzie pracuje jako pełnoetatowy programista Java.

Dowiedz się więcej na ten temat

  • 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

Ten artykuł „Java Tip 96: Użyj HTTPS w kodzie klienta Java” został pierwotnie opublikowany przez JavaWorld.