KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > ca > directory > jxplorer > editor > userpasswordeditor


1 /**
2  * All pluggable editors must be in this package
3  */

4
5 package com.ca.directory.jxplorer.editor;
6
7 import com.ca.commons.cbutil.*;
8 import com.ca.directory.jxplorer.HelpIDs;
9
10 import java.security.SecureRandom JavaDoc;
11 import java.security.MessageDigest JavaDoc;
12
13 import com.ca.commons.cbutil.CBBase64;
14
15 import javax.swing.*;
16 import java.awt.*;
17 import java.awt.event.*;
18 import java.io.*;
19 import java.util.logging.Logger JavaDoc;
20 import java.util.logging.Level JavaDoc;
21
22 public class userpasswordeditor extends JDialog
23         implements abstractbinaryeditor
24 {
25     /**
26      * MD5.
27      */

28     public static final String JavaDoc MD5 = "MD5";
29
30     /**
31      * SHA.
32      */

33     public static final String JavaDoc SHA = "SHA";
34
35     /**
36      * SSHA.
37      */

38     public static final String JavaDoc SSHA = "SSHA";
39
40     /**
41      * SMD5.
42      */

43     public static final String JavaDoc SMD5 = "SMD5";
44
45     protected JPasswordField oldPwd, newPwd;
46     protected CBButton btnOK, btnCancel, btnHelp;
47     protected editablebinary editMe = null;
48     protected CBPanel display;
49     protected JLabel oldLabel, newLabel;
50     protected CBJComboBox comboType;
51     protected boolean firstClick = true;
52
53     protected static int default_encryption = 4;
54
55     private static Logger JavaDoc log = Logger.getLogger(userpasswordeditor.class.getName());
56
57     /**
58      * Constructor - sets up the gui.
59      */

60     public userpasswordeditor(Frame owner)
61     {
62         super(owner);
63
64         setModal(true);
65         setTitle(CBIntText.get("User Password"));
66
67         display = new CBPanel();
68
69         oldPwd = new JPasswordField();
70         oldPwd.setBackground(Color.white); // For a better motif display
71
oldPwd.addMouseListener(new MouseListener()
72         {
73             public void mouseClicked(MouseEvent e) {}
74             public void mouseEntered(MouseEvent e) {}
75             public void mouseExited(MouseEvent e) {}
76             public void mouseReleased(MouseEvent e) {}
77             public void mousePressed(MouseEvent e)
78             {
79                 if (firstClick) // Only clear the field on the first click b/c if the pwd exists it will be encoded.
80
{
81                     oldPwd.setText("");
82                     firstClick = false;
83                 }
84             }
85         });
86
87         newPwd = new JPasswordField();
88         newPwd.setBackground(Color.white); // For a better motif display
89

90         oldLabel = new JLabel(CBIntText.get("Enter Password:"));
91         newLabel = new JLabel(CBIntText.get("Re-enter Password:"));
92
93         btnOK = new CBButton(CBIntText.get("OK"), CBIntText.get("Click here to save the changes (remember to click Submit in the table editor)."));
94         btnOK.addActionListener(new ActionListener()
95         {
96             public void actionPerformed(ActionEvent e)
97             {
98                 load();
99             }
100         });
101
102         btnCancel = new CBButton(CBIntText.get("Cancel"), CBIntText.get("Click here to exit."));
103         btnCancel.addActionListener(new ActionListener()
104         {
105             public void actionPerformed(ActionEvent e)
106             {
107                 quit();
108             }
109         });
110
111         comboType = new CBJComboBox();
112         comboType.addItem(CBIntText.get("verify"));
113         comboType.addItem(CBIntText.get("plain"));
114         comboType.addItem(CBIntText.get(MD5));
115         comboType.addItem(CBIntText.get(SMD5));
116         comboType.addItem(CBIntText.get(SHA));
117         comboType.addItem(CBIntText.get(SSHA));
118         comboType.setEditable(false);
119         comboType.setSelectedIndex(default_encryption);
120         comboType.addItemListener(new ItemListener()
121         {
122             // If "verify" is selected, the newPwd Password-field will be
123
// disabled.
124
public void itemStateChanged(ItemEvent e)
125             {
126                 if (comboType.getSelectedIndex() == 0)
127                     setNewPwdFieldEnabled(false);
128                 else
129                     setNewPwdFieldEnabled(true);
130             }
131         });
132
133         comboType.addActionListener(new ActionListener()
134         {
135             public void actionPerformed(ActionEvent e)
136             {
137                 default_encryption = comboType.getSelectedIndex();
138             }
139         });
140
141
142         btnHelp = new CBButton(CBIntText.get("Help"), CBIntText.get("Click here for Help."));
143         CBHelpSystem.useDefaultHelp(btnHelp, HelpIDs.ATTR_PASSWORD);
144
145         display.makeHeavy();
146         display.addln(oldLabel);
147         display.addln(oldPwd);
148         display.addln(newLabel);
149         display.addln(newPwd);
150         display.add(comboType);
151         display.addln(new JLabel(" "));
152         display.makeLight();
153
154         JPanel buttonPanel = new JPanel();
155         buttonPanel.add(btnOK);
156         buttonPanel.add(btnCancel);
157         buttonPanel.add(btnHelp);
158         display.addln(buttonPanel);
159
160         // Better way to implement keystroke listening...
161
display.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ENTER"), "enter");
162         display.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke("ESCAPE"), "escape");
163         display.getActionMap().put("enter", new MyAction(CBAction.ENTER));
164         display.getActionMap().put("escape", new MyAction(CBAction.ESCAPE));
165
166         setSize(300, 170);
167         CBUtility.center(this, owner); // Centres the window.
168
setTitle(CBIntText.get("User Password Data"));
169         getContentPane().add(display);
170
171         // If "verify" is selected, the newPwd Password-field will be
172
// disabled.
173
if (comboType.getSelectedIndex() == 0)
174             setNewPwdFieldEnabled(false);
175         else
176             setNewPwdFieldEnabled(true);
177     }
178
179     /**
180      * Enables or disables the newPwd password field.
181      * @param enabled True to enable the newPwd password field and false to disable the newPwd password field.
182      */

183     protected void setNewPwdFieldEnabled(boolean enabled)
184     {
185         newPwd.setFocusable(enabled);
186         newPwd.setEnabled(enabled);
187         newLabel.setEnabled(enabled);
188
189         if (enabled)
190             newPwd.setBackground(Color.white); // For a better motif display
191
else
192             newPwd.setBackground(Color.lightGray);
193     }
194
195     /**
196      * Apparently it is better to use key bindings rather than adding a KeyListener... "for reacting in a special way to
197      * particular keys, you usually should use key bindings instead of a key listener". This class lets the user set the
198      * key as an int. If a key is pressed and it matches the assigned int, a check is done for if it is an escape or
199      * enter key. (27 or 10). If escape, the quit method is called. If enter, the apply method is called. Bug 4646.
200      * @author Trudi.
201      */

202     private class MyAction extends CBAction
203     {
204         /**
205          * Calls super constructor.
206          * @param key
207          */

208         public MyAction(int key)
209         {
210             super(key);
211         }
212
213         /**
214          * quit is called if the Esc key pressed, load is called if Enter key is pressed.
215          * @param e never used.
216          */

217         public void actionPerformed(ActionEvent e)
218         {
219             if (getKey() == ESCAPE)
220                 quit();
221             else if (getKey() == ENTER)
222                 load();
223         }
224     }
225
226     /**
227      * This is the AbstractBinaryEditor interface method which is called when the user wants to edit the password
228      */

229     public void setValue(editablebinary editMe)
230     {
231         this.editMe = editMe;
232         oldPwd.setText(stringEncode(editMe.getValue()));
233     }
234
235     /**
236      *
237      * @param s
238      * @return
239      */

240     protected byte[] plainDecode(String JavaDoc s)
241     {
242         try
243         {
244             return s.getBytes("UTF-8");
245         }
246         catch (UnsupportedEncodingException e)
247         {
248             log.log(Level.WARNING, "Unexpected error encoding password ", e);
249             e.printStackTrace();
250             return new byte[0];
251         }
252     }
253
254     /**
255      * Calculates the pwd hash to be stored in the userPassword field.
256      * @param s The password in plaintext that should be hashed.
257      * @param type The encryption scheme (The {CRYPT} scheme is currently unsupported): t == 2 means MD5 (salt needs to
258      * be null) t == 3 means SMD5 (needs a salt != null) t == 4 means SHA (salt needs to be null) t == 5 means SSHA
259      * (needs a salt != null)
260      * @param salt The salt that is to be used together with the schemes {SMD5} and {SSHA}. Should be between 8 and 16
261      * Bytes. salt should be null for any other scheme.
262      * @return The base64-encoded hashed pwd with the following format: - {MD5}base64(MD5-hash) for MD5 hashes -
263      * {SHA}base64(SHA-hash) for SHA hashes - {SMD5}base64(MD5-hash+salt bytes) for SMD5 hashes -
264      * {SSHA}base64(SHA-hash+salt bytes) for SSHA hashes Or null if t is not one of 2, 3, 4, 5.
265      */

266     protected byte[] mdDecode(String JavaDoc s, int type, byte[] salt)
267     {
268         try
269         {
270             MessageDigest JavaDoc md;
271             StringBuffer JavaDoc hexString = new StringBuffer JavaDoc();
272
273             if ((type == 5) && (salt != null))
274             {
275                 md = MessageDigest.getInstance(SHA);
276                 hexString.append("{" + SSHA + "}");
277             }
278             else if (type == 4)
279             {
280                 md = MessageDigest.getInstance(SHA);
281                 hexString.append("{" + SHA + "}");
282             }
283             else if ((type == 3) && (salt != null))
284             {
285                 md = MessageDigest.getInstance(MD5);
286                 hexString.append("{" + SMD5 + "}");
287             }
288             else if (type == 2)
289             {
290                 md = MessageDigest.getInstance(MD5);
291                 hexString.append("{" + MD5 + "}");
292             }
293             else
294             {
295                 return (null);
296             }
297
298             md.reset();
299             md.update(s.getBytes("UTF-8"));
300
301             if (salt != null)
302             {
303                 // The way the salted hashes work is the following:
304
// h=HASH(pwd+salt)
305
//
306
// To be able to restore the salt it needs to be appended
307
// to the resulting hash.
308
// {SMD5|SSHA}base64(h+salt)
309

310                 // So, lets append the salt to the pwd-buffer in md.
311
md.update(salt);
312
313                 // Calculate the hash-value of s+salt
314
byte[] buff = md.digest();
315
316                 // And append the salt to the hashed pwd.
317
byte[] new_buf = new byte[buff.length + salt.length];
318                 for (int x = 0; x < buff.length; x++)
319                     new_buf[x] = buff[x];
320
321                 for (int x = buff.length; x < new_buf.length; x++)
322                     new_buf[x] = salt[x - buff.length];
323
324                 // New_buf now contains the 16(MD5), resp. 20(SHA1) hash
325
// bytes and the salt.
326
hexString.append(CBBase64.binaryToString(new_buf));
327             }
328             else
329             {
330                 byte[] buff = md.digest();
331                 hexString.append(CBBase64.binaryToString(buff));
332             }
333
334             return hexString.toString().getBytes("UTF-8");
335         }
336         catch (UnsupportedEncodingException e)
337         {
338             log.log(Level.WARNING, "Unexpected error encoding password ", e);
339             e.printStackTrace();
340             return new byte[0];
341         }
342         catch (java.security.NoSuchAlgorithmException JavaDoc e)
343         {
344             log.log(Level.WARNING, "Unexpected error encoding password ", e);
345             e.printStackTrace();
346             return new byte[0];
347         }
348     }
349
350     /**
351      * Converts between text and a byte array
352      */

353     protected byte[] stringDecode(String JavaDoc s, byte[] salt)
354     {
355         if (s == null)
356         {
357             return (new byte[0]);
358         }
359         else
360         {
361             switch (comboType.getSelectedIndex())
362             {
363                 case 1:
364                     return plainDecode(s);
365                 case 2:
366                     return mdDecode(s, 2, null);
367                 case 3:
368                     return mdDecode(s, 3, salt);
369                 case 4:
370                     return mdDecode(s, 4, null);
371                 case 5:
372                     return mdDecode(s, 5, salt);
373                 default:
374                     return mdDecode(s, 4, null);
375             }
376         }
377     }
378
379     /**
380      * Converts between a byte array and text
381      */

382     protected String JavaDoc stringEncode(byte[] b)
383     {
384         if (b == null || b.length == 0)
385         {
386             return new String JavaDoc();
387         }
388         else
389         {
390             try
391             {
392                 return new String JavaDoc(b, "UTF-8");
393             }
394             catch (UnsupportedEncodingException e)
395             {
396                 log.log(Level.WARNING, "Unexpected error decoding password ", e);
397                 e.printStackTrace();
398                 return new String JavaDoc(b); // Fall back on the platform default... not sure this is the best thing to do - CB
399
}
400         }
401     }
402
403     /**
404      * Verifies the given pwd if "verify" is selected or sets the value of the EditableBinary object with whatever the
405      * user has entered into the password text field.
406      */

407     protected void load()
408     {
409         // "verify" selected, so we just verify the given pwd
410
if (comboType.getSelectedIndex() == 0)
411         {
412             String JavaDoc msg_1 = CBIntText.get("Password not verified.");
413             String JavaDoc msg_2 = CBIntText.get("Password Verification.");
414
415             // nPwd is the plaintext pwd the user has entered and
416
// oPwd is the hashed value of the userPassword field.
417
String JavaDoc nPwd = new String JavaDoc(oldPwd.getPassword());
418             String JavaDoc oPwd = stringEncode(editMe.getValue());
419             if (passwordVerify(oPwd, nPwd))
420                 msg_1 = CBIntText.get("Password verified.");
421
422             JOptionPane.showMessageDialog(display, msg_1, msg_2, JOptionPane.INFORMATION_MESSAGE);
423         }
424         // Otherwise create the hashed userPassword
425
else if (passwordConfirm())
426         {
427             byte[] salt = null;
428             // If SSHA or SMD5 is selected, we will need some random salt bytes
429
if ((comboType.getSelectedIndex() == 3) || (comboType.getSelectedIndex() == 5))
430             {
431                 try
432                 {
433                     SecureRandom JavaDoc random = SecureRandom.getInstance("SHA1PRNG");
434                     salt = new byte[8];
435                     random.nextBytes(salt);
436                 }
437                 catch (java.security.NoSuchAlgorithmException JavaDoc e)
438                 {
439                     log.log(Level.WARNING, "Unexpected error encoding password ", e);
440                     e.printStackTrace();
441                     return;
442                 }
443             }
444
445             // Set the new userPassword value and quit()
446
editMe.setValue(stringDecode(new String JavaDoc(newPwd.getPassword()), salt));
447             quit();
448         }
449     }
450
451     /**
452      * Verifies a given password against the password stored in the userPassword-attribute.
453      * <p/>
454      * The userPassword-value should follow the following format: - {MD5}base64(MD5-hash) - {SHA}base64(SHA-hash) -
455      * {SMD5}base64(MD5-hash+salt bytes) - {SSHA}base64(SHA-hash+salt bytes) - plaintext password
456      * <p/>
457      * If the userPassword value does not start with one of the prefixes {MD5}, {SMD5}, {SHA} or {SSHA} it will be
458      * handled as a plaintext pwd.
459      * <p/>
460      * The Unix {CRYPT}-Scheme is currently not supported.
461      * @param oPwd The original pwd stored in the userPassword value.
462      * @param nPwd The password in plaintext that should be verified against the hashed pwd stored in the userPassword
463      * field.
464      * @return True - if the given plaintext pwd matches with the hashed pwd in the userPassword field, otherwise false.
465      * Returns also false for the {CRYPT}-scheme as it is currently unsupported.
466      */

467     protected boolean passwordVerify(String JavaDoc oPwd, String JavaDoc nPwd)
468     {
469         if (oPwd.startsWith("{MD5}"))
470         {
471             // nPwd is the given pwd in cleartext, so
472
// we create the MD5 hash for the given pwd
473
// and store it again in nPwd.
474
nPwd = new String JavaDoc(mdDecode(nPwd, 2, null));
475         }
476         else if (oPwd.startsWith("{SMD5}"))
477         {
478             // SMD5 means "Salted MD5". The "salt" is a
479
// String of an arbitrary length appended to
480
// the given MD5 hash in oPwd.
481

482             // To get the salt bytes first we need to base64-decode
483
// the pwd hash and store it in tmp.
484
byte[] tmp = CBBase64.stringToBinary(oPwd.substring(6));
485
486             // The length of an MD5-hash is always 16 Bytes. To get
487
// the size of the salt we need to subtract 16 Bytes from
488
// the total length of the base64-decoded String.
489
if (tmp != null)
490             {
491                 int len = tmp.length - 16;
492                 if (len > 0)
493                 {
494                     // Get the salt Bytes. As the MD5-Hash takes 16 Bytes,
495
// the remaining Bytes will be the salt.
496
byte[] salt = new byte[len];
497                     for (int x = 0; x < len; x++)
498                         salt[x] = tmp[x + 16];
499
500                     // nPwd is the given pwd in cleartext, so
501
// we create the hash for the given pwd
502
// and store it again in nPwd.
503
nPwd = new String JavaDoc(mdDecode(nPwd, 3, salt));
504                 }
505             }
506         }
507         else if (oPwd.startsWith("{SHA}"))
508         {
509             // nPwd is the given pwd in cleartext, so
510
// we create the hash for the given pwd
511
// and store it again in nPwd.
512
nPwd = new String JavaDoc(mdDecode(nPwd, 4, null));
513         }
514         else if (oPwd.startsWith("{SSHA}"))
515         {
516             // SSHA means "Salted SHA-1". The "salt" is a
517
// String of an arbitrary length appended to
518
// the given SHA-1 hash in oPwd.
519

520             // To get the salt bytes first we need to base64-decode
521
// the pwd hash and store it in tmp.
522
byte[] tmp = CBBase64.stringToBinary(oPwd.substring(6));
523
524             // The length of an SHA-1-hash is always 20 Bytes. To get
525
// the size of the salt we need to subtract 20 Bytes from
526
// the total length of the base64-decoded String.
527
if (tmp != null)
528             {
529                 int len = tmp.length - 20;
530                 if (len > 0)
531                 {
532                     // Get the salt Bytes. As the SHA-1-Hash takes 20 Bytes,
533
// the remaining Bytes will be the salt.
534
byte[] salt = new byte[len];
535                     for (int x = 0; x < len; x++)
536                         salt[x] = tmp[x + 20];
537
538                     // nPwd is the given pwd in cleartext, so
539
// we create the hash for the given pwd
540
// and store it again in nPwd.
541
nPwd = new String JavaDoc(mdDecode(nPwd, 5, salt));
542                 }
543             }
544         }
545         else
546         {
547             // nPwd is already cleartext, no need for
548
// hashing it.
549
nPwd = new String JavaDoc(plainDecode(nPwd));
550         }
551
552         // Compare the two hashed pwd-Strings and return true if
553
// they match, otherwise false. If the Unix {CRYPT}-scheme
554
// is used we return false as well.
555
if (nPwd.equals(oPwd))
556             return (true);
557
558         return (false);
559     }
560
561     /**
562      * Does some checks on the password.
563      * @return True - if the two password fields match. False - if the new password field is empty (an error message is
564      * displayed). False - if the password fields don't match (an error message is displayed).
565      */

566     protected boolean passwordConfirm()
567     {
568         if (new String JavaDoc(newPwd.getPassword()).equals(new String JavaDoc(oldPwd.getPassword()))) // If the two password fields match carry on saving the password.
569
{
570             return true;
571         }
572         else if (new String JavaDoc(newPwd.getPassword()).equals("")) // If the new password field is empty display error message.
573
{
574             JOptionPane.showMessageDialog(display, CBIntText.get("Empty password field, please fill in both fields"), CBIntText.get("Warning message"), JOptionPane.INFORMATION_MESSAGE);
575             newPwd.setText("");
576             return false;
577         }
578         else // If the password fields don't match display error message.
579
{
580             JOptionPane.showMessageDialog(display, CBIntText.get("Password typed incorrectly, please try again"), CBIntText.get("Warning message"), JOptionPane.INFORMATION_MESSAGE);
581             newPwd.setText("");
582             return false;
583         }
584     }
585
586     /**
587      * Shuts down the gui.
588      */

589     protected void quit()
590     {
591         setVisible(false);
592         dispose();
593     }
594 }
595
Popular Tags