diff --git a/briar-android/src/net/sf/briar/android/SetupActivity.java b/briar-android/src/net/sf/briar/android/SetupActivity.java
index 73dd6b6261917c928951596648684f9c04ff319c..14fc2bed19fc4f179512fefec256a5412e348dbe 100644
--- a/briar-android/src/net/sf/briar/android/SetupActivity.java
+++ b/briar-android/src/net/sf/briar/android/SetupActivity.java
@@ -13,7 +13,6 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
 import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
 
 import java.io.IOException;
-import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.concurrent.Executor;
 
@@ -23,6 +22,7 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.ReferenceManager;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.CryptoExecutor;
+import net.sf.briar.api.crypto.KeyPair;
 import net.sf.briar.api.db.DatabaseConfig;
 import net.sf.briar.util.StringUtils;
 import roboguice.activity.RoboActivity;
diff --git a/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java b/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java
index 00a4519dfefcad5d00a412216c326068c85d69ef..9f7cbeadeec7410980efbf288068f5c95c876cce 100644
--- a/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java
+++ b/briar-android/src/net/sf/briar/android/blogs/CreateBlogActivity.java
@@ -14,7 +14,6 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_MATCH;
 import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
 
 import java.io.IOException;
-import java.security.KeyPair;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.concurrent.Executor;
@@ -29,6 +28,7 @@ import net.sf.briar.api.ContactId;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.CryptoExecutor;
+import net.sf.briar.api.crypto.KeyPair;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.lifecycle.LifecycleManager;
diff --git a/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java b/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java
index d8885ffe34461406c063d6292e42a138c7790e2f..d28262c042c801d0fa1d30ecfecbe34e94189117 100644
--- a/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java
+++ b/briar-android/src/net/sf/briar/android/blogs/WriteBlogPostActivity.java
@@ -11,7 +11,6 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
 import java.util.Collection;
 import java.util.concurrent.Executor;
 import java.util.logging.Logger;
@@ -27,6 +26,7 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.crypto.PrivateKey;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.lifecycle.LifecycleManager;
diff --git a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
index d2bba879249ff600168aec3635715a9ffa755473..5c7b3ac37d8a7fe8baa459b02973a809ee035a9f 100644
--- a/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
+++ b/briar-android/src/net/sf/briar/android/groups/WriteGroupPostActivity.java
@@ -11,7 +11,6 @@ import static net.sf.briar.android.util.CommonLayoutParams.MATCH_WRAP;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Collections;
@@ -30,6 +29,7 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.crypto.PrivateKey;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.lifecycle.LifecycleManager;
diff --git a/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java b/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
index f89fff502b3ce5a6e99d9b12e406f0457e2b24a0..1df435f8d6b3bb956075762c83a4e93a38f83276 100644
--- a/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
+++ b/briar-android/src/net/sf/briar/android/identity/CreateIdentityActivity.java
@@ -15,7 +15,6 @@ import static net.sf.briar.android.util.CommonLayoutParams.WRAP_WRAP;
 import static net.sf.briar.api.messaging.Rating.GOOD;
 
 import java.io.IOException;
-import java.security.KeyPair;
 import java.util.concurrent.Executor;
 import java.util.logging.Logger;
 
@@ -25,6 +24,7 @@ import net.sf.briar.api.LocalAuthor;
 import net.sf.briar.api.android.DatabaseUiExecutor;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.CryptoExecutor;
+import net.sf.briar.api.crypto.KeyPair;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.lifecycle.LifecycleManager;
diff --git a/briar-api/src/net/sf/briar/api/crypto/AuthenticatedCipher.java b/briar-api/src/net/sf/briar/api/crypto/AuthenticatedCipher.java
index 0dfa322af5e79bd872598e3408b0b511d12769fe..6105b70dc6f2fc231e4952dff6f090d7c183a3ac 100644
--- a/briar-api/src/net/sf/briar/api/crypto/AuthenticatedCipher.java
+++ b/briar-api/src/net/sf/briar/api/crypto/AuthenticatedCipher.java
@@ -1,28 +1,20 @@
 package net.sf.briar.api.crypto;
 
-import java.security.InvalidKeyException;
-import java.security.Key;
+import java.security.GeneralSecurityException;
 
-import javax.crypto.BadPaddingException;
-import javax.crypto.IllegalBlockSizeException;
-
-/**
- * A wrapper for a provider-dependent cipher class, since javax.crypto.Cipher
- * doesn't support additional authenticated data until Java 7.
- */
+/** An authenticated cipher that support additional authenticated data. */
 public interface AuthenticatedCipher {
 
 	/**
 	 * Initializes this cipher with a key, an initialisation vector (IV) and
 	 * additional authenticated data (AAD).
 	 */
-	void init(int opmode, Key key, byte[] iv, byte[] aad)
-			throws InvalidKeyException;
+	void init(int opmode, SecretKey key, byte[] iv, byte[] aad)
+			throws GeneralSecurityException;
 
 	/** Encrypts or decrypts data in a single-part operation. */
 	int doFinal(byte[] input, int inputOff, int len, byte[] output,
-			int outputOff) throws IllegalBlockSizeException,
-			BadPaddingException;
+			int outputOff) throws GeneralSecurityException;
 
 	/** Returns the length of the message authenticated code (MAC) in bytes. */
 	int getMacLength();
diff --git a/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java b/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java
index a6ae9cbd3ac09b56541bdaeb3f23d8fda63d545f..baee643e92896ab1a0efb9b3f5ae43c706294bbc 100644
--- a/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/briar-api/src/net/sf/briar/api/crypto/CryptoComponent.java
@@ -1,13 +1,11 @@
 package net.sf.briar.api.crypto;
 
 import java.security.GeneralSecurityException;
-import java.security.KeyPair;
 import java.security.SecureRandom;
-import java.security.Signature;
 
 public interface CryptoComponent {
 
-	ErasableKey generateSecretKey();
+	SecretKey generateSecretKey();
 
 	MessageDigest getMessageDigest();
 
@@ -67,7 +65,7 @@ public interface CryptoComponent {
 	 * @param alice indicates whether the key is for connections initiated by
 	 * Alice or Bob.
 	 */
-	ErasableKey deriveTagKey(byte[] secret, boolean alice);
+	SecretKey deriveTagKey(byte[] secret, boolean alice);
 
 	/**
 	 * Derives a frame key from the given temporary secret and connection
@@ -77,14 +75,14 @@ public interface CryptoComponent {
 	 * @param initiator indicates whether the key is for the initiator's or the
 	 * responder's side of the connection.
 	 */
-	ErasableKey deriveFrameKey(byte[] secret, long connection, boolean alice,
+	SecretKey deriveFrameKey(byte[] secret, long connection, boolean alice,
 			boolean initiator);
 
 	/** Returns a cipher for encrypting and authenticating connections. */
 	AuthenticatedCipher getFrameCipher();
 
 	/** Encodes the pseudo-random tag that is used to recognise a connection. */
-	void encodeTag(byte[] tag, ErasableKey tagKey, long connection);
+	void encodeTag(byte[] tag, SecretKey tagKey, long connection);
 
 	/**
 	 * Encrypts and authenticates the given plaintext so it can be written to
diff --git a/briar-api/src/net/sf/briar/api/crypto/ErasableKey.java b/briar-api/src/net/sf/briar/api/crypto/ErasableKey.java
deleted file mode 100644
index 3503898ede179c0922a0e058f990c6441a83314e..0000000000000000000000000000000000000000
--- a/briar-api/src/net/sf/briar/api/crypto/ErasableKey.java
+++ /dev/null
@@ -1,12 +0,0 @@
-package net.sf.briar.api.crypto;
-
-import javax.crypto.SecretKey;
-
-public interface ErasableKey extends SecretKey {
-
-	/** Returns a copy of the key. */
-	ErasableKey copy();
-
-	/** Erases the key from memory. */
-	void erase();
-}
diff --git a/briar-api/src/net/sf/briar/api/crypto/KeyPair.java b/briar-api/src/net/sf/briar/api/crypto/KeyPair.java
new file mode 100644
index 0000000000000000000000000000000000000000..76be830d717ef67292c4d5fa40e249b7194b8dd1
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/crypto/KeyPair.java
@@ -0,0 +1,21 @@
+package net.sf.briar.api.crypto;
+
+/** A key pair consisting of a {@link PublicKey} and a {@link PrivateKey). */
+public class KeyPair {
+
+	private final PublicKey publicKey;
+	private final PrivateKey privateKey;
+
+	public KeyPair(PublicKey publicKey, PrivateKey privateKey) {
+		this.publicKey = publicKey;
+		this.privateKey = privateKey;
+	}
+
+	public PublicKey getPublic() {
+		return publicKey;
+	}
+
+	public PrivateKey getPrivate() {
+		return privateKey;
+	}
+}
diff --git a/briar-api/src/net/sf/briar/api/crypto/KeyParser.java b/briar-api/src/net/sf/briar/api/crypto/KeyParser.java
index da47281a822d2ed0144dd1548863986ac18e3517..835dcb3d736f01e146e95223a2ff207130422468 100644
--- a/briar-api/src/net/sf/briar/api/crypto/KeyParser.java
+++ b/briar-api/src/net/sf/briar/api/crypto/KeyParser.java
@@ -1,13 +1,11 @@
 package net.sf.briar.api.crypto;
 
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.spec.InvalidKeySpecException;
+import java.security.GeneralSecurityException;
 
 public interface KeyParser {
 
-	PublicKey parsePublicKey(byte[] encodedKey) throws InvalidKeySpecException;
+	PublicKey parsePublicKey(byte[] encodedKey) throws GeneralSecurityException;
 
 	PrivateKey parsePrivateKey(byte[] encodedKey)
-			throws InvalidKeySpecException;
+			throws GeneralSecurityException;
 }
diff --git a/briar-api/src/net/sf/briar/api/crypto/MessageDigest.java b/briar-api/src/net/sf/briar/api/crypto/MessageDigest.java
index 3855da89295be46d936fd2e8f92f7527150d4e52..406bd5dc74ee8ce02f02f1f5f7c393b68957c250 100644
--- a/briar-api/src/net/sf/briar/api/crypto/MessageDigest.java
+++ b/briar-api/src/net/sf/briar/api/crypto/MessageDigest.java
@@ -1,9 +1,5 @@
 package net.sf.briar.api.crypto;
 
-/**
- * A wrapper around a {@link java.security.MessageDigest} that allows it to be
- * replaced for testing.
- */
 public interface MessageDigest {
 
 	/** @see {@link java.security.MessageDigest#digest()} */
diff --git a/briar-api/src/net/sf/briar/api/crypto/PrivateKey.java b/briar-api/src/net/sf/briar/api/crypto/PrivateKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..332bf906c867f0cf3d57c9ea27a796ea67fe9e18
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/crypto/PrivateKey.java
@@ -0,0 +1,8 @@
+package net.sf.briar.api.crypto;
+
+/** The private half of a public/private {@link KeyPair}. */
+public interface PrivateKey {
+
+	/** Returns the encoded representation of this key. */
+	byte[] getEncoded();
+}
diff --git a/briar-api/src/net/sf/briar/api/crypto/PublicKey.java b/briar-api/src/net/sf/briar/api/crypto/PublicKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..27b851cf6ba3de8f5ddcee777f11278987c02b98
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/crypto/PublicKey.java
@@ -0,0 +1,8 @@
+package net.sf.briar.api.crypto;
+
+/** The public half of a public/private {@link KeyPair}. */
+public interface PublicKey {
+
+	/** Returns the encoded representation of this key. */
+	byte[] getEncoded();
+}
diff --git a/briar-api/src/net/sf/briar/api/crypto/SecretKey.java b/briar-api/src/net/sf/briar/api/crypto/SecretKey.java
new file mode 100644
index 0000000000000000000000000000000000000000..1149ae75399e511abf872078d830bf6384cb211d
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/crypto/SecretKey.java
@@ -0,0 +1,21 @@
+package net.sf.briar.api.crypto;
+
+/** A secret key used for encryption and/or authentication. */
+public interface SecretKey {
+
+	/** Returns the encoded representation of this key. */
+	byte[] getEncoded();
+
+	/**
+	 * Returns a copy of this key - erasing this key will erase the copy and
+	 * vice versa.
+	 */
+	SecretKey copy();
+
+	/**
+	 * Erases this key from memory. Any copies derived from this key via the
+	 * {@link #copy()} method, and any keys from which this key was derived via
+	 * the {@link #copy()} method, are also erased.
+	 */
+	void erase();
+}
diff --git a/briar-api/src/net/sf/briar/api/crypto/Signature.java b/briar-api/src/net/sf/briar/api/crypto/Signature.java
new file mode 100644
index 0000000000000000000000000000000000000000..f09595608b85f5d8b0a7f8a814cbaa380119cd85
--- /dev/null
+++ b/briar-api/src/net/sf/briar/api/crypto/Signature.java
@@ -0,0 +1,31 @@
+package net.sf.briar.api.crypto;
+
+import java.security.GeneralSecurityException;
+
+public interface Signature {
+
+	/**
+	 * @see {@link java.security.Signature#initSign(java.security.PrivateKey)}
+	 */
+	void initSign(PrivateKey k) throws GeneralSecurityException;
+
+	/**
+	 * @see {@link java.security.Signature#initVafiry(java.security.PublicKey)}
+	 */
+	void initVerify(PublicKey k) throws GeneralSecurityException;
+
+	/** @see {@link java.security.Signature#update(byte)} */
+	void update(byte b);
+
+	/** @see {@link java.security.Signature#update(byte[])} */
+	void update(byte[] b);
+
+	/** @see {@link java.security.Signature#update(byte[], int, int)} */
+	void update(byte[] b, int off, int len);
+
+	/** @see {@link java.security.Signature#sign()} */
+	byte[] sign();
+
+	/** @see {@link java.security.Signature#verify(byte[])} */
+	boolean verify(byte[] signature);
+}
diff --git a/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java b/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java
index a18b24df92f1387528ec99a470740719209ab6b0..4704d319d0bdafd51cdd9a37092db4f74d955fd5 100644
--- a/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java
+++ b/briar-api/src/net/sf/briar/api/messaging/MessageFactory.java
@@ -2,9 +2,9 @@ package net.sf.briar.api.messaging;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
 
 import net.sf.briar.api.Author;
+import net.sf.briar.api.crypto.PrivateKey;
 
 public interface MessageFactory {
 
diff --git a/briar-api/src/net/sf/briar/api/serial/SigningConsumer.java b/briar-api/src/net/sf/briar/api/serial/SigningConsumer.java
index e370317725eb5cd3e86e3e5c9fe6a5f544ccc2df..871d009263bb22c814eeec2358bfe061dab8603e 100644
--- a/briar-api/src/net/sf/briar/api/serial/SigningConsumer.java
+++ b/briar-api/src/net/sf/briar/api/serial/SigningConsumer.java
@@ -1,8 +1,6 @@
 package net.sf.briar.api.serial;
 
-import java.io.IOException;
-import java.security.Signature;
-import java.security.SignatureException;
+import net.sf.briar.api.crypto.Signature;
 
 /** A consumer that passes its input through a signature. */
 public class SigningConsumer implements Consumer {
@@ -13,19 +11,11 @@ public class SigningConsumer implements Consumer {
 		this.signature = signature;
 	}
 
-	public void write(byte b) throws IOException {
-		try {
-			signature.update(b);
-		} catch(SignatureException e) {
-			throw new IOException(e.toString());
-		}
+	public void write(byte b) {
+		signature.update(b);
 	}
 
-	public void write(byte[] b, int off, int len) throws IOException {
-		try {
-			signature.update(b, off, len);
-		} catch(SignatureException e) {
-			throw new IOException(e.toString());
-		}
+	public void write(byte[] b, int off, int len) {
+		signature.update(b, off, len);
 	}
 }
diff --git a/briar-core/.classpath b/briar-core/.classpath
index 86223b4428bcc3abb1c073ee13f09efd751f2fdf..60b3f548e5ae3922884a29707d4458eadebde45a 100644
--- a/briar-core/.classpath
+++ b/briar-core/.classpath
@@ -6,7 +6,6 @@
 	<classpathentry kind="lib" path="libs/jnotify-0.93.jar"/>
 	<classpathentry kind="lib" path="libs/jssc-0.9-briar.jar" sourcepath="libs/source/jssc-0.9-briar-source.jar"/>
 	<classpathentry kind="lib" path="libs/sc-light-jdk15on-1.47.0.3-SNAPSHOT.jar" sourcepath="libs/source/sc-light-jdk15on-1.47.0.3-SNAPSHOT-source.jar"/>
-	<classpathentry kind="lib" path="libs/scprov-jdk15on-1.47.0.3-SNAPSHOT.jar" sourcepath="libs/source/scprov-jdk15on-1.47.0.3-SNAPSHOT-source.jar"/>
 	<classpathentry kind="lib" path="libs/weupnp-0.1.1.jar"/>
 	<classpathentry kind="lib" path="libs/bluecove-2.1.1-SNAPSHOT-briar.jar"/>
 	<classpathentry kind="lib" path="libs/bluecove-gpl-2.1.1-SNAPSHOT.jar"/>
diff --git a/briar-core/libs/scprov-jdk15on-1.47.0.3-SNAPSHOT.jar b/briar-core/libs/scprov-jdk15on-1.47.0.3-SNAPSHOT.jar
deleted file mode 100644
index 4433cbb9589a4849281d51aaef404551d660974e..0000000000000000000000000000000000000000
Binary files a/briar-core/libs/scprov-jdk15on-1.47.0.3-SNAPSHOT.jar and /dev/null differ
diff --git a/briar-core/src/net/sf/briar/crypto/AuthenticatedCipherImpl.java b/briar-core/src/net/sf/briar/crypto/AuthenticatedCipherImpl.java
index 57d39f792df5672a6a380020b0f5a8c90c76b786..4597ebd34a54c7fb602a7327cc6de1c95c700cf5 100644
--- a/briar-core/src/net/sf/briar/crypto/AuthenticatedCipherImpl.java
+++ b/briar-core/src/net/sf/briar/crypto/AuthenticatedCipherImpl.java
@@ -1,13 +1,11 @@
 package net.sf.briar.crypto;
 
-import java.security.InvalidKeyException;
-import java.security.Key;
+import java.security.GeneralSecurityException;
 
-import javax.crypto.BadPaddingException;
 import javax.crypto.Cipher;
-import javax.crypto.IllegalBlockSizeException;
 
 import net.sf.briar.api.crypto.AuthenticatedCipher;
+import net.sf.briar.api.crypto.SecretKey;
 
 import org.spongycastle.crypto.DataLengthException;
 import org.spongycastle.crypto.InvalidCipherTextException;
@@ -26,8 +24,7 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher {
 	}
 
 	public int doFinal(byte[] input, int inputOff, int len, byte[] output,
-			int outputOff) throws IllegalBlockSizeException,
-			BadPaddingException {
+			int outputOff) throws GeneralSecurityException {
 		int processed = 0;
 		if(len != 0) {
 			processed = cipher.processBytes(input, inputOff, len, output,
@@ -36,14 +33,14 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher {
 		try {
 			return processed + cipher.doFinal(output, outputOff + processed);
 		} catch(DataLengthException e) {
-			throw new IllegalBlockSizeException(e.getMessage());
+			throw new GeneralSecurityException(e.getMessage());
 		} catch(InvalidCipherTextException e) {
-			throw new BadPaddingException(e.getMessage());
+			throw new GeneralSecurityException(e.getMessage());
 		}
 	}
 
-	public void init(int opmode, Key key, byte[] iv, byte[] aad)
-			throws InvalidKeyException {
+	public void init(int opmode, SecretKey key, byte[] iv, byte[] aad)
+			throws GeneralSecurityException {
 		KeyParameter k = new KeyParameter(key.getEncoded());
 		AEADParameters params = new AEADParameters(k, macLength * 8, iv, aad);
 		try {
@@ -59,8 +56,8 @@ class AuthenticatedCipherImpl implements AuthenticatedCipher {
 			default:
 				throw new IllegalArgumentException();
 			}
-		} catch(Exception e) {
-			throw new InvalidKeyException(e.getMessage());
+		} catch(IllegalArgumentException e) {
+			throw new GeneralSecurityException(e.getMessage());
 		}
 	}
 
diff --git a/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java b/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java
index da0a4b90bcbc2344f5026620118b7deac4fff3f5..4063ddf3ef93113831d6e63c5f7540850252a776 100644
--- a/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/briar-core/src/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -1,72 +1,54 @@
 package net.sf.briar.crypto;
 
-import static java.util.logging.Level.INFO;
 import static javax.crypto.Cipher.DECRYPT_MODE;
 import static javax.crypto.Cipher.ENCRYPT_MODE;
 import static net.sf.briar.api.invitation.InvitationConstants.CODE_BITS;
 import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
+import static net.sf.briar.crypto.P384Constants.P_384_PARAMS;
+import static net.sf.briar.crypto.P384Constants.P_384_Q;
 import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.math.BigInteger;
 import java.security.GeneralSecurityException;
-import java.security.KeyFactory;
-import java.security.KeyPair;
-import java.security.KeyPairGenerator;
-import java.security.PrivateKey;
-import java.security.PublicKey;
 import java.security.SecureRandom;
-import java.security.Security;
-import java.security.Signature;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECField;
-import java.security.spec.ECFieldFp;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.EllipticCurve;
 import java.util.Arrays;
-import java.util.logging.Logger;
-
-import javax.crypto.KeyAgreement;
 
 import net.sf.briar.api.crypto.AuthenticatedCipher;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.KeyPair;
 import net.sf.briar.api.crypto.KeyParser;
 import net.sf.briar.api.crypto.MessageDigest;
+import net.sf.briar.api.crypto.PrivateKey;
 import net.sf.briar.api.crypto.PseudoRandom;
+import net.sf.briar.api.crypto.PublicKey;
+import net.sf.briar.api.crypto.SecretKey;
+import net.sf.briar.api.crypto.Signature;
 import net.sf.briar.util.ByteUtils;
 
+import org.spongycastle.crypto.AsymmetricCipherKeyPair;
 import org.spongycastle.crypto.BlockCipher;
 import org.spongycastle.crypto.CipherParameters;
+import org.spongycastle.crypto.agreement.ECDHCBasicAgreement;
+import org.spongycastle.crypto.digests.SHA384Digest;
 import org.spongycastle.crypto.engines.AESFastEngine;
+import org.spongycastle.crypto.generators.ECKeyPairGenerator;
 import org.spongycastle.crypto.generators.PKCS5S2ParametersGenerator;
 import org.spongycastle.crypto.modes.AEADBlockCipher;
 import org.spongycastle.crypto.modes.GCMBlockCipher;
+import org.spongycastle.crypto.params.ECKeyGenerationParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
 import org.spongycastle.crypto.params.KeyParameter;
-import org.spongycastle.jcajce.provider.asymmetric.ec.KeyPairGeneratorSpi;
-import org.spongycastle.jcajce.provider.digest.SHA384;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
 import org.spongycastle.util.Strings;
 
 class CryptoComponentImpl implements CryptoComponent {
 
-	private static final Logger LOG =
-			Logger.getLogger(CryptoComponentImpl.class.getName());
-
-	private static final String PROVIDER = "SC"; // Spongy Castle
-	private static final String CIPHER_ALGO = "AES";
 	private static final int CIPHER_BLOCK_BYTES = 16; // 128 bits
 	private static final int CIPHER_KEY_BYTES = 32; // 256 bits
-	private static final String AGREEMENT_ALGO = "ECDHC";
-	private static final String AGREEMENT_KEY_PAIR_ALGO = "ECDH";
 	private static final int AGREEMENT_KEY_PAIR_BITS = 384;
-	private static final String SIGNATURE_ALGO = "ECDSA";
-	private static final String SIGNATURE_KEY_PAIR_ALGO = "ECDSA";
 	private static final int SIGNATURE_KEY_PAIR_BITS = 384;
-	private static final int GCM_MAC_BYTES = 16; // 128 bits
+	private static final int MAC_BYTES = 16; // 128 bits
 	private static final int STORAGE_IV_BYTES = 16; // 128 bits
 	private static final int PBKDF_SALT_BYTES = 16; // 128 bits
 	private static final int PBKDF_ITERATIONS = 1000;
@@ -93,83 +75,33 @@ class CryptoComponentImpl implements CryptoComponent {
 	// Blank secret for argument validation
 	private static final byte[] BLANK_SECRET = new byte[CIPHER_KEY_BYTES];
 
-	// Parameters for NIST elliptic curve P-384 - see "Suite B Implementer's
-	// Guide to NIST SP 800-56A", section A.2
-	private static final BigInteger P_384_Q = new BigInteger("FFFFFFFF" +
-			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
-			"FFFFFFFF" + "FFFFFFFE" + "FFFFFFFF" + "00000000" + "00000000" +
-			"FFFFFFFF", 16);
-	private static final BigInteger P_384_A = new BigInteger("FFFFFFFF" +
-			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
-			"FFFFFFFF" + "FFFFFFFE" + "FFFFFFFF" + "00000000" + "00000000" +
-			"FFFFFFFC", 16);
-	private static final BigInteger P_384_B = new BigInteger("B3312FA7" +
-			"E23EE7E4" + "988E056B" + "E3F82D19" + "181D9C6E" + "FE814112" +
-			"0314088F" + "5013875A" + "C656398D" + "8A2ED19D" + "2A85C8ED" +
-			"D3EC2AEF", 16);
-	private static final BigInteger P_384_G_X = new BigInteger("AA87CA22" +
-			"BE8B0537" + "8EB1C71E" + "F320AD74" + "6E1D3B62" + "8BA79B98" +
-			"59F741E0" + "82542A38" + "5502F25D" + "BF55296C" + "3A545E38" +
-			"72760AB7", 16);
-	private static final BigInteger P_384_G_Y = new BigInteger("3617DE4A" +
-			"96262C6F" + "5D9E98BF" + "9292DC29" + "F8F41DBD" + "289A147C" +
-			"E9DA3113" + "B5F0B8C0" + "0A60B1CE" + "1D7E819D" + "7A431D7C" +
-			"90EA0E5F", 16);
-	private static final BigInteger P_384_N = new BigInteger("FFFFFFFF" +
-			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
-			"C7634D81" + "F4372DDF" + "581A0DB2" + "48B0A77A" + "ECEC196A" +
-			"CCC52973", 16);
-	private static final int P_384_H = 1;
-	// Static parameter objects derived from the above parameters
-	private static final ECField P_384_FIELD = new ECFieldFp(P_384_Q);
-	private static final EllipticCurve P_384_CURVE =
-			new EllipticCurve(P_384_FIELD, P_384_A, P_384_B);
-	private static final ECPoint P_384_G = new ECPoint(P_384_G_X, P_384_G_Y);
-	private static final ECParameterSpec P_384_PARAMS =
-			new ECParameterSpec(P_384_CURVE, P_384_G, P_384_N, P_384_H);
-
 	private final KeyParser agreementKeyParser, signatureKeyParser;
-	private final KeyPairGenerator agreementKeyPairGenerator;
-	private final KeyPairGenerator signatureKeyPairGenerator;
 	private final SecureRandom secureRandom;
+	private final ECKeyPairGenerator agreementKeyPairGenerator;
+	private final ECKeyPairGenerator signatureKeyPairGenerator;
 
 	CryptoComponentImpl() {
-		Security.addProvider(new BouncyCastleProvider());
-		try {
-			KeyFactory agreementKeyFactory = KeyFactory.getInstance(
-					AGREEMENT_KEY_PAIR_ALGO, PROVIDER);
-			if(LOG.isLoggable(INFO)) {
-				LOG.info("Agreement KeyFactory: "
-						+ agreementKeyFactory.getClass().getName());
-			}
-			agreementKeyParser = new Sec1KeyParser(agreementKeyFactory,
-					P_384_PARAMS, P_384_Q, AGREEMENT_KEY_PAIR_BITS);
-			KeyFactory signatureKeyFactory = KeyFactory.getInstance(
-					SIGNATURE_KEY_PAIR_ALGO, PROVIDER);
-			if(LOG.isLoggable(INFO)) {
-				LOG.info("Signature KeyFactory: "
-						+ signatureKeyFactory.getClass().getName());
-			}
-			signatureKeyParser = new Sec1KeyParser(signatureKeyFactory,
-					P_384_PARAMS, P_384_Q, SIGNATURE_KEY_PAIR_BITS);
-			agreementKeyPairGenerator = new KeyPairGeneratorSpi.ECDH();
-			agreementKeyPairGenerator.initialize(AGREEMENT_KEY_PAIR_BITS);
-			signatureKeyPairGenerator = new KeyPairGeneratorSpi.ECDSA();
-			signatureKeyPairGenerator.initialize(SIGNATURE_KEY_PAIR_BITS);
-		} catch(GeneralSecurityException e) {
-			throw new RuntimeException(e);
-		}
+		agreementKeyParser = new Sec1KeyParser(P_384_PARAMS, P_384_Q,
+				AGREEMENT_KEY_PAIR_BITS);
+		signatureKeyParser = new Sec1KeyParser(P_384_PARAMS, P_384_Q,
+				SIGNATURE_KEY_PAIR_BITS);
 		secureRandom = new SecureRandom();
+		ECKeyGenerationParameters params = new ECKeyGenerationParameters(
+				P_384_PARAMS, secureRandom);
+		agreementKeyPairGenerator = new ECKeyPairGenerator();
+		agreementKeyPairGenerator.init(params);
+		signatureKeyPairGenerator = new ECKeyPairGenerator();
+		signatureKeyPairGenerator.init(params);
 	}
 
-	public ErasableKey generateSecretKey() {
+	public SecretKey generateSecretKey() {
 		byte[] b = new byte[CIPHER_KEY_BYTES];
 		secureRandom.nextBytes(b);
-		return new ErasableKeyImpl(b, CIPHER_ALGO);
+		return new SecretKeyImpl(b);
 	}
 
 	public MessageDigest getMessageDigest() {
-		return new DoubleDigest(new SHA384.Digest());
+		return new DoubleDigest(new SHA384Digest());
 	}
 
 	public PseudoRandom getPseudoRandom(int seed1, int seed2) {
@@ -181,25 +113,21 @@ class CryptoComponentImpl implements CryptoComponent {
 	}
 
 	public Signature getSignature() {
-		try {
-			Signature signature = Signature.getInstance(SIGNATURE_ALGO,
-					PROVIDER);
-			if(LOG.isLoggable(INFO))
-				LOG.info("Signature: " + signature.getClass().getName());
-			return signature;
-		} catch(GeneralSecurityException e) {
-			throw new RuntimeException(e);
-		}
+		return new SignatureImpl(secureRandom);
 	}
 
 	public KeyPair generateAgreementKeyPair() {
-		KeyPair keyPair = agreementKeyPairGenerator.generateKeyPair();
-		// Check that the key pair uses NIST curve P-384
-		ECPublicKey publicKey = checkP384Params(keyPair.getPublic());
+		AsymmetricCipherKeyPair keyPair =
+				agreementKeyPairGenerator.generateKeyPair();
 		// Return a wrapper that uses the SEC 1 encoding
-		publicKey = new Sec1PublicKey(publicKey, AGREEMENT_KEY_PAIR_BITS);
-		ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
-		privateKey = new Sec1PrivateKey(privateKey, AGREEMENT_KEY_PAIR_BITS);
+		ECPublicKeyParameters ecPublicKey =
+				(ECPublicKeyParameters) keyPair.getPublic();
+		PublicKey publicKey = new Sec1PublicKey(ecPublicKey,
+				AGREEMENT_KEY_PAIR_BITS);
+		ECPrivateKeyParameters ecPrivateKey =
+				(ECPrivateKeyParameters) keyPair.getPrivate();
+		PrivateKey privateKey = new Sec1PrivateKey(ecPrivateKey,
+				AGREEMENT_KEY_PAIR_BITS);
 		return new KeyPair(publicKey, privateKey);
 	}
 
@@ -208,13 +136,17 @@ class CryptoComponentImpl implements CryptoComponent {
 	}
 
 	public KeyPair generateSignatureKeyPair() {
-		KeyPair keyPair = signatureKeyPairGenerator.generateKeyPair();
-		// Check that the key pair uses NIST curve P-384
-		ECPublicKey publicKey = checkP384Params(keyPair.getPublic());
+		AsymmetricCipherKeyPair keyPair =
+				signatureKeyPairGenerator.generateKeyPair();
 		// Return a wrapper that uses the SEC 1 encoding
-		publicKey = new Sec1PublicKey(publicKey, SIGNATURE_KEY_PAIR_BITS);
-		ECPrivateKey privateKey = (ECPrivateKey) keyPair.getPrivate();
-		privateKey = new Sec1PrivateKey(privateKey, SIGNATURE_KEY_PAIR_BITS);
+		ECPublicKeyParameters ecPublicKey =
+				(ECPublicKeyParameters) keyPair.getPublic();
+		PublicKey publicKey = new Sec1PublicKey(ecPublicKey,
+				SIGNATURE_KEY_PAIR_BITS);
+		ECPrivateKeyParameters ecPrivateKey =
+				(ECPrivateKeyParameters) keyPair.getPrivate();
+		PrivateKey privateKey = new Sec1PrivateKey(ecPrivateKey,
+				SIGNATURE_KEY_PAIR_BITS);
 		return new KeyPair(publicKey, privateKey);
 	}
 
@@ -282,13 +214,16 @@ class CryptoComponentImpl implements CryptoComponent {
 	// Package access for testing
 	byte[] deriveSharedSecret(PrivateKey priv, PublicKey pub)
 			throws GeneralSecurityException {
-		KeyAgreement keyAgreement = KeyAgreement.getInstance(AGREEMENT_ALGO,
-				PROVIDER);
-		if(LOG.isLoggable(INFO))
-			LOG.info("KeyAgreement: " + keyAgreement.getClass().getName());
-		keyAgreement.init(priv);
-		keyAgreement.doPhase(pub, true);
-		return keyAgreement.generateSecret();
+		if(!(priv instanceof Sec1PrivateKey))
+			throw new IllegalArgumentException();
+		if(!(pub instanceof Sec1PublicKey))
+			throw new IllegalArgumentException();
+		ECPrivateKeyParameters ecPriv = ((Sec1PrivateKey) priv).getKey();
+		ECPublicKeyParameters ecPub = ((Sec1PublicKey) pub).getKey();
+		ECDHCBasicAgreement agreement = new ECDHCBasicAgreement();
+		agreement.init(ecPriv);
+		// FIXME: Should we use another format for the shared secret?
+		return agreement.calculateAgreement(ecPub).toByteArray();
 	}
 
 	public byte[] deriveInitialSecret(byte[] secret, int transportIndex) {
@@ -310,7 +245,7 @@ class CryptoComponentImpl implements CryptoComponent {
 		return counterModeKdf(secret, ROTATE, period);
 	}
 
-	public ErasableKey deriveTagKey(byte[] secret, boolean alice) {
+	public SecretKey deriveTagKey(byte[] secret, boolean alice) {
 		if(secret.length != CIPHER_KEY_BYTES)
 			throw new IllegalArgumentException();
 		if(Arrays.equals(secret, BLANK_SECRET))
@@ -319,7 +254,7 @@ class CryptoComponentImpl implements CryptoComponent {
 		else return deriveKey(secret, B_TAG, 0);
 	}
 
-	public ErasableKey deriveFrameKey(byte[] secret, long connection,
+	public SecretKey deriveFrameKey(byte[] secret, long connection,
 			boolean alice, boolean initiator) {
 		if(secret.length != CIPHER_KEY_BYTES)
 			throw new IllegalArgumentException();
@@ -336,21 +271,21 @@ class CryptoComponentImpl implements CryptoComponent {
 		}
 	}
 
-	private ErasableKey deriveKey(byte[] secret, byte[] label, long context) {
+	private SecretKey deriveKey(byte[] secret, byte[] label, long context) {
 		if(secret.length != CIPHER_KEY_BYTES)
 			throw new IllegalArgumentException();
 		if(Arrays.equals(secret, BLANK_SECRET))
 			throw new IllegalArgumentException();
 		byte[] key = counterModeKdf(secret, label, context);
-		return new ErasableKeyImpl(key, CIPHER_ALGO);
+		return new SecretKeyImpl(key);
 	}
 
 	public AuthenticatedCipher getFrameCipher() {
 		AEADBlockCipher cipher = new GCMBlockCipher(new AESFastEngine());
-		return new AuthenticatedCipherImpl(cipher, GCM_MAC_BYTES);
+		return new AuthenticatedCipherImpl(cipher, MAC_BYTES);
 	}
 
-	public void encodeTag(byte[] tag, ErasableKey tagKey, long connection) {
+	public void encodeTag(byte[] tag, SecretKey tagKey, long connection) {
 		if(tag.length < TAG_LENGTH) throw new IllegalArgumentException();
 		if(connection < 0 || connection > MAX_32_BIT_UNSIGNED)
 			throw new IllegalArgumentException();
@@ -367,12 +302,12 @@ class CryptoComponentImpl implements CryptoComponent {
 		secureRandom.nextBytes(salt);
 		// Derive the key from the password
 		byte[] keyBytes = pbkdf2(password, salt);
-		ErasableKey key = new ErasableKeyImpl(keyBytes, CIPHER_ALGO);
+		SecretKey key = new SecretKeyImpl(keyBytes);
 		// Generate a random IV
 		byte[] iv = new byte[STORAGE_IV_BYTES];
 		secureRandom.nextBytes(iv);
 		// The output contains the salt, IV, ciphertext and MAC
-		int outputLen = salt.length + iv.length + input.length + GCM_MAC_BYTES;
+		int outputLen = salt.length + iv.length + input.length + MAC_BYTES;
 		byte[] output = new byte[outputLen];
 		System.arraycopy(salt, 0, output, 0, salt.length);
 		System.arraycopy(iv, 0, output, salt.length, iv.length);
@@ -380,7 +315,7 @@ class CryptoComponentImpl implements CryptoComponent {
 		try {
 			AEADBlockCipher c = new GCMBlockCipher(new AESFastEngine());
 			AuthenticatedCipher cipher = new AuthenticatedCipherImpl(c,
-					GCM_MAC_BYTES);
+					MAC_BYTES);
 			cipher.init(ENCRYPT_MODE, key, iv, null);
 			int outputOff = salt.length + iv.length;
 			cipher.doFinal(input, 0, input.length, output, outputOff);
@@ -394,7 +329,7 @@ class CryptoComponentImpl implements CryptoComponent {
 
 	public byte[] decryptWithPassword(byte[] input, char[] password) {
 		// The input contains the salt, IV, ciphertext and MAC
-		if(input.length < PBKDF_SALT_BYTES + STORAGE_IV_BYTES + GCM_MAC_BYTES)
+		if(input.length < PBKDF_SALT_BYTES + STORAGE_IV_BYTES + MAC_BYTES)
 			return null; // Invalid
 		byte[] salt = new byte[PBKDF_SALT_BYTES];
 		System.arraycopy(input, 0, salt, 0, salt.length);
@@ -402,12 +337,12 @@ class CryptoComponentImpl implements CryptoComponent {
 		System.arraycopy(input, salt.length, iv, 0, iv.length);
 		// Derive the key from the password
 		byte[] keyBytes = pbkdf2(password, salt);
-		ErasableKey key = new ErasableKeyImpl(keyBytes, CIPHER_ALGO);
+		SecretKey key = new SecretKeyImpl(keyBytes);
 		// Initialise the cipher
 		AuthenticatedCipher cipher;
 		try {
 			AEADBlockCipher c = new GCMBlockCipher(new AESFastEngine());
-			cipher = new AuthenticatedCipherImpl(c, GCM_MAC_BYTES);
+			cipher = new AuthenticatedCipherImpl(c, MAC_BYTES);
 			cipher.init(DECRYPT_MODE, key, iv, null);
 		} catch(GeneralSecurityException e) {
 			key.erase();
@@ -417,7 +352,7 @@ class CryptoComponentImpl implements CryptoComponent {
 		try {
 			int inputOff = salt.length + iv.length;
 			int inputLen = input.length - salt.length - iv.length;
-			byte[] output = new byte[inputLen - GCM_MAC_BYTES];
+			byte[] output = new byte[inputLen - MAC_BYTES];
 			cipher.doFinal(input, inputOff, inputLen, output, 0);
 			return output;
 		} catch(GeneralSecurityException e) {
@@ -427,23 +362,6 @@ class CryptoComponentImpl implements CryptoComponent {
 		}
 	}
 
-	private ECPublicKey checkP384Params(PublicKey publicKey) {
-		if(!(publicKey instanceof ECPublicKey)) throw new RuntimeException();
-		ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
-		ECParameterSpec params = ecPublicKey.getParams();
-		EllipticCurve curve = params.getCurve();
-		ECField field = curve.getField();
-		if(!(field instanceof ECFieldFp)) throw new RuntimeException();
-		BigInteger q = ((ECFieldFp) field).getP();
-		if(!q.equals(P_384_Q)) throw new RuntimeException();
-		if(!curve.getA().equals(P_384_A)) throw new RuntimeException();
-		if(!curve.getB().equals(P_384_B)) throw new RuntimeException();
-		if(!params.getGenerator().equals(P_384_G)) throw new RuntimeException();
-		if(!params.getOrder().equals(P_384_N)) throw new RuntimeException();
-		if(!(params.getCofactor() == P_384_H)) throw new RuntimeException();
-		return ecPublicKey;
-	}
-
 	// Key derivation function based on a hash function - see NIST SP 800-56A,
 	// section 5.8
 	private byte[] concatenationKdf(byte[] rawSecret, byte[] label,
@@ -499,8 +417,6 @@ class CryptoComponentImpl implements CryptoComponent {
 
 	// Password-based key derivation function - see PKCS#5 v2.1, section 5.2
 	private byte[] pbkdf2(char[] password, byte[] salt) {
-		// This code is specific to Spongy Castle because the password-based
-		// KDF exposed through the JCE interface is PKCS#12
 		byte[] utf8 = toUtf8ByteArray(password);
 		PKCS5S2ParametersGenerator gen = new PKCS5S2ParametersGenerator();
 		gen.init(utf8, salt, PBKDF_ITERATIONS);
diff --git a/briar-core/src/net/sf/briar/crypto/DoubleDigest.java b/briar-core/src/net/sf/briar/crypto/DoubleDigest.java
index a3039845a887484c5594a01803a3d64623a983d8..20b42865fc6bb46d1017cf370d7a832c69770049 100644
--- a/briar-core/src/net/sf/briar/crypto/DoubleDigest.java
+++ b/briar-core/src/net/sf/briar/crypto/DoubleDigest.java
@@ -2,6 +2,8 @@ package net.sf.briar.crypto;
 
 import net.sf.briar.api.crypto.MessageDigest;
 
+import org.spongycastle.crypto.Digest;
+
 /**
  * A message digest that prevents length extension attacks - see Ferguson and
  * Schneier, <i>Practical Cryptography</i>, chapter 6.
@@ -13,20 +15,22 @@ import net.sf.briar.api.crypto.MessageDigest;
  */
 class DoubleDigest implements MessageDigest {
 
-	private final java.security.MessageDigest delegate;
+	private final Digest delegate;
 
-	DoubleDigest(java.security.MessageDigest delegate) {
+	DoubleDigest(Digest delegate) {
 		this.delegate = delegate;
 	}
 
 	public byte[] digest() {
-		byte[] digest = delegate.digest(); // h(m)
-		delegate.update(digest);
-		return delegate.digest(); // h(h(m))
+		byte[] digest = new byte[delegate.getDigestSize()];
+		delegate.doFinal(digest, 0); // h(m)
+		delegate.update(digest, 0, digest.length);
+		delegate.doFinal(digest, 0); // h(h(m))
+		return digest;
 	}
 
 	public byte[] digest(byte[] input) {
-		delegate.update(input);
+		delegate.update(input, 0, input.length);
 		return digest();
 	}
 
@@ -38,7 +42,7 @@ class DoubleDigest implements MessageDigest {
 	}
 
 	public int getDigestLength() {
-		return delegate.getDigestLength();
+		return delegate.getDigestSize();
 	}
 
 	public void reset() {
@@ -50,7 +54,7 @@ class DoubleDigest implements MessageDigest {
 	}
 
 	public void update(byte[] input) {
-		delegate.update(input);
+		delegate.update(input, 0, input.length);
 	}
 
 	public void update(byte[] input, int offset, int len) {
diff --git a/briar-core/src/net/sf/briar/crypto/ErasableKeyImpl.java b/briar-core/src/net/sf/briar/crypto/ErasableKeyImpl.java
deleted file mode 100644
index bdcd40861313fc58baabbf174376ad62e8d07846..0000000000000000000000000000000000000000
--- a/briar-core/src/net/sf/briar/crypto/ErasableKeyImpl.java
+++ /dev/null
@@ -1,42 +0,0 @@
-package net.sf.briar.crypto;
-
-import net.sf.briar.api.crypto.ErasableKey;
-import net.sf.briar.util.ByteUtils;
-
-class ErasableKeyImpl implements ErasableKey {
-
-	private static final long serialVersionUID = -4438380720846443120L;
-
-	private final byte[] key;
-	private final String algorithm;
-
-	private boolean erased = false; // Locking: this
-
-	ErasableKeyImpl(byte[] key, String algorithm) {
-		this.key = key;
-		this.algorithm = algorithm;
-	}
-
-	public String getAlgorithm() {
-		return algorithm;
-	}
-
-	public synchronized byte[] getEncoded() {
-		if(erased) throw new IllegalStateException();
-		return key;
-	}
-
-	public String getFormat() {
-		return "RAW";
-	}
-
-	public ErasableKey copy() {
-		return new ErasableKeyImpl(key.clone(), algorithm);
-	}
-
-	public synchronized void erase() {
-		if(erased) throw new IllegalStateException();
-		ByteUtils.erase(key);
-		erased = true;
-	}
-}
diff --git a/briar-core/src/net/sf/briar/crypto/P384Constants.java b/briar-core/src/net/sf/briar/crypto/P384Constants.java
new file mode 100644
index 0000000000000000000000000000000000000000..067dc6dcd2d0c69bd815ad63f4f3e48dbf45a60a
--- /dev/null
+++ b/briar-core/src/net/sf/briar/crypto/P384Constants.java
@@ -0,0 +1,41 @@
+package net.sf.briar.crypto;
+
+import java.math.BigInteger;
+
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.math.ec.ECCurve;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
+
+interface P384Constants {
+
+	// Parameters for NIST elliptic curve P-384 - see "Suite B Implementer's
+	// Guide to NIST SP 800-56A", section A.2
+	BigInteger P_384_Q = new BigInteger("FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFE" +
+			"FFFFFFFF" + "00000000" + "00000000" + "FFFFFFFF", 16);
+	BigInteger P_384_A = new BigInteger("FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "FFFFFFFE" +
+			"FFFFFFFF" + "00000000" + "00000000" + "FFFFFFFC", 16);
+	BigInteger P_384_B = new BigInteger("B3312FA7" + "E23EE7E4" + "988E056B" +
+			"E3F82D19" + "181D9C6E" + "FE814112" + "0314088F" + "5013875A" +
+			"C656398D" + "8A2ED19D" + "2A85C8ED" + "D3EC2AEF", 16);
+	BigInteger P_384_G_X = new BigInteger("AA87CA22" + "BE8B0537" + "8EB1C71E" +
+			"F320AD74" + "6E1D3B62" + "8BA79B98" + "59F741E0" + "82542A38" +
+			"5502F25D" + "BF55296C" + "3A545E38" + "72760AB7", 16);
+	BigInteger P_384_G_Y = new BigInteger("3617DE4A" + "96262C6F" + "5D9E98BF" +
+			"9292DC29" + "F8F41DBD" + "289A147C" + "E9DA3113" + "B5F0B8C0" +
+			"0A60B1CE" + "1D7E819D" + "7A431D7C" + "90EA0E5F", 16);
+	BigInteger P_384_N = new BigInteger("FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" +
+			"FFFFFFFF" + "FFFFFFFF" + "FFFFFFFF" + "C7634D81" + "F4372DDF" +
+			"581A0DB2" + "48B0A77A" + "ECEC196A" + "CCC52973", 16);
+	BigInteger P_384_H = BigInteger.ONE;
+
+	// Static parameter objects derived from the above parameters
+	ECCurve P_384_CURVE = new ECCurve.Fp(P_384_Q, P_384_A, P_384_B);
+	ECPoint P_384_G = new ECPoint.Fp(P_384_CURVE,
+			new ECFieldElement.Fp(P_384_Q, P_384_G_X),
+			new ECFieldElement.Fp(P_384_Q, P_384_G_Y));
+	ECDomainParameters P_384_PARAMS = new ECDomainParameters(P_384_CURVE,
+			P_384_G, P_384_N, P_384_H);
+}
diff --git a/briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java b/briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java
index 6759d7a42c6731b7c5067563e96a6432b244b414..915fcbd9cdd9c736278f9e307e17341675cc4b50 100644
--- a/briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java
+++ b/briar-core/src/net/sf/briar/crypto/Sec1KeyParser.java
@@ -1,18 +1,17 @@
 package net.sf.briar.crypto;
 
 import java.math.BigInteger;
-import java.security.KeyFactory;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
-import java.security.spec.ECPrivateKeySpec;
-import java.security.spec.ECPublicKeySpec;
-import java.security.spec.InvalidKeySpecException;
+import java.security.GeneralSecurityException;
 
 import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.crypto.PrivateKey;
+import net.sf.briar.api.crypto.PublicKey;
+
+import org.spongycastle.crypto.params.ECDomainParameters;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.math.ec.ECFieldElement;
+import org.spongycastle.math.ec.ECPoint;
 
 /**
  * A key parser that uses the encoding defined in "SEC 1: Elliptic Curve
@@ -21,14 +20,11 @@ import net.sf.briar.api.crypto.KeyParser;
  */
 class Sec1KeyParser implements KeyParser {
 
-	private final KeyFactory keyFactory;
-	private final ECParameterSpec params;
+	private final ECDomainParameters params;
 	private final BigInteger modulus;
 	private final int keyBits, bytesPerInt, publicKeyBytes, privateKeyBytes;
 
-	Sec1KeyParser(KeyFactory keyFactory, ECParameterSpec params,
-			BigInteger modulus, int keyBits) {
-		this.keyFactory = keyFactory;
+	Sec1KeyParser(ECDomainParameters params, BigInteger modulus, int keyBits) {
 		this.params = params;
 		this.modulus = modulus;
 		this.keyBits = keyBits;
@@ -38,43 +34,48 @@ class Sec1KeyParser implements KeyParser {
 	}
 
 	public PublicKey parsePublicKey(byte[] encodedKey)
-			throws InvalidKeySpecException {
+			throws GeneralSecurityException {
 		if(encodedKey.length != publicKeyBytes)
-			throw new InvalidKeySpecException();
+			throw new GeneralSecurityException();
 		// The first byte must be 0x04
-		if(encodedKey[0] != 4) throw new InvalidKeySpecException();
+		if(encodedKey[0] != 4) throw new GeneralSecurityException();
 		// The x co-ordinate must be >= 0 and < q
 		byte[] xBytes = new byte[bytesPerInt];
 		System.arraycopy(encodedKey, 1, xBytes, 0, bytesPerInt);
 		BigInteger x = new BigInteger(1, xBytes); // Positive signum
-		if(x.compareTo(modulus) >= 0) throw new InvalidKeySpecException();
+		if(x.compareTo(modulus) >= 0) throw new GeneralSecurityException();
 		// The y co-ordinate must be >= 0 and < q
 		byte[] yBytes = new byte[bytesPerInt];
 		System.arraycopy(encodedKey, bytesPerInt + 1, yBytes, 0, bytesPerInt);
 		BigInteger y = new BigInteger(1, yBytes); // Positive signum
-		if(y.compareTo(modulus) >= 0) throw new InvalidKeySpecException();
+		if(y.compareTo(modulus) >= 0) throw new GeneralSecurityException();
 		// Verify that y^2 == x^3 + ax + b (mod q)
-		BigInteger a = params.getCurve().getA(), b = params.getCurve().getB();
+		BigInteger a = params.getCurve().getA().toBigInteger();
+		BigInteger b = params.getCurve().getB().toBigInteger();
 		BigInteger lhs = y.multiply(y).mod(modulus);
 		BigInteger rhs = x.multiply(x).add(a).multiply(x).add(b).mod(modulus);
-		if(!lhs.equals(rhs)) throw new InvalidKeySpecException();
-		// FIXME: Verify that n times the point (x, y) = the point at infinity
+		if(!lhs.equals(rhs)) throw new GeneralSecurityException();
+		// Verify that the point (x, y) times n = the point at infinity
+		ECFieldElement elementX = new ECFieldElement.Fp(modulus, x);
+		ECFieldElement elementY = new ECFieldElement.Fp(modulus, y);
+		ECPoint pub = new ECPoint.Fp(params.getCurve(), elementX, elementY);
+		if(!pub.multiply(params.getN()).isInfinity())
+			throw new GeneralSecurityException();
 		// Construct a public key from the point (x, y) and the params
-		ECPoint pub = new ECPoint(x, y);
-		ECPublicKeySpec keySpec = new ECPublicKeySpec(pub, params);
-		ECPublicKey k = (ECPublicKey) keyFactory.generatePublic(keySpec);
+		ECPublicKeyParameters k = new ECPublicKeyParameters(pub, params);
 		return new Sec1PublicKey(k, keyBits);
 	}
 
 	public PrivateKey parsePrivateKey(byte[] encodedKey)
-			throws InvalidKeySpecException {
+			throws GeneralSecurityException {
 		if(encodedKey.length != privateKeyBytes)
-			throw new InvalidKeySpecException();
-		BigInteger s = new BigInteger(1, encodedKey); // Positive signum
-		if(s.compareTo(params.getOrder()) >= 0)
-			throw new InvalidKeySpecException();
-		ECPrivateKeySpec keySpec = new ECPrivateKeySpec(s, params);
-		ECPrivateKey k = (ECPrivateKey) keyFactory.generatePrivate(keySpec);
+			throw new GeneralSecurityException();
+		BigInteger d = new BigInteger(1, encodedKey); // Positive signum
+		// Verify that the private value is < n
+		if(d.compareTo(params.getN()) >= 0)
+			throw new GeneralSecurityException();
+		// Construct a private key from the private value and the params
+		ECPrivateKeyParameters k = new ECPrivateKeyParameters(d, params);
 		return new Sec1PrivateKey(k, keyBits);
 	}
 }
diff --git a/briar-core/src/net/sf/briar/crypto/Sec1PrivateKey.java b/briar-core/src/net/sf/briar/crypto/Sec1PrivateKey.java
index c590975cc64366027cdb49e64a704c2b7287bc6f..fc346e839e3dcef0dd7a5e696d50c03c8a4af5f1 100644
--- a/briar-core/src/net/sf/briar/crypto/Sec1PrivateKey.java
+++ b/briar-core/src/net/sf/briar/crypto/Sec1PrivateKey.java
@@ -1,57 +1,32 @@
 package net.sf.briar.crypto;
 
 import java.math.BigInteger;
-import java.security.interfaces.ECPrivateKey;
-import java.security.spec.ECParameterSpec;
 
-class Sec1PrivateKey implements ECPrivateKey,
-org.spongycastle.jce.interfaces.ECPrivateKey {
+import net.sf.briar.api.crypto.PrivateKey;
 
-	private static final long serialVersionUID = -493100835871466670L;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
 
-	private final ECPrivateKey key;
+class Sec1PrivateKey implements PrivateKey {
+
+	private final ECPrivateKeyParameters key;
 	private final int privateKeyBytes;
 
-	Sec1PrivateKey(ECPrivateKey key, int keyBits) {
-		// Spongy Castle only accepts instances of its own interface, so we
-		// have to wrap an instance of that interface and delegate to it
-		if(!(key instanceof org.spongycastle.jce.interfaces.ECPrivateKey))
-			throw new IllegalArgumentException();
+	Sec1PrivateKey(ECPrivateKeyParameters key, int keyBits) {
 		this.key = key;
 		privateKeyBytes = (int) Math.ceil(keyBits / 8.0);
 	}
 
-	public String getAlgorithm() {
-		return key.getAlgorithm();
-	}
-
 	public byte[] getEncoded() {
 		byte[] encodedKey = new byte[privateKeyBytes];
-		BigInteger s = key.getS();
+		BigInteger d = key.getD();
 		// Copy up to privateKeyBytes bytes into exactly privateKeyBytes bytes
-		byte[] sBytes = s.toByteArray();
-		for(int i = 0; i < sBytes.length && i < privateKeyBytes; i++)
-			encodedKey[privateKeyBytes - 1 - i] = sBytes[sBytes.length - 1 - i];
+		byte[] dBytes = d.toByteArray();
+		for(int i = 0; i < dBytes.length && i < privateKeyBytes; i++)
+			encodedKey[privateKeyBytes - 1 - i] = dBytes[dBytes.length - 1 - i];
 		return encodedKey;
 	}
 
-	public String getFormat() {
-		return "SEC1";
-	}
-
-	public ECParameterSpec getParams() {
-		return key.getParams();
-	}
-
-	public BigInteger getS() {
-		return key.getS();
-	}
-
-	public org.spongycastle.jce.spec.ECParameterSpec getParameters() {
-		return ((org.spongycastle.jce.interfaces.ECPrivateKey) key).getParameters();
-	}
-
-	public BigInteger getD() {
-		return ((org.spongycastle.jce.interfaces.ECPrivateKey) key).getD();
+	ECPrivateKeyParameters getKey() {
+		return key;
 	}
 }
diff --git a/briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java b/briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java
index e3c2e32263bc87fdd99aea75d71ba432c00c2b63..2cb9a213c8038c54b0ce373ce4b1fcf6636fa023 100644
--- a/briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java
+++ b/briar-core/src/net/sf/briar/crypto/Sec1PublicKey.java
@@ -1,42 +1,32 @@
 package net.sf.briar.crypto;
 
 import java.math.BigInteger;
-import java.security.interfaces.ECPublicKey;
-import java.security.spec.ECParameterSpec;
-import java.security.spec.ECPoint;
+
+import net.sf.briar.api.crypto.PublicKey;
+
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
 
 /**
  * An elliptic curve public key that uses the encoding defined in "SEC 1:
  * Elliptic Curve Cryptography", section 2.3 (Certicom Corporation, May 2009).
  * Point compression is not used.
  */
-class Sec1PublicKey implements ECPublicKey,
-org.spongycastle.jce.interfaces.ECPublicKey {
+class Sec1PublicKey implements PublicKey {
 
-	private static final long serialVersionUID = -2722797033851423987L;
-
-	private final ECPublicKey key;
+	private final ECPublicKeyParameters key;
 	private final int bytesPerInt, publicKeyBytes;
 
-	Sec1PublicKey(ECPublicKey key, int keyBits) {
-		// Spongy Castle only accepts instances of its own interface, so we
-		// have to wrap an instance of that interface and delegate to it
-		if(!(key instanceof org.spongycastle.jce.interfaces.ECPublicKey))
-			throw new IllegalArgumentException();
+	Sec1PublicKey(ECPublicKeyParameters key, int keyBits) {
 		this.key = key;
 		bytesPerInt = (int) Math.ceil(keyBits / 8.0);
 		publicKeyBytes = 1 + 2 * bytesPerInt;
 	}
 
-	public String getAlgorithm() {
-		return key.getAlgorithm();
-	}
-
 	public byte[] getEncoded() {
 		byte[] encodedKey = new byte[publicKeyBytes];
 		encodedKey[0] = 4;
-		BigInteger x = key.getW().getAffineX();
-		BigInteger y = key.getW().getAffineY();
+		BigInteger x = key.getQ().getX().toBigInteger();
+		BigInteger y = key.getQ().getY().toBigInteger();
 		// Copy up to bytesPerInt bytes into exactly bytesPerInt bytes
 		byte[] xBytes = x.toByteArray();
 		for(int i = 0; i < xBytes.length && i < bytesPerInt; i++)
@@ -47,23 +37,7 @@ org.spongycastle.jce.interfaces.ECPublicKey {
 		return encodedKey;
 	}
 
-	public String getFormat() {
-		return "SEC1";
-	}
-
-	public ECParameterSpec getParams() {
-		return key.getParams();
-	}
-
-	public ECPoint getW() {
-		return key.getW();
-	}
-
-	public org.spongycastle.jce.spec.ECParameterSpec getParameters() {
-		return ((org.spongycastle.jce.interfaces.ECPublicKey) key).getParameters();
-	}
-
-	public org.spongycastle.math.ec.ECPoint getQ() {
-		return ((org.spongycastle.jce.interfaces.ECPublicKey) key).getQ();
+	ECPublicKeyParameters getKey() {
+		return key;
 	}
 }
diff --git a/briar-core/src/net/sf/briar/crypto/SecretKeyImpl.java b/briar-core/src/net/sf/briar/crypto/SecretKeyImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..64bbad0634074312dfd9c31ad41eba717bdd556b
--- /dev/null
+++ b/briar-core/src/net/sf/briar/crypto/SecretKeyImpl.java
@@ -0,0 +1,30 @@
+package net.sf.briar.crypto;
+
+import net.sf.briar.api.crypto.SecretKey;
+import net.sf.briar.util.ByteUtils;
+
+class SecretKeyImpl implements SecretKey {
+
+	private final byte[] key;
+
+	private boolean erased = false; // Locking: this
+
+	SecretKeyImpl(byte[] key) {
+		this.key = key;
+	}
+
+	public synchronized byte[] getEncoded() {
+		if(erased) throw new IllegalStateException();
+		return key;
+	}
+
+	public SecretKey copy() {
+		return new SecretKeyImpl(key.clone());
+	}
+
+	public synchronized void erase() {
+		if(erased) throw new IllegalStateException();
+		ByteUtils.erase(key);
+		erased = true;
+	}
+}
diff --git a/briar-core/src/net/sf/briar/crypto/SignatureImpl.java b/briar-core/src/net/sf/briar/crypto/SignatureImpl.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9f39d42b81bc00418be2388e829dd24a1aa5fc9
--- /dev/null
+++ b/briar-core/src/net/sf/briar/crypto/SignatureImpl.java
@@ -0,0 +1,58 @@
+package net.sf.briar.crypto;
+
+import java.security.GeneralSecurityException;
+import java.security.SecureRandom;
+
+import net.sf.briar.api.crypto.PrivateKey;
+import net.sf.briar.api.crypto.PublicKey;
+import net.sf.briar.api.crypto.Signature;
+
+import org.spongycastle.crypto.digests.SHA384Digest;
+import org.spongycastle.crypto.params.ECPrivateKeyParameters;
+import org.spongycastle.crypto.params.ECPublicKeyParameters;
+import org.spongycastle.crypto.params.ParametersWithRandom;
+import org.spongycastle.crypto.signers.DSADigestSigner;
+import org.spongycastle.crypto.signers.ECDSASigner;
+
+class SignatureImpl implements Signature {
+
+	private final SecureRandom secureRandom;
+	private final DSADigestSigner signer;
+
+	SignatureImpl(SecureRandom secureRandom) {
+		this.secureRandom = secureRandom;
+		signer = new DSADigestSigner(new ECDSASigner(), new SHA384Digest());
+	}
+
+	public void initSign(PrivateKey k) throws GeneralSecurityException {
+		if(!(k instanceof Sec1PrivateKey)) throw new GeneralSecurityException();
+		ECPrivateKeyParameters priv = ((Sec1PrivateKey) k).getKey();
+		signer.init(true, new ParametersWithRandom(priv, secureRandom));
+	}
+
+	public void initVerify(PublicKey k) throws GeneralSecurityException {
+		if(!(k instanceof Sec1PublicKey)) throw new GeneralSecurityException();
+		ECPublicKeyParameters pub = ((Sec1PublicKey) k).getKey();
+		signer.init(false, pub);
+	}
+
+	public void update(byte b) {
+		signer.update(b);
+	}
+
+	public void update(byte[] b) {
+		update(b, 0, b.length);
+	}
+
+	public void update(byte[] b, int off, int len) {
+		signer.update(b, off, len);
+	}
+
+	public byte[] sign() {
+		return signer.generateSignature();
+	}
+
+	public boolean verify(byte[] signature) {
+		return signer.verifySignature(signature);
+	}
+}
diff --git a/briar-core/src/net/sf/briar/invitation/Connector.java b/briar-core/src/net/sf/briar/invitation/Connector.java
index 076f77cf304b84c629c4651e018e265a725be6d5..35283c7844e54cc100bb55b092175a402fe9c08b 100644
--- a/briar-core/src/net/sf/briar/invitation/Connector.java
+++ b/briar-core/src/net/sf/briar/invitation/Connector.java
@@ -13,8 +13,6 @@ import static net.sf.briar.api.messaging.Rating.GOOD;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.KeyPair;
-import java.security.Signature;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collections;
@@ -36,9 +34,11 @@ import net.sf.briar.api.UniqueId;
 import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyManager;
+import net.sf.briar.api.crypto.KeyPair;
 import net.sf.briar.api.crypto.KeyParser;
 import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.crypto.PseudoRandom;
+import net.sf.briar.api.crypto.Signature;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.db.NoSuchTransportException;
diff --git a/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java b/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java
index a04838b789014e0b8ce95ced7b497ca97af97f1d..7b2b07fdea3fb2a295434991e4d257464187a299 100644
--- a/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java
+++ b/briar-core/src/net/sf/briar/messaging/MessageFactoryImpl.java
@@ -16,14 +16,14 @@ import java.nio.ByteBuffer;
 import java.nio.charset.Charset;
 import java.nio.charset.CharsetDecoder;
 import java.security.GeneralSecurityException;
-import java.security.PrivateKey;
 import java.security.SecureRandom;
-import java.security.Signature;
 
 import net.sf.briar.api.Author;
 import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.MessageDigest;
+import net.sf.briar.api.crypto.PrivateKey;
+import net.sf.briar.api.crypto.Signature;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.Message;
 import net.sf.briar.api.messaging.MessageFactory;
diff --git a/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java b/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java
index eeaece89ab7ec9d95e3c0d83eb0d2b93aa132040..85d75d8880a8015f58ddd171a8e45db10adb8004 100644
--- a/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java
+++ b/briar-core/src/net/sf/briar/messaging/MessageVerifierImpl.java
@@ -1,13 +1,13 @@
 package net.sf.briar.messaging;
 
 import java.security.GeneralSecurityException;
-import java.security.PublicKey;
-import java.security.Signature;
 
 import net.sf.briar.api.Author;
 import net.sf.briar.api.crypto.CryptoComponent;
 import net.sf.briar.api.crypto.KeyParser;
 import net.sf.briar.api.crypto.MessageDigest;
+import net.sf.briar.api.crypto.PublicKey;
+import net.sf.briar.api.crypto.Signature;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.Message;
 import net.sf.briar.api.messaging.MessageId;
diff --git a/briar-core/src/net/sf/briar/transport/ConnectionReaderFactoryImpl.java b/briar-core/src/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
index ae4aefb064068e3027a800cc8ba9b4484d404514..cfd2b8246e1f4d89b77cf2cb9d0785ac21357ac4 100644
--- a/briar-core/src/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
+++ b/briar-core/src/net/sf/briar/transport/ConnectionReaderFactoryImpl.java
@@ -3,7 +3,7 @@ package net.sf.briar.transport;
 import java.io.InputStream;
 
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.ConnectionReader;
 import net.sf.briar.api.transport.ConnectionReaderFactory;
@@ -26,7 +26,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 		long connection = ctx.getConnectionNumber();
 		boolean weAreAlice = ctx.getAlice();
 		boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice;
-		ErasableKey frameKey = crypto.deriveFrameKey(secret, connection,
+		SecretKey frameKey = crypto.deriveFrameKey(secret, connection,
 				initiatorIsAlice, initiator);
 		FrameReader encryption = new IncomingEncryptionLayer(in,
 				crypto.getFrameCipher(), frameKey, maxFrameLength);
@@ -35,7 +35,7 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
 
 	public ConnectionReader createInvitationConnectionReader(InputStream in,
 			int maxFrameLength, byte[] secret, boolean alice) {
-		ErasableKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
+		SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
 		FrameReader encryption = new IncomingEncryptionLayer(in,
 				crypto.getFrameCipher(), frameKey, maxFrameLength);
 		return new ConnectionReaderImpl(encryption, maxFrameLength);
diff --git a/briar-core/src/net/sf/briar/transport/ConnectionWriterFactoryImpl.java b/briar-core/src/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
index 365ada7da594a5ca888d54d24eaccfbee201a2ef..fcb1e65eb5f7c11ee609b2c2224f6ec3306f81c9 100644
--- a/briar-core/src/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
+++ b/briar-core/src/net/sf/briar/transport/ConnectionWriterFactoryImpl.java
@@ -5,7 +5,7 @@ import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH;
 import java.io.OutputStream;
 
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
@@ -28,12 +28,12 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 		long connection = ctx.getConnectionNumber();
 		boolean weAreAlice = ctx.getAlice();
 		boolean initiatorIsAlice = incoming ? !weAreAlice : weAreAlice;
-		ErasableKey frameKey = crypto.deriveFrameKey(secret, connection,
+		SecretKey frameKey = crypto.deriveFrameKey(secret, connection,
 				initiatorIsAlice, initiator);
 		FrameWriter encryption;
 		if(initiator) {
 			byte[] tag = new byte[TAG_LENGTH];
-			ErasableKey tagKey = crypto.deriveTagKey(secret, initiatorIsAlice);
+			SecretKey tagKey = crypto.deriveTagKey(secret, initiatorIsAlice);
 			crypto.encodeTag(tag, tagKey, connection);
 			tagKey.erase();
 			encryption = new OutgoingEncryptionLayer(out, capacity,
@@ -47,7 +47,7 @@ class ConnectionWriterFactoryImpl implements ConnectionWriterFactory {
 
 	public ConnectionWriter createInvitationConnectionWriter(OutputStream out,
 			int maxFrameLength, byte[] secret, boolean alice) {
-		ErasableKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
+		SecretKey frameKey = crypto.deriveFrameKey(secret, 0, true, alice);
 		FrameWriter encryption = new OutgoingEncryptionLayer(out,
 				Long.MAX_VALUE, crypto.getFrameCipher(), frameKey,
 				maxFrameLength);
diff --git a/briar-core/src/net/sf/briar/transport/IncomingEncryptionLayer.java b/briar-core/src/net/sf/briar/transport/IncomingEncryptionLayer.java
index 01dc0cb4353de8267f9b8b5ff0f314d590f82eff..34ac59b205769d747118238328569c036b2d11a5 100644
--- a/briar-core/src/net/sf/briar/transport/IncomingEncryptionLayer.java
+++ b/briar-core/src/net/sf/briar/transport/IncomingEncryptionLayer.java
@@ -13,13 +13,13 @@ import java.security.GeneralSecurityException;
 
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.AuthenticatedCipher;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 
 class IncomingEncryptionLayer implements FrameReader {
 
 	private final InputStream in;
 	private final AuthenticatedCipher frameCipher;
-	private final ErasableKey frameKey;
+	private final SecretKey frameKey;
 	private final byte[] iv, aad, ciphertext;
 	private final int frameLength;
 
@@ -27,7 +27,7 @@ class IncomingEncryptionLayer implements FrameReader {
 	private boolean finalFrame;
 
 	IncomingEncryptionLayer(InputStream in, AuthenticatedCipher frameCipher,
-			ErasableKey frameKey, int frameLength) {
+			SecretKey frameKey, int frameLength) {
 		this.in = in;
 		this.frameCipher = frameCipher;
 		this.frameKey = frameKey;
diff --git a/briar-core/src/net/sf/briar/transport/OutgoingEncryptionLayer.java b/briar-core/src/net/sf/briar/transport/OutgoingEncryptionLayer.java
index 49203a107f9e6923bf3b5ab64996c9ce946742a6..40febbb2d088623786333351cf3be4dd718e031a 100644
--- a/briar-core/src/net/sf/briar/transport/OutgoingEncryptionLayer.java
+++ b/briar-core/src/net/sf/briar/transport/OutgoingEncryptionLayer.java
@@ -12,13 +12,13 @@ import java.io.OutputStream;
 import java.security.GeneralSecurityException;
 
 import net.sf.briar.api.crypto.AuthenticatedCipher;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 
 class OutgoingEncryptionLayer implements FrameWriter {
 
 	private final OutputStream out;
 	private final AuthenticatedCipher frameCipher;
-	private final ErasableKey frameKey;
+	private final SecretKey frameKey;
 	private final byte[] tag, iv, aad, ciphertext;
 	private final int frameLength, maxPayloadLength;
 
@@ -27,7 +27,7 @@ class OutgoingEncryptionLayer implements FrameWriter {
 
 	/** Constructor for the initiator's side of a connection. */
 	OutgoingEncryptionLayer(OutputStream out, long capacity,
-			AuthenticatedCipher frameCipher, ErasableKey frameKey,
+			AuthenticatedCipher frameCipher, SecretKey frameKey,
 			int frameLength, byte[] tag) {
 		this.out = out;
 		this.capacity = capacity;
@@ -45,7 +45,7 @@ class OutgoingEncryptionLayer implements FrameWriter {
 
 	/** Constructor for the responder's side of a connection. */
 	OutgoingEncryptionLayer(OutputStream out, long capacity,
-			AuthenticatedCipher frameCipher, ErasableKey frameKey,
+			AuthenticatedCipher frameCipher, SecretKey frameKey,
 			int frameLength) {
 		this.out = out;
 		this.capacity = capacity;
diff --git a/briar-core/src/net/sf/briar/transport/TransportConnectionRecogniser.java b/briar-core/src/net/sf/briar/transport/TransportConnectionRecogniser.java
index 8071c8334a802504f5ec71756ed62b43de775c55..d2eff12fb151a7508d8ef710e45c3ff42a7e4760 100644
--- a/briar-core/src/net/sf/briar/transport/TransportConnectionRecogniser.java
+++ b/briar-core/src/net/sf/briar/transport/TransportConnectionRecogniser.java
@@ -11,7 +11,7 @@ import net.sf.briar.api.Bytes;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.DbException;
 import net.sf.briar.api.transport.ConnectionContext;
@@ -41,7 +41,7 @@ class TransportConnectionRecogniser {
 		TagContext t = tagMap.remove(new Bytes(tag));
 		if(t == null) return null; // The tag was not expected
 		// Update the connection window and the expected tags
-		ErasableKey key = crypto.deriveTagKey(t.secret, !t.alice);
+		SecretKey key = crypto.deriveTagKey(t.secret, !t.alice);
 		for(long connection : t.window.setSeen(t.connection)) {
 			byte[] tag1 = new byte[TAG_LENGTH];
 			crypto.encodeTag(tag1, key, connection);
@@ -72,7 +72,7 @@ class TransportConnectionRecogniser {
 		long centre = s.getWindowCentre();
 		byte[] bitmap = s.getWindowBitmap();
 		// Create the connection window and the expected tags
-		ErasableKey key = crypto.deriveTagKey(secret, !alice);
+		SecretKey key = crypto.deriveTagKey(secret, !alice);
 		ConnectionWindow window = new ConnectionWindow(centre, bitmap);
 		for(long connection : window.getUnseen()) {
 			byte[] tag = new byte[TAG_LENGTH];
@@ -98,7 +98,7 @@ class TransportConnectionRecogniser {
 	// Locking: this
 	private void removeSecret(RemovalContext r) {
 		// Remove the expected tags
-		ErasableKey key = crypto.deriveTagKey(r.secret, !r.alice);
+		SecretKey key = crypto.deriveTagKey(r.secret, !r.alice);
 		byte[] tag = new byte[TAG_LENGTH];
 		for(long connection : r.window.getUnseen()) {
 			crypto.encodeTag(tag, key, connection);
diff --git a/briar-tests/.classpath b/briar-tests/.classpath
index ac0eb6392c7555acd4bc18c3a2d59d42e02397a2..190edff6cbb7259d4c4ced89fe016d67622f3a17 100644
--- a/briar-tests/.classpath
+++ b/briar-tests/.classpath
@@ -12,7 +12,6 @@
 	<classpathentry combineaccessrules="false" kind="src" path="/briar-core"/>
 	<classpathentry kind="lib" path="/briar-core/libs/commons-io-2.0.1.jar"/>
 	<classpathentry kind="lib" path="/briar-core/libs/jnotify-0.93.jar"/>
-	<classpathentry kind="lib" path="/briar-core/libs/scprov-jdk15on-1.47.0.3-SNAPSHOT.jar"/>
 	<classpathentry kind="lib" path="/briar-core/libs/jssc-0.9-briar.jar"/>
 	<classpathentry kind="output" path="bin"/>
 </classpath>
diff --git a/briar-tests/build.xml b/briar-tests/build.xml
index d634e63442c102bd80d55f77e835cb8d8fc7f555..9ea36a167b3c4674acc111a29b50433bce82129a 100644
--- a/briar-tests/build.xml
+++ b/briar-tests/build.xml
@@ -70,12 +70,11 @@
 			<jvmarg value='-Djava.library.path=../briar-core/libs'/>
 			<test name='net.sf.briar.LockFairnessTest'/>
 			<test name='net.sf.briar.ProtocolIntegrationTest'/>
-			<test name='net.sf.briar.crypto.CounterModeTest'/>
-			<test name='net.sf.briar.crypto.ErasableKeyTest'/>
 			<test name='net.sf.briar.crypto.KeyAgreementTest'/>
 			<test name='net.sf.briar.crypto.KeyDerivationTest'/>
 			<test name='net.sf.briar.crypto.KeyEncodingAndParsingTest'/>
 			<test name="net.sf.briar.crypto.PasswordBasedKdfTest"/>
+			<test name='net.sf.briar.crypto.SecretKeyImplTest'/>
 			<test name='net.sf.briar.db.BasicH2Test'/>
 			<test name='net.sf.briar.db.DatabaseCleanerImplTest'/>
 			<test name='net.sf.briar.db.DatabaseComponentImplTest'/>
diff --git a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
index e535ff331ed89ab38ac321c3f1a322989a0497f7..c67b30310dcd30ee1d3c7a46765afc2ec7532322 100644
--- a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java
@@ -8,7 +8,6 @@ import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.io.OutputStream;
-import java.security.KeyPair;
 import java.util.Arrays;
 import java.util.BitSet;
 import java.util.Collection;
@@ -21,6 +20,7 @@ import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.KeyPair;
 import net.sf.briar.api.messaging.Ack;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.GroupFactory;
diff --git a/briar-tests/src/net/sf/briar/crypto/CounterModeTest.java b/briar-tests/src/net/sf/briar/crypto/CounterModeTest.java
deleted file mode 100644
index d6c16f4666a3cdb4b3848aa5ad8f8d8f485a5707..0000000000000000000000000000000000000000
--- a/briar-tests/src/net/sf/briar/crypto/CounterModeTest.java
+++ /dev/null
@@ -1,155 +0,0 @@
-package net.sf.briar.crypto;
-
-import java.security.GeneralSecurityException;
-import java.security.SecureRandom;
-import java.security.Security;
-import java.util.HashSet;
-import java.util.Set;
-
-import javax.crypto.Cipher;
-import javax.crypto.spec.IvParameterSpec;
-import javax.crypto.spec.SecretKeySpec;
-
-import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.Bytes;
-
-import org.junit.Test;
-import org.spongycastle.jce.provider.BouncyCastleProvider;
-
-public class CounterModeTest extends BriarTestCase {
-
-	private static final String CIPHER_ALGO = "AES";
-	private static final String CIPHER_MODE = "AES/CTR/NoPadding";
-	private static final String PROVIDER = "SC";
-	private static final int KEY_SIZE_BYTES = 32; // AES-256
-	private static final int BLOCK_SIZE_BYTES = 16;
-
-	private final SecureRandom random;
-	private final byte[] keyBytes;
-	private final SecretKeySpec key;
-
-	public CounterModeTest() {
-		Security.addProvider(new BouncyCastleProvider());
-		random = new SecureRandom();
-		keyBytes = new byte[KEY_SIZE_BYTES];
-		random.nextBytes(keyBytes);
-		key = new SecretKeySpec(keyBytes, CIPHER_ALGO);
-	}
-
-	@Test
-	public void testEveryBitOfIvIsSignificant()
-	throws GeneralSecurityException {
-		// Set each bit of the IV in turn, encrypt the same plaintext and check
-		// that all the resulting ciphertexts are distinct
-		byte[] plaintext = new byte[BLOCK_SIZE_BYTES];
-		random.nextBytes(plaintext);
-		Set<Bytes> ciphertexts = new HashSet<Bytes>();
-		for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) {
-			// Set the i^th bit of the IV
-			byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
-			ivBytes[i / 8] |= (byte) (128 >> i % 8);
-			IvParameterSpec iv = new IvParameterSpec(ivBytes);
-			// Encrypt the plaintext
-			Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
-			cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-			byte[] ciphertext =
-				new byte[cipher.getOutputSize(plaintext.length)];
-			cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
-			ciphertexts.add(new Bytes(ciphertext));
-		}
-		// All the ciphertexts should be distinct using Arrays.equals()
-		assertEquals(BLOCK_SIZE_BYTES * 8, ciphertexts.size());
-	}
-
-	@Test
-	public void testRepeatedIvsProduceRepeatedCiphertexts()
-	throws GeneralSecurityException {
-		// This is the inverse of the previous test, to check that the
-		// distinct ciphertexts were due to using distinct IVs
-		byte[] plaintext = new byte[BLOCK_SIZE_BYTES];
-		random.nextBytes(plaintext);
-		byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
-		random.nextBytes(ivBytes);
-		IvParameterSpec iv = new IvParameterSpec(ivBytes);
-		Set<Bytes> ciphertexts = new HashSet<Bytes>();
-		for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) {
-			Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
-			cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-			byte[] ciphertext =
-				new byte[cipher.getOutputSize(plaintext.length)];
-			cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
-			ciphertexts.add(new Bytes(ciphertext));
-		}
-		assertEquals(1, ciphertexts.size());
-	}
-
-	@Test
-	public void testLeastSignificantBitsUsedAsCounter()
-	throws GeneralSecurityException {
-		// Initialise the least significant 16 bits of the IV to zero and
-		// encrypt ten blocks of zeroes
-		byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
-		byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
-		random.nextBytes(ivBytes);
-		ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
-		ivBytes[BLOCK_SIZE_BYTES - 1] = 0;
-		IvParameterSpec iv = new IvParameterSpec(ivBytes);
-		Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
-		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-		byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
-		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
-		// Make sure the IV array hasn't been modified
-		assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 2]);
-		assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 1]);
-		// Initialise the least significant 16 bits of the IV to one and
-		// encrypt another ten blocks of zeroes
-		ivBytes[BLOCK_SIZE_BYTES - 1] = 1;
-		iv = new IvParameterSpec(ivBytes);
-		cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
-		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-		byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)];
-		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1);
-		// The last nine blocks of the first ciphertext should be identical to
-		// the first nine blocks of the second ciphertext
-		for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) {
-			assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]);
-		}
-	}
-
-	@Test
-	public void testCounterUsesMoreThan16Bits()
-	throws GeneralSecurityException {
-		// Initialise the least significant bits of the IV to 2^16-1 and
-		// encrypt ten blocks of zeroes
-		byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10];
-		byte[] ivBytes = new byte[BLOCK_SIZE_BYTES];
-		random.nextBytes(ivBytes);
-		ivBytes[BLOCK_SIZE_BYTES - 3] = 0;
-		ivBytes[BLOCK_SIZE_BYTES - 2] = (byte) 255;
-		ivBytes[BLOCK_SIZE_BYTES - 1] = (byte) 255;
-		IvParameterSpec iv = new IvParameterSpec(ivBytes);
-		Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
-		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-		byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)];
-		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext);
-		// Make sure the IV array hasn't been modified
-		assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 3]);
-		assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 2]);
-		assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 1]);
-		// Initialise the least significant bits of the IV to 2^16 and
-		// encrypt another ten blocks of zeroes
-		ivBytes[BLOCK_SIZE_BYTES - 3] = 1;
-		ivBytes[BLOCK_SIZE_BYTES - 2] = 0;
-		ivBytes[BLOCK_SIZE_BYTES - 1] = 0;
-		iv = new IvParameterSpec(ivBytes);
-		cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER);
-		cipher.init(Cipher.ENCRYPT_MODE, key, iv);
-		byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)];
-		cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1);
-		// The last nine blocks of the first ciphertext should be identical to
-		// the first nine blocks of the second ciphertext
-		for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) {
-			assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]);
-		}
-	}
-}
diff --git a/briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java b/briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java
deleted file mode 100644
index eb448a5505c74ff09741eb247fab91c77fb489a8..0000000000000000000000000000000000000000
--- a/briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package net.sf.briar.crypto;
-
-import static org.junit.Assert.assertArrayEquals;
-
-import java.util.Random;
-
-import javax.crypto.Cipher;
-import javax.crypto.Mac;
-import javax.crypto.spec.IvParameterSpec;
-
-import net.sf.briar.BriarTestCase;
-import net.sf.briar.api.crypto.ErasableKey;
-
-import org.junit.Test;
-
-public class ErasableKeyTest extends BriarTestCase {
-
-	private static final String CIPHER = "AES";
-	private static final String CIPHER_MODE = "AES/CTR/NoPadding";
-	private static final int IV_BYTES = 16; // 128 bits
-	private static final int KEY_BYTES = 32; // 256 bits
-	private static final String MAC = "HMacSHA384";
-
-	private final Random random = new Random();
-
-	@Test
-	public void testCopiesAreErased() {
-		byte[] master = new byte[KEY_BYTES];
-		random.nextBytes(master);
-		ErasableKey k = new ErasableKeyImpl(master, CIPHER);
-		byte[] copy = k.getEncoded();
-		assertArrayEquals(master, copy);
-		k.erase();
-		byte[] blank = new byte[KEY_BYTES];
-		assertArrayEquals(blank, master);
-		assertArrayEquals(blank, copy);
-	}
-
-	@Test
-	public void testErasureDoesNotAffectCipher() throws Exception {
-		byte[] key = new byte[KEY_BYTES];
-		random.nextBytes(key);
-		ErasableKey k = new ErasableKeyImpl(key, CIPHER);
-		Cipher c = Cipher.getInstance(CIPHER_MODE);
-		IvParameterSpec iv = new IvParameterSpec(new byte[IV_BYTES]);
-		c.init(Cipher.ENCRYPT_MODE, k, iv);
-		// Encrypt a blank plaintext
-		byte[] plaintext = new byte[123];
-		byte[] ciphertext = c.doFinal(plaintext);
-		// Erase the key and encrypt again - erase() was called after doFinal()
-		k.erase();
-		byte[] ciphertext1 = c.doFinal(plaintext);
-		// Encrypt again - this time erase() was called before doFinal()
-		byte[] ciphertext2 = c.doFinal(plaintext);
-		// The ciphertexts should match
-		assertArrayEquals(ciphertext, ciphertext1);
-		assertArrayEquals(ciphertext, ciphertext2);
-	}
-
-	@Test
-	public void testErasureDoesNotAffectMac() throws Exception {
-		byte[] key = new byte[KEY_BYTES];
-		random.nextBytes(key);
-		ErasableKey k = new ErasableKeyImpl(key, CIPHER);
-		Mac m = Mac.getInstance(MAC);
-		m.init(k);
-		// Authenticate a blank plaintext
-		byte[] plaintext = new byte[123];
-		byte[] mac = m.doFinal(plaintext);
-		// Erase the key and authenticate again
-		k.erase();
-		byte[] mac1 = m.doFinal(plaintext);
-		// Authenticate again
-		byte[] mac2 = m.doFinal(plaintext);
-		// The MACs should match
-		assertArrayEquals(mac, mac1);
-		assertArrayEquals(mac, mac2);
-	}
-}
diff --git a/briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java b/briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java
index d328c35e7aea3a3c5e343a1bb148d35c350d6e35..65c7e97f424b75bdd178dde6e6719ccaa633e24a 100644
--- a/briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java
+++ b/briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java
@@ -2,10 +2,9 @@ package net.sf.briar.crypto;
 
 import static org.junit.Assert.assertArrayEquals;
 
-import java.security.KeyPair;
-
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.KeyPair;
 
 import org.junit.Test;
 
diff --git a/briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java b/briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java
index f3c568cec86d8d69cf0e6d5ac0cc979d55b75d2b..44c2ffaa26d4fd9121941b948eff9f17bdde57a4 100644
--- a/briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java
+++ b/briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java
@@ -7,7 +7,7 @@ import java.util.Random;
 
 import net.sf.briar.BriarTestCase;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 
 import org.junit.Test;
 
@@ -24,7 +24,7 @@ public class KeyDerivationTest extends BriarTestCase {
 
 	@Test
 	public void testKeysAreDistinct() {
-		List<ErasableKey> keys = new ArrayList<ErasableKey>();
+		List<SecretKey> keys = new ArrayList<SecretKey>();
 		keys.add(crypto.deriveFrameKey(secret, 0, false, false));
 		keys.add(crypto.deriveFrameKey(secret, 0, false, true));
 		keys.add(crypto.deriveFrameKey(secret, 0, true, false));
diff --git a/briar-tests/src/net/sf/briar/crypto/KeyEncodingAndParsingTest.java b/briar-tests/src/net/sf/briar/crypto/KeyEncodingAndParsingTest.java
index d327d6b83eb02f00735296d59ff38b87cb920038..9ecfed57b69e075f8d32779f17456e18ba2c17f8 100644
--- a/briar-tests/src/net/sf/briar/crypto/KeyEncodingAndParsingTest.java
+++ b/briar-tests/src/net/sf/briar/crypto/KeyEncodingAndParsingTest.java
@@ -2,12 +2,11 @@ package net.sf.briar.crypto;
 
 import static org.junit.Assert.assertArrayEquals;
 
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.PublicKey;
-
 import net.sf.briar.BriarTestCase;
+import net.sf.briar.api.crypto.KeyPair;
 import net.sf.briar.api.crypto.KeyParser;
+import net.sf.briar.api.crypto.PrivateKey;
+import net.sf.briar.api.crypto.PublicKey;
 
 import org.junit.Test;
 
diff --git a/briar-tests/src/net/sf/briar/crypto/SecretKeyImplTest.java b/briar-tests/src/net/sf/briar/crypto/SecretKeyImplTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..be69414b0b2d8b236ed494b1dd34d74dfea4119d
--- /dev/null
+++ b/briar-tests/src/net/sf/briar/crypto/SecretKeyImplTest.java
@@ -0,0 +1,28 @@
+package net.sf.briar.crypto;
+
+import static org.junit.Assert.assertArrayEquals;
+
+import java.util.Random;
+
+import net.sf.briar.BriarTestCase;
+import net.sf.briar.api.crypto.SecretKey;
+
+import org.junit.Test;
+
+public class SecretKeyImplTest extends BriarTestCase {
+
+	private static final int KEY_BYTES = 32; // 256 bits
+
+	@Test
+	public void testCopiesAreErased() {
+		byte[] master = new byte[KEY_BYTES];
+		new Random().nextBytes(master);
+		SecretKey k = new SecretKeyImpl(master);
+		byte[] copy = k.getEncoded();
+		assertArrayEquals(master, copy);
+		k.erase();
+		byte[] blank = new byte[KEY_BYTES];
+		assertArrayEquals(blank, master);
+		assertArrayEquals(blank, copy);
+	}
+}
diff --git a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
index d7aa694f64a759d9e0c81885a8272a5f917734e9..0760579ade6c3e82006d945835cdf03f9dc100a7 100644
--- a/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/ConstantsTest.java
@@ -12,9 +12,6 @@ import static net.sf.briar.api.messaging.MessagingConstants.MAX_PACKET_LENGTH;
 import static net.sf.briar.api.messaging.MessagingConstants.MAX_SUBSCRIPTIONS;
 
 import java.io.ByteArrayOutputStream;
-import java.security.KeyPair;
-import java.security.PrivateKey;
-import java.security.Signature;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Random;
@@ -29,6 +26,9 @@ import net.sf.briar.api.TransportId;
 import net.sf.briar.api.TransportProperties;
 import net.sf.briar.api.UniqueId;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.KeyPair;
+import net.sf.briar.api.crypto.PrivateKey;
+import net.sf.briar.api.crypto.Signature;
 import net.sf.briar.api.messaging.Ack;
 import net.sf.briar.api.messaging.Group;
 import net.sf.briar.api.messaging.GroupFactory;
diff --git a/briar-tests/src/net/sf/briar/messaging/ConsumersTest.java b/briar-tests/src/net/sf/briar/messaging/ConsumersTest.java
index 0432c699ddd01d4746f6e26dbc3acda7fe20a4a9..c5ea141fe7414085b39a6788afc126bf701b847f 100644
--- a/briar-tests/src/net/sf/briar/messaging/ConsumersTest.java
+++ b/briar-tests/src/net/sf/briar/messaging/ConsumersTest.java
@@ -64,7 +64,7 @@ public class ConsumersTest extends BriarTestCase {
 		private final java.security.MessageDigest delegate;
 
 		private TestMessageDigest() throws GeneralSecurityException {
-			delegate = java.security.MessageDigest.getInstance("SHA-256");
+			delegate = java.security.MessageDigest.getInstance("SHA-384");
 		}
 
 		public byte[] digest() {
diff --git a/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java b/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java
index af3edcc464c9d6a4e365c6278b854269b1d44ec1..1b4071dfd120f1b60f0734cb06f5161edac5b9a0 100644
--- a/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java
+++ b/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java
@@ -13,7 +13,7 @@ import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.AuthenticatedCipher;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Test;
@@ -31,7 +31,7 @@ public class IncomingEncryptionLayerTest extends BriarTestCase {
 
 	private final CryptoComponent crypto;
 	private final AuthenticatedCipher frameCipher;
-	private final ErasableKey frameKey;
+	private final SecretKey frameKey;
 
 	public IncomingEncryptionLayerTest() {
 		Injector i = Guice.createInjector(new CryptoModule(),
diff --git a/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java b/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java
index b106b04778fa4dd4549d028b4a157061e2b927c1..70d664955f4859867008f42c44ec0a37d723b301 100644
--- a/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/transport/KeyRotationIntegrationTest.java
@@ -14,7 +14,7 @@ import net.sf.briar.api.TransportId;
 import net.sf.briar.api.clock.Clock;
 import net.sf.briar.api.clock.Timer;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.db.event.DatabaseListener;
 import net.sf.briar.api.transport.ConnectionContext;
@@ -112,9 +112,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
-		final ErasableKey k0 = context.mock(ErasableKey.class, "k0");
-		final ErasableKey k1 = context.mock(ErasableKey.class, "k1");
-		final ErasableKey k2 = context.mock(ErasableKey.class, "k2");
+		final SecretKey k0 = context.mock(SecretKey.class, "k0");
+		final SecretKey k1 = context.mock(SecretKey.class, "k1");
+		final SecretKey k2 = context.mock(SecretKey.class, "k2");
 
 		final ConnectionRecogniser connectionRecogniser =
 				new ConnectionRecogniserImpl(crypto, db);
@@ -235,9 +235,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
-		final ErasableKey k0 = context.mock(ErasableKey.class, "k0");
-		final ErasableKey k1 = context.mock(ErasableKey.class, "k1");
-		final ErasableKey k2 = context.mock(ErasableKey.class, "k2");
+		final SecretKey k0 = context.mock(SecretKey.class, "k0");
+		final SecretKey k1 = context.mock(SecretKey.class, "k1");
+		final SecretKey k2 = context.mock(SecretKey.class, "k2");
 
 		final ConnectionRecogniser connectionRecogniser =
 				new ConnectionRecogniserImpl(crypto, db);
@@ -369,9 +369,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
-		final ErasableKey k0 = context.mock(ErasableKey.class, "k0");
-		final ErasableKey k1 = context.mock(ErasableKey.class, "k1");
-		final ErasableKey k2 = context.mock(ErasableKey.class, "k2");
+		final SecretKey k0 = context.mock(SecretKey.class, "k0");
+		final SecretKey k1 = context.mock(SecretKey.class, "k1");
+		final SecretKey k2 = context.mock(SecretKey.class, "k2");
 
 		final ConnectionRecogniser connectionRecogniser =
 				new ConnectionRecogniserImpl(crypto, db);
@@ -514,9 +514,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
-		final ErasableKey k0 = context.mock(ErasableKey.class, "k0");
-		final ErasableKey k1 = context.mock(ErasableKey.class, "k1");
-		final ErasableKey k2 = context.mock(ErasableKey.class, "k2");
+		final SecretKey k0 = context.mock(SecretKey.class, "k0");
+		final SecretKey k1 = context.mock(SecretKey.class, "k1");
+		final SecretKey k2 = context.mock(SecretKey.class, "k2");
 
 		final ConnectionRecogniser connectionRecogniser =
 				new ConnectionRecogniserImpl(crypto, db);
@@ -628,9 +628,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
-		final ErasableKey k1 = context.mock(ErasableKey.class, "k1");
-		final ErasableKey k2 = context.mock(ErasableKey.class, "k2");
-		final ErasableKey k3 = context.mock(ErasableKey.class, "k3");
+		final SecretKey k1 = context.mock(SecretKey.class, "k1");
+		final SecretKey k2 = context.mock(SecretKey.class, "k2");
+		final SecretKey k3 = context.mock(SecretKey.class, "k3");
 
 		final ConnectionRecogniser connectionRecogniser =
 				new ConnectionRecogniserImpl(crypto, db);
@@ -752,9 +752,9 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		final Clock clock = context.mock(Clock.class);
 		final Timer timer = context.mock(Timer.class);
-		final ErasableKey k2 = context.mock(ErasableKey.class, "k2");
-		final ErasableKey k3 = context.mock(ErasableKey.class, "k3");
-		final ErasableKey k4 = context.mock(ErasableKey.class, "k4");
+		final SecretKey k2 = context.mock(SecretKey.class, "k2");
+		final SecretKey k3 = context.mock(SecretKey.class, "k3");
+		final SecretKey k4 = context.mock(SecretKey.class, "k4");
 
 		final ConnectionRecogniser connectionRecogniser =
 				new ConnectionRecogniserImpl(crypto, db);
@@ -885,7 +885,7 @@ public class KeyRotationIntegrationTest extends BriarTestCase {
 
 		public Object invoke(Invocation invocation) throws Throwable {
 			byte[] tag = (byte[]) invocation.getParameter(0);
-			ErasableKey key = (ErasableKey) invocation.getParameter(1);
+			SecretKey key = (SecretKey) invocation.getParameter(1);
 			long connection = (Long) invocation.getParameter(2);
 			encodeTag(tag, key.getEncoded(), connection);
 			return null;
diff --git a/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java b/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java
index 648c3c0da295e17218e1575e50adff465cbb69c3..683ea2847cc369d19b47361e247408e69d0d5141 100644
--- a/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java
+++ b/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java
@@ -13,7 +13,7 @@ import net.sf.briar.BriarTestCase;
 import net.sf.briar.TestLifecycleModule;
 import net.sf.briar.api.crypto.AuthenticatedCipher;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Test;
@@ -47,7 +47,7 @@ public class OutgoingEncryptionLayerTest extends BriarTestCase {
 		byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH];
 		byte[] plaintext = new byte[FRAME_LENGTH - MAC_LENGTH];
 		byte[] ciphertext = new byte[FRAME_LENGTH];
-		ErasableKey frameKey = crypto.generateSecretKey();
+		SecretKey frameKey = crypto.generateSecretKey();
 		// Calculate the expected ciphertext
 		FrameEncoder.encodeIv(iv, 0);
 		FrameEncoder.encodeAad(aad, 0, plaintext.length);
diff --git a/briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java b/briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java
index 3f5b2225d2fc8bd47973443c0aa125d6596ac171..dd961e9b8eb4cf5549fb9f6ae1ec7870f14f299b 100644
--- a/briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java
+++ b/briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java
@@ -10,7 +10,7 @@ import net.sf.briar.TestUtils;
 import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.api.db.DatabaseComponent;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.TemporarySecret;
@@ -36,7 +36,7 @@ public class TransportConnectionRecogniserTest extends BriarTestCase {
 		final byte[] secret = new byte[32];
 		new Random().nextBytes(secret);
 		final boolean alice = false;
-		final ErasableKey tagKey = context.mock(ErasableKey.class);
+		final SecretKey tagKey = context.mock(SecretKey.class);
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		context.checking(new Expectations() {{
 			// Add secret
@@ -74,7 +74,7 @@ public class TransportConnectionRecogniserTest extends BriarTestCase {
 		final byte[] secret = new byte[32];
 		new Random().nextBytes(secret);
 		final boolean alice = false;
-		final ErasableKey tagKey = context.mock(ErasableKey.class);
+		final SecretKey tagKey = context.mock(SecretKey.class);
 		final DatabaseComponent db = context.mock(DatabaseComponent.class);
 		context.checking(new Expectations() {{
 			// Add secret
diff --git a/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java b/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java
index dd6d5adbcf5f7c984c126c77ce27d054cdaf1169..331d224388bb215345d58ff271e5d3f531c30d31 100644
--- a/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java
+++ b/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java
@@ -18,7 +18,7 @@ import net.sf.briar.api.ContactId;
 import net.sf.briar.api.TransportId;
 import net.sf.briar.api.crypto.AuthenticatedCipher;
 import net.sf.briar.api.crypto.CryptoComponent;
-import net.sf.briar.api.crypto.ErasableKey;
+import net.sf.briar.api.crypto.SecretKey;
 import net.sf.briar.api.transport.ConnectionContext;
 import net.sf.briar.api.transport.ConnectionWriter;
 import net.sf.briar.api.transport.ConnectionWriterFactory;
@@ -42,7 +42,7 @@ public class TransportIntegrationTest extends BriarTestCase {
 	private final AuthenticatedCipher frameCipher;
 	private final Random random;
 	private final byte[] secret;
-	private final ErasableKey frameKey;
+	private final SecretKey frameKey;
 
 	public TransportIntegrationTest() {
 		Module testModule = new AbstractModule() {
@@ -82,7 +82,7 @@ public class TransportIntegrationTest extends BriarTestCase {
 		byte[] frame1 = new byte[321];
 		random.nextBytes(frame1);
 		// Copy the frame key - the copy will be erased
-		ErasableKey frameCopy = frameKey.copy();
+		SecretKey frameCopy = frameKey.copy();
 		// Write the frames
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		FrameWriter encryptionOut = new OutgoingEncryptionLayer(out,