diff --git a/api/net/sf/briar/api/crypto/CryptoComponent.java b/api/net/sf/briar/api/crypto/CryptoComponent.java
index 6b98dfad8902562933786c37f3ec797a261a7a87..75c6b726f90b80bfea4b2844b78f62fc0949b233 100644
--- a/api/net/sf/briar/api/crypto/CryptoComponent.java
+++ b/api/net/sf/briar/api/crypto/CryptoComponent.java
@@ -1,7 +1,6 @@
 package net.sf.briar.api.crypto;
 
 import java.security.KeyPair;
-import java.security.MessageDigest;
 import java.security.SecureRandom;
 import java.security.Signature;
 
diff --git a/api/net/sf/briar/api/crypto/MessageDigest.java b/api/net/sf/briar/api/crypto/MessageDigest.java
new file mode 100644
index 0000000000000000000000000000000000000000..7162e107e2fa73a091c8249d84e72855316adafd
--- /dev/null
+++ b/api/net/sf/briar/api/crypto/MessageDigest.java
@@ -0,0 +1,29 @@
+package net.sf.briar.api.crypto;
+
+/** An interface that allows a java.security.MessageDigest to be wrapped. */
+public interface MessageDigest {
+
+    /** @see {@link java.security.MessageDigest#digest()} */
+	byte[] digest();
+
+	/** @see {@link java.security.MessageDigest#digest(byte[])} */
+	byte[] digest(byte[] input);
+
+	/** @see {@link java.security.MessageDigest#digest(byte[], int, int)} */
+	int digest(byte[] buf, int offset, int len);
+
+	/** @see {@link java.security.MessageDigest#getDigestLength()} */
+	int getDigestLength();
+
+	/** @see {@link java.security.MessageDigest#reset()} */
+	void reset();
+
+	/** @see {@link java.security.MessageDigest#update(byte)} */
+	void update(byte input);
+
+	/** @see {@link java.security.MessageDigest#update(byte[])} */
+	void update(byte[] input);
+
+	/** @see {@link java.security.MessageDigest#update(byte[], int, int)} */
+	void update(byte[] input, int offset, int len);
+}
diff --git a/components/net/sf/briar/protocol/CopyingConsumer.java b/api/net/sf/briar/api/serial/CopyingConsumer.java
similarity index 74%
rename from components/net/sf/briar/protocol/CopyingConsumer.java
rename to api/net/sf/briar/api/serial/CopyingConsumer.java
index 97129ab32713f6f08521073d0e103cff0c78e02b..3274c6a994f6e204bfcc1ea27158248cbeb44cda 100644
--- a/components/net/sf/briar/protocol/CopyingConsumer.java
+++ b/api/net/sf/briar/api/serial/CopyingConsumer.java
@@ -1,16 +1,14 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 
-import net.sf.briar.api.serial.Consumer;
-
 /** A consumer that makes a copy of the bytes consumed. */
-class CopyingConsumer implements Consumer {
+public class CopyingConsumer implements Consumer {
 
 	private final ByteArrayOutputStream out = new ByteArrayOutputStream();
 
-	byte[] getCopy() {
+	public byte[] getCopy() {
 		return out.toByteArray();
 	}
 
diff --git a/components/net/sf/briar/protocol/CountingConsumer.java b/api/net/sf/briar/api/serial/CountingConsumer.java
similarity index 76%
rename from components/net/sf/briar/protocol/CountingConsumer.java
rename to api/net/sf/briar/api/serial/CountingConsumer.java
index 7722ce5437cbaf3540e0f88d17722d98630a854c..901bbd5642e681b3259c67009e55bc9e5b355e32 100644
--- a/components/net/sf/briar/protocol/CountingConsumer.java
+++ b/api/net/sf/briar/api/serial/CountingConsumer.java
@@ -1,24 +1,23 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
 import java.io.IOException;
 
 import net.sf.briar.api.FormatException;
-import net.sf.briar.api.serial.Consumer;
 
 /**
  * A consumer that counts the number of bytes consumed and throws a
  * FormatException if the count exceeds a given limit.
  */
-class CountingConsumer implements Consumer {
+public class CountingConsumer implements Consumer {
 
 	private final long limit;
 	private long count = 0L;
 
-	CountingConsumer(long limit) {
+	public CountingConsumer(long limit) {
 		this.limit = limit;
 	}
 
-	long getCount() {
+	public long getCount() {
 		return count;
 	}
 
diff --git a/components/net/sf/briar/protocol/DigestingConsumer.java b/api/net/sf/briar/api/serial/DigestingConsumer.java
similarity index 59%
rename from components/net/sf/briar/protocol/DigestingConsumer.java
rename to api/net/sf/briar/api/serial/DigestingConsumer.java
index 29a0c260ef853632f4cae785396e1bf46a9ad660..843cf7185eeb561710ae09eba003b91942d5fa15 100644
--- a/components/net/sf/briar/protocol/DigestingConsumer.java
+++ b/api/net/sf/briar/api/serial/DigestingConsumer.java
@@ -1,15 +1,13 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
-import java.security.MessageDigest;
-
-import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.crypto.MessageDigest;
 
 /** A consumer that passes its input through a message digest. */
-class DigestingConsumer implements Consumer {
+public class DigestingConsumer implements Consumer {
 
 	private final MessageDigest messageDigest;
 
-	DigestingConsumer(MessageDigest messageDigest) {
+	public DigestingConsumer(MessageDigest messageDigest) {
 		this.messageDigest = messageDigest;
 	}
 
diff --git a/components/net/sf/briar/protocol/SigningConsumer.java b/api/net/sf/briar/api/serial/SigningConsumer.java
similarity index 79%
rename from components/net/sf/briar/protocol/SigningConsumer.java
rename to api/net/sf/briar/api/serial/SigningConsumer.java
index c3274ba7ff7ca5bdb0f74c0bcb5c1d6b5ec82c13..4b0349ed81d1af73948fecc326ea4140b45be4b5 100644
--- a/components/net/sf/briar/protocol/SigningConsumer.java
+++ b/api/net/sf/briar/api/serial/SigningConsumer.java
@@ -1,17 +1,15 @@
-package net.sf.briar.protocol;
+package net.sf.briar.api.serial;
 
 import java.io.IOException;
 import java.security.Signature;
 import java.security.SignatureException;
 
-import net.sf.briar.api.serial.Consumer;
-
 /** A consumer that passes its input through a signature. */
-class SigningConsumer implements Consumer {
+public class SigningConsumer implements Consumer {
 
 	private final Signature signature;
 
-	SigningConsumer(Signature signature) {
+	public SigningConsumer(Signature signature) {
 		this.signature = signature;
 	}
 
diff --git a/components/net/sf/briar/crypto/CryptoComponentImpl.java b/components/net/sf/briar/crypto/CryptoComponentImpl.java
index 352182701aa8460d82807dfae682a7c554042b86..78c7badbf34510d7cc2decc4563da8764671be2b 100644
--- a/components/net/sf/briar/crypto/CryptoComponentImpl.java
+++ b/components/net/sf/briar/crypto/CryptoComponentImpl.java
@@ -5,7 +5,6 @@ import java.security.InvalidAlgorithmParameterException;
 import java.security.InvalidKeyException;
 import java.security.KeyPair;
 import java.security.KeyPairGenerator;
-import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.security.NoSuchProviderException;
 import java.security.SecureRandom;
@@ -25,6 +24,7 @@ import javax.crypto.spec.SecretKeySpec;
 
 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.SecretStorageKey;
 
 import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -198,7 +198,8 @@ class CryptoComponentImpl implements CryptoComponent {
 
 	public MessageDigest getMessageDigest() {
 		try {
-			return MessageDigest.getInstance(DIGEST_ALGO, PROVIDER);
+			return new DoubleDigest(java.security.MessageDigest.getInstance(
+					DIGEST_ALGO, PROVIDER));
 		} catch(NoSuchAlgorithmException e) {
 			throw new RuntimeException(e);
 		} catch(NoSuchProviderException e) {
diff --git a/components/net/sf/briar/crypto/DoubleDigest.java b/components/net/sf/briar/crypto/DoubleDigest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a3039845a887484c5594a01803a3d64623a983d8
--- /dev/null
+++ b/components/net/sf/briar/crypto/DoubleDigest.java
@@ -0,0 +1,59 @@
+package net.sf.briar.crypto;
+
+import net.sf.briar.api.crypto.MessageDigest;
+
+/**
+ * A message digest that prevents length extension attacks - see Ferguson and
+ * Schneier, <i>Practical Cryptography</i>, chapter 6.
+ * <p>
+ * "Let h be an interative hash function. The hash function h<sub>d</sub> is
+ * defined by h<sub>d</sub> := h(h(m)), and has a claimed security level of
+ * min(k, n/2) where k is the security level of h and n is the size of the hash
+ * result."
+ */
+class DoubleDigest implements MessageDigest {
+
+	private final java.security.MessageDigest delegate;
+
+	DoubleDigest(java.security.MessageDigest delegate) {
+		this.delegate = delegate;
+	}
+
+	public byte[] digest() {
+		byte[] digest = delegate.digest(); // h(m)
+		delegate.update(digest);
+		return delegate.digest(); // h(h(m))
+	}
+
+	public byte[] digest(byte[] input) {
+		delegate.update(input);
+		return digest();
+	}
+
+	public int digest(byte[] buf, int offset, int len) {
+		byte[] digest = digest();
+		len = Math.min(len, digest.length);
+		System.arraycopy(digest, 0, buf, offset, len);
+		return len;
+	}
+
+	public int getDigestLength() {
+		return delegate.getDigestLength();
+	}
+
+	public void reset() {
+		delegate.reset();
+	}
+
+	public void update(byte input) {
+		delegate.update(input);
+	}
+
+	public void update(byte[] input) {
+		delegate.update(input);
+	}
+
+	public void update(byte[] input, int offset, int len) {
+		delegate.update(input, offset, len);
+	}
+}
diff --git a/components/net/sf/briar/protocol/AckReader.java b/components/net/sf/briar/protocol/AckReader.java
index af02456d4265197733832dd814e1f0ee44b8bfc4..870abefdfd12624925d392514fa3e590217a1e7c 100644
--- a/components/net/sf/briar/protocol/AckReader.java
+++ b/components/net/sf/briar/protocol/AckReader.java
@@ -8,6 +8,7 @@ import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/AuthorFactoryImpl.java b/components/net/sf/briar/protocol/AuthorFactoryImpl.java
index 7356a0414bb239785259e951a2f0ea5c14b532c5..d1fb362d5377bfff3fc297c1ec88283cca7bacb2 100644
--- a/components/net/sf/briar/protocol/AuthorFactoryImpl.java
+++ b/components/net/sf/briar/protocol/AuthorFactoryImpl.java
@@ -2,9 +2,9 @@ package net.sf.briar.protocol;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorFactory;
 import net.sf.briar.api.protocol.AuthorId;
diff --git a/components/net/sf/briar/protocol/AuthorReader.java b/components/net/sf/briar/protocol/AuthorReader.java
index 4451197bbe0cdc0c43e3757ab0c53bd57f2af6a1..eb18812bbeb72dfe018fa5644e636830e80481e5 100644
--- a/components/net/sf/briar/protocol/AuthorReader.java
+++ b/components/net/sf/briar/protocol/AuthorReader.java
@@ -1,14 +1,15 @@
 package net.sf.briar.protocol;
 
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorFactory;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/BatchReader.java b/components/net/sf/briar/protocol/BatchReader.java
index e77c711f582867235c662da4f01cba0927d56b26..04151a76a6a0dafb2f66247f9cafcec99fd026fa 100644
--- a/components/net/sf/briar/protocol/BatchReader.java
+++ b/components/net/sf/briar/protocol/BatchReader.java
@@ -1,16 +1,18 @@
 package net.sf.briar.protocol;
 
 import java.io.IOException;
-import java.security.MessageDigest;
 import java.util.List;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/GroupFactoryImpl.java b/components/net/sf/briar/protocol/GroupFactoryImpl.java
index 6df7b18553fef451c776006d8d8d9ebaf20d6926..3aebebff0148c2304387532c5da8204e2209eee6 100644
--- a/components/net/sf/briar/protocol/GroupFactoryImpl.java
+++ b/components/net/sf/briar/protocol/GroupFactoryImpl.java
@@ -2,9 +2,9 @@ package net.sf.briar.protocol;
 
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
diff --git a/components/net/sf/briar/protocol/GroupReader.java b/components/net/sf/briar/protocol/GroupReader.java
index 9e5dc2d14b2e62e2e6d186c0a25dc25a4dfb63b0..8eba20c68baddda0f806ae57d86f682e0afd0c3b 100644
--- a/components/net/sf/briar/protocol/GroupReader.java
+++ b/components/net/sf/briar/protocol/GroupReader.java
@@ -1,14 +1,15 @@
 package net.sf.briar.protocol;
 
 import java.io.IOException;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Group;
 import net.sf.briar.api.protocol.GroupFactory;
 import net.sf.briar.api.protocol.GroupId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/MessageEncoderImpl.java b/components/net/sf/briar/protocol/MessageEncoderImpl.java
index 5d5d68dd4fb22ade8b3291fac4195944d3172831..143c337831c8c2ec4b0ddcbef88a36534f4e4c1d 100644
--- a/components/net/sf/briar/protocol/MessageEncoderImpl.java
+++ b/components/net/sf/briar/protocol/MessageEncoderImpl.java
@@ -3,12 +3,12 @@ package net.sf.briar.protocol;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
 import java.security.PrivateKey;
 import java.security.SecureRandom;
 import java.security.Signature;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Author;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Group;
@@ -19,6 +19,9 @@ import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
+import net.sf.briar.api.serial.DigestingConsumer;
+import net.sf.briar.api.serial.SigningConsumer;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
diff --git a/components/net/sf/briar/protocol/MessageReader.java b/components/net/sf/briar/protocol/MessageReader.java
index 54502aff02970f7bf4c0d80c2f8acaa15d41ac95..3bba3525ec548b6d1fbaa66610c77dd713ed8b04 100644
--- a/components/net/sf/briar/protocol/MessageReader.java
+++ b/components/net/sf/briar/protocol/MessageReader.java
@@ -2,13 +2,13 @@ package net.sf.briar.protocol;
 
 import java.io.IOException;
 import java.security.GeneralSecurityException;
-import java.security.MessageDigest;
 import java.security.PublicKey;
 import java.security.Signature;
 
 import net.sf.briar.api.FormatException;
 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.protocol.Author;
 import net.sf.briar.api.protocol.AuthorId;
 import net.sf.briar.api.protocol.Group;
@@ -17,6 +17,8 @@ import net.sf.briar.api.protocol.Message;
 import net.sf.briar.api.protocol.MessageId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
+import net.sf.briar.api.serial.CopyingConsumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/OfferReader.java b/components/net/sf/briar/protocol/OfferReader.java
index 7d16c0b2f3c377e3886e9592548faba666b6a762..f40389f71aeb5a99a2f7a8f48992e4060773c4ce 100644
--- a/components/net/sf/briar/protocol/OfferReader.java
+++ b/components/net/sf/briar/protocol/OfferReader.java
@@ -8,6 +8,7 @@ import net.sf.briar.api.protocol.Offer;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/RequestReader.java b/components/net/sf/briar/protocol/RequestReader.java
index b2edd24cb9c6cad49d5a6ec538e7c47aa274b39a..f64fecf64bd645d5f84fe91d1511b98c362af462 100644
--- a/components/net/sf/briar/protocol/RequestReader.java
+++ b/components/net/sf/briar/protocol/RequestReader.java
@@ -7,6 +7,7 @@ import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Request;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/SubscriptionUpdateReader.java b/components/net/sf/briar/protocol/SubscriptionUpdateReader.java
index b5f37f01f8b53eeda0879da869bce6a82ddcc8a1..392a5c99cb518d12a6f9655b7710f2a57c352417 100644
--- a/components/net/sf/briar/protocol/SubscriptionUpdateReader.java
+++ b/components/net/sf/briar/protocol/SubscriptionUpdateReader.java
@@ -9,6 +9,7 @@ import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.SubscriptionUpdate;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/TransportUpdateReader.java b/components/net/sf/briar/protocol/TransportUpdateReader.java
index 5eae4d6138f10b8691fa7f0bfe0adb26e4f1b217..a3bed1a60db19a7e587141bb2af34b13e3de669e 100644
--- a/components/net/sf/briar/protocol/TransportUpdateReader.java
+++ b/components/net/sf/briar/protocol/TransportUpdateReader.java
@@ -15,6 +15,7 @@ import net.sf.briar.api.protocol.TransportUpdate;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.UniqueId;
 import net.sf.briar.api.serial.Consumer;
+import net.sf.briar.api.serial.CountingConsumer;
 import net.sf.briar.api.serial.ObjectReader;
 import net.sf.briar.api.serial.Reader;
 
diff --git a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
index cbae50f1d5910a3bd6f43fa4bb3502f8f5901af5..3c8d5fe93a9b3f20f40ad26d4603b58e87ed3720 100644
--- a/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
+++ b/components/net/sf/briar/protocol/writers/BatchWriterImpl.java
@@ -2,23 +2,24 @@ package net.sf.briar.protocol.writers;
 
 import java.io.IOException;
 import java.io.OutputStream;
-import java.security.DigestOutputStream;
-import java.security.MessageDigest;
 
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.ProtocolConstants;
 import net.sf.briar.api.protocol.Types;
 import net.sf.briar.api.protocol.writers.BatchWriter;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.api.serial.SerialComponent;
 import net.sf.briar.api.serial.Writer;
 import net.sf.briar.api.serial.WriterFactory;
 
 class BatchWriterImpl implements BatchWriter {
 
-	private final DigestOutputStream out;
+	private final OutputStream out;
 	private final int headerLength, footerLength;
 	private final Writer w;
 	private final MessageDigest messageDigest;
+	private final DigestingConsumer digestingConsumer;
 
 	private boolean started = false;
 	private int capacity = ProtocolConstants.MAX_PACKET_LENGTH;
@@ -26,12 +27,13 @@ class BatchWriterImpl implements BatchWriter {
 
 	BatchWriterImpl(OutputStream out, SerialComponent serial,
 			WriterFactory writerFactory, MessageDigest messageDigest) {
-		this.out = new DigestOutputStream(out, messageDigest);
+		this.out = out;
 		headerLength = serial.getSerialisedUserDefinedIdLength(Types.BATCH)
 		+ serial.getSerialisedListStartLength();
 		footerLength = serial.getSerialisedListEndLength();
 		w = writerFactory.createWriter(this.out);
 		this.messageDigest = messageDigest;
+		digestingConsumer = new DigestingConsumer(messageDigest);
 	}
 
 	public int getCapacity() {
@@ -58,6 +60,7 @@ class BatchWriterImpl implements BatchWriter {
 	public BatchId finish() throws IOException {
 		if(!started) start();
 		w.writeListEnd();
+		w.removeConsumer(digestingConsumer);
 		out.flush();
 		remaining = capacity = ProtocolConstants.MAX_PACKET_LENGTH;
 		started = false;
@@ -66,6 +69,7 @@ class BatchWriterImpl implements BatchWriter {
 
 	private void start() throws IOException {
 		messageDigest.reset();
+		w.addConsumer(digestingConsumer);
 		w.writeUserDefinedId(Types.BATCH);
 		w.writeListStart();
 		remaining -= headerLength;
diff --git a/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java b/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
index ed6b55844824e765bd3ed7ab42105a218a30d888..e977d2bd01b16b889949b3315434dd9dc7c926aa 100644
--- a/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
+++ b/components/net/sf/briar/protocol/writers/ProtocolWriterFactoryImpl.java
@@ -1,9 +1,9 @@
 package net.sf.briar.protocol.writers;
 
 import java.io.OutputStream;
-import java.security.MessageDigest;
 
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.writers.AckWriter;
 import net.sf.briar.api.protocol.writers.BatchWriter;
 import net.sf.briar.api.protocol.writers.OfferWriter;
diff --git a/test/net/sf/briar/protocol/BatchReaderTest.java b/test/net/sf/briar/protocol/BatchReaderTest.java
index c6727e2749f5a8f3a5cf8c20d0ab6086ac9bba3c..ec683095e62a0195a6415e0493feb0982db31552 100644
--- a/test/net/sf/briar/protocol/BatchReaderTest.java
+++ b/test/net/sf/briar/protocol/BatchReaderTest.java
@@ -3,12 +3,12 @@ package net.sf.briar.protocol;
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
-import java.security.MessageDigest;
 import java.util.Collections;
 
 import junit.framework.TestCase;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
 import net.sf.briar.api.protocol.Batch;
 import net.sf.briar.api.protocol.BatchId;
 import net.sf.briar.api.protocol.Message;
diff --git a/test/net/sf/briar/protocol/ConsumersTest.java b/test/net/sf/briar/protocol/ConsumersTest.java
index c2bc475fd3afde6bf9eb437fdc3547d5b8c64c31..9605c1c403daaabe83b84eb6f67383ee1ef0158b 100644
--- a/test/net/sf/briar/protocol/ConsumersTest.java
+++ b/test/net/sf/briar/protocol/ConsumersTest.java
@@ -2,12 +2,15 @@ package net.sf.briar.protocol;
 
 import static org.junit.Assert.assertArrayEquals;
 
-import java.security.MessageDigest;
 import java.util.Random;
 
 import junit.framework.TestCase;
 import net.sf.briar.api.FormatException;
 import net.sf.briar.api.crypto.CryptoComponent;
+import net.sf.briar.api.crypto.MessageDigest;
+import net.sf.briar.api.serial.CopyingConsumer;
+import net.sf.briar.api.serial.CountingConsumer;
+import net.sf.briar.api.serial.DigestingConsumer;
 import net.sf.briar.crypto.CryptoModule;
 
 import org.junit.Before;