View Javadoc
1   /*
2    * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
3    * The software in this package is published under the terms of the CPAL v1.0
4    * license, a copy of which has been included with this distribution in the
5    * LICENSE.txt file.
6    */
7   package org.mule.module.management.agent;
8   
9   import org.mule.api.security.provider.AutoDiscoverySecurityProviderFactory;
10  import org.mule.api.security.provider.SecurityProviderFactory;
11  import org.mule.api.security.provider.SecurityProviderInfo;
12  import org.mule.util.FileUtils;
13  import org.mule.util.IOUtils;
14  
15  import java.io.File;
16  import java.io.FileInputStream;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.net.InetAddress;
20  import java.net.ServerSocket;
21  import java.security.KeyStore;
22  import java.security.Security;
23  import java.security.UnrecoverableKeyException;
24  
25  import javax.net.ssl.KeyManagerFactory;
26  import javax.net.ssl.SSLContext;
27  import javax.net.ssl.SSLServerSocket;
28  import javax.net.ssl.SSLServerSocketFactory;
29  import javax.net.ssl.TrustManagerFactory;
30  
31  import mx4j.log.Log;
32  import mx4j.log.Logger;
33  import mx4j.tools.adaptor.ssl.SSLAdaptorServerSocketFactoryMBean;
34  
35  /**
36   * This MBean creates SSLServerSocket instances.
37   * <p>
38   * It can be configured to use a specific keystore and SSL protocol version to create
39   * SSLServerSockets that will use the keystore information to encrypt data. <br>
40   * <p/> A keystore can be created with this command:
41   * 
42   * <pre>
43   *  keytool -genkey -v -keystore store.key -storepass storepwd -keypass keypwd -dname &quot;CN=Simone Bordet, OU=Project Administrator, O=MX4J, L=Torino, S=TO, C=IT&quot; -validity 365
44   * </pre>
45   * 
46   * or with this minimal command (that will prompt you for further information):
47   * 
48   * <pre>
49   *  keytool -genkey -keystore store.key
50   * </pre>
51   * 
52   * <p/> A keystore may contains more than one entry, but only the first entry will be
53   * used for encryption, no matter which is the alias for that entry. <p/> Following
54   * the first example of generation of the keystore, this MBean must be instantiated
55   * and then setup by invoking the following methods:
56   * <ul>
57   * <li> {@link #setKeyStoreName}("store.key");
58   * <li> {@link #setKeyStorePassword}("storepwd");
59   * <li> {@link #setKeyManagerPassword}("keypwd");
60   * </ul>
61   * before {@link #createServerSocket} is called.
62   */
63  public class IBMSslAdapterServerSocketFactory implements SSLAdaptorServerSocketFactoryMBean
64  {
65  
66      // it will always be IBM, anyway
67      private SecurityProviderFactory spFactory = new AutoDiscoverySecurityProviderFactory();
68      private SecurityProviderInfo spInfo = spFactory.getSecurityProviderInfo();
69  
70      private String m_keyStoreType = "JKS";
71      private String m_trustStoreType = "JKS";
72      private String m_keyStoreName;
73      private String m_trustStoreName;
74      private String m_keyStorePassword;
75      private String m_trustStorePassword;
76      private String m_keyManagerAlgorithm = spInfo.getKeyManagerAlgorithm();
77      // the same?
78      private String m_trustManagerAlgorithm = spInfo.getKeyManagerAlgorithm();
79      private String m_keyManagerPassword;
80      // IMPORTANT Sun-specific version works with TLS protocol,
81      // but fails in Internet Explorer and IBM-specific provider.
82      // SSL works fine though
83      private String m_sslProtocol = "SSL";
84  
85      public IBMSslAdapterServerSocketFactory()
86      {
87          Security.addProvider(spFactory.getProvider());
88      }
89  
90      public void setKeyStoreType(String keyStoreType)
91      {
92          if (keyStoreType == null || keyStoreType.trim().length() == 0)
93          {
94              throw new IllegalArgumentException("Invalid KeyStore type");
95          }
96          m_keyStoreType = keyStoreType;
97      }
98  
99      public void setTrustStoreType(String trustStoreType)
100     {
101         if (trustStoreType == null || trustStoreType.trim().length() == 0)
102         {
103             throw new IllegalArgumentException("Invalid TrustStore type");
104         }
105         m_trustStoreType = trustStoreType;
106     }
107 
108     public void setKeyStoreName(String name)
109     {
110         if (name == null || name.trim().length() == 0)
111         {
112             throw new IllegalArgumentException("Invalid KeyStore name");
113         }
114         m_keyStoreName = name;
115     }
116 
117     public void setTrustStoreName(String name)
118     {
119         if (name == null || name.trim().length() == 0)
120         {
121             throw new IllegalArgumentException("Invalid TrustStore name");
122         }
123         m_trustStoreName = name;
124     }
125 
126     public void setKeyStorePassword(String password)
127     {
128         if (password == null || password.trim().length() == 0)
129         {
130             throw new IllegalArgumentException("Invalid KeyStore password");
131         }
132         m_keyStorePassword = password;
133     }
134 
135     public void setTrustStorePassword(String password)
136     {
137         if (password == null || password.trim().length() == 0)
138         {
139             throw new IllegalArgumentException("Invalid TrustStore password");
140         }
141         m_trustStorePassword = password;
142     }
143 
144     public void setKeyManagerAlgorithm(String algorithm)
145     {
146         if (algorithm == null || algorithm.trim().length() == 0)
147         {
148             throw new IllegalArgumentException("Invalid KeyManager algorithm");
149         }
150         m_keyManagerAlgorithm = algorithm;
151     }
152 
153     public void setTrustManagerAlgorithm(String algorithm)
154     {
155         if (algorithm == null || algorithm.trim().length() == 0)
156         {
157             throw new IllegalArgumentException("Invalid TrustManager algorithm");
158         }
159         m_trustManagerAlgorithm = algorithm;
160     }
161 
162     public void setKeyManagerPassword(String password)
163     {
164         if (password == null || password.trim().length() == 0)
165         {
166             throw new IllegalArgumentException("Invalid KeyManager password");
167         }
168         m_keyManagerPassword = password;
169     }
170 
171     public void setSSLProtocol(String protocol)
172     {
173         if (protocol == null || protocol.trim().length() == 0)
174         {
175             throw new IllegalArgumentException("Invalid SSL protocol");
176         }
177         m_sslProtocol = protocol;
178     }
179 
180     /**
181      * Returns a SSLServerSocket on the given port.
182      */
183     public ServerSocket createServerSocket(int port, int backlog, String host) throws IOException
184     {
185         if (m_keyStoreName == null)
186         {
187             throw new IOException("KeyStore file name cannot be null");
188         }
189         if (m_keyStorePassword == null)
190         {
191             throw new IOException("KeyStore password cannot be null");
192         }
193 
194         Logger logger = getLogger();
195         if (logger.isEnabledFor(Logger.TRACE))
196         {
197             logger.trace("Creating SSLServerSocket");
198             logger.trace("\tKeyStore " + m_keyStoreName + ", type " + m_keyStoreType);
199             logger.trace("\tKeyManager algorithm is " + m_keyManagerAlgorithm);
200             logger.trace("\tTrustStore " + m_trustStoreName + ", type " + m_trustStoreType);
201             logger.trace("\tTrustManager algorithm is " + m_trustManagerAlgorithm);
202             logger.trace("\tSSL protocol version is " + m_sslProtocol);
203         }
204 
205         try
206         {
207             KeyStore keystore = KeyStore.getInstance(m_keyStoreType);
208             InputStream keyStoreStream = IOUtils.getResourceAsStream(m_keyStoreName, getClass());
209             // Must check for nullity, otherwise a new empty keystore is created by
210             // KeyStore.load
211             if (keyStoreStream == null)
212             {
213                 // Let's look at the file system, maybe that the name provided is in
214                 // fact a file path
215                 File fle = FileUtils.newFile(m_keyStoreName);
216                 if (fle.exists()) keyStoreStream = new FileInputStream(fle);
217             }
218             if (keyStoreStream == null) throw new IOException("Cannot find KeyStore " + m_keyStoreName);
219             keystore.load(keyStoreStream, m_keyStorePassword.toCharArray());
220             try
221             {
222                 keyStoreStream.close();
223             }
224             catch (IOException x)
225             {
226                 // ignore
227             }
228 
229             KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(m_keyManagerAlgorithm);
230             // Use the keystore password as default if not given
231             keyFactory.init(keystore, m_keyManagerPassword == null
232                             ? m_keyStorePassword.toCharArray() : m_keyManagerPassword.toCharArray());
233 
234             TrustManagerFactory trustFactory = null;
235             if (m_trustStoreName != null)
236             {
237                 // User specified a trust store, retrieve it
238 
239                 if (m_trustStorePassword == null)
240                 {
241                     throw new IOException("TrustStore password cannot be null");
242                 }
243 
244                 KeyStore trustStore = KeyStore.getInstance(m_trustStoreType);
245                 InputStream trustStoreStream = IOUtils.getResourceAsStream(m_trustStoreName, getClass());
246                 // Check for nullity
247                 if (trustStoreStream == null)
248                 {
249                     throw new IOException("Cannot find TrustStore " + m_trustStoreName);
250                 }
251                 trustStore.load(trustStoreStream, m_trustStorePassword.toCharArray());
252 
253                 trustFactory = TrustManagerFactory.getInstance(m_trustManagerAlgorithm);
254                 trustFactory.init(trustStore);
255             }
256 
257             SSLContext context = SSLContext.getInstance(m_sslProtocol);
258             // Below call does not handle TrustManagers, needed when server must
259             // authenticate clients.
260             context.init(keyFactory.getKeyManagers(), trustFactory == null
261                             ? null : trustFactory.getTrustManagers(), null);
262 
263             SSLServerSocketFactory ssf = context.getServerSocketFactory();
264             SSLServerSocket serverSocket = (SSLServerSocket)ssf.createServerSocket(port, backlog,
265                 InetAddress.getByName(host));
266 
267             return serverSocket;
268         }
269         catch (IOException x)
270         {
271             logger.error("", x);
272             throw x;
273         }
274         catch (UnrecoverableKeyException x)
275         {
276             // Wrong password for the key
277             logger.error("Probably a bad key password", x);
278             throw new IOException("Probably a bad key password: " + x.toString());
279         }
280         catch (Exception x)
281         {
282             logger.error("Unexpected exception", x);
283             throw new IOException(x.toString());
284         }
285     }
286 
287     private Logger getLogger()
288     {
289         return Log.getLogger(getClass().getName());
290     }
291 }