javaspring-bootsslapache-httpcomponentsspring-resttemplate

Cannot finish dynamic SSL context with Apache HTTP client 5 and Spring Boot 3.4.4


I am using JDK 23, Apache HTTP Component 5.4.1, Spring Boot 3.4.4

<properties>
    <java.version>23</java.version>
    <maven.compiler.source>23</maven.compiler.source>
    <maven.compiler.target>23</maven.compiler.target>
</properties>

<!-- ... -->

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.4.4</version>
    <relativePath/>
</parent>

<!-- ... -->

<dependency>
    <groupId>org.apache.httpcomponents.client5</groupId>
    <artifactId>httpclient5</artifactId>
    <version>5.4.2</version>
</dependency>

File application.yml

server:
    port: 8555
    ssl:
        enabled: true
        bundle:
            pem:
                aceserver:
                    keystore:
                        certificate: C:\\foo\\bar\\aceserver.crt
                        private-key: C:\\foo\\bar\\aceserver.key
                    truststore:
                        certificate: C:\\foo\\bar\\aceserver-truststore.p12
                    reload-on-update: true
                    server042:
                        keystore:
                            certificate: C:\\foo\\bar\\server042.crt
                            private-key: C:\\foo\\bar\\server042.key
                        truststore:
                            certificate: C:\\foo\\bar\\xxx042.crt
                        reload-on-update: true
                    server044:
                        keystore:
                            certificate: C:\\foo\\bar\\server043.crt
                            private-key: C:\\foo\\bar\\server043.key
                        truststore:
                            certificate: C:\\foo\\bar\\xxx043.crt
                        reload-on-update: true

I have about 250 different SSLBundle items what declared inside application.yml . I cannot config 1 SSLBundle for global project as a normal way. I need RESTtempalte with dynamic ability of switching SSLBundle, therferefore, I must do few internal steps manually.

Due to few (many) changes in SSLBundle from Spring Boot 3.3.x to 3.4.x , additional, many change from HTTP Client 4 to HTTP Client 5. I struggled.

SslBundle sslBundle = sslBundles.getBundle("serverName042"); // Hundred of bundle, get from database, for demonstration, it is a hard-coded string.
if (sslBundle == null) {
    throw new IllegalArgumentException("SSL Bundle not found: " + "serverName042");
}

// Get KeyStore and TrustStore from SSLBundle
KeyStore keyStore = sslBundle.getStores().getKeyStore();
KeyStore trustStore = sslBundle.getStores().getTrustStore();

// https://hc.apache.org/httpcomponents-client-5.4.x/migration-guide/migration-to-classic.html
// https://github.com/apache/httpcomponents-client/blob/5.4.x/httpclient5/src/test/java/org/apache/hc/client5/http/examples/AsyncClientCustomSSL.java

// Build SSLContext manually
SSLContext sslContext = SSLContexts.custom()
        .loadKeyMaterial(keyStore, "secret123".toCharArray()) // Replace with actual password. I want a better way get password of key material from SSLBundle in bundle item of application.yml .
        .loadTrustMaterial(trustStore, null)
        .build();
// Manually create an SSLSocketFactory
//SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();

// Create an HTTP Client with the SSLContext.
SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext);
PoolingHttpClientConnectionManager connectionManager = new PoolingHttpClientConnectionManager();
connectionManager.setDefaultSocketConfig(null); // I cannot finish it, quite hard.

// I tried this but syntax error
// connectionManager.setDefaultSocketConfig(sslSocketFactory);

CloseableHttpClient httpClient = HttpClients.custom().build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(httpClient);

//...

String url = baseURL + "/xxx/yyy/zzz/" + dummyID + "/ggg/" + fuuBarID + "/detail";
log.info(">>> url = " + url);
ResponseEntity<Object> response = restTemplate.getForEntity(url, Object.class);

Related

Please help me complete this code snippet with HTPT Client 5.4.x and Spring Boot 3.4.4 and JDK 23. (ChatGPT with knowledge cut off on October 2023, I cannot get valuable information from chatbot, because these changes with HTTP Client 5 and Spring Boot 3.4.x SSLBundle methods are new).


Solution

  • SslBundle sslBundle = sslBundles.getBundle("foo");
    SSLContext sslContext = sslBundle.createSslContext();
    final HttpClient client = HttpClients.custom().setConnectionManager(PoolingHttpClientConnectionManagerBuilder.create().setTlsSocketStrategy(new DefaultClientTlsStrategy(sslContext, () -> true)).build()).build();
    final HttpGet httpget = new HttpGet("http://example.com");