web-dev-qa-db-fra.com

Consommation du service RESTful sur https avec certificat en utilisant Java

Je suis un nouvel utilisateur des services REST. J'ai besoin de consommer un service d'API RESTful généré avec Jersey. Le problème vient du fait que ce service est hébergé sur l'hôte distant et qu'il nécessite un accès https avec certificat.

J'ai obtenu mon certificat de l'organisation et je peux accéder à ce service REST API avec n'importe lequel de mes navigateurs (avec certificat défini sur eux).

J'ai lu beaucoup de messages ici et j'ai suivi la réponse sur ce sujet: tilisation de HTTPS avec REST en Java

Maintenant, j'ai ma configuration de certificat sur mon Java Keystore. Mais je ne sais pas comment l'utiliser sur mon Java program donc il utilise exactement le certificat j'ai besoin de faire la connexion https.

Ceci est mon code de connexion simple pour les tests locaux.

package rest.test.first;
import Java.net.URI;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.UriBuilder;

import com.Sun.jersey.api.client.Client;
import com.Sun.jersey.api.client.ClientResponse;
import com.Sun.jersey.api.client.WebResource;
import com.Sun.jersey.api.client.config.ClientConfig;
import com.Sun.jersey.api.client.config.DefaultClientConfig;

public class TestClient{
public static void main(String[]args){
    ClientConfig config= new DefaultClientConfig();
    Client client=Client.create(config);
    WebResource service=client.resource(getBaseURI());
    //Fluentinterfaces
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_PLAIN).get(ClientResponse.class).toString());
    //Getplaintext
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_PLAIN).get(String.class));
    //GetXML
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_XML).get(String.class));
    //TheHTML
    System.out.println(service.path("rest").path("hello").accept(MediaType.TEXT_HTML).get(String.class));
}

private static URI getBaseURI(){
    return UriBuilder.fromUri("http://localhost:8080/rest.test").build();
}
}

J'ai lu comment utiliser les propriétés de l'ensemble système pour spécifier le chemin d'accès au magasin de clés avec ce code:

System.setProperty("javax.net.ssl.keyStore", "/path/to/keystore.jks");
System.setProperty("javax.net.ssl.keyStorePassword", "password");

Je reçois toujours une erreur 401 lors de la connexion au serveur distant.

Mais je ne sais pas comment établir une connexion SSL en utilisant mon certificat sur keystore. J'ai également lu sur l'utilisation de sslSocketFactory à cette fin, mais je n'ai pas pu le faire fonctionner comme expliqué sur ce post: Comment puis-je utiliser différents certificats sur des connexions spécifiques?

J'ai réussi à récupérer mon certificat à partir du magasin de clés avec ce code .. maintenant j'ai juste besoin de savoir comment l'utiliser en connexion:

package rest.test.first;

    import Java.io.FileInputStream;
    import Java.security.KeyStore;
    import Java.security.cert.Certificate;

    public class keystore {

      public static void main(String[] args) throws Exception {
        String keystoreFilename = "/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts";

        char[] password = "changeit".toCharArray();
        String alias = "remote_https_server";

        FileInputStream fIn = new FileInputStream(keystoreFilename);
        KeyStore keystore = KeyStore.getInstance("JKS");

        keystore.load(fIn, password);

        Certificate cert = keystore.getCertificate(alias);

        System.out.println(cert);
      }
    }

Ok c'est le dernier script que j'ai écrit. Je peux me connecter à des sites https mais je ne peux toujours pas me connecter à des sites https qui nécessitent d'envoyer mon certificat pour s'authentifier.

package rest.test.first;


import Java.io.*;
import Java.net.*;
import Java.security.*;

import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;


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

     System.setProperty("javax.net.ssl.trustStore", "/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts"); 
  System.setProperty("javax.net.ssl.trustStorePassword", "changeit");
  Security.addProvider(new com.Sun.net.ssl.internal.ssl.Provider());

      //TrustStore..
      char[] passphrase = "changeit".toCharArray(); //password
      KeyStore keystore = KeyStore.getInstance("JKS");
      //KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
      keystore.load(new FileInputStream("/usr/lib/jvm/jdk1.6.0_32/jre/lib/security/cacerts"), passphrase); //path

      //TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509"); //instance
      TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
      tmf.init(keystore);


      SSLContext context = SSLContext.getInstance("TLS");
      TrustManager[] trustManagers = tmf.getTrustManagers();
      context.init(null, trustManagers, null);
      SSLSocketFactory sf = context.getSocketFactory();
      URL url = new URL("https://www.google.es");

      HttpsURLConnection httpsCon = (HttpsURLConnection) url.openConnection();
      httpsCon.setSSLSocketFactory(sf);
      httpsCon.setRequestMethod("GET");

      /*InputStream inStrm = httpsCon.getInputStream();
      System.out.println("\nContent at " + url);
      int ch;
        while (((ch = inStrm.read()) != -1)){
          System.out.print((char) ch);
        inStrm.close();
      }*/

      System.out.println("Response Message is " + httpsCon.getResponseMessage());

 }
}
13
gmarco

En supposant que vous déployez ce code sur un serveur et que vous avez fait tout le reste correctement (comme générer correctement le noyau du magasin de clés et le placer à un endroit où il peut être consulté par votre serveur, en utilisant le même Java version comme code pour générer le fichier de clés), alors je pense que ce que vous devez faire est d'ajouter ce qui suit

<Connector SSLEnabled="true" clientAuth="false" keystoreFile="pathToKeystore" keystorePass="password" maxThreads="150" port="443" protocol="HTTP/1.1" scheme="https" secure="true" sslProtocol="TLS"/>

dans server.xml de votre instance de serveur sur laquelle vous exécutez le client et

<Connector connectionTimeout="20000" port="80" protocol="HTTP/1.1" redirectPort="8443"/>

IMP: Si vous utilisez Skype avec ce code, assurez-vous de (décocher) changer la valeur par défaut car il utilise également les mêmes ports (80 et 443) pour des connexions supplémentaires

3
mayank vats