web-dev-qa-db-fra.com

Spécification des informations de magasin de confiance dans spring boot application.properties

J'utilisespringBootVersion 1.2.0.RELEASE. J'essaie de configurer mon magasin de clés et mon magasin de clés de confiance via application.properties.

Lorsque j'ajoute les paramètres suivants, le fichier de clés peut fonctionner, mais pas le fichier de clés certifiées.

server.ssl.key-store=classpath:foo.jks
server.ssl.key-store-password=password
server.ssl.key-password=password
server.ssl.trust-store=classpath:foo.jks
server.ssl.trust-store-password=password

Toutefois, si j'ajoute le fichier de clés de confiance via gradle:

bootRun {
    jvmArgs = [ "-Djavax.net.ssl.trustStore=c://foo.jks", "-Djavax.net.ssl.trustStorePassword=password"]
}

cela fonctionne très bien.

Quelqu'un at-il utilisé le application.properties pour les magasins de confiance?

43
user4408912

Si vous devez passer un appel REST, vous pouvez utiliser la méthode suivante.

Ceci fonctionnera pour les appels sortants via RestTemplate.

Déclarez RestTemplate bean comme ceci.

@Configuration
public class SslConfiguration {
    @Value("${http.client.ssl.trust-store}")
    private Resource keyStore;
    @Value("${http.client.ssl.trust-store-password}")
    private String keyStorePassword;

    @Bean
    RestTemplate restTemplate() throws Exception {
        SSLContext sslContext = new SSLContextBuilder()
                .loadTrustMaterial(
                        keyStore.getURL(),
                        keyStorePassword.toCharArray()
                ).build();
        SSLConnectionSocketFactory socketFactory = 
                new SSLConnectionSocketFactory(sslContext);
        HttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(socketFactory).build();
        HttpComponentsClientHttpRequestFactory factory = 
                new HttpComponentsClientHttpRequestFactory(httpClient);
        return new RestTemplate(factory);
    }
}

http.client.ssl.trust-store et http.client.ssl.trust-store-password désignent le fichier de clés certifiées au format JKS et le mot de passe du magasin de clés spécifié.

Cela remplacera le bean RestTemplate fourni avec Spring Boot et lui fera utiliser le magasin de données de confiance dont vous avez besoin. 

21
Oleksandr Shpota

J'ai le même problème, je vais essayer de l'expliquer un peu plus en détail.

J'utilise spring-boot 1.2.2-RELEASE et l'ai essayé sur Tomcat et Undertow avec le même résultat.

Définir le magasin de confiance dans application.yml comme suit:

server:
  ssl:
    trust-store: path-to-truststore...
    trust-store-password: my-secret-password...

Ne fonctionne pas, alors que:

$ Java -Djavax.net.debug=ssl -Djavax.net.ssl.trustStore=path-to-truststore... -Djavax.net.ssl.trustStorePassword=my-secret-password... -jar build/libs/*.jar  

fonctionne parfaitement bien.

Le moyen le plus simple de voir la différence à l’arrivée est d’activer ssl-debug dans le client. Lorsque vous travaillez (c'est-à-dire en utilisant les indicateurs -D), le texte suivant est écrit sur la console (lors du traitement de la première demande):

trustStore is: path-to-truststore...
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert:
  Subject: C=..., ST=..., O=..., OU=..., CN=...
  Issuer:  C=..., ST=..., O=..., OU=..., CN=...
  Algorithm: RSA; Serial number: 0x4d2
  Valid from Wed Oct 16 17:58:35 CEST 2013 until Tue Oct 11 17:58:35 CEST 2033

Sans les drapeaux -D, je reçois:

trustStore is: /Library/Java/JavaVirtualMachines/jdk1.8.0_11.jdk/Contents/Home/jre/lib/security/cacerts
trustStore type is : jks
trustStore provider is :
init truststore
adding as trusted cert: ... (one for each CA-cert in the defult truststore)

... et lors de l'exécution d'une requête, j'obtiens l'exception:

Sun.security.validator.ValidatorException: PKIX path building failed: Sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target

J'espère que cela aide à mieux comprendre le problème!

11
Magnus Larsson

J'ai eu le même problème avec Spring Boot, Spring Cloud (microservices) et un certificat SSL auto-signé. Keystore a fonctionné directement à partir des propriétés de l'application, contrairement à Truststore.

J'ai fini par conserver la configuration du fichier de clés et du fichier de relations de confiance dans application.properties, puis d'ajouter un bean de configuration distinct pour la configuration des propriétés du magasin de clés de confiance avec le système.

@Configuration
public class SSLConfig {
    @Autowired
    private Environment env;

    @PostConstruct
    private void configureSSL() {
      //set to TLSv1.1 or TLSv1.2
      System.setProperty("https.protocols", "TLSv1.1");

      //load the 'javax.net.ssl.trustStore' and
      //'javax.net.ssl.trustStorePassword' from application.properties
      System.setProperty("javax.net.ssl.trustStore", env.getProperty("server.ssl.trust-store")); 
      System.setProperty("javax.net.ssl.trustStorePassword",env.getProperty("server.ssl.trust-store-password"));
    }
}
10
Mate Šimović

J'avais également le même problème avec Spring Boot et Tomcat intégré.

D'après ce que j'ai compris, ces propriétés définissent uniquement les paramètres de configuration de Tomcat. Selon la documentation de Tomcat, cela n’est utilisé que pour l’authentification du client (c'est-à-dire pour le protocole SSL bidirectionnel) et non pour la vérification des certificats distants:

truststoreFile - Le fichier de magasin de confiance à utiliser pour valider client certificats.

(https://Tomcat.Apache.org/Tomcat-8.0-doc/config/http.html } _

Afin de configurer le magasin de données de confiance pour HttpClient, celui-ci dépend en grande partie de l'implémentation de HttpClient que vous utilisez. Par exemple, pour RestTemplate par défaut, Spring Boot utilise une classe SimpleClientHttpRequestFactory basée sur les classes J2SE standard telles que Java.net.HttpURLConnection.

J'ai mis au point une solution basée sur la documentation Apache HttpClient et sur ses publications: http://vincentdevillers.blogspot.pt/2013/02/configure-best-spring-resttemplate.htmlhttp://literatejava.com/networks/ignore-ssl-certificate-errors-Apache-httpclient-4-4/

En gros, cela permet de créer un bean RestTemplate qui approuve uniquement les certificats signés par l'autorité de certification racine dans le magasin de clés de confiance configuré.

@Configuration
public class RestClientConfig {

    // e.g. Add http.client.ssl.trust-store=classpath:ssl/truststore.jks to application.properties
    @Value("${http.client.ssl.trust-store}")
    private Resource trustStore;

    @Value("${http.client.ssl.trust-store-password}")
    private char[] trustStorePassword;

    @Value("${http.client.maxPoolSize}")
    private Integer maxPoolSize;


    @Bean
    public ClientHttpRequestFactory httpRequestFactory() {
        return new HttpComponentsClientHttpRequestFactory(httpClient());
    }

    @Bean
    public HttpClient httpClient() {

        // Trust own CA and all child certs
        Registry<ConnectionSocketFactory> socketFactoryRegistry = null;
        try {
            SSLContext sslContext = SSLContexts
                    .custom()
                    .loadTrustMaterial(trustStore.getFile(),
                            trustStorePassword)
                    .build();

            // Since only our own certs are trusted, hostname verification is probably safe to bypass
            SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext,
                    new HostnameVerifier() {

                        @Override
                        public boolean verify(final String hostname,
                                final SSLSession session) {
                            return true;
                        }
            });

            socketFactoryRegistry = RegistryBuilder.<ConnectionSocketFactory>create()
                    .register("http", PlainConnectionSocketFactory.getSocketFactory())
                    .register("https", sslSocketFactory)
                    .build();           

        } catch (Exception e) {
            //TODO: handle exceptions
            e.printStackTrace();
        }

        PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        connectionManager.setMaxTotal(maxPoolSize);
        // This client is for internal connections so only one route is expected
        connectionManager.setDefaultMaxPerRoute(maxPoolSize);
        return HttpClientBuilder.create()
                .setConnectionManager(connectionManager)
                .disableCookieManagement()
                .disableAuthCaching()
                .build();
    }

    @Bean
    public RestTemplate restTemplate() {
        RestTemplate restTemplate = new RestTemplate();
        restTemplate.setRequestFactory(httpRequestFactory());
        return restTemplate;
    }    
}

Et ensuite, vous pouvez utiliser ce client Rest personnalisé à tout moment, par exemple:

@Autowired
private RestTemplate restTemplate;

restTemplate.getForEntity(...)

Cela suppose que vous tentiez de vous connecter à un point de terminaison Rest, mais vous pouvez également utiliser le bean HttpClient ci-dessus pour tout ce que vous voulez.

7
jpt

Les propriétés Java "javax.net.ssl.trustStore" et "javax.net.ssl.trustStorePassword" ne correspondent pas à "server.ssl.trust-store" et "server.ssl.trust-store-password" à partir de Spring Boot " application.properties "(" application.yml ")

vous ne pouvez donc pas définir "javax.net.ssl.trustStore" et "javax.net.ssl.trustStorePassword" simplement en définissant "server.ssl.trust-store" et "server.ssl.trust-store-password" dans " application.properties "(" application.yml ")

une alternative consistant à définir "javax.net.ssl.trustStore" et "javax.net.ssl.trustStorePassword" est définie par Spring boot configuration externalisée

voici des extraits de ma mise en œuvre:

La classe Params contient les paramètres externes 

@Component
@ConfigurationProperties("params")
public class Params{

    //default values, can be override by external settings
    public static String trustStorePath = "config/client-truststore.jks";
    public static String trustStorePassword = "wso2carbon";
    public static String keyStorePath = "config/wso2carbon.jks";
    public static String keyStorePassword = "wso2carbon";
    public static String defaultType = "JKS";

    public void setTrustStorePath(String trustStorePath){
        Params.trustStorePath = trustStorePath;
    }

    public void settrustStorePassword(String trustStorePassword){
        Params.trustStorePassword=trustStorePassword;
    }

    public void setKeyStorePath(String keyStorePath){
        Params.keyStorePath = keyStorePath;
    }

    public void setkeyStorePassword(String keyStorePassword){
        Params.keyStorePassword = keyStorePassword;
    }

    public void setDefaultType(String defaultType){
        Params.defaultType = defaultType;
    }

La classe KeyStoreUtil gère les paramètres "javax.net.ssl.trustStore" et "javax.net.ssl.trustStorePassword".

public class KeyStoreUtil {

    public static void setTrustStoreParams() {
        File filePath = new File( Params.trustStorePath);
        String tsp = filePath.getAbsolutePath();
        System.setProperty("javax.net.ssl.trustStore", tsp);
        System.setProperty("javax.net.ssl.trustStorePassword", Params.trustStorePassword);
        System.setProperty("javax.net.ssl.keyStoreType", Params.defaultType);

    }

    public static void setKeyStoreParams() {
        File filePath = new File(Params.keyStorePath);
        String ksp = filePath.getAbsolutePath();
        System.setProperty("Security.KeyStore.Location", ksp);
        System.setProperty("Security.KeyStore.Password", Params.keyStorePassword);

    }     
}

vous obtenez les setters exécutés dans la fonction de démarrage 

@SpringBootApplication
@ComponentScan("com.myapp.profiles")
public class ProfilesApplication {

    public static void main(String[] args) {
        KeyStoreUtil.setKeyStoreParams();
        KeyStoreUtil.setTrustStoreParams();
        SpringApplication.run(ProfilesApplication.class, args);
    }
}

Modifié le 2018-10-03

vous voudrez peut-être aussi adopter l'annotation "PostConstruct" comme alternative à l'exécution des setters

import javax.annotation.PostConstruct;

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

@SpringBootApplication(scanBasePackages={"com.xxx"})
public class GateApplication {

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

    @PostConstruct
    void postConstruct(){
        setTrustStoreParams();
        setKeyStoreParams();
    }


    private static void setTrustStoreParams() {
        File filePath = new File( Params.trustStorePath);
        String tsp = filePath.getAbsolutePath();
        System.setProperty("javax.net.ssl.trustStore", tsp);
        System.setProperty("javax.net.ssl.trustStorePassword", Params.trustStorePassword);
        System.setProperty("javax.net.ssl.keyStoreType", Params.defaultType);

    }

    private static void setKeyStoreParams() {
        File filePath = new File(Params.keyStorePath);
        String ksp = filePath.getAbsolutePath();
        System.setProperty("Security.KeyStore.Location", ksp);
        System.setProperty("Security.KeyStore.Password", Params.keyStorePassword);

    }
}

l'application.yml 

---
 params: 
   trustStorePath: config/client-truststore.jks
   trustStorePassword: wso2carbon
   keyStorePath: config/wso2carbon.jks
   keyStorePassword: wso2carbon
   defaultType: JKS
---

enfin, dans l'environnement en cours d'exécution (serveur de déploiement), vous créez un dossier nommé "config" dans le même dossier que l'archive jar.

dans le dossier "config", vous stockez "application.yml", "client-truststore.jks" et "wso2carbon.jks". terminé!

Mise à jour du 2018-11-27 sur Spring Boot 2.x.x

à partir de spring boot 2.x.x, les propriétés statiques ne sont plus supportées, veuillez voir ici . Personnellement, je ne pense pas que ce soit un bon geste, car des changements complexes doivent être apportés tout au long de la chaîne de référence ...

de toute façon, un extrait d’impelmation pourrait ressembler à ceci 

la classe 'Params' 

    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;

    import lombok.Data;

    /**
     * Params class represent all config parameters that can 
     * be external set by spring xml file
     */

    @Component
    @ConfigurationProperties("params")
    @Data
    public class Params{

        //default values, can be override by external settings
        public String trustStorePath = "config/client-truststore.jks";
        public String trustStorePassword = "wso2carbon";
        public String keyStorePath = "config/wso2carbon.jks";
        public String keyStorePassword = "wso2carbon";
        public String defaultType = "JKS";  
}

la 'classe d'application Springboot' (avec 'PostConstruct')

import Java.io.File;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages={"com.xx.xx"})
public class BillingApplication {

    @Autowired
    Params params;

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

    @PostConstruct
    void postConstruct() {

        // set TrustStoreParams
        File trustStoreFilePath = new File(params.trustStorePath);
        String tsp = trustStoreFilePath.getAbsolutePath();
        System.setProperty("javax.net.ssl.trustStore", tsp);
        System.setProperty("javax.net.ssl.trustStorePassword", params.trustStorePassword);
        System.setProperty("javax.net.ssl.keyStoreType", params.defaultType);
        // set KeyStoreParams
        File keyStoreFilePath = new File(params.keyStorePath);
        String ksp = keyStoreFilePath.getAbsolutePath();
        System.setProperty("Security.KeyStore.Location", ksp);
        System.setProperty("Security.KeyStore.Password", params.keyStorePassword);
    }

}
4
George Wang

Voici ma version étendue de la réponse d'Oleksandr Shpota , y compris les importations. Le package org.Apache.http.* se trouve dans org.Apache.httpcomponents:httpclient . J'ai commenté les changements:

import org.Apache.http.client.HttpClient;
import org.Apache.http.conn.ssl.NoopHostnameVerifier;
import org.Apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.Apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.Apache.http.impl.client.HttpClients;
import org.Apache.http.ssl.SSLContexts;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;

@Value("${http.client.ssl.key-store}")
private Resource keyStore;

@Value("${http.client.ssl.trust-store}")
private Resource trustStore;

// I use the same pw for both keystores:
@Value("${http.client.ssl.trust-store-password}")
private String keyStorePassword;

// wasn't able to provide this as a @Bean...:
private RestTemplate getRestTemplate() {
  try {
    SSLContext sslContext = SSLContexts.custom()
        // keystore wasn't within the question's scope, yet it might be handy:
        .loadKeyMaterial(
            keyStore.getFile(),
            keyStorePassword.toCharArray(),
            keyStorePassword.toCharArray())
        .loadTrustMaterial(
            trustStore.getURL(),
            keyStorePassword.toCharArray(),
            // use this for self-signed certificates only:
            new TrustSelfSignedStrategy())
        .build();

    HttpClient httpClient = HttpClients.custom()
        // use NoopHostnameVerifier with caution, see https://stackoverflow.com/a/22901289/3890673
        .setSSLSocketFactory(new SSLConnectionSocketFactory(sslContext, new NoopHostnameVerifier()))
        .build();

    return new RestTemplate(new HttpComponentsClientHttpRequestFactory(httpClient));
  } catch (IOException | GeneralSecurityException e) {
    throw new RuntimeException(e);
  }
}
2
crusy

Si vous exécutez votre application Spring Boot en tant que service Linux (par exemple, un script init.d ou similaire), vous disposez également de l'option suivante: fichier. Son contenu devrait être quelque chose de similaire:

Java_OPTS="
-Djavax.net.ssl.trustStore=path-to-your-trustStore-file
-Djavax.net.ssl.trustStorePassword=yourCrazyPassword
"
0
SaWo