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