Bug 222074 Streamline password management
diff --git a/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/HighPriorityModule.java b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/HighPriorityModule.java
index bee36a2..8274c51 100644
--- a/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/HighPriorityModule.java
+++ b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/HighPriorityModule.java
@@ -21,16 +21,11 @@
 
 	public final static PBEKeySpec PASSWORD = new PBEKeySpec("HighPriorityPassword".toCharArray());
 
-	public PBEKeySpec login(IPreferencesContainer controller) {
+	public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
 		return PASSWORD;
 	}
 
-	public boolean changePassword(Exception e, IPreferencesContainer container) {
+	public boolean retryOnError(Exception e, IPreferencesContainer container) {
 		return false;
 	}
-
-	public void logout(IPreferencesContainer container) {
-		// noting to do
-	}
-
 }
diff --git a/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/LowPriorityModule.java b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/LowPriorityModule.java
index 1573917..3b44391 100644
--- a/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/LowPriorityModule.java
+++ b/bundles/org.eclipse.equinox.security.tests/src/org/eclipse/equinox/internal/security/tests/storage/LowPriorityModule.java
@@ -21,15 +21,11 @@
 
 	public final static PBEKeySpec PASSWORD = new PBEKeySpec("LowPriorityPassword".toCharArray());
 
-	public PBEKeySpec login(IPreferencesContainer controller) {
+	public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
 		return PASSWORD;
 	}
 
-	public boolean changePassword(Exception e, IPreferencesContainer container) {
+	public boolean retryOnError(Exception e, IPreferencesContainer container) {
 		return false;
 	}
-
-	public void logout(IPreferencesContainer container) {
-		// noting to do
-	}
 }
diff --git a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/SecUIMessages.java b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/SecUIMessages.java
index 148208d..8f0d606 100644
--- a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/SecUIMessages.java
+++ b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/SecUIMessages.java
@@ -27,11 +27,13 @@
 	public static String buttonLogin;
 	public static String buttonExit;
 	public static String messageLogin;
+	public static String messageLoginChange;
 	public static String messageEmptyPassword;
 	public static String messageNoMatch;
 	public static String labelPassword;
 	public static String labelConfirm;
 	public static String dialogTitle;
+	public static String passwordChangeTitle;
 	public static String showPassword;
 	public static String noDigestPassword;
 
@@ -130,6 +132,7 @@
 	public static String wizardEncode;
 	public static String wizardEncodeLabel;
 	public static String wizardDecodeWarning;
+	public static String wizardSwitchError;
 	public static String wizardDoneTitle;
 	public static String wizardDone;
 
diff --git a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/messages.properties b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/messages.properties
index d7556be..6376aa5 100644
--- a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/messages.properties
+++ b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/nls/messages.properties
@@ -19,6 +19,7 @@
 
 ## Login dialog
 messageLogin = Please enter the secure storage password
+messageLoginChange = Password change: please enter the NEW secure storage password
 messageEmptyPassword = Password can not be empty
 messageNoMatch = Password and confirmation characters did not match
 labelPassword = &Password:
@@ -26,6 +27,7 @@
 buttonLogin = Login
 buttonExit = Exit
 dialogTitle = Secure storage login
+passwordChangeTitle = Enter new password for the secure storage
 showPassword = &Show password
 noDigestPassword = The message digest algorithm \"{0}\" is not available.
 
@@ -36,7 +38,7 @@
 
 ## Properties pages
 selectCipher = &Select encryption algorithm to use in new storages:
-changePasswordButton = &Change password
+changePasswordButton = &Change password / Re-encrypt
 deleteButton = D&elete
 logoutButton = &Logout
 providersTable = Available password providers:
@@ -124,6 +126,7 @@
 wizardEncode = NEW password: the values will be encrypted.
 wizardEncodeLabel = At this point if you are prompted for a password, enter the NEW password.
 wizardDecodeWarning = A error occurred while decrypting stored values (see log for details), those values might become inaccessible.\nDo you want to cancel password change?
+wizardSwitchError = A error occurred while creating new password. See error log for details. 
 wizardDoneTitle = Complete
 wizardDone = Password change complete.
 
diff --git a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/ChangePasswordWizardDialog.java b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/ChangePasswordWizardDialog.java
index a3c05fc..797b9a6 100644
--- a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/ChangePasswordWizardDialog.java
+++ b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/ChangePasswordWizardDialog.java
@@ -44,7 +44,7 @@
 		if (currentPage instanceof ChangePasswordWizard.DecodePage) { // decrypt
 			if (!reEncrypter.decrypt()) {
 				MessageBox messageBox = new MessageBox(getShell(), SWT.YES | SWT.NO | SWT.ICON_WARNING);
-				messageBox.setText(SecUIMessages.wizardDecodeLabel);
+				messageBox.setText(SecUIMessages.changePasswordWizardTitle);
 				messageBox.setMessage(SecUIMessages.wizardDecodeWarning);
 				if (messageBox.open() == SWT.YES) {
 					setReturnCode(CANCEL);
@@ -53,6 +53,14 @@
 				}
 			}
 		} else if (currentPage instanceof ChangePasswordWizard.EncodePage) { // encrypt
+			if (!reEncrypter.switchToNewPassword()) {
+				MessageBox messageBox = new MessageBox(getShell(), SWT.OK | SWT.ICON_ERROR);
+				messageBox.setText(SecUIMessages.changePasswordWizardTitle);
+				messageBox.setMessage(SecUIMessages.wizardSwitchError);
+				messageBox.open();
+				close();
+				return;
+			}
 			reEncrypter.encrypt();
 			recodeDone = true;
 		}
diff --git a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/DefaultPasswordProvider.java b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/DefaultPasswordProvider.java
index ca6bd7a..c3d1912 100644
--- a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/DefaultPasswordProvider.java
+++ b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/DefaultPasswordProvider.java
@@ -24,35 +24,18 @@
  */
 public class DefaultPasswordProvider extends PasswordProvider {
 
-	private PBEKeySpec password = null;
+	public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
+		boolean newPassword = ((passwordType & CREATE_NEW_PASSWORD) != 0);
+		boolean passwordChange = ((passwordType & PASSWORD_CHANGE) != 0);
 
-	synchronized public PBEKeySpec login(IPreferencesContainer container) {
-		if (password != null)
-			return password;
 		String location = container.getLocation().getFile();
-		StorageLoginDialog loginDialog = new StorageLoginDialog(confirmPassword(container), location);
+		StorageLoginDialog loginDialog = new StorageLoginDialog(newPassword, passwordChange, location);
 		if (loginDialog.open() == Window.OK)
-			password = loginDialog.getGeneratedPassword();
-		return password;
+			return loginDialog.getGeneratedPassword();
+		return null;
 	}
 
-	synchronized public void logout(IPreferencesContainer container) {
-		if (password == null)
-			return;
-		password.clearPassword();
-		password = null;
-	}
-
-	private boolean confirmPassword(IPreferencesContainer container) {
-		if (!container.hasOption(IProviderHints.NEW_PASSWORD))
-			return false;
-		Object confirmationHint = container.getOption(IProviderHints.NEW_PASSWORD);
-		if (confirmationHint != null && confirmationHint instanceof Boolean)
-			return ((Boolean) confirmationHint).booleanValue();
-		return false;
-	}
-
-	public boolean changePassword(Exception e, IPreferencesContainer container) {
+	public boolean retryOnError(Exception e, IPreferencesContainer container) {
 		boolean canPrompt = true;
 		if (container.hasOption(IProviderHints.PROMPT_USER)) {
 			Object promptHint = container.getOption(IProviderHints.PROMPT_USER);
diff --git a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/StorageLoginDialog.java b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/StorageLoginDialog.java
index d3556a2..811ec52 100644
--- a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/StorageLoginDialog.java
+++ b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/StorageLoginDialog.java
@@ -41,11 +41,13 @@
 	protected PBEKeySpec generatedPassword;
 
 	final protected boolean confirmPassword;
+	final protected boolean passwordChange;
 	final protected String location;
 
-	public StorageLoginDialog(boolean confirmPassword, String location) {
+	public StorageLoginDialog(boolean confirmPassword, boolean passwordChange, String location) {
 		super(null);
 		this.confirmPassword = confirmPassword;
+		this.passwordChange = passwordChange;
 		this.location = location;
 	}
 
@@ -74,7 +76,7 @@
 	protected void configureShell(Shell shell) {
 		super.configureShell(shell);
 		if (location == null)
-			shell.setText(SecUIMessages.dialogTitle);
+			shell.setText(passwordChange ? SecUIMessages.passwordChangeTitle : SecUIMessages.dialogTitle);
 		else
 			shell.setText(location);
 
@@ -84,7 +86,7 @@
 	protected Control createDialogArea(Composite parent) {
 		Composite composite = (Composite) super.createDialogArea(parent);
 
-		setMessage(SecUIMessages.messageLogin);
+		setMessage(passwordChange ? SecUIMessages.messageLoginChange : SecUIMessages.messageLogin);
 
 		new Label(composite, SWT.LEFT).setText(SecUIMessages.labelPassword);
 		password = new Text(composite, SWT.LEFT | SWT.BORDER);
@@ -153,7 +155,7 @@
 				return false;
 			}
 		}
-		setMessage(SecUIMessages.messageLogin, IMessageProvider.NONE);
+		setMessage(passwordChange ? SecUIMessages.messageLoginChange : SecUIMessages.messageLogin, IMessageProvider.NONE);
 		return true;
 	}
 
diff --git a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/view/ValuesView.java b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/view/ValuesView.java
index 151c037..09e0549 100644
--- a/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/view/ValuesView.java
+++ b/bundles/org.eclipse.equinox.security.ui/src/org/eclipse/equinox/internal/security/ui/storage/view/ValuesView.java
@@ -32,7 +32,7 @@
 	/**
 	 * Line to show for encrypted values
 	 */
-	private final static String ENCRYPTED_SUBSTITUTE = "••••••••••"; //$NON-NLS-1$
+	private final static String ENCRYPTED_SUBSTITUTE = "**********"; //$NON-NLS-1$
 
 	protected SecurePreferencesView parentView;
 	protected TableViewer tableViewer;
diff --git a/bundles/org.eclipse.equinox.security.win32.x86/src/org/eclipse/equinox/internal/security/win32/WinCrypto.java b/bundles/org.eclipse.equinox.security.win32.x86/src/org/eclipse/equinox/internal/security/win32/WinCrypto.java
index 724fe80..bc8811e 100644
--- a/bundles/org.eclipse.equinox.security.win32.x86/src/org/eclipse/equinox/internal/security/win32/WinCrypto.java
+++ b/bundles/org.eclipse.equinox.security.win32.x86/src/org/eclipse/equinox/internal/security/win32/WinCrypto.java
@@ -47,7 +47,7 @@
 	 */
 	private final static int PASSWORD_LENGTH = 250;
 
-	public PBEKeySpec login(IPreferencesContainer container) {
+	public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
 		byte[] encryptedPassord = getEncryptedPassword(container);
 		if (encryptedPassord != null) {
 			byte[] decryptedPassword = windecrypt(encryptedPassord);
@@ -128,7 +128,7 @@
 		return true;
 	}
 
-	public boolean changePassword(Exception e, IPreferencesContainer container) {
+	public boolean retryOnError(Exception e, IPreferencesContainer container) {
 		// It would be rather dangerous to allow this password to be changed
 		// as it would permanently trash all entries in the secure storage.
 		// Rather applications using get...() should handle exceptions and offer to overwrite 
@@ -136,8 +136,4 @@
 		return false;
 	}
 
-	public void logout(IPreferencesContainer container) {
-		// nothing to do
-	}
-
 }
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java
index 1ae8370..cd22598 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderModuleExt.java
@@ -31,16 +31,12 @@
 		return moduleID;
 	}
 
-	public PBEKeySpec login(IPreferencesContainer container) {
-		return providerModule.login(container);
-	}
-
-	public void logout(IPreferencesContainer container) {
-		providerModule.logout(container);
+	public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType) {
+		return providerModule.getPassword(container, passwordType);
 	}
 
 	public boolean changePassword(Exception e, IPreferencesContainer controller) {
-		return providerModule.changePassword(e, controller);
+		return providerModule.retryOnError(e, controller);
 	}
 
 }
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java
index 7d0fef0..6661777 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/PasswordProviderSelector.java
@@ -115,21 +115,6 @@
 		return allAvailableModules;
 	}
 
-	/**
-	 * Let's all instantiated modules to clear cached passwords. Passwords
-	 * are cached in open preferences and might be cached in the password
-	 * providers.
-	 */
-	public void logout() {
-		SecurePreferencesMapper.clearCaches();
-		synchronized (modules) {
-			for (Iterator i = modules.values().iterator(); i.hasNext();) {
-				PasswordProviderModuleExt module = (PasswordProviderModuleExt) i.next();
-				module.logout(null);
-			}
-		}
-	}
-
 	public PasswordProviderModuleExt findStorageModule(String expectedID) throws StorageException {
 		if (expectedID != null)
 			expectedID = expectedID.toLowerCase(); // ID is case-insensitive
@@ -203,7 +188,11 @@
 	private void clearCaches() {
 		synchronized (modules) {
 			modules.clear();
-			SecurePreferencesMapper.clearCaches();
+			// If module was removed, clear its entry from the password cache.
+			// The code below clears all entries for simplicity, in future this
+			// can be made more limiting if a scenario exists where module
+			// removal/addition is a frequent event.
+			SecurePreferencesMapper.clearPasswordCache();
 		}
 	}
 }
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferences.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferences.java
index 837e23d..976bbdd 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferences.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferences.java
@@ -14,26 +14,13 @@
 import java.util.*;
 import javax.crypto.BadPaddingException;
 import javax.crypto.IllegalBlockSizeException;
-import javax.crypto.spec.PBEKeySpec;
 import org.eclipse.core.runtime.IPath;
 import org.eclipse.equinox.internal.security.auth.nls.SecAuthMessages;
 import org.eclipse.equinox.security.storage.StorageException;
-import org.eclipse.equinox.security.storage.provider.IPreferencesContainer;
-import org.eclipse.equinox.security.storage.provider.IProviderHints;
 import org.eclipse.osgi.util.NLS;
 
 public class SecurePreferences {
 
-	/**
-	 * Pseudo-module ID to use when encryption is done with the default password.
-	 */
-	protected final static String DEFAULT_PASSWORD_ID = "org.eclipse.equinox.security.noModule"; //$NON-NLS-1$
-
-	/**
-	 * Maximum unsuccessful decryption attempts per operation
-	 */
-	static protected final int MAX_ATTEMPTS = 20;
-
 	private static final String PATH_SEPARATOR = String.valueOf(IPath.SEPARATOR);
 
 	private static final String[] EMPTY_STRING_ARRAY = new String[0];
@@ -227,16 +214,11 @@
 			return;
 		}
 
-		PasswordExt passwordExt = getPassword(container);
+		PasswordExt passwordExt = getRoot().getPassword(null, container, true);
+		if (passwordExt == null)
+			throw new StorageException(StorageException.NO_PASSWORD, SecAuthMessages.loginNoPassword);
 
-		CryptoData encryptedValue;
-		try {
-			encryptedValue = getRoot().getCipher().encrypt(passwordExt, value.getBytes());
-		} catch (StorageException e) {
-			RuntimeException exception = new IllegalStateException();
-			exception.initCause(e);
-			throw exception;
-		}
+		CryptoData encryptedValue = getRoot().getCipher().encrypt(getRoot().getPassword(null, container, true), value.getBytes());
 		internalPut(key, encryptedValue.toString());
 		markModified();
 	}
@@ -255,8 +237,12 @@
 			return new String(data.getData());
 		}
 
+		PasswordExt passwordExt = getRoot().getPassword(moduleID, container, false);
+		if (passwordExt == null)
+			throw new StorageException(StorageException.NO_PASSWORD, SecAuthMessages.loginNoPassword);
+
 		try {
-			byte[] clearText = getRoot().getCipher().decrypt(getPassword(moduleID, container), data);
+			byte[] clearText = getRoot().getCipher().decrypt(passwordExt, data);
 			return new String(clearText);
 		} catch (IllegalBlockSizeException e) { // invalid password?
 			throw new StorageException(StorageException.DECRYPTION_ERROR, e);
@@ -265,59 +251,6 @@
 		}
 	}
 
-	///////////////////////////////////////////////////////////////////////////////////////////////////////
-	// Password handling routines
-
-	/**
-	 * Provides password for a new entry using:
-	 * 1) default password, if any
-	 * 2a) if options specify usage of specific module, that module is polled to produce password
-	 * 2b) otherwise, password provider with highest priority is used to produce password
-	 */
-	private PasswordExt getPassword(SecurePreferencesContainer container) throws StorageException {
-		PasswordExt defaultPassword = getDefaultPassword(container);
-		if (defaultPassword != null)
-			return defaultPassword;
-
-		String moduleID = null;
-		if (container.hasOption(IProviderHints.REQUIRED_MODULE_ID)) {
-			Object idHint = container.getOption(IProviderHints.REQUIRED_MODULE_ID);
-			if (idHint instanceof String)
-				moduleID = (String) idHint;
-		}
-		return getRoot().getModulePassword(moduleID, container);
-	}
-
-	/**
-	 * Provides password using specified password provider module
-	 */
-	private PasswordExt getPassword(String moduleID, SecurePreferencesContainer container) throws StorageException {
-		if (moduleID == null)
-			throw new StorageException(StorageException.NO_SECURE_MODULE, SecAuthMessages.invalidEntryFormat);
-		if (DEFAULT_PASSWORD_ID.equals(moduleID)) {
-			PasswordExt defaultPassword = getDefaultPassword(container);
-			if (defaultPassword != null)
-				return defaultPassword;
-			throw new StorageException(StorageException.NO_SECURE_MODULE, SecAuthMessages.noDefaultPassword);
-		}
-
-		return getRoot().getModulePassword(moduleID, container);
-	}
-
-	/**
-	 * Retrieves default password from options, if any
-	 */
-	private PasswordExt getDefaultPassword(IPreferencesContainer container) {
-		if (container.hasOption(IProviderHints.DEFAULT_PASSWORD)) {
-			Object passwordHint = container.getOption(IProviderHints.DEFAULT_PASSWORD);
-			if (passwordHint instanceof PBEKeySpec)
-				return new PasswordExt((PBEKeySpec) passwordHint, DEFAULT_PASSWORD_ID);
-		}
-		return null;
-	}
-
-	///////////////////////////////////////////////////////////////////////////////////////////////////////
-
 	synchronized protected void internalPut(String key, String value) {
 		if (values == null)
 			values = new HashMap(5);
@@ -525,17 +458,7 @@
 		return (moduleID != null);
 	}
 
-	public void clearPasswordVerification(SecurePreferencesContainer container) {
-		PasswordExt defaultPassword = getDefaultPassword(container);
-		if (defaultPassword != null)
-			return;
-
-		String moduleID = null;
-		if (container.hasOption(IProviderHints.REQUIRED_MODULE_ID)) {
-			Object idHint = container.getOption(IProviderHints.REQUIRED_MODULE_ID);
-			if (idHint instanceof String)
-				moduleID = (String) idHint;
-		}
-		getRoot().clearPasswordVerification(moduleID, container);
+	public boolean passwordChanging(SecurePreferencesContainer container) {
+		return getRoot().onChangePassword(container);
 	}
 }
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesMapper.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesMapper.java
index 32c9744..062bf1f 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesMapper.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesMapper.java
@@ -85,8 +85,7 @@
 		}
 	}
 
-	// Not an API; links to the dynamic bundles
-	static public void clearCaches() {
+	static public void clearPasswordCache() {
 		synchronized (preferences) {
 			for (Iterator i = preferences.values().iterator(); i.hasNext();) {
 				SecurePreferencesRoot provider = (SecurePreferencesRoot) i.next();
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java
index b4404ae..541b75b 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesRoot.java
@@ -16,10 +16,12 @@
 import javax.crypto.BadPaddingException;
 import javax.crypto.IllegalBlockSizeException;
 import javax.crypto.spec.PBEKeySpec;
+import org.eclipse.equinox.internal.security.auth.AuthPlugin;
 import org.eclipse.equinox.internal.security.auth.nls.SecAuthMessages;
 import org.eclipse.equinox.internal.security.storage.friends.IStorageConstants;
 import org.eclipse.equinox.security.storage.StorageException;
-import org.eclipse.equinox.security.storage.provider.IProviderHints;
+import org.eclipse.equinox.security.storage.provider.*;
+import org.eclipse.osgi.util.NLS;
 
 /**
  * Root secure preference node. In addition to usual things it stores location, modified
@@ -55,6 +57,16 @@
 	 */
 	private final static String PASSWORD_VERIFICATION_SAMPLE = "-> brown fox jumped over lazy dog <-"; //$NON-NLS-1$
 
+	/**
+	 * Pseudo-module ID to use when encryption is done with the default password.
+	 */
+	protected final static String DEFAULT_PASSWORD_ID = "org.eclipse.equinox.security.noModule"; //$NON-NLS-1$
+
+	/**
+	 * Maximum unsuccessful decryption attempts per operation
+	 */
+	static protected final int MAX_ATTEMPTS = 20;
+
 	final private URL location;
 
 	private boolean modified = false;
@@ -164,7 +176,32 @@
 		}
 	}
 
-	public PasswordExt getModulePassword(String moduleID, SecurePreferencesContainer container) throws StorageException {
+	/**
+	 * Provides password for a new entry using:
+	 * 1) default password, if any
+	 * 2a) if options specify usage of specific module, that module is polled to produce password
+	 * 2b) otherwise, password provider with highest priority is used to produce password
+	 */
+	public PasswordExt getPassword(String moduleID, IPreferencesContainer container, boolean encryption) throws StorageException {
+		if (encryption) { // provides password for a new entry
+			PasswordExt defaultPassword = getDefaultPassword(container);
+			if (defaultPassword != null)
+				return defaultPassword;
+			moduleID = getDefaultModuleID(container);
+		} else { // provides password for previously encrypted entry using its specified password provider module
+			if (moduleID == null)
+				throw new StorageException(StorageException.NO_SECURE_MODULE, SecAuthMessages.invalidEntryFormat);
+			if (DEFAULT_PASSWORD_ID.equals(moduleID)) { // was default password used?
+				PasswordExt defaultPassword = getDefaultPassword(container);
+				if (defaultPassword != null)
+					return defaultPassword;
+				throw new StorageException(StorageException.NO_SECURE_MODULE, SecAuthMessages.noDefaultPassword);
+			}
+		}
+		return getModulePassword(moduleID, container);
+	}
+
+	private PasswordExt getModulePassword(String moduleID, IPreferencesContainer container) throws StorageException {
 		if (DEFAULT_PASSWORD_ID.equals(moduleID)) // this should never happen but add this check just in case
 			throw new StorageException(StorageException.NO_PASSWORD, SecAuthMessages.loginNoPassword);
 
@@ -177,13 +214,13 @@
 			// is there password verification string already?
 			SecurePreferences node = node(PASSWORD_VERIFICATION_NODE);
 			boolean newPassword = !node.hasKey(key);
-			container.setOption(IProviderHints.NEW_PASSWORD, new Boolean(newPassword));
+			int passwordType = newPassword ? PasswordProvider.CREATE_NEW_PASSWORD : 0;
 
 			boolean validPassword = false;
 			PasswordExt passwordExt = null;
 
 			for (int i = 0; i < MAX_ATTEMPTS; i++) {
-				PBEKeySpec password = moduleExt.login(container);
+				PBEKeySpec password = moduleExt.getPassword(container, passwordType);
 				if (password == null)
 					return null;
 				passwordExt = new PasswordExt(password, key);
@@ -206,11 +243,9 @@
 				} catch (IllegalBlockSizeException e) {
 					if (!moduleExt.changePassword(e, container))
 						break;
-					moduleExt.logout(container);
 				} catch (BadPaddingException e) {
 					if (!moduleExt.changePassword(e, container))
 						break;
-					moduleExt.logout(container);
 				}
 			}
 			if (validPassword) {
@@ -221,22 +256,78 @@
 		}
 	}
 
+	/**
+	 * Retrieves default password from options, if any
+	 */
+	private PasswordExt getDefaultPassword(IPreferencesContainer container) {
+		if (container.hasOption(IProviderHints.DEFAULT_PASSWORD)) {
+			Object passwordHint = container.getOption(IProviderHints.DEFAULT_PASSWORD);
+			if (passwordHint instanceof PBEKeySpec)
+				return new PasswordExt((PBEKeySpec) passwordHint, DEFAULT_PASSWORD_ID);
+		}
+		return null;
+	}
+
+	/**
+	 * Retrieves requested module ID from options, if any
+	 */
+	private String getDefaultModuleID(IPreferencesContainer container) {
+		if (container.hasOption(IProviderHints.REQUIRED_MODULE_ID)) {
+			Object idHint = container.getOption(IProviderHints.REQUIRED_MODULE_ID);
+			if (idHint instanceof String)
+				return (String) idHint;
+		}
+		return null;
+	}
+
+	public boolean onChangePassword(IPreferencesContainer container) {
+		// validation: can't change externally supplied password
+		PasswordExt defaultPassword = getDefaultPassword(container);
+		if (defaultPassword != null)
+			return false;
+		String moduleID = getDefaultModuleID(container);
+
+		// validation: must have a password module
+		PasswordProviderModuleExt moduleExt;
+		try {
+			moduleExt = PasswordProviderSelector.getInstance().findStorageModule(moduleID);
+		} catch (StorageException e) {
+			return false; // no module -> nothing to do
+		}
+
+		// obtain new password first
+		int passwordType = PasswordProvider.CREATE_NEW_PASSWORD | PasswordProvider.PASSWORD_CHANGE;
+		PBEKeySpec password = moduleExt.getPassword(container, passwordType);
+		if (password == null)
+			return false;
+
+		synchronized (passwordCache) { // we are good to go, lock into single processing thread
+			// create verification node
+			String key = moduleExt.getID();
+			PasswordExt passwordExt = new PasswordExt(password, key);
+			CryptoData encryptedValue;
+			try {
+				encryptedValue = getCipher().encrypt(passwordExt, PASSWORD_VERIFICATION_SAMPLE.getBytes());
+			} catch (StorageException e) {
+				String msg = NLS.bind(SecAuthMessages.encryptingError, key, PASSWORD_VERIFICATION_NODE);
+				AuthPlugin.getDefault().logError(msg, e);
+				return false;
+			}
+
+			SecurePreferences node = node(PASSWORD_VERIFICATION_NODE);
+			node.internalPut(key, encryptedValue.toString());
+			markModified();
+
+			// store password in the memory cache
+			passwordCache.put(key, passwordExt);
+		}
+		return true;
+	}
+
 	public void clearPasswordCache() {
 		synchronized (passwordCache) {
 			passwordCache.clear();
 		}
 	}
 
-	public void clearPasswordVerification(String moduleID, SecurePreferencesContainer container) {
-		PasswordProviderModuleExt moduleExt;
-		try {
-			moduleExt = PasswordProviderSelector.getInstance().findStorageModule(moduleID);
-		} catch (StorageException e) {
-			return; // no module -> nothing to do
-		}
-		String key = moduleExt.getID();
-		SecurePreferences node = node(PASSWORD_VERIFICATION_NODE);
-		if (node.hasKey(key))
-			node.remove(key);
-	}
 }
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesWrapper.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesWrapper.java
index fe29024..931dc20 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesWrapper.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/SecurePreferencesWrapper.java
@@ -162,9 +162,7 @@
 		return node.isEncrypted(key);
 	}
 
-	/////////////////////////////////////////////////////////////////////////////////////
-	// really internal; used for password reset; don't use
-	public void clearPasswordVerification() {
-		node.clearPasswordVerification(container);
+	public boolean passwordChanging() {
+		return node.passwordChanging(container);
 	}
 }
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java
index b529dd3..02341f2 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/InternalExchangeUtils.java
@@ -38,7 +38,7 @@
 	 * Clears cached passwords from the open storages and password providers.
 	 */
 	static public void passwordProvidersLogout() {
-		PasswordProviderSelector.getInstance().logout();
+		SecurePreferencesMapper.clearPasswordCache();
 	}
 
 	/**
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/ReEncrypter.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/ReEncrypter.java
index 2bf08c6..f20c5e9 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/ReEncrypter.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/internal/security/storage/friends/ReEncrypter.java
@@ -86,15 +86,22 @@
 	}
 
 	/**
-	 * The method will encrypt all data from the memory structure created by
-	 * decrypt using current passwords and providers. The original encrypted
-	 * data will be overwritten.
+	 * The method try to create new password. 
+	 * <p>
+	 * <strong>Note</strong> that after the successful completion of this method the secure storage has
+	 * new verification string and previously decoded values <b>must</b> be added via encrypt() method 
+	 * or they will become unavailable via conventional APIs.
+	 * </p>
+	 */
+	public boolean switchToNewPassword() {
+		return ((SecurePreferencesWrapper) root).passwordChanging();
+	}
+
+	/**
+	 * The method will encrypt all data from the memory structure created by decrypt using current 
+	 * passwords and providers. The original encrypted data will be overwritten.
 	 */
 	public boolean encrypt() {
-		InternalExchangeUtils.passwordProvidersLogout();
-		// TBD let providers know that this is a new password?
-		((SecurePreferencesWrapper) root).clearPasswordVerification();
-
 		boolean result = true;
 		for (Iterator i = elements.iterator(); i.hasNext();) {
 			TmpElement element = (TmpElement) i.next();
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/IProviderHints.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/IProviderHints.java
index e401ac0..44148bf 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/IProviderHints.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/IProviderHints.java
@@ -48,10 +48,4 @@
 	 * Storage will use this password. Expected value: {@link PBEKeySpec}.
 	 */
 	static final public String DEFAULT_PASSWORD = "org.eclipse.equinox.security.storage.defaultPassword"; //$NON-NLS-1$
-
-	/**
-	 * Specifies if a new password is being requested. Expected value: {@link Boolean}.
-	 */
-	static final public String NEW_PASSWORD = "org.eclipse.equinox.security.storage.newPassword"; //$NON-NLS-1$
-
 }
diff --git a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/PasswordProvider.java b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/PasswordProvider.java
index f832231..d330d8a 100644
--- a/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/PasswordProvider.java
+++ b/bundles/org.eclipse.equinox.security/src/org/eclipse/equinox/security/storage/provider/PasswordProvider.java
@@ -28,26 +28,31 @@
 abstract public class PasswordProvider {
 
 	/**
+	 * Bit mask for the password type field of the {@link #getPassword(IPreferencesContainer, int)}
+	 * method. If value at this bit set to <code>1</code>, it indicates that a new
+	 * password should be created; otherwise this is a request for the password previously 
+	 * used for this secure storage.
+	 */
+	final public static int CREATE_NEW_PASSWORD = 1 << 0;
+
+	/**
+	 * Bit mask for the password type field of the {@link #getPassword(IPreferencesContainer, int)}
+	 * method. If value at this bit set to <code>1</code>, it indicates that a new password
+	 * is requested as a part of the password change operation.
+	 */
+	final public static int PASSWORD_CHANGE = 1 << 1;
+
+	/**
 	 * This method should return the password used to encrypt entries in the secure 
 	 * preferences.
 	 * @param container container of the secure preferences
+	 * @param passwordType the collection of bits that describes password type requested. See
+	 * {@link #CREATE_NEW_PASSWORD} and {@link #PASSWORD_CHANGE}. When evaluating value of this
+	 * field use bit-wise filters as additional bits might be used in future versions
 	 * @return password used to encrypt entries in the secure preferences, <code>null</code>
 	 * if unable to obtain password
 	 */
-	abstract public PBEKeySpec login(IPreferencesContainer container);
-
-	/**
-	 * A logical equivalent of "logout" for the password providers. 
-	 * <p>
-	 * The module should clear its cached password if it is the password that can be
-	 * re-obtained from some third party (such as asking user to type it in). Modules
-	 * should not discard auto generated passwords that are not available from other  
-	 * sources.
-	 * </p>
-	 * @param container container of the secure preferences, might be <code>null</code>
-	 * if logout request is not related to a specific container
-	 */
-	abstract public void logout(IPreferencesContainer container);
+	abstract public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType);
 
 	/**
 	 * Constructor.
@@ -64,7 +69,7 @@
 	 * @return <code>true</code> if a different password might be provided; <code>false</code>
 	 * otherwise. If in doubt, return <code>false</code>
 	 */
-	public boolean changePassword(Exception e, IPreferencesContainer container) {
+	public boolean retryOnError(Exception e, IPreferencesContainer container) {
 		return false;
 	}