001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one
003: * or more contributor license agreements. See the NOTICE file
004: * distributed with this work for additional information
005: * regarding copyright ownership. The ASF licenses this file
006: * to you under the Apache License, Version 2.0 (the
007: * "License"); you may not use this file except in compliance
008: * with the License. You may obtain a copy of the License at
009: *
010: * http://www.apache.org/licenses/LICENSE-2.0
011: *
012: * Unless required by applicable law or agreed to in writing,
013: * software distributed under the License is distributed on an
014: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015: * KIND, either express or implied. See the License for the
016: * specific language governing permissions and limitations
017: * under the License.
018: */package org.apache.cxf.transport.https;
019:
020: import java.io.IOException;
021: import java.net.HttpURLConnection;
022: import java.net.Proxy;
023: import java.net.URL;
024: import java.security.KeyManagementException;
025: import java.security.NoSuchAlgorithmException;
026: import java.security.NoSuchProviderException;
027: import java.util.logging.Handler;
028: import java.util.logging.Logger;
029:
030: import javax.imageio.IIOException;
031: import javax.net.ssl.HostnameVerifier;
032: import javax.net.ssl.HttpsURLConnection;
033: import javax.net.ssl.SSLContext;
034: import javax.net.ssl.SSLSession;
035:
036: import org.apache.cxf.common.logging.LogUtils;
037: import org.apache.cxf.configuration.jsse.TLSClientParameters;
038: import org.apache.cxf.transport.http.HTTPConduit;
039: import org.apache.cxf.transport.http.HttpURLConnectionFactory;
040: import org.apache.cxf.transport.http.HttpURLConnectionInfo;
041:
042: /**
043: * This HttpsURLConnectionFactory implements the HttpURLConnectionFactory
044: * for using the given SSL Policy to configure TLS connections for "https:"
045: * URLs.
046: *
047: */
048: public final class HttpsURLConnectionFactory implements
049: HttpURLConnectionFactory {
050:
051: /**
052: * This constant holds the URL Protocol Identifier for HTTPS
053: */
054: public static final String HTTPS_URL_PROTOCOL_ID = "https";
055:
056: private static final long serialVersionUID = 1L;
057: private static final Logger LOG = LogUtils
058: .getL7dLogger(HttpsURLConnectionFactory.class);
059:
060: /*
061: * For development and testing only
062: */
063: private static final String[] UNSUPPORTED = { "SessionCaching",
064: "SessionCacheKey", "MaxChainLength", "CertValidator",
065: "ProxyHost", "ProxyPort" };
066:
067: /*
068: * For development and testing only
069: */
070: private static final String[] DERIVATIVE = { "CiphersuiteFilters" };
071:
072: /**
073: * This field holds the conduit to which this connection factory
074: * is a slave.
075: */
076: HTTPConduit conduit;
077:
078: /**
079: * This field contains the TLS configuration for the URLs created by
080: * this factory.
081: */
082: TLSClientParameters tlsClientParameters;
083:
084: /**
085: * This constructor initialized the factory with the configured TLS
086: * Client Parameters for the HTTPConduit for which this factory is used.
087: *
088: * @param params The TLS Client Parameters. This parameter is guaranteed
089: * to be non-null.
090: */
091: public HttpsURLConnectionFactory(TLSClientParameters params) {
092: tlsClientParameters = params;
093: assert tlsClientParameters != null;
094: }
095:
096: /**
097: * Create a HttpURLConnection, proxified if neccessary.
098: *
099: *
100: * @param proxy This parameter is non-null if connection should be proxied.
101: * @param url The target URL. This parameter must be an https url.
102: *
103: * @return The HttpsURLConnection for the given URL.
104: * @throws IOException This exception is thrown if
105: * the "url" is not "https" or other IOException
106: * is thrown.
107: *
108: */
109: public HttpURLConnection createConnection(Proxy proxy, URL url)
110: throws IOException {
111:
112: if (!url.getProtocol().equals(HTTPS_URL_PROTOCOL_ID)) {
113: throw new IOException("Illegal Protocol "
114: + url.getProtocol()
115: + " for HTTPS URLConnection Factory.");
116: }
117:
118: HttpsURLConnection connection = (HttpsURLConnection) (proxy != null ? url
119: .openConnection(proxy)
120: : url.openConnection());
121:
122: if (tlsClientParameters != null) {
123: Exception ex = null;
124: try {
125: decorateWithTLS(connection);
126: } catch (Exception e) {
127: ex = e;
128: } finally {
129: if (ex != null) {
130: if (ex instanceof IOException) {
131: throw (IOException) ex;
132: }
133: throw new IIOException(
134: "Error while initializing secure socket",
135: ex);
136: }
137: }
138: } else {
139: assert false;
140: }
141:
142: return connection;
143: }
144:
145: /**
146: * This class is the default hostname verifier that the
147: * HttpsURLConnection implementation uses to verify that
148: * a hostname belongs to a particular verified key/certificate
149: * pair.
150: * <p>
151: * The default is to make sure that "CN=<hostname>", which
152: * isn't always desired. The MessageTrustDecider is
153: * the point at which an application can place trust in the
154: * certificate and target URL. We use this default of always
155: * returning true, delegate the trust decision to the
156: * MessageTrustDecider.
157: */
158: private class AlwaysTrueHostnameVerifier implements
159: HostnameVerifier {
160:
161: public boolean verify(String hostname, SSLSession sslSession) {
162: return true;
163: }
164:
165: }
166:
167: /**
168: * This method assigns the various TLS parameters on the HttpsURLConnection
169: * from the TLS Client Parameters.
170: */
171: protected void decorateWithTLS(HttpsURLConnection connection)
172: throws NoSuchAlgorithmException, NoSuchProviderException,
173: KeyManagementException {
174: String provider = tlsClientParameters.getJsseProvider();
175:
176: String protocol = tlsClientParameters.getSecureSocketProtocol() != null ? tlsClientParameters
177: .getSecureSocketProtocol()
178: : "TLS";
179:
180: SSLContext ctx = provider == null ? SSLContext
181: .getInstance(protocol) : SSLContext.getInstance(
182: protocol, provider);
183:
184: ctx.init(tlsClientParameters.getKeyManagers(),
185: tlsClientParameters.getTrustManagers(),
186: tlsClientParameters.getSecureRandom());
187:
188: // The "false" argument means opposite of exclude.
189: String[] cipherSuites = SSLUtils
190: .getCiphersuites(tlsClientParameters.getCipherSuites(),
191: SSLUtils.getSupportedCipherSuites(ctx),
192: tlsClientParameters.getCipherSuitesFilter(),
193: LOG, false);
194:
195: connection
196: .setHostnameVerifier(new AlwaysTrueHostnameVerifier());
197:
198: // The SSLSocketFactoryWrapper enables certain cipher suites
199: // from the policy.
200: connection.setSSLSocketFactory(new SSLSocketFactoryWrapper(ctx
201: .getSocketFactory(), cipherSuites));
202:
203: }
204:
205: /*
206: * For development and testing only
207: */
208: protected void addLogHandler(Handler handler) {
209: LOG.addHandler(handler);
210: }
211:
212: protected String[] getUnSupported() {
213: return UNSUPPORTED;
214: }
215:
216: protected String[] getDerivative() {
217: return DERIVATIVE;
218: }
219:
220: /**
221: * This operation returns an HttpsURLConnectionInfo for the
222: * given HttpsURLConnection.
223: *
224: * @param connection The HttpsURLConnection
225: * @return The HttpsURLConnectionInfo object for the given
226: * HttpsURLConnection.
227: * @throws IOException Normal IO Exceptions.
228: * @throws ClassCastException If "connection" is not an HttpsURLConnection.
229: */
230: public HttpURLConnectionInfo getConnectionInfo(
231: HttpURLConnection connection) throws IOException {
232: return new HttpsURLConnectionInfo(
233: (HttpsURLConnection) connection);
234: }
235: }
|