package com.aptana.ide.security.internal.linux;
import java.io.File;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.equinox.security.storage.provider.IPreferencesContainer;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.widgets.Display;
import org.eclipse.ui.PlatformUI;
import org.osgi.service.prefs.BackingStoreException;
import com.aptana.ide.core.AptanaCorePlugin;
import com.aptana.ide.core.Base64;
import com.aptana.ide.core.FileUtils;
import com.aptana.ide.core.IdeLog;
import com.aptana.ide.core.preferences.IPreferenceConstants;
import com.aptana.ide.core.ui.Messages;
import com.aptana.ide.security.linux.Activator;
public class PasswordProvider extends org.eclipse.equinox.security.storage.provider.PasswordProvider
{
private static final String ALGORITHM = "AES/ECB/PKCS5Padding"; //$NON-NLS-1$
private String accountName = System.getProperty("user.home"); //$NON-NLS-1$
@Override
public PBEKeySpec getPassword(IPreferencesContainer container, int passwordType)
{
if (accountName == null)
return null;
final boolean newPassword = ((passwordType & CREATE_NEW_PASSWORD) != 0);
final boolean passwordChange = ((passwordType & PASSWORD_CHANGE) != 0);
try
{
if (!newPassword && !passwordChange)
{
char[] existing = getPassword();
if (existing != null && existing.length != 0)
return new PBEKeySpec(existing);
}
// Prompt user via dialog!
if (!useUI())
return null;
final String[] result = new String[1];
PlatformUI.getWorkbench().getDisplay().syncExec(new Runnable()
{
public void run()
{
StorageLoginDialog loginDialog = new StorageLoginDialog(Display.getDefault().getActiveShell(),
newPassword, passwordChange);
if (loginDialog.open() == Window.OK)
result[0] = loginDialog.getPassword();
else
result[0] = null;
}
});
String password = result[0];
if (password == null || password.trim().length() == 0)
return null;
writePassword(password);
return new PBEKeySpec(password.toCharArray());
}
catch (IOException e)
{
IdeLog.logError(Activator.getDefault(), e.getMessage(), e);
}
return null;
}
/**
* Determines if it is a good idea to show UI prompts
*/
private static boolean useUI()
{
return PlatformUI.isWorkbenchRunning();
}
private void writePassword(String password) throws IOException
{
SecretKeySpec key = getKeySpec();
byte[] encrypted = encrypt(key, password);
if (encrypted != null && encrypted.length > 0)
{
String b64 = Base64.encodeBytes(encrypted);
FileUtils.writeStringToFile(b64, getPasswordFile());
}
}
private char[] getPassword() throws IOException
{
String encrypted = getEncryptedPassword();
if (encrypted == null)
return new char[0];
byte[] bytes = Base64.decode(encrypted);
if (bytes != null)
{
SecretKeySpec key = getKeySpec();
byte[] decrypted = decrypt(key, bytes);
if (decrypted == null || decrypted.length == 0)
return new char[0];
return new String(decrypted).toCharArray();
}
else
{
return new char[0];
}
}
private String getEncryptedPassword() throws IOException
{
File file = getPasswordFile();
if (!file.exists())
return null;
return FileUtils.readContent(file);
}
private File getPasswordFile() throws IOException
{
File file = new File(accountName + File.separator + ".aptanasecure", ".store");
file.getParentFile().mkdirs();
return file;
}
private SecretKeySpec getKeySpec()
{
String ksPref = Platform.getPreferencesService().getString(AptanaCorePlugin.ID, IPreferenceConstants.CACHED_KEY, "", null);
byte[] key = null;
if (!"".equals(ksPref)) //$NON-NLS-1$
{
try
{
byte[] bytes = Base64.decode(ksPref);
if (bytes != null)
{
key = bytes;
}
}
catch (Exception e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(),
Messages.AptanaAuthenticator_ERR_UnableToDecodeExistingKey, e);
}
}
KeyGenerator kgen;
if (key == null || key.length == 0) //$NON-NLS-1$
{
try
{
kgen = KeyGenerator.getInstance("AES"); //$NON-NLS-1$
kgen.init(128);
SecretKey skey = kgen.generateKey();
key = skey.getEncoded();
String b64 = Base64.encodeBytes(skey.getEncoded());
IEclipsePreferences node = new InstanceScope().getNode(AptanaCorePlugin.ID);
node.put(IPreferenceConstants.CACHED_KEY, b64);
node.flush();
}
catch (NoSuchAlgorithmException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_NoSuchAlgorithm, e);
return null;
}
catch (BackingStoreException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), com.aptana.ide.security.internal.linux.Messages.PasswordProvider_ERR_UnableToStoreKey, e);
return null;
}
}
return new SecretKeySpec(key, "AES"); //$NON-NLS-1$
}
private byte[] encrypt(SecretKeySpec skeySpec, String password)
{
try
{
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
return cipher.doFinal(password.getBytes());
}
catch (NoSuchAlgorithmException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_NoSuchAlgorithm, e);
}
catch (NoSuchPaddingException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_NoSuchPadding, e);
}
catch (InvalidKeyException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_InvalidKey, e);
}
catch (IllegalBlockSizeException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_IllegalBlockSize, e);
}
catch (BadPaddingException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_BadPadding, e);
}
return null;
}
private byte[] decrypt(SecretKeySpec skeySpec, byte[] encryptedPassword)
{
try
{
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
return cipher.doFinal(encryptedPassword);
}
catch (NoSuchAlgorithmException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_NoSuchAlgorithm, e);
}
catch (NoSuchPaddingException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_NoSuchPadding, e);
}
catch (InvalidKeyException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_InvalidKey, e);
}
catch (IllegalBlockSizeException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_IllegalBlockSize, e);
}
catch (BadPaddingException e)
{
IdeLog.logError(AptanaCorePlugin.getDefault(), Messages.AptanaAuthenticator_ERR_BadPadding, e);
}
return null;
}
}