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 018 package org.apache.activemq; 019 020 import java.io.ByteArrayInputStream; 021 import java.io.ByteArrayOutputStream; 022 import java.io.FileInputStream; 023 import java.io.IOException; 024 import java.io.InputStream; 025 import java.security.KeyStore; 026 027 import java.net.MalformedURLException; 028 import java.net.URI; 029 import java.net.URL; 030 import java.security.SecureRandom; 031 import javax.jms.JMSException; 032 import javax.net.ssl.KeyManager; 033 import javax.net.ssl.KeyManagerFactory; 034 import javax.net.ssl.TrustManager; 035 import javax.net.ssl.TrustManagerFactory; 036 037 import org.apache.activemq.broker.BrokerService; 038 import org.apache.activemq.broker.SslContext; 039 import org.apache.activemq.transport.Transport; 040 import org.apache.activemq.transport.tcp.SslTransportFactory; 041 import org.apache.activemq.util.JMSExceptionSupport; 042 import org.apache.commons.logging.Log; 043 import org.apache.commons.logging.LogFactory; 044 045 /** 046 * An ActiveMQConnectionFactory that allows access to the key and trust managers 047 * used for SslConnections. There is no reason to use this class unless SSL is 048 * being used AND the key and trust managers need to be specified from within 049 * code. In fact, if the URI passed to this class does not have an "ssl" scheme, 050 * this class will pass all work on to its superclass. 051 * 052 * There are two alternative approaches you can use to provide X.509 certificates 053 * for the SSL connections: 054 * 055 * Call <code>setTrustStore</code>, <code>setTrustStorePassword</code>, <code>setKeyStore</code>, 056 * and <code>setKeyStorePassword</code>. 057 * 058 * Call <code>setKeyAndTrustManagers</code>. 059 * 060 * @author sepandm@gmail.com 061 */ 062 public class ActiveMQSslConnectionFactory extends ActiveMQConnectionFactory { 063 private static final Log LOG = LogFactory.getLog(ActiveMQSslConnectionFactory.class); 064 // The key and trust managers used to initialize the used SSLContext. 065 protected KeyManager[] keyManager; 066 protected TrustManager[] trustManager; 067 protected SecureRandom secureRandom; 068 protected String trustStore; 069 protected String trustStorePassword; 070 protected String keyStore; 071 protected String keyStorePassword; 072 073 public ActiveMQSslConnectionFactory() { 074 super(); 075 } 076 077 public ActiveMQSslConnectionFactory(String brokerURL) { 078 super(brokerURL); 079 } 080 081 public ActiveMQSslConnectionFactory(URI brokerURL) { 082 super(brokerURL); 083 } 084 085 /** 086 * Sets the key and trust managers used when creating SSL connections. 087 * 088 * @param km The KeyManagers used. 089 * @param tm The TrustManagers used. 090 * @param random The SecureRandom number used. 091 */ 092 public void setKeyAndTrustManagers(final KeyManager[] km, final TrustManager[] tm, final SecureRandom random) { 093 keyManager = km; 094 trustManager = tm; 095 secureRandom = random; 096 } 097 098 /** 099 * Overriding to make special considerations for SSL connections. If we are 100 * not using SSL, the superclass's method is called. If we are using SSL, an 101 * SslConnectionFactory is used and it is given the needed key and trust 102 * managers. 103 * 104 * @author sepandm@gmail.com 105 */ 106 protected Transport createTransport() throws JMSException { 107 // If the given URI is non-ssl, let superclass handle it. 108 if (!brokerURL.getScheme().equals("ssl")) { 109 return super.createTransport(); 110 } 111 112 try { 113 if (keyManager == null || trustManager == null) { 114 trustManager = createTrustManager(); 115 keyManager = createKeyManager(); 116 // secureRandom can be left as null 117 } 118 SslTransportFactory sslFactory = new SslTransportFactory(); 119 SslContext ctx = new SslContext(keyManager, trustManager, secureRandom); 120 SslContext.setCurrentSslContext(ctx); 121 return sslFactory.doConnect(brokerURL); 122 } catch (Exception e) { 123 throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e); 124 } 125 } 126 127 protected TrustManager[] createTrustManager() throws Exception { 128 TrustManager[] trustStoreManagers = null; 129 KeyStore trustedCertStore = KeyStore.getInstance("jks"); 130 131 InputStream tsStream = getUrlOrResourceAsStream(trustStore); 132 133 trustedCertStore.load(tsStream, trustStorePassword.toCharArray()); 134 TrustManagerFactory tmf = 135 TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 136 137 tmf.init(trustedCertStore); 138 trustStoreManagers = tmf.getTrustManagers(); 139 return trustStoreManagers; 140 } 141 142 protected KeyManager[] createKeyManager() throws Exception { 143 KeyManagerFactory kmf = 144 KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 145 KeyStore ks = KeyStore.getInstance("jks"); 146 KeyManager[] keystoreManagers = null; 147 148 byte[] sslCert = loadClientCredential(keyStore); 149 150 151 if (sslCert != null && sslCert.length > 0) { 152 ByteArrayInputStream bin = new ByteArrayInputStream(sslCert); 153 ks.load(bin, keyStorePassword.toCharArray()); 154 kmf.init(ks, keyStorePassword.toCharArray()); 155 keystoreManagers = kmf.getKeyManagers(); 156 } 157 return keystoreManagers; 158 } 159 160 protected byte[] loadClientCredential(String fileName) throws IOException { 161 if (fileName == null) { 162 return null; 163 } 164 InputStream in = getUrlOrResourceAsStream(fileName); 165 //FileInputStream in = new FileInputStream(fileName); 166 ByteArrayOutputStream out = new ByteArrayOutputStream(); 167 byte[] buf = new byte[512]; 168 int i = in.read(buf); 169 while (i > 0) { 170 out.write(buf, 0, i); 171 i = in.read(buf); 172 } 173 in.close(); 174 return out.toByteArray(); 175 } 176 177 protected InputStream getUrlOrResourceAsStream(String urlOrResource) throws IOException { 178 InputStream ins = null; 179 try { 180 URL url = new URL(urlOrResource); 181 ins = url.openStream(); 182 } 183 catch (MalformedURLException ignore) { 184 ins = null; 185 } 186 187 // Alternatively, treat as classpath resource 188 if (ins == null) { 189 ins = getClass().getClassLoader().getResourceAsStream(urlOrResource); 190 } 191 192 if (ins == null) { 193 throw new java.io.IOException("Could not load resource: " + urlOrResource); 194 } 195 196 return ins; 197 } 198 199 public String getTrustStore() { 200 return trustStore; 201 } 202 203 /** 204 * The location of a keystore file (in <code>jks</code> format) containing one or more 205 * trusted certificates. 206 * 207 * @param trustStore If specified with a scheme, treat as a URL, otherwise treat as a classpath resource. 208 */ 209 public void setTrustStore(String trustStore) { 210 this.trustStore = trustStore; 211 trustManager = null; 212 } 213 214 public String getTrustStorePassword() { 215 return trustStorePassword; 216 } 217 218 /** 219 * The password to match the trust store specified by {@link setTrustStore}. 220 * 221 * @param trustStorePassword The password used to unlock the keystore file. 222 */ 223 public void setTrustStorePassword(String trustStorePassword) { 224 this.trustStorePassword = trustStorePassword; 225 } 226 227 public String getKeyStore() { 228 return keyStore; 229 } 230 231 /** 232 * The location of a keystore file (in <code>jks</code> format) containing a certificate 233 * and its private key. 234 * 235 * @param keyStore If specified with a scheme, treat as a URL, otherwise treat as a classpath resource. 236 */ 237 public void setKeyStore(String keyStore) { 238 this.keyStore = keyStore; 239 keyManager = null; 240 } 241 242 public String getKeyStorePassword() { 243 return keyStorePassword; 244 } 245 246 /** 247 * The password to match the key store specified by {@link setKeyStore}. 248 * 249 * @param keyStorePassword The password, which is used both to unlock the keystore file 250 * and as the pass phrase for the private key stored in the keystore. 251 */ 252 public void setKeyStorePassword(String keyStorePassword) { 253 this.keyStorePassword = keyStorePassword; 254 } 255 256 }