Software for Developing Secure P2P Applications


Developers who want to add security to peer-to-peer applications can choose from several toolkits that bundle many of the technologies outlined in this chapter:

The Intel Peer-to-Peer Trusted Library is an open source cross-platform solution for C++. It includes support for peer identification and authentication, secure storage, and networking. The library allows developers to work with digital certificates, digital signatures, digital envelopes, public key encryption, and symmetric key encryption. Additionally, it has useful classes to support multithreading and synchronization across platforms. More information can be found at its home on SourceForge see http://sourceforge.net/projects/ptptl. Java programmers can use the security features found natively in the Java 2 Standard Edition platform version 1.4 (J2SE v1.4).

The Java Cryptography Extension (JCE) offers support for identification and authentication using digital certificates, digital signatures and message digests. It also includes functions for symmetric and public key encryption. Finally, it provides an implementation of the Internet Proposed Standard Generic Security Services Application Programming Interface Version 2 (GSS-API) for securely exchanging messages between applications on a per-message basis. The GSS-API is particularly interesting in that it provides support for a number of underlying security mechanisms, such as Kerberos, to provide end-to-end security, including client identity, rights, confidentiality, and data integrity. It can be especially useful for peer-to-peer applications because it provides a significant amount of functionality that can be applied to short sessions.

Note

The GSS-API is also implemented in C and C++, Python, and other languages. See the "Additional Resources" section for a pointer to the specifications, including the language bindings.


The Java Secure Socket Extension (JSSE) enables secure Internet communications using SSL (Secure Sockets Layer) and TLS (Transport Layer Security).

The Java Authentication and Authorization Service (JAAS) enables services to authenticate and enforce access controls upon users. With JAAS, developers can leverage existing authentication and authorization services. Native support for Kerberos v.5 is included, and providers exist for authentication using Solaris, generic Unix, and Windows NT domains.

Using JAAS to Provide Authentication and Authorization

JAAS defines a number of core classes that are central to providing authentication and authorization. As mentioned earlier in this chapter, authentication confirms identity, and authorization provides access control. The JAAS core classes fall into three categories: common, authentication, and authorization.

The JAAS common classes are Subject, Principal, and classes associated with credentials.

A subject is any user of a computing service. This might be an actual person, or a running program or process. JAAS uses the subject to authorize access to resources.

A principal is a name associated with a subject. A subject might have one or more names associated with it. If you use multiple systems, it is likely that you have multiple logon identifiers. One system might require a unique ID, such as a username, and perhaps another system might require a social security number. The concept of a principal can be used to map multiple named associations to your identity, and help promote single sign-on environments.

A credential contains information used to authenticate a subject. The most common type of credential is a password. However, more secure credential representations exist, such as Kerberos tickets and public key certificates. JAAS also supports an extension mechanism that allows third-party credential implementations to be plugged-in. A subject's credentials are divided into two sets. One set contains the public credentials (public keys), and the other set contains the private credentials (passwords, private keys). Access to public credentials requires no special permissions, whereas access to private credentials is security-checked.

The authentication framework is based on Pluggable and Stackable Authentication Modules (PAM). The intent is to enable different vendors and administrators to plug in different authentication modules based on unique security policies.

The JAAS authentication classes are LoginContext, LoginModule, CallbackHandler, and Callback. These classes are central to JAAS authentication.

The LoginContext class shown in Listing 10.1 is an implementation of the PAM framework.

Listing 10.1 LoginContext Class
 public final class LoginContext {    public LoginContext(String name) { }    public LoginContext(String name, CallbackHandler callback) {}    public LoginContext(String name, Subject subject) {}    public LoginContext(String name, Subject subject, CallbackHandler callback) {}    public void login() { }           // two phase process    public void logout() { }    public Subject getSubject() { }   // get the authenticated Subject } 

The name parameter identifies a login configuration file used to identify one or more login modules.

If you are familiar with transactions, you will be familiar with the mechanics of authentication using pluggable authentication modules (see Listing 10.2).

Listing 10.2 LoginModule Class
 public interface LoginModule {    boolean login();      // 1st authentication phase    boolean commit();     // 2nd authentication phase    boolean abort();    boolean logout(); } 

To guarantee that either all LoginModules succeed or none succeed, the LoginContext performs the authentication steps in two phases:

  • Phase 1 In the first phase, the LoginContext invokes the LoginModules identified in the login configuration file. Each module is instructed to attempt authentication using whatever mechanism that LoginModule implements. If all the necessary LoginModules successfully pass this phase, the LoginContext enters Phase 2.

  • Phase 2 In the second phase, the LoginContext invokes the LoginModules again, instructing each to formally commit the authentication process. As can be seen, this two-phase commit is similar to transaction processing. During this phase, each LoginModule associates authenticated principals and credentials with the subject.

If either the first phase or the second phase fails, the LoginContext invokes the abort method on each configured LoginModule. Each LoginModule then cleans up any login state information it had associated with the authentication attempt.

As described in Sun's JAAS documentation (http://java.sun.com/security/jaas/doc/api.html), the following steps are performed to authenticate a Subject:

  1. The caller instantiates a LoginContext.

  2. The LoginContext uses a login configuration (Configuration object) to load the LoginModules configured for the application.

  3. The caller invokes the LoginContext's login method.

  4. The login method invokes all the loaded LoginModules.

  5. Each LoginModule tries to authenticate the Subject. After authentication is complete, the LoginModules associate relevant Principals and credentials with the Subject.

  6. The LoginContext returns the authentication status to the application (pass or fail).

  7. If authentication succeeds, the application retrieves the authenticated Subject from the LoginContext.

The following configuration file defines a single login module:

 /** Login Configuration for JWorkPlace **/  JWorkPlace {   org.jworkplace.login.WorkPlaceLoginModule required debug=true; }; 

A login module can be associated with a service by setting the following property:

 -Djava.security.auth.login.config==/usr/JiniServices/CMS/config/login.config  

The LoginHandler class shown in Listing 10.3 creates a LoginContext, and implements the CallbackHandler interface. The CallbackHandler interface implements one method:

    void handle(Callback[] callbacks)                  throws java.io.IOException, UnsupportedCallbackException; 

Security services make requests for different types of information by passing Callbacks to the CallbackHandler. The CallbackHandler implementation decides how to retrieve and display the information depending on the Callbacks passed to it. For example, if the service needs a username and password to authenticate a user, it uses a NameCallback and PasswordCallback. The CallbackHandler can then prompt for the appropriate information.

Listing 10.3 LoginHandler Class
 package org.jworkplace.login; import java.io.*; import java.util.*; import java.security.Principal; import javax.security.auth.*; import javax.security.auth.callback.*; import javax.security.auth.login.*; import javax.security.auth.spi.*; import com.sun.security.auth.*; public class LoginHandler implements CallbackHandler {   LoginContext lc;   NameCallback nameCallback;   PasswordCallback passwordCallback;   int loginTries = 0;   String user;   char[] password;   // create the LoginContext using the JWorkPlace configuration   public LoginHandler() {     try {      lc = new LoginContext("JWorkPlace", new Subject(), this);     } catch (LoginException le) {      le.printStackTrace();      System.exit(-1);     }   }   // The Callback Interface   // provides the types of callbacks used to interface with the LoginModule   public void handle(Callback[] callbacks) throws IOException,                       UnsupportedCallbackException {     for (int i = 0; i < callbacks.length; i++) {       if (callbacks[i] instanceof TextOutputCallback) {         // display the message according to the specified type         TextOutputCallback toc = (TextOutputCallback)callbacks[i];         switch (toc.getMessageType()) {         case TextOutputCallback.INFORMATION:           System.out.println(toc.getMessage());           break;         case TextOutputCallback.ERROR:           System.out.println("Login error: " + toc.getMessage());           break;         case TextOutputCallback.WARNING:           System.out.println("Login warning: " + toc.getMessage());           break;         default:           throw new IOException("Unknown message type: " +                    toc.getMessageType());         }        } else if (callbacks[i] instanceof NameCallback) {          nameCallback = (NameCallback)callbacks[i];          nameCallback.setName(user);        } else if (callbacks[i] instanceof PasswordCallback) {          passwordCallback = (PasswordCallback)callbacks[i];          passwordCallback.setPassword(password);        } else {         throw new UnsupportedCallbackException             (callbacks[i], "Unrecognized Callback");        }    }   }  // user defined login method which simply counts the number of login attempts  // and sets the user and password which will be used in the callback above   public boolean login(String user, char[] password)   {      if(loginTries > 2) {       System.out.println("Sorry charlie...");       return false;      } else {       this.user = user;       this.password = password;      }      loginTries++;      try {       // attempt authentication on the LoginContext        lc.login();       // authentication succeeded        return true;       } catch (AccountExpiredException aee) {          System.out.println("Your account has expired. " +                "Please notify your administrator.");       } catch (CredentialExpiredException cee) {         System.out.println("Your credentials have expired.");       } catch (FailedLoginException fle) {         System.out.println("Authentication Failed");       } catch (Exception e) {         System.out.println("Unexpected Exception - unable to continue");        e.printStackTrace();       }      // authentication did not succeed      return false;      } } 

The WorkPlacePrincipal class in Listing 10.4 implements the Principal interface.

Listing 10.4 WorkPlacePrincipal Class
 package org.jworkplace.login; import java.security.Principal; public class WorkPlacePrincipal implements Principal, java.io.Serializable {   private String name;   public WorkPlacePrincipal(String name) {     if (name == null)       throw new NullPointerException("illegal null input");     this.name = name;   }   public String getName() {     return name;   }   public String toString() {     return("WorkPlacePrincipal: " + name);   }   public boolean equals(Object o) {     if (o == null)       return false;     if (this == o)       return true;     if (!(o instanceof WorkPlacePrincipal))       return false;     WorkPlacePrincipal that = (WorkPlacePrincipal)o;     if (this.getName().equals(that.getName()))     return true;     return false;   }   public int hashCode() {     return name.hashCode();   } } 

To initialize a LoginModule, the LoginContext calls each configured LoginModule's no-argument constructor:

 void initialize(Subject subject, CallbackHandler callbackHandler,      Map sharedState, Map options); 

The WorkPlaceLoginModule class in Listing 10.5 implements the LoginModule interface. The specific validation of the username and password is not shown, as you would provide your own specific implementation.

Listing 10.5 WorkPlaceLoginModule Class
 package org.jworkplace.login; import java.util.*; import java.io.IOException; import javax.security.auth.*; import javax.security.auth.callback.*; import javax.security.auth.login.*; import javax.security.auth.spi.*; public class WorkPlaceLoginModule implements LoginModule {   // initial state   private Subject subject;   private CallbackHandler callbackHandler;   private Map sharedState;   private Map options;   // configurable option   private boolean debug = false;   // the authentication status   private boolean succeeded = false;   private boolean commitSucceeded = false;   // username and password   private String username;   private char[] password;   private WorkPlacePrincipal userPrincipal;   // the initialize method is called by the LoginContext   public void initialize(Subject subject, CallbackHandler callbackHandler,            Map sharedState, Map options) {     this.subject = subject;     this.callbackHandler = callbackHandler;     this.sharedState = sharedState;     this.options = options; }   // this is the login method that is called by the LoginContext   public boolean login() throws LoginException {     // prompt for a username and password     if (callbackHandler == null)       throw new LoginException("Error: no CallbackHandler available ");     // Two Callback are created, one for the name and one for the password     Callback[] callbacks = new Callback[2];     callbacks[0] = new NameCallback("JWorkPlace username: ");     callbacks[1] = new PasswordCallback("JWorkPlace password: ", false);     try {       // this is the callback to our LoginHandler       callbackHandler.handle(callbacks);       // get the username and password provided in the callback       username = ((NameCallback)callbacks[0]).getName();       char[] tmpPassword = ((PasswordCallback)callbacks[1]).getPassword();       if (tmpPassword == null) {         // treat a NULL password as an empty password         tmpPassword = new char[0];       }           password = new char[tmpPassword.length];           System.arraycopy(tmpPassword, 0,             password, 0, tmpPassword.length);       ((PasswordCallback)callbacks[1]).clearPassword();     } catch (java.io.IOException ioe) {       throw new LoginException(ioe.toString());     } catch (UnsupportedCallbackException uce) {       throw new LoginException("Error: " + uce.getCallback().toString());     }       // verify the username/password       // insert code to verify user / password here        // Authentication succeeded        succeeded = true;        return true;        // Authentication failed       succeeded = false;        throw new FailedLoginException("Password Incorrect"); }   // the 2 phases of authentication   success commit   public boolean commit() throws LoginException {     if (succeeded == false) {       return false;     } else {       // add a Principal (authenticated identity)       // to the Subject       // assume the user we authenticated is the WorkPlacePrincipal       userPrincipal = new WorkPlacePrincipal(username);       if (!subject.getPrincipals().contains(userPrincipal)) {         subject.getPrincipals().add(userPrincipal);      }       // clean out state       username = null;       for (int i = 0; i < password.length; i++)           password[i] = ' ';       password = null;       commitSucceeded = true;       return true;     }   }    // the 2 phases of authentication   failure abort   public boolean abort() throws LoginException {     if (succeeded == false) {       return false;     } else if (succeeded == true && commitSucceeded == false) {       // login succeeded but overall authentication failed       succeeded = false;       username = null;       if (password != null) {         for (int i = 0; i < password.length; i++)         password[i] = ' ';         password = null;       }       userPrincipal = null;     } else {       // overall authentication succeeded and commit succeeded,       // but someone else's commit failed       logout();     }     return true;   }   public boolean logout() throws LoginException {     subject.getPrincipals().remove(userPrincipal);     succeeded = false;     succeeded = commitSucceeded;     username = null;     if (password != null) {       for (int i = 0; i < password.length; i++)         password[i] = ' ';       password = null;     }     userPrincipal = null;     return true;   } } 

After successful authentication, JAAS provides the capability to enforce access controls on the principals associated with the authenticated subject. You will need to grant the necessary security permissions to your codebase. If you are using JDK 1.3, JAAS is not a part of the core distribution, and therefore requires a grant statement similar to the following:

 /** Java 2 Access Control Policy for the JAAS Application **/  /* grant the JAAS core library AllPermission */ grant codebase "file:/usr/java/jdk1.3/jre/lib/ext/jaas.jar" {   permission java.security.AllPermission; }; /* grant the sample LoginModule AllPermission */  grant codebase "file:/usr/JWorkPlace/lib/login.jar" {   permission java.security.AllPermission; }; grant codebase "file:/usr/JWorkPlace/lib/service.jar" {   permission javax.security.auth.AuthPermission "createLoginContext";   permission javax.security.auth.AuthPermission "doAs";   permission java.util.PropertyPermission "java.home", "read";   permission org.jworkplace.login.AccountPermission "createAccount"; }; 

A login policy can be associated with a service by setting the following property:

 -Djava.security.policy==/usr/JiniServices/CMS/config/login.policy  

The following policy extends the Java 2 codebase policy with Subject-based access control. This grant entry provides the necessary permissions to perform a sensitive operation (createAccount) to any Subject that has an associated WorkPlacePrincipal.

 /** Subject-Based Access Control Policy for the JWorkPlace Application **/  grant codebase "file:/usr/JWorkPlace/lib/service.jar",   Principal org.jworkplace.login.WorkPlacePrincipal * {     permission org.jworkplace.login.AccountPermission "createAccount"; }; 

A login authorization policy can be associated with a service by setting the following property:

 -Djava.security.auth.policy==/usr/JiniServices/CMS/config/login_jaas.policy  

JAAS supplements the Java 2 security with architecture using the Subject.doAs method to dynamically associate an authenticated subject with the current AccessControlContext. The AccessController can base its decisions on both the executing code itself and the principals associated with the Subject.

The WorkPlaceAction class in Listing 10.6 implements the PrivilegedAction interface. The PrivilegedAction interface is implemented by classes that require access control checks. The run method is called by the AccessController.doPrivileged method after enabling privileges.

Listing 10.6 WorkPlaceAction Class
 package org.jworkplace.login; import java.io.File; import java.security.PrivilegedAction; public class WorkPlaceAction implements PrivilegedAction {   // run method defined in the PrivilegedAction interface   public Object run() {    // get the security manager and check if permission has been granted    SecurityManager sm = System.getSecurityManager();    sm.checkPermission(new AccountPermission("createAccount"));    return null;   } } 

Finally, the AccountPermission class shown in Listing 10.7 extends BasicPermission.BasicPermission provides a simple base class for creating new permission types. In this case, you create a new AccountPermission class that is checked in the WorkPlaceAction to verify that the thread of control associated with the current Subject has the necessary permission to create an account.

Listing 10.7 AccountPermission Class
 package org.jworkplace.login; import java.security.BasicPermission; public final class AccountPermission extends BasicPermission                     implements java.io.Serializable {   public AccountPermission(String name) {     this(name,null);   }   public AccountPermission(String name, String action) {     super(name);   } } 


JavaT P2P Unleashed
JavaT P2P Unleashed
ISBN: N/A
EAN: N/A
Year: 2002
Pages: 209

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net