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.apache.commons.logging.Log; 053 import org.apache.commons.logging.LogFactory; 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 * @version $Revision$ 064 */ 065 public class SslTransportFactory extends TcpTransportFactory { 066 // The log this uses., 067 private static final Log LOG = LogFactory.getLog(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.parseParamters(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 }