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 }