/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.cxf.transport.https;import java.io.IOException;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.net.HttpURLConnection;import java.net.Proxy;import java.net.URL;import java.security.GeneralSecurityException;import java.util.logging.Handler;import java.util.logging.Logger;import javax.net.ssl.HostnameVerifier;import javax.net.ssl.HttpsURLConnection;import javax.net.ssl.SSLContext;import javax.net.ssl.SSLSocketFactory;import org.apache.cxf.common.logging.LogUtils;import org.apache.cxf.common.util.ReflectionInvokationHandler;import org.apache.cxf.common.util.ReflectionUtil;import org.apache.cxf.configuration.jsse.SSLUtils;import org.apache.cxf.configuration.jsse.TLSClientParameters;/** * This HttpsURLConnectionFactory implements the HttpURLConnectionFactory * for using the given SSL Policy to configure TLS connections for "https:" * URLs. */public class HttpsURLConnectionFactory {    /**     * This constant holds the URL Protocol Identifier for HTTPS     */    public static final String HTTPS_URL_PROTOCOL_ID = "https";    private static final Logger LOG =        LogUtils.getL7dLogger(HttpsURLConnectionFactory.class);    private static boolean weblogicWarned;    /**     * Cache the last SSLContext to avoid recreation     */    SSLSocketFactory socketFactory;    int lastTlsHash;    /**     * This constructor initialized the factory with the configured TLS     * Client Parameters for the HTTPConduit for which this factory is used.     */    public HttpsURLConnectionFactory() {    }    /**     * Create a HttpURLConnection, proxified if necessary.     *     *     * @param proxy This parameter is non-null if connection should be proxied.     * @param url   The target URL.     *     * @return The HttpURLConnection for the given URL.     * @throws IOException     */    public HttpURLConnection createConnection(TLSClientParameters tlsClientParameters,            Proxy proxy, URL url) throws IOException {        HttpURLConnection connection =            (HttpURLConnection) (proxy != null                                   ? url.openConnection(proxy)                                   : url.openConnection());        if (HTTPS_URL_PROTOCOL_ID.equals(url.getProtocol())) {            if (tlsClientParameters == null) {                tlsClientParameters = new TLSClientParameters();            }            try {                decorateWithTLS(tlsClientParameters, connection);            } catch (Throwable ex) {                if (ex instanceof IOException) {                    throw (IOException) ex;                }                IOException ioException = new IOException("Error while initializing secure socket", ex);                throw ioException;            }        }        return connection;    }    /**     * This method assigns the various TLS parameters on the HttpsURLConnection     * from the TLS Client Parameters. Connection parameter is of supertype HttpURLConnection,     * which allows internal cast to potentially divergent subtype (https) implementations.     */    protected synchronized void decorateWithTLS(TLSClientParameters tlsClientParameters,            HttpURLConnection connection) throws GeneralSecurityException {        int hash = tlsClientParameters.hashCode();        if (hash != lastTlsHash) {            lastTlsHash = hash;            socketFactory = null;        }        // always reload socketFactory from HttpsURLConnection.defaultSSLSocketFactory and        // tlsClientParameters.sslSocketFactory to allow runtime configuration change        if (tlsClientParameters.isUseHttpsURLConnectionDefaultSslSocketFactory()) {            socketFactory = HttpsURLConnection.getDefaultSSLSocketFactory();        } else if (tlsClientParameters.getSSLSocketFactory() != null) {            // see if an SSLSocketFactory was set. This allows easy interop            // with not-yet-commons-ssl.jar, or even just people who like doing their            // own JSSE.            socketFactory = tlsClientParameters.getSSLSocketFactory();        } else if (socketFactory == null) {            // ssl socket factory not yet instantiated, create a new one with tlsClientParameters's Trust            // Managers, Key Managers, etc            SSLContext ctx =                org.apache.cxf.transport.https.SSLUtils.getSSLContext(tlsClientParameters);            String[] cipherSuites =                SSLUtils.getCiphersuitesToInclude(tlsClientParameters.getCipherSuites(),                                                  tlsClientParameters.getCipherSuitesFilter(),                                                  ctx.getSocketFactory().getDefaultCipherSuites(),                                                  SSLUtils.getSupportedCipherSuites(ctx),                                                  LOG);            // The SSLSocketFactoryWrapper enables certain cipher suites            // from the policy.            String protocol = tlsClientParameters.getSecureSocketProtocol() != null ? tlsClientParameters                .getSecureSocketProtocol() : "TLS";            socketFactory = new SSLSocketFactoryWrapper(ctx.getSocketFactory(), cipherSuites,                                                        protocol);            //recalc the hashcode since some of the above MAY have changed the tlsClientParameters            lastTlsHash = tlsClientParameters.hashCode();        } else {           // ssl socket factory already initialized, reuse it to benefit of keep alive        }        HostnameVerifier verifier = org.apache.cxf.transport.https.SSLUtils            .getHostnameVerifier(tlsClientParameters);        if (connection instanceof HttpsURLConnection) {            // handle the expected case (javax.net.ssl)            HttpsURLConnection conn = (HttpsURLConnection) connection;            conn.setHostnameVerifier(verifier);            conn.setSSLSocketFactory(socketFactory);        } else {            // handle the deprecated sun case and other possible hidden API's            // that are similar to the Sun cases            try {                Method method = connection.getClass().getMethod("getHostnameVerifier");                InvocationHandler handler = new ReflectionInvokationHandler(verifier) {                    public Object invoke(Object proxy,                                         Method method,                                         Object[] args) throws Throwable {                        try {                            return super.invoke(proxy, method, args);                        } catch (Exception ex) {                            // BUG: CWE-755 Improper Handling of Exceptional Conditions                            // return true;                            // FIXED:                            return false;                        }                    }                };                Object proxy = java.lang.reflect.Proxy.newProxyInstance(this.getClass().getClassLoader(),                                                                        new Class[] {method.getReturnType()},                                                                        handler);                method = connection.getClass().getMethod("setHostnameVerifier", method.getReturnType());                method.invoke(connection, proxy);            } catch (Exception ex) {                //Ignore this one            }            try {                Method getSSLSocketFactory = connection.getClass().getMethod("getSSLSocketFactory");                Method setSSLSocketFactory = connection.getClass()                    .getMethod("setSSLSocketFactory", getSSLSocketFactory.getReturnType());                if (getSSLSocketFactory.getReturnType().isInstance(socketFactory)) {                    setSSLSocketFactory.invoke(connection, socketFactory);                } else {                    //need to see if we can create one - mostly the weblogic case.   The                    //weblogic SSLSocketFactory has a protected constructor that can take                    //a JSSE SSLSocketFactory so we'll try and use that                    Constructor<?> c = getSSLSocketFactory.getReturnType()                        .getDeclaredConstructor(SSLSocketFactory.class);                    ReflectionUtil.setAccessible(c);                    setSSLSocketFactory.invoke(connection, c.newInstance(socketFactory));                }            } catch (Exception ex) {                if (connection.getClass().getName().contains("weblogic")) {                    if (!weblogicWarned) {                        weblogicWarned = true;                        LOG.warning("Could not configure SSLSocketFactory on Weblogic.  "                                    + " Use the Weblogic control panel to configure the SSL settings.");                    }                    return;                }                //if we cannot set the SSLSocketFactory, we're in serious trouble.                throw new IllegalArgumentException("Error decorating connection class "                        + connection.getClass().getName(), ex);            }        }    }    /*     *  For development and testing only     */    protected void addLogHandler(Handler handler) {        LOG.addHandler(handler);    }}