diff --git a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java
index a9dab6370f0221813067a8410be81a650bf0c577..c931d867e48b4060ebd00591e71ee26d84a7a9e9 100644
--- a/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java
+++ b/bramble-android/src/main/java/org/briarproject/bramble/util/AndroidUtils.java
@@ -8,12 +8,10 @@ import android.os.Build;
 import android.provider.Settings;
 
 import java.io.File;
-import java.io.FileNotFoundException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
-import java.util.Scanner;
 import java.util.logging.Logger;
 
 import static android.content.Context.MODE_PRIVATE;
@@ -80,26 +78,6 @@ public class AndroidUtils {
 		}
 	}
 
-	public static File getSharedPrefsFile(Context ctx, String name) {
-		File dataDir = new File(ctx.getApplicationInfo().dataDir);
-		File prefsDir = new File(dataDir, "shared_prefs");
-		return new File(prefsDir, name + ".xml");
-	}
-
-	public static void logFileContents(File f) {
-		if (LOG.isLoggable(INFO)) {
-			LOG.info("Contents of " + f.getAbsolutePath() + ":");
-			try {
-				Scanner s = new Scanner(f);
-				while (s.hasNextLine()) LOG.info(s.nextLine());
-				s.close();
-			} catch (FileNotFoundException e) {
-				LOG.info(f.getAbsolutePath() + " not found");
-			}
-		}
-	}
-
-	@SuppressLint("ApplySharedPref")
 	public static void deleteAppData(Context ctx, SharedPreferences... clear) {
 		// Clear and commit shared preferences
 		for (SharedPreferences prefs : clear) {
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java
index 6a8aa19aa651dfbd6f6f08875b3c0a1dff9e3cf9..0be85eba93a8ab16a915e5574cc4b0329ea8112b 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseConfig.java
@@ -14,6 +14,8 @@ public interface DatabaseConfig {
 
 	File getDatabaseDirectory();
 
+	File getDatabaseKeyDirectory();
+
 	void setEncryptionKey(SecretKey key);
 
 	@Nullable
diff --git a/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java b/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java
index d6ee0325f70ff3fc03923d98ccabf92a6092ecfa..a830795f3dd4d28fecb7876df4965d543de1fb5c 100644
--- a/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java
+++ b/bramble-core/src/test/java/org/briarproject/bramble/test/TestDatabaseConfig.java
@@ -9,25 +9,31 @@ import java.io.File;
 @NotNullByDefault
 public class TestDatabaseConfig implements DatabaseConfig {
 
-	private final File dir;
+	private final File dbDir, keyDir;
 	private final long maxSize;
 	private volatile SecretKey key = new SecretKey(new byte[SecretKey.LENGTH]);
 
-	public TestDatabaseConfig(File dir, long maxSize) {
-		this.dir = dir;
+	public TestDatabaseConfig(File testDir, long maxSize) {
+		dbDir = new File(testDir, "db");
+		keyDir = new File(testDir, "key");
 		this.maxSize = maxSize;
 	}
 
 	@Override
 	public boolean databaseExists() {
-		if (!dir.isDirectory()) return false;
-		File[] files = dir.listFiles();
+		if (!dbDir.isDirectory()) return false;
+		File[] files = dbDir.listFiles();
 		return files != null && files.length > 0;
 	}
 
 	@Override
 	public File getDatabaseDirectory() {
-		return dir;
+		return dbDir;
+	}
+
+	@Override
+	public File getDatabaseKeyDirectory() {
+		return keyDir;
 	}
 
 	@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java b/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java
index 2fe5df11ae0ae6273bcf28b2be1fbeb3089ddd28..e92a356ee42f675fb8777cd1c4e361629d82367f 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AndroidDatabaseConfig.java
@@ -17,31 +17,32 @@ class AndroidDatabaseConfig implements DatabaseConfig {
 	private static final Logger LOG =
 			Logger.getLogger(AndroidDatabaseConfig.class.getName());
 
-	private final File dir;
+	private final File dbDir, keyDir;
 
 	@Nullable
 	private volatile SecretKey key = null;
 	@Nullable
 	private volatile String nickname = null;
 
-	AndroidDatabaseConfig(File dir) {
-		this.dir = dir;
+	AndroidDatabaseConfig(File dbDir, File keyDir) {
+		this.dbDir = dbDir;
+		this.keyDir = keyDir;
 	}
 
 	@Override
 	public boolean databaseExists() {
 		// FIXME should not run on UiThread #620
-		if (!dir.isDirectory()) {
+		if (!dbDir.isDirectory()) {
 			if (LOG.isLoggable(INFO))
-				LOG.info(dir.getAbsolutePath() + " is not a directory");
+				LOG.info(dbDir.getAbsolutePath() + " is not a directory");
 			return false;
 		}
-		File[] files = dir.listFiles();
+		File[] files = dbDir.listFiles();
 		if (LOG.isLoggable(INFO)) {
 			if (files == null) {
-				LOG.info("Could not list files in " + dir.getAbsolutePath());
+				LOG.info("Could not list files in " + dbDir.getAbsolutePath());
 			} else {
-				LOG.info("Files in " + dir.getAbsolutePath() + ":");
+				LOG.info("Files in " + dbDir.getAbsolutePath() + ":");
 				for (File f : files) LOG.info(f.getName());
 			}
 			LOG.info("Database exists: " + (files != null && files.length > 0));
@@ -51,10 +52,16 @@ class AndroidDatabaseConfig implements DatabaseConfig {
 
 	@Override
 	public File getDatabaseDirectory() {
-		File dir = this.dir;
 		if (LOG.isLoggable(INFO))
-			LOG.info("Database directory: " + dir.getAbsolutePath());
-		return dir;
+			LOG.info("Database directory: " + dbDir.getAbsolutePath());
+		return dbDir;
+	}
+
+	@Override
+	public File getDatabaseKeyDirectory() {
+		if (LOG.isLoggable(INFO))
+			LOG.info("Database key directory: " + keyDir.getAbsolutePath());
+		return keyDir;
 	}
 
 	@Override
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
index f8fdf0dfa23e4555c83985935dbaf1ecf1bace06..ed954e2414e1544cf18df754b7129f608c247893 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/AppModule.java
@@ -87,11 +87,13 @@ public class AppModule {
 		//FIXME: StrictMode
 		StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskReads();
 		StrictMode.allowThreadDiskWrites();
-		File dir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
+		File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
+		File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
 		StrictMode.setThreadPolicy(tp);
 		@MethodsNotNullByDefault
 		@ParametersNotNullByDefault
-		DatabaseConfig databaseConfig = new AndroidDatabaseConfig(dir);
+		DatabaseConfig databaseConfig =
+				new AndroidDatabaseConfig(dbDir, keyDir);
 		return databaseConfig;
 	}
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java
index 07b32b0e2f53bf2bd6f65b6aa165aff1c39c1411..e19452a9ce134d3baedbfe0c8478726ee90da922 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigController.java
@@ -12,7 +12,7 @@ public interface ConfigController {
 	@Nullable
 	String getEncryptedDatabaseKey();
 
-	void storeEncryptedDatabaseKey(String hex);
+	boolean storeEncryptedDatabaseKey(String hex);
 
 	void deleteAccount(Context ctx);
 
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
index b1a4168129798e5a9a4c5f48200f5842b444ab74..0248aae5ee348033293011d7e8d86d0b656f0eda 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/controller/ConfigControllerImpl.java
@@ -1,6 +1,5 @@
 package org.briarproject.briar.android.controller;
 
-import android.annotation.SuppressLint;
 import android.content.Context;
 import android.content.SharedPreferences;
 import android.support.v7.preference.PreferenceManager;
@@ -9,12 +8,19 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
 import org.briarproject.bramble.util.AndroidUtils;
 
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
 import java.util.logging.Logger;
 
 import javax.annotation.Nullable;
 import javax.inject.Inject;
 
 import static java.util.logging.Level.INFO;
+import static java.util.logging.Level.WARNING;
 
 @NotNullByDefault
 public class ConfigControllerImpl implements ConfigController {
@@ -23,8 +29,11 @@ public class ConfigControllerImpl implements ConfigController {
 			Logger.getLogger(ConfigControllerImpl.class.getName());
 
 	private static final String PREF_DB_KEY = "key";
+	private static final String DB_KEY_FILENAME = "db.key";
+	private static final String DB_KEY_BACKUP_FILENAME = "db.key.bak";
 
 	private final SharedPreferences briarPrefs;
+	private final File dbKeyFile, dbKeyBackupFile;
 	protected final DatabaseConfig databaseConfig;
 
 	@Inject
@@ -32,22 +41,113 @@ public class ConfigControllerImpl implements ConfigController {
 			DatabaseConfig databaseConfig) {
 		this.briarPrefs = briarPrefs;
 		this.databaseConfig = databaseConfig;
+		File keyDir = databaseConfig.getDatabaseKeyDirectory();
+		dbKeyFile = new File(keyDir, DB_KEY_FILENAME);
+		dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME);
 	}
 
 	@Override
 	@Nullable
 	public String getEncryptedDatabaseKey() {
+		String key = getDatabaseKeyFromPreferences();
+		if (key == null) key = getDatabaseKeyFromFile();
+		else migrateDatabaseKeyToFile(key);
+		return key;
+	}
+
+	@Nullable
+	private String getDatabaseKeyFromPreferences() {
 		String key = briarPrefs.getString(PREF_DB_KEY, null);
-		if (LOG.isLoggable(INFO))
-			LOG.info("Got database key from preferences: " + (key != null));
+		if (key == null) LOG.info("No database key in preferences");
+		else LOG.info("Found database key in preferences");
+		return key;
+	}
+
+	@Nullable
+	private String getDatabaseKeyFromFile() {
+		String key = readDbKeyFromFile(dbKeyFile);
+		if (key == null) {
+			LOG.info("No database key in primary file");
+			key = readDbKeyFromFile(dbKeyBackupFile);
+			if (key == null) LOG.info("No database key in backup file");
+			else LOG.warning("Found database key in backup file");
+		} else {
+			LOG.info("Found database key in primary file");
+		}
 		return key;
 	}
 
+	@Nullable
+	private String readDbKeyFromFile(File f) {
+		if (!f.exists()) {
+			LOG.info("Key file does not exist");
+			return null;
+		}
+		try {
+			BufferedReader reader = new BufferedReader(new InputStreamReader(
+					new FileInputStream(f), "UTF-8"));
+			String key = reader.readLine();
+			reader.close();
+			return key;
+		} catch (IOException e) {
+			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			return null;
+		}
+	}
+
+	private void migrateDatabaseKeyToFile(String key) {
+		if (storeEncryptedDatabaseKey(key)) {
+			if (briarPrefs.edit().remove(PREF_DB_KEY).commit())
+				LOG.info("Database key migrated to file");
+			else LOG.warning("Database key not removed from preferences");
+		} else {
+			LOG.warning("Database key not migrated to file");
+		}
+	}
+
 	@Override
-	@SuppressLint("ApplySharedPref")
-	public void storeEncryptedDatabaseKey(String hex) {
-		LOG.info("Storing database key in preferences");
-		briarPrefs.edit().putString(PREF_DB_KEY, hex).commit();
+	public boolean storeEncryptedDatabaseKey(String hex) {
+		LOG.info("Storing database key in file");
+		// Create the directory if necessary
+		if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
+			LOG.info("Created database key directory");
+		// If only the backup file exists, rename it so we don't overwrite it
+		if (dbKeyBackupFile.exists() && !dbKeyFile.exists()) {
+			if (dbKeyBackupFile.renameTo(dbKeyFile))
+				LOG.info("Renamed old backup");
+			else LOG.warning("Failed to rename old backup");
+		}
+		try {
+			// Write to the backup file
+			writeDbKeyToFile(hex, dbKeyBackupFile);
+			LOG.info("Stored database key in backup file");
+			// Delete the old primary file, if it exists
+			if (dbKeyFile.exists()) {
+				if (dbKeyFile.delete()) LOG.info("Deleted primary file");
+				else LOG.warning("Failed to delete primary file");
+			}
+			// The backup file becomes the new primary
+			if (dbKeyBackupFile.renameTo(dbKeyFile)) {
+				LOG.info("Renamed backup file to primary");
+			} else {
+				LOG.warning("Failed to rename backup file to primary");
+				return false; // Don't overwrite our only copy
+			}
+			// Write a second copy to the backup file
+			writeDbKeyToFile(hex, dbKeyBackupFile);
+			LOG.info("Stored second copy of database key in backup file");
+			return true;
+		} catch (IOException e) {
+			if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
+			return false;
+		}
+	}
+
+	private void writeDbKeyToFile(String key, File f) throws IOException {
+		FileOutputStream out = new FileOutputStream(f);
+		out.write(key.getBytes("UTF-8"));
+		out.flush();
+		out.close();
 	}
 
 	@Override
@@ -73,5 +173,4 @@ public class ConfigControllerImpl implements ConfigController {
 		if (LOG.isLoggable(INFO)) LOG.info("Signed in: " + signedIn);
 		return signedIn;
 	}
-
 }
diff --git a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
index d2c2e54f50e1e4048faa8a184efd0ffd0e2c9dec..121b7e119206f6e821db2bcd8868333f0420de49 100644
--- a/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
+++ b/briar-android/src/main/java/org/briarproject/briar/android/login/PasswordControllerImpl.java
@@ -72,8 +72,7 @@ public class PasswordControllerImpl extends ConfigControllerImpl
 			} else {
 				String hex =
 						encryptDatabaseKey(new SecretKey(key), newPassword);
-				storeEncryptedDatabaseKey(hex);
-				resultHandler.onResult(true);
+				resultHandler.onResult(storeEncryptedDatabaseKey(hex));
 			}
 		});
 	}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/TestDatabaseKeyUtils.java b/briar-android/src/test/java/org/briarproject/briar/android/TestDatabaseKeyUtils.java
new file mode 100644
index 0000000000000000000000000000000000000000..0e9173105fa19f99747e91c4c68d25fbb2bd2138
--- /dev/null
+++ b/briar-android/src/test/java/org/briarproject/briar/android/TestDatabaseKeyUtils.java
@@ -0,0 +1,35 @@
+package org.briarproject.briar.android;
+
+import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+
+import javax.annotation.Nullable;
+
+import static junit.framework.Assert.assertTrue;
+
+@NotNullByDefault
+public class TestDatabaseKeyUtils {
+
+	public static void storeDatabaseKey(File f, String hex) throws IOException {
+		f.getParentFile().mkdirs();
+		FileOutputStream out = new FileOutputStream(f);
+		out.write(hex.getBytes("UTF-8"));
+		out.flush();
+		out.close();
+	}
+
+	@Nullable
+	public static String loadDatabaseKey(File f) throws IOException {
+		BufferedReader reader = new BufferedReader(new InputStreamReader(
+				new FileInputStream(f), "UTF-8"));
+		String hex = reader.readLine();
+		reader.close();
+		return hex;
+	}
+}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..859792b3c9872de53ef0c8d03b430be30ef14f30
--- /dev/null
+++ b/briar-android/src/test/java/org/briarproject/briar/android/controller/ConfigControllerImplTest.java
@@ -0,0 +1,205 @@
+package org.briarproject.briar.android.controller;
+
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+import org.briarproject.bramble.api.db.DatabaseConfig;
+import org.briarproject.bramble.test.BrambleMockTestCase;
+import org.jmock.Expectations;
+import org.junit.After;
+import org.junit.Test;
+
+import java.io.File;
+
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertNull;
+import static junit.framework.Assert.assertTrue;
+import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
+import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
+import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
+import static org.briarproject.bramble.util.StringUtils.toHexString;
+import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
+import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
+
+public class ConfigControllerImplTest extends BrambleMockTestCase {
+
+	private final SharedPreferences prefs =
+			context.mock(SharedPreferences.class);
+	private final DatabaseConfig databaseConfig =
+			context.mock(DatabaseConfig.class);
+	private final Editor editor = context.mock(Editor.class);
+
+	private final byte[] encryptedKey = getRandomBytes(123);
+	private final String encryptedKeyHex = toHexString(encryptedKey);
+	private final String oldEncryptedKeyHex = toHexString(getRandomBytes(123));
+	private final File testDir = getTestDirectory();
+	private final File keyDir = new File(testDir, "key");
+	private final File keyFile = new File(keyDir, "db.key");
+	private final File keyBackupFile = new File(keyDir, "db.key.bak");
+
+	@Test
+	public void testDbKeyIsMigratedFromPreferencesToFile() throws Exception {
+		context.checking(new Expectations() {{
+			oneOf(prefs).getString("key", null);
+			will(returnValue(encryptedKeyHex));
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
+			oneOf(prefs).edit();
+			will(returnValue(editor));
+			oneOf(editor).remove("key");
+			will(returnValue(editor));
+			oneOf(editor).commit();
+			will(returnValue(true));
+		}});
+
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		ConfigControllerImpl c = new ConfigControllerImpl(prefs,
+				databaseConfig);
+
+		assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
+
+		assertTrue(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+	}
+
+	@Test
+	public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
+		context.checking(new Expectations() {{
+			oneOf(prefs).getString("key", null);
+			will(returnValue(null));
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
+		}});
+
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		storeDatabaseKey(keyFile, encryptedKeyHex);
+
+		assertTrue(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+
+		ConfigControllerImpl c = new ConfigControllerImpl(prefs,
+				databaseConfig);
+
+		assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
+
+		assertTrue(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+	}
+
+	@Test
+	public void testDbKeyIsLoadedFromBackupFile() throws Exception {
+		context.checking(new Expectations() {{
+			oneOf(prefs).getString("key", null);
+			will(returnValue(null));
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
+		}});
+
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		storeDatabaseKey(keyBackupFile, encryptedKeyHex);
+
+		assertFalse(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+
+		ConfigControllerImpl c = new ConfigControllerImpl(prefs,
+				databaseConfig);
+
+		assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
+
+		assertFalse(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+	}
+
+	@Test
+	public void testDbKeyIsNullIfNotFound() {
+		context.checking(new Expectations() {{
+			oneOf(prefs).getString("key", null);
+			will(returnValue(null));
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
+		}});
+
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		ConfigControllerImpl c = new ConfigControllerImpl(prefs,
+				databaseConfig);
+
+		assertNull(c.getEncryptedDatabaseKey());
+
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+	}
+
+	@Test
+	public void testStoringDbKeyOverwritesPrimary() throws Exception {
+		context.checking(new Expectations() {{
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
+		}});
+
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		storeDatabaseKey(keyFile, oldEncryptedKeyHex);
+
+		assertTrue(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+		assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyFile));
+
+		ConfigController c = new ConfigControllerImpl(prefs,
+				databaseConfig);
+
+		assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
+
+		assertTrue(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+	}
+
+	@Test
+	public void testStoringDbKeyOverwritesBackup() throws Exception {
+		context.checking(new Expectations() {{
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
+		}});
+
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		storeDatabaseKey(keyBackupFile, oldEncryptedKeyHex);
+
+		assertFalse(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
+
+		ConfigController c = new ConfigControllerImpl(prefs,
+				databaseConfig);
+
+		assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
+
+		assertTrue(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
+		assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
+	}
+
+	@After
+	public void tearDown() {
+		deleteTestDirectory(testDir);
+	}
+}
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java
index 9807c09c39f5811a37fe3d699a51bb7fc904bb6d..427dfa772d1cb52b75e2264344e8f4c2c8cf66d3 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/PasswordControllerImplTest.java
@@ -8,14 +8,23 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.test.BrambleMockTestCase;
 import org.briarproject.bramble.test.ImmediateExecutor;
 import org.jmock.Expectations;
+import org.junit.After;
 import org.junit.Test;
 
+import java.io.File;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
+import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
+import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
 import static org.briarproject.bramble.test.TestUtils.getSecretKey;
+import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
+import static org.briarproject.bramble.util.StringUtils.toHexString;
+import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
+import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
 
 public class PasswordControllerImplTest extends BrambleMockTestCase {
 
@@ -26,62 +35,90 @@ public class PasswordControllerImplTest extends BrambleMockTestCase {
 	private final CryptoComponent crypto = context.mock(CryptoComponent.class);
 	private final PasswordStrengthEstimator estimator =
 			context.mock(PasswordStrengthEstimator.class);
-	private final SharedPreferences.Editor editor =
-			context.mock(SharedPreferences.Editor.class);
 
 	private final Executor cryptoExecutor = new ImmediateExecutor();
 
 	private final String oldPassword = "some.old.pass";
 	private final String newPassword = "some.new.pass";
-	private final String oldEncryptedHex = "010203";
-	private final String newEncryptedHex = "020304";
-	private final byte[] oldEncryptedBytes = new byte[] {1, 2, 3};
-	private final byte[] newEncryptedBytes = new byte[] {2, 3, 4};
-	private final byte[] keyBytes = getSecretKey().getBytes();
+	private final byte[] oldEncryptedKey = getRandomBytes(123);
+	private final byte[] newEncryptedKey = getRandomBytes(123);
+	private final byte[] key = getSecretKey().getBytes();
+	private final File testDir = getTestDirectory();
+	private final File keyDir = new File(testDir, "key");
+	private final File keyFile = new File(keyDir, "db.key");
+	private final File keyBackupFile = new File(keyDir, "db.key.bak");
 
 	@Test
-	public void testChangePasswordReturnsTrue() {
+	public void testChangePasswordReturnsTrue() throws Exception {
 		context.checking(new Expectations() {{
 			// Look up the encrypted DB key
 			oneOf(briarPrefs).getString("key", null);
-			will(returnValue(oldEncryptedHex));
+			will(returnValue(null));
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
 			// Decrypt and re-encrypt the key
-			oneOf(crypto).decryptWithPassword(oldEncryptedBytes, oldPassword);
-			will(returnValue(keyBytes));
-			oneOf(crypto).encryptWithPassword(keyBytes, newPassword);
-			will(returnValue(newEncryptedBytes));
-			// Store the re-encrypted key
-			oneOf(briarPrefs).edit();
-			will(returnValue(editor));
-			oneOf(editor).putString("key", newEncryptedHex);
-			will(returnValue(editor));
-			oneOf(editor).commit();
+			oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
+			will(returnValue(key));
+			oneOf(crypto).encryptWithPassword(key, newPassword);
+			will(returnValue(newEncryptedKey));
 		}});
 
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
+		storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
+
 		PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
 				databaseConfig, cryptoExecutor, crypto, estimator);
 
 		AtomicBoolean capturedResult = new AtomicBoolean(false);
 		p.changePassword(oldPassword, newPassword, capturedResult::set);
 		assertTrue(capturedResult.get());
+
+		assertTrue(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(toHexString(newEncryptedKey), loadDatabaseKey(keyFile));
+		assertEquals(toHexString(newEncryptedKey),
+				loadDatabaseKey(keyBackupFile));
 	}
 
 	@Test
-	public void testChangePasswordReturnsFalseIfOldPasswordIsWrong() {
+	public void testChangePasswordReturnsFalseIfOldPasswordIsWrong()
+			throws Exception {
 		context.checking(new Expectations() {{
 			// Look up the encrypted DB key
 			oneOf(briarPrefs).getString("key", null);
-			will(returnValue(oldEncryptedHex));
+			will(returnValue(null));
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
 			// Try to decrypt the key - the password is wrong
-			oneOf(crypto).decryptWithPassword(oldEncryptedBytes, oldPassword);
+			oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
 			will(returnValue(null));
 		}});
 
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
+		storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
+		storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
+
 		PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
 				databaseConfig, cryptoExecutor, crypto, estimator);
 
 		AtomicBoolean capturedResult = new AtomicBoolean(true);
 		p.changePassword(oldPassword, newPassword, capturedResult::set);
 		assertFalse(capturedResult.get());
+
+		assertTrue(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(toHexString(oldEncryptedKey), loadDatabaseKey(keyFile));
+		assertEquals(toHexString(oldEncryptedKey),
+				loadDatabaseKey(keyBackupFile));
+	}
+
+	@After
+	public void tearDown() {
+		deleteTestDirectory(testDir);
 	}
 }
diff --git a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java
index 1aa79e74f80fbda9a087384781e45fd9f2e50a2b..b0df4cde8b324b80c67a42f4656749b08828a5b8 100644
--- a/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java
+++ b/briar-android/src/test/java/org/briarproject/briar/android/login/SetupControllerImplTest.java
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.crypto.SecretKey;
 import org.briarproject.bramble.api.db.DatabaseConfig;
 import org.briarproject.bramble.test.BrambleMockTestCase;
 import org.briarproject.bramble.test.ImmediateExecutor;
-import org.briarproject.bramble.test.TestUtils;
 import org.jmock.Expectations;
 import org.jmock.lib.legacy.ClassImposteriser;
 import org.junit.After;
@@ -19,10 +18,17 @@ import java.io.File;
 import java.util.concurrent.Executor;
 import java.util.concurrent.atomic.AtomicBoolean;
 
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
 import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
+import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
+import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
 import static org.briarproject.bramble.test.TestUtils.getSecretKey;
+import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
 import static org.briarproject.bramble.util.StringUtils.getRandomString;
+import static org.briarproject.bramble.util.StringUtils.toHexString;
+import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
 
 public class SetupControllerImplTest extends BrambleMockTestCase {
 
@@ -33,18 +39,18 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
 	private final CryptoComponent crypto = context.mock(CryptoComponent.class);
 	private final PasswordStrengthEstimator estimator =
 			context.mock(PasswordStrengthEstimator.class);
-	private final SharedPreferences.Editor editor =
-			context.mock(SharedPreferences.Editor.class);
 	private final SetupActivity setupActivity;
 
 	private final Executor cryptoExecutor = new ImmediateExecutor();
 
 	private final String authorName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
 	private final String password = "some.strong.pass";
-	private final String encryptedHex = "010203";
-	private final byte[] encryptedBytes = new byte[] {1, 2, 3};
+	private final byte[] encryptedKey = getRandomBytes(123);
 	private final SecretKey key = getSecretKey();
-	private final File testDir = TestUtils.getTestDirectory();
+	private final File testDir = getTestDirectory();
+	private final File keyDir = new File(testDir, "key");
+	private final File keyFile = new File(keyDir, "db.key");
+	private final File keyBackupFile = new File(keyDir, "db.key.bak");
 
 	public SetupControllerImplTest() {
 		context.setImposteriser(ClassImposteriser.INSTANCE);
@@ -53,7 +59,7 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
 
 	@Test
 	@SuppressWarnings("ResultOfMethodCallIgnored")
-	public void testCreateAccount() {
+	public void testCreateAccount() throws Exception {
 		context.checking(new Expectations() {{
 			// Allow the contents of the data directory to be logged
 			allowing(setupActivity).getApplicationInfo();
@@ -76,15 +82,15 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
 			oneOf(databaseConfig).setEncryptionKey(key);
 			// Encrypt the key with the password
 			oneOf(crypto).encryptWithPassword(key.getBytes(), password);
-			will(returnValue(encryptedBytes));
+			will(returnValue(encryptedKey));
 			// Store the encrypted key
-			oneOf(briarPrefs).edit();
-			will(returnValue(editor));
-			oneOf(editor).putString("key", encryptedHex);
-			will(returnValue(editor));
-			oneOf(editor).commit();
+			allowing(databaseConfig).getDatabaseKeyDirectory();
+			will(returnValue(keyDir));
 		}});
 
+		assertFalse(keyFile.exists());
+		assertFalse(keyBackupFile.exists());
+
 		SetupControllerImpl s = new SetupControllerImpl(briarPrefs,
 				databaseConfig, cryptoExecutor, crypto, estimator);
 		s.setSetupActivity(setupActivity);
@@ -94,10 +100,15 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
 		s.setPassword(password);
 		s.createAccount(result -> called.set(true));
 		assertTrue(called.get());
+
+		assertTrue(keyFile.exists());
+		assertTrue(keyBackupFile.exists());
+		assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyFile));
+		assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyBackupFile));
 	}
 
 	@After
 	public void tearDown() {
-		TestUtils.deleteTestDirectory(testDir);
+		deleteTestDirectory(testDir);
 	}
 }