keycloak-aplcache

KEYCLOAK-1881 Clone OIDC adapter HttpClient tools to SAML

11/4/2016 6:05:35 AM

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java
index 63cdab8..e6d6588 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/HttpClientBuilder.java
@@ -38,7 +38,7 @@ import org.apache.http.params.BasicHttpParams;
 import org.apache.http.params.HttpConnectionParams;
 import org.keycloak.common.util.EnvUtil;
 import org.keycloak.common.util.KeystoreUtil;
-import org.keycloak.representations.adapters.config.AdapterConfig;
+import org.keycloak.representations.adapters.config.AdapterHttpClientConfig;
 
 import javax.net.ssl.HostnameVerifier;
 import javax.net.ssl.SSLContext;
@@ -333,7 +333,7 @@ public class HttpClientBuilder {
         }
     }
 
-    public HttpClient build(AdapterConfig adapterConfig) {
+    public HttpClient build(AdapterHttpClientConfig adapterConfig) {
         disableCookieCache(); // disable cookie cache as we don't want sticky sessions for load balancing
 
         String truststorePath = adapterConfig.getTruststore();
@@ -379,13 +379,13 @@ public class HttpClientBuilder {
     /**
      * Configures a the proxy to use for auth-server requests if provided.
      * <p>
-     * If the given {@link AdapterConfig} contains the attribute {@code proxy-url} we use the
+     * If the given {@link AdapterHttpClientConfig} contains the attribute {@code proxy-url} we use the
      * given URL as a proxy server, otherwise the proxy configuration is ignored.
      * </p>
      *
      * @param adapterConfig
      */
-    private void configureProxyForAuthServerIfProvided(AdapterConfig adapterConfig) {
+    private void configureProxyForAuthServerIfProvided(AdapterHttpClientConfig adapterConfig) {
 
         if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
             return;
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/AdapterHttpClientConfig.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/AdapterHttpClientConfig.java
new file mode 100644
index 0000000..5c94fdb
--- /dev/null
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/AdapterHttpClientConfig.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.cloned;
+
+/**
+ * Configuration options relevant for configuring http client that can be used by adapter.
+ *
+ * NOTE: keep in sync with core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java until unified.
+ *
+ * @author hmlnarik
+ */
+public interface AdapterHttpClientConfig {
+
+    /**
+     * Returns truststore filename.
+     */
+    public String getTruststore();
+
+    /**
+     * Returns truststore password.
+     */
+    public String getTruststorePassword();
+
+    /**
+     * Returns keystore with client keys.
+     */
+    public String getClientKeystore();
+
+    /**
+     * Returns keystore password.
+     */
+    public String getClientKeystorePassword();
+
+    /**
+     * Returns boolean flag whether any hostname verification is done on the server's
+     * certificate, {@code true} means that verification is not done.
+     * @return
+     */
+    public boolean isAllowAnyHostname();
+
+    /**
+     * Returns boolean flag whether any trust management and hostname verification is done.
+     * <p>
+     * <i>NOTE</i> Disabling trust manager is a security hole, so only set this option
+     * if you cannot or do not want to verify the identity of the
+     * host you are communicating with.
+     */
+    public boolean isDisableTrustManager();
+
+    /**
+     * Returns size of connection pool.
+     */
+    public int getConnectionPoolSize();
+
+    /**
+     * Returns URL of HTTP proxy.
+     */
+    public String getProxyUrl();
+
+}
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpAdapterUtils.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpAdapterUtils.java
new file mode 100644
index 0000000..0fa330e
--- /dev/null
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpAdapterUtils.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.cloned;
+
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+
+import java.io.IOException;
+import java.io.InputStream;
+import javax.xml.crypto.dsig.keyinfo.KeyInfo;
+import org.apache.http.HttpStatus;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.util.EntityUtils;
+import org.keycloak.adapters.saml.descriptor.parsers.SamlDescriptorIDPKeysExtractor;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.saml.common.exceptions.ParsingException;
+
+/**
+ * @author <a href="mailto:hmlnarik@redhat.com">Hynek Mlnařík</a>
+ */
+public class HttpAdapterUtils {
+
+    public static MultivaluedHashMap<String, KeyInfo> downloadKeysFromSamlDescriptor(HttpClient client, String descriptorUrl) throws HttpClientAdapterException {
+        try {
+            HttpGet httpRequest = new HttpGet(descriptorUrl);
+            HttpResponse response = client.execute(httpRequest);
+            int status = response.getStatusLine().getStatusCode();
+            if (status != HttpStatus.SC_OK) {
+                EntityUtils.consumeQuietly(response.getEntity());
+                throw new HttpClientAdapterException("Unexpected status = " + status);
+            }
+
+            HttpEntity entity = response.getEntity();
+            if (entity == null) {
+                throw new HttpClientAdapterException("There was no entity.");
+            }
+
+            MultivaluedHashMap<String, KeyInfo> res;
+            try (InputStream is = entity.getContent()) {
+                res = extractKeysFromSamlDescriptor(is);
+            }
+
+            EntityUtils.consumeQuietly(entity);
+
+            return res;
+        } catch (IOException | ParsingException e) {
+            throw new HttpClientAdapterException("IO error", e);
+        }
+    }
+
+    /**
+     * Parses SAML descriptor and extracts keys from it.
+     * @param xmlStream
+     * @return List of KeyInfo objects containing keys from the descriptor.
+     * @throws IOException
+     */
+    public static MultivaluedHashMap<String, KeyInfo> extractKeysFromSamlDescriptor(InputStream xmlStream) throws ParsingException {
+        Object res = new SamlDescriptorIDPKeysExtractor().parse(xmlStream);
+        return (MultivaluedHashMap<String, KeyInfo>) res;
+    }
+
+}
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientAdapterException.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientAdapterException.java
new file mode 100644
index 0000000..e0371ad
--- /dev/null
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientAdapterException.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.cloned;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class HttpClientAdapterException extends Exception {
+
+    public HttpClientAdapterException(String message) {
+        super(message);
+    }
+
+    public HttpClientAdapterException(String message, Throwable t) {
+        super(message, t);
+    }
+}
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientBuilder.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientBuilder.java
new file mode 100644
index 0000000..7e26c01
--- /dev/null
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/HttpClientBuilder.java
@@ -0,0 +1,396 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.cloned;
+
+import org.apache.http.HttpHost;
+import org.apache.http.client.CookieStore;
+import org.apache.http.client.HttpClient;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.apache.http.conn.scheme.PlainSocketFactory;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.scheme.SchemeRegistry;
+import org.apache.http.conn.ssl.AllowAllHostnameVerifier;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.StrictHostnameVerifier;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.cookie.Cookie;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.conn.SingleClientConnManager;
+import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpConnectionParams;
+import org.keycloak.common.util.EnvUtil;
+import org.keycloak.common.util.KeystoreUtil;
+
+import javax.net.ssl.HostnameVerifier;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLException;
+import javax.net.ssl.SSLSession;
+import javax.net.ssl.SSLSocket;
+import javax.net.ssl.TrustManager;
+import javax.net.ssl.X509TrustManager;
+import java.io.IOException;
+import java.net.URI;
+import java.security.KeyStore;
+import java.security.SecureRandom;
+import java.security.cert.CertificateException;
+import java.security.cert.X509Certificate;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Abstraction for creating HttpClients. Allows SSL configuration.
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class HttpClientBuilder {
+    public static enum HostnameVerificationPolicy {
+        /**
+         * Hostname verification is not done on the server's certificate
+         */
+        ANY,
+        /**
+         * Allows wildcards in subdomain names i.e. *.foo.com
+         */
+        WILDCARD,
+        /**
+         * CN must match hostname connecting to
+         */
+        STRICT
+    }
+
+
+    /**
+     * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+     * @version $Revision: 1 $
+     */
+    private static class PassthroughTrustManager implements X509TrustManager {
+        public void checkClientTrusted(X509Certificate[] chain,
+                                       String authType) throws CertificateException {
+        }
+
+        public void checkServerTrusted(X509Certificate[] chain,
+                                       String authType) throws CertificateException {
+        }
+
+        public X509Certificate[] getAcceptedIssuers() {
+            return null;
+        }
+    }
+
+    protected KeyStore truststore;
+    protected KeyStore clientKeyStore;
+    protected String clientPrivateKeyPassword;
+    protected boolean disableTrustManager;
+    protected boolean disableCookieCache = true;
+    protected HostnameVerificationPolicy policy = HostnameVerificationPolicy.WILDCARD;
+    protected SSLContext sslContext;
+    protected int connectionPoolSize = 100;
+    protected int maxPooledPerRoute = 0;
+    protected long connectionTTL = -1;
+    protected TimeUnit connectionTTLUnit = TimeUnit.MILLISECONDS;
+    protected HostnameVerifier verifier = null;
+    protected long socketTimeout = -1;
+    protected TimeUnit socketTimeoutUnits = TimeUnit.MILLISECONDS;
+    protected long establishConnectionTimeout = -1;
+    protected TimeUnit establishConnectionTimeoutUnits = TimeUnit.MILLISECONDS;
+    protected HttpHost proxyHost;
+
+
+    /**
+     * Socket inactivity timeout
+     *
+     * @param timeout
+     * @param unit
+     * @return
+     */
+    public HttpClientBuilder socketTimeout(long timeout, TimeUnit unit) {
+        this.socketTimeout = timeout;
+        this.socketTimeoutUnits = unit;
+        return this;
+    }
+
+    /**
+     * When trying to make an initial socket connection, what is the timeout?
+     *
+     * @param timeout
+     * @param unit
+     * @return
+     */
+    public HttpClientBuilder establishConnectionTimeout(long timeout, TimeUnit unit) {
+        this.establishConnectionTimeout = timeout;
+        this.establishConnectionTimeoutUnits = unit;
+        return this;
+    }
+
+    public HttpClientBuilder connectionTTL(long ttl, TimeUnit unit) {
+        this.connectionTTL = ttl;
+        this.connectionTTLUnit = unit;
+        return this;
+    }
+
+    public HttpClientBuilder maxPooledPerRoute(int maxPooledPerRoute) {
+        this.maxPooledPerRoute = maxPooledPerRoute;
+        return this;
+    }
+
+    public HttpClientBuilder connectionPoolSize(int connectionPoolSize) {
+        this.connectionPoolSize = connectionPoolSize;
+        return this;
+    }
+
+    /**
+     * Disable trust management and hostname verification. <i>NOTE</i> this is a security
+     * hole, so only set this option if you cannot or do not want to verify the identity of the
+     * host you are communicating with.
+     */
+    public HttpClientBuilder disableTrustManager() {
+        this.disableTrustManager = true;
+        return this;
+    }
+
+    public HttpClientBuilder disableCookieCache() {
+        this.disableCookieCache = true;
+        return this;
+    }
+
+    /**
+     * SSL policy used to verify hostnames
+     *
+     * @param policy
+     * @return
+     */
+    public HttpClientBuilder hostnameVerification(HostnameVerificationPolicy policy) {
+        this.policy = policy;
+        return this;
+    }
+
+
+    public HttpClientBuilder sslContext(SSLContext sslContext) {
+        this.sslContext = sslContext;
+        return this;
+    }
+
+    public HttpClientBuilder trustStore(KeyStore truststore) {
+        this.truststore = truststore;
+        return this;
+    }
+
+    public HttpClientBuilder keyStore(KeyStore keyStore, String password) {
+        this.clientKeyStore = keyStore;
+        this.clientPrivateKeyPassword = password;
+        return this;
+    }
+
+    public HttpClientBuilder keyStore(KeyStore keyStore, char[] password) {
+        this.clientKeyStore = keyStore;
+        this.clientPrivateKeyPassword = new String(password);
+        return this;
+    }
+
+
+    static class VerifierWrapper implements X509HostnameVerifier {
+        protected HostnameVerifier verifier;
+
+        VerifierWrapper(HostnameVerifier verifier) {
+            this.verifier = verifier;
+        }
+
+        @Override
+        public void verify(String host, SSLSocket ssl) throws IOException {
+            if (!verifier.verify(host, ssl.getSession())) throw new SSLException("Hostname verification failure");
+        }
+
+        @Override
+        public void verify(String host, X509Certificate cert) throws SSLException {
+            throw new SSLException("This verification path not implemented");
+        }
+
+        @Override
+        public void verify(String host, String[] cns, String[] subjectAlts) throws SSLException {
+            throw new SSLException("This verification path not implemented");
+        }
+
+        @Override
+        public boolean verify(String s, SSLSession sslSession) {
+            return verifier.verify(s, sslSession);
+        }
+    }
+
+    public HttpClient build() {
+        X509HostnameVerifier verifier = null;
+        if (this.verifier != null) verifier = new VerifierWrapper(this.verifier);
+        else {
+            switch (policy) {
+                case ANY:
+                    verifier = new AllowAllHostnameVerifier();
+                    break;
+                case WILDCARD:
+                    verifier = new BrowserCompatHostnameVerifier();
+                    break;
+                case STRICT:
+                    verifier = new StrictHostnameVerifier();
+                    break;
+            }
+        }
+        try {
+            SSLSocketFactory sslsf = null;
+            SSLContext theContext = sslContext;
+            if (disableTrustManager) {
+                theContext = SSLContext.getInstance("SSL");
+                theContext.init(null, new TrustManager[]{new PassthroughTrustManager()},
+                        new SecureRandom());
+                verifier = new AllowAllHostnameVerifier();
+                sslsf = new SniSSLSocketFactory(theContext, verifier);
+            } else if (theContext != null) {
+                sslsf = new SniSSLSocketFactory(theContext, verifier);
+            } else if (clientKeyStore != null || truststore != null) {
+                sslsf = new SniSSLSocketFactory(SSLSocketFactory.TLS, clientKeyStore, clientPrivateKeyPassword, truststore, null, verifier);
+            } else {
+                final SSLContext tlsContext = SSLContext.getInstance(SSLSocketFactory.TLS);
+                tlsContext.init(null, null, null);
+                sslsf = new SniSSLSocketFactory(tlsContext, verifier);
+            }
+            SchemeRegistry registry = new SchemeRegistry();
+            registry.register(
+                    new Scheme("http", 80, PlainSocketFactory.getSocketFactory()));
+            Scheme httpsScheme = new Scheme("https", 443, sslsf);
+            registry.register(httpsScheme);
+            ClientConnectionManager cm = null;
+            if (connectionPoolSize > 0) {
+                ThreadSafeClientConnManager tcm = new ThreadSafeClientConnManager(registry, connectionTTL, connectionTTLUnit);
+                tcm.setMaxTotal(connectionPoolSize);
+                if (maxPooledPerRoute == 0) maxPooledPerRoute = connectionPoolSize;
+                tcm.setDefaultMaxPerRoute(maxPooledPerRoute);
+                cm = tcm;
+
+            } else {
+                cm = new SingleClientConnManager(registry);
+            }
+            BasicHttpParams params = new BasicHttpParams();
+
+            if (proxyHost != null) {
+                params.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxyHost);
+            }
+
+            if (socketTimeout > -1) {
+                HttpConnectionParams.setSoTimeout(params, (int) socketTimeoutUnits.toMillis(socketTimeout));
+
+            }
+            if (establishConnectionTimeout > -1) {
+                HttpConnectionParams.setConnectionTimeout(params, (int) establishConnectionTimeoutUnits.toMillis(establishConnectionTimeout));
+            }
+            DefaultHttpClient client = new DefaultHttpClient(cm, params);
+
+            if (disableCookieCache) {
+                client.setCookieStore(new CookieStore() {
+                    @Override
+                    public void addCookie(Cookie cookie) {
+                        //To change body of implemented methods use File | Settings | File Templates.
+                    }
+
+                    @Override
+                    public List<Cookie> getCookies() {
+                        return Collections.emptyList();
+                    }
+
+                    @Override
+                    public boolean clearExpired(Date date) {
+                        return false;  //To change body of implemented methods use File | Settings | File Templates.
+                    }
+
+                    @Override
+                    public void clear() {
+                        //To change body of implemented methods use File | Settings | File Templates.
+                    }
+                });
+
+            }
+            return client;
+        } catch (Exception e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public HttpClient build(AdapterHttpClientConfig adapterConfig) {
+        disableCookieCache(); // disable cookie cache as we don't want sticky sessions for load balancing
+
+        String truststorePath = adapterConfig.getTruststore();
+        if (truststorePath != null) {
+            truststorePath = EnvUtil.replace(truststorePath);
+            String truststorePassword = adapterConfig.getTruststorePassword();
+            try {
+                this.truststore = KeystoreUtil.loadKeyStore(truststorePath, truststorePassword);
+            } catch (Exception e) {
+                throw new RuntimeException("Failed to load truststore", e);
+            }
+        }
+        String clientKeystore = adapterConfig.getClientKeystore();
+        if (clientKeystore != null) {
+            clientKeystore = EnvUtil.replace(clientKeystore);
+            String clientKeystorePassword = adapterConfig.getClientKeystorePassword();
+            try {
+                KeyStore clientCertKeystore = KeystoreUtil.loadKeyStore(clientKeystore, clientKeystorePassword);
+                keyStore(clientCertKeystore, clientKeystorePassword);
+            } catch (Exception e) {
+                throw new RuntimeException("Failed to load keystore", e);
+            }
+        }
+        int size = 10;
+        if (adapterConfig.getConnectionPoolSize() > 0)
+            size = adapterConfig.getConnectionPoolSize();
+        HttpClientBuilder.HostnameVerificationPolicy policy = HttpClientBuilder.HostnameVerificationPolicy.WILDCARD;
+        if (adapterConfig.isAllowAnyHostname())
+            policy = HttpClientBuilder.HostnameVerificationPolicy.ANY;
+        connectionPoolSize(size);
+        hostnameVerification(policy);
+        if (adapterConfig.isDisableTrustManager()) {
+            disableTrustManager();
+        } else {
+            trustStore(truststore);
+        }
+
+        configureProxyForAuthServerIfProvided(adapterConfig);
+
+        return build();
+    }
+
+    /**
+     * Configures a the proxy to use for auth-server requests if provided.
+     * <p>
+     * If the given {@link AdapterHttpClientConfig} contains the attribute {@code proxy-url} we use the
+     * given URL as a proxy server, otherwise the proxy configuration is ignored.
+     * </p>
+     *
+     * @param adapterConfig
+     */
+    private void configureProxyForAuthServerIfProvided(AdapterHttpClientConfig adapterConfig) {
+
+        if (adapterConfig == null || adapterConfig.getProxyUrl() == null || adapterConfig.getProxyUrl().trim().isEmpty()) {
+            return;
+        }
+
+        URI uri = URI.create(adapterConfig.getProxyUrl());
+        this.proxyHost = new HttpHost(uri.getHost(), uri.getPort(), uri.getScheme());
+    }
+}
\ No newline at end of file
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/SniSSLSocketFactory.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/SniSSLSocketFactory.java
new file mode 100644
index 0000000..8c064b0
--- /dev/null
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/cloned/SniSSLSocketFactory.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.cloned;
+
+import org.apache.http.HttpHost;
+import org.apache.http.conn.scheme.HostNameResolver;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.TrustStrategy;
+import org.apache.http.conn.ssl.X509HostnameVerifier;
+import org.apache.http.protocol.HttpContext;
+
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.SSLSocket;
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.net.InetSocketAddress;
+import java.net.Socket;
+import java.security.AccessController;
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.security.SecureRandom;
+import java.security.UnrecoverableKeyException;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * SSLSocketFactory that uses Server Name Indication (SNI) TLS extension.
+ *
+ * <p>
+ * Originally copied from <b>keycloak-adapter-core</b> project.
+ *
+ * @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
+ * @author <a href="mailto:hmlnarik@redhat.com">Hynek Mlnařík</a>
+ */
+public class SniSSLSocketFactory extends SSLSocketFactory {
+
+    private static final Logger LOG = Logger.getLogger(SniSSLSocketFactory.class.getName());
+
+    public SniSSLSocketFactory(String algorithm, KeyStore keystore, String keyPassword, KeyStore truststore, SecureRandom random, HostNameResolver nameResolver) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(algorithm, keystore, keyPassword, truststore, random, nameResolver);
+    }
+
+    public SniSSLSocketFactory(String algorithm, KeyStore keystore, String keyPassword, KeyStore truststore, SecureRandom random, TrustStrategy trustStrategy, X509HostnameVerifier hostnameVerifier) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(algorithm, keystore, keyPassword, truststore, random, trustStrategy, hostnameVerifier);
+    }
+
+    public SniSSLSocketFactory(String algorithm, KeyStore keystore, String keyPassword, KeyStore truststore, SecureRandom random, X509HostnameVerifier hostnameVerifier) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(algorithm, keystore, keyPassword, truststore, random, hostnameVerifier);
+    }
+
+    public SniSSLSocketFactory(KeyStore keystore, String keystorePassword, KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(keystore, keystorePassword, truststore);
+    }
+
+    public SniSSLSocketFactory(KeyStore keystore, String keystorePassword) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(keystore, keystorePassword);
+    }
+
+    public SniSSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(truststore);
+    }
+
+    public SniSSLSocketFactory(TrustStrategy trustStrategy, X509HostnameVerifier hostnameVerifier) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(trustStrategy, hostnameVerifier);
+    }
+
+    public SniSSLSocketFactory(TrustStrategy trustStrategy) throws NoSuchAlgorithmException, KeyManagementException, KeyStoreException, UnrecoverableKeyException {
+        super(trustStrategy);
+    }
+
+    public SniSSLSocketFactory(SSLContext sslContext) {
+        super(sslContext);
+    }
+
+    public SniSSLSocketFactory(SSLContext sslContext, HostNameResolver nameResolver) {
+        super(sslContext, nameResolver);
+    }
+
+    public SniSSLSocketFactory(SSLContext sslContext, X509HostnameVerifier hostnameVerifier) {
+        super(sslContext, hostnameVerifier);
+    }
+
+    public SniSSLSocketFactory(SSLContext sslContext, String[] supportedProtocols, String[] supportedCipherSuites, X509HostnameVerifier hostnameVerifier) {
+        super(sslContext, supportedProtocols, supportedCipherSuites, hostnameVerifier);
+    }
+
+    public SniSSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory, X509HostnameVerifier hostnameVerifier) {
+        super(socketfactory, hostnameVerifier);
+    }
+
+    public SniSSLSocketFactory(javax.net.ssl.SSLSocketFactory socketfactory, String[] supportedProtocols, String[] supportedCipherSuites, X509HostnameVerifier hostnameVerifier) {
+        super(socketfactory, supportedProtocols, supportedCipherSuites, hostnameVerifier);
+    }
+
+    @Override
+    public Socket connectSocket(int connectTimeout, Socket socket, HttpHost host, InetSocketAddress remoteAddress, InetSocketAddress localAddress, HttpContext context) throws IOException {
+        return super.connectSocket(connectTimeout, applySNI(socket, host.getHostName()), host, remoteAddress, localAddress, context);
+    }
+
+    @Override
+    public Socket createLayeredSocket(Socket socket, String target, int port, HttpContext context) throws IOException {
+        return super.createLayeredSocket(applySNI(socket, target), target, port, context);
+    }
+
+    private Socket applySNI(final Socket socket, String hostname) {
+        if (socket instanceof SSLSocket) {
+            try {
+                Method setHostMethod = AccessController.doPrivileged(new PrivilegedExceptionAction<Method>() {
+                    @Override
+                    public Method run() throws NoSuchMethodException {
+                        return socket.getClass().getMethod("setHost", String.class);
+                    }
+                });
+
+                setHostMethod.invoke(socket, hostname);
+                LOG.log(Level.FINEST, "Applied SNI to socket for host {0}", hostname);
+            } catch (PrivilegedActionException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
+                LOG.log(Level.WARNING, "Failed to apply SNI to SSLSocket", e);
+            }
+        }
+        return socket;
+    }
+}
diff --git a/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java b/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java
new file mode 100644
index 0000000..2c03ef8
--- /dev/null
+++ b/adapters/saml/core/src/test/java/org/keycloak/adapters/cloned/HttpAdapterUtilsTest.java
@@ -0,0 +1,82 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.adapters.cloned;
+
+import java.io.InputStream;
+import java.security.cert.X509Certificate;
+import java.util.List;
+import javax.xml.crypto.dsig.keyinfo.KeyInfo;
+import javax.xml.crypto.dsig.keyinfo.KeyName;
+import javax.xml.crypto.dsig.keyinfo.X509Data;
+import static org.hamcrest.CoreMatchers.*;
+import org.junit.Test;
+import static org.junit.Assert.*;
+import org.keycloak.adapters.saml.config.parsers.ConfigXmlConstants;
+import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.dom.saml.v2.metadata.KeyTypes;
+import org.keycloak.saml.common.exceptions.ParsingException;
+
+/**
+ *
+ * @author hmlnarik
+ */
+public class HttpAdapterUtilsTest {
+
+    private <T> T getContent(List<Object> objects, Class<T> clazz) {
+        for (Object o : objects) {
+            if (clazz.isInstance(o)) {
+                return (T) o;
+            }
+        }
+        return null;
+    }
+
+    @Test
+    public void testExtractKeysFromSamlDescriptor() throws ParsingException {
+        InputStream xmlStream = HttpAdapterUtilsTest.class.getResourceAsStream("saml-descriptor-valid.xml");
+        MultivaluedHashMap<String, KeyInfo> res = HttpAdapterUtils.extractKeysFromSamlDescriptor(xmlStream);
+
+        assertThat(res, notNullValue());
+        assertThat(res.keySet(), hasItems(KeyTypes.SIGNING.value()));
+        assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR), notNullValue());
+        assertThat(res.get(ConfigXmlConstants.SIGNING_ATTR).size(), equalTo(2));
+
+        KeyInfo ki;
+        KeyName keyName;
+        X509Data x509data;
+        X509Certificate x509certificate;
+
+        ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(0);
+        assertThat(ki.getContent().size(), equalTo(2));
+        assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
+        assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));
+
+        keyName = getContent(ki.getContent(), KeyName.class);
+        assertThat(keyName.getName(), equalTo("rJkJlvowmv1Id74GznieaAC5jU5QQp_ILzuG-GsweTI"));
+
+        x509data = getContent(ki.getContent(), X509Data.class);
+        assertThat(x509data, notNullValue());
+        x509certificate = getContent(x509data.getContent(), X509Certificate.class);
+        assertThat(x509certificate, notNullValue());
+        assertThat(x509certificate.getSigAlgName(), equalTo("SHA256withRSA"));
+
+        ki = res.get(ConfigXmlConstants.SIGNING_ATTR).get(1);
+        assertThat(ki.getContent().size(), equalTo(2));
+        assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(X509Data.class)));
+        assertThat((List<Object>) ki.getContent(), hasItem(instanceOf(KeyName.class)));
+
+        keyName = getContent(ki.getContent(), KeyName.class);
+        assertThat(keyName.getName(), equalTo("BzYc4GwL8HVrAhNyNdp-lTah2DvU9jU03kby9Ynohr4"));
+
+        x509data = getContent(ki.getContent(), X509Data.class);
+        assertThat(x509data, notNullValue());
+        x509certificate = getContent(x509data.getContent(), X509Certificate.class);
+        assertThat(x509certificate, notNullValue());
+        assertThat(x509certificate.getSigAlgName(), equalTo("SHA256withRSA"));
+
+    }
+
+}
diff --git a/adapters/saml/core/src/test/resources/org/keycloak/adapters/cloned/saml-descriptor-valid.xml b/adapters/saml/core/src/test/resources/org/keycloak/adapters/cloned/saml-descriptor-valid.xml
new file mode 100644
index 0000000..8dd3e41
--- /dev/null
+++ b/adapters/saml/core/src/test/resources/org/keycloak/adapters/cloned/saml-descriptor-valid.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!--
+
+  ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+  ~ and other contributors as indicated by the @author tags.
+  ~
+  ~ Licensed under the Apache License, Version 2.0 (the "License");
+  ~ you may not use this file except in compliance with the License.
+  ~ You may obtain a copy of the License at
+  ~
+  ~ http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the License for the specific language governing permissions and
+  ~ limitations under the License.
+  
+-->
+<EntitiesDescriptor xmlns="urn:oasis:names:tc:SAML:2.0:metadata" xmlns:dsig="http://www.w3.org/2000/09/xmldsig#" Name="urn:keycloak">
+    <EntityDescriptor entityID="http://localhost:8081/auth/realms/master">
+        <IDPSSODescriptor WantAuthnRequestsSigned="true" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
+            <KeyDescriptor use="signing">
+                <dsig:KeyInfo>
+                    <dsig:KeyName>rJkJlvowmv1Id74GznieaAC5jU5QQp_ILzuG-GsweTI</dsig:KeyName>
+                    <dsig:X509Data>
+                        <dsig:X509Certificate>
+                            MIICmzCCAYMCBgFX/9ccIDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDI2MDcxMjUwWhcNMjYxMDI2MDgxNDMwWjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDPjDrM890OoFWLIU5xNT+v8B8EkpOGY1y/9Yi/yQd95uG/p5LaywiPsw+lPy4tSn1pH/2SxNDST2zynKPDd1lYDev43m0sC2FfD2H73q3udQRqSOxW1e8FrTrGDIHxb82UNrCPlu+fH+xYSkigrkOvLvPigTwSIcu8vgs0lk9FqJ81ty3Wj2e9lS7JJGAJ3pC7rp39VLdJSKbfyj/v2RYBeG5Pscncl8cjUOHUq5u19hThjkU2jOBzgIK2JS0bNmzSfH1eBTZMoCQBI1UJ1IbA8tqjQwpOXc+JkPBRU8T/JUQoQlSR6DTcPFvDgH2oGZYFHFfUontZqtz8jrIt2pxBAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK5VgQp1x1FKgabFI6W/iGuy9ZCRoAixOOEGGkDps6dOEFgTQKTy5D/FZts9KuNxhhiD+NvS0d5BKYa5ITPLVPnGucgYkZhz+/+GhxmbjeQr0eJPaY7ZgLfH3tPA6tfdIkA0iE1En1sKEwt6R6DZjh9jtP9laoUoddTvYaFLJpZ2u1Ik94q6ZqX0fS/RKchaBHjhg6MtqCcHt07CBKHh8XNmKPXVSJC/p0MjyXv+qLaNNqyaAvAw6P6DX1hNjzrdkuaaHGXhu6kkezZUVlDWAm9cd1ppqalSK6ggy7yMW1NWTd/NYOPsFU2TS8DDPzRo14s1Qvw4v+TY6yT0NURJPQA=
+                        </dsig:X509Certificate>
+                    </dsig:X509Data>
+                </dsig:KeyInfo>
+            </KeyDescriptor>
+            <KeyDescriptor use="signing">
+                <dsig:KeyInfo>
+                    <dsig:KeyName>BzYc4GwL8HVrAhNyNdp-lTah2DvU9jU03kby9Ynohr4</dsig:KeyName>
+                    <dsig:X509Data>
+                        <dsig:X509Certificate>
+                            MIICmzCCAYMCBgFX/9eK7TANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZtYXN0ZXIwHhcNMTYxMDI2MDcxMzE4WhcNMjYxMDI2MDgxNDU4WjARMQ8wDQYDVQQDDAZtYXN0ZXIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCDLT+40/BWzWPSVmpaSaZRs5lBMQ9VP9TCoXkby4PHqxIWRecTPM8fcNkPNPE/tiR2tUIpMXPDzgXNFA/EMoB3V1OEVXPecjKtiZczdR6pi75CBx7PJ2fSXg6xpjhZmHu0k7x591GZdP8Iiu2E6b9QA2p5VXgNgfuP07XzgabnSvIrLG60Imus3u6C2qA/QEuY7EYQWrFooriYLW6B8s3xU8R1a92SLMT8JsfMWXi+1CzAhIbVvdwUwkhVDDhAU6pUek88QQgxodd3FAMksoijCGFN1yrCkovlFhKb3j9AC6Icd9eeJuwYddN/nMeMGEDOeCcAGBACiaUisjUvZDw1AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAHAHbBI0CRfdw5ZHxHAjgSQvSj41c/4cfwln4Q7X3I5lMBbW3tcgond6Ku9eU46FzG5VpgXIgvEf4u0O9jUnxLlO50+t2SHwQ1RwHdBWQngVSZCRzscq3KrSzx1hx88qLyqcPrr3QtR92fYipDjENxttT/qJtDMrXlwLZEITlHDoneX319USYB9C4zlrCIsQ5XxQTTyCx886Pz15DSVSRxVp61HGk6ROsX/DG5/xwInlzgMZ0r3JWnAjtAaXqUrcwH9FXxco+xkiqKW79bGhWGQI9sXXvQSSNAaENMIUhxtd9uOi1l5e0EkKHE2fHlYyfdUDnFJWwSMXd/NM+hVI4Lw=
+                        </dsig:X509Certificate>
+                    </dsig:X509Data>
+                </dsig:KeyInfo>
+            </KeyDescriptor>
+            <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
+            <SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
+            <NameIDFormat>
+                urn:oasis:names:tc:SAML:2.0:nameid-format:persistent
+            </NameIDFormat>
+            <NameIDFormat>
+                urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+            </NameIDFormat>
+            <NameIDFormat>
+                urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified
+            </NameIDFormat>
+            <NameIDFormat>
+                urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress
+            </NameIDFormat>
+            <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
+            <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
+            <SingleSignOnService Binding="urn:oasis:names:tc:SAML:2.0:bindings:SOAP" Location="http://localhost:8081/auth/realms/master/protocol/saml"/>
+        </IDPSSODescriptor>
+    </EntityDescriptor>
+</EntitiesDescriptor>
\ No newline at end of file
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
index c4818b4..0ba327d 100755
--- a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterConfig.java
@@ -39,7 +39,7 @@ import com.fasterxml.jackson.annotation.JsonPropertyOrder;
         "proxy-url", "turn-off-change-session-id-on-login", "token-minimum-time-to-live", "min-time-between-jwks-requests",
         "policy-enforcer"
 })
-public class AdapterConfig extends BaseAdapterConfig {
+public class AdapterConfig extends BaseAdapterConfig implements AdapterHttpClientConfig {
 
     @JsonProperty("allow-any-hostname")
     protected boolean allowAnyHostname;
@@ -82,6 +82,7 @@ public class AdapterConfig extends BaseAdapterConfig {
     @JsonProperty("proxy-url")
     protected String proxyUrl;
 
+    @Override
     public boolean isAllowAnyHostname() {
         return allowAnyHostname;
     }
@@ -90,6 +91,7 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.allowAnyHostname = allowAnyHostname;
     }
 
+    @Override
     public boolean isDisableTrustManager() {
         return disableTrustManager;
     }
@@ -98,6 +100,7 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.disableTrustManager = disableTrustManager;
     }
 
+    @Override
     public String getTruststore() {
         return truststore;
     }
@@ -106,6 +109,7 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.truststore = truststore;
     }
 
+    @Override
     public String getTruststorePassword() {
         return truststorePassword;
     }
@@ -114,6 +118,7 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.truststorePassword = truststorePassword;
     }
 
+    @Override
     public String getClientKeystore() {
         return clientKeystore;
     }
@@ -122,6 +127,7 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.clientKeystore = clientKeystore;
     }
 
+    @Override
     public String getClientKeystorePassword() {
         return clientKeystorePassword;
     }
@@ -138,6 +144,7 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.clientKeyPassword = clientKeyPassword;
     }
 
+    @Override
     public int getConnectionPoolSize() {
         return connectionPoolSize;
     }
@@ -202,6 +209,7 @@ public class AdapterConfig extends BaseAdapterConfig {
         this.policyEnforcerConfig = policyEnforcerConfig;
     }
 
+    @Override
     public String getProxyUrl() {
         return proxyUrl;
     }
diff --git a/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java
new file mode 100644
index 0000000..fa4c87e
--- /dev/null
+++ b/core/src/main/java/org/keycloak/representations/adapters/config/AdapterHttpClientConfig.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.representations.adapters.config;
+
+/**
+ * Configuration options relevant for configuring http client that can be used by adapter.
+ *
+ * NOTE: keep in sync with adapters/saml/core/src/main/java/org/keycloak/adapters/AdapterHttpClientConfig.java until unified.
+ *
+ * @author hmlnarik
+ */
+public interface AdapterHttpClientConfig {
+
+    /**
+     * Returns truststore filename.
+     */
+    public String getTruststore();
+
+    /**
+     * Returns truststore password.
+     */
+    public String getTruststorePassword();
+
+    /**
+     * Returns keystore with client keys.
+     */
+    public String getClientKeystore();
+
+    /**
+     * Returns keystore password.
+     */
+    public String getClientKeystorePassword();
+
+    /**
+     * Returns boolean flag whether any hostname verification is done on the server's
+     * certificate, {@code true} means that verification is not done.
+     * @return
+     */
+    public boolean isAllowAnyHostname();
+
+    /**
+     * Returns boolean flag whether any trust management and hostname verification is done.
+     * <p>
+     * <i>NOTE</i> Disabling trust manager is a security hole, so only set this option
+     * if you cannot or do not want to verify the identity of the
+     * host you are communicating with.
+     */
+    public boolean isDisableTrustManager();
+
+    /**
+     * Returns size of connection pool.
+     */
+    public int getConnectionPoolSize();
+
+    /**
+     * Returns URL of HTTP proxy.
+     */
+    public String getProxyUrl();
+
+}