KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > jboss > security > auth > spi > BaseCertLoginModule


1 /*
2  * JBoss, Home of Professional Open Source
3  *
4  * Distributable under LGPL license.
5  * See terms of license at gnu.org.
6  */

7 package org.jboss.security.auth.spi;
8
9 import java.security.KeyStore JavaDoc;
10 import java.security.KeyStoreException JavaDoc;
11 import java.security.Principal JavaDoc;
12 import java.security.acl.Group JavaDoc;
13 import java.security.cert.X509Certificate JavaDoc;
14 import java.util.Map JavaDoc;
15 import java.util.Enumeration JavaDoc;
16 import java.util.ArrayList JavaDoc;
17 import java.io.IOException JavaDoc;
18
19 import javax.naming.InitialContext JavaDoc;
20 import javax.naming.NamingException JavaDoc;
21 import javax.security.auth.Subject JavaDoc;
22 import javax.security.auth.callback.Callback JavaDoc;
23 import javax.security.auth.callback.CallbackHandler JavaDoc;
24 import javax.security.auth.callback.NameCallback JavaDoc;
25 import javax.security.auth.callback.UnsupportedCallbackException JavaDoc;
26 import javax.security.auth.login.FailedLoginException JavaDoc;
27 import javax.security.auth.login.LoginException JavaDoc;
28
29 import org.jboss.security.SecurityDomain;
30 import org.jboss.security.auth.callback.ObjectCallback;
31 import org.jboss.security.auth.certs.X509CertificateVerifier;
32
33 /**
34  * Base Login Module that uses X509Certificates as credentials for
35  * authentication.
36  *
37  * This login module uses X509Certificates as a
38  * credential. It takes the cert as an object and checks to see if the alias in
39  * the truststore/keystore contains the same certificate. Subclasses of this
40  * module should implement the getRoleSets() method defined by
41  * AbstractServerLoginModule. Much of this module was patterned after the
42  * UserNamePasswordLoginModule.
43  *
44  * @author <a HREF="mailto:jasone@greenrivercomputing.com">Jason Essington</a>
45  * @author Scott.Stark@jboss.org
46  * @version $Revision: 1.4.2.2 $
47  */

48 public class BaseCertLoginModule extends AbstractServerLoginModule
49 {
50    /** A principal derived from the certificate alias */
51    private Principal JavaDoc identity;
52    /** The client certificate */
53    private X509Certificate JavaDoc credential;
54    /** The SecurityDomain to obtain the KeyStore/TrustStore from */
55    private SecurityDomain domain = null;
56    /** An option certificate verifier */
57    private X509CertificateVerifier verifier;
58    /** The trace level log flag */
59    private boolean trace;
60
61    /** Override the super version to pickup the following options after first
62     * calling the super method.
63     *
64     * option: securityDomain - the name of the SecurityDomain to obtain the
65     * trust and keystore from.
66     * option: verifier - the class name of the X509CertificateVerifier to use
67     * for verification of the login certificate
68     *
69     * @see SecurityDomain
70     * @see X509CertificateVerifier
71     *
72     * @param subject the Subject to update after a successful login.
73     * @param callbackHandler the CallbackHandler that will be used to obtain the
74     * the user identity and credentials.
75     * @param sharedState a Map shared between all configured login module instances
76     * @param options the parameters passed to the login module.
77     */

78    public void initialize(Subject JavaDoc subject, CallbackHandler JavaDoc callbackHandler,
79       Map JavaDoc sharedState, Map JavaDoc options)
80    {
81       super.initialize(subject, callbackHandler, sharedState, options);
82       trace = log.isTraceEnabled();
83
84       // Get the security domain and default to "other"
85
String JavaDoc sd = (String JavaDoc) options.get("securityDomain");
86       if (sd == null)
87          sd = "java:/jaas/other";
88
89       if( trace )
90          log.trace("securityDomain=" + sd);
91
92       try
93       {
94          Object JavaDoc tempDomain = new InitialContext JavaDoc().lookup(sd);
95          if (tempDomain instanceof SecurityDomain)
96          {
97             domain = (SecurityDomain) tempDomain;
98             if( trace )
99             {
100                if (domain != null)
101                   log.trace("found domain: " + domain.getClass().getName());
102                else
103                   log.trace("the domain " + sd + " is null!");
104             }
105          }
106          else
107          {
108             log.error("The domain " + sd + " is not a SecurityDomain. All authentication using this module will fail!");
109          }
110       }
111       catch (NamingException JavaDoc e)
112       {
113          log.error("Unable to find the securityDomain named: " + sd, e);
114       }
115
116       String JavaDoc option = (String JavaDoc) options.get("verifier");
117       if( option != null )
118       {
119          try
120          {
121             ClassLoader JavaDoc loader = Thread.currentThread().getContextClassLoader();
122             Class JavaDoc verifierClass = loader.loadClass(option);
123             verifier = (X509CertificateVerifier) verifierClass.newInstance();
124          }
125          catch(Throwable JavaDoc e)
126          {
127             if( trace )
128                log.trace("Failed to create X509CertificateVerifier", e);
129             IllegalArgumentException JavaDoc ex = new IllegalArgumentException JavaDoc("Invalid verifier: "+option);
130             ex.initCause(e);
131          }
132       }
133
134       if( trace )
135          log.trace("exit: initialize(Subject, CallbackHandler, Map, Map)");
136    }
137
138    /**
139     * Perform the authentication of the username and password.
140     */

141    public boolean login() throws LoginException JavaDoc
142    {
143       if( trace )
144          log.trace("enter: login()");
145       // See if shared credentials exist
146
if (super.login() == true)
147       {
148          // Setup our view of the user
149
Object JavaDoc username = sharedState.get("javax.security.auth.login.name");
150          if( username instanceof Principal JavaDoc )
151             identity = (Principal JavaDoc) username;
152          else
153          {
154             String JavaDoc name = username.toString();
155             try
156             {
157                identity = createIdentity(name);
158             }
159             catch(Exception JavaDoc e)
160             {
161                log.debug("Failed to create principal", e);
162                throw new LoginException JavaDoc("Failed to create principal: "+ e.getMessage());
163             }
164          }
165
166          Object JavaDoc password = sharedState.get("javax.security.auth.login.password");
167          if (password instanceof X509Certificate JavaDoc)
168             credential = (X509Certificate JavaDoc) password;
169          else if (password != null)
170          {
171             log.debug("javax.security.auth.login.password is not X509Certificate");
172             super.loginOk = false;
173             return false;
174          }
175          return true;
176       }
177
178       super.loginOk = false;
179       Object JavaDoc[] info = getAliasAndCert();
180       String JavaDoc alias = (String JavaDoc) info[0];
181       credential = (X509Certificate JavaDoc) info[1];
182
183       if (alias == null && credential == null)
184       {
185          identity = unauthenticatedIdentity;
186          super.log.trace("Authenticating as unauthenticatedIdentity=" + identity);
187       }
188
189       if (identity == null)
190       {
191          try
192          {
193             identity = createIdentity(alias);
194          }
195          catch(Exception JavaDoc e)
196          {
197             log.debug("Failed to create identity for alias:"+alias, e);
198          }
199
200          if (!validateCredential(alias, credential))
201          {
202             log.debug("Bad credential for alias=" + alias);
203             throw new FailedLoginException JavaDoc("Supplied Credential did not match existing credential for " + alias);
204          }
205       }
206
207       if (getUseFirstPass() == true)
208       {
209          // Add authentication info to shared state map
210
sharedState.put("javax.security.auth.login.name", alias);
211          sharedState.put("javax.security.auth.login.password", credential);
212       }
213       super.loginOk = true;
214       if( trace )
215       {
216          log.trace("User '" + identity + "' authenticated, loginOk=" + loginOk);
217          log.debug("exit: login()");
218       }
219       return true;
220    }
221
222    /** Override to add the X509Certificate to the public credentials
223     * @return
224     * @throws LoginException
225     */

226    public boolean commit() throws LoginException JavaDoc
227    {
228       boolean ok = super.commit();
229       if( ok == true )
230       {
231          // Add the cert to the public credentials
232
subject.getPublicCredentials().add(credential);
233       }
234       return ok;
235    }
236
237    /** Subclasses need to override this to provide the roles for authorization
238     * @return
239     * @throws LoginException
240     */

241    protected Group JavaDoc[] getRoleSets() throws LoginException JavaDoc
242    {
243       return new Group JavaDoc[0];
244    }
245
246    protected Principal JavaDoc getIdentity()
247    {
248       return identity;
249    }
250    protected Object JavaDoc getCredentials()
251    {
252       return credential;
253    }
254    protected String JavaDoc getUsername()
255    {
256       String JavaDoc username = null;
257       if (getIdentity() != null)
258          username = getIdentity().getName();
259       return username;
260    }
261
262    protected Object JavaDoc[] getAliasAndCert() throws LoginException JavaDoc
263    {
264       if( trace )
265          log.trace("enter: getAliasAndCert()");
266       Object JavaDoc[] info = { null, null };
267       // prompt for a username and password
268
if (callbackHandler == null)
269       {
270          throw new LoginException JavaDoc("Error: no CallbackHandler available to collect authentication information");
271       }
272       NameCallback JavaDoc nc = new NameCallback JavaDoc("Alias: ");
273       ObjectCallback oc = new ObjectCallback("Certificate: ");
274       Callback JavaDoc[] callbacks = { nc, oc };
275       String JavaDoc alias = null;
276       X509Certificate JavaDoc cert = null;
277       X509Certificate JavaDoc[] certChain;
278       try
279       {
280          callbackHandler.handle(callbacks);
281          alias = nc.getName();
282          Object JavaDoc tmpCert = oc.getCredential();
283          if (tmpCert != null)
284          {
285             if (tmpCert instanceof X509Certificate JavaDoc)
286             {
287                cert = (X509Certificate JavaDoc) tmpCert;
288                if( trace )
289                   log.trace("found cert " + cert.getSerialNumber().toString(16) + ":" + cert.getSubjectDN().getName());
290             }
291             else if( tmpCert instanceof X509Certificate JavaDoc[] )
292             {
293                certChain = (X509Certificate JavaDoc[]) tmpCert;
294                if( certChain.length > 0 )
295                   cert = certChain[0];
296             }
297             else
298             {
299                String JavaDoc msg = "Don't know how to obtain X509Certificate from: "
300                   +tmpCert.getClass();
301                log.warn(msg);
302                throw new LoginException JavaDoc(msg);
303             }
304          }
305       }
306       catch (IOException JavaDoc e)
307       {
308          log.debug("Failed to invoke callback", e);
309          throw new LoginException JavaDoc("Failed to invoke callback: "+e.toString());
310       }
311       catch (UnsupportedCallbackException JavaDoc uce)
312       {
313          throw new LoginException JavaDoc("CallbackHandler does not support: "
314             + uce.getCallback());
315       }
316
317       info[0] = alias;
318       info[1] = cert;
319       if( trace )
320          log.trace("exit: getAliasAndCert()");
321       return info;
322    }
323
324    protected boolean validateCredential(String JavaDoc alias, X509Certificate JavaDoc cert)
325    {
326       if( trace )
327          log.trace("enter: validateCredentail(String, X509Certificate)");
328       boolean isValid = false;
329
330       // if we don't have a trust store, we'll just use the key store.
331
KeyStore JavaDoc keyStore = null;
332       KeyStore JavaDoc trustStore = null;
333       if( domain != null )
334       {
335          keyStore = domain.getKeyStore();
336          trustStore = domain.getTrustStore();
337       }
338       if( trustStore == null )
339          trustStore = keyStore;
340
341       if( verifier != null )
342       {
343          // Have the verifier validate the cert
344
if( trace )
345             log.trace("Validating cert using: "+verifier);
346          isValid = verifier.verify(cert, alias, keyStore, trustStore);
347       }
348       else if (keyStore != null && cert != null)
349       {
350          // Look for the cert in the keystore using the alias
351
X509Certificate JavaDoc storeCert = null;
352          try
353          {
354             storeCert = (X509Certificate JavaDoc) keyStore.getCertificate(alias);
355             if( trace )
356             {
357                StringBuffer JavaDoc buf = new StringBuffer JavaDoc("\n\tSupplied Credential: ");
358                buf.append(cert.getSerialNumber().toString(16));
359                buf.append("\n\t\t");
360                buf.append(cert.getSubjectDN().getName());
361                buf.append("\n\n\tExisting Credential: ");
362                if( storeCert != null )
363                {
364                   buf.append(storeCert.getSerialNumber().toString(16));
365                   buf.append("\n\t\t");
366                   buf.append(storeCert.getSubjectDN().getName());
367                   buf.append("\n");
368                }
369                else
370                {
371                   ArrayList JavaDoc aliases = new ArrayList JavaDoc();
372                   Enumeration JavaDoc en = keyStore.aliases();
373                   while (en.hasMoreElements())
374                   {
375                      aliases.add(en.nextElement());
376                   }
377                   buf.append("No match for alias: "+alias+", we have aliases " + aliases);
378                }
379                log.trace(buf.toString());
380             }
381          }
382          catch (KeyStoreException JavaDoc e)
383          {
384             log.warn("failed to find the certificate for " + alias, e);
385          }
386          // Ensure that the two certs are equal
387
if (cert.equals(storeCert))
388             isValid = true;
389       }
390       else
391       {
392          log.warn("Domain, KeyStore, or cert is null. Unable to validate the certificate.");
393       }
394
395       if( trace )
396       {
397          log.trace("The supplied certificate "
398                + (isValid ? "matched" : "DID NOT match")
399                + " the certificate in the keystore.");
400
401          log.trace("exit: validateCredentail(String, X509Certificate)");
402       }
403       return isValid;
404    }
405
406 }
407
Popular Tags