Here is my repaired version of Jit Ghosh's AD/AM Membership Provider: 

 

using System;
using System.Configuration;
using System.Web.Configuration;
using System.Configuration.Provider;
using System.Web.Security;
using System.DirectoryServices;
using System.Security.Cryptography;
using System.Text;
using System.IO;

namespace CustomProviders
{
 /// <summary>
 /// Implements a custom provier for the ASP.NET 2.0 Membership Service.
 /// This provider uses Microsoft Active Directory Application Mode as the storage system for user information
 /// </summary>
 public class ADAMMembershipProvider: MembershipProvider
 {


  private string _Name = null;

  private string _ADAMServerUri = "localhost";
  private string _ADAMUserName = null;
  private string _ADAMPassword = null;
  private string _ADAMTopContainer = "CN=ASP.NET Security Provider";
  private string _ADAMUserContainer = "CN=Users";
  private bool _EnablePasswordRetrieval = false;
  private bool _EnablePasswordReset = true;
  private string _ApplicationName = null;
  private bool _RequiresQuestionAndAnswer = false;
  private bool _RequiresUniqueEMail = false;
  private byte[] _DecryptionKey = null;
  private byte[] _ValidationKey = null;

  private MembershipPasswordFormat _PasswordFormat = MembershipPasswordFormat.Hashed;


  private const int PAGEINDEX_NOPAGING = 1;
  private const int PAGESIZE_NOPAGING = int.MaxValue;

  //Filter formats for various LDAP queries

  private const string _ADAMCommonName = "CN={0}";
  private const string _ADAMGetAllUsersFilterAppSpecific = "(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserApplicationName={0}))";
  private const string _ADAMUserSearchByUserNameFilterAppSpecific = "(&(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserApplicationName={0}))(aspnetmembershipuserUserName={1}))";
  private const string _ADAMUserSearchByEmailFilterAppSpecific = "(&(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserApplicationName={0}))(aspnetmembershipuserEMail={1}))";
  private const string _ADAMUserSearchByNameAndEmailFilterAppSpecific = "(&(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserApplicationName={0}))(&(aspnetmembershipuserUserName={1})(aspnetmembershipuserEMail={2})))";
  private const string _ADAMCountUsersOnlineAppSpecific = "(&(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserApplicationName={0}))(aspnetmembershipuserLastActivityTimeStamp>={1:yyMMddHHmmss}Z))";
  private const string _ADAMGetAllUsersFilter = "(objectClass=aspnetmembershipuser)";
  private const string _ADAMUserSearchByUserNameFilter = "(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserUserName={0}))";
  private const string _ADAMUserSearchByEmailFilter = "(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserEMail={0}))";
  private const string _ADAMUserSearchByNameAndEmailFilter = "(&(objectClass=aspnetmembershipuser)(&(aspnetmembershipuserUserName={0})(aspnetmembershipuserEMail={1})))";
  private const string _ADAMCountUsersOnline = "(&(objectClass=aspnetmembershipuser)(aspnetmembershipuserLastActivityTimeStamp>={0:yyMMddHHmmss}Z))";
  private string _ADAMLDAPPathToUserContainer = null;

  //Various ADAM Schema class and attribute name literals
  private const string _ADAMUserObjectClass = "aspnetmembershipuser";
  private const string _ADAMPropApplicationName = "aspnetmembershipuserApplicationName";
  private const string _ADAMPropUserName = "aspnetmembershipuserUserName";
  private const string _ADAMPropUserId = "aspnetmembershipuserUserId";
  private const string _ADAMPropEMail = "aspnetmembershipuserEmail";
  private const string _ADAMPropComment = "aspnetmembershipuserComment";
  private const string _ADAMPropIsAnonymous = "aspnetmembershipuserIsAnonymous";
  private const string _ADAMPropIsApproved = "aspnetmembershipuserIsApproved";
  private const string _ADAMPropUserCreationTimestamp = "aspnetmembershipuserUserCreationTimeStamp";
  private const string _ADAMPropLastActivityTimestamp = "aspnetmembershipuserLastActivityTimeStamp";
  private const string _ADAMPropLastLoginTimestamp = "aspnetmembershipuserLastLoginTimeStamp";
  private const string _ADAMPropLastPasswordChangeTimestamp = "aspnetmembershipuserLastPasswordChangeTimestamp";
  private const string _ADAMPropPassword = "aspnetmembershipuserPassword";
  private const string _ADAMPropPasswordQuestion = "aspnetmembershipuserPasswordQuestion";
  private const string _ADAMPropPasswordAnswer = "aspnetmembershipuserPasswordAnswer";
  private const string _ADAMPropPasswordFormat = "aspnetmembershipuserPasswordFormat";
  private const string _ADAMPropPasswordSaltOrIV = "aspnetmembershipuserPasswordSaltOrIV";


  public ADAMMembershipProvider()
  {

  }


  public override string ResetPassword(string name, string answer)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;

   string RetVal = null;

   try
   {
    //Cannot reset password if not enabled via configuration settings
    if (_EnablePasswordReset == false)
     throw new NotSupportedException("Current configuration settings do not allow resetting passwords");
    //check if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, name) : string.Format(_ADAMUserSearchByUserNameFilter, name), deUserContainer, out deMembershipUser) == false)
     throw new ApplicationException(string.Format("User {0} does not exist", name));


    if (deMembershipUser != null)//user record found!!
    {
     deMembershipUser.RefreshCache();
     //if configuration settings require a security question/answer protocol for passwords
     //then the supplied answer parameter needs to match the stored password answer for the user
     if (_RequiresQuestionAndAnswer && (deMembershipUser.Properties[_ADAMPropPasswordAnswer].Value == null || (string)deMembershipUser.Properties[_ADAMPropPasswordAnswer].Value != answer))
      throw new ApplicationException("Password answer does not match");

     //generate a new random password. I have chosen to use a 6 character password.
     //It is advisable to make this a configuration setting as well
     RetVal = Membership.GeneratePassword(6);
     //convert password for storage per password format, and set the user record with the new password
     deMembershipUser.Properties[_ADAMPropPassword].Value = this.ConvertPasswordForStorage(RetVal);
     //record timestamp
     deMembershipUser.Properties[_ADAMPropLastPasswordChangeTimestamp].Value = DateTime.UtcNow;
     //save changes
     deMembershipUser.CommitChanges();
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error resetting password", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   //return new password
   return RetVal; ;
  }


  public override bool ChangePassword(string name, string oldPwd, string newPwd)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;

   try
   {

    //check for existence of user record
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, name) : string.Format(_ADAMUserSearchByUserNameFilter, name), deUserContainer, out deMembershipUser) == false)
     throw new ApplicationException(string.Format("User {0} does not exist", name));

    if (deMembershipUser != null)//user record found!!
    {
     deMembershipUser.RefreshCache();
     //check if the old password matches
     if (!ComparePassword(oldPwd, (byte[])deMembershipUser.Properties[_ADAMPropPassword].Value))
      throw new ApplicationException("Existing password does not match");
     ////convert password for storage per password format, and set the user record with the new password
     deMembershipUser.Properties[_ADAMPropPassword].Value = ConvertPasswordForStorage(newPwd);
     //record timestamp
     deMembershipUser.Properties[_ADAMPropLastPasswordChangeTimestamp].Value = DateTime.UtcNow;
     //save changes
     deMembershipUser.CommitChanges();
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error changing password", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   return true;
  }

  public override void UpdateUser(MembershipUser user)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;

   try
   {
    //check if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, user.UserName) : string.Format(_ADAMUserSearchByUserNameFilter, user.UserName), deUserContainer, out deMembershipUser) == false)
     throw new ApplicationException(string.Format("User {0} does not exist", user.UserName));

    int Count = 0;


    if (deMembershipUser != null)//user record found!!
    {
     deMembershipUser.RefreshCache();
     //if requiresUniqueEmail is true, the update cannot specify an email that violates uniqueness
     //if the email in the update is different than the original email in the user record, check to see if there
     //is another user in the system with the same email
     if (_RequiresUniqueEMail && deMembershipUser.Properties[_ADAMPropEMail].Value != user.Email
      && ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByEmailFilterAppSpecific, _ApplicationName, user.Email) : string.Format(_ADAMUserSearchByEmailFilter, user.Email), deUserContainer, out deMembershipUser) == true)
      throw new ApplicationException(string.Format("A user with email {0} already exists", user.Email));


     //set the user record with the updates
     deMembershipUser.Properties[_ADAMPropUserName].Value = user.UserName;
     deMembershipUser.Properties[_ADAMPropEMail].Value = user.Email;
     //deMembershipUser.Properties[_ADAMPropComment].Value = user.Comment;
     //deMembershipUser.Properties[_ADAMPropIsApproved].Value = user.IsApproved.ToString();
     deMembershipUser.Properties[_ADAMPropPasswordQuestion].Value = user.PasswordQuestion;
     
     

     //save changes
     deMembershipUser.CommitChanges();
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error updating User", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }

 

  }


  public override MembershipUser CreateUser(string username, string password, string email, string passwordQuestion, string passwordAnswer, bool isApproved, out MembershipCreateStatus status)
  {
   MembershipUser NewUser = null;
   DirectoryEntry deUserContainer = null;

   DirectoryEntry deMembershipUser = null;

   status = MembershipCreateStatus.UserRejected;

   try
   {
    //check if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, username) : string.Format(_ADAMUserSearchByUserNameFilter, username), deUserContainer, out deMembershipUser) == true)
    {
     //if found, return error
     status = MembershipCreateStatus.DuplicateUserName;
     return null;
    }
    //check for duplicate email constraint
    if (_RequiresUniqueEMail && ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByEmailFilterAppSpecific, _ApplicationName, email) : string.Format(_ADAMUserSearchByEmailFilter, email), deUserContainer, out deMembershipUser) == true)
    {
     //if violated return error
     status = MembershipCreateStatus.DuplicateEmail;
     return null;
    }

    //create new user id
    Guid UserId = Guid.NewGuid();
    DateTime TimeNow = DateTime.UtcNow;
    //add the object. Set CN to the user id GUID. CN needs to be unique. This avoids conflicts around having same user names
    //for multiple applications. Other alternative could be to use a concat to username and appname as CN
    deMembershipUser = deUserContainer.Children.Add(string.Format(_ADAMCommonName, UserId.ToString()), _ADAMUserObjectClass);
    //set other attributes
    deMembershipUser.Properties[_ADAMPropUserId].Value = UserId.ToByteArray();
    deMembershipUser.Properties[_ADAMPropUserName].Value = username;
    deMembershipUser.Properties[_ADAMPropApplicationName].Value = _ApplicationName;
    deMembershipUser.Properties[_ADAMPropPassword].Value = ConvertPasswordForStorage(password);
    deMembershipUser.Properties[_ADAMPropEMail].Value = email;
    deMembershipUser.Properties[_ADAMPropUserCreationTimestamp].Value = TimeNow;
    deMembershipUser.Properties[_ADAMPropPasswordQuestion].Value = passwordQuestion;
    deMembershipUser.Properties[_ADAMPropPasswordAnswer].Value = passwordAnswer;
    deMembershipUser.Properties[_ADAMPropIsApproved].Value = true;
    
    //save changes
    deMembershipUser.CommitChanges();
    //create new instance of MembershipUser and return it
    NewUser = new MembershipUser(this, username, UserId, email, null, null, true, (DateTime)deMembershipUser.Properties[_ADAMPropUserCreationTimestamp].Value, DateTime.Now, DateTime.Now, DateTime.Now);
    status = MembershipCreateStatus.Success;

   }
   catch (Exception Ex)
   {
    status = MembershipCreateStatus.ProviderError;
    NewUser = null;
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }

   return NewUser;


  }

  public override bool EnablePasswordReset
  {
   get
   {

    return _EnablePasswordReset;
   }
  }

  public override int GetNumberOfUsersOnline()
  {
   DirectoryEntry deUserContainer = null;

   int Count = 0;
   try
   {

    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    //search for all users where LastActivityTimeStamp >= TimeNow - UserIsOnlineTimeWindow
    string Filter = (_ApplicationName != null) ? string.Format(_ADAMCountUsersOnlineAppSpecific, _ApplicationName, DateTime.UtcNow.AddMinutes(Convert.ToDouble(-1 * Membership.UserIsOnlineTimeWindow))) : string.Format(_ADAMCountUsersOnline, DateTime.UtcNow.AddMinutes(Convert.ToDouble(-1 * Membership.UserIsOnlineTimeWindow)));
    ADAMFindUsers(Filter, deUserContainer, PAGEINDEX_NOPAGING, PAGESIZE_NOPAGING, out Count);
   }
   catch (Exception Ex)
   {
    throw;
   }
   finally
   {
    try
    {
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   //return count
   return Count;
  }

  public override bool ChangePasswordQuestionAndAnswer(string name, string password, string newPwdQuestion, string newPwdAnswer)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;

   try
   {
    //check if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, name) : string.Format(_ADAMUserSearchByUserNameFilter, name), deUserContainer, out deMembershipUser) == false)
     throw new ApplicationException(string.Format("User {0} does not exist", name));

    if (deMembershipUser != null)//user record found !!
    {
     deMembershipUser.RefreshCache();
     //check if the old password matches
     if (!ComparePassword(password, (byte[])deMembershipUser.Properties[_ADAMPropPassword].Value))
      throw new ApplicationException("Existing password does not match");
     //set properties and save changes
     deMembershipUser.Properties[_ADAMPropPasswordQuestion].Value = newPwdQuestion;
     deMembershipUser.Properties[_ADAMPropPasswordAnswer].Value = newPwdAnswer;
     deMembershipUser.CommitChanges();
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error changing password", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   return true;
  }


  public override MembershipUser GetUser(string name, bool userIsOnline)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;

   try
   {
    //check if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, name) : string.Format(_ADAMUserSearchByUserNameFilter, name), deUserContainer, out deMembershipUser) == false)
     throw new ApplicationException(string.Format("User {0} does not exist", name));

    if (deMembershipUser != null)//user record found!!
    {

     //refresh the AD cache to load all attributes
     deMembershipUser.RefreshCache();
     //if the user is indicated to be online
     if (userIsOnline)
     {
      //update the appropriate timestamp and save changes
      deMembershipUser.Properties[_ADAMPropLastActivityTimestamp].Value = DateTime.UtcNow;
      deMembershipUser.CommitChanges();
     }
     //return a new instance created from the retrieved user record
     return this.ConstructMembershipUserFromDirectoryEntry(deMembershipUser);
     /*
     return new MembershipUser(this,
      (string)deMembershipUser.Properties[_ADAMPropUserName].Value,
      deMembershipUser.Properties[_ADAMPropUserId].Value == null ? string.Empty : (string)deMembershipUser.Properties[_ADAMPropUserId].Value,
      deMembershipUser.Properties[_ADAMPropEMail].Value == null ? String.Empty : (string)deMembershipUser.Properties[_ADAMPropEMail].Value,
      deMembershipUser.Properties[_ADAMPropPasswordQuestion].Value == null ? String.Empty : (string)deMembershipUser.Properties[_ADAMPropPasswordQuestion].Value,
      deMembershipUser.Properties[_ADAMPropComment].Value == null ? String.Empty : (string)deMembershipUser.Properties[_ADAMPropComment].Value,
      deMembershipUser.Properties[_ADAMPropIsApproved].Value == null ? false : (bool)deMembershipUser.Properties[_ADAMPropIsApproved].Value,
      deMembershipUser.Properties[_ADAMPropUserCreationTimestamp].Value == null ? DateTime.Now : (DateTime)deMembershipUser.Properties[_ADAMPropUserCreationTimestamp].Value,
      deMembershipUser.Properties[_ADAMPropLastLoginTimestamp].Value == null ? DateTime.Now :  (DateTime)deMembershipUser.Properties[_ADAMPropLastLoginTimestamp].Value,
      deMembershipUser.Properties[_ADAMPropLastActivityTimestamp].Value == null ? DateTime.Now : (DateTime)deMembershipUser.Properties[_ADAMPropLastActivityTimestamp].Value,
      deMembershipUser.Properties[_ADAMPropLastPasswordChangeTimestamp].Value == null ? DateTime.Now :  (DateTime)deMembershipUser.Properties[_ADAMPropLastPasswordChangeTimestamp].Value);
     */
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error getting user", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   return null;
  }


  public override bool EnablePasswordRetrieval
  {
   get
   {
    return _EnablePasswordRetrieval;
   }
  }

  public override string ApplicationName
  {
   get
   {

    return _ApplicationName;
   }
   set
   {
    _ApplicationName = value;
   }
  }

  public override string GetUserNameByEmail(string email)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;
   string RetVal = null;

   try
   {
    //find the user for this application with the specified email address
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByEmailFilterAppSpecific, _ApplicationName, email) : string.Format(_ADAMUserSearchByEmailFilter, email), deUserContainer, out deMembershipUser))
     return null;

    if (deMembershipUser != null)//user record found!!
    {
     //refresh the cache with the attribute values
     deMembershipUser.RefreshCache();
     //return user name
     RetVal = (string)deMembershipUser.Properties[_ADAMPropUserName].Value;
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error retrieving user name", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   return RetVal;
  }


  public override bool ValidateUser(string name, string password)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;
   bool RetVal = false;

   try
   {

    //check if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, name) : string.Format(_ADAMUserSearchByUserNameFilter, name), deUserContainer, out deMembershipUser) == false)
     RetVal = false;

    if (deMembershipUser != null)//user record found!!
    {
     //refresh cache with attribute values
     deMembershipUser.RefreshCache();
     //compare supplied password with stored password(or hash of supplied password with the stored hash in case the password format is set to hashed)
     RetVal = ComparePassword(password, (byte[])deMembershipUser.Properties[_ADAMPropPassword].Value);
     DateTime TimeNow = DateTime.UtcNow;
     //set appropriate timestamps
     deMembershipUser.Properties[_ADAMPropLastLoginTimestamp].Value = TimeNow;
     deMembershipUser.Properties[_ADAMPropLastActivityTimestamp].Value = TimeNow;
     deMembershipUser.CommitChanges();
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error validating user", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   //return comparison result
   return RetVal;
  }


  public override string GetPassword(string name, string answer)
  {

   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;

   string RetVal = null;
   try
   {
    //if password retrieval is disabled via configuration settings or the password format is set to hashed
    //we cannot retriev the password
    if (!_EnablePasswordRetrieval || _PasswordFormat == MembershipPasswordFormat.Hashed)
     throw new ApplicationException("Current configuration settings do not allow password retrieval");
    //check if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, name) : string.Format(_ADAMUserSearchByUserNameFilter, name), deUserContainer, out deMembershipUser) == false)
     throw new ApplicationException(string.Format("User {0} does not exist", name));

    if (deMembershipUser != null)//user record found!!
    {
     //refresh cache with stored attributes
     deMembershipUser.RefreshCache();
     //if configuration settings demand a security question/answer protocol, the supplied password answer
     //needs to match the stored password answer
     if (_RequiresQuestionAndAnswer && (deMembershipUser.Properties[_ADAMPropPasswordAnswer].Value == null || (string)deMembershipUser.Properties[_ADAMPropPasswordAnswer].Value != answer))
      throw new ApplicationException("Password answer does not match");
     
     //decode password and return it
     RetVal = GetReadablePassword((byte[])deMembershipUser.Properties[_ADAMPropPassword].Value);
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error retrieving password", Ex);
   }
   finally
   {
    try
    {
     deMembershipUser.Close();
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   return RetVal;


  }

  public override bool DeleteUser(string name, bool deleteAllRelatedData)
  {
   DirectoryEntry deUserContainer = null;
   DirectoryEntry deMembershipUser = null;

   try
   {


    //check if user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    if (ADAMFindUser((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, name) : string.Format(_ADAMUserSearchByUserNameFilter, name), deUserContainer, out deMembershipUser) == false)
     return false;

    if (deMembershipUser != null)//user record found!!
    {

     //remove the record
     deUserContainer.Children.Remove(deMembershipUser);
     //save changes
     deUserContainer.CommitChanges();
    }
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error deleting user", Ex);
   }
   finally
   {
    try
    {
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }
   return true;
  }

 

  public override bool RequiresQuestionAndAnswer
  {
   get
   {
    return _RequiresQuestionAndAnswer;
   }
  }

 

  public override MembershipUserCollection FindUsersByEmail(string emailToMatch, int pageIndex, int pageSize, out int totalRecords)
  {
   totalRecords = 0;


   DirectoryEntry deUserContainer = null;

   MembershipUserCollection RetVal = null;
   try
   {

 

    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    //get all the users or the nth page of users, and return the collection
    RetVal = ADAMFindUsers((_ApplicationName != null) ? string.Format(_ADAMUserSearchByEmailFilterAppSpecific, _ApplicationName, emailToMatch) : string.Format(_ADAMUserSearchByEmailFilter, emailToMatch), deUserContainer, pageIndex, pageSize, out totalRecords);


   }
   catch (Exception Ex)
   {
    throw;
   }
   finally
   {
    try
    {
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }

   return RetVal;
  }

  public override MembershipUserCollection FindUsersByName(string usernameToMatch, int pageIndex, int pageSize, out int totalRecords)
  {
   totalRecords = 0;


   DirectoryEntry deUserContainer = null;

   MembershipUserCollection RetVal = null;
   try
   {

 

    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    //find the users matching
    RetVal = ADAMFindUsers((_ApplicationName != null) ? string.Format(_ADAMUserSearchByUserNameFilterAppSpecific, _ApplicationName, usernameToMatch) : string.Format(_ADAMUserSearchByUserNameFilter, usernameToMatch), deUserContainer, pageIndex, pageSize, out totalRecords);


   }
   catch (Exception Ex)
   {
    throw;
   }
   finally
   {
    try
    {
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }

   return RetVal;
  }

  public override MembershipUserCollection GetAllUsers(int pageIndex, int pageSize, out int totalRecords)
  {

   totalRecords = 0;


   DirectoryEntry deUserContainer = null;

   MembershipUserCollection RetVal = null;
   try
   {


    //let's first see if the user exists
    deUserContainer = (_ADAMUserName != null && _ADAMPassword != null) ? new DirectoryEntry(_ADAMLDAPPathToUserContainer, _ADAMUserName, _ADAMPassword) : new DirectoryEntry(_ADAMLDAPPathToUserContainer);
    RetVal = ADAMFindUsers((_ApplicationName != null) ? string.Format(_ADAMGetAllUsersFilterAppSpecific, _ApplicationName) : _ADAMGetAllUsersFilter, deUserContainer, pageIndex, pageSize, out totalRecords);


   }
   catch (Exception Ex)
   {
    throw;
   }
   finally
   {
    try
    {
     deUserContainer.Close();
    }
    catch (Exception Ex)
    {
    }
   }

   return RetVal;
  }

 

  public override string Name
  {
   get
   {
    return _Name;
   }
  }

  public override void Initialize(string name, System.Collections.Specialized.NameValueCollection config)
  {
   _Name = name;

   try
   {
    if (config["server"] != null)
     _ADAMServerUri = config["server"];

    if (config["userName"] != null)
     _ADAMUserName = config["userName"];

    if (config["password"] != null)
     _ADAMPassword = config["password"];

    if (config["topContainer"] != null)
     _ADAMTopContainer = config["topContainer"];

    if (config["userContainer"] != null)
     _ADAMUserContainer = config["userContainer"];

    if (config["applicationName"] != null)
     _ApplicationName = config["applicationName"];

    if (config["enablePasswordRetrieval"] != null)
     _EnablePasswordRetrieval = Convert.ToBoolean(config["enablePasswordRetrieval"].ToLower());

    if (config["enablePasswordReset"] != null)
     _EnablePasswordReset = Convert.ToBoolean(config["enablePasswordReset"].ToLower());


    if (config["requiresQuestionAndAnswer"] != null)
     _RequiresQuestionAndAnswer = Convert.ToBoolean(config["requiresQuestionAndAnswer"].ToLower());

    if (config["requiresUniqueEMail"] != null)
     _RequiresUniqueEMail = Convert.ToBoolean(config["requiresUniqueEMail"].ToLower());

    if (config["passwordFormat"] != null)
    {
     switch (config["passwordFormat"].ToLower())
     {
      case "clear":
       _PasswordFormat = MembershipPasswordFormat.Clear;
       break;
      case "hashed":
       _PasswordFormat = MembershipPasswordFormat.Hashed;
       break;
      case "encrypted":
       _PasswordFormat = MembershipPasswordFormat.Encrypted;
       break;
      default:
       throw new ConfigurationException(string.Format("Unknown password format {0}. Supported password formats are Clear,Hashed or Encrypted", config["passwordFormat"]));
     }
    }
    LoadKey(config);

 

   }
   catch (Exception Ex)
   {
    throw new System.Configuration.ConfigurationException("There was an error reading the membership configuration settings", Ex);
   }

   _ADAMLDAPPathToUserContainer = string.Format("LDAP://{0}/{1},{2}", _ADAMServerUri, _ADAMUserContainer, _ADAMTopContainer);

  }

  private void LoadKey(System.Collections.Specialized.NameValueCollection config)
  {
   if (_PasswordFormat != MembershipPasswordFormat.Clear)
   {
    //object section = ConfigurationSettings.GetConfig("system.web/machineKey");
    //MachineKeySection machineKeySection = (MachineKeySection)section;
    
    //MachineKeySection machineKeySection = (MachineKeySection)ConfigurationSettings.GetConfig("system.web/machineKey");
    Configuration cfg = Configuration.GetWebConfiguration(System.Web.Hosting.HostingEnvironment.ApplicationVirtualPath);
    MachineKeySection machineKeySection = (MachineKeySection)cfg.GetSection("system.web/machineKey");

    switch (_PasswordFormat)
    {
     case MembershipPasswordFormat.Encrypted: //we need a symmetric key

      if (!machineKeySection.DecryptionKey.ToLower().Contains("autogenerate"))
       _DecryptionKey = HexStringToByteArray(machineKeySection.DecryptionKey);
      else if (config["decryptionKey"] != null)
       _DecryptionKey = HexStringToByteArray(config["decryptionKey"]);
      else
       throw new ConfigurationException("Symmetric key required to encrypt passwords. Are you missing a <decryptionKey> in your provider entry ?");
      break;
     case MembershipPasswordFormat.Hashed:
      if (!machineKeySection.ValidationKey.ToLower().Contains("autogenerate"))
       _ValidationKey = HexStringToByteArray(machineKeySection.ValidationKey);
      else if (config["validationKey"] != null)
       _ValidationKey = HexStringToByteArray(config["validationKey"]);
      else
       throw new ConfigurationException("Symmetric key required to hash passwords. Are you missing a <validationKey> in your provider entry ?");
      break;
    }
   }
  }

  private MembershipUser ConstructMembershipUserFromDirectoryEntry(DirectoryEntry deMembershipUser)
  {
   object userName;
   object userId;
   object eMail;
   object passwordQuestion;
   string comment;
   object isApproved;
   object creationDate;
   object lastLoginDate;
   object lastActivityDate;
   object lastPasswordChangedDate;

   userName = deMembershipUser.Properties[_ADAMPropUserName].Value;
   userId = deMembershipUser.Properties[_ADAMPropUserId].Value;
   eMail = deMembershipUser.Properties[_ADAMPropEMail].Value;
   passwordQuestion = deMembershipUser.Properties[_ADAMPropPasswordQuestion].Value;
   comment = string.Empty;
   isApproved = deMembershipUser.Properties[_ADAMPropIsApproved].Value;
   creationDate = deMembershipUser.Properties[_ADAMPropUserCreationTimestamp].Value;
   lastLoginDate = deMembershipUser.Properties[_ADAMPropLastLoginTimestamp].Value;
   lastActivityDate = deMembershipUser.Properties[_ADAMPropLastActivityTimestamp].Value;
   lastPasswordChangedDate = deMembershipUser.Properties[_ADAMPropLastPasswordChangeTimestamp].Value;

   userName = userName == null ? null : (string)userName;
   userId = userId == null ? string.Empty : new Guid((byte[])userId).ToString(); ;
   eMail = eMail == null ? null : (string)eMail;
   passwordQuestion = passwordQuestion == null ? null : (string)passwordQuestion;
   isApproved = isApproved == null ? false : (bool)isApproved;
   creationDate = creationDate == null ? DateTime.Now : (DateTime)creationDate;
   lastLoginDate = lastLoginDate == null ? DateTime.Now : (DateTime)lastLoginDate;
   lastActivityDate = lastActivityDate == null ? DateTime.Now : (DateTime)lastActivityDate;
   lastPasswordChangedDate = lastPasswordChangedDate == null ? DateTime.Now : (DateTime)lastPasswordChangedDate;

   MembershipUser user = new MembershipUser(this, (string)userName, (string)userId, (string)eMail, (string)passwordQuestion, comment, (bool)isApproved, (DateTime)creationDate, (DateTime)lastLoginDate, (DateTime)lastActivityDate, (DateTime)lastPasswordChangedDate);
   return user;
  }

  private MembershipUserCollection ADAMFindUsers(string Filter, DirectoryEntry SearchRoot, int PageIndex, int PageSize, out int Count)
  {

   Count = 0;

   SearchResultCollection dsUserSearchResult = null;
   MembershipUserCollection RetVal = new MembershipUserCollection();

   DirectorySearcher dsUser = new DirectorySearcher(SearchRoot, Filter);
   dsUser.SearchScope = SearchScope.OneLevel;

   try
   {
    if ((dsUserSearchResult = dsUser.FindAll()) != null)
    {
     Count = dsUserSearchResult.Count;
     if (PageSize == PAGESIZE_NOPAGING) PageSize = Count;
     //int StartIndex = (PageIndex - 1) * PageSize;
     int StartIndex = PageIndex * PageSize;
     int EndIndex = ((PageIndex + 1) * PageSize) - 1;
     if (EndIndex > (Count - 1))
     {
      EndIndex = Count - 1;
     }

     MembershipUser user;
     DirectoryEntry deMembershipUser;
     for (int Idx = StartIndex; Idx <= EndIndex; Idx++)
     {
      
      deMembershipUser = dsUserSearchResult[Idx].GetDirectoryEntry();
      deMembershipUser.RefreshCache();

      /*
      userName = deMembershipUser.Properties[_ADAMPropUserName].Value;
      userId = deMembershipUser.Properties[_ADAMPropUserId].Value;
      eMail = deMembershipUser.Properties[_ADAMPropEMail].Value;
      passwordQuestion = deMembershipUser.Properties[_ADAMPropPasswordQuestion].Value;
      comment = string.Empty;
      isApproved = deMembershipUser.Properties[_ADAMPropIsApproved].Value;
      creationDate = deMembershipUser.Properties[_ADAMPropUserCreationTimestamp].Value;
      lastLoginDate = deMembershipUser.Properties[_ADAMPropLastLoginTimestamp].Value;
      lastActivityDate = deMembershipUser.Properties[_ADAMPropLastActivityTimestamp].Value;
      lastPasswordChangedDate = deMembershipUser.Properties[_ADAMPropLastPasswordChangeTimestamp].Value;

      userName = userName == null?null:(string)userName;
      userId = userId == null?string.Empty:new Guid((byte[])userId).ToString();;
      eMail = eMail == null?null:(string)eMail;
      passwordQuestion = passwordQuestion == null?null:(string)passwordQuestion;
      isApproved = isApproved == null?false:(bool)isApproved;
      creationDate = creationDate == null?DateTime.Now:(DateTime)creationDate;
      lastLoginDate = lastLoginDate == null?DateTime.Now:(DateTime)lastLoginDate;
      lastActivityDate = lastActivityDate == null?DateTime.Now:(DateTime)lastActivityDate;
      lastPasswordChangedDate = lastPasswordChangedDate == null?DateTime.Now:(DateTime)lastPasswordChangedDate;
      
      user = new MembershipUser(this,(string)userName,(string)userId,(string)eMail,(string)passwordQuestion,comment,(bool)isApproved,(DateTime)creationDate,(DateTime)lastLoginDate,(DateTime)lastActivityDate,(DateTime)lastPasswordChangedDate);
      */
      user = this.ConstructMembershipUserFromDirectoryEntry(deMembershipUser);
      RetVal.Add(user);
      
      deMembershipUser.Close();
     }

    }

    return RetVal;
   }
   catch (Exception Ex)
   {
    throw new ApplicationException("Error retrieving users", Ex);
   }

 

  }

  private bool ADAMFindUser(string Filter, DirectoryEntry SearchRoot, out DirectoryEntry deUser)
  {

   SearchResult dsUserSearchResult = null;
   deUser = null;


   DirectorySearcher dsUser = new DirectorySearcher(SearchRoot, Filter);
   dsUser.SearchScope = SearchScope.OneLevel;

   if ((dsUserSearchResult = dsUser.FindOne()) != null)
   {
    deUser = dsUserSearchResult.GetDirectoryEntry();
    return true;
   }
   else
    return false;

  }

  private string GetReadablePassword(byte[] StoredPassword)
  {
   System.Text.UnicodeEncoding ue = new System.Text.UnicodeEncoding();
   string RetVal = null;

   switch (_PasswordFormat)
   {
    case MembershipPasswordFormat.Clear:
     RetVal = ue.GetString(StoredPassword);
     break;

    case MembershipPasswordFormat.Hashed:
     throw new ApplicationException("Password cannot be recovered from a hashed format");


    case MembershipPasswordFormat.Encrypted:
     TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider();
     tripleDes.Key = _DecryptionKey;
     tripleDes.IV = new byte[8];

     CryptoStream cryptoStream = new CryptoStream(new MemoryStream(StoredPassword), tripleDes.CreateDecryptor(), CryptoStreamMode.Read);
     MemoryStream msPasswordDec = new MemoryStream();
     int BytesRead = 0;
     byte[] Buffer = new byte[32];
     while ((BytesRead = cryptoStream.Read(Buffer, 0, 32)) > 0)
     {
      msPasswordDec.Write(Buffer, 0, BytesRead);
     }
     cryptoStream.Close();

     RetVal = ue.GetString(msPasswordDec.ToArray());
     msPasswordDec.Close();
     break;
   }
   return RetVal;
  }
  private byte[] ConvertPasswordForStorage(string Password)
  {
   System.Text.UnicodeEncoding ue = new System.Text.UnicodeEncoding();
   byte[] uePassword = ue.GetBytes(Password);
   byte[] RetVal = null;

   switch (_PasswordFormat)
   {
    case MembershipPasswordFormat.Clear:
     RetVal = uePassword;
     break;
    case MembershipPasswordFormat.Hashed:

     HMACSHA1 SHA1KeyedHasher = new HMACSHA1();
     SHA1KeyedHasher.Key = _ValidationKey;
     RetVal = SHA1KeyedHasher.ComputeHash(uePassword);
     break;
    case MembershipPasswordFormat.Encrypted:
     TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider();
     tripleDes.Key = _DecryptionKey;
     tripleDes.IV = new byte[8];
     MemoryStream mStreamEnc = new MemoryStream();
     CryptoStream cryptoStream = new CryptoStream(mStreamEnc, tripleDes.CreateEncryptor(), CryptoStreamMode.Write);


     cryptoStream.Write(uePassword, 0, uePassword.Length);
     cryptoStream.FlushFinalBlock();
     RetVal = mStreamEnc.ToArray();
     cryptoStream.Close();
     break;

   }
   return RetVal;
  }

  private bool ComparePassword(string Password, byte[] StoredPassword)
  {
   UnicodeEncoding ue = new UnicodeEncoding();
   bool RetVal = false;
   switch (_PasswordFormat)
   {
    case MembershipPasswordFormat.Clear:
     RetVal = (ue.GetString(StoredPassword) == Password);
     break;
    case MembershipPasswordFormat.Hashed:
     byte[] uePassword = ue.GetBytes(Password);
     HMACSHA1 SHA1KeyedHasher = new HMACSHA1();
     SHA1KeyedHasher.Key = _ValidationKey;
     byte[] PasswordHash = SHA1KeyedHasher.ComputeHash(uePassword);

     if (PasswordHash.Length != StoredPassword.Length)
      RetVal = false;
     else
     {
      int idx = 0;
      for (idx = 0; (idx < StoredPassword.Length) && (PasswordHash[idx] == StoredPassword[idx]); idx++) ;
      RetVal = (idx == StoredPassword.Length) ? true : false;
     }
     break;
    case MembershipPasswordFormat.Encrypted:
     TripleDESCryptoServiceProvider tripleDes = new TripleDESCryptoServiceProvider();
     tripleDes.Key = _DecryptionKey;
     tripleDes.IV = new byte[8];

     CryptoStream cryptoStream = new CryptoStream(new MemoryStream(StoredPassword), tripleDes.CreateDecryptor(), CryptoStreamMode.Read);
     MemoryStream msPasswordDec = new MemoryStream();
     int BytesRead = 0;
     byte[] Buffer = new byte[32];
     while ((BytesRead = cryptoStream.Read(Buffer, 0, 32)) > 0)
     {
      msPasswordDec.Write(Buffer, 0, BytesRead);
     }
     cryptoStream.Close();

     RetVal = (ue.GetString(msPasswordDec.ToArray()) == Password);
     msPasswordDec.Close();
     break;
   }

   return RetVal;
  }

  private byte[] HexStringToByteArray(string HexString)
  {
   byte[] Result = new byte[HexString.Length / 2];
   for (int idx = 0; idx < Result.Length; idx++)
    Result[idx] = Convert.ToByte(HexString.Substring(idx * 2, 2), 16);
   return Result;
  }

 }
}