Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: hk/hku/cecid/phoenix/pki/CompositeKeyStore.java


1   /*
2    * Academic Free License
3    * Version 1.0
4    *
5    * This Academic Free License applies to any software and associated 
6    * documentation (the "Software") whose owner (the "Licensor") has placed the 
7    * statement "Licensed under the Academic Free License Version 1.0" immediately 
8    * after the copyright notice that applies to the Software. 
9    *
10   * Permission is hereby granted, free of charge, to any person obtaining a copy 
11   * of the Software (1) to use, copy, modify, merge, publish, perform, 
12   * distribute, sublicense, and/or sell copies of the Software, and to permit 
13   * persons to whom the Software is furnished to do so, and (2) under patent 
14   * claims owned or controlled by the Licensor that are embodied in the Software 
15   * as furnished by the Licensor, to make, use, sell and offer for sale the 
16   * Software and derivative works thereof, subject to the following conditions: 
17   *
18   * - Redistributions of the Software in source code form must retain all 
19   *   copyright notices in the Software as furnished by the Licensor, this list 
20   *   of conditions, and the following disclaimers. 
21   * - Redistributions of the Software in executable form must reproduce all 
22   *   copyright notices in the Software as furnished by the Licensor, this list 
23   *   of conditions, and the following disclaimers in the documentation and/or 
24   *   other materials provided with the distribution. 
25   * - Neither the names of Licensor, nor the names of any contributors to the 
26   *   Software, nor any of their trademarks or service marks, may be used to 
27   *   endorse or promote products derived from this Software without express 
28   *   prior written permission of the Licensor. 
29   *
30   * DISCLAIMERS: LICENSOR WARRANTS THAT THE COPYRIGHT IN AND TO THE SOFTWARE IS 
31   * OWNED BY THE LICENSOR OR THAT THE SOFTWARE IS DISTRIBUTED BY LICENSOR UNDER 
32   * A VALID CURRENT LICENSE. EXCEPT AS EXPRESSLY STATED IN THE IMMEDIATELY 
33   * PRECEDING SENTENCE, THE SOFTWARE IS PROVIDED BY THE LICENSOR, CONTRIBUTORS 
34   * AND COPYRIGHT OWNERS "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
35   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
36   * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE 
37   * LICENSOR, CONTRIBUTORS OR COPYRIGHT OWNERS BE LIABLE FOR ANY CLAIM, DAMAGES 
38   * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 
39   * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE. 
40   *
41   * This license is Copyright (C) 2002 Lawrence E. Rosen. All rights reserved. 
42   * Permission is hereby granted to copy and distribute this license without 
43   * modification. This license may not be modified without the express written 
44   * permission of its copyright owner. 
45   */
46  
47  /* ===== 
48   *
49   * $Header: /ebxml/staff/cecid/cvs_repository/pki/src/hk/hku/cecid/phoenix/pki/CompositeKeyStore.java,v 1.13 2002/12/13 03:59:13 kcyee Exp $
50   *
51   * Code authored by:
52   *
53   * kcyee [2002-05-02]
54   *
55   * Code reviewed by:
56   *
57   * username [YYYY-MM-DD]
58   *
59   * Remarks:
60   *
61   * =====
62   */
63  
64  package hk.hku.cecid.phoenix.pki;
65  
66  import java.io.File;
67  import java.io.FileInputStream;
68  import java.io.FileOutputStream;
69  import java.io.FileNotFoundException;
70  import java.io.IOException;
71  import java.io.ObjectInputStream;
72  import java.io.ObjectOutputStream;
73  import java.lang.reflect.Constructor;
74  import java.lang.reflect.InvocationTargetException;
75  import java.security.Key;
76  import java.security.KeyStore;
77  import java.security.KeyStoreException;
78  import java.security.NoSuchAlgorithmException;
79  import java.security.NoSuchProviderException;
80  import java.security.Provider;
81  import java.security.Security;
82  import java.security.UnrecoverableKeyException;
83  import java.security.cert.Certificate;
84  import java.security.cert.CertificateException;
85  import java.util.Date;
86  import java.util.Enumeration;
87  import java.util.Hashtable;
88  import java.util.Vector;
89  import hk.hku.cecid.phoenix.common.util.Logger;
90  import hk.hku.cecid.phoenix.common.util.Property;
91  import hk.hku.cecid.phoenix.common.util.Version;
92  
93  /**
94   * Composite keystore which manages keystores of different types. A typical
95   * Java keystore supports only one keystore type per file. That will be
96   * inconvenient for applications to manage several types of keystore. Also,
97   * this composite keystore supports managing multiple keystore files. This 
98   * can be viewed as a keystore registry, that is, this object manages a pool
99   * of keystore files.
100  *
101  * @author kcyee
102  * @version $Revision: 1.13 $
103  */
104 public class CompositeKeyStore {
105 
106     /**
107      * Logger
108      */
109     protected static Logger logger = 
110         Logger.getLogger(CompositeKeyStore.class.getName());
111 
112     /**
113      * Internal storage of the keystore file information
114      */
115     protected Hashtable storage;
116 
117     /**
118      * Internal storage of the aliases inside the keystore file
119      */
120     protected Hashtable cache;
121 
122     /**
123      * Internal storage of the keystore object
124      */
125     protected Vector keystores;
126 
127     /**
128      * Default constructor. The internal variables are being initialized.
129      */
130     public CompositeKeyStore() {
131         storage = new Hashtable();
132         cache = null;
133         keystores = new Vector();
134     }
135 
136     /**
137      * Adds a keystore file to the keystore management pool.
138      *
139      * @param keyFile the name of the keystore file
140      * @param type the type of the keystore
141      * @param password the password for accessing the keystore
142      */
143     public void addKeyStoreFile(String keyFile, String type, char[] password) {
144         if (keyFile == null) {
145             logger.debug("Keystore file cannot be <null>!");
146         }
147         else {
148             File f = new File(keyFile);
149             if (f == null || !f.exists()) {
150                 logger.debug("Keystore file " + keyFile + " not found!");
151             }
152             else {
153                 addKeyStoreFile(f, type, password);
154             }
155         }
156     }
157 
158     /**
159      * Adds a keystore file to the keystore management pool.
160      *
161      * @param keyFile the keystore file
162      * @param type the type of the keystore
163      * @param password the password for accessing the keystore
164      */
165     protected void addKeyStoreFile(File keyFile, String type, char[] password) {
166         KeyStoreFileProp ksp = new KeyStoreFileProp(type, password);
167         try {
168             storage.put(keyFile.getCanonicalPath(), ksp);
169         }
170         catch (IOException e) {}
171     }
172 
173     /**
174      * Gets the first KeyStore object from the keystore management pool.
175      *
176      * @return the first KeyStore object from the keystore management pool
177      */
178     public KeyStore getKeyStore() {
179         if (cache == null) {
180             loadCache();
181         }
182         if (keystores.size() > 0) {
183             return (KeyStore) keystores.get(0);
184         }
185         else {
186             return null;
187         }
188     }
189 
190     /**
191      * Removes a keystore file from the keystore management pool.
192      *
193      * @param keyFile the name of the keystore file
194      */
195     public void removeKeyStoreFile(String keyFile) {
196         removeKeyStoreFile(new File(keyFile));
197     }
198 
199     /**
200      * Removes a keystore file from the keystore management pool.
201      *
202      * @param keyFile the keystore file
203      */
204     protected void removeKeyStoreFile(File keyFile) {
205         try {
206             storage.remove(keyFile.getCanonicalPath());
207         }
208         catch (IOException e) {}
209     }
210 
211     /**
212      * Gets an instance of the keystore of correct type. This function
213      * will consider the Java version and determine whether to use
214      * JSSE or not. For Java version 1.4 or above, JSSE is built in.
215      * So, no need to call an external provider to create an instance
216      * of PKCS#12 formatted keystore. Otherwise, JSSE should be used, and
217      * we make use of dynamic binding to load the JSSE library.
218      *
219      * @param fileName the keystore file name to load
220      * @param ksp other keystore parameters for loading
221      * @return keystore instance of the correct type
222      */
223     protected KeyStore loadKeyStore(String fileName, KeyStoreFileProp ksp) {
224         KeyStore ks = null;
225         if (ksp.getType() == null) {
226             KeyStoreFileProp ksp_new = 
227                 new KeyStoreFileProp("JKS", ksp.getPassword());
228             ks = loadKeyStore(fileName, ksp_new);
229             if (ks == null) {
230                 ksp_new = new KeyStoreFileProp("PKCS12", ksp.getPassword());
231                 ks = loadKeyStore(fileName, ksp_new);
232             }
233             return ks;
234         }
235         else if (ksp.getType().toUpperCase().equals("JKS")) {
236             try {
237                 ks = KeyStore.getInstance("JKS");
238             }
239             catch (KeyStoreException e) {}
240         }
241         else if (ksp.getType().toUpperCase().equals("PKCS12")) {
242             if (isUsingJSSE()) {
243                 try {
244                     Class clsProv = Class.forName(
245                         "com.sun.net.ssl.internal.ssl.Provider");
246                     Constructor c = clsProv.getConstructor(null);
247                     Provider provider = (Provider) c.newInstance(null);
248                     if (Security.getProvider(provider.getName()) == null) {
249                         Security.addProvider(provider);
250                     }
251                 }
252                 catch (ClassNotFoundException e) {}
253                 catch (NoSuchMethodException e) {}
254                 catch (InstantiationException e) {}
255                 catch (IllegalAccessException e) {}
256                 catch (InvocationTargetException e) {}
257 
258                 try {
259                     ks = KeyStore.getInstance("PKCS12", "SunJSSE");
260                 }
261                 catch (NoSuchProviderException e) {}
262                 catch (KeyStoreException e) {}
263             }
264             else {
265                 try {
266                     ks = KeyStore.getInstance("PKCS12");
267                 }
268                 catch (KeyStoreException e) {}
269             }
270         }
271         if (ks != null) {
272             try {
273                 ks.load(new FileInputStream(fileName), ksp.getPassword());
274                 return ks;
275             }
276             catch (IOException e) {}
277             catch (NoSuchAlgorithmException e) {}
278             catch (CertificateException e) {}
279         }
280         return null;
281     }
282 
283     /**
284      * Loads the keystores pointed by this composite keystore into memory
285      * and create a caching of aliases.
286      */
287     protected void loadCache() {
288         cache = new Hashtable();
289 
290         Enumeration fileNames = storage.keys();
291         while (fileNames.hasMoreElements()) {
292             String fileName = (String) fileNames.nextElement();
293             KeyStoreFileProp ksp = (KeyStoreFileProp) storage.get(fileName);
294 
295             KeyStore ks = loadKeyStore(fileName, ksp);
296             keystores.add(ks);
297             if (ks != null) {
298                 try {
299                     Enumeration ks_alias = ks.aliases();
300                     while (ks_alias.hasMoreElements()) {
301                         cache.put(ks_alias.nextElement(), ks);
302                     }
303                 }
304                 catch (KeyStoreException e) {}
305             }
306         }
307     }
308 
309     /**
310      * Gets all the aliases of the keystores pointed by this composite
311      * keystore.
312      *
313      * @return an enumeration of string, holding the aliases of the keys
314      */
315     public Enumeration aliases() {
316         if (cache == null) {
317             loadCache();
318         }
319         return cache.keys();
320     }
321 
322     /**
323      * Determines whether a given alias exists in one of the keystores
324      * pointed by this composite keystore or not.
325      *
326      * @param alias the alias of the key/certificate
327      * @return true if the alias exists in one of the keystores, false
328      &         otherwise
329      */
330     public boolean containsAlias(String alias) {
331         if (cache == null) {
332             loadCache();
333         }
334         return (cache.get(alias) != null);
335     }
336 
337     /**
338      * Gets the certificate named by the given alias, from the collection
339      * of keystores pointed by this composite keystore.
340      *
341      * @param alias the alias of the key/certificate
342      * @return the certificate named by the given alias, null if not found
343      * @throws KeyStoreException the keystore is corrupted
344      */
345     public Certificate getCertificate(String alias) throws KeyStoreException {
346         if (cache == null) {
347             loadCache();
348         }
349         KeyStore ks = (KeyStore) cache.get(alias);
350         if (ks != null) {
351             return ks.getCertificate(alias);
352         }
353         return null;
354     }
355 
356     /**
357      * Gets the alias of the specified certificate.
358      *
359      * @param cert the certificate
360      * @return the alias of the certificate, if the certificate can be found
361      *         in the collection of keystores pointed by this composite
362      *         keystore. Otherwise, null will be returned
363      */
364     public String getCertificateAlias(Certificate cert) {
365         if (cache == null) {
366             loadCache();
367         }
368         Enumeration keyStores = cache.elements();
369         while (keyStores.hasMoreElements()) {
370             KeyStore ks = (KeyStore) keyStores.nextElement();
371             try {
372                 String alias = ks.getCertificateAlias(cert);
373                 if (alias != null) {
374                     return alias;
375                 }
376             }
377             catch (KeyStoreException e) {}
378         }
379         return null;
380     }
381 
382     /**
383      * Gets the certificate chain by the specified alias.
384      *
385      * @param alias the alias of the key/certificate
386      * @return the certificate chain by the specified alias, null if not found
387      * @throws KeyStoreException the keystore is corrupted
388      */
389     public Certificate[] getCertificateChain(String alias) 
390         throws KeyStoreException {
391         if (cache == null) {
392             loadCache();
393         }
394         KeyStore ks = (KeyStore) cache.get(alias);
395         if (ks != null) {
396             return ks.getCertificateChain(alias);
397         }
398         return null;
399     }
400 
401     /**
402      * Gets the creation date of the key/certificate by the specified alias.
403      *
404      * @param alias the alias of the key/certificate
405      * @return the creation date of the key/certificate by the specified alias, 
406      *         null if not found
407      * @throws KeyStoreException the keystore is corrupted
408      */
409     public Date getCreationDate(String alias) throws KeyStoreException {
410         if (cache == null) {
411             loadCache();
412         }
413         KeyStore ks = (KeyStore) cache.get(alias);
414         if (ks != null) {
415             return ks.getCreationDate(alias);
416         }
417         return null;
418     }
419 
420     /**
421      * Gets the key by the specified alias. A password should be given also
422      * to retrieve the key.
423      *
424      * @param alias the alias of the key/certificate
425      * @param password the password to retrieve the key
426      * @return the key specified by the alias, null if not found
427      * @throws KeyStoreException the keystore is corrupted
428      * @throws NoSuchAlgorithmException the keystore cannot be read
429      * @throws UnrecoverableKeyException the keystore cannot be read
430      */
431     public Key getKey(String alias, char[] password) throws 
432         KeyStoreException, NoSuchAlgorithmException, UnrecoverableKeyException {
433         if (cache == null) {
434             loadCache();
435         }
436         KeyStore ks = (KeyStore) cache.get(alias);
437         if (ks != null) {
438             return ks.getKey(alias, password);
439         }
440         return null;
441     }
442 
443     /**
444      * Determines whether the specified alias is specifying a certificate 
445      * or not.
446      *
447      * @param alias the alias of the key/certificate
448      * @throws KeyStoreException the keystore is corrupted
449      */
450     public boolean isCertificateEntry(String alias) throws KeyStoreException {
451         if (cache == null) {
452             loadCache();
453         }
454         KeyStore ks = (KeyStore) cache.get(alias);
455         if (ks != null) {
456             return ks.isCertificateEntry(alias);
457         }
458         return false;
459     }
460 
461     /**
462      * Determines whether the specified alias is specifying a key 
463      * or not.
464      *
465      * @param alias the alias of the key/certificate
466      * @throws KeyStoreException the keystore is corrupted
467      */
468     public boolean isKeyEntry(String alias) throws KeyStoreException {
469         if (cache == null) {
470             loadCache();
471         }
472         KeyStore ks = (KeyStore) cache.get(alias);
473         if (ks != null) {
474             return ks.isKeyEntry(alias);
475         }
476         return false;
477     }
478 
479     /**
480      * Gets the total number of keys/certificates in all the keystores
481      * pointed by this composite keystore.
482      *
483      * @return the total number of keys/certificates
484      */
485     public int size() {
486         if (cache == null) {
487             loadCache();
488         }
489         return cache.size();
490     }
491 
492     /**
493      * Loads the composite keystore from a persistent file in the file
494      * system.
495      *
496      * @param storeFileName the name of the composite keystore persistent file
497      * @throws InitializationException the persistent file is corrupted
498      */
499     public void load(String storeFileName) throws InitializationException {
500         load(new File(storeFileName));
501     }
502 
503     /**
504      * Loads the composite keystore from a persistent file in the file
505      * system.
506      *
507      * @param storeFile the composite keystore persistent file
508      * @throws InitializationException the persistent file is corrupted
509      */
510     public void load(File storeFile) throws InitializationException {
511         try {
512             ObjectInputStream ois = new ObjectInputStream(
513                 new FileInputStream(storeFile));
514             storage = (Hashtable) ois.readObject();
515             ois.close();
516         }
517         catch (FileNotFoundException e) {
518             storage = null;
519             throw new InitializationException("FileNotFound Exception\n" 
520                 + e.getMessage());
521         }
522         catch (IOException e) {
523             storage = null;
524             throw new InitializationException("IO Exception\n" 
525                 + e.getMessage());
526         }
527         catch (ClassNotFoundException e) {
528             storage = null;
529             throw new InitializationException("ClassNotFound Exception\n" 
530                 + e.getMessage());
531         }
532     }
533 
534     /**
535      * Stores the composite keystore to a persistent file in the file
536      * system.
537      *
538      * @param storeFileName the name of the composite keystore persistent file
539      * @throws StoreException the composite keystore is not successfully stored
540      */
541     public void store(String storeFileName) throws StoreException {
542         store(new File(storeFileName));
543     }
544 
545     /**
546      * Stores the composite keystore to a persistent file in the file
547      * system.
548      *
549      * @param storeFile the composite keystore persistent file
550      * @throws StoreException the composite keystore is not successfully stored
551      */
552     public void store(File storeFile) throws StoreException {
553         try {
554             ObjectOutputStream oos = new ObjectOutputStream(
555                 new FileOutputStream(storeFile));
556             oos.writeObject(storage);
557             oos.close();
558         }
559         catch (FileNotFoundException e) {
560             throw new StoreException("FileNotFound Exception\n" 
561                 + e.getMessage());
562         }
563         catch (IOException e) {
564             throw new StoreException("IO Exception\n" 
565                 + e.getMessage());
566         }
567     }
568 
569     /**
570      * Determines whether JSSE should be used to access PKCS#12 formatted
571      * keystore. For JDK1.4 or above, JSSE is built into the JDK. Therefore,
572      * no additional provider have to be added.
573      *
574      * @return true if JSSE should be used; false if otherwise
575      */
576     protected static boolean isUsingJSSE() {
577         double javaVersion = Version.getJDKVersion();
578         return (javaVersion < 1.4);
579     }
580 }