aboutsummaryrefslogtreecommitdiff
path: root/app/src/main/java/github/daneren2005/dsub/util
diff options
context:
space:
mode:
authorKevin T. Berstene <kberstene@gmail.com>2019-04-01 14:30:43 -0400
committerKevin T. Berstene <kberstene@gmail.com>2019-04-01 14:30:43 -0400
commit0e5c95e5cb6f7db5cc1c3ae711f622378d8ef786 (patch)
tree252846940f1e75a34b8da511d1975c68fa00c1f1 /app/src/main/java/github/daneren2005/dsub/util
parent2b26df335ccff17e3970ac94f4c0abfbd6898a47 (diff)
downloaddsub-0e5c95e5cb6f7db5cc1c3ae711f622378d8ef786.tar.gz
dsub-0e5c95e5cb6f7db5cc1c3ae711f622378d8ef786.tar.bz2
dsub-0e5c95e5cb6f7db5cc1c3ae711f622378d8ef786.zip
Added password encryption for SDK 23 and higher
Diffstat (limited to 'app/src/main/java/github/daneren2005/dsub/util')
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Constants.java1
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/KeyStoreUtil.java147
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/UserUtil.java4
-rw-r--r--app/src/main/java/github/daneren2005/dsub/util/Util.java2
4 files changed, 154 insertions, 0 deletions
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Constants.java b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
index 7f5ff3f1..017ba2f3 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Constants.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Constants.java
@@ -85,6 +85,7 @@ public final class Constants {
public static final String PREFERENCES_KEY_MUSIC_FOLDER_ID = "musicFolderId";
public static final String PREFERENCES_KEY_USERNAME = "username";
public static final String PREFERENCES_KEY_PASSWORD = "password";
+ public static final String PREFERENCES_KEY_ENCRYPTED_PASSWORD = "encryptedPassword";
public static final String PREFERENCES_KEY_INSTALL_TIME = "installTime";
public static final String PREFERENCES_KEY_THEME = "theme";
public static final String PREFERENCES_KEY_FULL_SCREEN = "fullScreen";
diff --git a/app/src/main/java/github/daneren2005/dsub/util/KeyStoreUtil.java b/app/src/main/java/github/daneren2005/dsub/util/KeyStoreUtil.java
new file mode 100644
index 00000000..10ec9497
--- /dev/null
+++ b/app/src/main/java/github/daneren2005/dsub/util/KeyStoreUtil.java
@@ -0,0 +1,147 @@
+package github.daneren2005.dsub.util;
+
+import android.annotation.TargetApi;
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyProperties;
+import android.support.annotation.NonNull;
+import android.util.Base64;
+import android.util.Log;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.UnrecoverableEntryException;
+import java.security.cert.CertificateException;
+
+import javax.crypto.Cipher;
+import javax.crypto.KeyGenerator;
+import javax.crypto.spec.IvParameterSpec;
+
+@TargetApi(23)
+public class KeyStoreUtil {
+ private static String TAG = KeyStoreUtil.class.getSimpleName();
+ private static final String KEYSTORE_ALIAS = "DSubKeyStoreAlias";
+ private static final String KEYSTORE_PROVIDER = "AndroidKeyStore";
+ private static final String KEYSTORE_CIPHER_PROVIDER = "AndroidKeyStoreBCWorkaround";
+ private static final String KEYSTORE_TRANSFORM = "AES/CBC/PKCS7Padding";
+ private static final String KEYSTORE_BYTE_ENCODING = "UTF-8";
+
+ public static void loadKeyStore() throws KeyStoreException, IOException,
+ CertificateException, NoSuchAlgorithmException {
+
+ // Load keystore
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
+ keyStore.load(null);
+
+ // Check if keystore has been used before
+ if (!keyStore.containsAlias(KEYSTORE_ALIAS)) {
+ // If alias does not exist, keystore hasn't been used before
+ // Create a new secret key to store in the keystore
+ try {
+ Log.w(TAG, "Generating keys.");
+ generateKeys();
+ } catch (Exception e) {
+ Log.w(TAG, "Key generation failed.");
+ Log.w(TAG, Log.getStackTraceString(e));
+ }
+ }
+ }
+
+ private static void generateKeys() throws InvalidAlgorithmParameterException,
+ NoSuchAlgorithmException, NoSuchProviderException {
+ KeyGenerator keyGen = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES, KEYSTORE_PROVIDER);
+ keyGen.init(new KeyGenParameterSpec.Builder(KEYSTORE_ALIAS,
+ KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_CBC)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .build());
+ keyGen.generateKey();
+ }
+
+ private static Key getKey() throws KeyStoreException, CertificateException,
+ NoSuchAlgorithmException, IOException, UnrecoverableEntryException {
+
+ // Attempt to load keystore
+ KeyStore keyStore = KeyStore.getInstance(KEYSTORE_PROVIDER);
+ keyStore.load(null);
+
+ // Fetch and return secret key
+ return keyStore.getKey(KEYSTORE_ALIAS, null);
+ }
+
+ public static String encrypt(@NonNull String plainTextString) {
+ Log.d(TAG, "Encrypting password...");
+ try {
+ // Retrieve secret key
+ final Key key = getKey();
+
+ // Initialize cipher
+ Cipher cipher = Cipher.getInstance(KEYSTORE_TRANSFORM, KEYSTORE_CIPHER_PROVIDER);
+ cipher.init(Cipher.ENCRYPT_MODE, key);
+
+ // Create stream for storing data
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+
+ // Write the IV length first so the IV can be split from the encrypted password
+ outputStream.write(cipher.getIV().length);
+
+ // Write the auto-generated IV
+ outputStream.write(cipher.getIV());
+
+ // Encrypt the plaintext and write the encrypted string
+ outputStream.write(cipher.doFinal(plainTextString.getBytes(KEYSTORE_BYTE_ENCODING)));
+
+ // Encode the return full stream for storage
+ Log.d(TAG, "Password encryption successful");
+ return Base64.encodeToString(outputStream.toByteArray(), Base64.NO_WRAP);
+
+ } catch (Exception e) {
+ Log.w(TAG, "Password encryption failed");
+ Log.d(TAG, Log.getStackTraceString(e));
+ return null;
+ }
+ }
+
+ public static String decrypt(@NonNull String encryptedString) {
+ Log.d(TAG, "Decrypting password...");
+ try {
+ // Retrieve secret key
+ final Key key = getKey();
+
+ // Decode the string from Base64
+ byte[] decodedBytes = Base64.decode(encryptedString, Base64.NO_WRAP);
+ int ivLength = decodedBytes[0];
+ int encryptedLength = decodedBytes.length - (ivLength + 1);
+
+ // Get IV from decoded string
+ byte[] ivBytes = new byte[ivLength];
+ System.arraycopy(decodedBytes, 1, ivBytes, 0, ivLength);
+
+ // Get encrypted password from decoded string
+ byte[] encryptedBytes = new byte[encryptedLength];
+ System.arraycopy(decodedBytes, ivLength + 1, encryptedBytes, 0, encryptedLength);
+
+ // Initialize cipher using the IV from the dencoded string
+ Cipher cipher = Cipher.getInstance(KEYSTORE_TRANSFORM, KEYSTORE_CIPHER_PROVIDER);
+ IvParameterSpec ivParamSpec = new IvParameterSpec(ivBytes);
+ cipher.init(Cipher.DECRYPT_MODE, key, ivParamSpec);
+
+ // Decrypt the password
+ String decryptedString = new String(cipher.doFinal(encryptedBytes));
+
+ // Return the decrypted password string
+ Log.d(TAG, "Password successfully decrypted");
+ return decryptedString;
+
+ } catch (Exception e) {
+ Log.w(TAG, "Password decryption failed");
+ Log.w(TAG, Log.getStackTraceString(e));
+ return null;
+ }
+ }
+}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
index db1c628f..0775c956 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/UserUtil.java
@@ -272,6 +272,10 @@ public final class UserUtil {
SharedPreferences prefs = Util.getPreferences(context);
String correctPassword = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + Util.getActiveServer(context), null);
+ if (prefs.getBoolean(Constants.PREFERENCES_KEY_ENCRYPTED_PASSWORD + instance, false)) {
+ correctPassword = KeyStoreUtil.decrypt(correctPassword);
+ }
+
return password != null && password.equals(correctPassword);
}
diff --git a/app/src/main/java/github/daneren2005/dsub/util/Util.java b/app/src/main/java/github/daneren2005/dsub/util/Util.java
index 78f3e2d6..791fea91 100644
--- a/app/src/main/java/github/daneren2005/dsub/util/Util.java
+++ b/app/src/main/java/github/daneren2005/dsub/util/Util.java
@@ -199,6 +199,7 @@ public final class Util {
String serverUrl = prefs.getString(Constants.PREFERENCES_KEY_SERVER_URL + newInstance, null);
String userName = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + newInstance, null);
String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + newInstance, null);
+ if ((password != null) && (prefs.getBoolean(Constants.PREFERENCES_KEY_ENCRYPTED_PASSWORD + instance, false))) password = KeyStoreUtil.decrypt(password);
String musicFolderId = prefs.getString(Constants.PREFERENCES_KEY_MUSIC_FOLDER_ID + newInstance, null);
// Store the +1 server details in the to be deleted instance
@@ -364,6 +365,7 @@ public final class Util {
String username = prefs.getString(Constants.PREFERENCES_KEY_USERNAME + instance, null);
String password = prefs.getString(Constants.PREFERENCES_KEY_PASSWORD + instance, null);
+ if ((password != null) && (prefs.getBoolean(Constants.PREFERENCES_KEY_ENCRYPTED_PASSWORD + instance, false))) password = KeyStoreUtil.decrypt(password);
builder.append(serverUrl);
if (builder.charAt(builder.length() - 1) != '/') {