/*
   Chapter 9:     Hands On
   Programmer:    Brad Shedd
   Date:          September 07, 2006
   Program Name:  Password.java
   Purpose:       To provide a reusable Password class
*/

import java.util.*;

public class Password
{
  
final static int MIN_SIZE = 6;
  
final static int MAX_SIZE = 15;
       
static int maxHistory = 4;
       
static int expiresNotifyLimit = 3;

  
private int maxUses = 120;
  
private int remainingUses = maxUses;
  
private boolean autoExpires = true;
  
private boolean expired = false;

  
private ArrayList pswdHistory;


  
// Constructors for objects of class Password
   public Password(String newPassword) throws Exception
   {
      pswdHistory =
new ArrayList(maxHistory);
      set(newPassword);
   }

  
public Password(String newPassword, int numMaxUses) throws Exception
   {
      pswdHistory =
new ArrayList(maxHistory);
      maxUses = numMaxUses;
      remainingUses = numMaxUses;
      set(newPassword);
   }

  
public Password(String newPassword, boolean pswdAutoExpires) throws Exception
   {
      pswdHistory =
new ArrayList(maxHistory);
     autoExpires = pswdAutoExpires;
      set(newPassword);
   }

  
public Password(String newPassword, int numMaxUses, boolean pswdAutoExpires) throws Exception
   {
      pswdHistory =
new ArrayList(maxHistory);
      maxUses = numMaxUses;
      remainingUses = numMaxUses;
      autoExpires = pswdAutoExpires;
      set(newPassword);
   }

  
public boolean getAutoExpires()
   {
     
return autoExpires;
   }

  
public void setAutoExpires(boolean autoExpires)
  {
     
this.autoExpires = autoExpires;
     
if(autoExpires)
         remainingUses = maxUses;
   }

  
public boolean isExpired()
   {
     
return expired;
   }

  
public void setExpired(boolean newExpired)
   {
      expired = newExpired;
   }

  
public int getExpiredNotifyLimit()
   {
     
return expiresNotifyLimit;
   }

  
public void setExpiresNotifyLimit(int newNotifyLimit)
   {
     
if(newNotifyLimit >= 2 && newNotifyLimit <= 20)
         expiresNotifyLimit = newNotifyLimit;
   }

  
public int getMaxHistory()
   {
     
return maxHistory;
   }

  
public void setMaxHistory(int newMaxHistory)
   {
     
int overage = 0;
     
if(newMaxHistory >= 1 && newMaxHistory <= 10)
      {
         maxHistory = newMaxHistory;
         overage = getHistorySize() - maxHistory;
        
if(overage > 0)                        // if size > max allowed
         {
           
do {
               pswdHistory.remove(0);          
// then remove overage number
               overage--;                    // of oldest pswds from list
            } while(overage > 0);

            pswdHistory.trimToSize();          
// resize capacity to max allowed
         }
      }
   }

  
public int getRemainingUses()
   {
     
return remainingUses;
   }

  
public int getHistorySize()
   {
     
return pswdHistory.size();
   }

  
public boolean isExpiring()
  {
     
boolean expiring = false;

     
if(autoExpires && remainingUses <= expiresNotifyLimit)
         expiring =
true;

        
return expiring;
      }

  
// Sets password to a new value; keeps current & previous values in history up to max number
   public void set(String pswd) throws Exception
   {
      String encryptPswd;
     
boolean pswdAdded = true;

      pswd = pswd.trim();              
// remove any leading, trailing white space
      verifyFormat(pswd);           // verify password was entered properly
      encryptPswd = encrypt(pswd);  // convert to encrypted form

      if(!pswdHistory.contains(encryptPswd))    // if pswd not in recently used list
      {
        
if(pswdHistory.size() == maxHistory)   // if list is at max size
            pswdHistory.remove(0);           // remove list, oldest, pswd from list

         pswdAdded = pswdHistory.add(encryptPswd); // add new pswd to end of ArrayList

         if(!pswdAdded)                   // should never happen
            throw new Exception("Internal list error - Password not accepted");

        
if(expired)                      // if pswd has expired,
            expired = false;              // reset to not expired

         if(autoExpires)                     // if pswd auto expires,
            remainingUses = maxUses;         // reset uses to max
         }
        
else
            throw new Exception("Password recently used");
         }

        
// Validates entered password against most recently saved value
         public void validate(String pswd) throws Exception
         {
            String encryptPswd;
            String currentPswd;
           
int currentPswdIndex;

            verifyFormat(pswd);          
// verify password was entered properly
            encryptPswd = encrypt(pswd);     // convert to encrypted form

            if(!pswdHistory.isEmpty())    // at least one password entry is in history
            {
               currentPswdIndex = pswdHistory.size()-1;
               currentPswd = (String)pswdHistory.get(currentPswdIndex);

              
if(!encryptPswd.equals(currentPswd)) // if not most recent pswd
                     throw new Exception("Password is invalid");

              
if(expired)
                    
throw new Exception("Password has expired - please change");

              
if(autoExpires)
               {
                 --remainingUses;
                 
if(remainingUses <= 0)
                        expired =
true;
                }
            }
           
else
               throw new Exception("No password on file - list corrupted!"); // should never happen


            }

           
// Verifies password has proper format
            private void verifyFormat(String pswd) throws Exception
            {
              
boolean numFound = false;

              
if(pswd.length() == 0)
                   
throw new Exception("No password provided!");

              
if(pswd.length() < MIN_SIZE)
                   
throw new Exception("Password must be at least "+MIN_SIZE+ " characters in length");

              
if(pswd.length() > MAX_SIZE)
                   
throw new Exception("Password cannot be greater than "+MAX_SIZE+ " characters in length");

              
// scan through password to find if at least 1 number is used
               for(int i=0; i < pswd.length() && !numFound; ++i)
                 
if(Character.isDigit(pswd.charAt(i)))
                      numFound =
true;

              
if(!numFound)
                 
throw new Exception("Password is invalid - must have at least one numeric digit");
             }

            
// Encrypts original password returning new encrypted String
             private String encrypt(String pswd)
             {
               StringBuffer encryptPswd;
              
int pswdSize = 0;
              
int midpoint = 0;
              
int hashCode = 0;

              
// swap first and last half of password
               pswdSize = pswd.length();
               midpoint = pswdSize/2;
               encryptPswd =
new StringBuffer(pswd.substring(midpoint)  // get last half of pswd
                  + pswd.substring(0,midpoint));                  // and cocatenate first half

               encryptPswd.reverse();  // reverses order of characters in password

               for(int i=0; i < pswdSize; ++i)
                 encryptPswd.setCharAt(i, (
char)(encryptPswd.charAt(i) & pswd.charAt(i)) );

               hashCode = pswd.hashCode();  
// hash code for original password
               encryptPswd.append(hashCode);

              
return encryptPswd.toString();
            }
         }

Homepage