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 trustStoreType = KeyStore.getDefaultType(); 066 protected String trustStore; 067 protected String trustStorePassword; 068 protected String keyStoreType = KeyStore.getDefaultType(); 069 protected String keyStore; 070 protected String keyStorePassword; 071 protected String keyStoreKeyPassword; 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 089 * The KeyManagers used. 090 * @param tm 091 * The TrustManagers used. 092 * @param random 093 * The SecureRandom number used. 094 */ 095 public void setKeyAndTrustManagers(final KeyManager[] km, final TrustManager[] tm, final SecureRandom random) { 096 keyManager = km; 097 trustManager = tm; 098 secureRandom = random; 099 } 100 101 /** 102 * Overriding to make special considerations for SSL connections. If we are 103 * not using SSL, the superclass's method is called. If we are using SSL, an 104 * SslConnectionFactory is used and it is given the needed key and trust 105 * managers. 106 * 107 * @author sepandm@gmail.com 108 */ 109 @Override 110 protected Transport createTransport() throws JMSException { 111 SslContext existing = SslContext.getCurrentSslContext(); 112 try { 113 if (keyStore != null || trustStore != null) { 114 keyManager = createKeyManager(); 115 trustManager = createTrustManager(); 116 } 117 if (keyManager != null || trustManager != null) { 118 SslContext.setCurrentSslContext(new SslContext(keyManager, trustManager, secureRandom)); 119 } 120 return super.createTransport(); 121 } catch (Exception e) { 122 throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e); 123 } finally { 124 SslContext.setCurrentSslContext(existing); 125 } 126 } 127 128 protected TrustManager[] createTrustManager() throws Exception { 129 TrustManager[] trustStoreManagers = null; 130 KeyStore trustedCertStore = KeyStore.getInstance(getTrustStoreType()); 131 132 if (trustStore != null) { 133 try(InputStream tsStream = getInputStream(trustStore)) { 134 135 trustedCertStore.load(tsStream, trustStorePassword.toCharArray()); 136 TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); 137 138 tmf.init(trustedCertStore); 139 trustStoreManagers = tmf.getTrustManagers(); 140 } 141 } 142 return trustStoreManagers; 143 } 144 145 protected KeyManager[] createKeyManager() throws Exception { 146 KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); 147 KeyStore ks = KeyStore.getInstance(getKeyStoreType()); 148 KeyManager[] keystoreManagers = null; 149 if (keyStore != null) { 150 byte[] sslCert = loadClientCredential(keyStore); 151 152 if (sslCert != null && sslCert.length > 0) { 153 try(ByteArrayInputStream bin = new ByteArrayInputStream(sslCert)) { 154 ks.load(bin, keyStorePassword.toCharArray()); 155 kmf.init(ks, keyStoreKeyPassword !=null ? keyStoreKeyPassword.toCharArray() : keyStorePassword.toCharArray()); 156 keystoreManagers = kmf.getKeyManagers(); 157 } 158 } 159 } 160 return keystoreManagers; 161 } 162 163 protected byte[] loadClientCredential(String fileName) throws IOException { 164 if (fileName == null) { 165 return null; 166 } 167 try(InputStream in = getInputStream(fileName); 168 ByteArrayOutputStream out = new ByteArrayOutputStream()) { 169 byte[] buf = new byte[512]; 170 int i = in.read(buf); 171 while (i > 0) { 172 out.write(buf, 0, i); 173 i = in.read(buf); 174 } 175 return out.toByteArray(); 176 } 177 } 178 179 protected InputStream getInputStream(String urlOrResource) throws IOException { 180 try { 181 File ifile = new File(urlOrResource); 182 // only open the file if and only if it exists 183 if (ifile.exists()) { 184 return new FileInputStream(ifile); 185 } 186 } catch (Exception e) { 187 } 188 189 InputStream ins = null; 190 191 try { 192 URL url = new URL(urlOrResource); 193 ins = url.openStream(); 194 if (ins != null) { 195 return ins; 196 } 197 } catch (MalformedURLException ignore) { 198 } 199 200 // Alternatively, treat as classpath resource 201 if (ins == null) { 202 ins = Thread.currentThread().getContextClassLoader().getResourceAsStream(urlOrResource); 203 } 204 205 if (ins == null) { 206 throw new IOException("Could not load resource: " + urlOrResource); 207 } 208 209 return ins; 210 } 211 212 public String getTrustStoreType() { 213 return trustStoreType; 214 } 215 216 public void setTrustStoreType(String type) { 217 trustStoreType = type; 218 } 219 220 public String getTrustStore() { 221 return trustStore; 222 } 223 224 /** 225 * The location of a keystore file (in <code>jks</code> format) containing 226 * one or more trusted certificates. 227 * 228 * @param trustStore 229 * If specified with a scheme, treat as a URL, otherwise treat as 230 * a classpath resource. 231 */ 232 public void setTrustStore(String trustStore) throws Exception { 233 this.trustStore = trustStore; 234 trustManager = null; 235 } 236 237 public String getTrustStorePassword() { 238 return trustStorePassword; 239 } 240 241 /** 242 * The password to match the trust store specified by {@link setTrustStore}. 243 * 244 * @param trustStorePassword 245 * The password used to unlock the keystore file. 246 */ 247 public void setTrustStorePassword(String trustStorePassword) { 248 this.trustStorePassword = trustStorePassword; 249 } 250 251 public String getKeyStoreType() { 252 return keyStoreType; 253 } 254 255 public void setKeyStoreType(String type) { 256 keyStoreType = type; 257 } 258 259 260 public String getKeyStore() { 261 return keyStore; 262 } 263 264 /** 265 * The location of a keystore file (in <code>jks</code> format) containing a 266 * certificate and its private key. 267 * 268 * @param keyStore 269 * If specified with a scheme, treat as a URL, otherwise treat as 270 * a classpath resource. 271 */ 272 public void setKeyStore(String keyStore) throws Exception { 273 this.keyStore = keyStore; 274 keyManager = null; 275 } 276 277 public String getKeyStorePassword() { 278 return keyStorePassword; 279 } 280 281 /** 282 * The password to match the key store specified by {@link setKeyStore}. 283 * 284 * @param keyStorePassword 285 * The password, which is used both to unlock the keystore file 286 * and as the pass phrase for the private key stored in the 287 * keystore. 288 */ 289 public void setKeyStorePassword(String keyStorePassword) { 290 this.keyStorePassword = keyStorePassword; 291 } 292 293 294 public String getKeyStoreKeyPassword() { 295 return keyStoreKeyPassword; 296 } 297 298 /** 299 * The password to match the key from the keyStore. 300 * 301 * @param keyStoreKeyPassword 302 * The password for the private key stored in the 303 * keyStore if different from keyStorePassword. 304 */ 305 public void setKeyStoreKeyPassword(String keyStoreKeyPassword) { 306 this.keyStoreKeyPassword = keyStoreKeyPassword; 307 } 308 309}