在春季由resttemplate发出的每个请求发送客户端证书的正确方法是啥?

Posted

技术标签:

【中文标题】在春季由resttemplate发出的每个请求发送客户端证书的正确方法是啥?【英文标题】:What is the right way to send a client certificate with every request made by the resttemplate in spring?在春季由resttemplate发出的每个请求发送客户端证书的正确方法是什么? 【发布时间】:2018-01-24 14:22:12 【问题描述】:

我想在我的 Spring 应用程序中使用 REST 服务。要访问该服务,我有一个用于授权的客户端证书(自签名和 .jks 格式)。 针对其余服务进行身份验证的正确方法是什么?

这是我的要求:

public List<Info> getInfo() throws RestClientException, URISyntaxException 

    HttpEntity<?> httpEntity = new HttpEntity<>(null, new HttpHeaders());

    ResponseEntity<Info[]> resp = restOperations.exchange(
            new URI(BASE_URL + "/Info"), HttpMethod.GET, 
            httpEntity, Info[].class);
    return Arrays.asList(resp.getBody());

【问题讨论】:

【参考方案1】:

或者您可以将证书导入到您的 JDK cacerts,所有使用 jdk(在您的情况下为 REST 模板)的 HTTP 客户端都将使用该证书进行 REST 调用。

keytool -import -keystore $JAVA_HOME/jre/lib/security/cacerts -file foo.cer -alias alias

P.S: 导入成功后别忘了重启服务器。密钥库的默认密码 - changeit

【讨论】:

如果我有几个不同的证书要用于不同的调用,这会导致问题吗? 当 cacerts 存储中有多个证书时,Java 如何知道要使用哪个客户端证书? 客户端证书未在 cacerts 文件中指定。这适用于受信任的证书,例如被调用的服务器证书的签名证书。这个答案是错误的。【参考方案2】:

这里是如何使用 RestTemplate 和 Apache HttpClient 的示例

您应该使用已配置的 SSL 上下文定义自己的 RestTemplate

@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception 
    char[] password = "password".toCharArray();

    SSLContext sslContext = SSLContextBuilder.create()
            .loadKeyMaterial(keyStore("classpath:cert.jks", password), password)
            .loadTrustMaterial(null, new TrustSelfSignedStrategy()).build();

    HttpClient client = HttpClients.custom().setSSLContext(sslContext).build();
    return builder
            .requestFactory(new HttpComponentsClientHttpRequestFactory(client))
            .build();


 private KeyStore keyStore(String file, char[] password) throws Exception 
    KeyStore keyStore = KeyStore.getInstance("PKCS12");
    File key = ResourceUtils.getFile(file);
    try (InputStream in = new FileInputStream(key)) 
        keyStore.load(in, password);
    
    return keyStore;

现在此模板执行的所有远程调用都将使用cert.jks 进行签名。 注意:您需要将cert.jks 放入您的类路径

@Autowired
private RestTemplate restTemplate;

public List<Info> getInfo() throws RestClientException, URISyntaxException 
    HttpEntity<?> httpEntity = new HttpEntity<>(null, new HttpHeaders());

    ResponseEntity<Info[]> resp = restTemplate.exchange(
            new URI(BASE_URL + "/Info"), HttpMethod.GET, 
            httpEntity, Info[].class);
    return Arrays.asList(resp.getBody());

【讨论】:

请注意,您可以直接在RestTemplate 对象上调用setRequestFactory,而不必使用RestTemplateBuilder 我没有.jks 文件。我已经从 GoDaddy 下载了客户端证书,并且有两个带有 .crt.pem 的文件。我用这些文件尝试了这段代码,但它抛出异常java.io.IOException: Invalid keystore format 请指导。谢谢 将 .pem 转换为 .jks ***.com/questions/22296312/… 以上代码对我不起作用。我从sslshopper.com/ssl-converter.html 的一堆 pem 文件中生成了 pfx/pkcs12,并将 pkcs12 转换为 jks。

以上是关于在春季由resttemplate发出的每个请求发送客户端证书的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

春季请求和会话范围有啥区别?

客户端可以在没有证书的情况下使用 RestTemplate 向安全 SSL 服务器发出请求吗?

如何使用@WebMvcTest 春季测试在模拟服务中注入模拟的restTemplate

resttemplate发送请求无服务报错

使用 WebClient 将带有标头的请求发送到第三方 api

传递 POST 请求。春季安全。休息模板。杰克逊转换器