Consuming RESTful service over https with certificate using Java
Asked Answered
A

2

14

I'm a new user to REST services. I need to consume a RESTful API service generated with Jersey. The problem comes because that service is hosted on remote host and it requires https access with certificate.

I got my certificate from the organization and i'm able to access that REST API service with any of my browsers (with certificate set on them).

I've read lot of posts over here, and I've followed the answer on this topic: Using HTTPS with REST in Java

Now I've my certificate setup on my Java Keystore. But i don't know how to use that on my Java program so it uses exactly the certificate i need to do the https connection.

This is my simple connection code for local tests.

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();
}
}

I'v read about using system set properties to specify path to keystore with this code:

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

I still get 401 Error on connection to the remote server.

But then i don't know how to make SSL connection using my certificate on keystore. I've been also reading about using sslSocketFactory for this purpose but i couldn't make it work as explained on this post: How can I use different certificates on specific connections?

I've managed to retrieve my cert from keystore with this code.. now I just need to know how to use it in connection:

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 that's the last script I've wrote. I can connect to https sites but i still can't connect to https sites which require to send my certificate to authenticate.

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());

 }
}
Anlage answered 11/7, 2012 at 9:41 Comment(1)
How did you generate your keystore? In the code you posted, why are using the cacerts truststore? I would suggest importing your certificate into its own JKS and using that in the call to System.setProperty("javax.net.ssl.keyStore", "...").Liquor
V
3

Assuming that you are deploying this code on a server and you have done Everything else correctly (Like generating keystore core correctly and placing it at a location where it can be accessed by your server ,using same java version as your code to generate the keystore )then i think what you need to do is add following

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

in server.xml of your Server instance on which you are running the client and

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

IMP: If you are using Skype alongside this code be sure to (uncheck) change the default value as it also uses the same ports (80 and 443) for additional connections

Velum answered 29/10, 2014 at 13:16 Comment(0)
D
-1
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.web.client.RestTemplate;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import javax.net.ssl.SSLContext;
import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.FileInputStream;
import java.security.KeyStore;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateFactory;
import java.util.ArrayList;

public class RestTemplateExampleWithPfx {

    private static final String PFX_FILE_PATH = "path/to/your/file.pfx";
    private static final String PFX_PASSWORD = "yourPfxPassword";

    public static void main(String[] args) throws Exception {
        // Configure SSL context with the .pfx file
        SSLContext sslContext = SSLContext.getInstance("TLS");
        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        try (FileInputStream fis = new FileInputStream(PFX_FILE_PATH)) {
            keyStore.load(fis, PFX_PASSWORD.toCharArray());
        }

        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
        keyManagerFactory.init(keyStore, PFX_PASSWORD.toCharArray());

        TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
        trustManagerFactory.init(keyStore);

        sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), null);
        SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

        // Create and configure RestTemplate
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());

        // Configure HttpClient with SSL settings
        org.apache.http.impl.client.CloseableHttpClient httpClient = org.apache.http.impl.client.HttpClients.custom()
                .setSSLSocketFactory(new org.apache.http.conn.ssl.NoopHostnameVerifier().createSocketFactory())
                .build();
        
        restTemplate.setRequestFactory(new org.springframework.http.client.HttpComponentsClientHttpRequestFactory(httpClient));

        // Define the custom media type
        String customMediaType = "application/vnd.kafka.json.v2+json";

        // Create headers
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(org.springframework.http.MediaType.valueOf(customMediaType));
        headers.setAccept(java.util.Collections.singletonList(org.springframework.http.MediaType.APPLICATION_JSON));

        // Create data to send
        ArrayList<String> data = new ArrayList<>();
        data.add("example1");
        data.add("example2");

        // Create HttpEntity
        HttpEntity<ArrayList<String>> requestEntity = new HttpEntity<>(data, headers);

        // Define the URL for the POST request
        String url = "https://example.com/api/resource";

        // Send the POST request
        ResponseEntity<String> response = restTemplate.exchange(url, HttpMethod.POST, requestEntity, String.class);

        // Process the response
        if (response.getStatusCode() == HttpStatus.OK) {
            System.out.println("Response: " + response.getBody());
        } else {
            System.out.println("Failed with HTTP error code: " + response.getStatusCodeValue());
        }
    }
}
Daves answered 29/7 at 17:36 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Veteran

© 2022 - 2024 — McMap. All rights reserved.