SpringでRestTemplateを使うときに接続先が自己署名証明書を使っている場合、SSLの検証が失敗してしまうので、ホスト名の検証を無効にし、自己署名証明書を使う設定を追加してみた。

2016/10/30 追記
JavaのKeyStoreに自己署名証明書を登録してしまう方法をオススメします。
Javaで自己署名証明書をkeystoreに登録する

環境

  • Java 8
  • Spring Boot 1.4.1
  • dependency
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    </dependency>

RestTemplateのコンストラクタにClientHttpRequestFactoryを渡せるのでSSLContextsとかSSL関連の設定をするためにhttpclientをdependencyに追加する。

実行

自己署名証明書を考慮せずに単純にRestTemplateを使うとSSL関連のExceptionが発生してアプリケーションの起動に失敗する。

[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.4.1.RELEASE:run (default-cli)
on project spring-boot-rest-template-sample: An exception occurred while running. null: InvocationTargetException:
Error creating bean with name 'springBootRestTemplateApplication': Invocation of init method failed; nested
exception is org.springframework.web.client.ResourceAccessException: I/O error on GET request for
"https://localhost/api/users": sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to
requested target; nested exception is 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 -> [Help 1]

自己署名証明書に対応する

TrustSelfSignedな証明書に対応するのと、証明書のホスト名が適切に設定されていない場合はホスト名の検証に引っかかるのでそれもついでに対応する。

RestTemplateConfigの設定は次の通り。
packageもわかりにくいので記載しておく。

config/RestTemplateConfig.java
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.SSLContexts;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;
import javax.net.ssl.SSLContext;

@Configuration
public class RestTemplateConfig {
@Bean
public RestTemplate restTemplate() throws Exception {
SSLContext sslContext = SSLContexts.custom()
.loadTrustMaterial(new TrustSelfSignedStrategy()).build();
CloseableHttpClient httpClient = HttpClients.custom()
.setSSLContext(sslContext)
.setSSLHostnameVerifier(NoopHostnameVerifier.INSTANCE)
.build();

HttpComponentsClientHttpRequestFactory requestFactory =
new HttpComponentsClientHttpRequestFactory();
RestTemplate restTemplate = new RestTemplate(requestFactory);
((HttpComponentsClientHttpRequestFactory) restTemplate
.getRequestFactory()).setHttpClient(httpClient);
return restTemplate;
}
}

簡単に説明しておくと、

  • SSLContextでTrustSelfSignedStrategyを設定して自己署名証明書でも通るようにして、
  • 更に、HostnameVerifierでホスト名の検証を行わないように設定し、
  • RestTemplateで使うhttpClientを自己署名証明書対応されたClientに差し替える

といった感じです。

RequestFactoryあたりはspringのドキュメントを参照してください。

自己署名証明書でも以下の様なコードが実行できるようになります。

@Repository
public class UserRestTemplate {
final String URI = "https://localhost:8080/api";

@Autowired
RestTemplate restTemplate;

public String getUsers() {
return restTemplate.getForObject(URI + "/users", String.class);
}
}

おわり。

参考

  1. http://docs.spring.io/spring/docs/current/spring-framework-reference/html/remoting.html
  2. http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html