在 spring boot application.properties 中指定信任存储信息

Posted

技术标签:

【中文标题】在 spring boot application.properties 中指定信任存储信息【英文标题】:Specifying trust store information in spring boot application.properties 【发布时间】:2015-02-27 17:53:02 【问题描述】:

我正在使用 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 做信托商店吗?

【问题讨论】:

您使用的是哪个嵌入式容器,您是如何确定它不起作用的? 【参考方案1】:

我也有同样的问题,我会尽量详细解释一下。

我正在使用 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

希望它有助于更​​好地理解这个问题!

【讨论】:

有人知道为什么application.yml 方式不起作用吗? 见Setting up a truststore without a keystore !!!请阅读!!!非常重要的精度:您必须在 jar 之前传递信任库,否则,Spring 上下文将接管并且您将继续收到相同的错误。 @avi.elkharrat 你能对此做更多解释吗? 查看命令行:$ java -Djavax.net.ssl.trustStore=path-to-truststore -jar blabla.jar,信任存储在jar之前。如果您在信任存储之前传递 jar,它将无法正常工作。我想,Spring 有一种机制来覆盖和锁定上下文。【参考方案2】:

我在使用 Spring Boot 和嵌入式 Tomcat 时也遇到了同样的问题。

据我了解,这些属性仅设置 Tomcat 配置参数。根据 Tomcat 文档,这仅用于客户端身份验证(即用于双向 SSL),而不用于验证远程证书:

truststoreFile - 用于验证客户端证书的信任存储文件。

https://tomcat.apache.org/tomcat-8.0-doc/config/http.html

要为 HttpClient 配置信任存储,很大程度上取决于您使用的 HttpClient 实现。例如,对于 RestTemplate,Spring Boot 默认使用基于标准 J2SE 类(如 java.net.HttpURLConnection)的 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/

基本上,这允许 RestTemplate bean 仅信任已配置信任库中由根 CA 签名的证书。

@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 来做任何您想要的事情。

【讨论】:

我对此表示赞同,因为我整理了一小段代码来演示和证明server.ssl.trust-store 用于双向 TLS(如果设置了server.ssl.client-auth,则有任何效果)而不是用于传出连接。答案可以被接受为 *** 答案。我还想在这里指出,文档已经足够了:docs.spring.io/spring-boot/docs/current/reference/html/…【参考方案3】:

在微服务基础设施中(我知道不适合这个问题;))你不能使用:

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

可以配置功能区负载均衡器:

ribbon: 
  TrustStore: keystore.jks
  TrustStorePassword : example
  ReadTimeout: 60000
  IsSecure: true
  MaxAutoRetries: 1

在这里https://github.com/rajaramkushwaha/https-zuul-proxy-spring-boot-app 你可以找到一个工作样本。 github上也有讨论过,但是没找到。

【讨论】:

投反对票。即使在微服务基础架构中,也存在绝对确实需要配置信任存储的情况。例如,如果您的服务访问了由您组织的内部 CA 签名的下游 HTTPS 端点。【参考方案4】:

如果您需要进行 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-storehttp.client.ssl.trust-store-password 指向JKS 格式的信任库和指定信任库的密码。

这将覆盖 Spring Boot 提供的 RestTemplate bean,并使其使用您需要的信任存储。

【讨论】:

“http.client.ssl.trust-store”是keystore的路径吗? 基本上它是信任库的路径。但是,您可能会将受信任的证书存储在您拥有自己的证书和私钥的同一文件中。如果是这种情况,您可以在那里使用密钥库。你可以在这里找到更多细节***.com/a/6341566/2065796 http.client.ssl.trust-storehttp.client.ssl.trust-store-password 是自定义属性,不是吗? @OleksandrShpota 是Resource 类型的全名?我应该将哪个课程导入我的课程? @riccardo.cardin org.springframework.core.io.Resource.【参考方案5】:

这是我的 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://***.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);
  

【讨论】:

【参考方案6】:

我在使用 Spring Boot、Spring Cloud(微服务)和自签名 SSL 证书时遇到了同样的问题。 Keystore 从应用程序属性中开箱即用,而 Truststore 没有。

我最终在 application.properties 中保留了 keystore 和 trustore 配置,并添加了一个单独的配置 bean 来配置系统的 truststore 属性。

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

【讨论】:

【参考方案7】:

java 属性“javax.net.ssl.trustStore”和“javax.net.ssl.trustStorePassword”不对应于“server.ssl.trust-store”和“server.ssl.trust-store-password”春季启动“application.properties”(“application.yml”)

所以你不能简单地通过设置“server.ssl.trust-store”和“server.ssl.trust-store-password”来设置“javax.net.ssl.trustStore”和“javax.net.ssl.trustStorePassword” " 在 "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 月 3 日编辑

您可能还希望采用注释“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 的更新

从spring boot 2.x.x开始,不再支持静态属性,请see here。我个人认为这不是一个好的举措,因为必须沿着参考链进行复杂的更改......

无论如何,实现摘录可能如下所示

“参数”类

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

【讨论】:

【参考方案8】:

如果您将 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
"

【讨论】:

【参考方案9】:

如果您在 Spring 中,请尝试为其添加属性(使用所需的属性),它应该适用于整个 JVM

javax:
  net:
    ssl:
      key-store-password: $KEYSTORE_SECRET
      key-store-type: PKCS12
      trust-store-password: $TRUSTSTORE_SECRET
      trust-store-type: PKCS12

【讨论】:

【参考方案10】:

虽然我评论晚了。但我已经使用这种方法来完成这项工作。在这里,当我运行我的 spring 应用程序时,我通过 -Dspring.config.location=file:/location-to-file/config-server-vault-application.yml 提供应用程序 yaml 文件,其中包含我的所有属性

config-server-vault-application.yml
***********************************
server:
  port: 8888
  ssl:
    trust-store: /trust-store/config-server-trust-store.jks
    trust-store-password: config-server
    trust-store-type: pkcs12

************************************
Java Code
************************************
@SpringBootApplication
public class ConfigServerApplication 

 public static void main(String[] args) throws IOException 
    setUpTrustStoreForApplication();
    SpringApplication.run(ConfigServerApplication.class, args);
 

 private static void setUpTrustStoreForApplication() throws IOException 
    YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
    List<PropertySource<?>> applicationYamlPropertySource = loader.load(
            "config-application-properties", new UrlResource(System.getProperty("spring.config.location")));
    Map<String, Object> source = ((MapPropertySource) applicationYamlPropertySource.get(0)).getSource();
    System.setProperty("javax.net.ssl.trustStore", source.get("server.ssl.trust-store").toString());
    System.setProperty("javax.net.ssl.trustStorePassword", source.get("server.ssl.trust-store-password").toString());
  

【讨论】:

我有一个单独的 SSLConfig 文件,带有 @Configuration 注释。它只是没有用。这是使我的代码正常工作的唯一答案。点赞!!【参考方案11】:

当一切听起来如此复杂时,这个命令对我有用:

keytool -genkey -alias foo -keystore cacerts -dname cn=test -storepass changeit -keypass changeit

当开发人员遇到麻烦时,我相信一个简单的工作解决方案 sn-p 对他来说已经足够了。稍后他可以诊断出问题的根本原因和基本了解。

【讨论】:

以上是关于在 spring boot application.properties 中指定信任存储信息的主要内容,如果未能解决你的问题,请参考以下文章

如何 JUnit 测试 Spring-Boot 的 Application.java

Spring boot 学习 四:spring boot 配置文件 application.yml

如何在 spring-boot 配置中加载 application.yaml 配置以进行硒测试

Spring Boot中application.properties和application.yml文件

如何在spring-boot中添加多个application.properties文件?

spring boot application.properties/application.yml 配置属性大全