View Javadoc

1   /*
2    * Copyright (c) 2006-2007 Creative Sphere Limited.
3    * All rights reserved. This program and the accompanying materials
4    * are made available under the terms of the Eclipse Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/epl-v10.html
7    *
8    * Contributors:
9    *
10   *   Creative Sphere - initial API and implementation
11   *
12   */
13  package org.abstracthorizon.extend.server.auth.jaas.keystore;
14  
15  import java.io.File;
16  import java.io.FileOutputStream;
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.io.OutputStream;
20  import java.lang.reflect.Method;
21  import java.math.BigInteger;
22  import java.net.MalformedURLException;
23  import java.net.URLConnection;
24  import java.security.Key;
25  import java.security.KeyPair;
26  import java.security.KeyPairGenerator;
27  import java.security.KeyStore;
28  import java.security.KeyStoreException;
29  import java.security.NoSuchAlgorithmException;
30  import java.security.NoSuchProviderException;
31  import java.security.Security;
32  import java.security.cert.Certificate;
33  import java.security.cert.CertificateException;
34  import java.util.ArrayList;
35  import java.util.Date;
36  import java.util.Enumeration;
37  import java.util.HashMap;
38  import java.util.List;
39  import java.util.Map;
40  
41  import javax.security.auth.login.AppConfigurationEntry;
42  import javax.security.auth.login.Configuration;
43  
44  import org.bouncycastle.jce.X509Principal;
45  import org.bouncycastle.jce.provider.BouncyCastleProvider;
46  import org.bouncycastle.x509.X509V3CertificateGenerator;
47  import org.slf4j.Logger;
48  import org.slf4j.LoggerFactory;
49  import org.springframework.beans.BeansException;
50  import org.springframework.beans.factory.BeanInitializationException;
51  import org.springframework.context.ApplicationContext;
52  import org.springframework.context.ApplicationContextAware;
53  import org.springframework.core.io.Resource;
54  
55  
56  /**
57   * This is keystore login module service. This class sets up keystore login module
58   *
59   * @author Daniel Sendula
60   */
61  public class KeyStoreModuleService implements ApplicationContextAware {
62  
63      protected Logger logger = LoggerFactory.getLogger(getClass());
64  
65      /** Keystore URL */
66      private String keystore;
67  
68      /** Keystore password */
69      private String keystorePassword;
70  
71      /** Keystore type */
72      private String keystoreType = KeyStore.getDefaultType();
73  
74      /** Keystore provider */
75      private String keystoreProvider = "";
76  
77      /** Login context name */
78      private String loginContext;
79  
80      /** Control flag defaulted to "requrired"*/
81      private String controlFlag = "required";
82  
83      /** Configuration */
84      private Configuration configuration;
85  
86      /** Options */
87      private Map<String, Object> options = new HashMap<String, Object>();
88  
89      /** Context this service was defined in */
90      private ApplicationContext context;
91  
92      private Resource keystoreResource;
93  
94      static {
95          Security.addProvider(new BouncyCastleProvider());
96      }
97  
98      /**
99       * Default constructor
100      */
101     public KeyStoreModuleService() {
102     }
103 
104     /**
105      * Starts the service - adding keystore login module to system application configuration entry
106      * @throws Exception
107      */
108     public void start() throws Exception {
109 
110         AppConfigurationEntry.LoginModuleControlFlag flag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
111 
112         if (AppConfigurationEntry.LoginModuleControlFlag.REQUIRED.toString().indexOf(controlFlag) > 0 ) {
113             flag = AppConfigurationEntry.LoginModuleControlFlag.REQUIRED;
114         } else if( AppConfigurationEntry.LoginModuleControlFlag.REQUISITE.toString().indexOf(controlFlag) > 0 ) {
115             flag = AppConfigurationEntry.LoginModuleControlFlag.REQUISITE;
116         } else if( AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT.toString().indexOf(controlFlag) > 0 ) {
117             flag = AppConfigurationEntry.LoginModuleControlFlag.SUFFICIENT;
118         }else if( AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL.toString().indexOf(controlFlag) > 0 ) {
119             flag = AppConfigurationEntry.LoginModuleControlFlag.OPTIONAL;
120         }
121 
122         AppConfigurationEntry entry = new AppConfigurationEntry(KeyStoreLoginModule.class.getName(), flag, options);
123 
124         AppConfigurationEntry[] list = new AppConfigurationEntry[1];
125         list[0] = entry;
126         Method method = configuration.getClass().getMethod("setAppConfigurationEntry", new Class[]{String.class, list.getClass()});
127         Object[] args = {loginContext, list};
128         method.invoke(configuration, args);
129         logger.info("Set up login context '" + loginContext + "'");
130     }
131 
132 
133     public void stop() throws Exception {
134         Method method = configuration.getClass().getMethod("removeAppConfigurationEntry", new Class[]{String.class});
135         Object[] args = {loginContext};
136         method.invoke(configuration, args);
137         logger.info("Removed login context '" + loginContext + "'");
138     }
139 
140     /**
141      * Lists certificates (users) from the keystore
142      * @return users as a string
143      * @throws Exception
144      */
145     public String listUsersString() throws Exception {
146         List<String> u = listUsers();
147         String[] users = new String[u.size()];
148         users = (String[])u.toArray(users);
149 
150         StringBuffer res = new StringBuffer();
151         res.append("[");
152         for (int i=0; i<users.length; i++) {
153             if (i > 0) {
154                 res.append(", ");
155             }
156             res.append(users[i]);
157         }
158         res.append("]");
159         return res.toString();
160     }
161 
162     /**
163      * Returns a list of all users in the keystore
164      * @return list of all users in the keystore
165      * @throws Exception
166      */
167     public List<String> listUsers() throws Exception {
168         KeyStore keyStore = loadKeyStore();
169         ArrayList<String> users = new ArrayList<String>();
170         Enumeration<String> en = keyStore.aliases();
171         while (en.hasMoreElements()) {
172             users.add(en.nextElement());
173         }
174         return users;
175     }
176 
177     /**
178      * Changes the password of the user
179      * @param user user
180      * @param oldPassword old password
181      * @param newPassword new password
182      * @throws Exception
183      */
184     public void changePassword(String user, String oldPassword, String newPassword) throws Exception {
185         KeyStore keyStore = loadKeyStore();
186         Key key = keyStore.getKey(user, oldPassword.toCharArray());
187 
188         Certificate[] certs = keyStore.getCertificateChain(user);
189 
190         keyStore.setKeyEntry(user, key, newPassword.toCharArray(), certs);
191         storeKeyStore(keyStore);
192     }
193 
194     /**
195      * Adds new user to the store
196      * @param user user
197      * @param passwd password
198      * @throws Exception
199      */
200     public void addUser(String user, String passwd) throws Exception {
201         KeyStore keyStore = loadKeyStore();
202 
203         String name = "CN="+user+", OU=, O=, L=, ST=, C=";
204             //CN=Me, OU=Java Card Development, O=MyFirm, C=UK, ST=MyCity";
205 
206         Security.addProvider(new BouncyCastleProvider());
207 
208         X509V3CertificateGenerator generator = new X509V3CertificateGenerator();
209         KeyPairGenerator kpGen = KeyPairGenerator.getInstance("RSA");
210         kpGen.initialize(1024);
211         KeyPair pair = kpGen.generateKeyPair();
212 
213         generator.setSerialNumber(BigInteger.valueOf(1));
214         generator.setIssuerDN(new X509Principal(name));
215         generator.setNotBefore(new Date());
216         generator.setNotAfter(new Date(System.currentTimeMillis()+1000*60*60*24*365));
217         generator.setSubjectDN(new X509Principal(name));
218         generator.setPublicKey(pair.getPublic());
219         generator.setSignatureAlgorithm("MD5WithRSAEncryption");
220 
221         Certificate cert = generator.generate(pair.getPrivate(), "BC");
222 
223         keyStore.setKeyEntry(user, pair.getPrivate(), passwd.toCharArray(), new Certificate[]{cert});
224         storeKeyStore(keyStore);
225     }
226 
227     /**
228      * Removes certificate  - user
229      * @param user username
230      * @throws Exception
231      */
232     public void removeUser(String user) throws Exception {
233         KeyStore keyStore = loadKeyStore();
234         keyStore.deleteEntry(user);
235         storeKeyStore(keyStore);
236     }
237 
238     protected KeyStore loadKeyStore() throws KeyStoreException, NoSuchProviderException, MalformedURLException,
239         IOException, NoSuchAlgorithmException, CertificateException {
240 
241         logger.info("Loading keystore from " + keystoreResource.toString() + " for login context '" + loginContext + "'");
242         InputStream keystoreInputStream = keystoreResource.getInputStream();
243         try {
244             KeyStore keyStore;
245             if ((keystoreProvider == null) || (keystoreProvider.length() == 0)) {
246                 keyStore = KeyStore.getInstance(keystoreType);
247             } else {
248                 keyStore = KeyStore.getInstance(keystoreType, keystoreProvider);
249             }
250 
251             /* Load KeyStore contents from file */
252             keyStore.load(keystoreInputStream, keystorePassword.toCharArray());
253             return keyStore;
254 
255         } finally {
256             keystoreInputStream.close();
257         }
258     }
259 
260     /**
261      * Stores keystore back to provided resource
262      * @param keystore keystore to be stored
263      * @throws KeyStoreException
264      * @throws NoSuchProviderException
265      * @throws MalformedURLException
266      * @throws IOException
267      * @throws NoSuchAlgorithmException
268      * @throws CertificateException
269      */
270     protected void storeKeyStore(KeyStore keystore) throws KeyStoreException, NoSuchProviderException, MalformedURLException,
271         IOException, NoSuchAlgorithmException, CertificateException {
272 
273         logger.info("Storing keystore to " + keystoreResource.toString() + " for login context '" + loginContext + "'");
274         OutputStream keystoreOutputStream = null;
275         try {
276             File file = keystoreResource.getFile();
277             keystoreOutputStream = new FileOutputStream(file);
278         } catch (IOException e) {
279             URLConnection connection = keystoreResource.getURL().openConnection();
280             connection.setDoOutput(true);
281             keystoreOutputStream = connection.getOutputStream();
282         }
283 
284         try {
285             keystore.store(keystoreOutputStream, keystorePassword.toCharArray());
286         } finally {
287             keystoreOutputStream.close();
288         }
289     }
290 
291     /**
292      * Sets keystore password
293      * @param password keystore password
294      */
295     public void setKeyStorePassword(String password) {
296         this.keystorePassword = password;
297         if (password != null) {
298             options.put("keyStorePassword", password);
299         } else {
300             options.remove("keyStorePassword");
301         }
302     }
303 
304     /**
305      * Keystoore location
306      * @param keystore keystore location
307      * @throws IOException
308      */
309     public void setKeyStore(String keystore) throws IOException {
310         this.keystore = keystore;
311 
312         if ((keystore != null) && (context != null)) {
313             keystoreResource = context.getResource(keystore);
314             options.put("keyStoreURL", keystoreResource.getURL().toString());
315         } else {
316             keystoreResource = null;
317             options.remove("keyStoreURL");
318         }
319     }
320 
321     /**
322      * Returns keystore url
323      * @return keystore url
324      */
325     public String getKeyStoreURL() {
326         return keystore;
327     }
328 
329     /**
330      * Sets keystore type
331      * @param type keystore type
332      */
333     public void setKeyStoreType(String type) {
334         keystoreType = type;
335         if (type != null) {
336             options.put("keyStoreType", type);
337         } else {
338             options.remove("keyStoreType");
339         }
340     }
341 
342     /**
343      * Returns keystore type
344      * @return keystore type
345      */
346     public String getKeyStoreType() {
347         return keystoreType;
348     }
349 
350     /**
351      * Sets keystore provider
352      * @param provider keystore provider
353      */
354     public void setKeyStoreProvider(String provider) {
355         this.keystoreProvider = provider;
356         if (provider != null) {
357             options.put("keyStoreProvider", provider);
358         } else {
359             options.remove("keyStoreProvider");
360         }
361     }
362 
363     /**
364      * Returns keystore provider
365      * @return keystore provider
366      */
367     public String getKeyStoreProvider() {
368         return keystoreProvider;
369     }
370 
371     /**
372      * Sets login context name
373      * @param loginContext login context name
374      */
375     public void setLoginContext(String loginContext) {
376         this.loginContext = loginContext;
377     }
378 
379     /**
380      * Returns login context name
381      * @return login context name
382      */
383     public String getLoginContext() {
384         return loginContext;
385     }
386 
387     /**
388      * Sets control flag
389      * @param controlFlag control flag
390      */
391     public void setControlFlag(String controlFlag) {
392         this.controlFlag = controlFlag;
393     }
394 
395     /**
396      * Returns control flag
397      * @return control flag
398      */
399     public String getControlFlag() {
400         return controlFlag;
401     }
402 
403     /**
404      * Returns configuration
405      * @return configuration
406      */
407     public Configuration getConfiguration() {
408         return configuration;
409     }
410 
411     /**
412      * Sets configuration
413      * @param configuration configuration
414      */
415     public void setConfiguration(Configuration configuration) {
416         this.configuration = configuration;
417     }
418 
419     /**
420      * Sets application context (module) this service is defined in
421      * @param context context
422      */
423     public void setApplicationContext(ApplicationContext context) throws BeansException {
424         this.context = context;
425         try {
426             setKeyStore(keystore);
427         } catch (IOException e) {
428             throw new BeanInitializationException("Problem setting keystore", e);
429         }
430     }
431 }