History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/com/sun/security/auth/module/KeyStoreLoginModule.java (1 of 18352 files). Show all

Unified · Split

java/pkg/com/sun/security/auth/module/KeyStoreLoginModule.java+947−0
@@ -0,0 +1,947 @@
1+/*
2+ * Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package com.sun.security.auth.module;
27+
28+import javax.security.auth.x500.X500Principal;
29+import java.io.File;
30+import java.io.IOException;
31+import java.io.InputStream;
32+import java.io.PushbackInputStream;
33+import java.net.MalformedURLException;
34+import java.net.URL;
35+import java.security.AuthProvider;
36+import java.security.GeneralSecurityException;
37+import java.security.Key;
38+import java.security.KeyStore;
39+import java.security.KeyStoreException;
40+import java.security.NoSuchAlgorithmException;
41+import java.security.NoSuchProviderException;
42+import java.security.Principal;
43+import java.security.PrivateKey;
44+import java.security.Provider;
45+import java.security.UnrecoverableKeyException;
46+import java.security.cert.*;
47+import java.security.cert.X509Certificate;
48+import java.util.Arrays;
49+import java.util.Iterator;
50+import java.util.LinkedList;
51+import java.util.Map;
52+import java.util.ResourceBundle;
53+import javax.security.auth.Destroyable;
54+import javax.security.auth.DestroyFailedException;
55+import javax.security.auth.Subject;
56+import javax.security.auth.x500.*;
57+import javax.security.auth.Subject;
58+import javax.security.auth.x500.*;
59+import javax.security.auth.callback.Callback;
60+import javax.security.auth.callback.CallbackHandler;
61+import javax.security.auth.callback.ConfirmationCallback;
62+import javax.security.auth.callback.NameCallback;
63+import javax.security.auth.callback.PasswordCallback;
64+import javax.security.auth.callback.TextOutputCallback;
65+import javax.security.auth.callback.UnsupportedCallbackException;
66+import javax.security.auth.login.FailedLoginException;
67+import javax.security.auth.login.LoginException;
68+import javax.security.auth.spi.LoginModule;
69+
70+import sun.security.util.AuthResources;
71+import sun.security.util.Password;
72+
73+/**
74+ * Provides a JAAS login module that prompts for a key store alias and
75+ * populates the subject with the alias's principal and credentials. Stores
76+ * an <code>X500Principal</code> for the subject distinguished name of the
77+ * first certificate in the alias's credentials in the subject's principals,
78+ * the alias's certificate path in the subject's public credentials, and a
79+ * <code>X500PrivateCredential</code> whose certificate is the first
80+ * certificate in the alias's certificate path and whose private key is the
81+ * alias's private key in the subject's private credentials. <p>
82+ *
83+ * Recognizes the following options in the configuration file:
84+ * <dl>
85+ *
86+ * <dt> <code>keyStoreURL</code> </dt>
87+ * <dd> A URL that specifies the location of the key store. Defaults to
88+ * a URL pointing to the .keystore file in the directory specified by the
89+ * <code>user.home</code> system property. The input stream from this
90+ * URL is passed to the <code>KeyStore.load</code> method.
91+ * "NONE" may be specified if a <code>null</code> stream must be
92+ * passed to the <code>KeyStore.load</code> method.
93+ * "NONE" should be specified if the KeyStore resides
94+ * on a hardware token device, for example.</dd>
95+ *
96+ * <dt> <code>keyStoreType</code> </dt>
97+ * <dd> The key store type. If not specified, defaults to the result of
98+ * calling <code>KeyStore.getDefaultType()</code>.
99+ * If the type is "PKCS11", then keyStoreURL must be "NONE"
100+ * and privateKeyPasswordURL must not be specified.</dd>
101+ *
102+ * <dt> <code>keyStoreProvider</code> </dt>
103+ * <dd> The key store provider. If not specified, uses the standard search
104+ * order to find the provider. </dd>
105+ *
106+ * <dt> <code>keyStoreAlias</code> </dt>
107+ * <dd> The alias in the key store to login as. Required when no callback
108+ * handler is provided. No default value. </dd>
109+ *
110+ * <dt> <code>keyStorePasswordURL</code> </dt>
111+ * <dd> A URL that specifies the location of the key store password. Required
112+ * when no callback handler is provided and
113+ * <code>protected</code> is false.
114+ * No default value. </dd>
115+ *
116+ * <dt> <code>privateKeyPasswordURL</code> </dt>
117+ * <dd> A URL that specifies the location of the specific private key password
118+ * needed to access the private key for this alias.
119+ * The keystore password
120+ * is used if this value is needed and not specified. </dd>
121+ *
122+ * <dt> <code>protected</code> </dt>
123+ * <dd> This value should be set to "true" if the KeyStore
124+ * has a separate, protected authentication path
125+ * (for example, a dedicated PIN-pad attached to a smart card).
126+ * Defaults to "false". If "true" keyStorePasswordURL and
127+ * privateKeyPasswordURL must not be specified.</dd>
128+ *
129+ * </dl>
130+ */
131+public class KeyStoreLoginModule implements LoginModule {
132+
133+ static final java.util.ResourceBundle rb =
134+ java.util.ResourceBundle.getBundle("sun.security.util.AuthResources");
135+
136+ /* -- Fields -- */
137+
138+ private static final int UNINITIALIZED = 0;
139+ private static final int INITIALIZED = 1;
140+ private static final int AUTHENTICATED = 2;
141+ private static final int LOGGED_IN = 3;
142+
143+ private static final int PROTECTED_PATH = 0;
144+ private static final int TOKEN = 1;
145+ private static final int NORMAL = 2;
146+
147+ private static final String NONE = "NONE";
148+ private static final String P11KEYSTORE = "PKCS11";
149+
150+ private static final TextOutputCallback bannerCallback =
151+ new TextOutputCallback
152+ (TextOutputCallback.INFORMATION,
153+ rb.getString("Please.enter.keystore.information"));
154+ private final ConfirmationCallback confirmationCallback =
155+ new ConfirmationCallback
156+ (ConfirmationCallback.INFORMATION,
157+ ConfirmationCallback.OK_CANCEL_OPTION,
158+ ConfirmationCallback.OK);
159+
160+ private Subject subject;
161+ private CallbackHandler callbackHandler;
162+ private Map sharedState;
163+ private Map<String, ?> options;
164+
165+ private char[] keyStorePassword;
166+ private char[] privateKeyPassword;
167+ private KeyStore keyStore;
168+
169+ private String keyStoreURL;
170+ private String keyStoreType;
171+ private String keyStoreProvider;
172+ private String keyStoreAlias;
173+ private String keyStorePasswordURL;
174+ private String privateKeyPasswordURL;
175+ private boolean debug;
176+ private javax.security.auth.x500.X500Principal principal;
177+ private Certificate[] fromKeyStore;
178+ private java.security.cert.CertPath certP = null;
179+ private X500PrivateCredential privateCredential;
180+ private int status = UNINITIALIZED;
181+ private boolean nullStream = false;
182+ private boolean token = false;
183+ private boolean protectedPath = false;
184+
185+ /* -- Methods -- */
186+
187+ /**
188+ * Initialize this <code>LoginModule</code>.
189+ *
190+ * <p>
191+ *
192+ * @param subject the <code>Subject</code> to be authenticated. <p>
193+ *
194+ * @param callbackHandler a <code>CallbackHandler</code> for communicating
195+ * with the end user (prompting for usernames and
196+ * passwords, for example),
197+ * which may be <code>null</code>. <p>
198+ *
199+ * @param sharedState shared <code>LoginModule</code> state. <p>
200+ *
201+ * @param options options specified in the login
202+ * <code>Configuration</code> for this particular
203+ * <code>LoginModule</code>.
204+ */
205+
206+ public void initialize(Subject subject,
207+ CallbackHandler callbackHandler,
208+ Map<String,?> sharedState,
209+ Map<String,?> options)
210+ {
211+ this.subject = subject;
212+ this.callbackHandler = callbackHandler;
213+ this.sharedState = sharedState;
214+ this.options = options;
215+
216+ processOptions();
217+ status = INITIALIZED;
218+ }
219+
220+ private void processOptions() {
221+ keyStoreURL = (String) options.get("keyStoreURL");
222+ if (keyStoreURL == null) {
223+ keyStoreURL =
224+ "file:" +
225+ System.getProperty("user.home").replace(
226+ File.separatorChar, '/') +
227+ '/' + ".keystore";
228+ } else if (NONE.equals(keyStoreURL)) {
229+ nullStream = true;
230+ }
231+ keyStoreType = (String) options.get("keyStoreType");
232+ if (keyStoreType == null) {
233+ keyStoreType = KeyStore.getDefaultType();
234+ }
235+ if (P11KEYSTORE.equalsIgnoreCase(keyStoreType)) {
236+ token = true;
237+ }
238+
239+ keyStoreProvider = (String) options.get("keyStoreProvider");
240+
241+ keyStoreAlias = (String) options.get("keyStoreAlias");
242+
243+ keyStorePasswordURL = (String) options.get("keyStorePasswordURL");
244+
245+ privateKeyPasswordURL = (String) options.get("privateKeyPasswordURL");
246+
247+ protectedPath = "true".equalsIgnoreCase((String)options.get
248+ ("protected"));
249+
250+ debug = "true".equalsIgnoreCase((String) options.get("debug"));
251+ if (debug) {
252+ debugPrint(null);
253+ debugPrint("keyStoreURL=" + keyStoreURL);
254+ debugPrint("keyStoreType=" + keyStoreType);
255+ debugPrint("keyStoreProvider=" + keyStoreProvider);
256+ debugPrint("keyStoreAlias=" + keyStoreAlias);
257+ debugPrint("keyStorePasswordURL=" + keyStorePasswordURL);
258+ debugPrint("privateKeyPasswordURL=" + privateKeyPasswordURL);
259+ debugPrint("protectedPath=" + protectedPath);
260+ debugPrint(null);
261+ }
262+ }
263+
264+ /**
265+ * Authenticate the user.
266+ *
267+ * <p> Get the Keystore alias and relevant passwords.
268+ * Retrieve the alias's principal and credentials from the Keystore.
269+ *
270+ * <p>
271+ *
272+ * @exception FailedLoginException if the authentication fails. <p>
273+ *
274+ * @return true in all cases (this <code>LoginModule</code>
275+ * should not be ignored).
276+ */
277+
278+ public boolean login() throws LoginException {
279+ switch (status) {
280+ case UNINITIALIZED:
281+ default:
282+ throw new LoginException("The login module is not initialized");
283+ case INITIALIZED:
284+ case AUTHENTICATED:
285+
286+ if (token && !nullStream) {
287+ throw new LoginException
288+ ("if keyStoreType is " + P11KEYSTORE +
289+ " then keyStoreURL must be " + NONE);
290+ }
291+
292+ if (token && privateKeyPasswordURL != null) {
293+ throw new LoginException
294+ ("if keyStoreType is " + P11KEYSTORE +
295+ " then privateKeyPasswordURL must not be specified");
296+ }
297+
298+ if (protectedPath &&
299+ (keyStorePasswordURL != null ||
300+ privateKeyPasswordURL != null)) {
301+ throw new LoginException
302+ ("if protected is true then keyStorePasswordURL and " +
303+ "privateKeyPasswordURL must not be specified");
304+ }
305+
306+ // get relevant alias and password info
307+
308+ if (protectedPath) {
309+ getAliasAndPasswords(PROTECTED_PATH);
310+ } else if (token) {
311+ getAliasAndPasswords(TOKEN);
312+ } else {
313+ getAliasAndPasswords(NORMAL);
314+ }
315+
316+ // log into KeyStore to retrieve data,
317+ // then clear passwords
318+
319+ try {
320+ getKeyStoreInfo();
321+ } finally {
322+ if (privateKeyPassword != null &&
323+ privateKeyPassword != keyStorePassword) {
324+ Arrays.fill(privateKeyPassword, '\0');
325+ privateKeyPassword = null;
326+ }
327+ if (keyStorePassword != null) {
328+ Arrays.fill(keyStorePassword, '\0');
329+ keyStorePassword = null;
330+ }
331+ }
332+ status = AUTHENTICATED;
333+ return true;
334+ case LOGGED_IN:
335+ return true;
336+ }
337+ }
338+
339+ /** Get the alias and passwords to use for looking up in the KeyStore. */
340+ private void getAliasAndPasswords(int env) throws LoginException {
341+ if (callbackHandler == null) {
342+
343+ // No callback handler - check for alias and password options
344+
345+ switch (env) {
346+ case PROTECTED_PATH:
347+ checkAlias();
348+ break;
349+ case TOKEN:
350+ checkAlias();
351+ checkStorePass();
352+ break;
353+ case NORMAL:
354+ checkAlias();
355+ checkStorePass();
356+ checkKeyPass();
357+ break;
358+ }
359+
360+ } else {
361+
362+ // Callback handler available - prompt for alias and passwords
363+
364+ NameCallback aliasCallback;
365+ if (keyStoreAlias == null || keyStoreAlias.length() == 0) {
366+ aliasCallback = new NameCallback(
367+ rb.getString("Keystore.alias."));
368+ } else {
369+ aliasCallback =
370+ new NameCallback(rb.getString("Keystore.alias."),
371+ keyStoreAlias);
372+ }
373+
374+ PasswordCallback storePassCallback = null;
375+ PasswordCallback keyPassCallback = null;
376+
377+ switch (env) {
378+ case PROTECTED_PATH:
379+ break;
380+ case NORMAL:
381+ keyPassCallback = new PasswordCallback
382+ (rb.getString("Private.key.password.optional."), false);
383+ // fall thru
384+ case TOKEN:
385+ storePassCallback = new PasswordCallback
386+ (rb.getString("Keystore.password."), false);
387+ break;
388+ }
389+ prompt(aliasCallback, storePassCallback, keyPassCallback);
390+ }
391+
392+ if (debug) {
393+ debugPrint("alias=" + keyStoreAlias);
394+ }
395+ }
396+
397+ private void checkAlias() throws LoginException {
398+ if (keyStoreAlias == null) {
399+ throw new LoginException
400+ ("Need to specify an alias option to use " +
401+ "KeyStoreLoginModule non-interactively.");
402+ }
403+ }
404+
405+ private void checkStorePass() throws LoginException {
406+ if (keyStorePasswordURL == null) {
407+ throw new LoginException
408+ ("Need to specify keyStorePasswordURL option to use " +
409+ "KeyStoreLoginModule non-interactively.");
410+ }
411+ InputStream in = null;
412+ try {
413+ in = new URL(keyStorePasswordURL).openStream();
414+ keyStorePassword = Password.readPassword(in);
415+ } catch (IOException e) {
416+ LoginException le = new LoginException
417+ ("Problem accessing keystore password \"" +
418+ keyStorePasswordURL + "\"");
419+ le.initCause(e);
420+ throw le;
421+ } finally {
422+ if (in != null) {
423+ try {
424+ in.close();
425+ } catch (IOException ioe) {
426+ LoginException le = new LoginException(
427+ "Problem closing the keystore password stream");
428+ le.initCause(ioe);
429+ throw le;
430+ }
431+ }
432+ }
433+ }
434+
435+ private void checkKeyPass() throws LoginException {
436+ if (privateKeyPasswordURL == null) {
437+ privateKeyPassword = keyStorePassword;
438+ } else {
439+ InputStream in = null;
440+ try {
441+ in = new URL(privateKeyPasswordURL).openStream();
442+ privateKeyPassword = Password.readPassword(in);
443+ } catch (IOException e) {
444+ LoginException le = new LoginException
445+ ("Problem accessing private key password \"" +
446+ privateKeyPasswordURL + "\"");
447+ le.initCause(e);
448+ throw le;
449+ } finally {
450+ if (in != null) {
451+ try {
452+ in.close();
453+ } catch (IOException ioe) {
454+ LoginException le = new LoginException(
455+ "Problem closing the private key password stream");
456+ le.initCause(ioe);
457+ throw le;
458+ }
459+ }
460+ }
461+ }
462+ }
463+
464+ private void prompt(NameCallback aliasCallback,
465+ PasswordCallback storePassCallback,
466+ PasswordCallback keyPassCallback)
467+ throws LoginException {
468+
469+ if (storePassCallback == null) {
470+
471+ // only prompt for alias
472+
473+ try {
474+ callbackHandler.handle(
475+ new Callback[] {
476+ bannerCallback, aliasCallback, confirmationCallback
477+ });
478+ } catch (IOException e) {
479+ LoginException le = new LoginException
480+ ("Problem retrieving keystore alias");
481+ le.initCause(e);
482+ throw le;
483+ } catch (UnsupportedCallbackException e) {
484+ throw new LoginException(
485+ "Error: " + e.getCallback().toString() +
486+ " is not available to retrieve authentication " +
487+ " information from the user");
488+ }
489+
490+ int confirmationResult = confirmationCallback.getSelectedIndex();
491+
492+ if (confirmationResult == ConfirmationCallback.CANCEL) {
493+ throw new LoginException("Login cancelled");
494+ }
495+
496+ saveAlias(aliasCallback);
497+
498+ } else if (keyPassCallback == null) {
499+
500+ // prompt for alias and key store password
501+
502+ try {
503+ callbackHandler.handle(
504+ new Callback[] {
505+ bannerCallback, aliasCallback,
506+ storePassCallback, confirmationCallback
507+ });
508+ } catch (IOException e) {
509+ LoginException le = new LoginException
510+ ("Problem retrieving keystore alias and password");
511+ le.initCause(e);
512+ throw le;
513+ } catch (UnsupportedCallbackException e) {
514+ throw new LoginException(
515+ "Error: " + e.getCallback().toString() +
516+ " is not available to retrieve authentication " +
517+ " information from the user");
518+ }
519+
520+ int confirmationResult = confirmationCallback.getSelectedIndex();
521+
522+ if (confirmationResult == ConfirmationCallback.CANCEL) {
523+ throw new LoginException("Login cancelled");
524+ }
525+
526+ saveAlias(aliasCallback);
527+ saveStorePass(storePassCallback);
528+
529+ } else {
530+
531+ // prompt for alias, key store password, and key password
532+
533+ try {
534+ callbackHandler.handle(
535+ new Callback[] {
536+ bannerCallback, aliasCallback,
537+ storePassCallback, keyPassCallback,
538+ confirmationCallback
539+ });
540+ } catch (IOException e) {
541+ LoginException le = new LoginException
542+ ("Problem retrieving keystore alias and passwords");
543+ le.initCause(e);
544+ throw le;
545+ } catch (UnsupportedCallbackException e) {
546+ throw new LoginException(
547+ "Error: " + e.getCallback().toString() +
548+ " is not available to retrieve authentication " +
549+ " information from the user");
550+ }
551+
552+ int confirmationResult = confirmationCallback.getSelectedIndex();
553+
554+ if (confirmationResult == ConfirmationCallback.CANCEL) {
555+ throw new LoginException("Login cancelled");
556+ }
557+
558+ saveAlias(aliasCallback);
559+ saveStorePass(storePassCallback);
560+ saveKeyPass(keyPassCallback);
561+ }
562+ }
563+
564+ private void saveAlias(NameCallback cb) {
565+ keyStoreAlias = cb.getName();
566+ }
567+
568+ private void saveStorePass(PasswordCallback c) {
569+ keyStorePassword = c.getPassword();
570+ if (keyStorePassword == null) {
571+ /* Treat a NULL password as an empty password */
572+ keyStorePassword = new char[0];
573+ }
574+ c.clearPassword();
575+ }
576+
577+ private void saveKeyPass(PasswordCallback c) {
578+ privateKeyPassword = c.getPassword();
579+ if (privateKeyPassword == null || privateKeyPassword.length == 0) {
580+ /*
581+ * Use keystore password if no private key password is
582+ * specified.
583+ */
584+ privateKeyPassword = keyStorePassword;
585+ }
586+ c.clearPassword();
587+ }
588+
589+ /** Get the credentials from the KeyStore. */
590+ private void getKeyStoreInfo() throws LoginException {
591+
592+ /* Get KeyStore instance */
593+ try {
594+ if (keyStoreProvider == null) {
595+ keyStore = KeyStore.getInstance(keyStoreType);
596+ } else {
597+ keyStore =
598+ KeyStore.getInstance(keyStoreType, keyStoreProvider);
599+ }
600+ } catch (KeyStoreException e) {
601+ LoginException le = new LoginException
602+ ("The specified keystore type was not available");
603+ le.initCause(e);
604+ throw le;
605+ } catch (NoSuchProviderException e) {
606+ LoginException le = new LoginException
607+ ("The specified keystore provider was not available");
608+ le.initCause(e);
609+ throw le;
610+ }
611+
612+ /* Load KeyStore contents from file */
613+ InputStream in = null;
614+ try {
615+ if (nullStream) {
616+ // if using protected auth path, keyStorePassword will be null
617+ keyStore.load(null, keyStorePassword);
618+ } else {
619+ in = new URL(keyStoreURL).openStream();
620+ keyStore.load(in, keyStorePassword);
621+ }
622+ } catch (MalformedURLException e) {
623+ LoginException le = new LoginException
624+ ("Incorrect keyStoreURL option");
625+ le.initCause(e);
626+ throw le;
627+ } catch (GeneralSecurityException e) {
628+ LoginException le = new LoginException
629+ ("Error initializing keystore");
630+ le.initCause(e);
631+ throw le;
632+ } catch (IOException e) {
633+ LoginException le = new LoginException
634+ ("Error initializing keystore");
635+ le.initCause(e);
636+ throw le;
637+ } finally {
638+ if (in != null) {
639+ try {
640+ in.close();
641+ } catch (IOException ioe) {
642+ LoginException le = new LoginException
643+ ("Error initializing keystore");
644+ le.initCause(ioe);
645+ throw le;
646+ }
647+ }
648+ }
649+
650+ /* Get certificate chain and create a certificate path */
651+ try {
652+ fromKeyStore =
653+ keyStore.getCertificateChain(keyStoreAlias);
654+ if (fromKeyStore == null
655+ || fromKeyStore.length == 0
656+ || !(fromKeyStore[0] instanceof X509Certificate))
657+ {
658+ throw new FailedLoginException(
659+ "Unable to find X.509 certificate chain in keystore");
660+ } else {
661+ LinkedList<Certificate> certList = new LinkedList<>();
662+ for (int i=0; i < fromKeyStore.length; i++) {
663+ certList.add(fromKeyStore[i]);
664+ }
665+ CertificateFactory certF=
666+ CertificateFactory.getInstance("X.509");
667+ certP =
668+ certF.generateCertPath(certList);
669+ }
670+ } catch (KeyStoreException e) {
671+ LoginException le = new LoginException("Error using keystore");
672+ le.initCause(e);
673+ throw le;
674+ } catch (CertificateException ce) {
675+ LoginException le = new LoginException
676+ ("Error: X.509 Certificate type unavailable");
677+ le.initCause(ce);
678+ throw le;
679+ }
680+
681+ /* Get principal and keys */
682+ try {
683+ X509Certificate certificate = (X509Certificate)fromKeyStore[0];
684+ principal = new javax.security.auth.x500.X500Principal
685+ (certificate.getSubjectDN().getName());
686+
687+ // if token, privateKeyPassword will be null
688+ Key privateKey = keyStore.getKey(keyStoreAlias, privateKeyPassword);
689+ if (privateKey == null
690+ || !(privateKey instanceof PrivateKey))
691+ {
692+ throw new FailedLoginException(
693+ "Unable to recover key from keystore");
694+ }
695+
696+ privateCredential = new X500PrivateCredential(
697+ certificate, (PrivateKey) privateKey, keyStoreAlias);
698+ } catch (KeyStoreException e) {
699+ LoginException le = new LoginException("Error using keystore");
700+ le.initCause(e);
701+ throw le;
702+ } catch (NoSuchAlgorithmException e) {
703+ LoginException le = new LoginException("Error using keystore");
704+ le.initCause(e);
705+ throw le;
706+ } catch (UnrecoverableKeyException e) {
707+ FailedLoginException fle = new FailedLoginException
708+ ("Unable to recover key from keystore");
709+ fle.initCause(e);
710+ throw fle;
711+ }
712+ if (debug) {
713+ debugPrint("principal=" + principal +
714+ "\n certificate="
715+ + privateCredential.getCertificate() +
716+ "\n alias =" + privateCredential.getAlias());
717+ }
718+ }
719+
720+ /**
721+ * Abstract method to commit the authentication process (phase 2).
722+ *
723+ * <p> This method is called if the LoginContext's
724+ * overall authentication succeeded
725+ * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
726+ * succeeded).
727+ *
728+ * <p> If this LoginModule's own authentication attempt
729+ * succeeded (checked by retrieving the private state saved by the
730+ * <code>login</code> method), then this method associates a
731+ * <code>X500Principal</code> for the subject distinguished name of the
732+ * first certificate in the alias's credentials in the subject's
733+ * principals,the alias's certificate path in the subject's public
734+ * credentials, and a<code>X500PrivateCredential</code> whose certificate
735+ * is the first certificate in the alias's certificate path and whose
736+ * private key is the alias's private key in the subject's private
737+ * credentials. If this LoginModule's own
738+ * authentication attempted failed, then this method removes
739+ * any state that was originally saved.
740+ *
741+ * <p>
742+ *
743+ * @exception LoginException if the commit fails
744+ *
745+ * @return true if this LoginModule's own login and commit
746+ * attempts succeeded, or false otherwise.
747+ */
748+
749+ public boolean commit() throws LoginException {
750+ switch (status) {
751+ case UNINITIALIZED:
752+ default:
753+ throw new LoginException("The login module is not initialized");
754+ case INITIALIZED:
755+ logoutInternal();
756+ throw new LoginException("Authentication failed");
757+ case AUTHENTICATED:
758+ if (commitInternal()) {
759+ return true;
760+ } else {
761+ logoutInternal();
762+ throw new LoginException("Unable to retrieve certificates");
763+ }
764+ case LOGGED_IN:
765+ return true;
766+ }
767+ }
768+
769+ private boolean commitInternal() throws LoginException {
770+ /* If the subject is not readonly add to the principal and credentials
771+ * set; otherwise just return true
772+ */
773+ if (subject.isReadOnly()) {
774+ throw new LoginException ("Subject is set readonly");
775+ } else {
776+ subject.getPrincipals().add(principal);
777+ subject.getPublicCredentials().add(certP);
778+ subject.getPrivateCredentials().add(privateCredential);
779+ status = LOGGED_IN;
780+ return true;
781+ }
782+ }
783+
784+ /**
785+ * <p> This method is called if the LoginContext's
786+ * overall authentication failed.
787+ * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
788+ * did not succeed).
789+ *
790+ * <p> If this LoginModule's own authentication attempt
791+ * succeeded (checked by retrieving the private state saved by the
792+ * <code>login</code> and <code>commit</code> methods),
793+ * then this method cleans up any state that was originally saved.
794+ *
795+ * <p> If the loaded KeyStore's provider extends
796+ * <code>java.security.AuthProvider</code>,
797+ * then the provider's <code>logout</code> method is invoked.
798+ *
799+ * <p>
800+ *
801+ * @exception LoginException if the abort fails.
802+ *
803+ * @return false if this LoginModule's own login and/or commit attempts
804+ * failed, and true otherwise.
805+ */
806+
807+ public boolean abort() throws LoginException {
808+ switch (status) {
809+ case UNINITIALIZED:
810+ default:
811+ return false;
812+ case INITIALIZED:
813+ return false;
814+ case AUTHENTICATED:
815+ logoutInternal();
816+ return true;
817+ case LOGGED_IN:
818+ logoutInternal();
819+ return true;
820+ }
821+ }
822+ /**
823+ * Logout a user.
824+ *
825+ * <p> This method removes the Principals, public credentials and the
826+ * private credentials that were added by the <code>commit</code> method.
827+ *
828+ * <p> If the loaded KeyStore's provider extends
829+ * <code>java.security.AuthProvider</code>,
830+ * then the provider's <code>logout</code> method is invoked.
831+ *
832+ * <p>
833+ *
834+ * @exception LoginException if the logout fails.
835+ *
836+ * @return true in all cases since this <code>LoginModule</code>
837+ * should not be ignored.
838+ */
839+
840+ public boolean logout() throws LoginException {
841+ if (debug)
842+ debugPrint("Entering logout " + status);
843+ switch (status) {
844+ case UNINITIALIZED:
845+ throw new LoginException
846+ ("The login module is not initialized");
847+ case INITIALIZED:
848+ case AUTHENTICATED:
849+ default:
850+ // impossible for LoginModule to be in AUTHENTICATED
851+ // state
852+ // assert status != AUTHENTICATED;
853+ return false;
854+ case LOGGED_IN:
855+ logoutInternal();
856+ return true;
857+ }
858+ }
859+
860+ private void logoutInternal() throws LoginException {
861+ if (debug) {
862+ debugPrint("Entering logoutInternal");
863+ }
864+
865+ // assumption is that KeyStore.load did a login -
866+ // perform explicit logout if possible
867+ LoginException logoutException = null;
868+ Provider provider = keyStore.getProvider();
869+ if (provider instanceof AuthProvider) {
870+ AuthProvider ap = (AuthProvider)provider;
871+ try {
872+ ap.logout();
873+ if (debug) {
874+ debugPrint("logged out of KeyStore AuthProvider");
875+ }
876+ } catch (LoginException le) {
877+ // save but continue below
878+ logoutException = le;
879+ }
880+ }
881+
882+ if (subject.isReadOnly()) {
883+ // attempt to destroy the private credential
884+ // even if the Subject is read-only
885+ principal = null;
886+ certP = null;
887+ status = INITIALIZED;
888+ // destroy the private credential
889+ Iterator<Object> it = subject.getPrivateCredentials().iterator();
890+ while (it.hasNext()) {
891+ Object obj = it.next();
892+ if (privateCredential.equals(obj)) {
893+ privateCredential = null;
894+ try {
895+ ((Destroyable)obj).destroy();
896+ if (debug)
897+ debugPrint("Destroyed private credential, " +
898+ obj.getClass().getName());
899+ break;
900+ } catch (DestroyFailedException dfe) {
901+ LoginException le = new LoginException
902+ ("Unable to destroy private credential, "
903+ + obj.getClass().getName());
904+ le.initCause(dfe);
905+ throw le;
906+ }
907+ }
908+ }
909+
910+ // throw an exception because we can not remove
911+ // the principal and public credential from this
912+ // read-only Subject
913+ throw new LoginException
914+ ("Unable to remove Principal ("
915+ + "X500Principal "
916+ + ") and public credential (certificatepath) "
917+ + "from read-only Subject");
918+ }
919+ if (principal != null) {
920+ subject.getPrincipals().remove(principal);
921+ principal = null;
922+ }
923+ if (certP != null) {
924+ subject.getPublicCredentials().remove(certP);
925+ certP = null;
926+ }
927+ if (privateCredential != null) {
928+ subject.getPrivateCredentials().remove(privateCredential);
929+ privateCredential = null;
930+ }
931+
932+ // throw pending logout exception if there is one
933+ if (logoutException != null) {
934+ throw logoutException;
935+ }
936+ status = INITIALIZED;
937+ }
938+
939+ private void debugPrint(String message) {
940+ // we should switch to logging API
941+ if (message == null) {
942+ System.err.println();
943+ } else {
944+ System.err.println("Debug KeyStoreLoginModule: " + message);
945+ }
946+ }
947+}