1 /** 2 * Licensed to the Apache Software Foundation (ASF) under one or more 3 * contributor license agreements. See the NOTICE file distributed with 4 * this work for additional information regarding copyright ownership. 5 * The ASF licenses this file to You under the Apache License, Version 2.0 6 * (the "License"); you may not use this file except in compliance with 7 * the License. You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 package org.apache.activemq.transport.tcp; 18 19 import java.io.IOException; 20 import java.net.URI; 21 import java.net.URISyntaxException; 22 import java.net.UnknownHostException; 23 import java.security.KeyManagementException; 24 import java.security.NoSuchAlgorithmException; 25 import java.security.NoSuchProviderException; 26 import java.security.SecureRandom; 27 import java.util.HashMap; 28 import java.util.Map; 29 30 import javax.net.ServerSocketFactory; 31 import javax.net.SocketFactory; 32 import javax.net.ssl.KeyManager; 33 import javax.net.ssl.SSLContext; 34 import javax.net.ssl.SSLServerSocketFactory; 35 import javax.net.ssl.SSLSocketFactory; 36 import javax.net.ssl.TrustManager; 37 38 import org.apache.activemq.broker.BrokerService; 39 import org.apache.activemq.broker.BrokerServiceAware; 40 import org.apache.activemq.broker.SslContext; 41 import org.apache.activemq.openwire.OpenWireFormat; 42 import org.apache.activemq.transport.InactivityMonitor; 43 import org.apache.activemq.transport.Transport; 44 import org.apache.activemq.transport.TransportFactory; 45 import org.apache.activemq.transport.TransportLoggerFactory; 46 import org.apache.activemq.transport.TransportServer; 47 import org.apache.activemq.transport.WireFormatNegotiator; 48 import org.apache.activemq.util.IOExceptionSupport; 49 import org.apache.activemq.util.IntrospectionSupport; 50 import org.apache.activemq.util.URISupport; 51 import org.apache.activemq.wireformat.WireFormat; 52 import org.apache.commons.logging.Log; 53 import org.apache.commons.logging.LogFactory; 54 55 /** 56 * An implementation of the TcpTransportFactory using SSL. The major 57 * contribution from this class is that it is aware of SslTransportServer and 58 * SslTransport classes. All Transports and TransportServers created from this 59 * factory will have their needClientAuth option set to false. 60 * 61 * @author sepandm@gmail.com (Sepand) 62 * @author David Martin Clavo david(dot)martin(dot)clavo(at)gmail.com (logging improvement modifications) 63 * @version $Revision$ 64 */ 65 public class SslTransportFactory extends TcpTransportFactory { 66 // The log this uses., 67 private static final Log LOG = LogFactory.getLog(SslTransportFactory.class); 68 69 /** 70 * Overriding to use SslTransportServer and allow for proper reflection. 71 */ 72 public TransportServer doBind(final URI location) throws IOException { 73 try { 74 Map<String, String> options = new HashMap<String, String>(URISupport.parseParamters(location)); 75 76 ServerSocketFactory serverSocketFactory = createServerSocketFactory(); 77 SslTransportServer server = new SslTransportServer(this, location, (SSLServerSocketFactory)serverSocketFactory); 78 server.setWireFormatFactory(createWireFormatFactory(options)); 79 IntrospectionSupport.setProperties(server, options); 80 Map<String, Object> transportOptions = IntrospectionSupport.extractProperties(options, "transport."); 81 server.setTransportOption(transportOptions); 82 server.bind(); 83 84 return server; 85 } catch (URISyntaxException e) { 86 throw IOExceptionSupport.create(e); 87 } 88 } 89 90 /** 91 * Overriding to allow for proper configuration through reflection. 92 */ 93 public Transport compositeConfigure(Transport transport, WireFormat format, Map options) { 94 95 SslTransport sslTransport = (SslTransport)transport.narrow(SslTransport.class); 96 IntrospectionSupport.setProperties(sslTransport, options); 97 98 Map<String, Object> socketOptions = IntrospectionSupport.extractProperties(options, "socket."); 99 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 }