blob: ce73f094e911bd122b37b77588d242ec93e98d11 [file] [log] [blame]
/*
* Copyright (C) 2012 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.motorolamobility.studio.android.certmanager.core;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.eclipse.equinox.security.storage.ISecurePreferences;
import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
import org.eclipse.equinox.security.storage.StorageException;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.swt.SWT;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.PlatformUI;
import com.motorola.studio.android.common.log.StudioLogger;
import com.motorola.studio.android.common.utilities.EclipseUtils;
import com.motorolamobility.studio.android.certmanager.CertificateManagerActivator;
import com.motorolamobility.studio.android.certmanager.exception.KeyStoreManagerException;
import com.motorolamobility.studio.android.certmanager.i18n.CertificateManagerNLS;
/**
* This class is responsible to retrieve passwords for a keystore and its entries.
* Usage:
* Instantiate with a keyStoreFile, call the methods getPassword.
* If needed a dialog will be shown, asking user to type the password.
*/
public class PasswordProvider
{
private static final String PREF_ROOT_NODE = CertificateManagerActivator.PLUGIN_ID
+ "_passwords"; //$NON-NLS-1$
private static final String KS_PASSWORD_KEY = "KS_PASSWORD"; //$NON-NLS-1$
private final class KeyStorePasswdDialog extends Dialog
{
private final File keyStoreFile;
private String passwd;
private boolean savePasswd;
private Text paswordText;
private Button saveCheckBox;
private final String alias;
private KeyStorePasswdDialog(Shell parentShell, File keyStoreFile, String alias)
{
super(parentShell);
this.keyStoreFile = keyStoreFile;
this.alias = alias;
}
@Override
protected Control createDialogArea(Composite parent)
{
Composite mainComposite = new Composite(parent, SWT.NONE);
GridLayout gridLayout = new GridLayout(2, false);
mainComposite.setLayout(gridLayout);
//Creates the message
Label messageLabel = new Label(mainComposite, SWT.NONE);
GridData gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 2);
messageLabel.setLayoutData(gridData);
if (this.alias.equals(KS_PASSWORD_KEY))
{
getShell().setText(CertificateManagerNLS.PasswordProvider_DialogTitle);
messageLabel.setText(NLS.bind(CertificateManagerNLS.PasswordProvider_MessageLabel,
keyStoreFile.getName()));
}
else
{
getShell().setText(CertificateManagerNLS.CertificateBlock_KeyPassword_Label);
messageLabel.setText(NLS.bind(
CertificateManagerNLS.PasswordProvider_Key_MessageLabel, alias));
}
//Creates the text field label
Label passwdLabel = new Label(mainComposite, SWT.NONE);
gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false);
passwdLabel.setLayoutData(gridData);
passwdLabel.setText(CertificateManagerNLS.PasswordProvider_PasswordLabel);
//Creates the password text
paswordText = new Text(mainComposite, SWT.BORDER | SWT.PASSWORD);
gridData = new GridData(SWT.FILL, SWT.CENTER, true, false);
paswordText.setLayoutData(gridData);
//Creates the save password checkbox
saveCheckBox = new Button(mainComposite, SWT.CHECK);
saveCheckBox.setText(CertificateManagerNLS.PasswordProvider_SaveThisPassword);
saveCheckBox.setSelection(false);
gridData = new GridData(SWT.LEFT, SWT.CENTER, false, false, 2, 1);
saveCheckBox.setLayoutData(gridData);
saveCheckBox.setVisible(KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile));
return super.createDialogArea(parent);
}
@Override
protected void okPressed()
{
passwd = paswordText.getText();
savePasswd = saveCheckBox.getSelection();
super.okPressed();
}
public String getPasswd()
{
return passwd;
}
public boolean mustSavePasswd()
{
return savePasswd;
}
}
private final File keyStoreFile;
private final ISecurePreferences securePreferences;
private boolean canSavePassword = true;
public PasswordProvider(File keyStoreFile)
{
this(keyStoreFile, KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile));
}
public PasswordProvider(File keyStoreFile, boolean canSavePassword)
{
this.keyStoreFile = keyStoreFile;
this.securePreferences = SecurePreferencesFactory.getDefault();
this.canSavePassword = canSavePassword;
}
/**
* Retrieves the KeyStore password.
* @param promptPassword whether the password entry dialog will be shown or not
* @param useSavedPassword whether to use the keyStore saved password
* @return the password string or null if user canceled the dialog
* @throws KeyStoreManagerException
*/
public String getKeyStorePassword(boolean promptPassword, boolean useSavedPassword)
throws KeyStoreManagerException
{
return getPassword(KS_PASSWORD_KEY, promptPassword, useSavedPassword);
}
/**
* Retrieves the KeyStore password.
* This method will always attempt to retrieve the saved password.
* It's behavior is the same as of calling the method getPassword(promptPassword, true)
* @param promptPassword whether the password entry dialog will be shown or not
* @return the password string or null if user canceled the dialog
* @throws KeyStoreManagerException
*/
public String getKeyStorePassword(boolean promptPassword) throws KeyStoreManagerException
{
return getPassword(KS_PASSWORD_KEY, promptPassword, true);
}
/**
* Retrieves the password for a given alias within a keyStore.
* This method will always attempt to retrieve the saved password.
* It's behavior is the same as of calling the method getPassword(promptPassword, true)
* @param promptPassword whether the password entry dialog will be shown or not
* @return the password string or null if user canceled the dialog
* @throws KeyStoreManagerException
*/
public String getPassword(String alias, boolean promptPassword) throws KeyStoreManagerException
{
return getPassword(alias, promptPassword, true);
}
/**
* Retrieves the password for a given alias within a keyStore.
* This method will always attempt to retrieve the saved password.
* It's behavior is the same as of calling the method getPassword(promptPassword, true)
* @param promptPassword whether the password entry dialog will be shown or not
* @param useSavedPassword whether to use the keyStore saved password
* @return the password string or null if user canceled the dialog
* @throws KeyStoreManagerException
*/
public String getPassword(String alias, boolean promptPassword, boolean useSavedPassword)
throws KeyStoreManagerException
{
String password = null;
if (useSavedPassword)
{
if (securePreferences != null)
{
String prefKey = alias;
password = getSavedPasswd(prefKey);
}
else
{
throw new KeyStoreManagerException(
CertificateManagerNLS.PasswordProvider_Error_WhileSaving);
}
}
if ((password == null) && promptPassword)
{
password = promptPassword(alias);
}
return password;
}
private String getSavedPasswd(String prefKey)
{
String password = null;
// Try to get the password from secure storage
if (securePreferences.nodeExists(PREF_ROOT_NODE))
{
ISecurePreferences node = securePreferences.node(PREF_ROOT_NODE);
try
{
if (node.nodeExists(keyStoreFile.getAbsolutePath()))
{
ISecurePreferences ksNode = node.node(keyStoreFile.getAbsolutePath());
password = ksNode.get(prefKey, null);
}
}
catch (StorageException e)
{
//Do nothing, password will be null.
}
}
return password;
}
private String promptPassword(final String alias) throws KeyStoreManagerException
{
final String[] result = new String[1];
final Boolean[] canProceed = new Boolean[1];
Display.getDefault().syncExec(new Runnable()
{
@Override
public void run()
{
KeyStorePasswdDialog dialog =
new KeyStorePasswdDialog(PlatformUI.getWorkbench()
.getModalDialogShellProvider().getShell(), keyStoreFile, alias);
int diagStatus = dialog.open();
if (diagStatus == Dialog.OK)
{
//Read the values from the dialog and do the actions, return passwd and save if required
result[0] = dialog.getPasswd();
canSavePassword = KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile);
canProceed[0] = dialog.mustSavePasswd();
}
else
{
//dialog cancelled
canProceed[0] = false;
result[0] = null;
}
}
});
if (canProceed[0] && canSavePassword)
{
if (securePreferences != null)
{
savePassword(alias, result[0]);
}
else
{
EclipseUtils.showWarningDialog(CertificateManagerNLS.PasswordProvider_DialogTitle,
CertificateManagerNLS.PasswordProvider_Error_WhileSaving);
}
}
return result[0];
}
public void saveKeyStorePassword(String password) throws KeyStoreManagerException
{
savePassword(KS_PASSWORD_KEY, password);
}
public void savePassword(final String alias, String password) throws KeyStoreManagerException
{
String prefKey;
canSavePassword = KeyStoreManager.getInstance().isKeystoreMapped(keyStoreFile);
if (canSavePassword) //protect from saving
{
if (alias != null)
{
prefKey = alias;
}
else
{
prefKey = KS_PASSWORD_KEY;
}
ISecurePreferences rootNode = securePreferences.node(PREF_ROOT_NODE);
try
{
ISecurePreferences ksNode = rootNode.node(keyStoreFile.getAbsolutePath());
ksNode.put(prefKey, password, true);
ksNode.flush();
}
catch (Exception e)
{
throw new KeyStoreManagerException(
CertificateManagerNLS.PasswordProvider_Error_WhileSaving);
}
}
}
/**
* Deletes the entire node (including KS_PASSWORD_KEY and children aliases)
* @throws KeyStoreManagerException
*/
public void deleteKeyStoreSavedPasswordNode() throws KeyStoreManagerException
{
deleteSavedPassword(null);
}
/**
* Deletes only KS_PASSWORD_KEY (not children aliases)
*/
public void deleteKeyStoreSavedPassword() throws KeyStoreManagerException
{
deleteSavedPassword(KS_PASSWORD_KEY);
}
public void deleteSavedPassword(String alias) throws KeyStoreManagerException
{
ISecurePreferences ksNode = getKeyStoreNode();
if (ksNode != null)
{
if (alias == null)
{
ksNode.removeNode();
}
else
{
ksNode.remove(alias);
//if no item has no child, then we can remove the node
if (ksNode.keys().length == 0)
{
ksNode.removeNode();
}
}
try
{
ksNode.flush();
}
catch (IllegalStateException e)
{
//Do nothing, node has already been removed
}
catch (IOException e)
{
throw new KeyStoreManagerException(NLS.bind(
CertificateManagerNLS.PasswordProvider_Error_WhileRemovingPassword,
keyStoreFile.getName()));
}
}
}
/**
* This method will remove all saved entries for this keystore file that is not listed on the aliasList.
* The idea is to remove all saved passwords that makes reference to non-existant entries.
* @param aliasList the list of alias to be kept if available on the security keystore
* @throws KeyStoreManagerException if writing the security keystore fails for some reason
*/
public void cleanModel(List<String> aliasList) throws KeyStoreManagerException
{
ISecurePreferences keyStoreNode = getKeyStoreNode();
if (keyStoreNode != null)
{
String[] savedKeys = keyStoreNode.keys();
for (String savedAlias : savedKeys)
{
if (!savedAlias.equals(KS_PASSWORD_KEY) && !aliasList.contains(savedAlias))
{
keyStoreNode.remove(savedAlias);
}
}
try
{
keyStoreNode.flush();
}
catch (IOException e)
{
throw new KeyStoreManagerException(NLS.bind(
CertificateManagerNLS.PasswordProvider_Error_WhileRemovingPassword,
keyStoreFile.getName()));
}
}
}
/*
* @return the keystore node if it exists
*/
private ISecurePreferences getKeyStoreNode()
{
ISecurePreferences ksNode = null;
if (securePreferences.nodeExists(PREF_ROOT_NODE))
{
ISecurePreferences rootNode = securePreferences.node(PREF_ROOT_NODE);
if (rootNode.nodeExists(keyStoreFile.getAbsolutePath()))
{
ksNode = rootNode.node(keyStoreFile.getAbsolutePath());
}
}
return ksNode;
}
/**
* If keystore password is saved.
*/
public boolean isPasswordSaved()
{
return isPasswordSaved(KS_PASSWORD_KEY);
}
/**
* If alias password is saved.
*/
public boolean isPasswordSaved(String prefKey)
{
ISecurePreferences ksNode = null;
boolean isSaved = false;
if (securePreferences.nodeExists(PREF_ROOT_NODE))
{
ISecurePreferences rootNode = securePreferences.node(PREF_ROOT_NODE);
ksNode = rootNode.node(keyStoreFile.getAbsolutePath());
try
{
String value = ksNode.get(prefKey, null);
isSaved = value != null; //password is saved if it is not the default value (because password length should be at least 6
}
catch (StorageException e)
{
StudioLogger.debug("It was not possible to get if the " + prefKey
+ " is saved or not");
isSaved = false;
}
}
return isSaved;
}
}