web-dev-qa-db-ja.com

スプリングブートapplication.propertiesでトラストストア情報を指定する

springBootVersion 1.2.0.RELEASEを使用しています。 application.propertiesを使用してキーストアとトラストストアを設定しようとしています。

次の設定を追加すると、キーストアは機能しますが、トラストストアは機能しません。

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

ただし、gradleを介してトラストストアを追加する場合:

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

それはうまく機能します。

誰かがapplication.propertiesをトラストストアに使用しましたか?

47
user4408912

REST呼び出しを行う必要がある場合は、次の方法を使用できます。

これはRestTemplateを介した発信呼び出しで機能します

このようにRestTemplate Beanを宣言します。

@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およびhttp.client.ssl.trust-store-passwordは、JKS形式のトラストストアと、指定されたトラストストアのパスワードを指します。

これにより、Spring Bootで提​​供されるRestTemplate Beanがオーバーライドされ、必要なトラストストアが使用されるようになります。

28
Sasha Shpota

私は同じ問題を抱えています。もう少し詳しく説明しようと思います。

私はspring-boot 1.2.2-RELEASEを使用していますが、TomcatとUndertowの両方で同じ結果が得られました。

Application.ymlでトラストストアを次のように定義します:

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

動作しませんが、:

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

完璧に動作します。

Rutimeで違いを確認する最も簡単な方法は、クライアントでssl-debugを有効にすることです。作業中(つまり-Dフラグを使用)、次のようなものがコンソールに書き込まれます(最初のリクエストの処理中):

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

-Dフラグを使用しない場合:

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)

...リクエストを実行すると、例外が発生します:

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

問題をよりよく理解するのに役立つことを願っています!

13
Magnus Larsson

Spring Boot、Spring Cloud(マイクロサービス)、および自己署名SSL証明書でも同じ問題が発生しました。キーストアはアプリケーションのプロパティからそのまま使用できましたが、トラストストアは使用できませんでした。

最終的に、application.propertiesにキーストアとトラストアーの両方の構成を保持し、システムでトラストストアのプロパティーを構成するための別個の構成Beanを追加しました。

@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"));
    }
}
12
Mate Šimović

Spring Bootと組み込みTomcatでも同じ問題が発生していました。

私が理解していることから、これらのプロパティはTomcat構成パラメータのみを設定します。 Tomcatのドキュメントによると、これはクライアント認証(つまり、双方向SSL)にのみ使用され、リモート証明書の検証には使用されません。

truststoreFile-検証に使用するトラストストアファイル クライアント 証明書。

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

HttpClientのトラストストアを設定するには、使用するHttpClient実装に大きく依存します。たとえば、デフォルトでRestTemplateの場合、Spring BootはJava.net.HttpURLConnectionなどの標準J2SEクラスに基づいたSimpleClientHttpRequestFactoryを使用します。

Apache HttpClientのドキュメントとこれらの投稿に基づいたソリューションを考え出しました: http://vincentdevillers.blogspot.pt/2013/02/configure-best-spring-resttemplate.html - http://literatejava.com/networks/ignore-ssl-certificate-errors-Apache-httpclient-4-4/

基本的に、これにより、構成されたトラストストア内のルートCAによって署名された証明書のみを信頼するRestTemplate Beanが許可されます。

@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;
    }    
}

そして、必要なときにいつでもこのカスタムRestクライアントを使用できます。

@Autowired
private RestTemplate restTemplate;

restTemplate.getForEntity(...)

これは、Restエンドポイントに接続しようとしていることを前提としていますが、必要に応じて上記のHttpClient Beanも使用できます。

7
jpt

Javaプロパティ「javax.net.ssl.trustStore」と「javax.net.ssl.trustStorePassword」は、Springブートの「server.ssl.trust-store」と「server.ssl.trust-store-password」に対応していませんapplication.properties "(" application.yml ")

そのため、「javax.net.ssl.trustStore」と「javax.net.ssl.trustStorePassword」を「server.ssl.trust-store」と「server.ssl.trust-store-password」に設定するだけでは設定できませんapplication.properties "(" application.yml ")

「javax.net.ssl.trustStore」と「javax.net.ssl.trustStorePassword」を設定する代替方法は、Spring boot Externalized Configuration

以下は私の実装の抜粋です:

Paramsクラスは外部設定を保持します

@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;
    }

KeyStoreUtilクラスは、「javax.net.ssl.trustStore」および「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);

    }     
}

スタートアップ関数内でセッターを実行します

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

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

2018-10-03に編集

また、セッターを実行する代わりに注釈「PostConstruct」を採用することもできます。

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);

    }
}

application.yml

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

最後に、実行中の環境(展開サーバー)内で、jarアーカイブが保存されているフォルダーと同じフォルダーの下に「config」という名前のフォルダーを作成します。

「config」フォルダー内に、「application.yml」、「client-truststore.jks」、および「wso2carbon.jks」を保存します。できた!

2018-11-27にSpring boot 2.x.xについての更新

スプリングブート2.x.x以降、静的プロパティはサポートされなくなりました。 こちらをご覧ください をご覧ください。私は個人的にはそれが良い動きだとは思わない、なぜなら複雑な変更が参照チェーンに沿ってなされなければならないからだ...

とにかく、強制排除の抜粋は次のようになります

「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";  
}

「Springbootアプリケーションクラス」(「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);
    }

}
5
George Wang

Oleksandr Shpota's answer の拡張バージョン(インポートを含む)。パッケージorg.Apache.http.*org.Apache.httpcomponents:httpclient にあります。変更についてコメントしました。

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

Spring BootアプリケーションをLinuxサービス(たとえば、init.dスクリプトなど)として実行する場合、次のオプションもあります:yourApplication.confというファイルを作成し、実行可能なwar/jarファイルの隣に配置します。内容は次のようになります。

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