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.SecureRandom;
024 import java.util.HashMap;
025 import java.util.Map;
026
027 import javax.net.ServerSocketFactory;
028 import javax.net.SocketFactory;
029 import javax.net.ssl.KeyManager;
030 import javax.net.ssl.SSLServerSocketFactory;
031 import javax.net.ssl.SSLSocketFactory;
032 import javax.net.ssl.TrustManager;
033
034 import org.apache.activemq.broker.SslContext;
035 import org.apache.activemq.transport.Transport;
036 import org.apache.activemq.transport.TransportServer;
037 import org.apache.activemq.util.IOExceptionSupport;
038 import org.apache.activemq.util.IntrospectionSupport;
039 import org.apache.activemq.util.URISupport;
040 import org.apache.activemq.wireformat.WireFormat;
041 import org.slf4j.Logger;
042 import org.slf4j.LoggerFactory;
043
044 /**
045 * An implementation of the TcpTransportFactory using SSL. The major
046 * contribution from this class is that it is aware of SslTransportServer and
047 * SslTransport classes. All Transports and TransportServers created from this
048 * factory will have their needClientAuth option set to false.
049 *
050 * @author sepandm@gmail.com (Sepand)
051 * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com (logging improvement modifications)
052 *
053 */
054 public class SslTransportFactory extends TcpTransportFactory {
055 // The log this uses.,
056 private static final Logger LOG = LoggerFactory.getLogger(SslTransportFactory.class);
057
058 /**
059 * Overriding to use SslTransportServer and allow for proper reflection.
060 */
061 public TransportServer doBind(final URI location) throws IOException {
062 try {
063 Map<String, String> options = new HashMap<String, String>(URISupport.parseParameters(location));
064
065 ServerSocketFactory serverSocketFactory = createServerSocketFactory();
066 SslTransportServer server = new SslTransportServer(this, location, (SSLServerSocketFactory)serverSocketFactory);
067 server.setWireFormatFactory(createWireFormatFactory(options));
068 IntrospectionSupport.setProperties(server, options);
069 Map<String, Object> transportOptions = IntrospectionSupport.extractProperties(options, "transport.");
070 server.setTransportOption(transportOptions);
071 server.bind();
072
073 return server;
074 } catch (URISyntaxException e) {
075 throw IOExceptionSupport.create(e);
076 }
077 }
078
079 /**
080 * Overriding to allow for proper configuration through reflection but delegate to get common
081 * configuration
082 */
083 @SuppressWarnings("rawtypes")
084 public Transport compositeConfigure(Transport transport, WireFormat format, Map options) {
085
086 SslTransport sslTransport = (SslTransport)transport.narrow(SslTransport.class);
087 IntrospectionSupport.setProperties(sslTransport, options);
088
089 return super.compositeConfigure(transport, format, options);
090 }
091
092 /**
093 * Overriding to use SslTransports.
094 */
095 protected Transport createTransport(URI location, WireFormat wf) throws UnknownHostException, IOException {
096 URI localLocation = null;
097 String path = location.getPath();
098 // see if the path is a local URI location
099 if (path != null && path.length() > 0) {
100 int localPortIndex = path.indexOf(':');
101 try {
102 Integer.parseInt(path.substring(localPortIndex + 1, path.length()));
103 String localString = location.getScheme() + ":/" + path;
104 localLocation = new URI(localString);
105 } catch (Exception e) {
106 LOG.warn("path isn't a valid local location for SslTransport to use", e);
107 }
108 }
109 SocketFactory socketFactory = createSocketFactory();
110 return new SslTransport(wf, (SSLSocketFactory)socketFactory, location, localLocation, false);
111 }
112
113 /**
114 * Creates a new SSL ServerSocketFactory. The given factory will use
115 * user-provided key and trust managers (if the user provided them).
116 *
117 * @return Newly created (Ssl)ServerSocketFactory.
118 * @throws IOException
119 */
120 protected ServerSocketFactory createServerSocketFactory() throws IOException {
121 if( SslContext.getCurrentSslContext()!=null ) {
122 SslContext ctx = SslContext.getCurrentSslContext();
123 try {
124 return ctx.getSSLContext().getServerSocketFactory();
125 } catch (Exception e) {
126 throw IOExceptionSupport.create(e);
127 }
128 } else {
129 return SSLServerSocketFactory.getDefault();
130 }
131 }
132
133 /**
134 * Creates a new SSL SocketFactory. The given factory will use user-provided
135 * key and trust managers (if the user provided them).
136 *
137 * @return Newly created (Ssl)SocketFactory.
138 * @throws IOException
139 */
140 protected SocketFactory createSocketFactory() throws IOException {
141
142 if( SslContext.getCurrentSslContext()!=null ) {
143 SslContext ctx = SslContext.getCurrentSslContext();
144 try {
145 return ctx.getSSLContext().getSocketFactory();
146 } catch (Exception e) {
147 throw IOExceptionSupport.create(e);
148 }
149 } else {
150 return SSLSocketFactory.getDefault();
151 }
152 }
153
154 /**
155 *
156 * @param km
157 * @param tm
158 * @param random
159 * @deprecated "Do not use anymore... using static initializers like this method only allows the JVM to use 1 SSL configuration per broker."
160 * @see org.apache.activemq.broker.SslContext#setCurrentSslContext(SslContext)
161 * @see org.apache.activemq.broker.SslContext#getSSLContext()
162 */
163 public void setKeyAndTrustManagers(KeyManager[] km, TrustManager[] tm, SecureRandom random) {
164 SslContext ctx = new SslContext(km, tm, random);
165 SslContext.setCurrentSslContext(ctx);
166 }
167
168 }