How to made SSL connection for MarkLogic 7? - java

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.

Related

SSLSocketFactory in Java, LDAP network connection

My question is similar to: SSLSocketFactory in java
I need to set a custom SSLSocketFactory...except I do NOT have an https connection (it's LDAPS), so can't use:
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
...to set the SSLSocketFactory. I have an SSLContext object initialized but when I make the LDAP connection the default SSLContext is called automatically since my custom one is not set:
dirContext = new InitialDirContext(env); // <-- reverts to default ssl context
Is there a non-HTTPS equivalent method to line #3 below:
SSLContext sc = SSLContext.getInstance("SSL");
sc.init(myKeyManagerFactory.getKeyManagers(), myTrustManagerArray, new java.security.SecureRandom());
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());
Yes, there is.
env.put("java.naming.ldap.factory.socket", UnsecuredSSLSocketFactory.class.getName());
UnsecuredSSLSocketFactory.java:
public class UnsecuredSSLSocketFactory extends SSLSocketFactory
{
private SSLSocketFactory socketFactory;
public UnsecuredSSLSocketFactory()
{
try
{
var sslContext = SSLContext.getInstance("TLS");
sslContext.init(null, new TrustManager[]{new X509TrustManager()
{
#Override
public void checkClientTrusted(X509Certificate[] xcs, String string){}
#Override
public void checkServerTrusted(X509Certificate[] xcs, String string){}
#Override
public X509Certificate[] getAcceptedIssuers()
{
return null;
}
}}, new SecureRandom());
socketFactory = sslContext.getSocketFactory();
}
catch(Exception e)
{
throw new RuntimeException(e);
}
}
#SuppressWarnings("unused")
public static SocketFactory getDefault()
{
return new UnsecuredSSLSocketFactory();
}
#Override
public String[] getDefaultCipherSuites()
{
return socketFactory.getDefaultCipherSuites();
}
#Override
public String[] getSupportedCipherSuites()
{
return socketFactory.getSupportedCipherSuites();
}
#Override
public Socket createSocket(Socket socket, String string, int i, boolean bln) throws IOException
{
return socketFactory.createSocket(socket, string, i, bln);
}
#Override
public Socket createSocket(String string, int i) throws IOException
{
return socketFactory.createSocket(string, i);
}
#Override
public Socket createSocket(String string, int i, InetAddress ia, int i1) throws IOException
{
return socketFactory.createSocket(string, i, ia, i1);
}
#Override
public Socket createSocket(InetAddress ia, int i) throws IOException
{
return socketFactory.createSocket(ia, i);
}
#Override
public Socket createSocket(InetAddress ia, int i, InetAddress ia1, int i1) throws IOException
{
return socketFactory.createSocket(ia, i, ia1, i1);
}
#Override
public Socket createSocket() throws IOException
{
return socketFactory.createSocket();
}
}
Note, if the issue is just a hostname mismatch (which is super common in clustered Active Directory Environments), you can just set the system property com.sun.jndi.ldap.object.disableEndpointIdentification to true, so as a command line arg -Dcom.sun.jndi.ldap.object.disableEndpointIdentification=true
Note this will only ignore a hostname mismatch on the certificate, you will still need to have a trust chain from ldap's cert to something in your truststore, but this seems to be the most common issue people have with SSL, LDAP and Active Directory, as the certificate's the domain generate for each domain controller don't include a subject alternate name for the domain itself, so if you follow the standard example of just pointing ldap to yourcomapanydomain.com, when it resolves to domaincontroller1.yourcompanydomain.com you get a failure. Note, if you are upgrading from an old java version, this behavior changed in https://www.oracle.com/java/technologies/javase/8u181-relnotes.html

java.net.HttpClient fails with EOFException while making POST

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;
}
}

How to ignore SSL certificate error using Apache HTTPClient but log it

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();

javax.net.ssl.SSLException: hostname in certificate didn't match

My Android app tells me that my https certificate doesn't match the hostname:
javax.net.ssl.SSLException: hostname in certificate didn't match: <hostname1> != <oldhostname>
What is odd is that
The website (hostname1) gives the correct certificate (checked with browsers and the ssllabs tool)
oldhostname is the previous hostname I had set up in previous versions of the app
Is there some kind of cache for certificates? I cant't find any info on that
Add this class
public class HttpsTrustManager implements X509TrustManager {
private static TrustManager[] trustManagers;
private static final X509Certificate[] _AcceptedIssuers = new X509Certificate[]{};
#Override
public void checkClientTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
#Override
public void checkServerTrusted(
X509Certificate[] x509Certificates, String s)
throws java.security.cert.CertificateException {
}
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 | KeyManagementException e) {
e.printStackTrace();
}
HttpsURLConnection.setDefaultSSLSocketFactory(context != null ? context.getSocketFactory() : null);
}
}
and call it from your MainActivity with HttpsTrustManager.allowAllSSL();
Although it's not save approach but i solve my problem with this.

Grizzly 2 Embedded Https Server?

I'm trying to get a simple https embedded server running. This is for a test prototype, so real security isn't important at this point and fake or omitted certificates are fine, but my situation requires https vs regular http.
This code seems to call the right APIs, it runs, but the client browser gets "SSL Connection Error":
public class SimpleHttps {
public static final String SERVER_NAME = "https://localhost:8090";
public static final URI BASE_URI = URI.create(SERVER_NAME);
private static class TrustAllCerts implements X509TrustManager {
#Override public void checkClientTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { }
#Override public void checkServerTrusted(X509Certificate[] x509Certificates, String s) throws CertificateException { }
#Override public X509Certificate[] getAcceptedIssuers() { return new X509Certificate[0]; }
}
private final static TrustManager[] trustAllCerts = new TrustManager[] { new TrustAllCerts() };
public static SSLEngineConfigurator getSslEngineConfig() throws KeyManagementException, NoSuchAlgorithmException {
SSLContext sc = SSLContext.getInstance("TLS");
sc.init(null, trustAllCerts, new SecureRandom());
SSLEngineConfigurator sslEngineConfigurator = new SSLEngineConfigurator(sc, false, false, false);
return sslEngineConfigurator;
}
public static void main(String[] args) throws Exception {
final ResourceConfig rc = new ResourceConfig().register(MyResource.class);
HttpServer httpServer = GrizzlyHttpServerFactory.createHttpServer(BASE_URI, rc, true, getSslEngineConfig());
System.in.read();
httpServer.stop();
}
}

Categories