6
votes

I'm trying to use the SSL certificate obtained with StartSSL.com on an Apache server. The connection with browser is fine, but when I try to use a Java application, I got this exeption:

Exception in thread "main" 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 don't understand what could be the problem, because with the browser, I got the SSL with green label.

5
you probably didn't import your certificate into the JVM see details hereuser4545399
in my own HTTPS library, I have a convenience method trustAll() so that all server certificates, including self-signed ones, are accepted. this is probably OK for some client applications (but certainly not for a major browser!) The security threat is that some man-in-the-middle may be able to eavesdrop or even modify the traffic; but that is quite improbable. It requires resources like NSA has; but I'm pretty sure NSA can hack into SSL anyway :)ZhongYu
@bayou.io Actually, it can happen in many other places, e.g. public wifis.Bruno
@Bruno - the problem is certificate-tempering is immediately noticeable to browser users. so if our client is a small fish under the radar, we might get away with not being strict. :)ZhongYu
@bayou.io That's just non-sense. That's why there's plenty of mobile apps that are insecure, for example.Bruno

5 Answers

10
votes

The problem is your Java is not trusting the certificate. You have to import it to your java's truststore.

# Copy the certificate into the directory Java_home\Jre\Lib\Security
# Change your directory to Java_home\Jre\Lib\Security>
# Import the certificate to a trust store.
# Here's the import command:

keytool -import -alias ca -file somecert.cer -keystore cacerts -storepass changeit [Return]

Trust this certificate: [Yes]

changeit is default truststore password.

For every certificate that you imported into your truststore you have to provide a new alias.

The import method is a quote from Here

1
votes

This message is due to:

  1. Either the JRE does not have the root CA as a trusted entry in its keystore.
  2. The server is not sending a proper chain.

But, 2 is not valid in your case since the browser is able to construct the chain and validate the certificate.

Consequently you need to get the root CA and put it in the JRE keystore as a trusted entry. There are lots of resources that document the "how". One of them is: https://access.redhat.com/documentation/en-US/Fuse_Message_Broker/5.3/html/Security_Guide/files/i379776.html

EDIT 1: Since you want to share the java app, it would we worth the effort to get a certificate from a CA whose root is already trusted in the trust store for the Java versions that your app supports.

0
votes

As far as I understand this is related to the Java Certificate Keystore. Your certificate is not accepted by java. Here is a link how to add your certificate to Java trusted Certificates keystore: https://docs.oracle.com/javase/tutorial/security/toolsign/rstep2.html

0
votes

There are a few questions like this already on SO (like this one: PKIX path building failed: unable to find valid certification path to requested target).

The usual answer to this is that your java client doesn't have certificates needed to complete the certificate chain.

If you need proper certificate validation, then you'll have to figure out where the certificate chain breaks down. If you don't (because this is a proof of concept or a dev sandbox, or whatever), then you can easily work around this by adding the certificate to the trust store you use with your client.

Edit:

As for why your browser accepts this, it's likely that either your browser has the certificates in the chain that you need or you've absentmindedly told your browser to trust the certificate even though it also wasn't able to validate the certificate.

0
votes

I recomend use http-request built on apache http api.

import org.junit.Test;

import static org.apache.http.HttpHeaders.ACCEPT;
import static org.apache.http.HttpStatus.SC_OK;
import static org.apache.http.entity.ContentType.APPLICATION_XML;
import static org.junit.Assert.assertEquals;

public class HttpRequestSSLTest {

private final HttpRequest<?> httpRequest = HttpRequestBuilder.createGet("https://mms.nw.ru/")
        .trustAllCertificates()
        .trustAllHosts()
        .addDefaultHeader(ACCEPT, APPLICATION_XML.getMimeType())
        .build();

@Test
public final void ignoreSSLAndHostsTest() throws Exception {

    assertEquals(SC_OK, httpRequest.execute().getStatusCode());
}

}