cancel
Showing results for 
Search instead for 
Did you mean: 

Accessing an HTTPS destination using Java Tomcat

ChrisPaine
Active Contributor
0 Kudos

Hi,

when trying to access HTTPS destinations using Java Tomcat I'm getting errors from

javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target 

I'm guessing that this is because I don't have a certificate loaded for the site I am trying to connect to.

However, as I'm using Tomcat, I'm not using the connectivity service (other than to store the URL of the destination). How do I go about loading a certificate for this connection?

Of course running locally the Tomcat instance happily negotiates the SSL connection since my local java truststore has the main trusted CA certs.

I can, of course, generate a new truststore, add the certificate of my destination in there, but how do I specify the password that should be used to access the truststore/or that it should be used at all?

If it's not possible to add certs to the Tomcat runtime, it would be nice if the major trusted CAs were loaded by default.

I however, have the solution happily negotiating to an XS service on HCP using HTTPS.

Thanks for some help!

Chris

Accepted Solutions (1)

Accepted Solutions (1)

ChrisPaine
Active Contributor
0 Kudos

OK for the record - here is the solution that worked:

in my servlet


ArrayList<String> myTrustedCerts = new ArrayList<String>();

myTrustedCerts.add(request.getServletContext().getRealPath("/Certs/linkedin.cer"));

myTrustedCerts.add(request.getServletContext().getRealPath("/Certs/digicertGlobalRootCA.cer"));

myTrustedCerts.add(request.getServletContext().getRealPath("/Certs/digicert2.cer"));

TrustManager[] trustLinkedIn = new TrustManager[] { new MyTrustManager(myTrustedCerts) };

try {

  SSLContext sc = SSLContext.getInstance("SSL");

  sc.init(null, trustLinkedIn, new java.security.SecureRandom());

  HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

} catch (GeneralSecurityException e) {

  logger.error("problem faking LI cert lookup", e);

}

and then my trust manager


public class MyTrustManager implements X509TrustManager {

  private static final Logger logger = LoggerFactory.getLogger(MyTrustManager.class);

  private ArrayList<X509Certificate> certs;

  private X509TrustManager defaultTm;

  public MyTrustManager(List<String> certLocations) {

  super();

  defaultTm = null;

  TrustManagerFactory tmf;

  try {

  tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());

  // Using null here initialises the TMF with the default trust store.

  tmf.init((KeyStore) null);

  // Get hold of the default trust manager

  for (TrustManager tm : tmf.getTrustManagers()) {

  if (tm instanceof X509TrustManager) {

  defaultTm = (X509TrustManager) tm;

  break;

  }

  }

  certs = new ArrayList<X509Certificate>();

  InputStream inStream;

  for (String certLocation : certLocations) {

  inStream = new FileInputStream(certLocation);

  CertificateFactory cf = CertificateFactory.getInstance("X.509");

  X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);

  inStream.close();

  certs.add(cert);

  }

  } catch (NoSuchAlgorithmException | KeyStoreException | IOException | CertificateException e) {

  logger.error("issues with additional trust manager", e);

  }

  }

  @Override

  public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

  try {

  defaultTm.checkClientTrusted(chain, authType);

  } catch (CertificateException e) {

  for (X509Certificate cert : chain) {

  boolean certNotFound = true;

  for (X509Certificate knownCert : certs) {

  if (cert.getPublicKey().equals(knownCert.getPublicKey())) {

  certNotFound = false;

  break;

  }

  if (certNotFound) {

  throw new CertificateException();

  }

  }

  }

  }

  }

  @Override

  public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

  try {

  defaultTm.checkClientTrusted(chain, authType);

  } catch (CertificateException e) {

  for (X509Certificate cert : chain) {

  boolean certNotFound = true;

  for (X509Certificate knownCert : certs) {

  if (cert.getPublicKey().equals(knownCert.getPublicKey())) {

  certNotFound = false;

  }

  }

  if (certNotFound) {

  throw new CertificateException();

  }

  }

  }

  }

  @Override

  public X509Certificate[] getAcceptedIssuers() {

  X509Certificate[] certArray = new X509Certificate[128];

  if (certs.size() == 3) {

  certs.addAll(Arrays.asList(defaultTm.getAcceptedIssuers()));

  }

  return certs.toArray(certArray);

  }

}

It works. It will only allow calls to the server that identifies itself with the same certificates that I have loaded. I'm guessing there are better ways that I could have done this if I'd just verified the root cert, but it would have been a darn sight harder.

I'll have to watch out for those certificates expiring, but hopefully before then a way to add root CAs to a HCP Java Web Tomcat servers will arrive

Cheers,


Chris

lazar_kirchev
Explorer
0 Kudos

Hi Chris,

You could use the procedure described in [1] to upload your custom keystore to the cloud. Then you could use it in your app, e.g. as in [2].

Regards,

Lazar

[1].SAP HANA Cloud Platform

[2].SAP HANA Cloud Platform

ChrisPaine
Active Contributor
0 Kudos

Hi Lazar,

It's great that these new API are available. When I wrote the question the keystore API was not available in tomcat.

Cheers,

Chris

Answers (2)

Answers (2)

ChrisPaine
Active Contributor
0 Kudos

So - got around it by doing this:


public class MyTrustManager implements X509TrustManager {

  public MyTrustManager(String certLocation) {

  super();

  this.certLocation = certLocation;

  }

  private String certLocation;

  @Override

  public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {

  }

  @Override

  public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {

  // TODO Auto-generated method stub

  }

  @Override

  public X509Certificate[] getAcceptedIssuers() {

  X509Certificate[] certArray = new X509Certificate[1];

  ArrayList<X509Certificate> certs = new ArrayList<X509Certificate>();

  InputStream inStream;

  try {

  inStream = new FileInputStream(certLocation);

  CertificateFactory cf = CertificateFactory.getInstance("X.509");

  X509Certificate cert = (X509Certificate) cf.generateCertificate(inStream);

  inStream.close();

  certs.add(cert);

  } catch (IOException | CertificateException e) {

  // TODO Auto-generated catch block

  e.printStackTrace();

  }

  return certs.toArray(certArray);

  }

}

and just before making the HTTPS call:


TrustManager[] trustLinkedIn = new TrustManager[] { new MyTrustManager(request.getServletContext().getRealPath("/Certs/linkedIn.cer"))

try {

SSLContext sc = SSLContext.getInstance("SSL");

sc.init(null, trustLinkedIn, new java.security.SecureRandom());

HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

} catch (GeneralSecurityException e) {

   logger.error("problem faking LI cert lookup", e);

}

So now outbound connections to the linkedIn API server are being accepted, but it's still not ideal. I will try to improve it, but it's going to have to do unless the normal root CA certs are available somehow.

ChrisPaine
Active Contributor
0 Kudos

Problem is this breaks the connectivity service...

ChrisPaine
Active Contributor
0 Kudos

the problem was actually my proxy implementation - sticking with the default proxy solution worked, explicitly specifying the proxy broke the destination service logic.

ChrisPaine
Active Contributor
0 Kudos

Probably worth noting that in past I've used non-Tomcat runtimes and have had no problems connecting to the same destination - and I haven't used the destination API, or loaded certificates, it seems the standard root CA's were loaded by default. But not in this case. (apparently - happy to be wrong!)