如何解决SpringBoot双向SSL认证报Received fatal alert: bad_certificate错误
SpringBoot服务双向SSL认证握手失败问题解决方案
本地搭建两个SpringBoot服务配置双向SSL认证时,完成常规配置后接口调用抛出SSL握手异常,以下是完整问题场景及解决方法:
常规配置步骤
- 分别为服务端、客户端生成JKS格式密钥库,使用
keytool命令操作 - 导出双方X.509格式证书,分别导入对方的信任库中实现证书互信,对应操作均使用
keytool命令完成 - 分别配置服务端、客户端的
application.properties参数:开启SSL、配置密钥库与信任库的路径、密码、类型,服务端额外设置server.ssl.client-auth=need强制校验客户端证书 - 客户端与服务端的启动类均通过
@Value注解读取SSL配置,写入javax.net.ssl相关系统属性 - 客户端通过默认初始化的
RestTemplate调用服务端HTTPS接口时触发异常:- 客户端报错:
javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate - 服务端报错:
javax.net.ssl.SSLHandshakeException: Empty client certificate chain
- 客户端报错:
解决方案
默认初始化的RestTemplate不会加载自定义的密钥库、信任库配置,导致请求时未携带客户端证书,触发校验失败。需要自定义配置绑定了对应SSL上下文的RestTemplate,参考配置代码如下:
import org.apache.http.conn.ssl.SSLConnectionSocketFactory; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.ssl.SSLContextBuilder; import org.springframework.beans.factory.annotation.Value; 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; import java.io.FileInputStream; import java.security.KeyStore; @Configuration public class SslRestTemplateConfig { @Value("${server.ssl.key-store}") private String keyStorePath; @Value("${server.ssl.key-store-password}") private String keyStorePass; @Value("${server.ssl.trust-store}") private String trustStorePath; @Value("${server.ssl.trust-store-password}") private String trustStorePass; @Bean public RestTemplate sslRestTemplate() throws Exception { // 加载客户端密钥库 KeyStore keyStore = KeyStore.getInstance("JKS"); try (FileInputStream ksIs = new FileInputStream(keyStorePath)) { keyStore.load(ksIs, keyStorePass.toCharArray()); } // 加载客户端信任库 KeyStore trustStore = KeyStore.getInstance("JKS"); try (FileInputStream tsIs = new FileInputStream(trustStorePath)) { trustStore.load(tsIs, trustStorePass.toCharArray()); } // 构建适配双向认证的SSL上下文 SSLContext sslContext = SSLContextBuilder.create() .loadKeyMaterial(keyStore, keyStorePass.toCharArray()) .loadTrustMaterial(trustStore, null) .build(); SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext); CloseableHttpClient httpClient = HttpClients.custom() .setSSLSocketFactory(socketFactory) .build(); HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient); return new RestTemplate(requestFactory); } }
后续业务代码中直接注入该sslRestTemplate实例发起HTTPS请求,即可正常完成双向SSL校验。
内容的提问来源于stack exchange,提问作者Yesora Choi
相关产品推荐
相关产品推荐

