diff --git a/components/net/sf/briar/protocol/BundleReaderImpl.java b/components/net/sf/briar/protocol/BundleReaderImpl.java
index 2474a5e08553eb55a5e51b005fee88f1c4d25ed7..2c80d9b1b22fafa8b6e2f86dde34dc364b47b42d 100644
--- a/components/net/sf/briar/protocol/BundleReaderImpl.java
+++ b/components/net/sf/briar/protocol/BundleReaderImpl.java
@@ -123,7 +123,6 @@ class BundleReaderImpl implements BundleReader {
 		List<Message> messages = new ArrayList<Message>();
 		reader.readListStart();
 		while(!reader.hasListEnd()) {
-			reader.readUserDefinedTag(Tags.MESSAGE);
 			messages.add(messageReader.readMessage(reader));
 		}
 		reader.readListEnd();
diff --git a/components/net/sf/briar/protocol/BundleWriterImpl.java b/components/net/sf/briar/protocol/BundleWriterImpl.java
index 5640faae10416e02a5e3a9d03c3e82b650b52aab..6f4802e7a12e69f134a42d3044499594bbb6bbec 100644
--- a/components/net/sf/briar/protocol/BundleWriterImpl.java
+++ b/components/net/sf/briar/protocol/BundleWriterImpl.java
@@ -90,11 +90,8 @@ class BundleWriterImpl implements BundleWriter {
 		out.setSigning(true);
 		writer.writeUserDefinedTag(Tags.BATCH);
 		writer.writeListStart();
-		for(Raw message : messages) {
-			writer.writeUserDefinedTag(Tags.MESSAGE);
-			// Bypass the writer and write the raw message directly
-			out.write(message.getBytes());
-		}
+		// Bypass the writer and write the raw messages directly
+		for(Raw message : messages) out.write(message.getBytes());
 		writer.writeListEnd();
 		out.setSigning(false);
 		// Create and write the signature
diff --git a/components/net/sf/briar/protocol/CopyingConsumer.java b/components/net/sf/briar/protocol/CopyingConsumer.java
index f84f5a746b9f6652fa10d841df134e9686eaa5ef..6e84d5929a90c64d1ac9d0132ea6faf6b286cf60 100644
--- a/components/net/sf/briar/protocol/CopyingConsumer.java
+++ b/components/net/sf/briar/protocol/CopyingConsumer.java
@@ -5,7 +5,8 @@ import java.io.IOException;
 
 import net.sf.briar.api.serial.Consumer;
 
-public class CopyingConsumer implements Consumer {
+/** A consumer that makes a copy of the bytes consumed. */
+class CopyingConsumer implements Consumer {
 
 	private final ByteArrayOutputStream out = new ByteArrayOutputStream();
 
diff --git a/components/net/sf/briar/protocol/MessageEncoderImpl.java b/components/net/sf/briar/protocol/MessageEncoderImpl.java
index 58d52b1be405ac7f254d37924c11f8b0a58a3c8f..f59ba57eee22b8cb3d033daae6ad1b62b3196a19 100644
--- a/components/net/sf/briar/protocol/MessageEncoderImpl.java
+++ b/components/net/sf/briar/protocol/MessageEncoderImpl.java
@@ -36,6 +36,7 @@ class MessageEncoderImpl implements MessageEncoder {
 		byte[] encodedKey = keyPair.getPublic().getEncoded();
 		ByteArrayOutputStream out = new ByteArrayOutputStream();
 		Writer w = writerFactory.createWriter(out);
+		w.writeUserDefinedTag(Tags.MESSAGE);
 		parent.writeTo(w);
 		group.writeTo(w);
 		w.writeUserDefinedTag(Tags.TIMESTAMP);
@@ -60,6 +61,7 @@ class MessageEncoderImpl implements MessageEncoder {
 		// The author ID is the hash of the author's nick and public key
 		out.reset();
 		w = writerFactory.createWriter(out);
+		w.writeUserDefinedTag(Tags.AUTHOR);
 		w.writeString(nick);
 		w.writeRaw(encodedKey);
 		w.close();
diff --git a/components/net/sf/briar/protocol/MessageReaderImpl.java b/components/net/sf/briar/protocol/MessageReaderImpl.java
index cbc4a19e8e552e254ab4e2da14fe843e9dabcd53..f865fc021ae16c4e0a40c8f85ca3242c9f080e6f 100644
--- a/components/net/sf/briar/protocol/MessageReaderImpl.java
+++ b/components/net/sf/briar/protocol/MessageReaderImpl.java
@@ -39,7 +39,9 @@ class MessageReaderImpl implements MessageReader {
 		messageDigest.reset();
 		reader.addConsumer(copying);
 		reader.addConsumer(counting);
-		// Read the parent message ID
+		// Read the initial tag
+		reader.readUserDefinedTag(Tags.MESSAGE);
+		// Read the parent's message ID
 		reader.readUserDefinedTag(Tags.MESSAGE_ID);
 		byte[] b = reader.readRaw();
 		if(b.length != UniqueId.LENGTH) throw new FormatException();
@@ -54,8 +56,8 @@ class MessageReaderImpl implements MessageReader {
 		long timestamp = reader.readInt64();
 		if(timestamp < 0L) throw new FormatException();
 		// Hash the author's nick and public key to get the author ID
-		reader.readUserDefinedTag(Tags.AUTHOR);
 		reader.addConsumer(digesting);
+		reader.readUserDefinedTag(Tags.AUTHOR);
 		reader.readString();
 		byte[] encodedKey = reader.readRaw();
 		reader.removeConsumer(digesting);
diff --git a/test/net/sf/briar/protocol/ConsumersTest.java b/test/net/sf/briar/protocol/ConsumersTest.java
index 38c49599c1afa3eceab1151f7f669290b98fdde2..fdfba1c5fa3394937a81e9a1fe7405ee83e98dd4 100644
--- a/test/net/sf/briar/protocol/ConsumersTest.java
+++ b/test/net/sf/briar/protocol/ConsumersTest.java
@@ -70,4 +70,16 @@ public class ConsumersTest extends TestCase {
 			assertTrue(false);
 		} catch(FormatException expected) {}
 	}
+
+	@Test
+	public void testCopyingConsumer() throws Exception {
+		byte[] data = new byte[1234];
+		new Random().nextBytes(data);
+		// Check that a CopyingConsumer creates a faithful copy
+		CopyingConsumer cc = new CopyingConsumer();
+		cc.write(data[0]);
+		cc.write(data, 1, data.length - 2);
+		cc.write(data[data.length - 1]);
+		assertTrue(Arrays.equals(data, cc.getCopy()));
+	}
 }