KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > commons > security > KeystoreGUI


1 package com.ca.commons.security;
2
3 import java.io.*;
4 import java.awt.*;
5 import java.awt.event.*;
6 import java.util.*;
7 import java.util.logging.Logger JavaDoc;
8 import java.util.logging.Level JavaDoc;
9 import javax.swing.*;
10
11 import java.security.*;
12 import java.security.cert.*;
13 import java.security.spec.*;
14
15
16 //use Van Bui's Certificate Viewer
17
import com.ca.commons.cbutil.*;
18 import com.ca.commons.security.cert.CertViewer;
19
20 public class KeystoreGUI extends CBDialog implements ActionListener
21 {
22
23     public static final String JavaDoc ERRORCERT = "<unable to read>";
24     public static final String JavaDoc DELETEDCERT = "<deleted>";
25     
26     CBButton viewCert, addCert, deleteCert, passwordButton,
27     importKeyButton, exportKeyButton;
28     
29     CBButton[] commandButtons;
30     
31     protected KeyStore keystore = null;
32     
33     final JList certList; // final is for ease of use in mouse listener
34
DefaultListModel certListModel;
35     
36     public static ImageIcon smallCert;
37     public static ImageIcon smallKeyCert;
38     
39     Properties properties;
40     
41     protected CBHelpSystem helpBroker;
42     
43     char[] password = null;
44     
45     protected String JavaDoc keystoreFile;
46     
47     protected String JavaDoc keystoreType;
48
49     private static Logger JavaDoc log = Logger.getLogger(KeystoreGUI.class.getName());
50
51     /**
52      * Whether to cripple the GUI because we're displaying a losing
53      * key format (e.g. KSE) which doesn't support a bunch of operations...
54      */

55      
56     private boolean crippled = false;
57     
58     /**
59      * Whether to additionally cripple the set password because we're
60      * displaying a key format which doesn't support 'set password'
61      */

62      
63     private boolean cripplePassword = false;
64     
65     // whether the keystore has been modified and must be written back to disk.
66
private boolean changed = false;
67     
68     /**
69      * This creates the Keystore config window to manage a particular keystore.
70      * @param owner the parent frame (used for internal GUI stuff)
71      * @param props the JX property list (used to get and set default keystore directories)
72      * @param keyStoreLocation the location of the java keystore to manage.
73      * @param keyStorePassword the password of the encrypted keystore - may be null,
74      * in which case the user will be prompted.
75      * @param keyStoreType the java abreviation of the keystore type (typically 'jks' for
76      * 'java keystore' - the default java file based keystore).
77      * @param title a meaningfull (to the user) name for the keystore
78      * @param handlePrivateKeys whether the keystore manager will allow the
79      * user to associate a private key with a particular certificate.
80      * @param helpTopic the link into the default java help system (if used). See
81      * @see com.ca.commons.cbutil.CBHelpSystem
82      */

83     
84     public KeystoreGUI( Frame owner, Properties props, String JavaDoc keyStoreLocation,
85                         char[] keyStorePassword, String JavaDoc keyStoreType, String JavaDoc title,
86                         boolean handlePrivateKeys, String JavaDoc helpTopic)
87     {
88         super(owner, title, helpTopic); // create modal dialog ...
89

90         if ("KSE".equals(keyStoreType))
91             crippled = true;
92         
93         properties = props;
94         
95         password = keyStorePassword;
96         
97         CertViewer.setProperties(properties);
98         
99         if (smallCert == null)
100             smallCert = getImageIcon("sslcert.gif");
101         if (smallKeyCert == null)
102             smallKeyCert = getImageIcon("sslkeycert.gif");
103             
104         keystoreFile = keyStoreLocation;
105         
106         keystoreType = keyStoreType;
107         
108         display.makeHeavy();
109         
110         JScrollPane scrollPane = new JScrollPane();
111         
112         certList = new JList();
113         
114         /*
115          * Problem here - some keystores require passwords to
116          * even look at them, while others don't. Not sure how
117          * to handle this in general... in the meantime we have a
118          * a series of hacks...
119          */

120         
121         if (password != null || "JKS".equalsIgnoreCase(keystoreType))
122         {
123             setupCertificateList();
124                     
125         }
126         else if ("KSE".equalsIgnoreCase(keystoreType) && keystoreFile!= null &&
127                  keystoreFile.toLowerCase().endsWith(".der"))
128         {
129             setupCertificateList();
130             cripplePassword = true;
131         }
132         else
133         {
134             if (setupPasswordAndKeystore(keystoreType, keystoreFile, this)) // no password, = no keystore
135
{
136                 refreshView(); // reset certListModel
137
certList.setModel(certListModel); // set the display JList of certs..
138
}
139         }
140         
141         scrollPane.getViewport().setView(certList);
142         
143         display.add(scrollPane, 1, 1, 2, ((handlePrivateKeys)?7:5));
144         
145         display.makeLight();
146         
147         display.add(viewCert = new CBButton(" " + CBIntText.get("View Certificate"), CBIntText.get("View a certificate in detail."), getImageIcon("sslview.gif")), 3, 1);
148
149         display.add(addCert = new CBButton(" " + CBIntText.get("Add Certificate"), CBIntText.get("Add a new trusted server certificate"), getImageIcon("ssladd.gif")), 3, 2);
150         if (crippled)
151             //addCert.disable();
152
addCert.setEnabled(false);
153
154         display.add(deleteCert = new CBButton(" " + CBIntText.get("Delete Certificate"), CBIntText.get("Delete an unwanted or out of date server certificate"), getImageIcon("ssldelete.gif")), 3, 3);
155
156         display.add(passwordButton = new CBButton(" " + CBIntText.get("Set Password"), CBIntText.get("Change the certificate keystore password."), getImageIcon("sslpassword.gif")), 3, 4);
157
158         importKeyButton = new CBButton(" " + CBIntText.get("Set Private Key"), CBIntText.get("Match a PKCS-8 private key with a certificate"), getImageIcon("sslprivatekey.gif"));
159
160         exportKeyButton = new CBButton(" " + CBIntText.get("Export Private Key"), CBIntText.get("Export the PKCS-8 private key matching a certificate"), getImageIcon("sslexprivatekey.gif"));
161
162         if (handlePrivateKeys)
163         {
164             display.add(importKeyButton, 3, 5);
165             display.add(exportKeyButton, 3, 6);
166         }
167
168                 
169         commandButtons = new CBButton[] {viewCert, addCert, deleteCert, passwordButton, importKeyButton, exportKeyButton};
170         
171         for (int i=0; i<commandButtons.length; i++)
172         {
173             commandButtons[i].setHorizontalAlignment(SwingConstants.LEFT);
174             commandButtons[i].addActionListener(this);
175         }
176
177         if (crippled)
178         {
179             JButton[] crippledButton = {addCert, deleteCert, exportKeyButton, importKeyButton};
180             for (int i=0; i<4; i++)
181             {
182                 //crippledButton[i].disable();
183
crippledButton[i].setEnabled(false);
184                 crippledButton[i].removeActionListener(this);
185                 crippledButton[i].setToolTipText(CBIntText.get("Not available with this security provider"));
186                 crippledButton[i].setForeground(Color.gray);
187             }
188         }
189         
190         if (cripplePassword)
191         {
192              //passwordButton.disable();
193
passwordButton.setEnabled(false);
194              passwordButton.removeActionListener(this);
195              passwordButton.setToolTipText(CBIntText.get("Not available with this security provider"));
196              passwordButton.setForeground(Color.gray);
197         }
198
199         
200         // special hack for double clicks
201

202         MouseListener mouseListener = new MouseAdapter()
203                                       {
204                                           public void mouseClicked(MouseEvent e)
205                                           {
206                                               if (e.getClickCount() == 2)
207                                               {
208                                                   if (e.getModifiers() == MouseEvent.BUTTON1_MASK)
209                                                   {
210                                                       //int index = certList.locationToIndex(e.getPoint());
211
CertItem cert = (CertItem)certList.getSelectedValue();
212                                                       viewCurrentCert(cert);
213                                                   }
214                                               }
215                                           }
216                                       };
217                                       
218         certList.addMouseListener(mouseListener);
219         
220         display.add(new JLabel(" "), 3, ((handlePrivateKeys)?7:5)); // padding...
221
}
222     
223     /**
224      * checks actions on the various keystore affecting buttons.
225      * Note that the OK and Cancel button are handled by doOK() and
226      * doCancel() inherited from the base class.
227      */

228      
229     public void actionPerformed(ActionEvent e)
230     {
231     
232         JButton src = ((JButton)e.getSource());
233     
234         CertItem cert = (CertItem)certList.getSelectedValue();
235         
236         if (src == viewCert)
237         {
238             viewCurrentCert(cert);
239         }
240         else if (src == addCert)
241         {
242             addNewCert();
243         }
244         else if (src == deleteCert)
245         {
246             if(cert==null)
247                 CBUtility.error(CBIntText.get("Please select a certificate to delete."), null);
248             else
249                 deleteCurrentCert(cert);
250         }
251         else if (src == passwordButton)
252         {
253             setupPasswords();
254         }
255         else if (src == importKeyButton)
256         {
257             importKey(cert);
258         }
259         else if (src == exportKeyButton)
260         {
261             exportKey(cert);
262         }
263     }
264     
265      
266     /**
267      * If the user is satisfied with their changes, attempt to
268      * write the keystore. Some checks may be required first,
269      * depending on the keystore type.
270      */

271      
272     public void doOK()
273     {
274         if (changed)
275         {
276             /* check that the user has entered a valid passphrase */
277             if (checkPassword() == false)
278                 return; // nothing to do.
279

280             try
281             {
282                 if (writeKeyStore(password, keystore, keystoreFile, keystoreType) == false)
283                 {
284                     clearPassword(password);
285                     password = null;
286                     return; // error given by writeKeyStore() method.
287
}
288             }
289             catch (Exception JavaDoc e)
290             {
291                 CBUtility.error(CBIntText.get("Error importing key file."), e);
292                 return;
293             }
294         }
295         
296         changed = false;
297         
298         // clean up the old password
299

300         clearPassword(password);
301         password = null;
302                   
303         super.doOK();
304 //System.exit(0); //XXX TEMP
305
}
306
307     public void doCancel()
308     {
309         if (changed)
310         {
311             String JavaDoc[] options = { CBIntText.get("Revise Changes"), CBIntText.get("Discard Changes") };
312             
313             int opt = JOptionPane.showOptionDialog(null, CBIntText.get("You have unsaved changes!"), "Warning",
314                       JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
315                       null, options, options[0]);
316             
317             if (opt == 0) return;
318         }
319         
320         super.doCancel();
321 //System.exit(0); //XXX TEMP
322

323     }
324     
325     /**
326      * Allows the user to match a private key with a particular certificate.
327      * (Currently limited to pkcs 8 - other may be possible depending on keystore
328      * implementation).
329      * @param certItem the certificate whose private key is to be imported.
330      */

331     
332     protected void importKey(CertItem certItem)
333     {
334         try
335         {
336             /* Check that the user has selected a certificate to associate with the new key */
337             
338             if (certItem == null || certItem.getX509Cert() == null)
339             {
340                 CBUtility.error(CBIntText.get("Please select a certificate to match with a key."), null);
341                 return;
342             }
343             
344             /* Get the user to select a pkcs 8 private key file */
345             
346             File keyFile = getKeyFile(CBIntText.get("Select a pkcs8 private key file"));
347             
348             if (keyFile == null)
349                 return; // nothing to do.
350

351             /* Read the file data into a byte array */
352             
353             FileInputStream in = new FileInputStream(keyFile);
354             byte [] buffer = new byte[(int) (keyFile.length())];
355             in.read(buffer);
356             in.close();
357             
358             /* check if this is pem base64 encoded data - if it is, translate it */
359             if (CBSecurity.isPEM(buffer))
360             {
361                 //TODO: XXX <your code to handle encrypted private keys here> XXX//
362

363                 byte[] pemData = CBSecurity.convertFromPEM(buffer, new String JavaDoc(CBSecurity.PEM_KEY_HEADER).getBytes());
364                 if (pemData != null)
365                     buffer = pemData;
366                 else
367                 {
368                     CBUtility.error(CBIntText.get("Unable to load key: does not begin with {0} ", new String JavaDoc[] {new String JavaDoc(CBSecurity.PEM_KEY_HEADER)}));
369                     return;
370                 }
371             }
372             
373             /* check that the user has entered a valid passphrase */
374             if (checkPassword() == false)
375                 return; // nothing to do.
376

377             /* import key */
378             
379             String JavaDoc alias = certItem.getAlias();
380             
381             java.security.cert.Certificate JavaDoc[] certChain = keystore.getCertificateChain(alias);
382             
383             //XXX <your code to handle unencrypted private keys here> XXX//
384

385             PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(buffer);
386             
387             KeyFactory factory = KeyFactory.getInstance("RSA");
388             
389             PrivateKey key = factory.generatePrivate(keySpec);
390             
391             if (certChain == null || certChain.length == 0) // ...which it often does, since cert
392
{ // chains often aren't stored properly
393
certChain = new java.security.cert.Certificate JavaDoc[1]; // in the keystore
394
certChain[0] = certItem.getX509Cert();
395             }
396             
397             keystore.setKeyEntry(alias, key, password, certChain);
398             
399             refreshView();
400             changed = true;
401         }
402         catch (Exception JavaDoc e)
403         {
404             CBUtility.error("Error importing key file.", e);
405             e.printStackTrace();
406         }
407         
408     }
409     
410     /**
411      * Allows the user to export a private key with a particular certificate.
412      * (Currently limited to pkcs 8 - other may be possible depending on keystore
413      * implementation).
414      * @param certItem the certificate whose private key is to be exported.
415      */

416     
417     protected void exportKey(CertItem certItem)
418     {
419         try
420         {
421             /* Check that the user has selected a certificate to associate with the new key */
422             
423             if (certItem == null || certItem.getX509Cert() == null)
424             {
425                 CBUtility.error(CBIntText.get("Please select a certificate to match with a key."), null);
426                 return;
427             }
428             
429             /* Get the user to select a pkcs 8 private key file */
430             
431             File keyFile = getKeyFile(CBIntText.get("Select a file to save the pkcs8 key to."));
432             
433             if (keyFile == null)
434                 return; // nothing to do.
435

436             /* check that the user has entered a valid passphrase */
437             
438             if (checkPassword() == false)
439                 return; // nothing to do.
440

441             /* read key from keystore */
442             
443             Key myKey = keystore.getKey(certItem.getAlias(), password);
444             
445             if (myKey == null)
446             {
447                 return;
448             }
449             byte[] data = myKey.getEncoded();
450             
451             if (data == null)
452             {
453                 throw new Exception JavaDoc("Unable to access encoded private key data");
454             }
455             
456             if (keyFile.toString().toLowerCase().endsWith(".pem"))
457             {
458                 data = CBSecurity.convertToPEMPrivateKey(data);
459             }
460             
461             FileOutputStream out = new FileOutputStream(keyFile);
462             out.write(data);
463             out.close();
464         }
465         catch (Exception JavaDoc e)
466         {
467             CBUtility.error("Error exporting key file.", e);
468             e.printStackTrace();
469         }
470         
471     }
472     
473     
474     
475     
476     /**
477      * This prompts the user to select a pkcs8 file to import, and
478      * attach to an existing certificate.
479      * @return the File name of the selected pkcs8 file.
480      */

481     
482     protected File getKeyFile(String JavaDoc title)
483     {
484         JFileChooser chooser = new JFileChooser(properties.getProperty("cert.homeDir"));
485         chooser.addChoosableFileFilter(new CBFileFilter(new String JavaDoc[] {"der", "pem"},"Certificate Files (*.der, *.pem)"));
486         chooser.setDialogTitle(title);
487         
488         int option = chooser.showOpenDialog(owner);
489         
490         while (true)
491         {
492             if (option == JFileChooser.APPROVE_OPTION) // only do something if user chose 'ok'
493
{
494                 File keyFile = chooser.getSelectedFile();
495                 if (keyFile == null)
496                     CBUtility.error(CBIntText.get("Please select a file"));
497                 else
498                 {
499                     properties.setProperty("cert.homeDir", keyFile.getParent());
500                     chooser = null;
501                     return keyFile;
502                 }
503             }
504             else
505             {
506                 chooser = null;
507                 return null; // user selected cancel, or closed the window.
508
}
509         }
510     }
511     
512     /**
513      * Uses the CertViewer to display the contents of the selected
514      * certificate.
515      * @param cert the certificate to display.
516      */

517     
518     protected void viewCurrentCert(CertItem cert)
519     {
520         if (cert == null || cert.getX509Cert() == null) // nothing to do.
521
{
522             CBUtility.error(CBIntText.get("Please select a certificate to view."), null);
523             return;
524         }
525         
526         CertViewer viewer = new CertViewer(owner, cert.getX509Cert());
527         viewer.setVisible(true);
528     }
529     
530     /**
531      * Checks the list to see which the currently selected certificate is,
532      * and then prompts the user to confirm the deletion.
533      * @param certItem the certificate to delete.
534      */

535     
536     protected void deleteCurrentCert(CertItem certItem)
537     {
538     
539         if (certItem == null)
540             return; // nothing to do.
541

542         int delete = JOptionPane.showConfirmDialog(this, CBIntText.get("delete certificate: {0} ?", new String JavaDoc[] {certItem.getAlias()}),
543                      CBIntText.get("Confirm Certificate Deletion"), JOptionPane.OK_CANCEL_OPTION);
544                      
545         if (delete != JOptionPane.OK_OPTION)
546             return; // nothing to do
547

548         if (keystore == null) // ? Can't see how this would happen
549
{
550             CBUtility.error(CBIntText.get("Internal Error: unable to find Certificate Keystore"), null);
551             return;
552         }
553         
554         if (checkPassword() == false)
555             return; // nothing to do.
556

557         try
558         {
559             keystore.deleteEntry(certItem.getAlias());
560
561             refreshView();
562             changed = true;
563             
564             return;
565 /* DEFER
566             if (writeKeyStore(password, keystore, keystoreFile) == true)
567             {
568                 refreshView();
569                 return; // SUCCESS!!!
570             }
571 */

572         }
573         catch (KeyStoreException e)
574         {
575             CBUtility.error(CBIntText.get("Error - unable to delete key: {0} from key store", new String JavaDoc[] {certItem.getAlias()}), e);
576         }
577         
578         // FAILURE!!!
579
try // try to reset entry in local keystore
580
{
581             keystore.setCertificateEntry(certItem.getAlias(), certItem.getX509Cert());
582         }
583         catch (Exception JavaDoc e)
584         {
585             log.log(Level.WARNING, "unable to recover key store.",e);
586         }
587     }
588     
589     
590     /**
591      * checks that the user has entered a valid password. If they haven't,
592      * it prompts for one.
593      * @return whether a valid password has been entered and checked against
594      * the keystore.
595      */

596     
597     protected boolean checkPassword()
598     {
599         if (password != null)
600             return true; // we already have a password.
601

602         return setupPasswordAndKeystore(keystoreType, keystoreFile, this); // we don't, so try to get one...
603
}
604     
605     /**
606      * <p>This allows the user to enter their password, which remains valid
607      * for the life of this component. </p>
608      *
609      * <p>This also sets up the keystore</p>
610      * @return whether the password successfully opened the keystore.
611      */

612     
613     public boolean setupPasswordAndKeystore(String JavaDoc keystoreType, String JavaDoc keystoreFile, Component owner)
614     {
615         if ((password != null) && (keystore != null)) // no thanks, we already have one...
616
return true;
617
618         String JavaDoc message = CBIntText.get("Enter Key Store Password");
619         while ((password = getPassword(owner, message)) != null)
620         {
621             keystore = readKeyStore(password, keystoreType, keystoreFile);
622
623             if (keystore != null)
624             {
625                 return true; // we have a valid keystore!
626
}
627             // this message is only displayed if we go around the loop again.
628
message = CBIntText.get("Password incorrect. Please try again.");
629         }
630
631         return false; // user hasn't entered a password and has cancelled out.
632
}
633
634     public static char[] getPassword(Component owner, String JavaDoc message)
635     {
636         char[] password;
637         JPasswordField passwordInput = new JPasswordField();
638         int response = JOptionPane.showConfirmDialog(owner, passwordInput,
639                        message, JOptionPane.OK_CANCEL_OPTION);
640
641         if (response != JOptionPane.OK_OPTION)
642             password = null; // give up, go home
643
else
644             password = passwordInput.getPassword();
645         return password;
646     }
647     
648     /**
649      * Sets up the keystore variable, using the
650      * current password (may be null) and keystore file.
651      */

652
653     /*
654     public static boolean setupKeyStore(char[] password, KeyStore keyStore, String keyStoreType, String keyStoreFile)
655     {
656         KeyStore newKeystore = readKeyStore(password, keyStoreType, keyStoreFile);
657         if (newKeystore == null)
658         {
659             return false;
660         }
661         else
662         {
663             keyStore = newKeystore;
664             return true;
665         }
666     }
667     */

668     /**
669      * Checks if the given alias name already exists in the
670      * Keystore.
671      */

672      
673     private boolean listContains(String JavaDoc aliasName)
674     {
675         if (aliasName == null) return false;
676         
677         for (int i=0; i<certListModel.size(); i++)
678             if (aliasName.equals(((CertItem)certListModel.get(i)).alias))
679                 return true;
680                 
681         return false;
682     }
683     
684     /**
685      * Allows the User to browse to a new Cert (on disk) and
686      * import it.
687      */

688     
689     protected void addNewCert()
690     {
691         CertViewer.CertAndFileName info = CertViewer.loadCertificate(owner);
692         if (info == null || info.cert == null)
693         {
694             return; // no cert selected
695
}
696         
697         String JavaDoc alias = null;
698         
699         if (info.fileName != null)
700         {
701             alias = new File(info.fileName).getName();
702             if (alias != null && alias.indexOf('.')>0)
703                 alias = alias.substring(0, alias.indexOf('.')); // trim to get the stem
704
}
705         
706         if (alias == null)
707             alias = CBIntText.get("default");
708         
709         boolean nameAlreadyExists = false;
710         do
711         {
712             alias = (String JavaDoc)JOptionPane.showInputDialog(this, CBIntText.get("Please enter a short unique name for this Certificate"),
713                 CBIntText.get("Enter Certificate Alias"), JOptionPane.QUESTION_MESSAGE, null, null, alias);
714
715             nameAlreadyExists = listContains(alias);
716             if (nameAlreadyExists)
717             {
718                 JOptionPane.showMessageDialog(this, CBIntText.get("That name already exists."),
719                 CBIntText.get("Duplicate Alias"), JOptionPane.ERROR_MESSAGE);
720             }
721         }
722         while (nameAlreadyExists);
723                 
724         if (alias == null || alias.length() == 0)
725             return; // nothing to do
726

727         if (checkPassword() == false)
728             return; // nothing to do.
729

730         try
731         {
732             keystore.setCertificateEntry(alias, info.cert);
733
734             refreshView();
735             changed = true;
736             return;
737         }
738         catch (KeyStoreException e)
739         {
740             CBUtility.error(CBIntText.get("Error - unable to add key: {0} from key store", new String JavaDoc[] {alias}), e);
741         }
742         // FAILURE!
743
try
744         {
745             keystore.deleteEntry(alias); // try to clean up.
746
}
747         catch (Exception JavaDoc e)
748         {}
749     }
750     
751     /**
752      * Reread the key store after an addition or deletion operation,
753      * and refresh certListModel.
754      */

755     
756     protected void refreshView()
757     {
758         CertItem[] certs = getKeyStoreCerts(keystore);
759         
760         if (certListModel == null)
761             setupCertificateListGUI();
762             
763         certListModel.removeAllElements();
764         for (int i=0; i<certs.length; i++)
765             certListModel.addElement(certs[i]);
766     }
767     
768     /**
769      * Initialise empty list models, and associate the
770      * certificate list renderer with the cert list.
771      *
772      */

773     
774     protected void setupCertificateListGUI()
775     {
776         certListModel = new DefaultListModel();
777         
778         certList.setModel(certListModel);
779         
780         certList.setCellRenderer(new CertificateListRenderer());
781     }
782     
783     /**
784      * Initialises a selection list of CertItems from the keystore.
785      */

786     
787     protected void setupCertificateList()
788     {
789         // Initially read the keystore without a password, for
790
// simple listing...
791

792         keystore = readKeyStore(password, keystoreType, keystoreFile);
793         
794         setupCertificateListGUI();
795         
796         if (keystore == null)
797             JOptionPane.showMessageDialog(this, CBIntText.get("Unable to find/open keystore: {0}", new String JavaDoc[] {keystoreFile}), CBIntText.get("Error: no Keystore"), JOptionPane.ERROR_MESSAGE);
798         else
799             refreshView();
800     }
801     
802     
803     /**
804      * The keystore has a particular password protecting its contents.
805      * This menu allows the user to change that password.
806      */

807     
808     public class PasswordDialog extends CBDialog
809     {
810         public JPasswordField old, new1, new2;
811         
812         public PasswordDialog(Frame owner)
813         {
814             super(owner, CBIntText.get("Change the Key Store Password."), null);
815             addln(new JLabel(getImageIcon("sslpassword.gif")));
816             addln(new JLabel(CBIntText.get("This screen allows you to enter")));
817             addln(new JLabel(CBIntText.get("a new key store password")));
818             addln(new JLabel(" "));
819             addln(new JLabel(CBIntText.get("Enter the old password")));
820             addln(old = new JPasswordField());
821             addln(new JLabel(CBIntText.get("The new Password") + ":"));
822             addln(new1 = new JPasswordField());
823             addln(new JLabel(CBIntText.get("Confirm the new Password") + ":"));
824             addln(new2 = new JPasswordField());
825             setSize(240, 320);
826             CBUtility.center(this, owner);
827         }
828         
829     }
830     
831     /**
832      * This allows the user to change the password used to protect
833      * the keystore.
834      *
835      */

836     
837     protected void setupPasswords()
838     {
839         PasswordDialog newPassword = new PasswordDialog(owner);
840         
841         
842         // Various things can go wrong here - keep showing the
843
// user the password change window until they enter a
844
// valid set of passwords, or get sick of it...
845

846         while (newPassword.wasCancelled() == false)
847         {
848             newPassword.setVisible(true);
849             
850             if (newPassword.wasCancelled())
851                 return; // do nothing.
852

853             char[] oldPass, newPass1, newPass2;
854             oldPass = newPassword.old.getPassword();
855             newPass1 = newPassword.new1.getPassword();
856             newPass2 = newPassword.new2.getPassword();
857             
858             if (Arrays.equals(newPass1, newPass2) == true)
859             {
860                 // this throws an error directly to the user if it fails
861
KeyStore newKeystore = readKeyStore(oldPass, keystoreType, keystoreFile);
862                 if (newKeystore != null)
863                 {
864                     if (writeKeyStore(newPass1, newKeystore, keystoreFile, keystoreType) == true)
865                     {
866                         keystore = newKeystore;
867                         password = newPass1;
868                         
869                         JOptionPane.showMessageDialog(this, CBIntText.get("Passwords successfully changed!"),
870                                                       CBIntText.get("Success!"), JOptionPane.INFORMATION_MESSAGE);
871                         return; // SUCCESS!
872
}
873                 }
874                 else
875                     CBUtility.error(CBIntText.get("Unable to change password - incorrect password entered?"));
876                     
877             }
878             else
879             {
880                 CBUtility.error(CBIntText.get("The new passwords were not identical!"), null);
881             }
882         }
883         
884     }
885     
886     protected void clearPassword(char[] c)
887     {
888         if (c != null)
889             for (int i=0; i<c.length; i++)
890                 c[i] = 0;
891     }
892     
893     /**
894      * This extracts an array of CertItem-s from a keystore,
895      * for display in the GUI.
896      * @param keystore the keystore to use.
897      * @return an array of CertItem-s representing the certificates and aliases
898      * stored in the keystore.
899      */

900     
901     public static CertItem[] getKeyStoreCerts(KeyStore keystore)
902     {
903         try
904         {
905             Vector certVector = new Vector(10); // vector of cert items...
906

907             //PrivateKey privKey=null;
908

909             Enumeration a = keystore.aliases();
910             while ( a.hasMoreElements() )
911             {
912                 String JavaDoc alias = (String JavaDoc) a.nextElement();
913                 CertItem item = new CertItem(alias);
914                 
915                 if ( keystore.isKeyEntry(alias) )
916                 {
917                     X509Certificate userCert = (X509Certificate)keystore.getCertificate(alias);
918                     item.addX509Cert(userCert);
919                     item.setHasPrivateKey(true);
920                 }
921                 else
922                 {
923                     X509Certificate userCert = (X509Certificate)keystore.getCertificate(alias);
924                     item.addX509Cert(userCert);
925                 }
926                 certVector.add(item);
927             }
928             
929             return (CertItem[]) certVector.toArray(new CertItem[0]);
930         }
931         catch (Exception JavaDoc e)
932         {
933             CBUtility.error(CBIntText.get("Error reading certificate from keystore."), e);
934             return null;
935         }
936         
937         
938     }
939     
940     /**
941      * initialises the keystore by reading the saved keystore file.
942      * @param pass the password protecting the keystore. If this is
943      * null, the keystore will be read-only, and no validation
944      * will be performed.
945      * @param storeType - the type of the keystore. Unless a custom
946      * security provider is being used, this will almost certainly
947      * be 'jks'.
948      * @param keyFile the file name of the keystore.
949      * @return the new keystore, or null if an error occurred.
950      */

951     
952     public static KeyStore readKeyStore(char[] pass, String JavaDoc storeType, String JavaDoc keyFile)
953     {
954         //byte[] b=null;
955

956         try
957         {
958             KeyStore keystore = KeyStore.getInstance( storeType ); // storeType is usually 'jks' for default java keystore
959

960             FileInputStream fis = new FileInputStream(keyFile);
961             keystore.load(fis, pass);
962             
963             fis.close();
964             
965             return keystore;
966         }
967         catch (Exception JavaDoc e)
968         {
969         
970             CBUtility.error(CBIntText.get("Error opening certificate keystore {0}. Probably an incorrect password", new String JavaDoc[] {keyFile}), e);
971                             
972             return null;
973         }
974     }
975     
976     /**
977      * writes the keystore to a password protected file.
978      * @param password the password to use while saving it.
979      * @param keystore the certificate key store to save.
980      * @param keyFile the name of the file to save to.
981      * @param keystoreType the type of store - e.g. "JKS" or "KSE" or "PKCS12"
982      * @return the success status of the operation.
983      */

984     
985     public static boolean writeKeyStore(char[] password, KeyStore keystore, String JavaDoc keyFile, String JavaDoc keystoreType)
986     {
987         if ("KSE".equalsIgnoreCase(keystoreType))
988         {
989             CertItem[] certs = getKeyStoreCerts(keystore);
990             
991             if (certs.length > 2)
992                return givePKCS12ErrorMsg(CBIntText.get("This PKCS12 File can only have one certificate, one key, and one CA certificate"));
993             
994             if (certs.length == 2 && certs[0].hasPrivateKey && certs[1].hasPrivateKey)
995                return givePKCS12ErrorMsg(CBIntText.get("This PKCS12 File can only have one certificate, one key, and one CA certificate"));
996                
997             // XXXcheck for if second cert if server certificate?
998
}
999         FileOutputStream fos = null;
1000        try
1001        {
1002            if (password == null)
1003                throw new KeyStoreException("null password not allowed");
1004            fos = new FileOutputStream(keyFile);
1005            keystore.store(fos, password);
1006            fos.close();
1007            return true;
1008        }
1009        catch (Exception JavaDoc e) // IOException or KeyStoreException
1010
{
1011            CBUtility.error(CBIntText.get("Error saving certificate keystore.") +
1012                            "\n" + CBIntText.get("Probably an invalid password"), e);
1013
1014            // try to clean up any mess.
1015
if (fos != null)
1016                try {fos.close();} catch(IOException e2) {}
1017
1018            return false;
1019        }
1020
1021    }
1022    
1023    /**
1024     * Utility to reduce code duplication above
1025     */

1026     
1027    private static boolean givePKCS12ErrorMsg(String JavaDoc msg)
1028    {
1029        CBUtility.error(msg);
1030        return false;
1031    }
1032    
1033    
1034    /**
1035     * A representation of a certificate that is displayed
1036     * in the certificate list.
1037     */

1038    
1039    public static class CertItem
1040    {
1041        public String JavaDoc alias;
1042        
1043        public X509Certificate x509Cert = null;
1044        
1045        public boolean hasPrivateKey = false;
1046        
1047        /**
1048         * Initialises a certitem with the alias name of a
1049         * certificate only (the actual cert can be added
1050         * seperately)
1051         * @param certAlias the alias of the certificate, the arbitrary user assigned
1052         * name the certificate is labelled by in the keystore.
1053         */

1054        
1055        public CertItem(String JavaDoc certAlias)
1056        {
1057            this(certAlias, null);
1058        }
1059        
1060        /**
1061         * Initialises a certItem with the alias name of a
1062         * certificate and the actual certificate data.
1063         * @param certAlias the alias of the certificate, the arbitrary user assigned
1064         * name the certificate is labelled by in the keystore.
1065         * @param cert the actual X509 Certificate data.
1066         */

1067        
1068        public CertItem(String JavaDoc certAlias, X509Certificate cert)
1069        {
1070            alias = certAlias;
1071            x509Cert = cert;
1072        }
1073        
1074        
1075        /**
1076         * Adds (or Replaces) the X509Cert data.
1077         * @param x the actual X509 Certificate data.
1078         */

1079        
1080        public void addX509Cert(X509Certificate x)
1081        {
1082            x509Cert = x;
1083        }
1084        
1085        /**
1086         * Returns a formatted string identifying the cert by the alias.
1087         * @return the alias assigned to this cert.
1088         */

1089        
1090        public String JavaDoc toString()
1091        {
1092            if (hasPrivateKey)
1093                return "<html><b><font color=black>" + alias + "</font><br><font color=blue>(has private key)</font></b></html>";
1094            else
1095                return alias;
1096        }
1097        
1098        /**
1099         * Returns a formatted string identifying the cert by the alias.
1100         * @return the alias assigned to this cert.
1101         */

1102        
1103        public String JavaDoc getSelectedText()
1104        {
1105            if (hasPrivateKey)
1106                return "<html><b><font color=white>" + alias + "</font><br><font color=white>(has private key)</font></b></html>";
1107            else
1108                return alias;
1109        }
1110        
1111        /**
1112         * returns the raw alias for this cert.
1113         * @return the alias assigned to this cert.
1114         */

1115        
1116        public String JavaDoc getAlias()
1117        {
1118            return alias;
1119        }
1120        
1121        /**
1122         * Returns an image representing this CertItem.
1123         */

1124        
1125        public ImageIcon getIcon()
1126        {
1127            if (hasPrivateKey)
1128                return smallKeyCert;
1129            else
1130                return smallCert;
1131        }
1132        
1133        /**
1134         * Returns the X509 certificate data (may be null if this hasn't
1135         * been set).
1136         * @return the X509 certificate stored in this CertItem
1137         */

1138        
1139        public X509Certificate getX509Cert()
1140        {
1141            return x509Cert;
1142        }
1143        
1144        public void setHasPrivateKey(boolean state)
1145        {
1146            hasPrivateKey = state;
1147        }
1148        
1149        public boolean getHasPrivateKey()
1150        {
1151            return hasPrivateKey;
1152        }
1153    }
1154    
1155    
1156    /**
1157     * A quicky cell renderer to allow us to display active and pending
1158     * queries in different colours, and to display the text of a
1159     * QueryBroker object.
1160     */

1161    
1162    class CertificateListRenderer extends JLabel implements ListCellRenderer
1163    {
1164        Color highlight = new Color(0,0,128); // Colour of 'selected' text
1165

1166        CertificateListRenderer()
1167        {
1168            setOpaque(true);
1169        }
1170        
1171        public Component getListCellRendererComponent(JList list, Object JavaDoc value, int index,
1172                boolean isSelected, boolean cellHasFocus)
1173        {
1174            if (value instanceof CertItem == false) // should never happen :-)
1175
{
1176                System.err.println("Rendering error in KeystoreGUI");
1177                setText(ERRORCERT);
1178                return this;
1179            }
1180            
1181            if (index == -1)
1182            {
1183                index = list.getSelectedIndex();
1184                if (index == -1)
1185                {
1186                    setText("<error>");
1187                    return this;
1188                }
1189            }
1190            
1191            if (value == null) // shouldn't happen
1192
{
1193                setBackground(Color.white);
1194                setForeground(Color.gray);
1195                setText("<deleted>");
1196                return this;
1197            }
1198            
1199            CertItem item = (CertItem)value;
1200            
1201            setIcon(item.getIcon());
1202            
1203            if (isSelected)
1204            {
1205                setText(item.getSelectedText());
1206                setBackground(highlight);
1207                setForeground(Color.white);
1208            }
1209            else
1210            {
1211                setText(item.toString());
1212                setBackground(Color.white);
1213                setForeground(Color.black);
1214            }
1215            return this;
1216        }
1217    }
1218    
1219    public ImageIcon getImageIcon(String JavaDoc name)
1220    {
1221        ImageIcon newIcon = new ImageIcon(properties.getProperty("dir.images") + name);
1222        return newIcon;
1223    }
1224 
1225
1226
1227    
1228    
1229    private static void printUsageAndExit()
1230    {
1231        System.out.println("USAGE: java KeystoreGUI [keystore file|path] [keystore password] [keystore type] [provider]\n" +
1232                          "(defaults are 'security/clientcerts' and 'jks'");
1233        System.exit(0);
1234    }
1235    
1236    /**
1237     * Main method for stand alone usage and provider testing.
1238     */

1239    
1240    public static void main(String JavaDoc[] argsv)
1241    {
1242        String JavaDoc keystoreType = "jks";
1243        
1244        String JavaDoc provider = null;
1245        
1246        String JavaDoc password = null;
1247        
1248        Frame rootFrame = new Frame();
1249        
1250        CBUtility.initDefaultDisplay(rootFrame);
1251        
1252        // stand alone demo...
1253

1254        System.out.println("running KeystoreGUI 1.0 stand alone demo - Chris Betts 2002\n");
1255        
1256        
1257                           
1258                           
1259        String JavaDoc localDir = System.getProperty("user.dir") + File.separator;
1260        
1261        Properties props = new Properties();
1262        
1263        /*
1264         * This sets the directory that the file browsers start in.
1265         * This can be saved/read from file to allow the user to start
1266         * loading/saving from the same place.
1267         */

1268        
1269        props.setProperty("cert.homeDir", localDir + "certs" + File.separator);
1270        
1271        /*
1272         * This simply sets the directory where the GUI will try to load
1273         * its button images from.
1274         */

1275        
1276        props.setProperty("dir.images", localDir + "images" + File.separator);
1277        
1278        /*
1279         * Set the location of the java keystore to manipulate.
1280         */

1281        
1282        String JavaDoc keystoreName = localDir + "security" + File.separator + "clientcerts";
1283        
1284        if (argsv.length < 1)
1285            printUsageAndExit();
1286        
1287        if (argsv[0].startsWith("-h"))
1288            printUsageAndExit();
1289        
1290        if (argsv[0].length() < 2)
1291        {
1292            keystoreName = argsv[0];
1293        }
1294        else if (argsv[0].charAt(1) == ':' || argsv[0].charAt(0) == '/') // absolute path
1295
{
1296            keystoreName = argsv[0];
1297        }
1298        else // assume relative path.
1299
{
1300            keystoreName = localDir + argsv[0];
1301        }
1302        
1303        /*
1304         * Keystore Password
1305         */

1306        
1307        if (argsv.length > 1)
1308        {
1309            password = argsv[1];
1310        }
1311        
1312        /*
1313         * Read optional keystore type (e.g. "KSE")
1314         */

1315        
1316        if (argsv.length > 2)
1317        {
1318            keystoreType = argsv[2];
1319        }
1320        
1321        /*
1322         * Read optional provider (e.g. com.ca.pki.security.provider.KeyStoreEngine )
1323         */

1324        
1325        if (argsv.length > 3)
1326        {
1327            provider = argsv[3];
1328            
1329            // register the new provider
1330
try
1331            {
1332                Class JavaDoc providerClass = Class.forName(provider);
1333                Provider providerObject = (Provider)providerClass.newInstance();
1334                Security.insertProviderAt(providerObject, 1);
1335                System.out.println("\nPROVIDER: " + providerObject.getName() + " v" + providerObject.getVersion() + " has been registered ");
1336            }
1337            catch (Exception JavaDoc e)
1338            {
1339                System.err.println("\n*** unable to load new security provider: " + ((provider==null)?"null":provider));
1340                System.err.println(e + "\n");
1341                printUsageAndExit();
1342            }
1343        }
1344
1345        Provider[] current = Security.getProviders();
1346        for (int i=0; i<current.length; i++)
1347            System.out.println("registered security providers: " + i + " = " + current[i].getName() + " " + current[i].getInfo());
1348        
1349        // Extend KeystoreGUI to add 'exit on close' behaviour
1350

1351        class StandaloneKeystore extends KeystoreGUI
1352        {
1353            public StandaloneKeystore( Frame owner, Properties props, String JavaDoc keyStoreLocation, char[] pwd, String JavaDoc keystoreType, String JavaDoc title, boolean handlePrivateKeys, String JavaDoc helpTopic)
1354            {
1355                super(owner, props, keyStoreLocation, pwd, keystoreType, title, handlePrivateKeys, helpTopic);
1356            }
1357            
1358            public void doOK()
1359            {
1360                super.doOK();
1361                System.exit(0);
1362            }
1363            
1364            public void doCancel()
1365            {
1366                super.doCancel();
1367                System.exit(0);
1368            }
1369        }
1370        
1371        char[] pwd = null; //((password==null)?null:password.toCharArray());
1372

1373        StandaloneKeystore gui = new StandaloneKeystore(rootFrame, props, keystoreName, pwd, keystoreType, "CB Keystore GUI Demo", true, null);
1374        gui.setSize(450,440);
1375        
1376        CBUtility.center(gui, null);
1377        
1378        gui.setVisible(true);
1379    }
1380   
1381    
1382}
1383
Popular Tags