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    }