View Javadoc
1   /*
2    * Copyright 2019-2021 the original author or authors.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *      https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package nl.altindag.ssl;
18  
19  import nl.altindag.log.LogCaptor;
20  import nl.altindag.ssl.exception.GenericKeyManagerException;
21  import nl.altindag.ssl.exception.GenericKeyStoreException;
22  import nl.altindag.ssl.exception.GenericSecurityException;
23  import nl.altindag.ssl.exception.GenericTrustManagerException;
24  import nl.altindag.ssl.keymanager.CompositeX509ExtendedKeyManager;
25  import nl.altindag.ssl.keymanager.HotSwappableX509ExtendedKeyManager;
26  import nl.altindag.ssl.trustmanager.HotSwappableX509ExtendedTrustManager;
27  import nl.altindag.ssl.trustmanager.UnsafeX509ExtendedTrustManager;
28  import nl.altindag.ssl.util.KeyManagerUtils;
29  import nl.altindag.ssl.util.KeyStoreUtils;
30  import nl.altindag.ssl.util.TrustManagerUtils;
31  import org.junit.jupiter.api.Test;
32  import org.junit.jupiter.api.extension.ExtendWith;
33  import org.mockito.junit.jupiter.MockitoExtension;
34  import org.slf4j.Logger;
35  import org.slf4j.LoggerFactory;
36  
37  import javax.net.ssl.HostnameVerifier;
38  import javax.net.ssl.KeyManager;
39  import javax.net.ssl.KeyManagerFactory;
40  import javax.net.ssl.SSLEngine;
41  import javax.net.ssl.SSLSession;
42  import javax.net.ssl.SSLSessionContext;
43  import javax.net.ssl.TrustManager;
44  import javax.net.ssl.TrustManagerFactory;
45  import javax.net.ssl.X509ExtendedKeyManager;
46  import javax.net.ssl.X509ExtendedTrustManager;
47  import javax.security.auth.x500.X500Principal;
48  import java.io.IOException;
49  import java.io.InputStream;
50  import java.net.URI;
51  import java.nio.file.Files;
52  import java.nio.file.Path;
53  import java.nio.file.Paths;
54  import java.security.KeyStore;
55  import java.security.KeyStoreException;
56  import java.security.NoSuchAlgorithmException;
57  import java.security.PrivateKey;
58  import java.security.SecureRandom;
59  import java.security.Security;
60  import java.security.UnrecoverableKeyException;
61  import java.security.cert.Certificate;
62  import java.security.cert.CertificateException;
63  import java.security.cert.X509Certificate;
64  import java.util.Arrays;
65  import java.util.Collections;
66  import java.util.Objects;
67  import java.util.regex.Matcher;
68  import java.util.regex.Pattern;
69  
70  import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
71  import static nl.altindag.ssl.TestConstants.EMPTY;
72  import static nl.altindag.ssl.TestConstants.IDENTITY_FILE_NAME;
73  import static nl.altindag.ssl.TestConstants.IDENTITY_PASSWORD;
74  import static nl.altindag.ssl.TestConstants.KEYSTORE_LOCATION;
75  import static nl.altindag.ssl.TestConstants.TEMPORALLY_KEYSTORE_LOCATION;
76  import static nl.altindag.ssl.TestConstants.TRUSTSTORE_FILE_NAME;
77  import static nl.altindag.ssl.TestConstants.TRUSTSTORE_PASSWORD;
78  import static org.assertj.core.api.Assertions.assertThat;
79  import static org.assertj.core.api.Assertions.assertThatThrownBy;
80  import static org.assertj.core.api.Assertions.fail;
81  import static org.mockito.Mockito.mock;
82  import static org.mockito.Mockito.spy;
83  import static org.mockito.Mockito.when;
84  
85  /**
86   * @author Hakan Altindag
87   */
88  @ExtendWith(MockitoExtension.class)
89  class SSLFactoryShould {
90  
91      private static final Logger LOGGER = LoggerFactory.getLogger(SSLFactoryShould.class);
92  
93      private static final String GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE = "Identity details are empty, which are required to be present when SSL/TLS is enabled";
94      private static final String GENERIC_TRUSTSTORE_VALIDATION_EXCEPTION_MESSAGE = "TrustStore details are empty, which are required to be present when SSL/TLS is enabled";
95  
96      @Test
97      void buildSSLFactoryWithTrustMaterial() {
98          SSLFactory sslFactory = SSLFactory.builder()
99                  .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
100                 .build();
101 
102         assertThat(sslFactory.getSslContext()).isNotNull();
103 
104         assertThat(sslFactory.getTrustManager()).isPresent();
105         assertThat(sslFactory.getTrustManager().get()).isNotInstanceOf(HotSwappableX509ExtendedTrustManager.class);
106         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
107         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
108         assertThat(sslFactory.getTrustStores()).isNotEmpty();
109         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
110         assertThat(sslFactory.getKeyManager()).isNotPresent();
111         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
112     }
113 
114     @Test
115     void buildSSLFactoryWithSwappableTrustMaterial() {
116         SSLFactory sslFactory = SSLFactory.builder()
117                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
118                 .withSwappableTrustMaterial()
119                 .build();
120 
121         assertThat(sslFactory.getSslContext()).isNotNull();
122 
123         assertThat(sslFactory.getTrustManager()).isPresent();
124         assertThat(sslFactory.getTrustManager().get()).isInstanceOf(HotSwappableX509ExtendedTrustManager.class);
125         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
126         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
127         assertThat(sslFactory.getTrustStores()).isNotEmpty();
128         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
129         assertThat(sslFactory.getKeyManager()).isNotPresent();
130         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
131     }
132 
133     @Test
134     void buildSSLFactoryWithTrustMaterialWithoutPassword() {
135         SSLFactory sslFactory = SSLFactory.builder()
136                 .withTrustMaterial(KEYSTORE_LOCATION + "truststore-without-password.jks", null)
137                 .build();
138 
139         assertThat(sslFactory.getSslContext()).isNotNull();
140 
141         assertThat(sslFactory.getTrustManager()).isPresent();
142         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
143         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
144         assertThat(sslFactory.getTrustStores()).isNotEmpty();
145         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
146 
147         assertThat(sslFactory.getKeyManager()).isNotPresent();
148         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
149         assertThat(sslFactory.getIdentities()).isEmpty();
150     }
151 
152     @Test
153     void buildSSLFactoryWithTrustMaterialFromPath() throws IOException {
154         Path trustStorePath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
155 
156         SSLFactory sslFactory = SSLFactory.builder()
157                 .withTrustMaterial(trustStorePath, TRUSTSTORE_PASSWORD)
158                 .build();
159 
160         assertThat(sslFactory.getSslContext()).isNotNull();
161 
162         assertThat(sslFactory.getTrustManager()).isPresent();
163         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
164         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
165         assertThat(sslFactory.getTrustStores()).isNotEmpty();
166         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
167 
168         assertThat(sslFactory.getKeyManager()).isNotPresent();
169         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
170         assertThat(sslFactory.getIdentities()).isEmpty();
171 
172         Files.delete(trustStorePath);
173     }
174 
175     @Test
176     void buildSSLFactoryWithTrustMaterialFromInputStream() {
177         InputStream trustStoreStream = getResourceAsStream(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
178 
179         SSLFactory sslFactory = SSLFactory.builder()
180                 .withTrustMaterial(trustStoreStream, TRUSTSTORE_PASSWORD)
181                 .build();
182 
183         assertThat(sslFactory.getSslContext()).isNotNull();
184 
185         assertThat(sslFactory.getTrustManager()).isPresent();
186         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
187         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
188         assertThat(sslFactory.getTrustStores()).isNotEmpty();
189         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
190         assertThat(sslFactory.getKeyManager()).isNotPresent();
191         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
192     }
193 
194     @Test
195     void buildSSLFactoryWithTrustMaterialFromKeyStore() {
196         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
197 
198         SSLFactory sslFactory = SSLFactory.builder()
199                 .withTrustMaterial(trustStore, TRUSTSTORE_PASSWORD)
200                 .build();
201 
202         assertThat(sslFactory.getSslContext()).isNotNull();
203 
204         assertThat(sslFactory.getTrustManager()).isPresent();
205         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
206         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
207         assertThat(sslFactory.getTrustStores()).isNotEmpty();
208         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
209 
210         assertThat(sslFactory.getKeyManager()).isNotPresent();
211         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
212         assertThat(sslFactory.getIdentities()).isEmpty();
213     }
214 
215     @Test
216     void buildSSLFactoryWithTrustMaterialFromKeyStoreWithoutAdditionalPassword() {
217         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
218 
219         SSLFactory sslFactory = SSLFactory.builder()
220                 .withTrustMaterial(trustStore)
221                 .build();
222 
223         assertThat(sslFactory.getSslContext()).isNotNull();
224 
225         assertThat(sslFactory.getTrustManager()).isPresent();
226         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
227         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
228         assertThat(sslFactory.getTrustStores()).isNotEmpty();
229         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
230 
231         assertThat(sslFactory.getKeyManager()).isNotPresent();
232         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
233         assertThat(sslFactory.getIdentities()).isEmpty();
234     }
235 
236     @Test
237     void buildSSLFactoryWithTrustMaterialFromTrustManager() {
238         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
239         X509ExtendedTrustManager trustManager = TrustManagerUtils.createTrustManager(trustStore);
240 
241         SSLFactory sslFactory = SSLFactory.builder()
242                 .withTrustMaterial(trustManager)
243                 .build();
244 
245         assertThat(sslFactory.getSslContext()).isNotNull();
246 
247         assertThat(sslFactory.getTrustManager()).isPresent();
248         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
249         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
250         assertThat(sslFactory.getTrustStores()).isEmpty();
251         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
252 
253         assertThat(sslFactory.getKeyManager()).isNotPresent();
254         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
255         assertThat(sslFactory.getIdentities()).isEmpty();
256     }
257 
258     @Test
259     void buildSSLFactoryWithTrustMaterialFromTrustManagerFactory() throws NoSuchAlgorithmException, KeyStoreException {
260         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
261         TrustManagerFactory trustManagerFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
262         trustManagerFactory.init(trustStore);
263 
264         SSLFactory sslFactory = SSLFactory.builder()
265                 .withTrustMaterial(trustManagerFactory)
266                 .build();
267 
268         assertThat(sslFactory.getSslContext()).isNotNull();
269 
270         assertThat(sslFactory.getTrustManager()).isPresent();
271         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
272         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
273         assertThat(sslFactory.getTrustStores()).isEmpty();
274         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
275 
276         assertThat(sslFactory.getKeyManager()).isNotPresent();
277         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
278         assertThat(sslFactory.getIdentities()).isEmpty();
279     }
280 
281     @Test
282     void buildSSLFactoryWithTrustMaterialFromCertificates() {
283         X509Certificate[] certificates = TrustManagerUtils.createTrustManagerWithJdkTrustedCertificates()
284                 .getAcceptedIssuers();
285 
286         SSLFactory sslFactory = SSLFactory.builder()
287                 .withTrustMaterial(certificates)
288                 .build();
289 
290         assertThat(sslFactory.getSslContext()).isNotNull();
291 
292         assertThat(sslFactory.getTrustManager()).isPresent();
293         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
294         assertThat(sslFactory.getTrustStores()).isNotEmpty();
295         assertThat(sslFactory.getTrustedCertificates()).hasSizeGreaterThan(10);
296 
297         assertThat(sslFactory.getKeyManager()).isNotPresent();
298         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
299         assertThat(sslFactory.getIdentities()).isEmpty();
300     }
301 
302     @Test
303     void buildSSLFactoryWithTrustMaterialFromOnlyJdkTrustedCertificates() {
304         SSLFactory sslFactory = SSLFactory.builder()
305                 .withDefaultTrustMaterial()
306                 .build();
307 
308         assertThat(sslFactory.getSslContext()).isNotNull();
309 
310         assertThat(sslFactory.getTrustManager()).isPresent();
311         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
312         assertThat(sslFactory.getTrustStores()).isEmpty();
313         assertThat(sslFactory.getTrustedCertificates()).hasSizeGreaterThan(10);
314 
315         assertThat(sslFactory.getKeyManager()).isNotPresent();
316         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
317         assertThat(sslFactory.getIdentities()).isEmpty();
318     }
319 
320     @Test
321     void buildSSLFactoryWithTrustMaterialFromOnlySystemTrustedCertificates() {
322         String operatingSystem = System.getProperty("os.name").toLowerCase();
323         if (operatingSystem.contains("mac") || operatingSystem.contains("windows")) {
324             SSLFactory sslFactory = SSLFactory.builder()
325                     .withSystemTrustMaterial()
326                     .build();
327 
328             assertThat(sslFactory.getSslContext()).isNotNull();
329 
330             assertThat(sslFactory.getTrustManager()).isPresent();
331             assertThat(sslFactory.getTrustManagerFactory()).isPresent();
332             assertThat(sslFactory.getTrustStores()).isEmpty();
333 
334             assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
335             assertThat(sslFactory.getKeyManager()).isNotPresent();
336             assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
337             assertThat(sslFactory.getIdentities()).isEmpty();
338         }
339 
340         if (operatingSystem.contains("linux")) {
341             SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder()
342                     .withSystemTrustMaterial();
343 
344             assertThatThrownBy(sslFactoryBuilder::build)
345                     .isInstanceOf(GenericSecurityException.class)
346                     .hasMessage("Could not create instance of SSLFactory because Identity and Trust material are not present. Please provide at least a Trust material.");
347         }
348     }
349 
350     @Test
351     void buildSSLFactoryWithSecureRandom() throws NoSuchAlgorithmException {
352         SSLFactory sslFactory = SSLFactory.builder()
353                 .withSecureRandom(SecureRandom.getInstanceStrong())
354                 .withDefaultTrustMaterial()
355                 .build();
356 
357         assertThat(sslFactory.getSslContext()).isNotNull();
358 
359         assertThat(sslFactory.getTrustManager()).isPresent();
360         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
361         assertThat(sslFactory.getTrustStores()).isEmpty();
362         assertThat(sslFactory.getTrustedCertificates()).hasSizeGreaterThan(10);
363 
364         assertThat(sslFactory.getKeyManager()).isNotPresent();
365         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
366         assertThat(sslFactory.getIdentities()).isEmpty();
367     }
368 
369     @Test
370     void buildSSLFactoryWithTrustMaterialFromJdkTrustedCertificatesAndCustomTrustStore() {
371         SSLFactory sslFactory = SSLFactory.builder()
372                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
373                 .withDefaultTrustMaterial()
374                 .withPasswordCaching()
375                 .build();
376 
377         assertThat(sslFactory.getTrustManager()).isPresent();
378         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
379         assertThat(sslFactory.getTrustStores()).isNotEmpty();
380         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEqualTo(TRUSTSTORE_PASSWORD);
381         assertThat(sslFactory.getTrustedCertificates()).hasSizeGreaterThan(10);
382         assertThat(sslFactory.getTrustedCertificates().stream()
383                 .map(X509Certificate::getSubjectX500Principal)
384                 .map(X500Principal::toString)).contains("CN=*.google.com, O=Google LLC, L=Mountain View, ST=California, C=US");
385 
386         assertThat(sslFactory.getKeyManager()).isNotPresent();
387         assertThat(sslFactory.getKeyManagerFactory()).isNotPresent();
388         assertThat(sslFactory.getIdentities()).isEmpty();
389     }
390 
391     @Test
392     void buildSSLFactoryWithIdentityMaterial() {
393         SSLFactory sslFactory = SSLFactory.builder()
394                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
395                 .withPasswordCaching()
396                 .build();
397 
398         assertThat(sslFactory.getSslContext()).isNotNull();
399 
400         assertThat(sslFactory.getKeyManager()).isPresent();
401         assertThat(sslFactory.getKeyManager().get()).isNotInstanceOf(HotSwappableX509ExtendedKeyManager.class);
402         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
403         assertThat(sslFactory.getIdentities()).isNotEmpty();
404         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
405 
406         assertThat(sslFactory.getTrustManager()).isNotPresent();
407         assertThat(sslFactory.getTrustManagerFactory()).isNotPresent();
408         assertThat(sslFactory.getTrustedCertificates()).isEmpty();
409         assertThat(sslFactory.getTrustStores()).isEmpty();
410     }
411 
412     @Test
413     void buildSSLFactoryWithSwappableIdentityMaterial() {
414         SSLFactory sslFactory = SSLFactory.builder()
415                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
416                 .withSwappableIdentityMaterial()
417                 .build();
418 
419         assertThat(sslFactory.getSslContext()).isNotNull();
420 
421         assertThat(sslFactory.getKeyManager()).isPresent();
422         assertThat(sslFactory.getKeyManager().get()).isInstanceOf(HotSwappableX509ExtendedKeyManager.class);
423         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
424         assertThat(sslFactory.getIdentities()).isNotEmpty();
425 
426         assertThat(sslFactory.getTrustManager()).isNotPresent();
427         assertThat(sslFactory.getTrustManagerFactory()).isNotPresent();
428         assertThat(sslFactory.getTrustedCertificates()).isEmpty();
429         assertThat(sslFactory.getTrustStores()).isEmpty();
430     }
431 
432     @Test
433     void buildSSLFactoryWithIdentityMaterialAndTrustMaterial() {
434         SSLFactory sslFactory = SSLFactory.builder()
435                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
436                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
437                 .withPasswordCaching()
438                 .build();
439 
440         assertThat(sslFactory.getSslContext()).isNotNull();
441 
442         assertThat(sslFactory.getKeyManager()).isPresent();
443         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
444         assertThat(sslFactory.getIdentities()).isNotEmpty();
445         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
446 
447         assertThat(sslFactory.getTrustManager()).isPresent();
448         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
449         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
450         assertThat(sslFactory.getTrustStores()).isNotEmpty();
451         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEqualTo(TRUSTSTORE_PASSWORD);
452         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
453     }
454 
455     @Test
456     void buildSSLFactoryWithIdentityMaterialWithoutPasswordAndTrustMaterialWithoutPassword() {
457         SSLFactory sslFactory = SSLFactory.builder()
458                 .withIdentityMaterial(KEYSTORE_LOCATION + "identity-without-password.jks", null, "secret".toCharArray())
459                 .withTrustMaterial(KEYSTORE_LOCATION + "truststore-without-password.jks", null)
460                 .withPasswordCaching()
461                 .build();
462 
463         assertThat(sslFactory.getSslContext()).isNotNull();
464 
465         assertThat(sslFactory.getKeyManager()).isPresent();
466         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
467         assertThat(sslFactory.getIdentities()).isNotEmpty();
468         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isNull();
469 
470         assertThat(sslFactory.getTrustManager()).isPresent();
471         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
472         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
473         assertThat(sslFactory.getTrustStores()).isNotEmpty();
474         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isNull();
475         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
476     }
477 
478     @Test
479     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialWithKeyStoreTypesIncluded() {
480         SSLFactory sslFactory = SSLFactory.builder()
481                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD, KeyStore.getDefaultType())
482                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD, KeyStore.getDefaultType())
483                 .withPasswordCaching()
484                 .build();
485 
486         assertThat(sslFactory.getSslContext()).isNotNull();
487 
488         assertThat(sslFactory.getKeyManager()).isPresent();
489         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
490         assertThat(sslFactory.getIdentities()).isNotEmpty();
491         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
492 
493         assertThat(sslFactory.getTrustManager()).isPresent();
494         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
495         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
496         assertThat(sslFactory.getTrustStores()).isNotEmpty();
497         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEqualTo(TRUSTSTORE_PASSWORD);
498         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
499     }
500 
501     @Test
502     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromInputStream() {
503         InputStream identityStream = getResourceAsStream(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
504         InputStream trustStoreStream = getResourceAsStream(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
505 
506         SSLFactory sslFactory = SSLFactory.builder()
507                 .withIdentityMaterial(identityStream, IDENTITY_PASSWORD)
508                 .withTrustMaterial(trustStoreStream, TRUSTSTORE_PASSWORD)
509                 .build();
510 
511         assertThat(sslFactory.getSslContext()).isNotNull();
512 
513         assertThat(sslFactory.getKeyManager()).isPresent();
514         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
515         assertThat(sslFactory.getIdentities()).isNotEmpty();
516 
517         assertThat(sslFactory.getTrustManager()).isPresent();
518         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
519         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
520         assertThat(sslFactory.getTrustStores()).isNotEmpty();
521         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
522     }
523 
524     @Test
525     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromInputStreamWithCustomKeyStoreType() {
526         InputStream identityStream = getResourceAsStream(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
527         InputStream trustStoreStream = getResourceAsStream(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
528 
529         SSLFactory sslFactory = SSLFactory.builder()
530                 .withIdentityMaterial(identityStream, IDENTITY_PASSWORD, KeyStore.getDefaultType())
531                 .withTrustMaterial(trustStoreStream, TRUSTSTORE_PASSWORD)
532                 .build();
533 
534         assertThat(sslFactory.getSslContext()).isNotNull();
535 
536         assertThat(sslFactory.getKeyManager()).isPresent();
537         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
538         assertThat(sslFactory.getIdentities()).isNotEmpty();
539 
540         assertThat(sslFactory.getTrustManager()).isPresent();
541         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
542         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
543         assertThat(sslFactory.getTrustStores()).isNotEmpty();
544         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
545     }
546 
547     @Test
548     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromIdentityManagerAndTrustStore() {
549         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
550         X509ExtendedKeyManager identityManager = KeyManagerUtils.createKeyManager(identity, IDENTITY_PASSWORD);
551 
552         SSLFactory sslFactory = SSLFactory.builder()
553                 .withIdentityMaterial(identityManager)
554                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
555                 .build();
556 
557         assertThat(sslFactory.getSslContext()).isNotNull();
558 
559         assertThat(sslFactory.getKeyManager()).isPresent();
560         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
561         assertThat(sslFactory.getIdentities()).isEmpty();
562 
563         assertThat(sslFactory.getTrustManager()).isPresent();
564         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
565         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
566         assertThat(sslFactory.getTrustStores()).isNotEmpty();
567         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEmpty();
568         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
569     }
570 
571     @Test
572     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromIdentityManagerFactoryAndTrustStore() throws Exception {
573         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
574         KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
575         keyManagerFactory.init(identity, IDENTITY_PASSWORD);
576 
577         SSLFactory sslFactory = SSLFactory.builder()
578                 .withIdentityMaterial(keyManagerFactory)
579                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
580                 .build();
581 
582         assertThat(sslFactory.getSslContext()).isNotNull();
583 
584         assertThat(sslFactory.getKeyManager()).isPresent();
585         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
586         assertThat(sslFactory.getIdentities()).isEmpty();
587 
588         assertThat(sslFactory.getTrustManager()).isPresent();
589         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
590         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
591         assertThat(sslFactory.getTrustStores()).isNotEmpty();
592         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEmpty();
593         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
594     }
595 
596     @Test
597     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromKeyStoreAndOnlyJdkTrustedCertificates() {
598         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
599         SSLFactory sslFactory = SSLFactory.builder()
600                 .withIdentityMaterial(identity, IDENTITY_PASSWORD)
601                 .withDefaultTrustMaterial()
602                 .build();
603 
604         assertThat(sslFactory.getSslContext()).isNotNull();
605 
606         assertThat(sslFactory.getKeyManager()).isPresent();
607         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
608         assertThat(sslFactory.getIdentities()).isNotEmpty();
609         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEmpty();
610 
611         assertThat(sslFactory.getTrustManager()).isPresent();
612         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
613         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
614         assertThat(sslFactory.getTrustStores()).isEmpty();
615         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
616     }
617 
618     @Test
619     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromPrivateKey() throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
620         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
621         PrivateKey privateKey = (PrivateKey) identity.getKey("dummy-client", IDENTITY_PASSWORD);
622         Certificate[] certificateChain = identity.getCertificateChain("dummy-client");
623 
624         SSLFactory sslFactory = SSLFactory.builder()
625                 .withIdentityMaterial(privateKey, IDENTITY_PASSWORD, certificateChain)
626                 .withDefaultTrustMaterial()
627                 .build();
628 
629         assertThat(sslFactory.getSslContext()).isNotNull();
630 
631         assertThat(sslFactory.getKeyManager()).isPresent();
632         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
633         assertThat(sslFactory.getIdentities()).isNotEmpty();
634         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEmpty();
635         assertThat(sslFactory.getIdentities().get(0).getKeyStore()
636                 .containsAlias("cn=prof oak,ou=oak pokémon research lab,o=oak pokémon research lab,c=pallet town")).isTrue();
637 
638         assertThat(sslFactory.getTrustManager()).isPresent();
639         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
640         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
641         assertThat(sslFactory.getTrustStores()).isEmpty();
642         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
643     }
644 
645     @Test
646     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromPrivateKeyWithCustomAlias() throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException {
647         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
648         PrivateKey privateKey = (PrivateKey) identity.getKey("dummy-client", IDENTITY_PASSWORD);
649         Certificate[] certificateChain = identity.getCertificateChain("dummy-client");
650 
651         SSLFactory sslFactory = SSLFactory.builder()
652                 .withIdentityMaterial(privateKey, IDENTITY_PASSWORD, "thunder-client", certificateChain)
653                 .withDefaultTrustMaterial()
654                 .build();
655 
656         assertThat(sslFactory.getSslContext()).isNotNull();
657 
658         assertThat(sslFactory.getKeyManager()).isPresent();
659         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
660         assertThat(sslFactory.getIdentities()).isNotEmpty();
661         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEmpty();
662         assertThat(sslFactory.getIdentities().get(0).getKeyStore().containsAlias("thunder-client")).isTrue();
663 
664         assertThat(sslFactory.getTrustManager()).isPresent();
665         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
666         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
667         assertThat(sslFactory.getTrustStores()).isEmpty();
668         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
669     }
670 
671     @Test
672     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromKeyStoreWithDifferentKeyPasswordAndOnlyJdkTrustedCertificates() {
673         SSLFactory sslFactory = SSLFactory.builder()
674                 .withIdentityMaterial(KEYSTORE_LOCATION + "identity-with-different-key-password.jks", IDENTITY_PASSWORD, "my-precious".toCharArray())
675                 .withDefaultTrustMaterial()
676                 .withPasswordCaching()
677                 .build();
678 
679         assertThat(sslFactory.getSslContext()).isNotNull();
680 
681         assertThat(sslFactory.getKeyManager()).isPresent();
682         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
683         assertThat(sslFactory.getIdentities()).isNotEmpty();
684         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
685         assertThat(sslFactory.getIdentities().get(0).getKeyPassword()).isEqualTo("my-precious".toCharArray());
686 
687         assertThat(sslFactory.getTrustManager()).isPresent();
688         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
689         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
690         assertThat(sslFactory.getTrustStores()).isEmpty();
691         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
692     }
693 
694     @Test
695     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromKeyStorePathWithDifferentKeyPasswordAndOnlyJdkTrustedCertificates() throws IOException {
696         Path identityPath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, "identity-with-different-key-password.jks");
697 
698         SSLFactory sslFactory = SSLFactory.builder()
699                 .withIdentityMaterial(identityPath, IDENTITY_PASSWORD, "my-precious".toCharArray())
700                 .withDefaultTrustMaterial()
701                 .withPasswordCaching()
702                 .build();
703 
704         assertThat(sslFactory.getSslContext()).isNotNull();
705 
706         assertThat(sslFactory.getKeyManager()).isPresent();
707         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
708         assertThat(sslFactory.getIdentities()).isNotEmpty();
709         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
710         assertThat(sslFactory.getIdentities().get(0).getKeyPassword()).isEqualTo("my-precious".toCharArray());
711 
712         assertThat(sslFactory.getTrustManager()).isPresent();
713         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
714         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
715         assertThat(sslFactory.getTrustStores()).isEmpty();
716         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
717 
718         Files.delete(identityPath);
719     }
720 
721     @Test
722     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromKeyStoreAndTrustStoreWithPath() throws IOException {
723         Path identityPath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
724         Path trustStorePath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
725 
726         SSLFactory sslFactory = SSLFactory.builder()
727                 .withIdentityMaterial(identityPath, IDENTITY_PASSWORD)
728                 .withTrustMaterial(trustStorePath, TRUSTSTORE_PASSWORD)
729                 .withPasswordCaching()
730                 .build();
731 
732         assertThat(sslFactory.getSslContext()).isNotNull();
733 
734         assertThat(sslFactory.getKeyManager()).isPresent();
735         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
736         assertThat(sslFactory.getIdentities()).isNotEmpty();
737         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
738 
739         assertThat(sslFactory.getTrustManager()).isPresent();
740         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
741         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
742         assertThat(sslFactory.getTrustStores()).isNotEmpty();
743         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEqualTo(TRUSTSTORE_PASSWORD);
744         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
745 
746         Files.delete(identityPath);
747         Files.delete(trustStorePath);
748     }
749 
750     @Test
751     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromKeyStoreAndTrustStoreWithPathAndWithKeyStoreTypesIncluded() throws IOException {
752         Path identityPath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
753         Path trustStorePath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
754 
755         SSLFactory sslFactory = SSLFactory.builder()
756                 .withIdentityMaterial(identityPath, IDENTITY_PASSWORD, KeyStore.getDefaultType())
757                 .withTrustMaterial(trustStorePath, TRUSTSTORE_PASSWORD, KeyStore.getDefaultType())
758                 .withPasswordCaching()
759                 .build();
760 
761         assertThat(sslFactory.getSslContext()).isNotNull();
762 
763         assertThat(sslFactory.getKeyManager()).isPresent();
764         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
765         assertThat(sslFactory.getIdentities()).isNotEmpty();
766         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
767 
768         assertThat(sslFactory.getTrustManager()).isPresent();
769         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
770         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
771         assertThat(sslFactory.getTrustStores()).isNotEmpty();
772         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEqualTo(TRUSTSTORE_PASSWORD);
773         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
774 
775         Files.delete(identityPath);
776         Files.delete(trustStorePath);
777     }
778 
779     @Test
780     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromKeyStoreAndTrustStore() {
781         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
782         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
783 
784         SSLFactory sslFactory = SSLFactory.builder()
785                 .withIdentityMaterial(identity, IDENTITY_PASSWORD)
786                 .withTrustMaterial(trustStore, TRUSTSTORE_PASSWORD)
787                 .withPasswordCaching()
788                 .build();
789 
790         assertThat(sslFactory.getSslContext()).isNotNull();
791 
792         assertThat(sslFactory.getKeyManager()).isPresent();
793         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
794         assertThat(sslFactory.getIdentities()).isNotEmpty();
795         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEqualTo(IDENTITY_PASSWORD);
796 
797         assertThat(sslFactory.getTrustManager()).isPresent();
798         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
799         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
800         assertThat(sslFactory.getTrustStores()).isNotEmpty();
801         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEqualTo(TRUSTSTORE_PASSWORD);
802         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
803     }
804 
805     @Test
806     void buildSSLFactoryWithIdentityMaterialAndTrustMaterialFromKeyStoreAndTrustStoreWithoutCachingPasswords() {
807         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
808         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
809 
810         SSLFactory sslFactory = SSLFactory.builder()
811                 .withIdentityMaterial(identity, IDENTITY_PASSWORD)
812                 .withTrustMaterial(trustStore, TRUSTSTORE_PASSWORD)
813                 .build();
814 
815         assertThat(sslFactory.getSslContext()).isNotNull();
816 
817         assertThat(sslFactory.getKeyManager()).isPresent();
818         assertThat(sslFactory.getKeyManagerFactory()).isPresent();
819         assertThat(sslFactory.getIdentities()).isNotEmpty();
820         assertThat(sslFactory.getIdentities().get(0).getKeyStorePassword()).isEmpty();
821 
822         assertThat(sslFactory.getTrustManager()).isPresent();
823         assertThat(sslFactory.getTrustManagerFactory()).isPresent();
824         assertThat(sslFactory.getTrustedCertificates()).isNotEmpty();
825         assertThat(sslFactory.getTrustStores()).isNotEmpty();
826         assertThat(sslFactory.getTrustStores().get(0).getKeyStorePassword()).isEmpty();
827         assertThat(sslFactory.getHostnameVerifier()).isNotNull();
828     }
829 
830     @Test
831     void buildSSLFactoryByDefaultWithTlsSslContextAlgorithm() {
832         SSLFactory sslFactory = SSLFactory.builder()
833                 .withDefaultTrustMaterial()
834                 .build();
835 
836         assertThat(sslFactory.getSslContext().getProtocol()).isEqualTo("TLS");
837     }
838 
839     @Test
840     void buildSSLFactoryWithSslContextAlgorithm() {
841         SSLFactory sslFactory = SSLFactory.builder()
842                 .withDefaultTrustMaterial()
843                 .withSslContextAlgorithm("TLSv1.2")
844                 .build();
845 
846         assertThat(sslFactory.getSslContext().getProtocol()).isEqualTo("TLSv1.2");
847     }
848 
849     @Test
850     void buildSSLFactoryWithCustomHostnameVerifier() {
851         SSLFactory sslFactory = SSLFactory.builder()
852                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
853                 .withHostnameVerifier((host, sslSession) -> true)
854                 .build();
855 
856         HostnameVerifier hostnameVerifier = sslFactory.getHostnameVerifier();
857         assertThat(hostnameVerifier.verify("qwerty", null)).isTrue();
858     }
859 
860     @Test
861     void buildSSLFactoryWithoutHostnameVerifierProvidesDefaultHostnameVerifier() {
862         SSLFactory sslFactory = SSLFactory.builder()
863                 .withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD)
864                 .build();
865 
866         SSLSession sslSession = mock(SSLSession.class);
867         when(sslSession.getPeerHost()).thenReturn("localhost");
868 
869         HostnameVerifier hostnameVerifier = sslFactory.getHostnameVerifier();
870         assertThat(hostnameVerifier.verify("localhost", sslSession)).isTrue();
871     }
872 
873     @Test
874     void buildSSLFactoryWithTrustingAllCertificatesWithoutValidation() {
875         LogCaptor logCaptor = LogCaptor.forClass(SSLFactory.class);
876 
877         SSLFactory sslFactory = SSLFactory.builder()
878                 .withTrustingAllCertificatesWithoutValidation()
879                 .build();
880 
881         assertThat(sslFactory.getSslContext()).isNotNull();
882         assertThat(sslFactory.getTrustedCertificates()).isEmpty();
883         assertThat(sslFactory.getTrustStores()).isEmpty();
884         assertThat(sslFactory.getTrustManager()).isPresent();
885         assertThat(sslFactory.getTrustManager().get()).isInstanceOf(UnsafeX509ExtendedTrustManager.class);
886         assertThat(logCaptor.getWarnLogs()).contains("UnsafeTrustManager is being used. Client/Server certificates will be accepted without validation.");
887     }
888 
889     @Test
890     void buildSSLFactoryWithSecurityProvider() {
891         SSLFactory sslFactory = SSLFactory.builder()
892                 .withDefaultTrustMaterial()
893                 .withSslContextAlgorithm("TLS")
894                 .withSecurityProvider(Security.getProvider("SunJSSE"))
895                 .build();
896 
897         assertThat(sslFactory.getSslContext()).isNotNull();
898         assertThat(sslFactory.getSslContext().getProvider().getName()).isEqualTo("SunJSSE");
899     }
900 
901     @Test
902     void buildSSLFactoryWithSecurityProviderName() {
903         SSLFactory sslFactory = SSLFactory.builder()
904                 .withDefaultTrustMaterial()
905                 .withSslContextAlgorithm("TLS")
906                 .withSecurityProvider("SunJSSE")
907                 .build();
908 
909         assertThat(sslFactory.getSslContext()).isNotNull();
910         assertThat(sslFactory.getSslContext().getProvider().getName()).isEqualTo("SunJSSE");
911     }
912 
913     @Test
914     void throwExceptionWhenSSLFactoryIsBuildWithoutIdentityAndTrustMaterial() {
915         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
916 
917         assertThatThrownBy(factoryBuilder::build)
918                 .isInstanceOf(GenericSecurityException.class)
919                 .hasMessage("Could not create instance of SSLFactory because Identity and Trust material are not present. Please provide at least a Trust material.");
920     }
921 
922     @Test
923     void buildSSLFactoryWithTLSProtocolVersionOneDotThreeIfJavaVersionIsElevenOrGreater() {
924         Pattern valueBeforeDotPattern = Pattern.compile("^([^.]+)");
925 
926         String javaVersion = System.getProperty("java.version");
927         Matcher matcher = valueBeforeDotPattern.matcher(javaVersion);
928         if (!matcher.find()) {
929             fail("Could not find the java version");
930         }
931 
932         int javaMajorVersion = Integer.parseInt(matcher.group(0));
933         if (javaMajorVersion < 11) {
934             LOGGER.info("skipping unit test [{}] because TLSv1.3 is not available for this java {} version",
935                         new Object() {}.getClass().getEnclosingMethod().getName(),
936                         javaVersion);
937             return;
938         }
939 
940         LOGGER.info("Found java version {}, including testing SSLFactory with TLSv1.3 protocol", javaMajorVersion);
941         SSLFactory sslFactory = SSLFactory.builder()
942                 .withDefaultTrustMaterial()
943                 .build();
944 
945         assertThat(sslFactory.getSslContext()).isNotNull();
946         assertThat(sslFactory.getProtocols()).contains("TLSv1.3");
947     }
948 
949     @Test
950     void returnSslSocketFactory() {
951         SSLFactory sslFactory = SSLFactory.builder()
952                 .withDefaultTrustMaterial()
953                 .withCiphers("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384")
954                 .build();
955 
956         assertThat(sslFactory.getSslContext()).isNotNull();
957         assertThat(sslFactory.getSslSocketFactory()).isNotNull();
958         assertThat(sslFactory.getSslSocketFactory().getDefaultCipherSuites()).containsExactly("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
959         assertThat(sslFactory.getSslSocketFactory().getSupportedCipherSuites()).containsExactly("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
960     }
961 
962     @Test
963     void returnSslServerSocketFactory() {
964         SSLFactory sslFactory = SSLFactory.builder()
965                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
966                 .withDefaultTrustMaterial()
967                 .withCiphers("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384")
968                 .build();
969 
970         assertThat(sslFactory.getSslContext()).isNotNull();
971         assertThat(sslFactory.getSslServerSocketFactory()).isNotNull();
972         assertThat(sslFactory.getSslServerSocketFactory().getDefaultCipherSuites()).containsExactly("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
973         assertThat(sslFactory.getSslServerSocketFactory().getSupportedCipherSuites()).containsExactly("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384");
974     }
975 
976     @Test
977     void returnDefaultCiphersWhenNoneSpecified() {
978         SSLFactory sslFactory = SSLFactory.builder()
979                 .withDefaultTrustMaterial()
980                 .build();
981 
982         assertThat(sslFactory.getSslContext()).isNotNull();
983         assertThat(sslFactory.getCiphers()).isNotEmpty();
984         assertThat(sslFactory.getCiphers()).containsExactlyInAnyOrder(sslFactory.getSslContext().getDefaultSSLParameters().getCipherSuites());
985     }
986 
987     @Test
988     void returnSpecifiedCiphers() {
989         SSLFactory sslFactory = SSLFactory.builder()
990                 .withDefaultTrustMaterial()
991                 .withCiphers("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384")
992                 .build();
993 
994         assertThat(sslFactory.getSslContext()).isNotNull();
995         assertThat(sslFactory.getCiphers())
996                 .containsExactlyInAnyOrder("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
997     }
998 
999     @Test
1000     void returnSpecifiedCiphersAndProtocolsWithinSslParameters() {
1001         SSLFactory sslFactory = SSLFactory.builder()
1002                 .withDefaultTrustMaterial()
1003                 .withCiphers("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384")
1004                 .withProtocols("TLSv1.2", "TLSv1.1")
1005                 .build();
1006 
1007         assertThat(sslFactory.getSslContext()).isNotNull();
1008         assertThat(sslFactory.getSslParameters().getCipherSuites())
1009                 .containsExactlyInAnyOrder("TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384");
1010         assertThat(sslFactory.getSslParameters().getProtocols())
1011                 .containsExactlyInAnyOrder("TLSv1.2", "TLSv1.1");
1012         assertThat(sslFactory.getSslParameters())
1013                 .isNotEqualTo(sslFactory.getSslContext().getDefaultSSLParameters());
1014     }
1015 
1016     @Test
1017     void returnDefaultProtocolsWhenNoneSpecified() {
1018         SSLFactory sslFactory = SSLFactory.builder()
1019                 .withDefaultTrustMaterial()
1020                 .build();
1021 
1022         assertThat(sslFactory.getSslContext()).isNotNull();
1023         assertThat(sslFactory.getProtocols()).containsExactlyInAnyOrder(sslFactory.getSslContext().getDefaultSSLParameters().getProtocols());
1024     }
1025 
1026     @Test
1027     void returnSpecifiedProtocols() {
1028         SSLFactory sslFactory = SSLFactory.builder()
1029                 .withDefaultTrustMaterial()
1030                 .withProtocols("TLSv1.2", "TLSv1.1")
1031                 .build();
1032 
1033         assertThat(sslFactory.getSslContext()).isNotNull();
1034         assertThat(sslFactory.getProtocols()).containsExactlyInAnyOrder("TLSv1.2", "TLSv1.1");
1035     }
1036 
1037     @Test
1038     void returnSpecifiedNeedClientAuthenticationWithoutOptions() {
1039         SSLFactory sslFactory = SSLFactory.builder()
1040                 .withDefaultTrustMaterial()
1041                 .withNeedClientAuthentication()
1042                 .build();
1043 
1044         assertThat(sslFactory.getSslParameters().getNeedClientAuth()).isTrue();
1045         assertThat(sslFactory.getSslParameters().getWantClientAuth()).isFalse();
1046     }
1047 
1048     @Test
1049     void returnSpecifiedNeedClientAuthenticationWithOptions() {
1050         SSLFactory sslFactory = SSLFactory.builder()
1051                 .withDefaultTrustMaterial()
1052                 .withNeedClientAuthentication(true)
1053                 .build();
1054 
1055         assertThat(sslFactory.getSslParameters().getNeedClientAuth()).isTrue();
1056         assertThat(sslFactory.getSslParameters().getWantClientAuth()).isFalse();
1057     }
1058 
1059     @Test
1060     void returnSpecifiedWantClientAuthenticationWithoutOptions() {
1061         SSLFactory sslFactory = SSLFactory.builder()
1062                 .withDefaultTrustMaterial()
1063                 .withWantClientAuthentication()
1064                 .build();
1065 
1066         assertThat(sslFactory.getSslParameters().getWantClientAuth()).isTrue();
1067         assertThat(sslFactory.getSslParameters().getNeedClientAuth()).isFalse();
1068     }
1069 
1070     @Test
1071     void returnSpecifiedWantClientAuthenticationWithOptions() {
1072         SSLFactory sslFactory = SSLFactory.builder()
1073                 .withDefaultTrustMaterial()
1074                 .withWantClientAuthentication(true)
1075                 .build();
1076 
1077         assertThat(sslFactory.getSslParameters().getWantClientAuth()).isTrue();
1078         assertThat(sslFactory.getSslParameters().getNeedClientAuth()).isFalse();
1079     }
1080 
1081     @Test
1082     void returnSslEngineWithSslParameters() {
1083         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
1084         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
1085 
1086         SSLFactory sslFactory = SSLFactory.builder()
1087                 .withIdentityMaterial(identity, IDENTITY_PASSWORD)
1088                 .withTrustMaterial(trustStore, TRUSTSTORE_PASSWORD)
1089                 .withNeedClientAuthentication()
1090                 .build();
1091 
1092         SSLEngine sslEngine = sslFactory.getSSLEngine();
1093 
1094         assertThat(sslEngine).isNotNull();
1095         assertThat(sslEngine.getPeerHost()).isNull();
1096         assertThat(sslEngine.getPeerPort()).isEqualTo(-1);
1097         assertThat(sslEngine.getNeedClientAuth()).isTrue();
1098     }
1099 
1100     @Test
1101     void returnSslEngineWithoutHostAndPortIfOnlyHostIsDefined() {
1102         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
1103         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
1104 
1105         SSLFactory sslFactory = SSLFactory.builder()
1106                 .withIdentityMaterial(identity, IDENTITY_PASSWORD)
1107                 .withTrustMaterial(trustStore, TRUSTSTORE_PASSWORD)
1108                 .withNeedClientAuthentication()
1109                 .build();
1110 
1111         SSLEngine sslEngine = sslFactory.getSSLEngine("localhost", null);
1112 
1113         assertThat(sslEngine).isNotNull();
1114         assertThat(sslEngine.getPeerHost()).isNull();
1115         assertThat(sslEngine.getPeerPort()).isEqualTo(-1);
1116         assertThat(sslEngine.getNeedClientAuth()).isTrue();
1117     }
1118 
1119     @Test
1120     void returnSslEngineWithHostAndPortAndWithSslParameters() {
1121         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
1122         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
1123 
1124         SSLFactory sslFactory = SSLFactory.builder()
1125                 .withIdentityMaterial(identity, IDENTITY_PASSWORD)
1126                 .withTrustMaterial(trustStore, TRUSTSTORE_PASSWORD)
1127                 .withNeedClientAuthentication()
1128                 .build();
1129 
1130         SSLEngine sslEngine = sslFactory.getSSLEngine("localhost", 8443);
1131 
1132         assertThat(sslEngine).isNotNull();
1133         assertThat(sslEngine.getPeerHost()).isEqualTo("localhost");
1134         assertThat(sslEngine.getPeerPort()).isEqualTo(8443);
1135         assertThat(sslEngine.getNeedClientAuth()).isTrue();
1136     }
1137 
1138     @Test
1139     void createMultipleRoutesForSingleClientIdentity() {
1140         SSLFactory sslFactory = SSLFactory.builder()
1141                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
1142                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
1143                 .withClientIdentityRoute("some-client-alias", "https://localhost:8443", "https://localhost:8444")
1144                 .build();
1145 
1146         assertThat(sslFactory.getKeyManager()).isPresent();
1147         assertThat(KeyManagerUtils.getClientIdentityRoute(sslFactory.getKeyManager().get()))
1148                 .containsKey("some-client-alias")
1149                 .containsValue(Arrays.asList("https://localhost:8443", "https://localhost:8444"));
1150 
1151         assertThat(((CompositeX509ExtendedKeyManager)sslFactory.getKeyManager().get()).getPreferredClientAliasToHosts())
1152                 .containsKey("some-client-alias")
1153                 .containsValue(Arrays.asList(URI.create("https://localhost:8443"), URI.create("https://localhost:8444")));
1154     }
1155 
1156     @Test
1157     void createMultipleRoutesForSingleClientIdentityAndUpdateAfterCreation() {
1158         SSLFactory sslFactory = SSLFactory.builder()
1159                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
1160                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
1161                 .withClientIdentityRoute("some-client-alias", "https://localhost:8443", "https://localhost:8444")
1162                 .build();
1163 
1164         assertThat(sslFactory.getKeyManager()).isPresent();
1165         assertThat(KeyManagerUtils.getClientIdentityRoute(sslFactory.getKeyManager().get()))
1166                 .containsKey("some-client-alias")
1167                 .containsValue(Arrays.asList("https://localhost:8443", "https://localhost:8444"))
1168                 .doesNotContainValue(Collections.singletonList("https://localhost:8445"));
1169 
1170         KeyManagerUtils.addClientIdentityRoute(sslFactory.getKeyManager().get(), "some-client-alias", "https://localhost:8445");
1171 
1172         assertThat(KeyManagerUtils.getClientIdentityRoute(sslFactory.getKeyManager().get()))
1173                 .containsKey("some-client-alias")
1174                 .containsValue(Arrays.asList("https://localhost:8443", "https://localhost:8444", "https://localhost:8445"));
1175     }
1176 
1177     @Test
1178     void createSSLFactoryWithSessionTimeout() {
1179         SSLFactory sslFactory = SSLFactory.builder()
1180                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
1181                 .withDefaultTrustMaterial()
1182                 .withSessionTimeout(10)
1183                 .build();
1184 
1185         int clientSessionTimeout = sslFactory.getSslContext()
1186                 .getClientSessionContext()
1187                 .getSessionTimeout();
1188 
1189         int serverSessionTimeout = sslFactory.getSslContext()
1190                 .getServerSessionContext()
1191                 .getSessionTimeout();
1192 
1193         assertThat(clientSessionTimeout).isEqualTo(10);
1194         assertThat(serverSessionTimeout).isEqualTo(10);
1195     }
1196 
1197     @Test
1198     void createSSLFactoryWithSessionCacheSize() {
1199         SSLFactory sslFactory = SSLFactory.builder()
1200                 .withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD)
1201                 .withDefaultTrustMaterial()
1202                 .withSessionCacheSize(1024)
1203                 .build();
1204 
1205         int clientSessionCacheSize = sslFactory.getSslContext()
1206                 .getClientSessionContext()
1207                 .getSessionCacheSize();
1208 
1209         int serverSessionCacheSize = sslFactory.getSslContext()
1210                 .getServerSessionContext()
1211                 .getSessionCacheSize();
1212 
1213         assertThat(clientSessionCacheSize).isEqualTo(1024);
1214         assertThat(serverSessionCacheSize).isEqualTo(1024);
1215     }
1216 
1217     @Test
1218     void throwExceptionWhenBuildingSSLFactoryWithTrustStoreWhileProvidingWrongPassword() {
1219         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1220         char[] trustStorePassword = "password".toCharArray();
1221 
1222         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, trustStorePassword))
1223                 .isInstanceOf(GenericKeyStoreException.class)
1224                 .hasMessageContaining("keystore password was incorrect");
1225     }
1226 
1227     @Test
1228     void throwExceptionWhenBuildingSSLFactoryWithTrustStoreFromPathWhileProvidingWrongPassword() throws IOException {
1229         Path trustStorePath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
1230         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1231         char[] trustStorePassword = "password".toCharArray();
1232 
1233         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial(trustStorePath, trustStorePassword))
1234                 .isInstanceOf(GenericKeyStoreException.class)
1235                 .hasMessageContaining("keystore password was incorrect");
1236 
1237         Files.delete(trustStorePath);
1238     }
1239 
1240     @Test
1241     void throwExceptionWhenBuildingSSLFactoryWithIdentityWhileProvidingWrongPassword() {
1242         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1243         char[] identityStorePassword = "password".toCharArray();
1244 
1245         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, identityStorePassword))
1246                 .isInstanceOf(GenericKeyStoreException.class)
1247                 .hasMessageContaining("keystore password was incorrect");
1248     }
1249 
1250     @Test
1251     void throwExceptionWhenBuildingSSLFactoryWithIdentityFromPathWhileProvidingWrongPassword() throws IOException {
1252         Path identityPath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
1253         char[] identityStorePassword = "password".toCharArray();
1254         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1255 
1256         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial(identityPath, identityStorePassword))
1257                 .isInstanceOf(GenericKeyStoreException.class)
1258                 .hasMessageContaining("keystore password was incorrect");
1259 
1260         Files.delete(identityPath);
1261     }
1262 
1263     @Test
1264     void throwExceptionWhenBuildingSSLFactoryWithNullAsTrustStorePath() {
1265         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1266 
1267         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial((Path) null, TRUSTSTORE_PASSWORD))
1268                 .isInstanceOf(GenericKeyStoreException.class)
1269                 .hasMessage(GENERIC_TRUSTSTORE_VALIDATION_EXCEPTION_MESSAGE);
1270     }
1271 
1272     @Test
1273     void throwExceptionWhenBuildingSSLFactoryWithEmptyTrustStoreType() throws IOException {
1274         Path trustStorePath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
1275         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1276 
1277         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial(trustStorePath, TRUSTSTORE_PASSWORD, EMPTY))
1278                 .isInstanceOf(GenericKeyStoreException.class)
1279                 .hasMessage(GENERIC_TRUSTSTORE_VALIDATION_EXCEPTION_MESSAGE);
1280 
1281         Files.delete(trustStorePath);
1282     }
1283 
1284     @Test
1285     void throwExceptionWhenBuildingSSLFactoryWithTrustStoreAsNull() {
1286         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1287 
1288         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial((KeyStore) null, TRUSTSTORE_PASSWORD))
1289                 .isInstanceOf(GenericKeyStoreException.class)
1290                 .hasMessage(GENERIC_TRUSTSTORE_VALIDATION_EXCEPTION_MESSAGE);
1291     }
1292 
1293     @Test
1294     void throwExceptionWhenKeyStoreFileIsNotFound() {
1295         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1296 
1297         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial(KEYSTORE_LOCATION + "not-existing-truststore.jks", TRUSTSTORE_PASSWORD))
1298                 .isInstanceOf(GenericKeyStoreException.class)
1299                 .hasMessageContaining("KeyStore is not present for the giving input");
1300     }
1301 
1302     @Test
1303     void throwExceptionWhenTrustStorePathIsNotProvided() {
1304         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1305 
1306         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial(EMPTY, TRUSTSTORE_PASSWORD))
1307                 .isInstanceOf(GenericKeyStoreException.class)
1308                 .hasMessage(GENERIC_TRUSTSTORE_VALIDATION_EXCEPTION_MESSAGE);
1309     }
1310 
1311     @Test
1312     void throwExceptionWhenIdentityPathIsNotProvided() {
1313         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1314 
1315         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial(EMPTY, IDENTITY_PASSWORD))
1316                 .isInstanceOf(GenericKeyStoreException.class)
1317                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1318     }
1319 
1320     @Test
1321     void throwExceptionWhenIdentityPathAsStringIsNull() {
1322         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1323 
1324         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial((String) null, IDENTITY_PASSWORD))
1325                 .isInstanceOf(GenericKeyStoreException.class)
1326                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1327     }
1328 
1329     @Test
1330     void throwExceptionWhenIdentityPathAsStringContainsOnlyWhiteSpace() {
1331         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1332 
1333         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial("    ", IDENTITY_PASSWORD))
1334                 .isInstanceOf(GenericKeyStoreException.class)
1335                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1336     }
1337 
1338     @Test
1339     void throwExceptionWhenIdentityTypeIsNotProvided() {
1340         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1341 
1342         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD, EMPTY))
1343                 .isInstanceOf(GenericKeyStoreException.class)
1344                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1345     }
1346 
1347     @Test
1348     void throwExceptionWhenIdentityPathIsNull() {
1349         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1350 
1351         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial((Path) null, IDENTITY_PASSWORD))
1352                 .isInstanceOf(GenericKeyStoreException.class)
1353                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1354     }
1355 
1356     @Test
1357     void throwExceptionWhenIdentityIsNull() {
1358         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1359 
1360         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial((KeyStore) null, IDENTITY_PASSWORD))
1361                 .isInstanceOf(GenericKeyStoreException.class)
1362                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1363     }
1364 
1365     @Test
1366     void throwExceptionWhenIdentityTypeIsNotProvidedWhileUsingPath() throws IOException {
1367         Path identityPath = copyKeystoreToHomeDirectory(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
1368         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1369 
1370         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial(identityPath, IDENTITY_PASSWORD, EMPTY))
1371                 .isInstanceOf(GenericKeyStoreException.class)
1372                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1373 
1374         Files.delete(identityPath);
1375     }
1376 
1377     @Test
1378     void throwExceptionWhenIdentityStreamIsNull() {
1379         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1380 
1381         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial((InputStream) null, IDENTITY_PASSWORD))
1382                 .isInstanceOf(GenericKeyStoreException.class)
1383                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1384     }
1385 
1386     @Test
1387     void throwExceptionWhenIdentityTypeIsNotProvidedWhileUsingInputStream() {
1388         InputStream identityStream = getResourceAsStream(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
1389         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1390 
1391         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial(identityStream, IDENTITY_PASSWORD, EMPTY))
1392                 .isInstanceOf(GenericKeyStoreException.class)
1393                 .hasMessage(GENERIC_IDENTITY_VALIDATION_EXCEPTION_MESSAGE);
1394     }
1395 
1396     @Test
1397     void throwExceptionWhenUnknownIdentityTypeIsProvidedWhileUsingInputStream() {
1398         InputStream identityStream = getResourceAsStream(KEYSTORE_LOCATION, IDENTITY_FILE_NAME);
1399         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1400 
1401         assertThatThrownBy(() -> factoryBuilder.withIdentityMaterial(identityStream, IDENTITY_PASSWORD, "KABOOM"))
1402                 .isInstanceOf(GenericKeyStoreException.class)
1403                 .hasMessageContaining("KABOOM not found");
1404     }
1405 
1406     @Test
1407     void throwExceptionWhenUnknownTrustStoreTypeIsProvidedWhileUsingInputStream() {
1408         InputStream trustStoreStream = getResourceAsStream(KEYSTORE_LOCATION, TRUSTSTORE_FILE_NAME);
1409         SSLFactory.Builder factoryBuilder = SSLFactory.builder();
1410 
1411         assertThatThrownBy(() -> factoryBuilder.withTrustMaterial(trustStoreStream, TRUSTSTORE_PASSWORD, "KABOOM"))
1412                 .isInstanceOf(GenericKeyStoreException.class)
1413                 .hasMessageContaining("KABOOM not found");
1414     }
1415 
1416     @Test
1417     void throwExceptionWhenProvidingWrongKeyPassword() {
1418         SSLFactory.Builder factoryBuilder = SSLFactory.builder()
1419                 .withIdentityMaterial(
1420                         KEYSTORE_LOCATION + "identity-with-different-key-password.jks",
1421                         IDENTITY_PASSWORD,
1422                         IDENTITY_PASSWORD)
1423                 .withDefaultTrustMaterial();
1424 
1425         assertThatThrownBy(factoryBuilder::build)
1426                 .isInstanceOf(GenericSecurityException.class)
1427                 .hasMessage("java.security.UnrecoverableKeyException: Get Key failed: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.");
1428     }
1429 
1430     @Test
1431     void throwExceptionWhenUnknownSslContextAlgorithmIsProvided() {
1432         SSLFactory.Builder factoryBuilder = SSLFactory.builder()
1433                 .withDefaultTrustMaterial()
1434                 .withSslContextAlgorithm("KABOOM");
1435 
1436         assertThatThrownBy(factoryBuilder::build)
1437                 .isInstanceOf(GenericSecurityException.class)
1438                 .hasMessage("java.security.NoSuchAlgorithmException: KABOOM SSLContext not available");
1439     }
1440 
1441     @Test
1442     void throwExceptionWhenUnknownSecurityProviderNameIsProvided() {
1443         SSLFactory.Builder factoryBuilder = SSLFactory.builder()
1444                 .withDefaultTrustMaterial()
1445                 .withSslContextAlgorithm("TLS")
1446                 .withSecurityProvider("KABOOOM");
1447 
1448         assertThatThrownBy(factoryBuilder::build)
1449                 .isInstanceOf(GenericSecurityException.class)
1450                 .hasMessage("java.security.NoSuchProviderException: no such provider: KABOOOM");
1451     }
1452 
1453     @Test
1454     void throwExceptionNullIsIsProvidedWhenUsingPrivateKey() throws CertificateException, NoSuchAlgorithmException, KeyStoreException, IOException, UnrecoverableKeyException {
1455         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
1456         Certificate[] certificateChain = identity.getCertificateChain("dummy-client");
1457 
1458         SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder();
1459 
1460         assertThatThrownBy(() -> sslFactoryBuilder.withIdentityMaterial(null, IDENTITY_PASSWORD, certificateChain))
1461                 .isInstanceOf(GenericSecurityException.class)
1462                 .hasMessageContaining("Unsupported Key type");
1463     }
1464 
1465     @Test
1466     void throwExceptionWhenKeyManagerFactoryDoesNotContainsKeyManagersOfX509KeyManagerType() throws Exception {
1467         KeyStore identity = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + IDENTITY_FILE_NAME, IDENTITY_PASSWORD);
1468         KeyManagerFactory keyManagerFactory = spy(KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()));
1469         keyManagerFactory.init(identity, IDENTITY_PASSWORD);
1470 
1471         when(keyManagerFactory.getKeyManagers()).thenReturn(new KeyManager[] { mock(KeyManager.class) });
1472         SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder();
1473 
1474         assertThatThrownBy(() -> sslFactoryBuilder.withIdentityMaterial(keyManagerFactory))
1475                 .isInstanceOf(GenericKeyManagerException.class)
1476                 .hasMessage("Input does not contain KeyManagers");
1477     }
1478 
1479     @Test
1480     void throwExceptionWhenTrustManagerFactoryDoesNotContainsTrustManagersOfX509TrustManagerType() throws Exception {
1481         KeyStore trustStore = KeyStoreUtils.loadKeyStore(KEYSTORE_LOCATION + TRUSTSTORE_FILE_NAME, TRUSTSTORE_PASSWORD);
1482         TrustManagerFactory trustManagerFactory = spy(TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()));
1483         trustManagerFactory.init(trustStore);
1484 
1485         when(trustManagerFactory.getTrustManagers()).thenReturn(new TrustManager[] { mock(TrustManager.class) });
1486         SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder();
1487 
1488         assertThatThrownBy(() -> sslFactoryBuilder.withTrustMaterial(trustManagerFactory))
1489                 .isInstanceOf(GenericTrustManagerException.class)
1490                 .hasMessage("Input does not contain TrustManager");
1491     }
1492 
1493     @Test
1494     void throwExceptionWhenClientAliasIsNotPresentWhenRoutingIdentities() {
1495         SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder();
1496         assertThatThrownBy(() -> sslFactoryBuilder.withClientIdentityRoute(null, "https://localhost:8443"))
1497                 .isInstanceOf(IllegalArgumentException.class)
1498                 .hasMessage("clientAlias should be present");
1499     }
1500 
1501     @Test
1502     void throwExceptionWhenRouteIsNotPresentForClientIdentityRoute() {
1503         SSLFactory.Builder sslFactoryBuilder = SSLFactory.builder();
1504         assertThatThrownBy(() -> sslFactoryBuilder.withClientIdentityRoute("some-client-alias"))
1505                 .isInstanceOf(IllegalArgumentException.class)
1506                 .hasMessage("At least one host should be present. No host(s) found for the given alias: [some-client-alias]");
1507     }
1508 
1509     @SuppressWarnings("SameParameterValue")
1510     private Path copyKeystoreToHomeDirectory(String path, String fileName) throws IOException {
1511         try (InputStream keystoreInputStream = getResourceAsStream(path, fileName)) {
1512             Path destination = Paths.get(TEMPORALLY_KEYSTORE_LOCATION, fileName);
1513             Files.copy(Objects.requireNonNull(keystoreInputStream), destination, REPLACE_EXISTING);
1514             return destination;
1515         }
1516     }
1517 
1518     private InputStream getResourceAsStream(String path, String fileName) {
1519         return Thread.currentThread().getContextClassLoader().getResourceAsStream(path + fileName);
1520     }
1521 
1522 }