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