Search code examples
javaspring-bootresttemplateclient-certificates

Spring Boot restTemplate POST request bad_certificate, but works in SOAPUI and Postman


I'm trying to make a https post request to a service that requires a certificate. I've gotten the same request to work in SOAPUI and Postman. I've also been able to get other, non-certificate-requiring POST requests to work in Spring Boot using restTemplate. However, when I try making certificate POST requests with restTemplate, I get receive the following exception:

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

Below is the raw request SOAPUI makes which works.:

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)

Here is the relevant Spring Boot code, which doesn't work

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

Further details: I'm not sure if this matters, but I've also tried the -Dtrust_all_cert=true flag, but same results. I've also tried the -Djavax.net.debug=all flag, but all the outputs seem successful. I can include the debugs if it would help, but they are long. I have no other configuration setup specific for this certificate-POST request.

I'm using Java 1.8.

I should add, the certificate part of my code, I copied from an answer on Spring Boot restTemplate POST request bad_certificate, but works in SOAPUI and Postman


Solution

  • In case anyone else encounters this situation,

    The cause ended up being that the keystore I was using included multiple certificates. SoapUI and Postman had no problems with this, but restTemplate (apache I believe) would only attempt using the first certificate in the keystore. Removing the extraneous certificates from the keystore with the java keystore tool resolved the issue.