KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > security > auth > login > ClientCertificateLoginModule


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23 package com.sun.enterprise.security.auth.login;
24
25 import java.util.Map JavaDoc;
26 import java.util.Enumeration JavaDoc;
27 import java.security.KeyStore JavaDoc;
28 import java.security.cert.X509Certificate JavaDoc;
29 import javax.net.ssl.*;
30 import javax.security.auth.*;
31 import javax.security.auth.callback.*;
32 import javax.security.auth.login.LoginException JavaDoc;
33 import javax.security.auth.spi.LoginModule JavaDoc;
34 import com.sun.enterprise.config.clientbeans.Ssl;
35 import com.sun.enterprise.deployment.PrincipalImpl;
36 import com.sun.enterprise.security.SSLUtils;
37 import com.sun.enterprise.util.LocalStringManagerImpl;
38 import com.sun.enterprise.security.auth.LoginContextDriver;
39 import java.util.logging.*;
40 import com.sun.logging.*;
41
42
43 /**
44  * <p> This LoginModule authenticates users with X509 certificates.
45  *
46  * <p> If testUser successfully authenticates itself,
47  * a <code>PrincipalImpl</code> with the testUser's username
48  * is added to the Subject.
49  *
50  * <p> This LoginModule recognizes the debug option.
51  * If set to true in the login Configuration,
52  * debug messages will be output to the output stream, System.out.
53  *
54  * @author Harpreet Singh (harpreet.singh@sun.com)
55  */

56
57 public class ClientCertificateLoginModule implements LoginModule JavaDoc {
58
59     private static Logger _logger=null;
60     static {
61         _logger=LogDomains.getLogger(LogDomains.SECURITY_LOGGER);
62     }
63
64     private static LocalStringManagerImpl localStrings =
65     new LocalStringManagerImpl(ClientCertificateLoginModule.class);
66
67     private static KeyStore JavaDoc ks = null;
68
69     // initial state
70
private Subject subject;
71     private CallbackHandler callbackHandler;
72     private Map JavaDoc sharedState;
73     private Map JavaDoc options;
74
75     // configurable option
76
private boolean debug = false; // default
77

78     // the authentication status
79
private boolean succeeded = false;
80     private boolean commitSucceeded = false;
81
82     private String JavaDoc alias;
83     private X509Certificate JavaDoc certificate;
84
85     // testUser's PrincipalImpl
86
private PrincipalImpl userPrincipal;
87
88     /**
89      * Initialize this <code>LoginModule</code>.
90      *
91      * <p>
92      *
93      * @param subject the <code>Subject</code> to be authenticated. <p>
94      *
95      * @param callbackHandler a <code>CallbackHandler</code> for communicating
96      * with the end user (prompting for usernames and
97      * passwords, for example). <p>
98      *
99      * @param sharedState shared <code>LoginModule</code> state. <p>
100      *
101      * @param options options specified in the login
102      * <code>Configuration</code> for this particular
103      * <code>LoginModule</code>.
104      */

105     public void initialize(Subject subject, CallbackHandler callbackHandler,
106             Map JavaDoc sharedState, Map JavaDoc options) {
107  
108
109     this.subject = subject;
110     this.callbackHandler = callbackHandler;
111     this.sharedState = sharedState;
112     this.options = options;
113
114     // initialize any configured options
115
debug = "true".equalsIgnoreCase((String JavaDoc)options.get("debug"));
116
117     init();
118     }
119
120     /**
121      * Initialize the key store.
122      */

123     private void init() {
124     try {
125         if(ks == null) {
126             SSLUtils.initStoresAtStartup();
127         }
128
129     } catch(Exception JavaDoc e) {
130         _logger.log(Level.SEVERE,"java_security.initkeystore_exception",e);
131     }
132     }
133
134
135     /**
136      * Authenticate the user by prompting for a username and password.
137      *
138      * <p>
139      *
140      * @return true in all cases since this <code>LoginModule</code>
141      * should not be ignored.
142      *
143      * @exception FailedLoginException if the authentication fails. <p>
144      *
145      * @exception LoginException if this <code>LoginModule</code>
146      * is unable to perform the authentication.
147      */

148     public boolean login() throws LoginException JavaDoc {
149
150     // prompt for a username and password
151
if (callbackHandler == null){
152         throw new LoginException JavaDoc("Error: no CallbackHandler available " +
153             "to garner authentication information from the user");
154         }
155
156     try {
157         String JavaDoc[] as = new String JavaDoc[ks.size()];
158         String JavaDoc[] aliasString = new String JavaDoc[ks.size()];
159         Enumeration JavaDoc aliases = ks.aliases();
160         for(int i = 0; i < ks.size(); i++) {
161             aliasString[i] = (String JavaDoc) aliases.nextElement();
162             as[i] = ((X509Certificate JavaDoc)ks.getCertificate(aliasString[i])).getSubjectDN().getName();
163         }
164
165         Callback[] callbacks = new Callback[1];
166         callbacks[0] = new ChoiceCallback(localStrings.getLocalString("login.certificate", "Choose from list of certificates: "), as, 0, false);
167  
168         callbackHandler.handle(callbacks);
169         String JavaDoc[] choices = ((ChoiceCallback)callbacks[0]).getChoices();
170         int[] idx = ((ChoiceCallback)callbacks[0]).getSelectedIndexes();
171         if (choices == null) {
172         // figure out
173
}
174         if (idx == null) {
175         throw new LoginException JavaDoc ("No certificate selected!");
176         } else if (idx[0] == -1){
177         throw new LoginException JavaDoc ("Incorrect keystore password");
178         }
179         // print debugging information
180
if (debug) {
181                 if(_logger.isLoggable(Level.FINE)){
182                     _logger.log(Level.FINE,"\t\t[ClientCertificateLoginModule] " +
183                                 "user entered certificate: ");
184                     for (int i = 0; i < idx.length; i++){
185                         _logger.log(Level.FINE,aliasString[idx[i]]);
186                     }
187                 }
188         }
189             
190         // the authenticate method previously picked out the
191
// wrong alias.
192
// since we allow only 1 choice the first element in idx
193
// idx[0] should have the selected index.
194
this.alias = aliasString[idx[0]];
195         certificate = (X509Certificate JavaDoc) ks.getCertificate(alias);
196         // the authenticate should always return a true.
197
if (debug){
198                 if(_logger.isLoggable(Level.FINE)){
199                     _logger.log(Level.FINE,"\t\t[ClientCertificateLoginModule] " +
200                             "authentication succeeded");
201                 }
202         }
203         succeeded = true;
204         return true;
205     } catch (java.io.IOException JavaDoc ioe) {
206         throw new LoginException JavaDoc(ioe.toString());
207     } catch (UnsupportedCallbackException uce) {
208         throw new LoginException JavaDoc("Error: " + uce.getCallback().toString() +
209         " not available to garner authentication information " +
210         "from the user");
211     } catch (Exception JavaDoc e) {
212         throw new LoginException JavaDoc(e.toString());
213     }
214     }
215     /**
216      * <p> This method is called if the LoginContext's
217      * overall authentication succeeded
218      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
219      * succeeded).
220      *
221      * <p> If this LoginModule's own authentication attempt
222      * succeeded (checked by retrieving the private state saved by the
223      * <code>login</code> method), then this method associates a
224      * <code>PrincipalImpl</code>
225      * with the <code>Subject</code> located in the
226      * <code>LoginModule</code>. If this LoginModule's own
227      * authentication attempted failed, then this method removes
228      * any state that was originally saved.
229      *
230      * <p>
231      *
232      * @exception LoginException if the commit fails.
233      *
234      * @return true if this LoginModule's own login and commit
235      * attempts succeeded, or false otherwise.
236      */

237     public boolean commit() throws LoginException JavaDoc {
238     if (succeeded == false) {
239         return false;
240     } else {
241         // add a Principal (authenticated identity)
242
// to the Subject
243

244         // assume the user we authenticated is the PrincipalImpl
245
userPrincipal = new PrincipalImpl(alias);
246         if (!subject.getPrincipals().contains(userPrincipal)){
247         subject.getPrincipals().add(userPrincipal);
248             }
249
250         if (debug) {
251                 if(_logger.isLoggable(Level.FINE)){
252                     _logger.log(Level.FINE,"\t\t[ClientCertificateLoginModule] " +
253                                 "added PrincipalImpl to Subject");
254                 }
255         }
256             Ssl ssl = new Ssl();
257             ssl.setCertNickname(this.alias);
258             SSLUtils.setAppclientSsl(ssl);
259         String JavaDoc realm = LoginContextDriver.CERT_REALMNAME;
260         X509Certificate JavaDoc[] certChain = new X509Certificate JavaDoc[1];
261         certChain[0] = certificate;
262         X509CertificateCredential pc =
263         new X509CertificateCredential(certChain, alias, realm);
264         if(!subject.getPrivateCredentials().contains(pc)) {
265         subject.getPrivateCredentials().add(pc);
266             }
267
268         commitSucceeded = true;
269         return true;
270     }
271     }
272
273     /**
274      * <p> This method is called if the LoginContext's
275      * overall authentication failed.
276      * (the relevant REQUIRED, REQUISITE, SUFFICIENT and OPTIONAL LoginModules
277      * did not succeed).
278      *
279      * <p> If this LoginModule's own authentication attempt
280      * succeeded (checked by retrieving the private state saved by the
281      * <code>login</code> and <code>commit</code> methods),
282      * then this method cleans up any state that was originally saved.
283      *
284      * <p>
285      *
286      * @exception LoginException if the abort fails.
287      *
288      * @return false if this LoginModule's own login and/or commit attempts
289      * failed, and true otherwise.
290      */

291     public boolean abort() throws LoginException JavaDoc {
292     if (succeeded == false) {
293         return false;
294     } else if (succeeded == true && commitSucceeded == false) {
295         // login succeeded but overall authentication failed
296
succeeded = false;
297         alias = null;
298         userPrincipal = null;
299     } else {
300         // overall authentication succeeded and commit succeeded,
301
// but someone else's commit failed
302
logout();
303     }
304     return true;
305     }
306
307     /**
308      * Logout the user.
309      *
310      * <p> This method removes the <code>PrincipalImpl</code>
311      * that was added by the <code>commit</code> method.
312      *
313      * <p>
314      *
315      * @exception LoginException if the logout fails.
316      *
317      * @return true in all cases since this <code>LoginModule</code>
318      * should not be ignored.
319      */

320     public boolean logout() throws LoginException JavaDoc {
321     // unset the alias
322
SSLUtils.setAppclientSsl(null);
323     
324     subject.getPrincipals().remove(userPrincipal);
325     succeeded = false;
326     succeeded = commitSucceeded;
327     alias = null;
328     userPrincipal = null;
329     return true;
330     }
331
332     public static void setKeyStore(KeyStore JavaDoc keyStore) {
333     ks = keyStore;
334     }
335 }
336
Popular Tags