From 683487aafe395704dfd80fecb72f8a120f3209f8 Mon Sep 17 00:00:00 2001 From: Marko Strukelj Date: Wed, 3 Feb 2016 14:44:33 +0100 Subject: [PATCH] KEYCLOAK-2439 Client adapter fails to set SNI field during TLS handshake --- .../keycloak/adapters/HttpClientBuilder.java | 8 +- .../adapters/SniSSLSocketFactory.java | 134 ++++++++++++++++++ 2 files changed, 138 insertions(+), 4 deletions(-) create mode 100644 adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/SniSSLSocketFactory.java 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 f461577e0c..cfcbe19540 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 @@ -260,15 +260,15 @@ public class HttpClientBuilder { theContext.init(null, new TrustManager[]{new PassthroughTrustManager()}, new SecureRandom()); verifier = new AllowAllHostnameVerifier(); - sslsf = new SSLSocketFactory(theContext, verifier); + sslsf = new SniSSLSocketFactory(theContext, verifier); } else if (theContext != null) { - sslsf = new SSLSocketFactory(theContext, verifier); + sslsf = new SniSSLSocketFactory(theContext, verifier); } else if (clientKeyStore != null || truststore != null) { - sslsf = new SSLSocketFactory(SSLSocketFactory.TLS, clientKeyStore, clientPrivateKeyPassword, truststore, null, verifier); + 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 SSLSocketFactory(tlsContext, verifier); + sslsf = new SniSSLSocketFactory(tlsContext, verifier); } SchemeRegistry registry = new SchemeRegistry(); registry.register( diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/SniSSLSocketFactory.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/SniSSLSocketFactory.java new file mode 100644 index 0000000000..9ccbcde43a --- /dev/null +++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/SniSSLSocketFactory.java @@ -0,0 +1,134 @@ +/* + * 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; + +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.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.PrivilegedExceptionAction; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.util.logging.Level; +import java.util.logging.Logger; + +/** + * @author Marko Strukelj + */ +public class SniSSLSocketFactory extends SSLSocketFactory { + + private static 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() { + public Method run() throws NoSuchMethodException { + return socket.getClass().getMethod("setHost", String.class); + } + }); + + setHostMethod.invoke(socket, hostname); + log.finest("Applied SNI to socket for: " + hostname); + } catch (Exception e) { + log.log(Level.WARNING, "Failed to apply SNI to SSLSocket", e); + } + } + return socket; + } +}