Créez des applications réseau sécurisées avec SSL et l'API JSSE

Internet est un endroit dangereux. Il est tout simplement trop facile d'espionner, d'usurper et de voler des informations non protégées lorsqu'elles passent sur les fils. Le mois dernier, j'ai écrit le dernier article d'une série sur les certificats X.509 et l'infrastructure à clé publique (PKI), les technologies qui sécurisent la plupart des activités de commerce électronique sur Internet. Vers la fin de l'article, j'ai suggéré d'examiner le protocole SSL (Secure Socket Layer) pour savoir comment les certificats X.509 sont utilisés dans la pratique. SSL est l'application tueur X.509 - presque tous les navigateurs et les serveurs Web et d'applications les plus populaires la prennent en charge.

Ce mois-ci, j'explorerai SSL tel qu'implémenté par le JSSE (Java Secure Socket Extension), et vous montrerai comment créer des applications réseau sécurisées en Java à l'aide de SSL et JSSE.

Commençons par une simple démonstration. JSSE fournit une boîte à outils SSL pour les applications Java. En plus des classes et interfaces nécessaires, JSSE fournit un commutateur de débogage de ligne de commande pratique que vous pouvez utiliser pour observer le protocole SSL en action. En plus de fournir des informations utiles pour déboguer une application récalcitrante, jouer avec la boîte à outils est un excellent moyen de se mouiller les pieds avec SSL et JSSE.

Pour exécuter la démonstration, vous devez d'abord compiler la classe suivante:

public class Test {public static void main (String [] arstring) {try {new java.net.URL ("//" + arstring [0] + "/"). getContent (); } catch (exception d'exception) {exception.printStackTrace (); }}}

Ensuite, vous devez activer le débogage SSL et exécuter l'application ci-dessus. L'application se connecte au site Web sécurisé que vous spécifiez sur la ligne de commande à l'aide du protocole SSL via HTTPS. La première option charge le gestionnaire de protocole HTTPS. La deuxième option, l'option de débogage, amène le programme à imprimer son comportement. Voici la commande (remplacez-la par le nom d'un serveur Web sécurisé):

 java -Djava.protocol.handler.pkgs = com.sun.net.ssl.internal.www.protocol -Djavax.net.debug = test ssl  

Vous devez installer JSSE; reportez-vous aux ressources si vous ne savez pas comment.

Passons maintenant aux choses sérieuses et parlons de SSL et JSSE.

Un bref aperçu de SSL

Le code de l'introduction montre le moyen le plus simple d'ajouter SSL à vos applications - via la java.net.URLclasse. Cette approche est utile, mais n'est pas suffisamment flexible pour vous permettre de créer une application sécurisée qui utilise des sockets génériques.

Avant de vous montrer comment ajouter cette flexibilité, jetons un coup d'œil aux fonctionnalités de SSL.

Comme son nom l'indique, SSL vise à fournir aux applications une boîte à outils sécurisée en forme de socket. Idéalement, il devrait être facile de convertir une application qui utilise des sockets normaux en une application qui utilise SSL.

SSL résout trois problèmes de sécurité importants:

  1. Il fournit une authentification, ce qui permet de garantir la légitimité des entités impliquées dans un dialogue.
  2. Il assure l'intimité. SSL permet de garantir qu'un tiers ne peut pas déchiffrer le dialogue entre deux entités.
  3. Il maintient l'intégrité. L'utilisation d'un MAC (code d'authentification de message), similaire à une somme de contrôle, permet de garantir qu'un dialogue entre deux entités n'est pas modifié par un tiers.

SSL repose fortement sur la cryptographie à clé publique et à clé secrète. Il utilise la cryptographie à clé secrète pour crypter en masse les données échangées entre deux applications. SSL fournit la solution idéale car les algorithmes à clé secrète sont à la fois sécurisés et rapides. La cryptographie à clé publique, qui est plus lente que la cryptographie à clé secrète, est un meilleur choix pour l'authentification et l'échange de clés.

L'implémentation de référence JSSE de Sun est fournie avec toute la technologie nécessaire pour ajouter SSL à vos applications. Il inclut la prise en charge de la cryptographie RSA (Rivest-Shamir-Adleman) - la norme de facto pour la sécurité sur Internet. Il inclut une implémentation de SSL 3.0 - le standard SSL actuel - et TLS (Transport Layer Security) 1.0, la prochaine génération de SSL. JSSE fournit également une suite d'API pour créer et utiliser des sockets sécurisés.

L'API JSSE

L'architecture de sécurité Java utilise fortement le modèle de conception Factory . Pour les non - initiés, le modèle de conception d' usine usages spéciaux usine objets à des instances de construire, plutôt que d' appeler leurs constructeurs directement. (Voir Ressources pour les avantages et les inconvénients de la classe d'usine.)

Dans JSSE, tout commence par l'usine; il y a une usine pour les sockets SSL et une usine pour les sockets de serveur SSL. Étant donné que les sockets génériques et les sockets serveur sont déjà assez fondamentaux pour la programmation réseau Java, je suppose que vous êtes familier avec les deux et que vous comprenez leurs rôles et leurs différences. Si vous ne l'êtes pas, je vous recommande de prendre un bon livre sur la programmation réseau Java.

SSLSocketFactory

Les méthodes de la javax.net.ssl.SSLSocketFactoryclasse se divisent en trois catégories. La première consiste en une seule méthode statique qui récupère l'usine de socket SSL par défaut: static SocketFactory getDefault().

La deuxième catégorie se compose de quatre méthodes héritées de javax.net.SocketFactoryce miroir des quatre constructeurs clés trouvés sur la java.net.Socketclasse et d'une méthode qui encapsule un socket existant avec un socket SSL. Ils renvoient chacun un socket SSL:

  1. Socket createSocket(String host, int port)
  2. Socket createSocket(String host, int port, InetAddress clientHost, int clientPort)
  3. Socket createSocket(InetAddress host, int port)
  4. Socket createSocket(InetAddress host, int port, InetAddress clientHost, int clientPort)
  5. Socket createSocket(Socket socket, String host, int port, boolean autoClose)

Les deux méthodes de la troisième catégorie renvoient la liste des suites de chiffrement SSL qui sont activées par défaut et la liste complète des suites de chiffrement SSL prises en charge:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

Une suite de chiffrement est une combinaison d'algorithmes cryptographiques qui définissent un niveau particulier de sécurité pour une connexion SSL. Une suite de chiffrement définit si la connexion est chiffrée, si l'intégrité du contenu est vérifiée et comment l'authentification se produit.

SSLServerSocketFactory

Les méthodes de la javax.net.ssl.SSLServerSocketFactoryclasse appartiennent aux trois mêmes catégories que SSLSocketFactory. Tout d' abord, il y a la méthode statique unique qui récupère l'usine socket serveur SSL par défaut: static ServerSocketFactory getDefault().

Les méthodes qui retournent des sockets de serveur SSL reflètent les constructeurs trouvés dans la java.net.ServerSocketclasse:

  1. ServerSocket createServerSocket(int port)
  2. ServerSocket createServerSocket(int port, int backlog)
  3. ServerSocket createServerSocket(int port, int backlog, InetAddress address)

Enfin, les SSLServerSocketFactoryfonctionnalités des deux méthodes qui renvoient la liste des chiffrements activés par défaut et la liste des chiffrements pris en charge, respectivement:

  1. String [] getDefaultCipherSuites()
  2. String [] getSupportedCipherSuites()

Jusqu'à présent, l'API est assez simple.

SSLSocket

Les choses deviennent intéressantes dans la javax.net.ssl.SSLSocketclasse. Je suppose que vous connaissez déjà les méthodes fournies par son parent, la Socketclasse, je vais donc me concentrer sur les méthodes qui fournissent des fonctionnalités liées à SSL.

Comme les deux classes d'usine SSL, les deux premières méthodes répertoriées ci-dessous récupèrent respectivement les suites de chiffrement SSL activées et prises en charge. La troisième méthode définit les suites de chiffrement activées. Une application peut utiliser la troisième opération pour mettre à niveau ou rétrograder la plage de sécurité acceptable que l'application autorisera:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods determine whether the socket can establish new SSL sessions, which maintain connection details -- like the shared secret key -- between connections:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The next two methods determine whether the socket will require client authentication. The methods only make sense when invoked on server mode sockets. Remember, according to the SSL specification, client authentication is optional. For example, most Web applications don't require it:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean need)

The methods below change the socket from client mode to server mode. This affects who initiates the SSL handshake and who authenticates first:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean mode)

Method void startHandshake() forces an SSL handshake. It's possible, but not common, to force a new handshake operation in an existing connection.

Method SSLSession getSession() retrieves the SSL session. You will seldom need to access the SSL session directly.

The two methods listed below add and remove an SSL handshake listener object. The handshake listener object is notified whenever an SSL handshake operation completes on the socket.

  1. void addHandshakeCompletedListener(HandshakeCompletedListener listener)
  2. void removeHandshakeCompletedListener(HandshakeCompletedListener listener)

SSLServerSocket

The javax.net.ssl.SSLServerSocket class is similar to the javax.net.ssl.SSLSocket class; it doesn't require much individual attention. In fact, the set of methods on javax.net.ssl.SSLServerSocket class is a subset of the methods on the javax.net.ssl.SSLSocket class.

The first two methods listed below retrieve the enabled and supported SSL cipher suites. The third method sets the enabled cipher suite:

  1. String [] getEnabledCipherSuites()
  2. String [] getSupportedCipherSuites()
  3. void setEnabledCipherSuites(String [] suites)

These two methods control whether or not the server socket can establish new SSL sessions:

  1. boolean getEnableSessionCreation()
  2. void setEnableSessionCreation(boolean flag)

The following methods determine whether the accepted sockets will require client authentication:

  1. boolean getNeedClientAuth()
  2. void setNeedClientAuth(boolean flag)

The methods below change the accepted socket from client mode to server mode:

  1. boolean getUseClientMode()
  2. void setUseClientMode(boolean flag)

A simple example

To make this toolkit tutorial clearer, I've included the source code for a simple server and a compatible client below. It's a secure variation on the typical echo application that many introductory networking texts provide.

The server, shown below, uses JSSE to create a secure server socket. It listens on the server socket for connections from secure clients. When running the server, you must specify the keystore to use. The keystore contains the server's certificate. I have created a simple keystore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream; import java.io.InputStreamReader; import java.io.BufferedReader; import java.io.IOException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; public class EchoServer { public static void main(String [] arstring) { try { SSLServerSocketFactory sslserversocketfactory = (SSLServerSocketFactory)SSLServerSocketFactory.getDefault(); SSLServerSocket sslserversocket = (SSLServerSocket)sslserversocketfactory.createServerSocket(9999); SSLSocket sslsocket = (SSLSocket)sslserversocket.accept(); InputStream inputstream = sslsocket.getInputStream(); InputStreamReader inputstreamreader = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); String string = null; while ((string = bufferedreader.readLine()) != null) { System.out.println(string); System.out.flush(); } } catch (Exception exception) { exception.printStackTrace(); } } } 

Use the following command to start the server (foobar is both the name of the keystore file and its password):

 java -Djavax.net.ssl.keyStore=foobar -Djavax.net.ssl.keyStorePassword=foobar EchoServer 

The client, shown below, uses JSSE to securely connect to the server. When running the client, you must specify the truststore to use, which contains the list of trusted certificates. I have created a simple truststore that contains a single certificate. (See Resources to download the certificate.)

import java.io.InputStream; import java.io.OutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.IOException; import javax.net.ssl.SSLSocket; import javax.net.ssl.SSLSocketFactory; public class EchoClient { public static void main(String [] arstring) { try { SSLSocketFactory sslsocketfactory = (SSLSocketFactory)SSLSocketFactory.getDefault(); SSLSocket sslsocket = (SSLSocket)sslsocketfactory.createSocket("localhost", 9999); InputStream inputstream = System.in; InputStreamReader inputstreamreader = new InputStreamReader(inputstream); BufferedReader bufferedreader = new BufferedReader(inputstreamreader); OutputStream outputstream = sslsocket.getOutputStream(); OutputStreamWriter outputstreamwriter = new OutputStreamWriter(outputstream); BufferedWriter bufferedwriter = new BufferedWriter(outputstreamwriter); String string = null; while ((string = bufferedreader.readLine()) != null) { bufferedwriter.write(string + '\n'); bufferedwriter.flush(); } } catch (Exception exception) { exception.printStackTrace(); } } } 

Utilisez la commande suivante pour démarrer le client ( foobarest à la fois le nom du fichier truststore et son mot de passe):

 java -Djavax.net.ssl.trustStore = foobar -Djavax.net.ssl.trustStorePassword = foobar EchoClient