I have an application that uses openjdk-11.0.1 java.net.HttpClient to make a multipart POST request to a REST(ish) endpoint. That endpoint blocks the response while it does whatever work, and when the work is done, it returns the response. The nature of this endpoint is such that a response can take anywhere from 1s to 1d, so this connection can be held for a very long time.
So anyway, recently I've started to encounter the below error, even though the process on the target server still seems to be executing just fine. Does anyone have any ideas about possible causes to this error?
Caused by: java.util.concurrent.ExecutionException: java.io.IOException: HTTP/1.1 header parser received no bytes
at java.base/java.util.concurrent.CompletableFuture.reportGet(CompletableFuture.java:395)
at java.base/java.util.concurrent.CompletableFuture.get(CompletableFuture.java:1999)
at com.company.perf.rdp.profile.DatasetProfiler.toJson(DatasetProfiler.java:190)
... 9 common frames omitted
Caused by: java.io.IOException: HTTP/1.1 header parser received no bytes
at java.net.http/jdk.internal.net.http.common.Utils.wrapWithExtraDetail(Utils.java:293)
at java.net.http/jdk.internal.net.http.Http1Response$HeadersReader.onReadError(Http1Response.java:657)
at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.checkForErrors(Http1AsyncReceiver.java:297)
at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.flush(Http1AsyncReceiver.java:263)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SynchronizedRestartableTask.run(SequentialScheduler.java:175)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:147)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:198)
at java.net.http/jdk.internal.net.http.HttpClientImpl$DelegatingExecutor.execute(HttpClientImpl.java:153)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:273)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:242)
at java.net.http/jdk.internal.net.http.Http1AsyncReceiver.onReadError(Http1AsyncReceiver.java:506)
at java.net.http/jdk.internal.net.http.Http1AsyncReceiver$Http1TubeSubscriber.onComplete(Http1AsyncReceiver.java:591)
at java.net.http/jdk.internal.net.http.common.SSLTube$DelegateWrapper.onComplete(SSLTube.java:268)
at java.net.http/jdk.internal.net.http.common.SSLTube$SSLSubscriberWrapper.complete(SSLTube.java:411)
at java.net.http/jdk.internal.net.http.common.SSLTube$SSLSubscriberWrapper.onComplete(SSLTube.java:540)
at java.net.http/jdk.internal.net.http.common.SubscriberWrapper.checkCompletion(SubscriberWrapper.java:443)
at java.net.http/jdk.internal.net.http.common.SubscriberWrapper$DownstreamPusher.run1(SubscriberWrapper.java:322)
at java.net.http/jdk.internal.net.http.common.SubscriberWrapper$DownstreamPusher.run(SubscriberWrapper.java:261)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SynchronizedRestartableTask.run(SequentialScheduler.java:175)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:147)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:198)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:271)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler.runOrSchedule(SequentialScheduler.java:224)
at java.net.http/jdk.internal.net.http.common.SubscriberWrapper.outgoing(SubscriberWrapper.java:234)
at java.net.http/jdk.internal.net.http.common.SSLFlowDelegate$Reader.processData(SSLFlowDelegate.java:467)
at java.net.http/jdk.internal.net.http.common.SSLFlowDelegate$Reader$ReaderDownstreamPusher.run(SSLFlowDelegate.java:263)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SynchronizedRestartableTask.run(SequentialScheduler.java:175)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$CompleteRestartableTask.run(SequentialScheduler.java:147)
at java.net.http/jdk.internal.net.http.common.SequentialScheduler$SchedulableTask.run(SequentialScheduler.java:198)
... 3 common frames omitted
Caused by: java.io.EOFException: EOF reached while reading
... 21 common frames omitted
Here's the creation of my HttpClient:
default HttpClient create() {
final SSLContextBuilder sslContextBuilder;
try {
sslContextBuilder = new SSLContextBuilder().loadTrustMaterial(TrustSelfSignedStrategy.INSTANCE);
} catch (NoSuchAlgorithmException | KeyStoreException e) {
throw new RuntimeException("Unable to build SSLContext", e);
}
// PREVENTS HOST VALIDATION
final Properties props = System.getProperties();
props.setProperty("jdk.internal.httpclient.disableHostnameVerification", Boolean.TRUE.toString());
// SHOULD PREVENT HOST VALIDATION
final SSLParameters sslParams = new SSLParameters();
sslParams.setEndpointIdentificationAlgorithm(null);
try {
final SSLContext sslContext = sslContextBuilder.build();
ignoreExpiredCerts(sslContext);
return HttpClient.newBuilder().version(Version.HTTP_1_1).sslContext(sslContext).sslParameters(sslParams)
.build();
} catch (KeyManagementException | NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to build HttpClient", e);
}
}
private void ignoreExpiredCerts(final SSLContext sslContext) throws KeyManagementException {
TrustManagerFactory tmf;
try {
tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
} catch (final NoSuchAlgorithmException e) {
throw new RuntimeException("Unable to build HttpClient", e);
}
try {
tmf.init((KeyStore) null);
} catch (final KeyStoreException e) {
throw new RuntimeException("Unable to build HttpClient", e);
}
final TrustManager[] trustManagers = tmf.getTrustManagers();
final X509TrustManager origTrustmanager = (X509TrustManager) trustManagers[0];
final AtomicBoolean logged = new AtomicBoolean(false);
final TrustManager[] wrappedTrustManagers = new TrustManager[] { new X509TrustManager() {
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return origTrustmanager.getAcceptedIssuers();
}
#Override
public void checkClientTrusted(final X509Certificate[] certs, final String authType)
throws CertificateException {
origTrustmanager.checkClientTrusted(certs, authType);
}
#Override
public void checkServerTrusted(final X509Certificate[] certs, final String authType)
throws CertificateException {
try {
origTrustmanager.checkServerTrusted(certs, authType);
} catch (final CertificateExpiredException e) {
if (!logged.get()) {
LOGGER.warn("Server certificate expired", e);
logged.set(true);
}
} catch (final Exception e) {
if (e.getCause() != null && e.getCause().getCause() != null
&& e.getCause().getCause() instanceof CertificateExpiredException) {
if (!logged.get()) {
LOGGER.warn("Server certificate expired", e.getCause().getCause());
logged.set(true);
}
} else {
throw e;
}
}
}
} };
sslContext.init(null, wrappedTrustManagers, null);
}
update:
i did not find an answer to this. upgrading to newest java version helps (8 vs 14 changes a lot under the hood for java httpclient).
additionally, this response often is generated by the downstream server, not the client. meaning that the server to which i was connecting closed the connection. we think this is because the connection is detected as "idle" while transferring large volumes of data.
for older java httpclients, turning off the keepalive seems to help this issue as well. before instantiating your httpclient, make these calls:
// ALLOWS CONNECTION CLOSE HEADER
props.setProperty("jdk.httpclient.allowRestrictedHeaders", EnumSet.allOf(HttpHeaders.class).stream().filter(HttpHeaders::isRestricted).map(HttpHeaders::getName).map(StringUtils::lowerCase).collect(Collectors.joining(",")));
// TURN IT OFF
props.setProperty("jdk.httpclient.keepalive.timeout", "0");
on each request, i close the connection:
// CLOSE SOCKET AFTER EACH REQUEST
requestBuilder.header(HttpHeaders.CONNECTION.getName(), "close");
the referenced HttpHeaders class:
package com.company.perf.api.http;
import org.apache.commons.lang3.StringUtils;
import com.company.api.http.client.IMakeHttpRequests;
public enum HttpHeaders {
ACCEPT("Accept"),
ACCEPT_CHARSET("Accept-Charset"),
ACCEPT_ENCODING("Accept-Encoding"),
ACCEPT_LANGUAGE("Accept-Language"),
ALLOW("Allow"),
AUTHORIZATION("Authorization"),
BOUNDARY("boundary"),
CACHE_CONTROL("Cache-Control"),
CONNECTION("Connection", true),
CONTENT_DISPOSITION("Content-Disposition"),
CONTENT_ENCODING("Content-Encoding"),
CONTENT_ID("Content-ID"),
CONTENT_LANGUAGE("Content-Language"),
CONTENT_LENGTH("Content-Length", true),
CONTENT_LOCATION("Content-Location"),
CONTENT_TYPE("Content-Type"),
COOKIE("Cookie"),
DATE("Date"),
ETAG("ETag"),
EXPECT("Expect", true),
EXPIRES("Expires"),
HOST("Host", true),
IF_MATCH("If-Match"),
IF_MODIFIED_SINCE("If-Modified-Since"),
IF_NONE_MATCH("If-None-Match"),
IF_UNMODIFIED_SINCE("If-Unmodified-Since"),
LAST_MODIFIED("Last-Modified"),
LINK("Link"),
LOCATION("Location"),
RETRY_AFTER("Retry-After"),
SET_COOKIE("Set-Cookie"),
UPGRADE("Upgrade", true),
USER_AGENT("User-Agent"),
VARY("Vary"),
WWW_AUTHENTICATE("WWW-Authenticate");
private final String name;
private final boolean restricted;
private HttpHeaders(final String name, final boolean restricted) {
assert StringUtils.isNotBlank(name) : "name cannot be blank";
this.name = name;
this.restricted = restricted;
}
private HttpHeaders(final String name) {
this(name, false);
}
public String getName() {
return this.name;
}
/**
* #return whether or not this header's use is restricted by the
* {#link IMakeHttpRequests Java HTTP Client}
*/
public boolean isRestricted() {
return this.restricted;
}
}
Related
i had a code which app can access https, just like this :
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
try {
chain[0].checkValidity();
} catch (Exception e) {
throw new CertificateException("Certificate not valid or trusted.");
}
}
public boolean isClientTrusted(X509Certificate[] chain) {
return true;
}
public boolean isServerTrusted(X509Certificate[] chain) {
return true;
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return _AcceptedIssuers;
}
public static void allowAllSSL() {
HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String arg0, SSLSession arg1) {
return true;
}
});
SSLContext context = null;
if (trustManagers == null) {
trustManagers = new TrustManager[]{new HttpsTrustManager()};
}
try {
context = SSLContext.getInstance("TLS");
context.init(null, trustManagers, new SecureRandom());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context
.getSocketFactory());
}
}
after that, google play console give me a notice :
Apache Trust Manager
Your app is using an unsafe implementation of the X509TrustManager interface with an Apache HTTP client, resulting in a security vulnerability. Please see this Google Help Center article for details, including the deadline for fixing the vulnerability.
Insecure Hostname Verifier
Your app is using an unsafe implementation of HostnameVerifier. Please see this Google Help Center article for details, including the deadline for fixing the vulnerability.
is there something wrong with my code ??
I have been searching how to implement Conscrypt SSL provider using conscrypt-openjdk-uber-1.4.1.jar for jdk8 to support ALPN for making a http2(using apache httpclient 5) connection to a server as jdk8 does not support ALPN by default or the other solution is to migrate to jdk9(or higher) which is not feasible for now as our product is heavily dependent on jdk8
I have been searching extensively for some docs or examples to implement but I could not find one.
I have tried to insert conscrypt provider as default and my program takes it as default provider but still it fails to connect with http2 server, my example is as follows,
public static void main(final String[] args) throws Exception {
Security.insertProviderAt(new OpenSSLProvider(), 1);
final SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(new TrustAllStrategy()).build();
final PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create().setTlsStrategy(new H2TlsStrategy(sslContext, NoopHostnameVerifier.INSTANCE)).build();
final IOReactorConfig ioReactorConfig = IOReactorConfig.custom().setSoTimeout(Timeout.ofSeconds(5)).build();
final MinimalHttpAsyncClient client = HttpAsyncClients.createMinimal(HttpVersionPolicy.FORCE_HTTP_2, H2Config.DEFAULT, null, ioReactorConfig, connectionManager);
client.start();
final HttpHost target = new HttpHost("localhost", 8082, "https");
final Future<AsyncClientEndpoint> leaseFuture = client.lease(target, null);
final AsyncClientEndpoint endpoint = leaseFuture.get(10, TimeUnit.SECONDS);
try {
String[] requestUris = new String[] {"/"};
CountDownLatch latch = new CountDownLatch(requestUris.length);
for (final String requestUri: requestUris) {
SimpleHttpRequest request = SimpleHttpRequest.get(target, requestUri);
endpoint.execute(SimpleRequestProducer.create(request), SimpleResponseConsumer.create(), new FutureCallback<SimpleHttpResponse>() {
#Override
public void completed(final SimpleHttpResponse response) {
latch.countDown();
System.out.println(requestUri + "->" + response.getCode());
System.out.println(response.getBody());
}
#Override
public void failed(final Exception ex) {
latch.countDown();
System.out.println(requestUri + "->" + ex);
ex.printStackTrace();
}
#Override
public void cancelled() {
latch.countDown();
System.out.println(requestUri + " cancelled");
}
});
}
latch.await();
} catch (Exception e) {
e.printStackTrace();
}finally {
endpoint.releaseAndReuse();
}
client.shutdown(ShutdownType.GRACEFUL);
}
this programs gives the output as
org.apache.hc.core5.http.ConnectionClosedException: Connection closed
org.apache.hc.core5.http.ConnectionClosedException: Connection closed
at org.apache.hc.core5.http2.impl.nio.FrameInputBuffer.read(FrameInputBuffer.java:146)
at org.apache.hc.core5.http2.impl.nio.AbstractHttp2StreamMultiplexer.onInput(AbstractHttp2StreamMultiplexer.java:415)
at org.apache.hc.core5.http2.impl.nio.AbstractHttp2IOEventHandler.inputReady(AbstractHttp2IOEventHandler.java:63)
at org.apache.hc.core5.http2.impl.nio.ClientHttp2IOEventHandler.inputReady(ClientHttp2IOEventHandler.java:38)
at org.apache.hc.core5.reactor.InternalDataChannel.onIOEvent(InternalDataChannel.java:117)
at org.apache.hc.core5.reactor.InternalChannel.handleIOEvent(InternalChannel.java:50)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.processEvents(SingleCoreIOReactor.java:173)
at org.apache.hc.core5.reactor.SingleCoreIOReactor.doExecute(SingleCoreIOReactor.java:123)
at org.apache.hc.core5.reactor.AbstractSingleCoreIOReactor.execute(AbstractSingleCoreIOReactor.java:80)
at org.apache.hc.core5.reactor.IOReactorWorker.run(IOReactorWorker.java:44)
at java.lang.Thread.run(Thread.java:748)
If I print the provider and version it prints as Conscrypt version 1.0 and JDK 1.8.0_162, but still it fails to connect with a http2 endpoint
same chunk of code works perfectly if I connect using jdk9 with default provider, what I m missing here in conscrypt configuration?
Any help is appreciated
Thanks in advance
Just replacing the default JSSE provider with Conscrypt is not enough. One also needs a custom TlsStrategy that can take advantage of Conscrypt APIs.
This what works for me with Java 1.8 and Conscrypt 1.4.1
static class ConscriptClientTlsStrategy implements TlsStrategy {
private final SSLContext sslContext;
public ConscriptClientTlsStrategy(final SSLContext sslContext) {
this.sslContext = Args.notNull(sslContext, "SSL context");
}
#Override
public boolean upgrade(
final TransportSecurityLayer tlsSession,
final HttpHost host,
final SocketAddress localAddress,
final SocketAddress remoteAddress,
final Object attachment) {
final String scheme = host != null ? host.getSchemeName() : null;
if (URIScheme.HTTPS.same(scheme)) {
tlsSession.startTls(
sslContext,
host,
SSLBufferMode.STATIC,
(endpoint, sslEngine) -> {
final SSLParameters sslParameters = sslEngine.getSSLParameters();
sslParameters.setProtocols(H2TlsSupport.excludeBlacklistedProtocols(sslParameters.getProtocols()));
sslParameters.setCipherSuites(H2TlsSupport.excludeBlacklistedCiphers(sslParameters.getCipherSuites()));
H2TlsSupport.setEnableRetransmissions(sslParameters, false);
final HttpVersionPolicy versionPolicy = attachment instanceof HttpVersionPolicy ?
(HttpVersionPolicy) attachment : HttpVersionPolicy.NEGOTIATE;
final String[] appProtocols;
switch (versionPolicy) {
case FORCE_HTTP_1:
appProtocols = new String[] { ApplicationProtocols.HTTP_1_1.id };
break;
case FORCE_HTTP_2:
appProtocols = new String[] { ApplicationProtocols.HTTP_2.id };
break;
default:
appProtocols = new String[] { ApplicationProtocols.HTTP_2.id, ApplicationProtocols.HTTP_1_1.id };
}
if (Conscrypt.isConscrypt(sslEngine)) {
sslEngine.setSSLParameters(sslParameters);
Conscrypt.setApplicationProtocols(sslEngine, appProtocols);
} else {
H2TlsSupport.setApplicationProtocols(sslParameters, appProtocols);
sslEngine.setSSLParameters(sslParameters);
}
},
(endpoint, sslEngine) -> {
if (Conscrypt.isConscrypt(sslEngine)) {
return new TlsDetails(sslEngine.getSession(), Conscrypt.getApplicationProtocol(sslEngine));
}
return null;
});
return true;
}
return false;
}
}
public static void main(String[] args) throws Exception {
final SSLContext sslContext = SSLContexts.custom()
.setProvider(Conscrypt.newProvider())
.build();
final PoolingAsyncClientConnectionManager cm = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(new ConscriptClientTlsStrategy(sslContext))
.build();
try (CloseableHttpAsyncClient client = HttpAsyncClients.custom()
.setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
.setConnectionManager(cm)
.build()) {
client.start();
final HttpHost target = new HttpHost("nghttp2.org", 443, "https");
final String requestUri = "/httpbin";
final HttpClientContext clientContext = HttpClientContext.create();
final SimpleHttpRequest request = SimpleHttpRequests.GET.create(target, requestUri);
final Future<SimpleHttpResponse> future = client.execute(
SimpleRequestProducer.create(request),
SimpleResponseConsumer.create(),
clientContext,
new FutureCallback<SimpleHttpResponse>() {
#Override
public void completed(final SimpleHttpResponse response) {
System.out.println(requestUri + "->" + response.getCode() + " " +
clientContext.getProtocolVersion());
System.out.println(response.getBody());
final SSLSession sslSession = clientContext.getSSLSession();
if (sslSession != null) {
System.out.println("SSL protocol " + sslSession.getProtocol());
System.out.println("SSL cipher suite " + sslSession.getCipherSuite());
}
}
#Override
public void failed(final Exception ex) {
System.out.println(requestUri + "->" + ex);
}
#Override
public void cancelled() {
System.out.println(requestUri + " cancelled");
}
});
future.get();
System.out.println("Shutting down");
client.shutdown(CloseMode.GRACEFUL);
}
}
There is a ton of examples on how to ignore SSL certificates using Apache HTTPClient; I created a client along the lines of this SO answer. So far so good. Problem is, when a certificate is invalid, the client blindly accepts it, just like I told it too. But I don't want to quietly accept it; I'd like to log a warning of some sort letting me know that an invalid certificate was accepted.
Is there any way to do this?
P.S.: This is for internal tooling, not prod code. I understand and accept the risks of ignoring the certs so, please, don't start a "holier than thou" lecture.
It is just a simple matter of decorating X509TrustManager instances passed to the SSLContext#init method
static class TrustManagerDelegate implements X509TrustManager {
private final X509TrustManager trustManager;
TrustManagerDelegate(final X509TrustManager trustManager) {
super();
this.trustManager = trustManager;
}
#Override
public void checkClientTrusted(
final X509Certificate[] chain, final String authType) throws CertificateException {
trustManager.checkClientTrusted(chain, authType);
}
#Override
public void checkServerTrusted(
final X509Certificate[] chain, final String authType) {
try {
trustManager.checkServerTrusted(chain, authType);
} catch (CertificateException ex) {
// Implement proper logging;
System.out.println(chain[0]);
ex.printStackTrace(System.out);
}
}
#Override
public X509Certificate[] getAcceptedIssuers() {
return trustManager.getAcceptedIssuers();
}
}
...
TrustManagerFactory tmfactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
tmfactory.init((KeyStore) null);
final TrustManager[] tms = tmfactory.getTrustManagers();
if (tms != null) {
for (int i = 0; i < tms.length; i++) {
final TrustManager tm = tms[i];
if (tm instanceof X509TrustManager) {
tms[i] = new TrustManagerDelegate((X509TrustManager) tm);
}
}
}
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, tms, null);
CloseableHttpClient client = HttpClientBuilder.create()
.setSSLContext(sslContext)
.build();
I am using retrofit in android to connect with server.
public class ApiClient {
public static final String BASE_URL = "https://example.com/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit==null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
This is my dev. server and I want to disable certificate check. How can I implement in this code?
ERROR: javax.net.ssl.SSLHandshakeException: java.security.cert.CertPathValidatorException: Trust anchor for certification path not found.
Use this class to get unsafe Retrofit instance. I have included imports to avoid confusion.
import java.security.cert.CertificateException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
import view.utils.AppConstants;
/**
* Created by Hitesh.Sahu on 11/23/2016.
*/
public class NetworkHandler {
public static Retrofit getRetrofit() {
return new Retrofit.Builder()
.baseUrl(AppConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(getUnsafeOkHttpClient())
.build();
}
private static OkHttpClient getUnsafeOkHttpClient() {
try {
// Create a trust manager that does not validate certificate chains
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
// Install the all-trusting trust manager
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
// Create an ssl socket factory with our all-trusting manager
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.sslSocketFactory(sslSocketFactory);
builder.hostnameVerifier(new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true;
}
});
OkHttpClient okHttpClient = builder.build();
return okHttpClient;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
And then simply use retrofit without ssl check like this
private void postFeedbackOnServer() {
MyApiEndpointInterface apiService =
NetworkHandler.getRetrofit().create(MyApiEndpointInterface.class);
Call<ResponseBE> call = apiService.submitFeedbackToServer(requestObject);
Log.e(TAG , "Request is" + new Gson().toJson(requestObject).toString() );
call.enqueue(new Callback<ResponseBE>() {
#Override
public void onResponse(Call<ResponseBE> call, Response<ResponseBE> response) {
int statusCode = response.code();
if (statusCode == HttpURLConnection.HTTP_OK) {
......
} else {
Toast.makeText(FeedbackActivity.this, "Failed to submit Data" + statusCode, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseBE> call, Throwable t) {
// Log error here since request failed
Toast.makeText(FeedbackActivity.this, "Failure" + t.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
}
});
}
The syntax has changed a little since Hitesh Sahu's answer was posted. Now you can use lambdas for some of the methods, remove some throw clauses and chain builder method invocations.
private static OkHttpClient createOkHttpClient() {
try {
final TrustManager[] trustAllCerts = new TrustManager[] {
new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
}
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.hostnameVerifier((hostname, session) -> true)
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
In my case I solved like this with kotlin:
object Instance {
private const val BASE_URL: String = "https://base_url/"
val service: Service by lazy {
Retrofit
.Builder()
.baseUrl(BASE_URL)
.client(getUnsafeOkHttpClient())
.build().create(Service::class.java)
}
private fun getUnsafeOkHttpClient(): OkHttpClient? {
return try {
// Create a trust manager that does not validate certificate chains
val trustAllCerts = arrayOf<TrustManager>(
object : X509TrustManager {
#Throws(CertificateException::class)
override fun checkClientTrusted(
chain: Array<X509Certificate?>?,
authType: String?
) {
}
#Throws(CertificateException::class)
override fun checkServerTrusted(
chain: Array<X509Certificate?>?,
authType: String?
) {
}
override fun getAcceptedIssuers(): Array<X509Certificate?>? {
return arrayOf()
}
}
)
// Install the all-trusting trust manager
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
// Create an ssl socket factory with our all-trusting manager
val sslSocketFactory = sslContext.socketFactory
val trustManagerFactory: TrustManagerFactory =
TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())
trustManagerFactory.init(null as KeyStore?)
val trustManagers: Array<TrustManager> =
trustManagerFactory.trustManagers
check(!(trustManagers.size != 1 || trustManagers[0] !is X509TrustManager)) {
"Unexpected default trust managers:" + trustManagers.contentToString()
}
val trustManager =
trustManagers[0] as X509TrustManager
val builder = OkHttpClient.Builder()
builder.sslSocketFactory(sslSocketFactory, trustManager)
builder.hostnameVerifier(HostnameVerifier { _, _ -> true })
builder.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
}
IMO, you can read Google's documentation - Security with HTTPS and SSL.
About sample code to use Retrofit with your self-signed certificate, please try the following, hope it helps!
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try{
OkHttpClient client = new OkHttpClient.Builder()
.sslSocketFactory(getSSLSocketFactory())
.hostnameVerifier(getHostnameVerifier())
.build();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(API_URL_BASE)
.addConverterFactory(GsonConverterFactory.create())
.client(client)
.build();
WebAPIService service = retrofit.create(WebAPIService.class);
Call<JsonObject> jsonObjectCall = service.getData(...);
...
} catch (Exception e) {
e.printStackTrace();
}
}
// for SSL...
// Read more at https://developer.android.com/training/articles/security-ssl.html#CommonHostnameProbs
private HostnameVerifier getHostnameVerifier() {
return new HostnameVerifier() {
#Override
public boolean verify(String hostname, SSLSession session) {
return true; // verify always returns true, which could cause insecure network traffic due to trusting TLS/SSL server certificates for wrong hostnames
//HostnameVerifier hv = HttpsURLConnection.getDefaultHostnameVerifier();
//return hv.verify("localhost", session);
}
};
}
private TrustManager[] getWrappedTrustManagers(TrustManager[] trustManagers) {
final X509TrustManager originalTrustManager = (X509TrustManager) trustManagers[0];
return new TrustManager[]{
new X509TrustManager() {
public X509Certificate[] getAcceptedIssuers() {
return originalTrustManager.getAcceptedIssuers();
}
public void checkClientTrusted(X509Certificate[] certs, String authType) {
try {
if (certs != null && certs.length > 0){
certs[0].checkValidity();
} else {
originalTrustManager.checkClientTrusted(certs, authType);
}
} catch (CertificateException e) {
Log.w("checkClientTrusted", e.toString());
}
}
public void checkServerTrusted(X509Certificate[] certs, String authType) {
try {
if (certs != null && certs.length > 0){
certs[0].checkValidity();
} else {
originalTrustManager.checkServerTrusted(certs, authType);
}
} catch (CertificateException e) {
Log.w("checkServerTrusted", e.toString());
}
}
}
};
}
private SSLSocketFactory getSSLSocketFactory()
throws CertificateException, KeyStoreException, IOException,
NoSuchAlgorithmException, KeyManagementException {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
InputStream caInput = getResources().openRawResource(R.raw.your_cert); // File path: app\src\main\res\raw\your_cert.cer
Certificate ca = cf.generateCertificate(caInput);
caInput.close();
KeyStore keyStore = KeyStore.getInstance("BKS");
keyStore.load(null, null);
keyStore.setCertificateEntry("ca", ca);
String tmfAlgorithm = TrustManagerFactory.getDefaultAlgorithm();
TrustManagerFactory tmf = TrustManagerFactory.getInstance(tmfAlgorithm);
tmf.init(keyStore);
TrustManager[] wrappedTrustManagers = getWrappedTrustManagers(tmf.getTrustManagers());
SSLContext sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, wrappedTrustManagers, null);
return sslContext.getSocketFactory();
}
...
I tried #whirlwin's solution on this page but that didn't work with java 9+. Some small changes resulted in this:
private static OkHttpClient createTrustingOkHttpClient() {
try {
X509TrustManager x509TrustManager = new X509TrustManager() {
#Override
public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType) {}
#Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return new java.security.cert.X509Certificate[]{};
}
};
final TrustManager[] trustAllCerts = new TrustManager[] {
x509TrustManager
};
final SSLContext sslContext = SSLContext.getInstance("SSL");
sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
return new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), x509TrustManager)
.hostnameVerifier((hostname, session) -> true)
.build();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
And this worked for me as you can imagine. Happy days! Still, be careful when using this.
Adding code for doing same in Kotlin based on #Hitesh Sahu's answer :
fun getRetrofirApiService(currentBaseURL: String): YourAPIService{
val TIMEOUT = 2L
val logging = HttpLoggingInterceptor()
logging.setLevel(HttpLoggingInterceptor.Level.BODY)
val retrofit = Retrofit.Builder()
.baseUrl(currentBaseURL)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(NullOnEmptyConverterFactory())
.addConverterFactory(GsonConverterFactory.create())
.client(createOkHttpClient())
.build()
return retrofit.create(APIService::class.java)
}
Now create Http client for same as shown below :
private fun createOkHttpClient(): OkHttpClient {
return try {
val trustAllCerts: Array<TrustManager> = arrayOf(MyManager())
val sslContext = SSLContext.getInstance("SSL")
sslContext.init(null, trustAllCerts, SecureRandom())
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory())
.addInterceptor(logging)
.hostnameVerifier { hostname: String?, session: SSLSession? -> true }
.build()
} catch (e: Exception) {
throw RuntimeException(e)
}
}
MyManager class is as shown below :
class MyManager : X509TrustManager {
override fun checkServerTrusted(
p0: Array<out java.security.cert.X509Certificate>?,
p1: String?
) {
//allow all
}
override fun checkClientTrusted(
p0: Array<out java.security.cert.X509Certificate>?,
p1: String?
) {
//allow all
}
override fun getAcceptedIssuers(): Array<java.security.cert.X509Certificate> {
return arrayOf()
}
}
Imports for same are as shown below :
import okhttp3.MediaType
import okhttp3.OkHttpClient
import okhttp3.RequestBody
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.Result
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import java.security.SecureRandom
import java.util.concurrent.TimeUnit
import javax.net.ssl.SSLContext
import javax.net.ssl.SSLSession
import javax.net.ssl.TrustManager
import javax.net.ssl.X509TrustManager
Implementation of such workaround in code, even for testing purposes is a bad practice.
You can:
Generate your CA.
Sign your certificate with CA.
Add your CA as trusted.
Some links that may be useful:
https://jamielinux.com/docs/openssl-certificate-authority/create-the-root-pair.html
http://wiki.cacert.org/FAQ/ImportRootCert
I want to make a SSL connection as given on this http://docs.marklogic.com/guide/admin/SSL
But I'm getting the following exception:
Exception in thread "main"
com.marklogic.xcc.exceptions.ServerConnectionException: Unrecognized
SSL message, plaintext connection? [Session: user=demo, cb=Arg
[ContentSource: user=demo, cb=Arg [provider: SSLconn
address=localhost/127.0.0.1:9470, pool=0/64]]] [Client: XCC/7.0-2]
at
com.marklogic.xcc.impl.handlers.AbstractRequestController.runRequest(AbstractRequestController.java:124)
at
com.marklogic.xcc.impl.SessionImpl.submitRequestInternal(SessionImpl.java:388)
at
com.marklogic.xcc.impl.SessionImpl.submitRequest(SessionImpl.java:371)
at com.demoe2.MarklogicDemo.main(MarklogicDemo.java:41) Caused by:
javax.net.ssl.SSLException: Unrecognized SSL message, plaintext
connection? at
sun.security.ssl.EngineInputRecord.bytesInCompletePacket(Unknown
Source) at sun.security.ssl.SSLEngineImpl.readNetRecord(Unknown
Source) at sun.security.ssl.SSLEngineImpl.unwrap(Unknown Source
My source code is for Java
public class MarklogicDemo {
public static void main(String[] args) throws Exception {
URI uri = new URI("xcc://demo:password#localhost:9470/Arg");
query = "for $x in cts:search(//PLAY,cts:element-word-query(xs:QName(\"LINE\"),\"King\")) return ($x//TITLE)";
ContentSource con =
ContentSourceFactory.newContentSource(
"localhost", 9470,
"demo", "password",
"Arg", newTrustOptions());
Session see = con.newSession();
Request req = see.newAdhocQuery(query);
ResultSequence rs = see.submitRequest (req);
System.out.println (rs.asString());
see.close();
}
protected static SecurityOptions newTrustOptions() throws Exception {
TrustManager[] trust =
new TrustManager[] {
new X509TrustManager() {
public void checkClientTrusted(
X509Certificate[] x509Certificates,
String s) throws CertificateException {
}
public void checkServerTrusted(
X509Certificate[] x509Certificates,
String s) throws CertificateException {
}
public X509Certificate[] getAcceptedIssuers() {
return null;
}
}
};
SSLContext sslContext = SSLContext.getInstance("SSLv3");
sslContext.init(null, trust, null);
return new SecurityOptions(sslContext);
}
}
In the connection URL, use xccs instead of xcc.
You can also look at https://github.com/marklogic/xqsync/blob/1cf82faa2fa2e7fb3fa06f41e0938bf5b002b7fa/src/java/com/marklogic/ps/Connection.java#L100 to see an example that handles both xcc and xccs.