diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/EncryptionHelper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/EncryptionHelper.java
index 57b37edb..437967ec 100644
--- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/EncryptionHelper.java
+++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/EncryptionHelper.java
@@ -23,10 +23,16 @@
package org.shadowice.flocke.andotp.Utilities;
+import android.content.Context;
+
+import java.io.File;
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;
+import java.security.GeneralSecurityException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
+import java.security.KeyPair;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
@@ -51,6 +57,7 @@ import static org.shadowice.flocke.andotp.Utilities.Constants.ALGORITHM_ASYMMETR
import static org.shadowice.flocke.andotp.Utilities.Constants.ALGORITHM_SYMMETRIC;
public class EncryptionHelper {
+ private final static int KEY_LENGTH = 16;
private final static int IV_LENGTH = 12;
public final static int PBKDF2_MIN_ITERATIONS = 1000;
@@ -151,4 +158,31 @@ public class EncryptionHelper {
return cipher.doFinal(cipherText);
}
+
+ /**
+ * Load our symmetric secret key.
+ * The symmetric secret key is stored securely on disk by wrapping
+ * it with a public/private key pair, possibly backed by hardware.
+ */
+ public static SecretKey loadOrGenerateWrappedKey(Context context, File keyFile, KeyPair keyPair)
+ throws GeneralSecurityException, IOException {
+ final SecretKeyWrapper wrapper = new SecretKeyWrapper(keyPair);
+
+ // Generate secret key if none exists
+ if (!keyFile.exists()) {
+ final byte[] raw = EncryptionHelper.generateRandom(KEY_LENGTH);
+
+ final SecretKey key = new SecretKeySpec(raw, "AES");
+ final byte[] wrapped = wrapper.wrap(key);
+
+
+ FileHelper.writeBytesToFile(keyFile, wrapped);
+ }
+
+ // Even if we just generated the key, always read it back to ensure we
+ // can read it successfully.
+ final byte[] wrapped = FileHelper.readFileToBytes(keyFile);
+
+ return wrapper.unwrap(wrapped);
+ }
}
diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/KeyStoreHelper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/KeyStoreHelper.java
index d8490d87..06a14b98 100644
--- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/KeyStoreHelper.java
+++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/KeyStoreHelper.java
@@ -42,7 +42,6 @@ import java.util.Calendar;
import java.util.GregorianCalendar;
import javax.crypto.SecretKey;
-import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
public class KeyStoreHelper {
@@ -50,8 +49,6 @@ public class KeyStoreHelper {
public static final String KEYSTORE_ALIAS_WRAPPING = "settings";
- private final static int KEY_LENGTH = 16;
-
public static KeyPair loadOrGenerateAsymmetricKeyPair(Context context, String alias)
throws GeneralSecurityException, IOException {
final KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
@@ -91,42 +88,25 @@ public class KeyStoreHelper {
return new KeyPair(entry.getCertificate().getPublicKey(), entry.getPrivateKey());
}
- /**
- * Load our symmetric secret key.
- * The symmetric secret key is stored securely on disk by wrapping
- * it with a public/private key pair, possibly backed by hardware.
- */
- public static SecretKey loadOrGenerateWrappedKey(Context context, File keyFile)
- throws GeneralSecurityException, IOException {
- final SecretKeyWrapper wrapper = new SecretKeyWrapper(context, KEYSTORE_ALIAS_WRAPPING);
-
- // Generate secret key if none exists
- if (!keyFile.exists()) {
- final byte[] raw = EncryptionHelper.generateRandom(KEY_LENGTH);
-
- final SecretKey key = new SecretKeySpec(raw, "AES");
- final byte[] wrapped = wrapper.wrap(key);
-
-
- FileHelper.writeBytesToFile(keyFile, wrapped);
- }
-
- // Even if we just generated the key, always read it back to ensure we
- // can read it successfully.
- final byte[] wrapped = FileHelper.readFileToBytes(keyFile);
-
- return wrapper.unwrap(wrapped);
- }
-
public static SecretKey loadEncryptionKeyFromKeyStore(Context context) {
+ KeyPair pair = null;
+
try {
- return loadOrGenerateWrappedKey(context, new File(context.getFilesDir() + "/" + KEY_FILE));
+ pair = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, KEYSTORE_ALIAS_WRAPPING);
} catch (GeneralSecurityException | IOException e) {
e.printStackTrace();
-
- UIHelper.showGenericDialog(context, R.string.dialog_title_keystore_error, R.string.dialog_msg_keystore_error);
-
- return null;
+ UIHelper.showGenericDialog(context, R.string.dialog_title_encryption_error, R.string.dialog_msg_keystore_error);
}
+
+ if (pair != null) {
+ try {
+ return EncryptionHelper.loadOrGenerateWrappedKey(context, new File(context.getFilesDir() + "/" + KEY_FILE), pair);
+ } catch (GeneralSecurityException | IOException e) {
+ e.printStackTrace();
+ UIHelper.showGenericDialog(context, R.string.dialog_title_encryption_error, R.string.dialog_msg_unwrap_error);
+ }
+ }
+
+ return null;
}
}
diff --git a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/SecretKeyWrapper.java b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/SecretKeyWrapper.java
index 0e6be63f..3b9a4bbf 100644
--- a/app/src/main/java/org/shadowice/flocke/andotp/Utilities/SecretKeyWrapper.java
+++ b/app/src/main/java/org/shadowice/flocke/andotp/Utilities/SecretKeyWrapper.java
@@ -17,7 +17,6 @@
package org.shadowice.flocke.andotp.Utilities;
import android.annotation.SuppressLint;
-import android.content.Context;
import java.io.IOException;
import java.security.GeneralSecurityException;
@@ -48,10 +47,10 @@ public class SecretKeyWrapper {
* If no pair with that alias exists, it will be generated.
*/
@SuppressLint("GetInstance")
- public SecretKeyWrapper(Context context, String alias)
+ public SecretKeyWrapper(KeyPair keyPair)
throws GeneralSecurityException, IOException {
mCipher = Cipher.getInstance(ALGORITHM_ASYMMETRIC);
- mPair = KeyStoreHelper.loadOrGenerateAsymmetricKeyPair(context, alias);
+ mPair = keyPair;
}
/**
diff --git a/app/src/main/res/values/strings_main.xml b/app/src/main/res/values/strings_main.xml
index 5453bdd2..935db31a 100644
--- a/app/src/main/res/values/strings_main.xml
+++ b/app/src/main/res/values/strings_main.xml
@@ -63,7 +63,7 @@
Rename
Security and Backups
Last used
- KeyStore error
+ Encryption error
Please enter your device credentials to start andOTP.
Are you sure you want do remove the account \"%1$s\"?
@@ -84,6 +84,10 @@
In order for andOTP to recognize which token was used last
you have to have \"tap to reveal\" enabled or use the copy button.\n\nThis message will not
be shown again.
- Failed to load the encryption key from the KeyStore.
- You won\'t be able to use this app. Please contact the developer and provide a logcat!
+
+ Failed to generate or load the wrapping key from the
+ KeyStore. You won\'t be able to use this app. Please contact the developer and provide a
+ logcat!
+ Failed to generate or unwrap the encryption key. You
+ won\'t be able to use this app. Please contact the developer and provide a logcat!