KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sslexplorer > security > PublicKeyStore


1 package com.sslexplorer.security;
2
3 import java.io.ByteArrayInputStream JavaDoc;
4 import java.io.ByteArrayOutputStream JavaDoc;
5 import java.io.IOException JavaDoc;
6 import java.io.OutputStream JavaDoc;
7 import java.util.HashMap JavaDoc;
8 import java.util.StringTokenizer JavaDoc;
9
10 import javax.crypto.Cipher;
11
12 import org.apache.commons.logging.Log;
13 import org.apache.commons.logging.LogFactory;
14
15 import sun.awt.image.ByteArrayImageSource;
16
17 import com.maverick.crypto.encoders.Base64;
18 import com.sslexplorer.boot.RepositoryFactory;
19 import com.sslexplorer.boot.RepositoryStore;
20 import com.sslexplorer.boot.Util;
21 import com.sslexplorer.properties.Property;
22 import com.sslexplorer.properties.impl.systemconfig.SystemConfigKey;
23 import com.sslexplorer.security.pki.InvalidKeyException;
24 import com.sslexplorer.security.pki.SshKeyGenerator;
25 import com.sslexplorer.security.pki.SshPrivateKey;
26 import com.sslexplorer.security.pki.SshPrivateKeyFile;
27 import com.sslexplorer.security.pki.SshPublicKey;
28 import com.sslexplorer.security.pki.SshPublicKeyFile;
29
30 /**
31  * Provides a repository backed PKI store.
32  * @author lee
33  *
34  */

35 public class PublicKeyStore {
36
37     private static PublicKeyStore instance;
38
39     static Log log = LogFactory.getLog(PublicKeyStore.class);
40     
41     HashMap JavaDoc loadedPrivateKeys = new HashMap JavaDoc();
42     HashMap JavaDoc loadedPublicKeys = new HashMap JavaDoc();
43     RepositoryStore store;
44     
45     String JavaDoc keyType = "rsa";
46     int bitLength = 1024;
47     
48     public static PublicKeyStore getInstance() {
49         return instance==null ? instance = new PublicKeyStore() : instance;
50     }
51     
52     PublicKeyStore() {
53         store = RepositoryFactory.getRepository().getStore("PKI");
54         
55         try {
56             keyType = Property.getProperty(new SystemConfigKey("pki.algorithm"));
57             bitLength = Property.getPropertyInt(new SystemConfigKey("pki.bitLength"));
58         } catch (Exception JavaDoc e) {
59             log.error("Could not get PKI properties! defaults will be used");
60             keyType = "rsa";
61             bitLength = 1024;
62         }
63     }
64     
65     /**
66      * Must be called after a user has logged on to initialize their private key. This will be used
67      * to encrypt/decrpyt confidential data.
68      *
69      * @param user
70      * @param pw
71      * @throws PromptForPasswordException The UI must prompt the user for their password and call this method again
72      * @throws FatalKeyException A critical error has occurred.
73      * @throws UpdatePrivateKeyPassphraseException The UI must prompt for the users old password so that their private key can be updated.
74      * The changePrivateKeyPassphrase method must be called prior to calling this method again.
75      */

76     public void verifyPrivateKey(String JavaDoc username, char[] pw) throws PromptForPasswordException, FatalKeyException, UpdatePrivateKeyPassphraseException {
77         
78         
79         if(!PublicKeyStore.getInstance().hasPrivateKey(username)) {
80
81             if(pw==null) {
82             
83                 /**
84                  * We need to generate a private key so we must ask the user for their
85                  * current password so we can encrpyt it.
86                  */

87                 throw new PromptForPasswordException();
88             }
89             
90             try {
91                 /**
92                  * This call will generate a private key for the user and load it into memory
93                  */

94                 getPrivateKey(username, new String JavaDoc(pw));
95             } catch (Exception JavaDoc e) {
96                 log.error("Error creating users private key", e);
97                 
98                 /**
99                  * This is a critical error and will stop the user using confidential
100                  * attributes.
101                  */

102                 throw new FatalKeyException();
103             }
104         }
105         
106         /**
107          * The user has a private key so lets check to see if we need to ask for
108          * their password in order to decrypt it
109          */

110         if(!PublicKeyStore.getInstance().hasLoadedKey(username)) {
111             
112             if(pw==null) {
113                
114                 /**
115                  * At some point we're going to have to ask for the users password
116                  * so that we can load the private key into memory. Should this be done now
117                  * or later?
118                  *
119                  * If we do this now and persist keys in memory then the user will only
120                  * have to do this when they login, saving us from having to ask elsewhere.
121                  * The only issue is that when a password changes the user will not be prompted
122                  * until the server has been restarted, this may be sometime after the password
123                  * has changed.
124                  */

125                 throw new PromptForPasswordException();
126                 
127             } else {
128                
129                 if(!PublicKeyStore.getInstance().hasPassphraseChanged(username, new String JavaDoc(pw)))
130                     throw new UpdatePrivateKeyPassphraseException();
131                 
132                 try {
133                     PublicKeyStore.getInstance().getPrivateKey(username, new String JavaDoc(pw));
134                 } catch (IOException JavaDoc e) {
135                     log.error("Error loading users private key", e);
136                     throw new FatalKeyException();
137                     
138                 } catch (InvalidKeyException e) {
139                     throw new UpdatePrivateKeyPassphraseException();
140                 }
141             }
142         }
143     }
144     
145     /**
146      * Change the private key passphrase.
147      * @param username username
148      * @param oldPassphrase
149      * @param newPassphrase
150      * @throws FatalKeyException
151      */

152     public void changePrivateKeyPassphrase(String JavaDoc username, String JavaDoc oldPassphrase, String JavaDoc newPassphrase) throws FatalKeyException {
153         
154         
155         try {
156             changePassphrase(username, oldPassphrase, newPassphrase);
157         } catch (Exception JavaDoc e) {
158             log.error("Error changing users private key passphrase", e);
159             throw new FatalKeyException();
160         }
161         
162     }
163     
164     /**
165      * Encrypt some text with the users public key. This can only be decrypted by the user.
166      * @param text
167      * @param username
168      * @return
169      * @throws FatalKeyException
170      */

171     public String JavaDoc encryptText(String JavaDoc text, String JavaDoc username) throws FatalKeyException {
172         
173         try {
174             
175             String JavaDoc cipherText = "";
176             
177             
178             byte[] plainText = text.getBytes();
179             
180             for(int i=0;i<plainText.length;i+=117) {
181                 SshPublicKey pk = getPublicKey(username);
182                 
183                 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
184                 
185                 cipher.init(Cipher.ENCRYPT_MODE, pk.getPublicKey());
186                 
187                 byte[] ctext = cipher.doFinal(plainText, i, (plainText.length - i > 117 ? 117 : plainText.length - i));
188             
189                 String JavaDoc section = new String JavaDoc(Base64.encode(ctext), "US-ASCII");
190                 cipherText += section + "\n";
191                 
192                 
193                 
194             }
195             
196         
197             return cipherText;
198             
199         } catch (Exception JavaDoc e) {
200             log.error("Error encrpyting data", e);
201             throw new FatalKeyException();
202         }
203     }
204     
205     /**
206      * Decrypt some cipher text into plain text.
207      * @param text
208      * @param username
209      * @return
210      * @throws FatalKeyException
211      * @throws PrivateKeyNotInitializedException
212      */

213     public String JavaDoc decryptText(String JavaDoc text, String JavaDoc username) throws FatalKeyException, PrivateKeyNotInitializedException {
214         
215         if(!hasLoadedKey(username))
216             throw new PrivateKeyNotInitializedException();
217         
218         try {
219             SshPrivateKey pk = (SshPrivateKey) loadedPrivateKeys.get(username);
220             
221             
222             StringTokenizer JavaDoc blocks = new StringTokenizer JavaDoc(text, "\n");
223             
224             String JavaDoc plainText = "";
225             
226             
227             while(blocks.hasMoreTokens()) {
228                 Cipher cipher = Cipher.getInstance("RSA/ECB/PKCS1Padding");
229                 
230                 cipher.init(Cipher.DECRYPT_MODE, pk.getPrivateKey());
231                 
232                 byte[] encrypted = Base64.decode(blocks.nextToken());
233                 byte[] ctext = cipher.doFinal(encrypted);
234                 
235                 plainText += new String JavaDoc(ctext);
236             }
237             
238             
239             return plainText;
240             
241         } catch (Exception JavaDoc e) {
242             log.error("Error decrypting cipher text", e);
243             throw new FatalKeyException();
244         }
245     }
246         
247     
248     /**
249      * Get a users private key. If the key is currently cached then return it, else try to unlock and cache
250      * @param username
251      * @param passphrase
252      * @return
253      * @throws IOException
254      * @throws InvalidKeyException
255      */

256     protected SshPrivateKey getPrivateKey(String JavaDoc username, String JavaDoc passphrase) throws IOException JavaDoc, InvalidKeyException {
257         
258         if(loadedPrivateKeys.containsKey(username))
259             return (SshPrivateKey) loadedPrivateKeys.get(username);
260         
261         SshPrivateKey pk = getPrivateKeyFromStore(username, passphrase);
262         
263         loadedPrivateKeys.put(username, pk);
264         
265         return pk;
266     
267     }
268
269     /**
270      * Remove the users cached keys. This would generally be called when the
271      * users password changes.
272      *
273      * @param username user to remove cache keys for
274      */

275     public void removeCachedKeys(String JavaDoc username) {
276         loadedPrivateKeys.remove(username);
277         loadedPublicKeys.remove(username);
278     }
279     
280     /**
281      * Remove a users keys
282      *
283      * @param username username to remove
284      */

285     public void removeKeys(String JavaDoc username) {
286         removeCachedKeys(username);
287         String JavaDoc filename = username + ".prv";
288         if(store.hasEntry(filename)) {
289             try {
290                 store.removeEntry(filename);
291             } catch (IOException JavaDoc e) {
292                 log.error("Failed to remove private key for " + username + ".", e);
293             }
294         }
295         filename = username + ".pub";
296         if(store.hasEntry(filename)) {
297             try {
298                 store.removeEntry(filename);
299             } catch (IOException JavaDoc e) {
300                 log.error("Failed to remove public key for " + username + ".", e);
301             }
302         }
303     }
304     
305     /**
306      * Get the users private key directly from the repository. This will not return any cached key,
307      * @param username
308      * @param passphrase
309      * @return
310      * @throws IOException
311      * @throws InvalidKeyException
312      */

313     protected SshPrivateKey getPrivateKeyFromStore(String JavaDoc username, String JavaDoc passphrase) throws IOException JavaDoc, InvalidKeyException {
314
315         String JavaDoc filename = username + ".prv";
316
317         if (!store.hasEntry(filename)) {
318              generateKey(username, keyType, bitLength, passphrase);
319         }
320
321         SshPrivateKeyFile f = SshPrivateKeyFile.parse(store.getEntryInputStream(filename));
322         
323         return f.toPrivateKey(passphrase);
324     
325     }
326     
327     /**
328      * Determine if the user has a private key
329      * @param username
330      * @return
331      */

332     public boolean hasPrivateKey(String JavaDoc username) {
333         return store.hasEntry(username + ".prv");
334     }
335     
336     /**
337      * Determine if the users key is cached.
338      * @param username
339      * @return
340      */

341     public boolean hasLoadedKey(String JavaDoc username) {
342         return loadedPrivateKeys.containsKey(username);
343     }
344     
345     /**
346      * Determine whether the users passphrase has changed since last time.
347      * @param username
348      * @param passphrase
349      * @return
350      */

351     public boolean hasPassphraseChanged(String JavaDoc username, String JavaDoc passphrase) {
352         
353         try {
354             getPrivateKeyFromStore(username, passphrase);
355             return true;
356             
357         } catch (IOException JavaDoc e) {
358             return false;
359         } catch (InvalidKeyException e) {
360             return false;
361         }
362     }
363     
364     /**
365      * Get a users public key.
366      * @param username
367      * @return
368      * @throws IOException
369      * @throws InvalidKeyException
370      */

371     protected SshPublicKey getPublicKey(String JavaDoc username) throws IOException JavaDoc, InvalidKeyException {
372
373         if(loadedPublicKeys.containsKey(username))
374             return (SshPublicKey) loadedPublicKeys.get(username);
375         
376         String JavaDoc filename = username + ".pub";
377         
378         if(store.hasEntry(filename)) {
379              SshPublicKeyFile f = SshPublicKeyFile.parse(store.getEntryInputStream(filename));
380              SshPublicKey pk = f.toPublicKey();
381              loadedPublicKeys.put(username, pk);
382              return pk;
383         }
384         else
385             throw new IOException JavaDoc("User does not have a key in the repository!");
386     }
387     
388     protected void changePassphrase(String JavaDoc username, String JavaDoc oldPassphrase, String JavaDoc newPassphrase) throws IOException JavaDoc, InvalidKeyException, PromptForPasswordException, FatalKeyException, UpdatePrivateKeyPassphraseException {
389         String JavaDoc filename = username + ".prv";
390         if(store.hasEntry(filename)) {
391             ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
392             SshKeyGenerator.changePassphrase(store.getEntryInputStream(filename), baos, oldPassphrase, newPassphrase);
393             
394             /*
395              * Tried this, didn't work
396             loadedPrivateKeys.put(username, SshPrivateKeyFile.parse(
397                 new ByteArrayInputStream(baos.toByteArray())));
398                 
399              */

400             OutputStream JavaDoc out = store.getEntryOutputStream(filename);
401             out.write(baos.toByteArray());
402             out.flush();
403             Util.closeStream(out);
404         } else
405             throw new IOException JavaDoc("User does not have a key in the repository!");
406     }
407     
408     protected void generateKey(String JavaDoc username, String JavaDoc type, int bitlength, String JavaDoc passphrase) throws IOException JavaDoc, InvalidKeyException {
409         
410         SshKeyGenerator.generateKeyPair(type, bitlength, username, passphrase, store.getEntryOutputStream(username + ".prv"),
411                 store.getEntryOutputStream(username + ".pub"));
412     }
413     
414 }
415
Popular Tags