001 /**
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements. See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License. You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017 package org.apache.activemq.transport.tcp;
018
019 import java.io.IOException;
020 import java.net.URI;
021 import java.net.URISyntaxException;
022 import java.net.UnknownHostException;
023 import java.security.KeyManagementException;
024 import java.security.NoSuchAlgorithmException;
025 import java.security.NoSuchProviderException;
026 import java.security.SecureRandom;
027 import java.util.HashMap;
028 import java.util.Map;
029
030 import javax.net.ServerSocketFactory;
031 import javax.net.SocketFactory;
032 import javax.net.ssl.KeyManager;
033 import javax.net.ssl.SSLContext;
034 import javax.net.ssl.SSLServerSocketFactory;
035 import javax.net.ssl.SSLSocketFactory;
036 import javax.net.ssl.TrustManager;
037
038 import org.apache.activemq.broker.BrokerService;
039 import org.apache.activemq.broker.BrokerServiceAware;
040 import org.apache.activemq.broker.SslContext;
041 import org.apache.activemq.openwire.OpenWireFormat;
042 import org.apache.activemq.transport.InactivityMonitor;
043 import org.apache.activemq.transport.Transport;
044 import org.apache.activemq.transport.TransportFactory;
045 import org.apache.activemq.transport.TransportLoggerFactory;
046 import org.apache.activemq.transport.TransportServer;
047 import org.apache.activemq.transport.WireFormatNegotiator;
048 import org.apache.activemq.util.IOExceptionSupport;
049 import org.apache.activemq.util.IntrospectionSupport;
050 import org.apache.activemq.util.URISupport;
051 import org.apache.activemq.wireformat.WireFormat;
052 import org.slf4j.Logger;
053 import org.slf4j.LoggerFactory;
054
055 /**
056 * An implementation of the TcpTransportFactory using SSL. The major
057 * contribution from this class is that it is aware of SslTransportServer and
058 * SslTransport classes. All Transports and TransportServers created from this
059 * factory will have their needClientAuth option set to false.
060 *
061 * @author sepandm@gmail.com (Sepand)
062 * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com (logging improvement modifications)
063 *
064 */
065 public class SslTransportFactory extends TcpTransportFactory {
066 // The log this uses.,
067 private static final Logger LOG = LoggerFactory.getLogger(SslTransportFactory.class);
068
069 /**
070 * Overriding to use SslTransportServer and allow for proper reflection.
071 */
072 public TransportServer doBind(final URI location) throws IOException {
073 try {
074 Map<String, String> options = new HashMap<String, String>(URISupport.parseParameters(location));
075
076 ServerSocketFactory serverSocketFactory = createServerSocketFactory();
077 SslTransportServer server = new SslTransportServer(this, location, (SSLServerSocketFactory)serverSocketFactory);
078 server.setWireFormatFactory(createWireFormatFactory(options));
079 IntrospectionSupport.setProperties(server, options);
080 Map<String, Object> transportOptions = IntrospectionSupport.extractProperties(options, "transport.");
081 server.setTransportOption(transportOptions);
082 server.bind();
083
084 return server;
085 } catch (URISyntaxException e) {
086 throw IOExceptionSupport.create(e);
087 }
088 }
089
090 /**
091 * Overriding to allow for proper configuration through reflection.
092 */
093 public Transport compositeConfigure(Transport transport, WireFormat format, Map options) {
094
095 SslTransport sslTransport = (SslTransport)transport.narrow(SslTransport.class);
096 IntrospectionSupport.setProperties(sslTransport, options);
097
098 Map<String, Object> socketOptions = IntrospectionSupport.extractProperties(options, "socket.");
099
100 sslTransport.setSocketOptions(socketOptions);
101
102 if (sslTransport.isTrace()) {
103 try {
104 transport = TransportLoggerFactory.getInstance().createTransportLogger(transport,
105 sslTransport.getLogWriterName(), sslTransport.isDynamicManagement(), sslTransport.isStartLogging(), sslTransport.getJmxPort());
106 } catch (Throwable e) {
107 LOG.error("Could not create TransportLogger object for: " + sslTransport.getLogWriterName() + ", reason: " + e, e);
108 }
109 }
110
111 transport = new InactivityMonitor(transport, format);
112
113 // Only need the WireFormatNegotiator if using openwire
114 if (format instanceof OpenWireFormat) {
115 transport = new WireFormatNegotiator(transport, (OpenWireFormat)format, sslTransport.getMinmumWireFormatVersion());
116 }
117
118 return transport;
119 }
120
121 /**
122 * Overriding to use SslTransports.
123 */
124 protected Transport createTransport(URI location, WireFormat wf) throws UnknownHostException, IOException {
125 URI localLocation = null;
126 String path = location.getPath();
127 // see if the path is a local URI location
128 if (path != null && path.length() > 0) {
129 int localPortIndex = path.indexOf(':');
130 try {
131 Integer.parseInt(path.substring(localPortIndex + 1, path.length()));
132 String localString = location.getScheme() + ":/" + path;
133 localLocation = new URI(localString);
134 } catch (Exception e) {
135 LOG.warn("path isn't a valid local location for SslTransport to use", e);
136 }
137 }
138 SocketFactory socketFactory = createSocketFactory();
139 return new SslTransport(wf, (SSLSocketFactory)socketFactory, location, localLocation, false);
140 }
141
142
143
144 /**
145 * Creates a new SSL ServerSocketFactory. The given factory will use
146 * user-provided key and trust managers (if the user provided them).
147 *
148 * @return Newly created (Ssl)ServerSocketFactory.
149 * @throws IOException
150 */
151 protected ServerSocketFactory createServerSocketFactory() throws IOException {
152 if( SslContext.getCurrentSslContext()!=null ) {
153 SslContext ctx = SslContext.getCurrentSslContext();
154 try {
155 return ctx.getSSLContext().getServerSocketFactory();
156 } catch (Exception e) {
157 throw IOExceptionSupport.create(e);
158 }
159 } else {
160 return SSLServerSocketFactory.getDefault();
161 }
162 }
163
164 /**
165 * Creates a new SSL SocketFactory. The given factory will use user-provided
166 * key and trust managers (if the user provided them).
167 *
168 * @return Newly created (Ssl)SocketFactory.
169 * @throws IOException
170 */
171 protected SocketFactory createSocketFactory() throws IOException {
172
173 if( SslContext.getCurrentSslContext()!=null ) {
174 SslContext ctx = SslContext.getCurrentSslContext();
175 try {
176 return ctx.getSSLContext().getSocketFactory();
177 } catch (Exception e) {
178 throw IOExceptionSupport.create(e);
179 }
180 } else {
181 return SSLSocketFactory.getDefault();
182 }
183
184 }
185
186 /**
187 *
188 * @param km
189 * @param tm
190 * @param random
191 * @deprecated "Do not use anymore... using static initializers like this method only allows the JVM to use 1 SSL configuration per broker."
192 * @see org.apache.activemq.broker.SslContext#setCurrentSslContext(SslContext)
193 * @see org.apache.activemq.broker.SslContext#getSSLContext()
194 */
195 public void setKeyAndTrustManagers(KeyManager[] km, TrustManager[] tm, SecureRandom random) {
196 SslContext ctx = new SslContext(km, tm, random);
197 SslContext.setCurrentSslContext(ctx);
198 }
199
200 }