Spring 引导 restTemplate POST 请求 bad_certificate,但在 SOAPUI 和 Postman 中有效
Spring Boot restTemplate POST request bad_certificate, but works in SOAPUI and Postman
我正在尝试向需要证书的服务发出 https post 请求。我收到了在 SOAPUI 和 Postman 中工作的相同请求。我还能够使用 restTemplate 获得其他不需要证书的 POST 请求以在 Spring 引导中工作。但是,当我尝试使用 restTemplate 发出证书 POST 请求时,我收到以下异常:
http-nio-8080-exec-5, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
2017-08-04 10:13:23.953 ERROR 54082 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "htpps://...": Received fatal alert: bad_certificate; nested exception is javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate] with root cause
javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
下面是 SOAPUI 发出的有效请求。:
POST https://... HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Content-Length: 0
Host: ...
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
这是相关的Spring引导代码,它不起作用
// request info
logger.info("enter");
String endpoint = "https://...";
String certFileName = "src/main/resources/keystore.jks";
String pass = "tempPass";
String body = "{}";
// set up cert
logger.info("begin load cert");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new File(certFileName)), pass.toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).loadKeyMaterial(keyStore, pass.toCharArray()).build());
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
// set up request
logger.info("setup request");
RestTemplate restTemplate = new RestTemplate(requestFactory);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(body);
// make request
logger.info("make request");
ResponseEntity<Object> res = restTemplate.postForEntity(endpoint, entity, Object.class);
// ResponseEntity<Object> res = restTemplate.exchange(endpoint, HttpMethod.POST, entity, Object.class);
// handle output
logger.info("received output");
if (res.getStatusCode().is2xxSuccessful())
return res.getBody().toString();
else
return res.getStatusCode().toString();
更多详情:
我不确定这是否重要,但我也尝试了 -Dtrust_all_cert=true
标志,但结果相同。我也试过 -Djavax.net.debug=all
标志,但所有输出似乎都成功了。如果有帮助,我可以包括调试,但它们很长。我没有其他特定于此证书的配置设置-POST 请求。
我正在使用 Java 1.8.
我应该补充一下,我代码的证书部分,我从
上的一个答案中复制而来
万一其他人遇到这种情况,
最终的原因是我使用的密钥库包含多个证书。 SoapUI 和 Postman 对此没有问题,但 restTemplate(我相信是 apache)只会尝试使用密钥库中的第一个证书。使用 java 密钥库工具从密钥库中删除无关的证书解决了这个问题。
我正在尝试向需要证书的服务发出 https post 请求。我收到了在 SOAPUI 和 Postman 中工作的相同请求。我还能够使用 restTemplate 获得其他不需要证书的 POST 请求以在 Spring 引导中工作。但是,当我尝试使用 restTemplate 发出证书 POST 请求时,我收到以下异常:
http-nio-8080-exec-5, handling exception: javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
2017-08-04 10:13:23.953 ERROR 54082 --- [nio-8080-exec-5] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.ResourceAccessException: I/O error on POST request for "htpps://...": Received fatal alert: bad_certificate; nested exception is javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate] with root cause
javax.net.ssl.SSLHandshakeException: Received fatal alert: bad_certificate
下面是 SOAPUI 发出的有效请求。:
POST https://... HTTP/1.1
Accept-Encoding: gzip,deflate
Content-Type: application/json
Content-Length: 0
Host: ...
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)
这是相关的Spring引导代码,它不起作用
// request info
logger.info("enter");
String endpoint = "https://...";
String certFileName = "src/main/resources/keystore.jks";
String pass = "tempPass";
String body = "{}";
// set up cert
logger.info("begin load cert");
KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
keyStore.load(new FileInputStream(new File(certFileName)), pass.toCharArray());
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(new SSLContextBuilder().loadTrustMaterial(null, new TrustSelfSignedStrategy()).loadKeyMaterial(keyStore, pass.toCharArray()).build());
HttpClient httpClient = HttpClients.custom().setSSLSocketFactory(socketFactory).build();
ClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(httpClient);
// set up request
logger.info("setup request");
RestTemplate restTemplate = new RestTemplate(requestFactory);
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(body);
// make request
logger.info("make request");
ResponseEntity<Object> res = restTemplate.postForEntity(endpoint, entity, Object.class);
// ResponseEntity<Object> res = restTemplate.exchange(endpoint, HttpMethod.POST, entity, Object.class);
// handle output
logger.info("received output");
if (res.getStatusCode().is2xxSuccessful())
return res.getBody().toString();
else
return res.getStatusCode().toString();
更多详情:
我不确定这是否重要,但我也尝试了 -Dtrust_all_cert=true
标志,但结果相同。我也试过 -Djavax.net.debug=all
标志,但所有输出似乎都成功了。如果有帮助,我可以包括调试,但它们很长。我没有其他特定于此证书的配置设置-POST 请求。
我正在使用 Java 1.8.
我应该补充一下,我代码的证书部分,我从
万一其他人遇到这种情况,
最终的原因是我使用的密钥库包含多个证书。 SoapUI 和 Postman 对此没有问题,但 restTemplate(我相信是 apache)只会尝试使用密钥库中的第一个证书。使用 java 密钥库工具从密钥库中删除无关的证书解决了这个问题。