diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
index 2a4f262de853052e12b216ec8d008dd1ad81c475..fcf5efe46c8b58bf47b1776f7588f620b60aab08 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/db/DatabaseComponent.java
@@ -481,6 +481,12 @@ public interface DatabaseComponent {
 	 */
 	void removeTransport(Transaction txn, TransportId t) throws DbException;
 
+	/**
+	 * Removes the given transport keys from the database.
+	 */
+	void removeTransportKeys(Transaction txn, TransportId t, KeySetId k)
+		throws DbException;
+
 	/**
 	 * Marks the given contact as verified.
 	 */
diff --git a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java
index 50a5cf9480c430d8eb876fdb67642f86a2c30282..06b2f472587b0e2004696e47bafa22c0ac79b60e 100644
--- a/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java
+++ b/bramble-api/src/main/java/org/briarproject/bramble/api/transport/KeyManager.java
@@ -40,6 +40,13 @@ public interface KeyManager {
 	void bindKeys(Transaction txn, ContactId c, Map<TransportId, KeySetId> keys)
 			throws DbException;
 
+	/**
+	 * Removes the given transport keys, which must not have been bound, from
+	 * the manager and the database.
+	 */
+	void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
+		throws DbException;
+
 	/**
 	 * Returns a {@link StreamContext} for sending a stream to the given
 	 * contact over the given transport, or null if an error occurs or the
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
index b11e1b828118593e3d1052f4c2c512c5c309c384..6ee996b2b8eedd8d4812005ae62eb5dc30a7e5ea 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/Database.java
@@ -593,6 +593,12 @@ interface Database<T> {
 	 */
 	void removeTransport(T txn, TransportId t) throws DbException;
 
+	/**
+	 * Removes the given transport keys from the database.
+	 */
+	void removeTransportKeys(T txn, TransportId t, KeySetId k)
+			throws DbException;
+
 	/**
 	 * Resets the transmission count and expiry time of the given message with
 	 * respect to the given contact.
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
index 305b672900fa61c8dc2cb10759f198c95f9c5b0c..697bcdd3d66adc7595002d2b5591101786c5e261 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/DatabaseComponentImpl.java
@@ -791,6 +791,16 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
 		db.removeTransport(txn, t);
 	}
 
+	@Override
+	public void removeTransportKeys(Transaction transaction,
+			TransportId t, KeySetId k) throws DbException {
+		if (transaction.isReadOnly()) throw new IllegalArgumentException();
+		T txn = unbox(transaction);
+		if (!db.containsTransport(txn, t))
+			throw new NoSuchTransportException();
+		db.removeTransportKeys(txn, t, k);
+	}
+
 	@Override
 	public void setContactVerified(Transaction transaction, ContactId c)
 			throws DbException {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
index 747145a3442087ffc2d6b22e203b07e666fcdf59..25cfb4d5aa5aa0177360d8b8d03532438eddb542 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/db/JdbcDatabase.java
@@ -2681,6 +2681,27 @@ abstract class JdbcDatabase implements Database<Connection> {
 		}
 	}
 
+	@Override
+	public void removeTransportKeys(Connection txn, TransportId t, KeySetId k)
+			throws DbException {
+		PreparedStatement ps = null;
+		try {
+			// Delete any existing outgoing keys - this will also remove any
+			// incoming keys with the same key set ID
+			String sql = "DELETE FROM outgoingKeys"
+					+ " WHERE transportId = ? AND keySetId = ?";
+			ps = txn.prepareStatement(sql);
+			ps.setString(1, t.getString());
+			ps.setInt(2, k.getInt());
+			int affected = ps.executeUpdate();
+			if (affected < 0) throw new DbStateException();
+			ps.close();
+		} catch (SQLException e) {
+			tryToClose(ps);
+			throw new DbException(e);
+		}
+	}
+
 	@Override
 	public void resetExpiryTime(Connection txn, ContactId c, MessageId m)
 			throws DbException {
@@ -2905,30 +2926,10 @@ abstract class JdbcDatabase implements Database<Connection> {
 	@Override
 	public void updateTransportKeys(Connection txn, Collection<KeySet> keys)
 			throws DbException {
-		PreparedStatement ps = null;
-		try {
-			// Delete any existing outgoing keys - this will also remove any
-			// incoming keys with the same key set ID
-			// TODO: Add an index to speed this up?
-			String sql = "DELETE FROM outgoingKeys WHERE keySetId = ?";
-			ps = txn.prepareStatement(sql);
-			for (KeySet ks : keys) {
-				ps.setInt(1, ks.getKeySetId().getInt());
-				ps.addBatch();
-			}
-			int[] batchAffected = ps.executeBatch();
-			if (batchAffected.length != keys.size())
-				throw new DbStateException();
-			for (int rows: batchAffected)
-				if (rows < 0) throw new DbStateException();
-			ps.close();
-		} catch (SQLException e) {
-			tryToClose(ps);
-			throw new DbException(e);
-		}
-		// Store the new keys
 		for (KeySet ks : keys) {
-			addTransportKeys(txn, ks.getContactId(), ks.getTransportKeys());
+			TransportKeys k = ks.getTransportKeys();
+			removeTransportKeys(txn, k.getTransportId(), ks.getKeySetId());
+			addTransportKeys(txn, ks.getContactId(), k);
 		}
 	}
 }
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java
index c07c9e1e72ee6f48ecade12704b2f52e474396ec..1a06ef4a6503819f7c2761a8062d5531b419a8bc 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/KeyManagerImpl.java
@@ -132,6 +132,20 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
 		}
 	}
 
+	@Override
+	public void removeKeys(Transaction txn, Map<TransportId, KeySetId> keys)
+			throws DbException {
+		for (Entry<TransportId, KeySetId> e : keys.entrySet()) {
+			TransportId t = e.getKey();
+			TransportKeyManager m = managers.get(t);
+			if (m == null) {
+				if (LOG.isLoggable(INFO)) LOG.info("No key manager for " + t);
+			} else {
+				m.removeKeys(txn, e.getValue());
+			}
+		}
+	}
+
 	@Override
 	public StreamContext getStreamContext(ContactId c, TransportId t)
 			throws DbException {
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java
index 41e31fbe62e2abb8727541fd1ffecc25f517fd4a..4eff0b289d4043715a18abfb27911619487b7c6e 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManager.java
@@ -23,6 +23,8 @@ interface TransportKeyManager {
 
 	void bindKeys(Transaction txn, ContactId c, KeySetId k) throws DbException;
 
+	void removeKeys(Transaction txn, KeySetId k) throws DbException;
+
 	void removeContact(ContactId c);
 
 	@Nullable
diff --git a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
index 6b9a1519ebe7ef4fbba42bb2d0e2500c0dd9c967..889485d0dc34795bda559dcce63020613e11cf67 100644
--- a/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
+++ b/bramble-core/src/main/java/org/briarproject/bramble/transport/TransportKeyManagerImpl.java
@@ -219,6 +219,20 @@ class TransportKeyManagerImpl implements TransportKeyManager {
 		}
 	}
 
+	@Override
+	public void removeKeys(Transaction txn, KeySetId k) throws DbException {
+		lock.lock();
+		try {
+			MutableKeySet ks = keys.remove(k);
+			if (ks == null) throw new IllegalArgumentException();
+			if (ks.getContactId() != null) throw new IllegalArgumentException();
+			TransportId t = ks.getTransportKeys().getTransportId();
+			db.removeTransportKeys(txn, t, k);
+		} finally {
+			lock.unlock();
+		}
+	}
+
 	@Override
 	public void removeContact(ContactId c) {
 		lock.lock();