diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java b/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java index 3e89e8f..e22105a 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/Crypto.java @@ -1,11 +1,22 @@ package info.nerull7.mysqlbrowser; +import android.content.Context; +import android.util.Base64; import android.util.Log; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.nio.charset.Charset; import java.security.InvalidKeyException; +import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.SecureRandom; +import java.security.spec.KeySpec; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; @@ -13,57 +24,83 @@ import javax.crypto.IllegalBlockSizeException; import javax.crypto.KeyGenerator; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; +import javax.crypto.spec.PBEKeySpec; /** * Created by nerull7 on 28.07.14. */ public class Crypto { private static final String KEY_FILE = "null_file"; // to trick h4x0r5 +// private static final String ENCRYPTION_ALGORITHM = "AES/CBC/PKCS5Padding"; // doesn't work TODO: Maybe fix? private static final String ENCRYPTION_ALGORITHM = "AES"; + private static final String KEY_ALGORITHM = "AES"; private static final int OUTPUT_KEY_LENGTH = 256; - private static SecretKey secretKey; + private SecretKey secretKey; + private Context context; - private static SecretKey generateKey() throws NoSuchAlgorithmException { + public Crypto(Context context){ + this.context = context; + + try { + getSecretKey(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private SecretKey generateKey() throws NoSuchAlgorithmException { SecureRandom secureRandom = new SecureRandom(); - KeyGenerator keyGenerator = KeyGenerator.getInstance(ENCRYPTION_ALGORITHM); + KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM); keyGenerator.init(OUTPUT_KEY_LENGTH, secureRandom); SecretKey secretKey = keyGenerator.generateKey(); return secretKey; } - private static SecretKey getSecretKey() throws NoSuchAlgorithmException { - if(secretKey==null) - secretKey = generateKey(); + private void getSecretKey() throws NoSuchAlgorithmException, IOException, ClassNotFoundException { + String key; - Log.d("SecureKEY", "Hash:" + secretKey.hashCode()); - return secretKey; + // First try to open file + File keyFile = new File(context.getFilesDir(), KEY_FILE); + if(!keyFile.exists()) { // new key + secretKey = generateKey(); + FileOutputStream fileOutputStream = new FileOutputStream(keyFile); + ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream); + objectOutputStream.writeObject(secretKey); + objectOutputStream.close(); + fileOutputStream.close(); + } else { // read existing key from file + FileInputStream fileInputStream = new FileInputStream(keyFile); + ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream); + secretKey = (SecretKey) objectInputStream.readObject(); + objectInputStream.close(); + fileInputStream.close(); + } } - public static byte[] encrypt(String input) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + public byte[] encrypt(String input) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { byte[] output; Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); - cipher.init(Cipher.ENCRYPT_MODE, getSecretKey()); + cipher.init(Cipher.ENCRYPT_MODE, secretKey); output = cipher.doFinal(input.getBytes(Charset.defaultCharset())); return output; } - public static String decrypt(byte[] input) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { + public String decrypt(byte[] input) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException { String output; - byte [] tmp; // TODO: REMOVE - Cipher cipher = Cipher.getInstance(ENCRYPTION_ALGORITHM); - cipher.init(Cipher.DECRYPT_MODE, getSecretKey()); -// output = String.valueOf(cipher.doFinal(input)); - tmp = cipher.doFinal(input); - -// output = tmp.toString(); - output = new String(input, Charset.defaultCharset()); + cipher.init(Cipher.DECRYPT_MODE, secretKey); + output = new String(cipher.doFinal(input), Charset.defaultCharset()); return output; } + public String decryptBase64(String encodedString) throws IllegalBlockSizeException, InvalidKeyException, BadPaddingException, NoSuchAlgorithmException, NoSuchPaddingException { + byte [] encryptedString = Base64.decode(encodedString, Base64.DEFAULT); + String decrypted = decrypt(encryptedString); + return decrypted; + } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java index 3ac2322..0c06399 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/LoginFragment.java @@ -63,8 +63,14 @@ public class LoginFragment extends Fragment implements View.OnClickListener, Asy if(sharedPreferences.getBoolean(SettingsFragment.SAVE_SERVER_CREDENTIALS, false)){ urlTextbox.setText(sharedPreferences.getString(SettingsFragment.URL_CREDENTIALS, null)); loginTextbox.setText(sharedPreferences.getString(SettingsFragment.LOGIN_CREDENTIALS, null)); - passwordTextbox.setText(sharedPreferences.getString(SettingsFragment.PASSWORD_CREDENTIALS, null)); - test(urlTextbox.getText().toString()); + Crypto crypto = new Crypto(getActivity()); + String password = sharedPreferences.getString(SettingsFragment.PASSWORD_CREDENTIALS, null); + if(password!=null) { + try { + passwordTextbox.setText(crypto.decryptBase64(password)); + } catch (Exception e) { e.printStackTrace(); } + } +// test(urlTextbox.getText().toString()); } } @@ -72,10 +78,9 @@ public class LoginFragment extends Fragment implements View.OnClickListener, Asy String tmp; byte [] tmp_byte; try { - tmp_byte = Crypto.encrypt(text); - Log.d("Crypto", "encrypted: " + tmp_byte); - tmp = Crypto.decrypt(tmp_byte); - Log.d("Crypto", "decrypted: " + tmp); + Crypto crypto = new Crypto(getActivity()); + tmp_byte = crypto.encrypt(text); + crypto.decrypt(tmp_byte); } catch (Exception e) { e.printStackTrace(); } } diff --git a/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java b/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java index 4379169..612337a 100644 --- a/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java +++ b/app/src/main/java/info/nerull7/mysqlbrowser/SettingsFragment.java @@ -7,11 +7,19 @@ import android.preference.EditTextPreference; import android.preference.Preference; import android.preference.PreferenceFragment; import android.preference.PreferenceScreen; +import android.util.Base64; + +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; + +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; /** * Created by nerull7 on 18.07.14. */ -public class SettingsFragment extends PreferenceFragment implements NumberPickerDialog.OnNumberSetListener, Preference.OnPreferenceClickListener { +public class SettingsFragment extends PreferenceFragment implements NumberPickerDialog.OnNumberSetListener, Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { public static final String ENTRIES_PAGE_LIMIT = "entries_limit"; public static final String SAVE_SERVER_CREDENTIALS = "save_credentials_enabled"; public static final String URL_CREDENTIALS = "url"; @@ -29,10 +37,13 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker private EditTextPreference loginCredentials; private EditTextPreference passwordCredentials; + private Crypto crypto; + @Override public void onCreate(Bundle savedInstanceState){ super.onCreate(savedInstanceState); preferences = getPreferenceManager().getDefaultSharedPreferences(getActivity()); + crypto = new Crypto(getActivity()); loadPrefs(); } @@ -49,9 +60,11 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker // Settings fields setEntriesPageLimitSummary(); + setPasswordCredentials(); // Settings Listener saveCredentials.setOnPreferenceClickListener(this); + passwordCredentials.setOnPreferenceChangeListener(this); } private void setSaveServerCredentials(boolean isEnabled){ @@ -85,6 +98,26 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker mEntriesLimit.setSummary(getString(R.string.entries_summary, getEntriesPageLimit())); } + private void setPasswordCredentials(){ + String password; + password = preferences.getString(PASSWORD_CREDENTIALS, null); + if(password != null) + try { + passwordCredentials.setText(crypto.decryptBase64(password)); + } catch (Exception e) { e.printStackTrace(); } // TODO: Something useful + } + + private void savePassword(String password){ + try { + SharedPreferences.Editor editor = preferences.edit(); + byte [] encryptedPassword; + encryptedPassword = crypto.encrypt(password); + String passwordBase64 = Base64.encodeToString(encryptedPassword, Base64.DEFAULT); + editor.putString(PASSWORD_CREDENTIALS, passwordBase64); + editor.apply(); + } catch (Exception e) { e.printStackTrace(); } // TODO: Something useful + } + @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { if(preference == mEntriesLimit){ @@ -112,4 +145,12 @@ public class SettingsFragment extends PreferenceFragment implements NumberPicker return true; } + + @Override + public boolean onPreferenceChange(Preference preference, Object newValue) { + if(preference == passwordCredentials){ + savePassword((String) newValue); + } + return false; + } }