KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > security > auth > module > KeyStoreLoginModule


1 /*
2  * @(#)KeyStoreLoginModule.java 1.18 04/05/05
3  *
4  * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
5  * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
6  */

7
8 package com.sun.security.auth.module;
9
10 import javax.security.auth.x500.X500Principal JavaDoc;
11 import java.io.File JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.InputStream JavaDoc;
14 import java.io.PushbackInputStream JavaDoc;
15 import java.net.MalformedURLException JavaDoc;
16 import java.net.URL JavaDoc;
17 import java.security.AuthProvider JavaDoc;
18 import java.security.GeneralSecurityException JavaDoc;
19 import java.security.Key JavaDoc;
20 import java.security.KeyStore JavaDoc;
21 import java.security.KeyStoreException JavaDoc;
22 import java.security.NoSuchAlgorithmException JavaDoc;
23 import java.security.NoSuchProviderException JavaDoc;
24 import java.security.Principal JavaDoc;
25 import java.security.PrivateKey JavaDoc;
26 import java.security.Provider JavaDoc;
27 import java.security.UnrecoverableKeyException JavaDoc;
28 import java.security.cert.*;
29 import java.security.cert.X509Certificate JavaDoc;
30 import java.util.Arrays JavaDoc;
31 import java.util.Iterator JavaDoc;
32 import java.util.LinkedList JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.ResourceBundle JavaDoc;
35 import javax.security.auth.Destroyable JavaDoc;
36 import javax.security.auth.DestroyFailedException JavaDoc;
37 import javax.security.auth.Subject JavaDoc;
38 import javax.security.auth.x500.*;
39 import javax.security.auth.Subject JavaDoc;
40 import javax.security.auth.x500.*;
41 import javax.security.auth.callback.Callback JavaDoc;
42 import javax.security.auth.callback.CallbackHandler JavaDoc;
43 import javax.security.auth.callback.ConfirmationCallback JavaDoc;
44 import javax.security.auth.callback.NameCallback JavaDoc;
45 import javax.security.auth.callback.PasswordCallback JavaDoc;
46 import javax.security.auth.callback.TextOutputCallback JavaDoc;
47 import javax.security.auth.callback.UnsupportedCallbackException JavaDoc;
48 import javax.security.auth.login.FailedLoginException JavaDoc;
49 import javax.security.auth.login.LoginException JavaDoc;
50 import javax.security.auth.spi.LoginModule JavaDoc;
51
52 import sun.security.util.AuthResources;
53 import sun.security.util.Password;
54
55 /**
56  * Provides a JAAS login module that prompts for a key store alias and
57  * populates the subject with the alias's principal and credentials. Stores
58  * an <code>X500Principal</code> for the subject distinguished name of the
59  * first certificate in the alias's credentials in the subject's principals,
60  * the alias's certificate path in the subject's public credentials, and a
61  * <code>X500PrivateCredential</code> whose certificate is the first
62  * certificate in the alias's certificate path and whose private key is the
63  * alias's private key in the subject's private credentials. <p>
64  *
65  * Recognizes the following options in the configuration file:
66  * <dl>
67  *
68  * <dt> <code>keyStoreURL</code> </dt>
69  * <dd> A URL that specifies the location of the key store. Defaults to
70  * a URL pointing to the .keystore file in the directory specified by the
71  * <code>user.home</code> system property. The input stream from this
72  * URL is passed to the <code>KeyStore.load</code> method.
73  * "NONE" may be specified if a <code>null</code> stream must be
74  * passed to the <code>KeyStore.load</code> method.
75  * "NONE" should be specified if the KeyStore resides
76  * on a hardware token device, for example.</dd>
77  *
78  * <dt> <code>keyStoreType</code> </dt>
79  * <dd> The key store type. If not specified, defaults to the result of
80  * calling <code>KeyStore.getDefaultType()</code>.
81  * If the type is "PKCS11", then keyStoreURL must be "NONE"
82  * and privateKeyPasswordURL must not be specified.</dd>
83  *
84  * <dt> <code>keyStoreProvider</code> </dt>
85  * <dd> The key store provider. If not specified, uses the standard search
86  * order to find the provider. </dd>
87  *
88  * <dt> <code>keyStoreAlias</code> </dt>
89  * <dd> The alias in the key store to login as. Required when no callback
90  * handler is provided. No default value. </dd>
91  *
92  * <dt> <code>keyStorePasswordURL</code> </dt>
93  * <dd> A URL that specifies the location of the key store password. Required
94  * when no callback handler is provided and
95  * <code>protected</code> is false.
96  * No default value. </dd>
97  *
98  * <dt> <code>privateKeyPasswordURL</code> </dt>
99  * <dd> A URL that specifies the location of the specific private key password
100  * needed to access the private key for this alias.
101  * The keystore password
102  * is used if this value is needed and not specified. </dd>
103  *
104  * <dt> <code>protected</code> </dt>
105  * <dd> This value should be set to "true" if the KeyStore
106  * has a separate, protected authentication path
107  * (for example, a dedicated PIN-pad attached to a smart card).
108  * Defaults to "false". If "true" keyStorePasswordURL and
109  * privateKeyPasswordURL must not be specified.</dd>
110  *
111  * </dl>
112  */

113 public class KeyStoreLoginModule implements LoginModule JavaDoc {
114
115    static final java.util.ResourceBundle JavaDoc rb =
116         java.util.ResourceBundle.getBundle("sun.security.util.AuthResources");
117
118     /* -- Fields -- */
119
120     private static final int UNINITIALIZED = 0;
121     private static final int INITIALIZED = 1;
122     private static final int AUTHENTICATED = 2;
123     private static final int LOGGED_IN = 3;
124
125     private static final int PROTECTED_PATH = 0;
126     private static final int TOKEN = 1;
127     private static final int NORMAL = 2;
128
129     private static final String JavaDoc NONE = "NONE";
130     private static final String JavaDoc P11KEYSTORE = "PKCS11";
131
132     private static final TextOutputCallback JavaDoc bannerCallback =
133         new TextOutputCallback JavaDoc
134             (TextOutputCallback.INFORMATION,
135             rb.getString("Please enter keystore information"));
136     private final ConfirmationCallback JavaDoc confirmationCallback =
137         new ConfirmationCallback JavaDoc
138             (ConfirmationCallback.INFORMATION,
139             ConfirmationCallback.OK_CANCEL_OPTION,
140             ConfirmationCallback.OK);
141
142     private Subject JavaDoc subject;
143     private CallbackHandler JavaDoc callbackHandler;
144     private Map JavaDoc sharedState;
145     private Map JavaDoc options;
146
147     private char[] keyStorePassword;
148     private char[] privateKeyPassword;
149     private KeyStore JavaDoc keyStore;
150
151     private String JavaDoc keyStoreURL;
152     private String JavaDoc keyStoreType;
153     private String JavaDoc keyStoreProvider;
154     private String JavaDoc keyStoreAlias;
155     private String JavaDoc keyStorePasswordURL;
156     private String JavaDoc privateKeyPasswordURL;
157     private boolean debug;
158     private javax.security.auth.x500.X500Principal JavaDoc principal;
159     private Certificate JavaDoc[] fromKeyStore;
160     private java.security.cert.CertPath JavaDoc certP = null;
161     private X500PrivateCredential privateCredential;
162     private int status = UNINITIALIZED;
163     private boolean nullStream = false;
164     private boolean token = false;
165     private boolean protectedPath = false;
166
167     /* -- Methods -- */
168   
169     /**
170      * Initialize this <code>LoginModule</code>.
171      *
172      * <p>
173      *
174      * @param subject the <code>Subject</code> to be authenticated. <p>
175      *
176      * @param callbackHandler a <code>CallbackHandler</code> for communicating
177      * with the end user (prompting for usernames and
178      * passwords, for example),
179      * which may be <code>null</code>. <p>
180      *
181      * @param sharedState shared <code>LoginModule</code> state. <p>
182      *
183      * @param options options specified in the login
184      * <code>Configuration</code> for this particular
185      * <code>LoginModule</code>.
186      */

187
188     public void initialize(Subject JavaDoc subject,
189                CallbackHandler JavaDoc callbackHandler,
190                Map JavaDoc<String JavaDoc,?> sharedState,
191                Map JavaDoc<String JavaDoc,?> options)
192     {
193     this.subject = subject;
194     this.callbackHandler = callbackHandler;
195     this.sharedState = sharedState;
196     this.options = options;
197
198     processOptions();
199     status = INITIALIZED;
200     }
201
202     private void processOptions() {
203     keyStoreURL = (String JavaDoc) options.get("keyStoreURL");
204     if (keyStoreURL == null) {
205         keyStoreURL =
206         "file:" +
207         System.getProperty("user.home").replace(
208             File.separatorChar, '/') +
209         '/' + ".keystore";
210     } else if (NONE.equals(keyStoreURL)) {
211         nullStream = true;
212     }
213     keyStoreType = (String JavaDoc) options.get("keyStoreType");
214     if (keyStoreType == null) {
215         keyStoreType = KeyStore.getDefaultType();
216     }
217     if (P11KEYSTORE.equalsIgnoreCase(keyStoreType)) {
218         token = true;
219     }
220
221     keyStoreProvider = (String JavaDoc) options.get("keyStoreProvider");
222
223     keyStoreAlias = (String JavaDoc) options.get("keyStoreAlias");
224
225     keyStorePasswordURL = (String JavaDoc) options.get("keyStorePasswordURL");
226
227     privateKeyPasswordURL = (String JavaDoc) options.get("privateKeyPasswordURL");
228
229     protectedPath = "true".equalsIgnoreCase((String JavaDoc)options.get
230                     ("protected"));
231
232     debug = "true".equalsIgnoreCase((String JavaDoc) options.get("debug"));
233     if (debug) {
234         debugPrint(null);
235         debugPrint("keyStoreURL=" + keyStoreURL);
236         debugPrint("keyStoreType=" + keyStoreType);
237         debugPrint("keyStoreProvider=" + keyStoreProvider);
238         debugPrint("keyStoreAlias=" + keyStoreAlias);
239         debugPrint("keyStorePasswordURL=" + keyStorePasswordURL);
240         debugPrint("privateKeyPasswordURL=" + privateKeyPasswordURL);
241         debugPrint("protectedPath=" + protectedPath);
242         debugPrint(null);
243     }
244     }
245     
246     /**
247      * Authenticate the user.
248      *
249      * <p> Get the Keystore alias and relevant passwords.
250      * Retrieve the alias's principal and credentials from the Keystore.
251      *
252      * <p>
253      *
254      * @exception FailedLoginException if the authentication fails. <p>
255      *
256      * @return true in all cases (this <code>LoginModule</code>
257      * should not be ignored).
258      */

259
260     public boolean login() throws LoginException JavaDoc {
261     switch (status) {
262     case UNINITIALIZED:
263     default:
264         throw new LoginException JavaDoc("The login module is not initialized");
265     case INITIALIZED:
266     case AUTHENTICATED:
267
268         if (token && !nullStream) {
269         throw new LoginException JavaDoc
270             ("if keyStoreType is " + P11KEYSTORE +
271             " then keyStoreURL must be " + NONE);
272         }
273
274         if (token && privateKeyPasswordURL != null) {
275         throw new LoginException JavaDoc
276             ("if keyStoreType is " + P11KEYSTORE +
277             " then privateKeyPasswordURL must not be specified");
278         }
279
280         if (protectedPath &&
281         (keyStorePasswordURL != null ||
282             privateKeyPasswordURL != null)) {
283         throw new LoginException JavaDoc
284             ("if protected is true then keyStorePasswordURL and " +
285             "privateKeyPasswordURL must not be specified");
286         }
287
288         // get relevant alias and password info
289

290         if (protectedPath) {
291         getAliasAndPasswords(PROTECTED_PATH);
292         } else if (token) {
293         getAliasAndPasswords(TOKEN);
294         } else {
295         getAliasAndPasswords(NORMAL);
296         }
297
298         // log into KeyStore to retrieve data,
299
// then clear passwords
300

301         try {
302         getKeyStoreInfo();
303         } finally {
304         if (privateKeyPassword != null &&
305             privateKeyPassword != keyStorePassword) {
306             Arrays.fill(privateKeyPassword, '\0');
307             privateKeyPassword = null;
308         }
309         if (keyStorePassword != null) {
310             Arrays.fill(keyStorePassword, '\0');
311             keyStorePassword = null;
312         }
313         }
314         status = AUTHENTICATED;
315         return true;
316     case LOGGED_IN:
317         return true;
318     }
319     }
320
321     /** Get the alias and passwords to use for looking up in the KeyStore. */
322     private void getAliasAndPasswords(int env) throws LoginException JavaDoc {
323     if (callbackHandler == null) {
324
325         // No callback handler - check for alias and password options
326

327         switch (env) {
328         case PROTECTED_PATH:
329         checkAlias();
330         break;
331         case TOKEN:
332         checkAlias();
333         checkStorePass();
334         break;
335         case NORMAL:
336         checkAlias();
337         checkStorePass();
338         checkKeyPass();
339         break;
340         }
341
342     } else {
343
344         // Callback handler available - prompt for alias and passwords
345

346         NameCallback JavaDoc aliasCallback;
347         if (keyStoreAlias == null || keyStoreAlias.length() == 0) {
348         aliasCallback = new NameCallback JavaDoc(
349                         rb.getString("Keystore alias: "));
350         } else {
351         aliasCallback =
352             new NameCallback JavaDoc(rb.getString("Keystore alias: "),
353                      keyStoreAlias);
354         }
355
356         PasswordCallback JavaDoc storePassCallback = null;
357         PasswordCallback JavaDoc keyPassCallback = null;
358
359         switch (env) {
360         case PROTECTED_PATH:
361         break;
362         case NORMAL:
363         keyPassCallback = new PasswordCallback JavaDoc
364             (rb.getString("Private key password (optional): "), false);
365         // fall thru
366
case TOKEN:
367         storePassCallback = new PasswordCallback JavaDoc
368             (rb.getString("Keystore password: "), false);
369         break;
370         }
371         prompt(aliasCallback, storePassCallback, keyPassCallback);
372     }
373
374     if (debug) {
375         debugPrint("alias=" + keyStoreAlias);
376     }
377     }
378
379     private void checkAlias() throws LoginException JavaDoc {
380     if (keyStoreAlias == null) {
381         throw new LoginException JavaDoc
382         ("Need to specify an alias option to use " +
383         "KeyStoreLoginModule non-interactively.");
384     }
385     }
386
387     private void checkStorePass() throws LoginException JavaDoc {
388     if (keyStorePasswordURL == null) {
389         throw new LoginException JavaDoc
390         ("Need to specify keyStorePasswordURL option to use " +
391         "KeyStoreLoginModule non-interactively.");
392     }
393     try {
394         InputStream JavaDoc in = new URL JavaDoc(keyStorePasswordURL).openStream();
395         keyStorePassword = Password.readPassword(in);
396         in.close();
397     } catch (IOException JavaDoc e) {
398         LoginException JavaDoc le = new LoginException JavaDoc
399         ("Problem accessing keystore password \"" +
400         keyStorePasswordURL + "\"");
401         le.initCause(e);
402         throw le;
403     }
404     }
405
406     private void checkKeyPass() throws LoginException JavaDoc {
407     if (privateKeyPasswordURL == null) {
408         privateKeyPassword = keyStorePassword;
409     } else {
410         try {
411         InputStream JavaDoc in = new URL JavaDoc(privateKeyPasswordURL).openStream();
412         privateKeyPassword = Password.readPassword(in);
413         in.close();
414         } catch (IOException JavaDoc e) {
415         LoginException JavaDoc le = new LoginException JavaDoc
416             ("Problem accessing private key password \"" +
417             privateKeyPasswordURL + "\"");
418         le.initCause(e);
419         throw le;
420         }
421     }
422     }
423
424     private void prompt(NameCallback JavaDoc aliasCallback,
425             PasswordCallback JavaDoc storePassCallback,
426             PasswordCallback JavaDoc keyPassCallback)
427         throws LoginException JavaDoc {
428
429     if (storePassCallback == null) {
430
431         // only prompt for alias
432

433         try {
434         callbackHandler.handle(
435             new Callback JavaDoc[] {
436             bannerCallback, aliasCallback, confirmationCallback
437             });
438         } catch (IOException JavaDoc e) {
439         LoginException JavaDoc le = new LoginException JavaDoc
440             ("Problem retrieving keystore alias");
441         le.initCause(e);
442         throw le;
443         } catch (UnsupportedCallbackException JavaDoc e) {
444         throw new LoginException JavaDoc(
445             "Error: " + e.getCallback().toString() +
446             " is not available to retrieve authentication " +
447             " information from the user");
448         }
449
450         int confirmationResult = confirmationCallback.getSelectedIndex();
451
452         if (confirmationResult == ConfirmationCallback.CANCEL) {
453         throw new LoginException JavaDoc("Login cancelled");
454         }
455
456         saveAlias(aliasCallback);
457
458     } else if (keyPassCallback == null) {
459
460         // prompt for alias and key store password
461

462         try {
463         callbackHandler.handle(
464             new Callback JavaDoc[] {
465             bannerCallback, aliasCallback,
466             storePassCallback, confirmationCallback
467             });
468         } catch (IOException JavaDoc e) {
469         LoginException JavaDoc le = new LoginException JavaDoc
470             ("Problem retrieving keystore alias and password");
471         le.initCause(e);
472         throw le;
473         } catch (UnsupportedCallbackException JavaDoc e) {
474         throw new LoginException JavaDoc(
475             "Error: " + e.getCallback().toString() +
476             " is not available to retrieve authentication " +
477             " information from the user");
478         }
479
480         int confirmationResult = confirmationCallback.getSelectedIndex();
481
482         if (confirmationResult == ConfirmationCallback.CANCEL) {
483         throw new LoginException JavaDoc("Login cancelled");
484         }
485
486         saveAlias(aliasCallback);
487         saveStorePass(storePassCallback);
488
489     } else {
490
491         // prompt for alias, key store password, and key password
492

493         try {
494         callbackHandler.handle(
495             new Callback JavaDoc[] {
496             bannerCallback, aliasCallback,
497             storePassCallback, keyPassCallback,
498             confirmationCallback
499             });
500         } catch (IOException JavaDoc e) {
501         LoginException JavaDoc le = new LoginException JavaDoc
502             ("Problem retrieving keystore alias and passwords");
503         le.initCause(e);
504         throw le;
505         } catch (UnsupportedCallbackException JavaDoc e) {
506         throw new LoginException JavaDoc(
507             "Error: " + e.getCallback().toString() +
508             " is not available to retrieve authentication " +
509             " information from the user");
510         }
511
512         int confirmationResult = confirmationCallback.getSelectedIndex();
513
514         if (confirmationResult == ConfirmationCallback.CANCEL) {
515         throw new LoginException JavaDoc("Login cancelled");
516         }
517
518         saveAlias(aliasCallback);
519         saveStorePass(storePassCallback);
520         saveKeyPass(keyPassCallback);
521     }
522     }
523
524     private void saveAlias(NameCallback JavaDoc cb) {
525     keyStoreAlias = cb.getName();
526     }
527    
528     private void saveStorePass(PasswordCallback JavaDoc c) {
529     keyStorePassword = c.getPassword();
530     if (keyStorePassword == null) {
531         /* Treat a NULL password as an empty password */
532         keyStorePassword = new char[0];
533     }
534     c.clearPassword();
535     }
536
537     private void saveKeyPass(PasswordCallback JavaDoc c) {
538     privateKeyPassword = c.getPassword();
539     if (privateKeyPassword == null || privateKeyPassword.length == 0) {
540         /*
541          * Use keystore password if no private key password is
542          * specified.
543          */

544         privateKeyPassword = keyStorePassword;
545     }
546     c.clearPassword();
547     }
548
549     /** Get the credentials from the KeyStore. */
550     private void getKeyStoreInfo() throws LoginException JavaDoc {
551
552     /* Get KeyStore instance */
553     try {
554         if (keyStoreProvider == null) {
555         keyStore = KeyStore.getInstance(keyStoreType);
556         } else {
557         keyStore =
558             KeyStore.getInstance(keyStoreType, keyStoreProvider);
559         }
560     } catch (KeyStoreException JavaDoc e) {
561         LoginException JavaDoc le = new LoginException JavaDoc
562         ("The specified keystore type was not available");
563         le.initCause(e);
564         throw le;
565     } catch (NoSuchProviderException JavaDoc e) {
566         LoginException JavaDoc le = new LoginException JavaDoc
567         ("The specified keystore provider was not available");
568         le.initCause(e);
569         throw le;
570     }
571
572     /* Load KeyStore contents from file */
573     try {
574         if (nullStream) {
575         // if using protected auth path, keyStorePassword will be null
576
keyStore.load(null, keyStorePassword);
577         } else {
578         InputStream JavaDoc in = new URL JavaDoc(keyStoreURL).openStream();
579         keyStore.load(in, keyStorePassword);
580         in.close();
581         }
582     } catch (MalformedURLException JavaDoc e) {
583         LoginException JavaDoc le = new LoginException JavaDoc
584                 ("Incorrect keyStoreURL option");
585         le.initCause(e);
586         throw le;
587     } catch (GeneralSecurityException JavaDoc e) {
588         LoginException JavaDoc le = new LoginException JavaDoc
589                 ("Error initializing keystore");
590         le.initCause(e);
591         throw le;
592     } catch (IOException JavaDoc e) {
593         LoginException JavaDoc le = new LoginException JavaDoc
594                 ("Error initializing keystore");
595         le.initCause(e);
596         throw le;
597     }
598
599     /* Get certificate chain and create a certificate path */
600     try {
601         fromKeyStore =
602         keyStore.getCertificateChain(keyStoreAlias);
603         if (fromKeyStore == null
604         || fromKeyStore.length == 0
605         || !(fromKeyStore[0] instanceof X509Certificate JavaDoc))
606         {
607         throw new FailedLoginException JavaDoc(
608             "Unable to find X.509 certificate chain in keystore");
609         } else {
610         LinkedList JavaDoc certList = new LinkedList JavaDoc();
611         for (int i=0; i < fromKeyStore.length; i++) {
612             certList.add(fromKeyStore[i]);
613         }
614         CertificateFactory certF=
615             CertificateFactory.getInstance("X.509");
616         certP =
617             certF.generateCertPath(certList);
618         }
619     } catch (KeyStoreException JavaDoc e) {
620         LoginException JavaDoc le = new LoginException JavaDoc("Error using keystore");
621         le.initCause(e);
622         throw le;
623     } catch (CertificateException ce) {
624         LoginException JavaDoc le = new LoginException JavaDoc
625         ("Error: X.509 Certificate type unavailable");
626         le.initCause(ce);
627         throw le;
628     }
629
630     /* Get principal and keys */
631     try {
632         X509Certificate JavaDoc certificate = (X509Certificate JavaDoc)fromKeyStore[0];
633         principal = new javax.security.auth.x500.X500Principal JavaDoc
634         (certificate.getSubjectDN().getName());
635
636         // if token, privateKeyPassword will be null
637
Key JavaDoc privateKey = keyStore.getKey(keyStoreAlias, privateKeyPassword);
638         if (privateKey == null
639         || !(privateKey instanceof PrivateKey JavaDoc))
640         {
641         throw new FailedLoginException JavaDoc(
642             "Unable to recover key from keystore");
643         }
644
645         privateCredential = new X500PrivateCredential(
646         certificate, (PrivateKey JavaDoc) privateKey, keyStoreAlias);
647     } catch (KeyStoreException JavaDoc e) {
648         LoginException JavaDoc le = new LoginException JavaDoc("Error using keystore");
649         le.initCause(e);
650         throw le;
651     } catch (NoSuchAlgorithmException JavaDoc e) {
652         LoginException JavaDoc le = new LoginException JavaDoc("Error using keystore");
653         le.initCause(e);
654         throw le;
655     } catch (UnrecoverableKeyException JavaDoc e) {
656         FailedLoginException JavaDoc fle = new FailedLoginException JavaDoc
657                 ("Unable to recover key from keystore");
658         fle.initCause(e);
659         throw fle;
660     }
661     if (debug) {
662         debugPrint("principal=" + principal +
663                "\n certificate="
664                + privateCredential.getCertificate() +
665                "\n alias =" + privateCredential.getAlias());
666     }
667     }
668
669     /**
670      * Abstract method to commit the authentication process (phase 2).
671      *
672      * <p> This method is called if the LoginContext's
673      * overall authentication succeeded
674      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
675      * succeeded).
676      *
677      * <p> If this LoginModule's own authentication attempt
678      * succeeded (checked by retrieving the private state saved by the
679      * <code>login</code> method), then this method associates a
680      * <code>X500Principal</code> for the subject distinguished name of the
681      * first certificate in the alias's credentials in the subject's
682      * principals,the alias's certificate path in the subject's public
683      * credentials, and a<code>X500PrivateCredential</code> whose certificate
684      * is the first certificate in the alias's certificate path and whose
685      * private key is the alias's private key in the subject's private
686      * credentials. If this LoginModule's own
687      * authentication attempted failed, then this method removes
688      * any state that was originally saved.
689      *
690      * <p>
691      *
692      * @exception LoginException if the commit fails
693      *
694      * @return true if this LoginModule's own login and commit
695      * attempts succeeded, or false otherwise.
696      */

697
698     public boolean commit() throws LoginException JavaDoc {
699     switch (status) {
700     case UNINITIALIZED:
701     default:
702         throw new LoginException JavaDoc("The login module is not initialized");
703     case INITIALIZED:
704         logoutInternal();
705         throw new LoginException JavaDoc("Authentication failed");
706     case AUTHENTICATED:
707         if (commitInternal()) {
708         return true;
709         } else {
710         logoutInternal();
711         throw new LoginException JavaDoc("Unable to retrieve certificates");
712         }
713     case LOGGED_IN:
714         return true;
715     }
716     }
717
718     private boolean commitInternal() throws LoginException JavaDoc {
719     /* If the subject is not readonly add to the principal and credentials
720      * set; otherwise just return true
721      */

722     if (subject.isReadOnly()) {
723         throw new LoginException JavaDoc ("Subject is set readonly");
724     } else {
725         subject.getPrincipals().add(principal);
726         subject.getPublicCredentials().add(certP);
727         subject.getPrivateCredentials().add(privateCredential);
728         status = LOGGED_IN;
729         return true;
730     }
731     }
732
733     /**
734      * <p> This method is called if the LoginContext's
735      * overall authentication failed.
736      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
737      * did not succeed).
738      *
739      * <p> If this LoginModule's own authentication attempt
740      * succeeded (checked by retrieving the private state saved by the
741      * <code>login</code> and <code>commit</code> methods),
742      * then this method cleans up any state that was originally saved.
743      *
744      * <p> If the loaded KeyStore's provider extends
745      * <code>java.security.AuthProvider</code>,
746      * then the provider's <code>logout</code> method is invoked.
747      *
748      * <p>
749      *
750      * @exception LoginException if the abort fails.
751      *
752      * @return false if this LoginModule's own login and/or commit attempts
753      * failed, and true otherwise.
754      */

755     
756     public boolean abort() throws LoginException JavaDoc {
757     switch (status) {
758     case UNINITIALIZED:
759     default:
760         return false;
761     case INITIALIZED:
762         return false;
763     case AUTHENTICATED:
764         logoutInternal();
765         return true;
766     case LOGGED_IN:
767         logoutInternal();
768         return true;
769     }
770     }
771     /**
772      * Logout a user.
773      *
774      * <p> This method removes the Principals, public credentials and the
775      * private credentials that were added by the <code>commit</code> method.
776      *
777      * <p> If the loaded KeyStore's provider extends
778      * <code>java.security.AuthProvider</code>,
779      * then the provider's <code>logout</code> method is invoked.
780      *
781      * <p>
782      *
783      * @exception LoginException if the logout fails.
784      *
785      * @return true in all cases since this <code>LoginModule</code>
786      * should not be ignored.
787      */

788
789     public boolean logout() throws LoginException JavaDoc {
790     if (debug)
791         debugPrint("Entering logout " + status);
792     switch (status) {
793     case UNINITIALIZED:
794         throw new LoginException JavaDoc
795         ("The login module is not initialized");
796     case INITIALIZED:
797     case AUTHENTICATED:
798     default:
799        // impossible for LoginModule to be in AUTHENTICATED
800
// state
801
// assert status != AUTHENTICATED;
802
return false;
803     case LOGGED_IN:
804         logoutInternal();
805         return true;
806     }
807     }
808
809     private void logoutInternal() throws LoginException JavaDoc {
810     if (debug) {
811         debugPrint("Entering logoutInternal");
812     }
813
814     // assumption is that KeyStore.load did a login -
815
// perform explicit logout if possible
816
LoginException JavaDoc logoutException = null;
817     Provider JavaDoc provider = keyStore.getProvider();
818     if (provider instanceof AuthProvider JavaDoc) {
819         AuthProvider JavaDoc ap = (AuthProvider JavaDoc)provider;
820         try {
821         ap.logout();
822         if (debug) {
823             debugPrint("logged out of KeyStore AuthProvider");
824         }
825         } catch (LoginException JavaDoc le) {
826         // save but continue below
827
logoutException = le;
828         }
829     }
830
831     if (subject.isReadOnly()) {
832         // attempt to destroy the private credential
833
// even if the Subject is read-only
834
principal = null;
835         certP = null;
836         status = INITIALIZED;
837         // destroy the private credential
838
Iterator JavaDoc it = subject.getPrivateCredentials().iterator();
839         while (it.hasNext()) {
840         Object JavaDoc obj = it.next();
841         if (privateCredential.equals(obj)) {
842             privateCredential = null;
843             try {
844             ((Destroyable JavaDoc)obj).destroy();
845             if (debug)
846                 debugPrint("Destroyed private credential, " +
847                        obj.getClass().getName());
848             break;
849             } catch (DestroyFailedException JavaDoc dfe) {
850             LoginException JavaDoc le = new LoginException JavaDoc
851                 ("Unable to destroy private credential, "
852                  + obj.getClass().getName());
853             le.initCause(dfe);
854             throw le;
855             }
856         }
857         }
858         
859         // throw an exception because we can not remove
860
// the principal and public credential from this
861
// read-only Subject
862
throw new LoginException JavaDoc
863         ("Unable to remove Principal ("
864          + "X500Principal "
865          + ") and public credential (certificatepath) "
866          + "from read-only Subject");
867     }
868     if (principal != null) {
869         subject.getPrincipals().remove(principal);
870         principal = null;
871     }
872     if (certP != null) {
873         subject.getPublicCredentials().remove(certP);
874         certP = null;
875     }
876     if (privateCredential != null) {
877         subject.getPrivateCredentials().remove(privateCredential);
878         privateCredential = null;
879     }
880
881     // throw pending logout exception if there is one
882
if (logoutException != null) {
883         throw logoutException;
884     }
885     status = INITIALIZED;
886     }
887
888     private void debugPrint(String JavaDoc message) {
889     // we should switch to logging API
890
if (message == null) {
891         System.err.println();
892     } else {
893         System.err.println("Debug KeyStoreLoginModule: " + message);
894     }
895     }
896 }
897
Popular Tags