spring-projects / spring-boot

Spring Boot helps you to create Spring-powered, production-grade applications and services with absolute minimum fuss.
https://spring.io/projects/spring-boot
Apache License 2.0
75.38k stars 40.72k forks source link

Truststore not found in Spring Boot #3156

Closed cbelleza closed 9 years ago

cbelleza commented 9 years ago

Defining a trust-store in application.properties Spring Boot can not recognize and gives the exception:

Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:387)
    at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:292)
    at sun.security.validator.Validator.validate(Validator.java:260)
    at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
    at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
    at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
    at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1460)
    ... 64 common frames omitted
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
    at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:145)
    at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:131)
    at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
    at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:382)
    ... 70 common frames omitted

application.properties

server.ssl.trust-store=C:\\Temp\\config\\localhost.jks
server.ssl.trust-store-password=localhost

But If I set it via java parameters, Spring Boot can works with my trust-sore. -Djavax.net.debug=ssl -Djavax.net.ssl.trustStore=C:\Temp\config\localhost.jks -Djavax.net.ssl.trustStorePassword=localhost

Why does it happen? It seems setting the property in Spring Boot file does not take effects.

Thanks!

wilkinsona commented 9 years ago

You haven't really provided enough information to know why it isn't working. Which embedded servlet container at you using? How are you running your application?

We have a test that verifies basic trust store support with an embedded container. If you want us to look at this further, please provide a sample that illustrates the problem.

cbelleza commented 9 years ago

Hi @wilkinsona

I tried with Undertow and Tomcat, when I open an https Url it shows this error saying that my trust store does not contain the CA which the certificate was signed. (Internal CA in this case).

If I add the java variables the url works (-Djavax.net.ssl.trustStore, but via Spring boot properties don't. I even found an issue about some people complaining about it :http://stackoverflow.com/questions/27724544/specifying-trust-store-information-in-spring-boot-application-properties

Do you have any idea? Thanks

wilkinsona commented 9 years ago

I commented on that Stack Overflow post asking for some more information, but none was provided. As I said above, "if you want us to look at this further, please provide a sample that illustrates the problem".

cbelleza commented 9 years ago

Ok, but this error happens using a custom CA.

wilkinsona commented 9 years ago

If you can't provide a sample that reproduces the problem, or detailed steps that will allow us to reproduce the problem, it's very unlikely that we're going to be able to help you. It's not even clear at this point that there's a bug in Spring Boot.

We'll happily take a sample project any way you can get it to us: something on Dropbox, your own GitHub repo, a pull request again the Spring Boot issues repository etc.

cbelleza commented 9 years ago

Hi @wilkinsona

Please take a look at this java code, this https URL uses a certificate issued by a non-authorized company such as Verisign. So if you import the certificate to .jks file and I set into Spring Boot property (trustStore), it does no take any effect, but if I run the application passing java VM parameters it works fine and prints page content.

package com.teste;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;

import javax.annotation.PostConstruct;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SampleApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(SampleApplication.class, args);
    }

    @PostConstruct
    public void init() throws MalformedURLException {
        try {
            String a = "https://cav.receita.fazenda.gov.br/";
            URL url = new URL(a);
            URLConnection conn = url.openConnection();

            // open the stream and put it into BufferedReader
            BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream()));

            String inputLine;
            while ((inputLine = br.readLine()) != null) {
                System.out.println(inputLine);
            }
            br.close();

            System.out.println("Done");

        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
wilkinsona commented 9 years ago

Setting server.ssl.trust-store isn't going to have any effect on a standard URLConnection. server.ssl.trust-store configures the embedded servlet container's trust store when you're using client authentication, i.e it tells the servlet container which SSL certificates it should trust when it receives a request. It'll have no effect at all when you're making an outbound request using url.openConnection(), or any other mechanism for that matter.

cbelleza commented 9 years ago

@wilkinsona You mean, should I always set my trust store via java parameters? -Djavax.net.ssl.trustStore=C:\Temp\config\localhost.jks -Djavax.net.ssl.trustStorePassword=localhost

I do not really understand whats the purpose of "server.ssl.trust-store" property so? Because when I need to create an https port for my web server I can use "server.ssl.key-store" property to find my certificates, but for trust store it does not work.

wilkinsona commented 9 years ago

The server.ssl.trust-store is used to configure a store of the SSL certificates that the server trusts. When a client wants to connect to the server over HTTPS, it must provide a trusted certificate to the server to be allow to connect.

Judging by your code above, you have a client that's connecting to a server over HTTPS. You need to configure the client to trust the server's SSL certificate. One way to do this is via the javax.net.ssl.trustStore system property. This will affect every HTTPS client in the JVM which, depending on your use case, may or may not be too broad. An alternative is to use an HTTP client that allows you to configure a trust store that's scoped to the client. Here's an example showing how to do this with Apache HTTP components client.