diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java
index a8f3530f459e442954af5cb4d81bb17bb85dbb95..373daea801c4430184d185c2c74a47dfd45e4048 100644
--- a/api/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/api/net/sf/briar/api/crypto/CryptoComponent.java
@@ -10,11 +10,17 @@ import javax.crypto.SecretKey;
 
 public interface CryptoComponent {
 
-	SecretKey deriveMacKey(byte[] secret);
+	SecretKey deriveIncomingMacKey(byte[] secret);
 
-	SecretKey derivePacketKey(byte[] secret);
+	SecretKey deriveIncomingPacketKey(byte[] secret);
 
-	SecretKey deriveTagKey(byte[] secret);
+	SecretKey deriveIncomingTagKey(byte[] secret);
+
+	SecretKey deriveOutgoingMacKey(byte[] secret);
+
+	SecretKey deriveOutgoingPacketKey(byte[] secret);
+
+	SecretKey deriveOutgoingTagKey(byte[] secret);
 
 	KeyPair generateKeyPair();
 
diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index 99cbe824d79198553fb776de824c274c44d836b4..366c37fd54949a7f8220f5a2545030f9c9c4e682 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -68,9 +68,13 @@ class CryptoComponentImpl implements CryptoComponent {
 		}
 	}
 
-	public SecretKey deriveMacKey(byte[] secret) {
+	public SecretKey deriveIncomingMacKey(byte[] secret) {
 		SharedSecret s = new SharedSecret(secret);
-		if(s.getAlice()) return deriveKey("MACA", s.getIv(), s.getCiphertext());
+		return deriveMacKey(s, !s.getAlice());
+	}
+
+	private SecretKey deriveMacKey(SharedSecret s, boolean alice) {
+		if(alice) return deriveKey("MACA", s.getIv(), s.getCiphertext());
 		else return deriveKey("MACB", s.getIv(), s.getCiphertext());
 	}
 
@@ -110,18 +114,41 @@ class CryptoComponentImpl implements CryptoComponent {
 		}
 	}
 
-	public SecretKey derivePacketKey(byte[] secret) {
+	public SecretKey deriveIncomingPacketKey(byte[] secret) {
 		SharedSecret s = new SharedSecret(secret);
-		if(s.getAlice()) return deriveKey("PKTA", s.getIv(), s.getCiphertext());
+		return derivePacketKey(s, !s.getAlice());
+	}
+
+	private SecretKey derivePacketKey(SharedSecret s, boolean alice) {
+		if(alice) return deriveKey("PKTA", s.getIv(), s.getCiphertext());
 		else return deriveKey("PKTB", s.getIv(), s.getCiphertext());
 	}
 
-	public SecretKey deriveTagKey(byte[] secret) {
+	public SecretKey deriveIncomingTagKey(byte[] secret) {
 		SharedSecret s = new SharedSecret(secret);
-		if(s.getAlice()) return deriveKey("TAGA", s.getIv(), s.getCiphertext());
+		return deriveTagKey(s, !s.getAlice());
+	}
+
+	private SecretKey deriveTagKey(SharedSecret s, boolean alice) {
+		if(alice) return deriveKey("TAGA", s.getIv(), s.getCiphertext());
 		else return deriveKey("TAGB", s.getIv(), s.getCiphertext());
 	}
 
+	public SecretKey deriveOutgoingMacKey(byte[] secret) {
+		SharedSecret s = new SharedSecret(secret);
+		return deriveMacKey(s, s.getAlice());
+	}
+
+	public SecretKey deriveOutgoingPacketKey(byte[] secret) {
+		SharedSecret s = new SharedSecret(secret);
+		return derivePacketKey(s, s.getAlice());
+	}
+
+	public SecretKey deriveOutgoingTagKey(byte[] secret) {
+		SharedSecret s = new SharedSecret(secret);
+		return deriveTagKey(s, s.getAlice());
+	}
+
 	public KeyPair generateKeyPair() {
 		return keyPairGenerator.generateKeyPair();
 	}
diff --git a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
index 4fcf5deff1dfef570d55fd64496373bc024f12a2..77865ddfc11b4ae838cb2ab448de18178d269674 100644
--- a/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
+++ b/components/net/sf/briar/transport/ConnectionRecogniserImpl.java
@@ -50,7 +50,8 @@ DatabaseListener {
 		for(ContactId c : db.getContacts()) {
 			try {
 				// Initialise and store the contact's tag cipher
-				SecretKey tagKey = crypto.deriveTagKey(db.getSharedSecret(c));
+				byte[] secret = db.getSharedSecret(c);
+				SecretKey tagKey = crypto.deriveIncomingTagKey(secret);
 				Cipher cipher = crypto.getTagCipher();
 				try {
 					cipher.init(Cipher.ENCRYPT_MODE, tagKey);
diff --git a/components/net/sf/briar/transport/PacketReaderFactoryImpl.java b/components/net/sf/briar/transport/PacketReaderFactoryImpl.java
index 5e5a7f2bff4bf5bbd3ae273d4a63e7ff4d6f2645..3d8ccbdafe380b8ec5de4ae6163abcb048a4bc36 100644
--- a/components/net/sf/briar/transport/PacketReaderFactoryImpl.java
+++ b/components/net/sf/briar/transport/PacketReaderFactoryImpl.java
@@ -24,9 +24,9 @@ class PacketReaderFactoryImpl implements PacketReaderFactory {
 
 	public PacketReader createPacketReader(byte[] firstTag, InputStream in,
 			int transportId, long connection, byte[] secret) {
-		SecretKey macKey = crypto.deriveMacKey(secret);
-		SecretKey tagKey = crypto.deriveTagKey(secret);
-		SecretKey packetKey = crypto.derivePacketKey(secret);
+		SecretKey macKey = crypto.deriveIncomingMacKey(secret);
+		SecretKey tagKey = crypto.deriveIncomingTagKey(secret);
+		SecretKey packetKey = crypto.deriveIncomingPacketKey(secret);
 		Cipher tagCipher = crypto.getTagCipher();
 		Cipher packetCipher = crypto.getPacketCipher();
 		Mac mac = crypto.getMac();
diff --git a/components/net/sf/briar/transport/PacketWriterFactoryImpl.java b/components/net/sf/briar/transport/PacketWriterFactoryImpl.java
index cf411c2e08ae46a3cddcf9e0e7979b50975654e0..a59d201c4c27ae2ffc88b4101872fb38eed11c2a 100644
--- a/components/net/sf/briar/transport/PacketWriterFactoryImpl.java
+++ b/components/net/sf/briar/transport/PacketWriterFactoryImpl.java
@@ -24,9 +24,9 @@ class PacketWriterFactoryImpl implements PacketWriterFactory {
 
 	public PacketWriter createPacketWriter(OutputStream out, int transportId,
 			long connection, byte[] secret) {
-		SecretKey macKey = crypto.deriveMacKey(secret);
-		SecretKey tagKey = crypto.deriveTagKey(secret);
-		SecretKey packetKey = crypto.derivePacketKey(secret);
+		SecretKey macKey = crypto.deriveOutgoingMacKey(secret);
+		SecretKey tagKey = crypto.deriveOutgoingTagKey(secret);
+		SecretKey packetKey = crypto.deriveOutgoingPacketKey(secret);
 		Cipher tagCipher = crypto.getTagCipher();
 		Cipher packetCipher = crypto.getPacketCipher();
 		Mac mac = crypto.getMac();
diff --git a/test/build.xml b/test/build.xml
index d12ada885d0073a7257998f68fc8bcd874b98c4b..f63488bbfffcdf781d493a939c4e0da5bd8d5b76 100644
--- a/test/build.xml
+++ b/test/build.xml
@@ -16,6 +16,8 @@
 			<test name='net.sf.briar.FileReadWriteTest'/>
 			<test name='net.sf.briar.LockFairnessTest'/>
 			<test name='net.sf.briar.crypto.CounterModeTest'/>
+			<test name='net.sf.briar.crypto.CryptoComponentTest'/>
+			<test name='net.sf.briar.crypto.SharedSecretTest'/>
 			<test name='net.sf.briar.db.BasicH2Test'/>
 			<test name='net.sf.briar.db.DatabaseCleanerImplTest'/>
 			<test name='net.sf.briar.db.H2DatabaseTest'/>
diff --git a/test/net/sf/briar/FileReadWriteTest.java b/test/net/sf/briar/FileReadWriteTest.java
index 4988507957df41291d9977927d801662c90d2488..363710f53ba5c94c1b4bc7563cfa0bd9518efd25 100644
--- a/test/net/sf/briar/FileReadWriteTest.java
+++ b/test/net/sf/briar/FileReadWriteTest.java
@@ -71,7 +71,7 @@ public class FileReadWriteTest extends TestCase {
 	private final ProtocolReaderFactory protocolReaderFactory;
 	private final ProtocolWriterFactory protocolWriterFactory;
 	private final CryptoComponent crypto;
-	private final byte[] secret = new byte[45];
+	private final byte[] aliceSecret, bobSecret;
 	private final int transportId = 123;
 	private final long connection = 234L;
 	private final Author author;
@@ -94,6 +94,10 @@ public class FileReadWriteTest extends TestCase {
 		crypto = i.getInstance(CryptoComponent.class);
 		assertEquals(crypto.getMessageDigest().getDigestLength(),
 				UniqueId.LENGTH);
+		// Create matching secrets: one for Alice, one for Bob
+		aliceSecret = new byte[45];
+		aliceSecret[16] = (byte) 1;
+		bobSecret = new byte[45];
 		// Create two groups: one restricted, one unrestricted
 		GroupFactory groupFactory = i.getInstance(GroupFactory.class);
 		group = groupFactory.createGroup("Unrestricted group", null);
@@ -129,8 +133,9 @@ public class FileReadWriteTest extends TestCase {
 	@Test
 	public void testWriteFile() throws Exception {
 		OutputStream out = new FileOutputStream(file);
+		// Use Alice's secret for writing
 		PacketWriter packetWriter = packetWriterFactory.createPacketWriter(out,
-				transportId, connection, secret);
+				transportId, connection, aliceSecret);
 		out = packetWriter.getOutputStream();
 
 		AckWriter a = protocolWriterFactory.createAckWriter(out);
@@ -194,8 +199,9 @@ public class FileReadWriteTest extends TestCase {
 			offset += read;
 		}
 		assertEquals(16, offset);
+		// Use Bob's secret for reading
 		PacketReader packetReader = packetReaderFactory.createPacketReader(
-				firstTag, in, transportId, connection, secret);
+				firstTag, in, transportId, connection, bobSecret);
 		in = packetReader.getInputStream();
 		ProtocolReader protocolReader =
 			protocolReaderFactory.createProtocolReader(in);
diff --git a/test/net/sf/briar/crypto/CryptoComponentTest.java b/test/net/sf/briar/crypto/CryptoComponentTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d7dad93ed9b8933ad64725111c383580401161f9
--- /dev/null
+++ b/test/net/sf/briar/crypto/CryptoComponentTest.java
@@ -0,0 +1,49 @@
+package net.sf.briar.crypto;
+
+import junit.framework.TestCase;
+import net.sf.briar.api.crypto.CryptoComponent;
+
+import org.junit.Test;
+
+import com.google.inject.Guice;
+import com.google.inject.Injector;
+
+public class CryptoComponentTest extends TestCase {
+
+	private final CryptoComponent crypto;
+
+	public CryptoComponentTest() {
+		super();
+		Injector i = Guice.createInjector(new CryptoModule());
+		crypto = i.getInstance(CryptoComponent.class);
+	}
+
+	@Test
+	public void testKeyDerivation() {
+		// Create matching secrets: one for Alice, one for Bob
+		byte[] aliceSecret = new byte[123];
+		aliceSecret[SharedSecret.IV_BYTES] = (byte) 1;
+		byte[] bobSecret = new byte[123];
+		// Check that Alice's incoming keys match Bob's outgoing keys
+		assertEquals(crypto.deriveIncomingMacKey(aliceSecret),
+				crypto.deriveOutgoingMacKey(bobSecret));
+		assertEquals(crypto.deriveIncomingPacketKey(aliceSecret),
+				crypto.deriveOutgoingPacketKey(bobSecret));
+		assertEquals(crypto.deriveIncomingTagKey(aliceSecret),
+				crypto.deriveOutgoingTagKey(bobSecret));
+		// Check that Alice's outgoing keys match Bob's incoming keys
+		assertEquals(crypto.deriveOutgoingMacKey(aliceSecret),
+				crypto.deriveIncomingMacKey(bobSecret));
+		assertEquals(crypto.deriveOutgoingPacketKey(aliceSecret),
+				crypto.deriveIncomingPacketKey(bobSecret));
+		assertEquals(crypto.deriveOutgoingTagKey(aliceSecret),
+				crypto.deriveIncomingTagKey(bobSecret));
+		// Check that Alice's incoming and outgoing keys are different
+		assertFalse(crypto.deriveIncomingMacKey(aliceSecret).equals(
+				crypto.deriveOutgoingMacKey(aliceSecret)));
+		assertFalse(crypto.deriveIncomingPacketKey(aliceSecret).equals(
+				crypto.deriveOutgoingPacketKey(aliceSecret)));
+		assertFalse(crypto.deriveIncomingTagKey(aliceSecret).equals(
+				crypto.deriveOutgoingTagKey(aliceSecret)));
+	}
+}
diff --git a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
index 948a7872ae05661597d8fc581ba5fcc5fa7fb013..c5009db87de80dffeaeb594e821ce13798993955 100644
--- a/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
+++ b/test/net/sf/briar/transport/ConnectionRecogniserImplTest.java
@@ -60,7 +60,7 @@ public class ConnectionRecogniserImplTest extends TestCase {
 	@Test
 	public void testExpectedTag() throws Exception {
 		// Calculate the expected tag for connection number 3
-		SecretKey tagKey = crypto.deriveTagKey(secret);
+		SecretKey tagKey = crypto.deriveIncomingTagKey(secret);
 		Cipher tagCipher = crypto.getTagCipher();
 		tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
 		byte[] tag = TagEncoder.encodeTag(transportId, 3L, 0);
diff --git a/test/net/sf/briar/transport/PacketReadWriteTest.java b/test/net/sf/briar/transport/PacketReadWriteTest.java
index a4f47e1bfa1a5df8ac645d00d26f1de1c6f9827d..106dff792f7d086725d739e888a037e5ce0e0b7c 100644
--- a/test/net/sf/briar/transport/PacketReadWriteTest.java
+++ b/test/net/sf/briar/transport/PacketReadWriteTest.java
@@ -39,9 +39,10 @@ public class PacketReadWriteTest extends TestCase {
 		crypto = i.getInstance(CryptoComponent.class);
 		tagCipher = crypto.getTagCipher();
 		packetCipher = crypto.getPacketCipher();
-		macKey = crypto.deriveMacKey(secret);
-		tagKey = crypto.deriveTagKey(secret);
-		packetKey = crypto.derivePacketKey(secret);
+		// Since we're sending packets to ourselves, we only need outgoing keys
+		macKey = crypto.deriveOutgoingMacKey(secret);
+		tagKey = crypto.deriveOutgoingTagKey(secret);
+		packetKey = crypto.deriveOutgoingPacketKey(secret);
 		mac = crypto.getMac();
 		random = new Random();
 	}