Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

I am trying to connect to a secure external rest service using Jersey 1.X version.

I used the following code

public class MyRestClient
{
  private static final String API_USER_NAME = "some value";
  private static final String API_PASSWORD = "some value";
  private static final String REST_URL = "https://<somevalue>";

  public static void main(String[] args)
  {
    ClientConfig config = new DefaultClientConfig();
    Client client = Client.create(config);
    client.addFilter(new HTTPBasicAuthFilter(API_USER_NAME, API_PASSWORD));
    WebResource webResource =
      client.resource(UriBuilder.fromUri(REST_URL).build());

    ClientResponse response = webResource.post(ClientResponse.class);
    System.out.println(response);
  }
}

But I keep hitting this exception..

com.sun.jersey.api.client.ClientHandlerException: javax.net.ssl.SSLHandshakeException: java.security.cert.CertificateException: No name matching 'somevalue' found

I checked out the API of this external rest service and it says it support Basic HTTP authentication but I dont know why I keep hitting this error.

Any thoughts?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
186 views
Welcome To Ask or Share your Answers For Others

1 Answer

Because the lack of security in Basic Auth by itself, it's usually done over SSL, as you can see in the https schema in the URL. With SSL, there are certificates used. The SSL handshake consists of the server sending its certificate and the client checking its truststore to see if the certificate is trusted. The platform should have a list of Certificate authorities that it trusts. For instance if we try and access

WebTarget target = client.target("https://wikipedia.org");

this will work as the cert sent by wikipedia is signed by a trusted authority in the system. On the other hand, if the certificate from the server is not signed by one of those trused authorities, then the SSL handshake will fail.

If this is the case, then Client needs to be configured to handle the SSL handshake, that's why you are getting the exception. You can see some good answers here on how to configure the Client for working with https


UPDATE

The link you have provided is dead so I dont know what 'myTrustManager' and 'hostnameVerifier' is...can you share some info on how can I supply that?

import com.sun.jersey.api.client.Client;
import com.sun.jersey.api.client.ClientResponse;
import com.sun.jersey.api.client.config.ClientConfig;
import com.sun.jersey.api.client.config.DefaultClientConfig;
import com.sun.jersey.client.urlconnection.HTTPSProperties;
import java.io.FileInputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import org.junit.Test;

public class JUnitTest {

    private static final String TRUSTSTORE_FILE = "<location-of-truststore";
    private static final String TRUSTSTORE_PASSWORD = "trustStorePassword";

    @Test
    public void test() throws Exception {
        KeyStore truststore = KeyStore.getInstance("JKS");
        truststore.load(new FileInputStream(TRUSTSTORE_FILE), 
                                            TRUSTSTORE_PASSWORD.toCharArray());
        TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
        tmf.init(truststore);
        SSLContext sslContext = SSLContext.getInstance("SSL");
        sslContext.init(null, tmf.getTrustManagers(), null);

        ClientConfig config = new DefaultClientConfig();
        config.getProperties().put(HTTPSProperties.PROPERTY_HTTPS_PROPERTIES, 
                new HTTPSProperties(null, sslContext));
        Client client = Client.create(config);

        final String httpsUrl = "https://...";
        ClientResponse response = client.resource(httpsUrl).get(ClientResponse.class);
        System.out.println(response.getStatus());
        System.out.println(response.getEntity(String.class));
    } 
}

Basically you need to get the X.509 Certificate from the people who own the API (see link below for programmatic way). Then you import it into your trust store. This is how your client knows to trust the connection. If you trust that the server is who they say they are, then the connection can be encrypted.

Once you have the cert you can import it with the Java keytool. By doing this

keytool -import -alias serverCert -file <cert.file> -keystore <client_trust_file>

You will be asked for a password. Then asked if you trust the cert. Type 'yes', then you're done. This is the file (client_trust_file) and password you typed, should be used in the above code.


Update 2

Instructions on creating a simple app that is over a secured SSL connection with Tomcat. The use the client code above to access it. I am going to be using Netbeans 8, but will try to include instructions for doing this in a general way also. I will also be using Tomcat 8 (the configuration may be a bit different fro Tomcat 7. You should consult the documentation for any differences). I will be using Maven, so hopefully you are comfortable using it.

Step 1:

Crate a new app. I will create a simple Jersey app from a Maven archetype. In Netbeans

File → New Project → Maven → Project from Archetype → Search "jersey-quickstart-webapp"; choose the one with groupId "org.glassfish.jersey.archetypes" → Next → Name the project "secured-rest-app" → Hopefully you can complete the rest. You should end up with a Maven app.

In any other IDE that supports Maven, just look for an archetype with the coordinates:

  • groupId: org.glassfish.jersey.archetypes
  • artifactId: jersey-quickstart-webapp
  • version: 2.13

From command line: do

mvn archetype:generate -DarchetypeArtifactId=jersey-quickstart-grizzly2 
    -DarchetypeGroupId=org.glassfish.jersey.archetypes -DinteractiveMode=false 
    -DgroupId=com.example -DartifactId=secured-rest-app -Dpackage=secured.rest.app 
    -DarchetypeVersion=2.13 

Step 2:

We need to set up Basic Authentication in the app. This can be done in the web.xm l. Open up the web.xml of the project and add this below the </servlet-mapping>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Secured Rest App</web-resource-name>
        <url-pattern>/webapi/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name>
    </auth-constraint>
</security-constraint>

<login-config>
    <auth-method>BASIC</auth-method>
    <realm-name>secured-rest-app.com</realm-name>
</login-config>

<security-role>
    <role-name>user</role-name>
</security-role>

Step 3:

Now we just need to set up the realm in Tomcat. By default Tomcat use a realm names UserDatabaseRealm. It basically just reads from an xml file. This is probably not the most desired way in production, but this is the easiest to work with for an example. For more information on realms, see Realm Configuration HOW-TO. For this particular realm, the file is already set up. We just need to add our user. Open the <tomcat-home>/conf/tomcat-users.xml and enter the following user inside <tomcat-users>

<user password="secret" roles="user" username="peeskillet"/>

Step 4:

Now we can test it. If you already have Tomcat setup on Netbeans, all we need to do is Run and select the server. This should automatically open up the browser to our index.jsp. The file is not secured, as it doesn't fit within the <url-pattern>/webapi/*</url-pattern> of out security constraint. Click the Jersey Resource link and you will see the Basic Auth login. Type in peeskillet and secret for the username and password, respectively. Now you can access the resource.

Step 5:

All of the above just set us up for Basic Authentication, but because all Basic Auth is, is just base64 encoding our username and password, it can be easily decoded, so we need to authenticate over a secure connection.

First thing we need to do is create a keystore for our server. What we will be going here is creating a self signed certificate, which should only be done in development. In production, you will want to get a cert from a trusted CA authority

cd to <tomcat-home>/conf and type the following (all on one line)

keytool -genkey -alias localhost -keyalg RSA -keysize 1024
        -dname "CN=localhost"
        -keypass supersecret
        -keystore tomcat-keystore.jks
        -storepass supersecret

You should now see a file tomcat-keystore.jks in the conf dir. Now we can export the certificate.

keytool -export -alias localhost -rfc -keystore ./tomcat-keystore.jks > ./tomcat.cert

You will be prompted for the password, type supersecret. You should now see a tomcat.cert file created in the conf dir. Copy that file into the project root of your application you created above.

From the command line cd to the project root, the location of the tomcat.cert and type the following

keytool -import -alias tomcatCert -file ./tomcat.cert -keystore ./client-truststore.jks

You will be prompted for a password for the truststore. Use trustpass. You will need to enter it twice. When done, it will prompt to trust the certificate, type yes and enter. You should now see a client-truststore.jks file in the project root. This is what we will use for our client app.

Now we just need to configure Tomcat to use our keystore for the connection. In the <tomcat-home>/conf/server.xml, inside the the <Service> element . (Note Tomcat 7 may be a little different)

<Connector port="8443" 
    protocol="org.apache.coyote.http11.Http11NioProtocol"
    maxThreads="150" 
    SSLEnabled="true" 
    scheme="https" 
    secure="true"
    keystoreFile="absolute/path/to/tomcat-keystore.jks"
    keystorePass="supersecret"
    clientAuth="false" 
    sslProtocol="TLS" />

And finally, in our webapp, we should add the secure connection support by altering the <security-constraint>

<security-constraint>
    <web-resource-collection>
        <web-resource-name>Secured Rest App</web-resource-name>
        <url-pattern>/webapi/*</url-pattern>
    </web-resource-collection>
    <auth-constraint>
        <role-name>*</role-name>
    </auth-constraint>
    <user-data-constraint>
        <transport-guarantee>CONFIDENTIAL</transport-guarantee>
    </user-data-constraint>
</security-constraint>

Now we are set to use our client code.

Step 6:

The code above, uses Jersey 1 client. We are using Jersey 2, so the code will be slightly different. Anywhere in the app, just create a class with a main method to run our client. Here's what I use:

import java.io.FileInputStream;
import java.security.KeyStore;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response;
import org.glassfish.jersey.client.authentication.HttpAuthenticatio

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
...