Skip to content

Commit b72c443

Browse files
committed
Cleanup and polishing for the solution with two separate SSLContexts.
1 parent 55ac525 commit b72c443

File tree

8 files changed

+112
-42
lines changed

8 files changed

+112
-42
lines changed

README.md

+104-6
Original file line numberDiff line numberDiff line change
@@ -152,19 +152,117 @@ In KeyStore Explorer this should look like this:
152152
![client-truststore](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/blob/master/client-truststore.png)
153153

154154

155-
#### 2. Java Keystore, that inherits Public and Private Keys (keypair): client-keystore.p12
155+
#### 2. Java Keystores, that inherit Public and Private Keys (keypair): copy alice-keystore.p12 & tom-keystore.p12
156156

157-
We need a way to import multiple private keys and certificates into the same `client-keystore.jks`, so that our implementation could call multiple secured endpoints. This seems to be a harder task then one could think beforehand. But luckily there´s a simple way: Just copy both `alice-keystore.p12` and `tom-keystore.p12` into __client-bob/src/main/resources__ and use keytool as follows:
157+
As Apache HttpClient isn´t able to handle [more than one client certificate for the same SSLContext](http://mail-archives.apache.org/mod_mbox/hc-httpclient-users/201109.mbox/%3C1315998630.3176.17.camel@ubuntu%3E), we need to provide two of them. Therefore we don´t need to add two private keys and certificates to one Keystore - we can just use both Keystores we already assembled before. So we copy `alice-keystore.p12` & `tom-keystore.p12` to clien-bob/src/main/resources and use them in the [RestClientCertConfiguration](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/blob/master/client-bob/src/main/java/de/jonashackt/configuration/RestClientCertConfiguration.java) like this:
158158

159159
```
160-
keytool -importkeystore -srckeystore alice-keystore.p12 -srcstoretype pkcs12 -destkeystore client-keystore.jks -deststoretype JKS
161-
keytool -importkeystore -srckeystore tom-keystore.p12 -srcstoretype pkcs12 -destkeystore client-keystore.jks -deststoretype JKS
160+
import org.apache.commons.io.FileUtils;
161+
import org.apache.http.client.HttpClient;
162+
import org.apache.http.impl.client.HttpClients;
163+
import org.apache.http.ssl.SSLContextBuilder;
164+
import org.springframework.beans.factory.annotation.Value;
165+
import org.springframework.context.annotation.Bean;
166+
import org.springframework.context.annotation.Configuration;
167+
import org.springframework.core.io.Resource;
168+
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
169+
170+
import javax.net.ssl.SSLContext;
171+
import java.io.File;
172+
import java.io.IOException;
173+
174+
@Configuration
175+
public class RestClientCertConfiguration {
176+
177+
private char[] bobPassword = "bobpassword".toCharArray();
178+
private char[] tomPassword = "tompassword".toCharArray();
179+
180+
@Value("classpath:alice-keystore.p12")
181+
private Resource aliceKeystoreResource;
182+
183+
@Value("classpath:tom-keystore.p12")
184+
private Resource tomKeystoreResource;
185+
186+
@Value("classpath:client-truststore.jks")
187+
private Resource truststoreResource;
188+
private char[] alicePassword = "alicepassword".toCharArray();
189+
190+
@Bean
191+
public HttpComponentsClientHttpRequestFactory serverTomClientHttpRequestFactory() throws Exception {
192+
SSLContext sslContext = SSLContextBuilder
193+
.create()
194+
.loadKeyMaterial(inStream2File(tomKeystoreResource), tomPassword, tomPassword)
195+
.loadTrustMaterial(inStream2File(truststoreResource), bobPassword)
196+
.build();
197+
198+
HttpClient client = HttpClients.custom()
199+
.setSSLContext(sslContext)
200+
.build();
201+
202+
return new HttpComponentsClientHttpRequestFactory(client);
203+
}
204+
205+
@Bean
206+
public HttpComponentsClientHttpRequestFactory serverAliceClientHttpRequestFactory() throws Exception {
207+
SSLContext sslContext = SSLContextBuilder
208+
.create()
209+
.loadKeyMaterial(inStream2File(aliceKeystoreResource), alicePassword, alicePassword)
210+
.loadTrustMaterial(inStream2File(truststoreResource), bobPassword)
211+
.build();
212+
213+
HttpClient client = HttpClients.custom()
214+
.setSSLContext(sslContext)
215+
.build();
216+
217+
return new HttpComponentsClientHttpRequestFactory(client);
218+
}
219+
220+
private File inStream2File(Resource resource) {
221+
try {
222+
File tempFile = File.createTempFile("file", ".tmp");
223+
FileUtils.copyInputStreamToFile(resource.getInputStream(), tempFile);
224+
return tempFile;
225+
} catch (IOException e) {
226+
throw new RuntimeException("Problems loading Keystores", e);
227+
}
228+
}
229+
}
162230
```
163231

164-
The result should look like this:
232+
Now we´re able to insert individual SSLContexts into Spring´s RestTemplate. Therefore see [ServerClientImpl](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/blob/master/client-bob/src/main/java/de/jonashackt/client/ServerClientImpl.java):
165233

166-
![client-keystore](https://github.com/jonashackt/spring-boot-rest-clientcertificates-docker-compose/blob/master/client-keystore.png)
234+
```
235+
import org.springframework.beans.factory.annotation.Autowired;
236+
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
237+
import org.springframework.stereotype.Component;
238+
import org.springframework.web.client.RestTemplate;
239+
240+
@Component
241+
public class ServerClientImpl implements ServerClient {
242+
243+
@Autowired
244+
private HttpComponentsClientHttpRequestFactory serverAliceClientHttpRequestFactory;
245+
246+
@Autowired
247+
private HttpComponentsClientHttpRequestFactory serverTomClientHttpRequestFactory;
248+
249+
private RestTemplate restTemplate = new RestTemplate();
167250
251+
@Override
252+
public String callServerAlice() {
253+
restTemplate.setRequestFactory(serverAliceClientHttpRequestFactory);
254+
255+
return restTemplate.getForObject("https://server-alice:8443/hello", String.class);
256+
}
257+
258+
@Override
259+
public String callServerTom() {
260+
restTemplate.setRequestFactory(serverTomClientHttpRequestFactory);
261+
262+
return restTemplate.getForObject("https://server-tom:8443/hello", String.class);
263+
}
264+
}
265+
```
168266

169267

170268

client-bob/src/main/java/de/jonashackt/client/ServerClientImpl.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,14 @@
88
@Component
99
public class ServerClientImpl implements ServerClient {
1010

11-
//@Autowired
12-
private RestTemplate restTemplate = new RestTemplate();
13-
1411
@Autowired
1512
private HttpComponentsClientHttpRequestFactory serverAliceClientHttpRequestFactory;
1613

1714
@Autowired
1815
private HttpComponentsClientHttpRequestFactory serverTomClientHttpRequestFactory;
1916

17+
private RestTemplate restTemplate = new RestTemplate();
18+
2019
@Override
2120
public String callServerAlice() {
2221
restTemplate.setRequestFactory(serverAliceClientHttpRequestFactory);

client-bob/src/main/java/de/jonashackt/configuration/RestClientCertConfiguration.java

+6-33
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,10 @@
55
import org.apache.http.impl.client.HttpClients;
66
import org.apache.http.ssl.SSLContextBuilder;
77
import org.springframework.beans.factory.annotation.Value;
8-
import org.springframework.boot.web.client.RestTemplateBuilder;
98
import org.springframework.context.annotation.Bean;
109
import org.springframework.context.annotation.Configuration;
1110
import org.springframework.core.io.Resource;
1211
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
13-
import org.springframework.web.client.RestTemplate;
1412

1513
import javax.net.ssl.SSLContext;
1614
import java.io.File;
@@ -20,49 +18,24 @@
2018
public class RestClientCertConfiguration {
2119

2220
private char[] bobPassword = "bobpassword".toCharArray();
21+
private char[] tomPassword = "tompassword".toCharArray();
2322

2423
@Value("classpath:alice-keystore.p12")
2524
private Resource aliceKeystoreResource;
2625

27-
@Value("classpath:alice-truststore.jks")
28-
private Resource aliceTruststoreResource;
29-
3026
@Value("classpath:tom-keystore.p12")
3127
private Resource tomKeystoreResource;
3228

33-
@Value("classpath:tom-truststore.jks")
34-
private Resource tomTruststoreResource;
35-
3629
@Value("classpath:client-truststore.jks")
3730
private Resource truststoreResource;
38-
39-
/* @Bean
40-
public RestTemplate restTemplate(RestTemplateBuilder builder) throws Exception {
41-
42-
43-
44-
SSLContext sslContext = SSLContextBuilder
45-
.create()
46-
.loadKeyMaterial(inStream2File(aliceKeystoreResource), "alicepassword".toCharArray(), "alicepassword".toCharArray())
47-
.loadKeyMaterial(inStream2File(tomKeystoreResource), "tompassword".toCharArray(), "tompassword".toCharArray())
48-
.loadTrustMaterial(inStream2File(truststoreResource), bobPassword)
49-
.build();
50-
51-
HttpClient client = HttpClients.custom()
52-
.setSSLContext(sslContext)
53-
.build();
54-
55-
return builder
56-
.requestFactory(new HttpComponentsClientHttpRequestFactory(client))
57-
.build();
58-
}*/
31+
private char[] alicePassword = "alicepassword".toCharArray();
5932

6033
@Bean
6134
public HttpComponentsClientHttpRequestFactory serverTomClientHttpRequestFactory() throws Exception {
6235
SSLContext sslContext = SSLContextBuilder
6336
.create()
64-
.loadKeyMaterial(inStream2File(tomKeystoreResource), "tompassword".toCharArray(), "tompassword".toCharArray())
65-
.loadTrustMaterial(inStream2File(tomTruststoreResource), "tompassword".toCharArray())
37+
.loadKeyMaterial(inStream2File(tomKeystoreResource), tomPassword, tomPassword)
38+
.loadTrustMaterial(inStream2File(truststoreResource), bobPassword)
6639
.build();
6740

6841
HttpClient client = HttpClients.custom()
@@ -76,8 +49,8 @@ public HttpComponentsClientHttpRequestFactory serverTomClientHttpRequestFactory(
7649
public HttpComponentsClientHttpRequestFactory serverAliceClientHttpRequestFactory() throws Exception {
7750
SSLContext sslContext = SSLContextBuilder
7851
.create()
79-
.loadKeyMaterial(inStream2File(aliceKeystoreResource), "alicepassword".toCharArray(), "alicepassword".toCharArray())
80-
.loadTrustMaterial(inStream2File(aliceTruststoreResource), "alicepassword".toCharArray())
52+
.loadKeyMaterial(inStream2File(aliceKeystoreResource), alicePassword, alicePassword)
53+
.loadTrustMaterial(inStream2File(truststoreResource), bobPassword)
8154
.build();
8255

8356
HttpClient client = HttpClients.custom()
-749 Bytes
Binary file not shown.
Binary file not shown.
-737 Bytes
Binary file not shown.

client-keystore.png

-124 KB
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)