| /** |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.activemq; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.net.MalformedURLException; |
| import java.net.URI; |
| import java.net.URL; |
| import java.security.KeyStore; |
| import java.security.SecureRandom; |
| |
| import javax.jms.JMSException; |
| import javax.net.ssl.KeyManager; |
| import javax.net.ssl.KeyManagerFactory; |
| import javax.net.ssl.TrustManager; |
| import javax.net.ssl.TrustManagerFactory; |
| |
| import org.apache.activemq.broker.SslContext; |
| import org.apache.activemq.transport.Transport; |
| import org.apache.activemq.util.JMSExceptionSupport; |
| |
| /** |
| * An ActiveMQConnectionFactory that allows access to the key and trust managers |
| * used for SslConnections. There is no reason to use this class unless SSL is |
| * being used AND the key and trust managers need to be specified from within |
| * code. In fact, if the URI passed to this class does not have an "ssl" scheme, |
| * this class will pass all work on to its superclass. |
| * |
| * There are two alternative approaches you can use to provide X.509 |
| * certificates for the SSL connections: |
| * |
| * Call <code>setTrustStore</code>, <code>setTrustStorePassword</code>, |
| * <code>setKeyStore</code>, and <code>setKeyStorePassword</code>. |
| * |
| * Call <code>setKeyAndTrustManagers</code>. |
| * |
| * @author sepandm@gmail.com |
| */ |
| public class ActiveMQSslConnectionFactory extends ActiveMQConnectionFactory { |
| |
| // The key and trust managers used to initialize the used SSLContext. |
| protected KeyManager[] keyManager; |
| protected TrustManager[] trustManager; |
| protected SecureRandom secureRandom; |
| protected String trustStoreType = KeyStore.getDefaultType(); |
| protected String trustStore; |
| protected String trustStorePassword; |
| protected String keyStoreType = KeyStore.getDefaultType(); |
| protected String keyStore; |
| protected String keyStorePassword; |
| protected String keyStoreKeyPassword; |
| |
| public ActiveMQSslConnectionFactory() { |
| super(); |
| } |
| |
| public ActiveMQSslConnectionFactory(String brokerURL) { |
| super(brokerURL); |
| } |
| |
| public ActiveMQSslConnectionFactory(URI brokerURL) { |
| super(brokerURL); |
| } |
| |
| /** |
| * Sets the key and trust managers used when creating SSL connections. |
| * |
| * @param km |
| * The KeyManagers used. |
| * @param tm |
| * The TrustManagers used. |
| * @param random |
| * The SecureRandom number used. |
| */ |
| public void setKeyAndTrustManagers(final KeyManager[] km, final TrustManager[] tm, final SecureRandom random) { |
| keyManager = km; |
| trustManager = tm; |
| secureRandom = random; |
| } |
| |
| /** |
| * Overriding to make special considerations for SSL connections. If we are |
| * not using SSL, the superclass's method is called. If we are using SSL, an |
| * SslConnectionFactory is used and it is given the needed key and trust |
| * managers. |
| * |
| * @author sepandm@gmail.com |
| */ |
| @Override |
| protected Transport createTransport() throws JMSException { |
| SslContext existing = SslContext.getCurrentSslContext(); |
| try { |
| if (keyStore != null || trustStore != null) { |
| keyManager = createKeyManager(); |
| trustManager = createTrustManager(); |
| } |
| if (keyManager != null || trustManager != null) { |
| SslContext.setCurrentSslContext(new SslContext(keyManager, trustManager, secureRandom)); |
| } |
| return super.createTransport(); |
| } catch (Exception e) { |
| throw JMSExceptionSupport.create("Could not create Transport. Reason: " + e, e); |
| } finally { |
| SslContext.setCurrentSslContext(existing); |
| } |
| } |
| |
| protected TrustManager[] createTrustManager() throws Exception { |
| TrustManager[] trustStoreManagers = null; |
| KeyStore trustedCertStore = KeyStore.getInstance(getTrustStoreType()); |
| |
| if (trustStore != null) { |
| try(InputStream tsStream = getInputStream(trustStore)) { |
| |
| trustedCertStore.load(tsStream, trustStorePassword.toCharArray()); |
| TrustManagerFactory tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm()); |
| |
| tmf.init(trustedCertStore); |
| trustStoreManagers = tmf.getTrustManagers(); |
| } |
| } |
| return trustStoreManagers; |
| } |
| |
| protected KeyManager[] createKeyManager() throws Exception { |
| KeyManagerFactory kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm()); |
| KeyStore ks = KeyStore.getInstance(getKeyStoreType()); |
| KeyManager[] keystoreManagers = null; |
| if (keyStore != null) { |
| byte[] sslCert = loadClientCredential(keyStore); |
| |
| if (sslCert != null && sslCert.length > 0) { |
| try(ByteArrayInputStream bin = new ByteArrayInputStream(sslCert)) { |
| ks.load(bin, keyStorePassword.toCharArray()); |
| kmf.init(ks, keyStoreKeyPassword !=null ? keyStoreKeyPassword.toCharArray() : keyStorePassword.toCharArray()); |
| keystoreManagers = kmf.getKeyManagers(); |
| } |
| } |
| } |
| return keystoreManagers; |
| } |
| |
| protected byte[] loadClientCredential(String fileName) throws IOException { |
| if (fileName == null) { |
| return null; |
| } |
| try(InputStream in = getInputStream(fileName); |
| ByteArrayOutputStream out = new ByteArrayOutputStream()) { |
| byte[] buf = new byte[512]; |
| int i = in.read(buf); |
| while (i > 0) { |
| out.write(buf, 0, i); |
| i = in.read(buf); |
| } |
| return out.toByteArray(); |
| } |
| } |
| |
| protected InputStream getInputStream(String urlOrResource) throws IOException { |
| try { |
| File ifile = new File(urlOrResource); |
| // only open the file if and only if it exists |
| if (ifile.exists()) { |
| return new FileInputStream(ifile); |
| } |
| } catch (Exception e) { |
| } |
| |
| InputStream ins = null; |
| |
| try { |
| URL url = new URL(urlOrResource); |
| ins = url.openStream(); |
| if (ins != null) { |
| return ins; |
| } |
| } catch (MalformedURLException ignore) { |
| } |
| |
| // Alternatively, treat as classpath resource |
| if (ins == null) { |
| ins = Thread.currentThread().getContextClassLoader().getResourceAsStream(urlOrResource); |
| } |
| |
| if (ins == null) { |
| throw new IOException("Could not load resource: " + urlOrResource); |
| } |
| |
| return ins; |
| } |
| |
| public String getTrustStoreType() { |
| return trustStoreType; |
| } |
| |
| public void setTrustStoreType(String type) { |
| trustStoreType = type; |
| } |
| |
| public String getTrustStore() { |
| return trustStore; |
| } |
| |
| /** |
| * The location of a keystore file (in <code>jks</code> format) containing |
| * one or more trusted certificates. |
| * |
| * @param trustStore |
| * If specified with a scheme, treat as a URL, otherwise treat as |
| * a classpath resource. |
| */ |
| public void setTrustStore(String trustStore) throws Exception { |
| this.trustStore = trustStore; |
| trustManager = null; |
| } |
| |
| public String getTrustStorePassword() { |
| return trustStorePassword; |
| } |
| |
| /** |
| * The password to match the trust store specified by {@link setTrustStore}. |
| * |
| * @param trustStorePassword |
| * The password used to unlock the keystore file. |
| */ |
| public void setTrustStorePassword(String trustStorePassword) { |
| this.trustStorePassword = trustStorePassword; |
| } |
| |
| public String getKeyStoreType() { |
| return keyStoreType; |
| } |
| |
| public void setKeyStoreType(String type) { |
| keyStoreType = type; |
| } |
| |
| |
| public String getKeyStore() { |
| return keyStore; |
| } |
| |
| /** |
| * The location of a keystore file (in <code>jks</code> format) containing a |
| * certificate and its private key. |
| * |
| * @param keyStore |
| * If specified with a scheme, treat as a URL, otherwise treat as |
| * a classpath resource. |
| */ |
| public void setKeyStore(String keyStore) throws Exception { |
| this.keyStore = keyStore; |
| keyManager = null; |
| } |
| |
| public String getKeyStorePassword() { |
| return keyStorePassword; |
| } |
| |
| /** |
| * The password to match the key store specified by {@link setKeyStore}. |
| * |
| * @param keyStorePassword |
| * The password, which is used both to unlock the keystore file |
| * and as the pass phrase for the private key stored in the |
| * keystore. |
| */ |
| public void setKeyStorePassword(String keyStorePassword) { |
| this.keyStorePassword = keyStorePassword; |
| } |
| |
| |
| public String getKeyStoreKeyPassword() { |
| return keyStoreKeyPassword; |
| } |
| |
| /** |
| * The password to match the key from the keyStore. |
| * |
| * @param keyStoreKeyPassword |
| * The password for the private key stored in the |
| * keyStore if different from keyStorePassword. |
| */ |
| public void setKeyStoreKeyPassword(String keyStoreKeyPassword) { |
| this.keyStoreKeyPassword = keyStoreKeyPassword; |
| } |
| |
| } |