Something that was supposed to be simple is proving to be a nightmare. I have two webapps - the first (webapp1) should post to the other (webapp2) with the use of RestTemplate and a X509 certificate in the keystore, and the second one (webapp2) filters the request and checks the validity of the X509 certificate in it's truststore.
I'm using ApacheHttp in webapp1 in a custom RestTemplate bean as follows:
@Bean
public RestTemplate restTemplate() {
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(this.httpClient());
RestTemplate restTemplate = new RestTemplate(factory);
restTemplate.getMessageConverters().add(0, new StringHttpMessageConverter(StandardCharsets.UTF_8));
restTemplate.getInterceptors().add(new RestTemplateHeaderModifierInterceptor());
restTemplate.setRequestFactory(factory);
return restTemplate;
}
public SSLConnectionSocketFactory sslConnectionSocketFactory() throws Exception {
return new SSLConnectionSocketFactory(sslContext(), NoopHostnameVerifier.INSTANCE);
}
@Bean
public HttpClient httpClient() throws Exception {
return HttpClientBuilder.create().setSSLSocketFactory(sslConnectionSocketFactory())
.addInterceptorFirst(new HttpComponentsMessageSender.RemoveSoapHeadersInterceptor()).build();
}
private SSLContext sslContext() throws Exception {
return SSLContextBuilder.create()
.loadKeyMaterial(resourceLoader.getResource(keyStorePath).getFile(), keyStorePassword.toCharArray(), keyStorePassword.toCharArray())
.loadTrustMaterial(resourceLoader.getResource(trustStorePath).getFile(), trustStorePassword.toCharArray())
.build();
}
In webapp2, I use a X509AuthenticationFilter that should fetch me a PreAuthenticatedAuthenticationToken which can then be accessed through:
SecurityContextHolder.getContext().getAuthentication()
Alas, webapp2 says the authentication object is null (when posted from Postman with the correct certificate, the request succeeds and the object is not null), therefore I cannot extract a X509 certificate from it for further validation. The request seems to arrive without the certificate that the RestTemplate should append through the ApacheHttp factory. When I debug the code before RestTemplate.exchange(...), I can see the factory has a HttpClient object that contains an SSLSocketFactory with a credentialsMap where the certificate from the specified keystore is appended.
As far as I'm aware, this should be working out of the box, but it seems like I'm missing a piece of the puzzle. I've exhausted a number of code rewrites, but in vain. I can't seem to pinpoint how certificate authentication with RestTemplate works. Can someone point me in the right direction?
Also worth mentioning, the truststore part in webapp1 works (an SSL handshake is received). Also also, both the keystore and truststore are loaded correctly and the correct certs in them are consumed, I can see as much.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…