Reputation: 4970
I'm calling the same API endpoint once with WebClient
, and once with RestTemplate
. The RestTemplate
call succeeds, the WebClient
call fails due to handshake_failure
.
Here's the configuration for my WebClient
bean.
@Bean
WebClient webClient(String baseUrl,
@Value("classpath:bundle.pem") Resource pemBundleResource) throws Exception {
final SslContext sslContext = SslContextBuilder
.forClient()
.trustManager(pemBundleResource.getInputStream())
.build();
HttpClient httpClient = HttpClient
.create()
.secure(sslContextSpec -> sslContextSpec.sslContext(sslContext));
return WebClient.builder()
.clientConnector(new ReactorClientHttpConnector(httpClient))
.baseUrl(baseUrl)
.build();
}
I've also tried an InsecureTrustManagerFactory.INSTANCE
to ignore SSL altogether, but that request fails as well with the same error which tells me the configuration might not be getting used.
Any help would be appreciated!
Here's the stack trace:
reactor.core.Exceptions$ReactiveException: javax.net.ssl.SSLException: Received fatal alert: handshake_failure
at reactor.core.Exceptions.propagate(Exceptions.java:326)
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:91)
at reactor.core.publisher.Mono.block(Mono.java:1494)
at com.example.APITestsIT.callApi(APITestsIT.java:50)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74)
at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84)
at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Suppressed: java.lang.Exception: #block terminated with an error
at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:93)
... 32 more
Caused by: javax.net.ssl.SSLException: Received fatal alert: handshake_failure
at sun.security.ssl.Alerts.getSSLException(Alerts.java:208)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1647)
at sun.security.ssl.SSLEngineImpl.fatal(SSLEngineImpl.java:1615)
at sun.security.ssl.SSLEngineImpl.recvAlert(SSLEngineImpl.java:1781)
at sun.security.ssl.SSLEngineImpl.readRecord(SSLEngineImpl.java:1070)
at sun.security.ssl.SSLEngineImpl.readNetRecord(SSLEngineImpl.java:896)
at sun.security.ssl.SSLEngineImpl.unwrap(SSLEngineImpl.java:766)
at javax.net.ssl.SSLEngine.unwrap(SSLEngine.java:624)
at io.netty.handler.ssl.SslHandler$SslEngineType$3.unwrap(SslHandler.java:295)
at io.netty.handler.ssl.SslHandler.unwrap(SslHandler.java:1330)
at io.netty.handler.ssl.SslHandler.decodeJdkCompatible(SslHandler.java:1225)
at io.netty.handler.ssl.SslHandler.decode(SslHandler.java:1272)
at io.netty.handler.codec.ByteToMessageDecoder.decodeRemovalReentryProtection(ByteToMessageDecoder.java:502)
at io.netty.handler.codec.ByteToMessageDecoder.callDecode(ByteToMessageDecoder.java:441)
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:278)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:337)
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:359)
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:345)
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:677)
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:612)
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:529)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:491)
at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)
at java.lang.Thread.run(Thread.java:748)
Upvotes: 4
Views: 4651
Reputation: 379
works with spring-boot 2.5.2 and httpclient5
import lombok.extern.slf4j.*;
import org.apache.hc.client5.http.config.*;
import org.apache.hc.client5.http.impl.async.*;
import org.apache.hc.client5.http.impl.nio.*;
import org.apache.hc.client5.http.nio.*;
import org.apache.hc.client5.http.ssl.*;
import org.apache.hc.core5.http.config.*;
import org.apache.hc.core5.http.config.Lookup;
import org.apache.hc.core5.http.nio.ssl.*;
import org.apache.hc.core5.ssl.*;
import org.apache.hc.core5.util.*;
import org.springframework.beans.factory.annotation.*;
import org.springframework.context.annotation.*;
import org.springframework.http.client.reactive.*;
import org.springframework.web.reactive.function.client.*;
import javax.net.ssl.*;
import java.security.*;
import java.util.concurrent.*;
@Slf4j
@Configuration
public class WebClientConfig {
@Value("${webclient.connection-timeout-millis:60000}")
protected long connectionTimeout;
@Value("${webclient.response-timeout-millis:300000}")
protected long responseTimeout;
@Value("${webclient.request-timeout-millis:300000}")
protected long requestTimeout;
@Bean
public WebClient webClient() throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException {
RequestConfig config = RequestConfig.custom()
.setConnectTimeout(connectionTimeout, TimeUnit.MILLISECONDS)
.setConnectionRequestTimeout(requestTimeout, TimeUnit.MILLISECONDS)
.setResponseTimeout(responseTimeout, TimeUnit.MILLISECONDS)
.setHardCancellationEnabled(true)
.setConnectionKeepAlive(TimeValue.ofHours(1))
.build();
final SSLContext sslcontext = SSLContexts.custom()
.loadTrustMaterial(null, new TrustAllStrategy())
.build();
final Lookup<TlsStrategy> tlsStrategyLookup = RegistryBuilder.<TlsStrategy>create()
.register("https", new DefaultClientTlsStrategy(sslcontext))
.build();
final AsyncClientConnectionManager cm = new PoolingAsyncClientConnectionManager(tlsStrategyLookup);
CloseableHttpAsyncClient client = HttpAsyncClients
.custom()
.useSystemProperties()
.setConnectionManager(cm)
.setDefaultRequestConfig(config)
.build();
// init spring-boot wrapper for httpclient5
WebClient wc = WebClient
.builder()
.clientConnector(new HttpComponentsClientHttpConnector(client))
.build();
return wc;
}
}
Upvotes: 0
Reputation: 91
This one might work in your case,
# The ciphers which are needed
val allowedCiphers = listOf("TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_DHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384")
@Bean
fun webClient(): WebClient {
val sslContext = SslContextBuilder
.forClient()
.protocols("SSLv3","TLSv1","TLSv1.1","TLSv1.2")
.ciphers(allowedCiphers)
.build();
val httpClient = HttpClient.create()
.secure { it.sslContext(sslContext) }
return WebClient.builder()
.clientConnector(ReactorClientHttpConnector(httpClient))
.baseUrl(baseUrl)
.build()
}
Upvotes: 4