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