Saturday, September 29, 2012

Maven Repositories

Search Public Maven Artifacts:
http://search.maven.org/
http://download.java.net/maven/2
http://download.java.net/maven/1
http://repo.maven.apache.org/maven2


Spring Framework:
Releases:
<repository>  
  <id>com.springsource.repository.bundles.release</id>  
  <name>SpringSource Enterprise Bundle Repository - SpringSource Bundle Releases</name>  
  <url>http://repository.springsource.com/maven/bundles/release</url> 
</repository>
...
<dependency>  
  <groupId>org.springframework</groupId>  
  <artifactId>org.springframework.core</artifactId>  
  <version>3.0.2.RELEASE</version> 
</dependency>

Milestone:
<repositories>
 <repository>
  <id>springsource maven repo</id>
  <url>http://maven.springframework.org/milestone</url>
 </repository>
</repositories>
...
<dependencies>
  <dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-core</artifactId>
    <version>3.0.0.RC1</version>
  </dependency>
</dependencies>



Hibernate Framework:
<repositories>
    <repository>
        <id>jboss-public-repository-group</id>
        <name>JBoss Public Maven Repository Group</name>
        <url>https://repository.jboss.org/nexus/content/groups/public/</url>
        <layout>default</layout>
        <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
        </releases>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
        </snapshots>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>jboss-public-repository-group</id>
        <name>JBoss Public Maven Repository Group</name>
        <url>https://repository.jboss.org/nexus/content/groups/public/</url>
        <layout>default</layout>
        <releases>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
        </releases>
        <snapshots>
            <enabled>true</enabled>
            <updatePolicy>never</updatePolicy>
        </snapshots>
    </pluginRepository>
</pluginRepositories>


Here is the dependency you need:
    <dependency>
        <groupId>org.hibernate</groupId>
        <artifactId>hibernate-core</artifactId>
        <version>3.5.4-Final</version>
    </dependency>

Following dependencies is required for slf4j error if you are using hibernate:
<dependency>
 <groupId>org.slf4j</groupId>
 <artifactId>slf4j-log4j12</artifactId>
 <version>1.5.6</version>
</dependency>

Tuesday, September 25, 2012

Avoiding the "javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed" error

Please note: this post focuses on the standard Java https implementation.

The SSLHandshakeException is thrown by java when the host you are trying to contact doesn't have a valid SSL certificate for that hostname. Most of the time this is very useful, since it means something on that host is wrong (the certificate has expired, the machine you're contacting is not who it is pretending to be etc...). However, in development mode you often don't want to pay for a "real" certificate, signed by a CA (certificate authority) like Verisign. You will then use a self-signed certificate, which gets rejected by java. It's for these cases that we're going to build a workaround. Please note that you should probably not use this code in a production environment. If you do, there's no reason to use https, since you're bypassing its functionality and you might just as well stick to http.

The first thing we need to do is create a custom TrustManager for SSL. SSL uses a protocol called X.509.  We will build a TrustManager that trusts all servers:
X509TrustManager tm = new X509TrustManager() { 
  @Override
  public X509Certificate[] getAcceptedIssuers() {
    return null;
  }  
  @Override
  public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {   }  
  @Override
  public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { }
};


As you can see, the checkXXXTrusted() methods throw Exceptions when something is wrong. We never throw an exception, effectively trusting all hosts.

The next thing we'll need to do is use this TrustManager on an SSLContext. An SSLContext is a factory class that is used to create socket factories, which in their turn create the actual ssl sockets used to communicate with the server. Here's how we do this:

SSLContext ctx = SSLContext.getInstance("TLS"); 
ctx.init(null, new TrustManager[] { tm }, null);
SSLContext.setDefault(ctx);


There now remains one more thing to be done: set a custom HostnameVerifier. A HostnameVerifier is a class that makes sure the host you are contacting doesn't use a spoofed URL. We will again build a HostnameVerifier that trusts all hosts:
HttpsURLConnection conn = (HttpsURLConnection) new URL("https://serverAddress").openConnection(); 
conn.setHostnameVerifier(new HostnameVerifier() {  
 @Override
 public boolean verify(String paramString, SSLSession paramSSLSession) {
  return true;
 }
});


Again, this HostnameVerifier will trust all hosts.
Putting all our code together, the final class will look like this:


public static void main(String[] args) throws NoSuchAlgorithmException, KeyManagementException, MalformedURLException, IOException {
 X509TrustManager tm = new X509TrustManager() {
  @Override
  public X509Certificate[] getAcceptedIssuers() {
   return null;
  }  
  @Override
  public void checkServerTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException {   }  
  @Override
  public void checkClientTrusted(X509Certificate[] paramArrayOfX509Certificate, String paramString) throws CertificateException { }
 };
 SSLContext ctx = SSLContext.getInstance("TLS");
 ctx.init(null, new TrustManager[] { tm }, null);
 SSLContext.setDefault(ctx);  
 HttpsURLConnection conn = (HttpsURLConnection) new URL("https://serverAddress").openConnection();
 conn.setHostnameVerifier(new HostnameVerifier() {  
  @Override
  public boolean verify(String paramString, SSLSession paramSSLSession) {
   return true;
  }
 });
 conn.connect();  
}


One final note: I prefer the way the Apache HttpClient library handles this. In the HttpClient library you can make a clean separation between the ssl verification logic and the code that does the actual work. This allows you to easily remove the code in the production environment or to use a switch between the development and production environment. This is much harder in the plain java version, since the code is more entangled. See this post for how to do this with the Apache HttpClient.
 

Avoiding the "javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated" with HttpClient

Please note: This article focuses on the Apache HttpClient library.

When developing a https application, your test server often doesn't have a (valid) SSL certificate. This will cause the following exception to be thrown when connecting your client to the test server: "javax.net.ssl.SSLPeerUnverifiedException: peer not authenticated".
I will be discussing a way to fix this issue with the apache HttpClient, version 4.0.1

Bits and pieces
--------------------
You usually create your HttpClient like this:
client = new DefaultHttpClient();

We will need to tell the client to use a different TrustManager. A TrustManager is a class that checks if given credentials (or certificates) are valid. The scheme used by SSL is called X.509, and Java has a specific TrustManager for this scheme, called X509 TrustManager. First thing we will need to do is create such a TrustManager:
X509TrustManager tm = new X509TrustManager() {  
 public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }  
 public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }  
 public X509Certificate[] getAcceptedIssuers() {
 return null;
 }
};


As you can see, this code doesn't do much: if a certificate is invalid the TrustManager is supposed to throw a CertificateException in the checkXXX methods. Since we always want to accept all certificates, we never throw an exception.

Next we need to find a way to set this TrustManager in our HttpClient. The TrustManager is used by the SSL sockets. Sockets are created using a SocketFactory. For SSL sockets this is an SSLSocketFactory.

When creating a new SSLSocketFactory, you need to pass an SSLContext to the constructor. It is this SSLContext that will contain our newly created TrustManager.

First thing we need to do is get an SSLContext:SSLContext ctx = SSLContext.getInstance("TLS");

TLS is the successor to SSL, but they use the same SSLContext.
Then we initialize this context with our new TrustManager that we created above:ctx.init(null, new TrustManager[]{tm}, null);

We can then finally create our SSLSocketFactory:SSLSocketFactory ssf = new SSLSocketFactory(ctx);

Now we still need to register this SSLSocketFactory with our HttpClient. This is done in the SchemeRegistry of the ConnectionManager of the HttpClient:ClientConnectionManager ccm = base.getConnectionManager();
SchemeRegistry sr = ccm.getSchemeRegistry();
sr.register(new Scheme("https", ssf, 443));


We register a new Scheme, with the protocol https, our newly created SSLSocketFactory which contains our TrustManager and we tell the HttpClient that the default port for https is port 443.

Putting it all together:
===================
The following class takes a HttpClient and returns a new HttpClient that accepts any SSL certificate:
/* This code is public domain: you are free to use, link and/or modify it in any way you want, for all purposes including commercial applications.  */
public class WebClientDevWrapper { 
 public static HttpClient wrapClient(HttpClient base) {
 try { 

  SSLContext ctx = SSLContext.getInstance("TLS");
  X509TrustManager tm = new X509TrustManager() {  
   public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException { }  
   public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException { }  
   public X509Certificate[] getAcceptedIssuers() {
    return null;
   }
  }; 


  ctx.init(null, new TrustManager[]{tm}, null);
  SSLSocketFactory ssf = new SSLSocketFactory(ctx);
  ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
  ClientConnectionManager ccm = base.getConnectionManager();
  SchemeRegistry sr = ccm.getSchemeRegistry();
  sr.register(new Scheme("https", ssf, 443));
  return new DefaultHttpClient(ccm, base.getParams());
 } catch (Exception ex) {
  ex.printStackTrace();
  return null;
 }
      }
}


You can then do something like this in the code that creates the HttpClient:
this.client = new DefaultHttpClient();
if(dev) {
 this.client = WebClientDevWrapper.wrapClient(client);
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - -  - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Update:
In some exceptional cases, the method described above doesn't work. This is due to the Apache AllowAllHostnameVerifier still being to strict. In this case, you will need your own X509HostnameVerifier. Create it as follows:
X509HostnameVerifier verifier = new X509HostnameVerifier() {                  
 @Override               
 public void verify(String string, SSLSocket ssls) throws IOException {}                  
 @Override               
 public void verify(String string, X509Certificate xc) throws SSLException {}
 @Override               
 public void verify(String string, String[] strings, String[] strings1) throws SSLException {}
 @Override               
 public boolean verify(String string, SSLSession ssls) {
  return true;                
 }            
};


Then set it on your socket factory:
ssf.setHostnameVerifier(verifier);

If we put everything together, the new code looks like this:
/* This code is public domain: you are free to use, link and/or modify it in any way you want, for all purposes including commercial applications.  */

public class WebClientDevWrapper {      
 public static HttpClient wrapClient(HttpClient base) {        
  try {             

   SSLContext ctx = SSLContext.getInstance("TLS");
   X509TrustManager tm = new X509TrustManager() {                  
    public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
    public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {}
    public X509Certificate[] getAcceptedIssuers() {                    
     return null;                
    }            
   };

            
   X509HostnameVerifier verifier = new X509HostnameVerifier() {                   

   @Override               
   public void verify(String string, SSLSocket ssls) throws IOException {}

   @Override               
   public void verify(String string, X509Certificate xc) throws SSLException {}

   @Override               
   public void verify(String string, String[] strings, String[] strings1) throws SSLException {}

   @Override               
   public boolean verify(String string, SSLSession ssls) {
    return true; 
   }            
  };             

  ctx.init(null, new TrustManager[]{tm}, null);            
  SSLSocketFactory ssf = new SSLSocketFactory(ctx);            
  ssf.setHostnameVerifier(verifier);            
  ClientConnectionManager ccm = base.getConnectionManager();    
  SchemeRegistry sr = ccm.getSchemeRegistry();            
  sr.register(new Scheme("https", ssf, 443));            
  return new DefaultHttpClient(ccm, base.getParams());         

 } catch (Exception ex) {            
  ex.printStackTrace();            
  return null;        
 }    
    }
}

You can then do something like this in the code that creates the HttpClient:
this.client = new DefaultHttpClient();
if(dev) {
 this.client = WebClientDevWrapper.wrapClient(client);
}

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Working Example implemented in one of my project:

package com.company.test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;

import java.security.KeyManagementException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;


public class HttpsTestClient {
 public static void main(String[] args) throws KeyManagementException, UnrecoverableKeyException, NoSuchAlgorithmException, KeyStoreException {
  SchemeRegistry registry = new SchemeRegistry();
  SSLSocketFactory socketFactory = new SSLSocketFactory(new TrustStrategy() {
   public boolean isTrusted(X509Certificate[] arg0, String arg1) throws CertificateException {
    return true;
   }
  }, org.apache.http.conn.ssl.SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
 
  registry.register(new Scheme("https", 443, socketFactory));
  ThreadSafeClientConnManager mgr = new ThreadSafeClientConnManager(registry);
 
  DefaultHttpClient client = new DefaultHttpClient(mgr, new DefaultHttpClient().getParams());
       
     HttpPost post = new HttpPost("
https://host:7032/Xyz/service");
     try {
      String send = "< ?xml version=\"1.0\" encoding=\"UTF-8\"?> <msg> <head><Client>ComXCalFL</Client> <Region>QAYA</Region> <Source>E26XCALIBUR</Source><Destination>CCS01</Destination> <Origin>L0OG1-X-CAL ORDER DETAIL UPDATED</Origin> <Version>2.25</Version> <RoundTrip>MDU6MDAwMDAwMDM6MjAxMjA4MzE6MTE0OTE0MDA6ODIxMDAwMDA6TDBPRzowMg==</RoundTrip> <UserId>LRM 0B72</UserId> <ServerId>sbappre02-z04</ServerId> <TriggerDate>2012-08-31</TriggerDate><TriggerTime>11:49:14</TriggerTime> </head><body><OrderDetailUpdated> XML_PAYLOAD</OrderDetailUpdated></body></msg>";

     
      StringEntity strEnt = new StringEntity(send);
      post.setEntity(strEnt);
       HttpResponse response = client.execute( post);
       BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
       String line = "";
       while ((line = rd.readLine()) != null) {
         System.out.println(line);
       }

     } catch (IOException e) {
       e.printStackTrace();
     }
   }

}