diff --git a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
index d7c9c051dceeaf287d8c8e916892998f37448b90..ab8fa52cc1bbc23b0ff5a70d52bdf6e3723c6195 100644
--- a/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
+++ b/briar-android-tests/src/test/java/org/briarproject/PrivateGroupManagerTest.java
@@ -5,6 +5,7 @@ import net.jodah.concurrentunit.Waiter;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.clients.ContactGroupFactory;
 import org.briarproject.api.clients.MessageTracker.GroupCount;
+import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager;
 import org.briarproject.api.crypto.CryptoComponent;
@@ -29,7 +30,7 @@ import org.briarproject.api.privategroup.JoinMessageHeader;
 import org.briarproject.api.privategroup.PrivateGroup;
 import org.briarproject.api.privategroup.PrivateGroupFactory;
 import org.briarproject.api.privategroup.PrivateGroupManager;
-import org.briarproject.api.sync.Group;
+import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.briarproject.api.sync.SyncSession;
@@ -65,7 +66,7 @@ import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
 import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
 import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US;
 import static org.briarproject.api.privategroup.Visibility.VISIBLE;
-import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
+import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
 import static org.briarproject.api.sync.Group.Visibility.SHARED;
 import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
 import static org.briarproject.api.sync.ValidationManager.State.INVALID;
@@ -103,6 +104,8 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 	PrivateGroupFactory privateGroupFactory;
 	@Inject
 	GroupMessageFactory groupMessageFactory;
+	@Inject
+	GroupInvitationFactory groupInvitationFactory;
 
 	// objects accessed from background threads need to be volatile
 	private volatile Waiter validationWaiter;
@@ -358,13 +361,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author1 joins privateGroup0 with wrong timestamp
 		joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author1.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		Contact c1 = contactManager0.getContact(contactId1);
+		byte[] creatorSignature = groupInvitationFactory
+				.signInvitation(c1, privateGroup0.getId(), inviteTime,
+						author0.getPrivateKey());
 		GroupMessage joinMsg1 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
 						inviteTime, creatorSignature);
@@ -402,13 +402,11 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author0 joins privateGroup0 with wrong member's join message
 		long joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime - 1;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author0.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		BdfList toSign = groupInvitationFactory
+				.createInviteToken(author0.getId(), author0.getId(),
+						privateGroup0.getId(), inviteTime);
+		byte[] creatorSignature = clientHelper
+				.sign(SIGNING_LABEL_INVITE, toSign, author0.getPrivateKey());
 		// join message should not include invite time and creator's signature
 		GroupMessage joinMsg0 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author0,
@@ -426,13 +424,11 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author1 joins privateGroup0 with wrong signature in join message
 		joinTime = clock.currentTimeMillis();
 		inviteTime = joinTime - 1;
-		invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author1.getId());
-		toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
 		// signature uses joiner's key, not creator's key
-		creatorSignature = clientHelper.sign(toSign, author1.getPrivateKey());
+		Contact c1 = contactManager0.getContact(contactId1);
+		creatorSignature = groupInvitationFactory
+				.signInvitation(c1, privateGroup0.getId(), inviteTime,
+						author1.getPrivateKey());
 		GroupMessage joinMsg1 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
 						inviteTime, creatorSignature);
@@ -529,13 +525,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author2 joins privateGroup0
 		long joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime - 1;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author2.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		Contact c2 = contactManager0.getContact(contactId2);
+		byte[] creatorSignature = groupInvitationFactory
+				.signInvitation(c2, privateGroup0.getId(), inviteTime,
+						author0.getPrivateKey());
 		GroupMessage joinMsg2 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author2,
 						inviteTime, creatorSignature);
@@ -753,13 +746,10 @@ public class PrivateGroupManagerTest extends BriarIntegrationTest {
 		// author1 joins privateGroup0
 		joinTime = clock.currentTimeMillis();
 		long inviteTime = joinTime - 1;
-		Group invitationGroup = contactGroupFactory
-				.createContactGroup(CLIENT_ID, author0.getId(),
-						author1.getId());
-		BdfList toSign = BdfList.of(0, inviteTime, invitationGroup.getId(),
-				privateGroup0.getId());
-		byte[] creatorSignature =
-				clientHelper.sign(toSign, author0.getPrivateKey());
+		Contact c1 = contactManager0.getContact(contactId1);
+		byte[] creatorSignature = groupInvitationFactory
+				.signInvitation(c1, privateGroup0.getId(), inviteTime,
+						author0.getPrivateKey());
 		GroupMessage joinMsg1 = groupMessageFactory
 				.createJoinMessage(privateGroup0.getId(), joinTime, author1,
 						inviteTime, creatorSignature);
diff --git a/briar-android-tests/src/test/java/org/briarproject/introduction/IntroductionIntegrationTest.java b/briar-android-tests/src/test/java/org/briarproject/introduction/IntroductionIntegrationTest.java
index 86ed51afb12f38f345522aa389ba7aa7970a846b..826524cc18ae7f9a0d86ca37befe6bd924827e5d 100644
--- a/briar-android-tests/src/test/java/org/briarproject/introduction/IntroductionIntegrationTest.java
+++ b/briar-android-tests/src/test/java/org/briarproject/introduction/IntroductionIntegrationTest.java
@@ -17,7 +17,6 @@ import org.briarproject.api.contact.ContactManager;
 import org.briarproject.api.crypto.CryptoComponent;
 import org.briarproject.api.crypto.KeyPair;
 import org.briarproject.api.crypto.SecretKey;
-import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfEntry;
 import org.briarproject.api.data.BdfList;
@@ -95,6 +94,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUE
 import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
 import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
 import static org.briarproject.api.sync.ValidationManager.State.INVALID;
+import static org.briarproject.introduction.IntroduceeManager.SIGNING_LABEL_RESPONSE;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -922,10 +922,8 @@ public class IntroductionIntegrationTest extends BriarIntegrationTest {
 		byte[] nonce1 = crypto.deriveSignatureNonce(secretKey, true);
 
 		// Signature 1
-		Signature signature = crypto.getSignature();
-		signature.initSign(keyPair1.getPrivate());
-		signature.update(nonce1);
-		byte[] sig1 = signature.sign();
+		byte[] sig1 = crypto.sign(SIGNING_LABEL_RESPONSE, nonce1,
+				keyPair1.getPrivate().getEncoded());
 
 		// MAC 1
 		SecretKey macKey1 = crypto.deriveMacKey(secretKey, true);
diff --git a/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
index 7b05c0078ec5d5ba84e86412e5291020b1c196a1..4f8292720ad203b1f87af6afcedb190ac5c4591d 100644
--- a/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
+++ b/briar-api/src/org/briarproject/api/blogs/BlogPostFactory.java
@@ -11,8 +11,13 @@ import org.jetbrains.annotations.Nullable;
 
 import java.security.GeneralSecurityException;
 
+import static org.briarproject.api.blogs.BlogManager.CLIENT_ID;
+
 public interface BlogPostFactory {
 
+	String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
+	String SIGNING_LABEL_COMMENT = CLIENT_ID + "/COMMENT";
+
 	BlogPost createBlogPost(@NotNull GroupId groupId, long timestamp,
 			@Nullable MessageId parent, @NotNull LocalAuthor author,
 			@NotNull String body)
diff --git a/briar-api/src/org/briarproject/api/clients/ClientHelper.java b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
index 7743f3ba5eb052e9474c2ee8a05384511c17b140..32af81f627d94021792f0451cd5bf90857142fa2 100644
--- a/briar-api/src/org/briarproject/api/clients/ClientHelper.java
+++ b/briar-api/src/org/briarproject/api/clients/ClientHelper.java
@@ -92,10 +92,10 @@ public interface ClientHelper {
 
 	BdfList toList(Message m) throws FormatException;
 
-	byte[] sign(BdfList toSign, byte[] privateKey)
+	byte[] sign(String label, BdfList toSign, byte[] privateKey)
 			throws FormatException, GeneralSecurityException;
 
-	void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
-			throws FormatException, GeneralSecurityException;
+	void verifySignature(String label, byte[] sig, byte[] publicKey,
+			BdfList signed) throws FormatException, GeneralSecurityException;
 
 }
diff --git a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
index 50a9c94d565d612f645fc5b5f8cbba561d937f18..60a606889a14f1ab846f0c512c467eb96b924561 100644
--- a/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
+++ b/briar-api/src/org/briarproject/api/crypto/CryptoComponent.java
@@ -16,8 +16,6 @@ public interface CryptoComponent {
 
 	SecureRandom getSecureRandom();
 
-	Signature getSignature();
-
 	KeyPair generateAgreementKeyPair();
 
 	KeyParser getAgreementKeyParser();
@@ -143,6 +141,26 @@ public interface CryptoComponent {
 	/** Encodes the pseudo-random tag that is used to recognise a stream. */
 	void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
 
+	/**
+	 * Signs the given byte[] with the given PrivateKey.
+	 *
+	 * @param label A label specific to this signature
+	 *              to ensure that the signature cannot be repurposed
+	 */
+	byte[] sign(String label, byte[] toSign, byte[] privateKey)
+			throws GeneralSecurityException;
+
+	/**
+	 * Verifies that the given signature is valid for the signedData
+	 * and the given publicKey.
+	 *
+	 * @param label A label that was specific to this signature
+	 *              to ensure that the signature cannot be repurposed
+	 * @return true if the signature was valid, false otherwise.
+	 */
+	boolean verify(String label, byte[] signedData, byte[] publicKey,
+			byte[] signature) throws GeneralSecurityException;
+
 	/**
 	 * Returns the hash of the given inputs. The inputs are unambiguously
 	 * combined by prefixing each input with its length.
diff --git a/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java b/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java
index 820a11acc25f5682f17dca3f36c210c366afe0c8..d5b8d9b35614555678a7c67848cad529e3606fee 100644
--- a/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java
+++ b/briar-api/src/org/briarproject/api/forum/ForumPostFactory.java
@@ -8,8 +8,12 @@ import org.briarproject.api.sync.MessageId;
 
 import java.security.GeneralSecurityException;
 
+import static org.briarproject.api.forum.ForumManager.CLIENT_ID;
+
 public interface ForumPostFactory {
 
+	String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
+
 	@CryptoExecutor
 	ForumPost createPost(GroupId groupId, long timestamp, MessageId parent,
 			LocalAuthor author, String body)
diff --git a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
index ff1b72e84f482a70274718e37428a11b6025c472..220e958fc07f3339e2a13c804a0f3daa794e65d5 100644
--- a/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
+++ b/briar-api/src/org/briarproject/api/privategroup/GroupMessageFactory.java
@@ -1,16 +1,20 @@
 package org.briarproject.api.privategroup;
 
 import org.briarproject.api.crypto.CryptoExecutor;
-import org.briarproject.api.identity.Author;
 import org.briarproject.api.identity.LocalAuthor;
 import org.briarproject.api.nullsafety.NotNullByDefault;
 import org.briarproject.api.sync.GroupId;
 import org.briarproject.api.sync.MessageId;
 import org.jetbrains.annotations.Nullable;
 
+import static org.briarproject.api.privategroup.PrivateGroupManager.CLIENT_ID;
+
 @NotNullByDefault
 public interface GroupMessageFactory {
 
+	String SIGNING_LABEL_JOIN = CLIENT_ID + "/JOIN";
+	String SIGNING_LABEL_POST = CLIENT_ID + "/POST";
+
 	/**
 	 * Creates a join announcement message for the creator of a group.
 	 *
diff --git a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java
index e5cf41adf714a6e73ee0a36bdac94e3305a2b596..fdde77abe33e8c6003b60e14790d659cdbd28fe1 100644
--- a/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java
+++ b/briar-api/src/org/briarproject/api/privategroup/invitation/GroupInvitationFactory.java
@@ -6,8 +6,12 @@ import org.briarproject.api.data.BdfList;
 import org.briarproject.api.identity.AuthorId;
 import org.briarproject.api.sync.GroupId;
 
+import static org.briarproject.api.privategroup.invitation.GroupInvitationManager.CLIENT_ID;
+
 public interface GroupInvitationFactory {
 
+	String SIGNING_LABEL_INVITE = CLIENT_ID + "/INVITE";
+
 	/**
 	 * Returns a signature to include when inviting a member to join a private
 	 * group. If the member accepts the invitation, the signature will be
@@ -24,4 +28,5 @@ public interface GroupInvitationFactory {
 	 */
 	BdfList createInviteToken(AuthorId creatorId, AuthorId memberId,
 			GroupId privateGroupId, long timestamp);
+
 }
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
index fdab87cac682357641e27d1b1f92674b197b85c3..52cbc72a76e3e6db71ed4471c24bf7626f408734 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostFactoryImpl.java
@@ -52,7 +52,8 @@ class BlogPostFactoryImpl implements BlogPostFactory {
 		BdfList signed = BdfList.of(groupId, timestamp, body);
 
 		// Generate the signature
-		byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
+		byte[] sig = clientHelper
+				.sign(SIGNING_LABEL_POST, signed, author.getPrivateKey());
 
 		// Serialise the signed message
 		BdfList message = BdfList.of(POST.getInt(), body, sig);
@@ -77,7 +78,8 @@ class BlogPostFactoryImpl implements BlogPostFactory {
 		// Generate the signature
 		BdfList signed =
 				BdfList.of(groupId, timestamp, comment, pOriginalId, parentId);
-		byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
+		byte[] sig = clientHelper
+				.sign(SIGNING_LABEL_COMMENT, signed, author.getPrivateKey());
 
 		// Serialise the signed message
 		BdfList message =
diff --git a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
index 088354fc1697caa67d714f2eb554fc4a332e42e5..4b7dd148e39197e64f86a63084075895896dc19c 100644
--- a/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
+++ b/briar-core/src/org/briarproject/blogs/BlogPostValidator.java
@@ -39,6 +39,8 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_TIME_RECEIVED;
 import static org.briarproject.api.blogs.BlogConstants.KEY_TYPE;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_COMMENT_LENGTH;
 import static org.briarproject.api.blogs.BlogConstants.MAX_BLOG_POST_BODY_LENGTH;
+import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_COMMENT;
+import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_POST;
 import static org.briarproject.api.blogs.MessageType.COMMENT;
 import static org.briarproject.api.blogs.MessageType.POST;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
@@ -105,7 +107,9 @@ class BlogPostValidator extends BdfMessageValidator {
 		Blog b = blogFactory.parseBlog(g);
 		Author a = b.getAuthor();
 		try {
-			clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+			clientHelper
+					.verifySignature(SIGNING_LABEL_POST, sig, a.getPublicKey(),
+							signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
@@ -150,7 +154,8 @@ class BlogPostValidator extends BdfMessageValidator {
 		Blog b = blogFactory.parseBlog(g);
 		Author a = b.getAuthor();
 		try {
-			clientHelper.verifySignature(sig, a.getPublicKey(), signed);
+			clientHelper.verifySignature(SIGNING_LABEL_COMMENT, sig,
+					a.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
diff --git a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
index 7356ca1d1cd164b42d2f71b2e124a6af8b2ac561..bca3812f35a16366c1b570c58df898c8c0adc16d 100644
--- a/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
+++ b/briar-core/src/org/briarproject/clients/ClientHelperImpl.java
@@ -3,10 +3,6 @@ package org.briarproject.clients;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.crypto.KeyParser;
-import org.briarproject.api.crypto.PrivateKey;
-import org.briarproject.api.crypto.PublicKey;
-import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.BdfReader;
@@ -346,27 +342,15 @@ class ClientHelperImpl implements ClientHelper {
 	}
 
 	@Override
-	public byte[] sign(BdfList toSign, byte[] privateKey)
+	public byte[] sign(String label, BdfList toSign, byte[] privateKey)
 			throws FormatException, GeneralSecurityException {
-		Signature signature = crypto.getSignature();
-		KeyParser keyParser = crypto.getSignatureKeyParser();
-		PrivateKey key = keyParser.parsePrivateKey(privateKey);
-		signature.initSign(key);
-		signature.update(toByteArray(toSign));
-		return signature.sign();
+		return crypto.sign(label, toByteArray(toSign), privateKey);
 	}
 
 	@Override
-	public void verifySignature(byte[] sig, byte[] publicKey, BdfList signed)
-			throws FormatException, GeneralSecurityException {
-		// Parse the public key
-		KeyParser keyParser = crypto.getSignatureKeyParser();
-		PublicKey key = keyParser.parsePublicKey(publicKey);
-		// Verify the signature
-		Signature signature = crypto.getSignature();
-		signature.initVerify(key);
-		signature.update(toByteArray(signed));
-		if (!signature.verify(sig)) {
+	public void verifySignature(String label, byte[] sig, byte[] publicKey,
+			BdfList signed) throws FormatException, GeneralSecurityException {
+		if (!crypto.verify(label, toByteArray(signed), publicKey, sig)) {
 			throw new GeneralSecurityException("Invalid signature");
 		}
 	}
diff --git a/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java b/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java
index 85eb36f9a63ea7900a38fff294d81f5d7fabc0e4..ec825052cc7643795930e40ab89d55b73c205484 100644
--- a/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java
+++ b/briar-core/src/org/briarproject/contact/ContactExchangeTaskImpl.java
@@ -7,9 +7,7 @@ import org.briarproject.api.contact.ContactExchangeTask;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager;
 import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.crypto.KeyParser;
 import org.briarproject.api.crypto.SecretKey;
-import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.data.BdfReader;
 import org.briarproject.api.data.BdfReaderFactory;
@@ -55,6 +53,8 @@ public class ContactExchangeTaskImpl extends Thread
 
 	private static final Logger LOG =
 			Logger.getLogger(ContactExchangeTaskImpl.class.getName());
+	private static final String SIGNING_LABEL_EXCHANGE =
+			"org.briarproject.briar.contact/EXCHANGE";
 
 	private final DatabaseComponent db;
 	private final AuthorFactory authorFactory;
@@ -219,12 +219,9 @@ public class ContactExchangeTaskImpl extends Thread
 	private void sendPseudonym(BdfWriter w, byte[] nonce)
 			throws GeneralSecurityException, IOException {
 		// Sign the nonce
-		Signature signature = crypto.getSignature();
-		KeyParser keyParser = crypto.getSignatureKeyParser();
 		byte[] privateKey = localAuthor.getPrivateKey();
-		signature.initSign(keyParser.parsePrivateKey(privateKey));
-		signature.update(nonce);
-		byte[] sig = signature.sign();
+		byte[] sig = crypto.sign(SIGNING_LABEL_EXCHANGE, nonce, privateKey);
+
 		// Write the name, public key and signature
 		w.writeListStart();
 		w.writeString(localAuthor.getName());
@@ -244,11 +241,7 @@ public class ContactExchangeTaskImpl extends Thread
 		r.readListEnd();
 		LOG.info("Received pseudonym");
 		// Verify the signature
-		Signature signature = crypto.getSignature();
-		KeyParser keyParser = crypto.getSignatureKeyParser();
-		signature.initVerify(keyParser.parsePublicKey(publicKey));
-		signature.update(nonce);
-		if (!signature.verify(sig)) {
+		if (!crypto.verify(SIGNING_LABEL_EXCHANGE, nonce, publicKey, sig)) {
 			if (LOG.isLoggable(INFO))
 				LOG.info("Invalid signature");
 			throw new GeneralSecurityException();
diff --git a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
index 22698a22ff70aac5087158a3f17684aab0c87686..eb98217947a30dc7a464033d3a94f5881557003a 100644
--- a/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
+++ b/briar-core/src/org/briarproject/crypto/CryptoComponentImpl.java
@@ -164,11 +164,6 @@ class CryptoComponentImpl implements CryptoComponent {
 		return secret;
 	}
 
-	@Override
-	public Signature getSignature() {
-		return new SignatureImpl(secureRandom);
-	}
-
 	@Override
 	public KeyPair generateAgreementKeyPair() {
 		AsymmetricCipherKeyPair keyPair =
@@ -399,6 +394,40 @@ class CryptoComponentImpl implements CryptoComponent {
 		System.arraycopy(mac, 0, tag, 0, TAG_LENGTH);
 	}
 
+	@Override
+	public byte[] sign(String label, byte[] toSign, byte[] privateKey)
+			throws GeneralSecurityException {
+		Signature signature = new SignatureImpl(secureRandom);
+		KeyParser keyParser = getSignatureKeyParser();
+		PrivateKey key = keyParser.parsePrivateKey(privateKey);
+		signature.initSign(key);
+		updateSignature(signature, label, toSign);
+		return signature.sign();
+	}
+
+	@Override
+	public boolean verify(String label, byte[] signedData, byte[] publicKey,
+			byte[] signature) throws GeneralSecurityException {
+		Signature sig = new SignatureImpl(secureRandom);
+		KeyParser keyParser = getSignatureKeyParser();
+		PublicKey key = keyParser.parsePublicKey(publicKey);
+		sig.initVerify(key);
+		updateSignature(sig, label, signedData);
+		return sig.verify(signature);
+	}
+
+	private void updateSignature(Signature signature, String label,
+			byte[] toSign) {
+		byte[] labelBytes = StringUtils.toUtf8(label);
+		byte[] length = new byte[INT_32_BYTES];
+		ByteUtils.writeUint32(labelBytes.length, length, 0);
+		signature.update(length);
+		signature.update(labelBytes);
+		ByteUtils.writeUint32(toSign.length, length, 0);
+		signature.update(length);
+		signature.update(toSign);
+	}
+
 	@Override
 	public byte[] hash(byte[]... inputs) {
 		MessageDigest digest = getMessageDigest();
diff --git a/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java b/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
index f4ddfad0f0463d321e87bdcea4a67143fbece460..70a545e1f9f6d80c3729ad026aa100ed56d55516 100644
--- a/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
+++ b/briar-core/src/org/briarproject/forum/ForumPostFactoryImpl.java
@@ -39,7 +39,8 @@ class ForumPostFactoryImpl implements ForumPostFactory {
 		BdfList signed = BdfList.of(groupId, timestamp, parent, authorList,
 				body);
 		// Sign the data
-		byte[] sig = clientHelper.sign(signed, author.getPrivateKey());
+		byte[] sig = clientHelper
+				.sign(SIGNING_LABEL_POST, signed, author.getPrivateKey());
 		// Serialise the signed message
 		BdfList message = BdfList.of(parent, authorList, body, sig);
 		Message m = clientHelper.createMessage(groupId, timestamp, message);
diff --git a/briar-core/src/org/briarproject/forum/ForumPostValidator.java b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
index 19e62584c84227c35d35b1b54a53c740e36da380..b7d2f70660889b0094038f99015ebc6468ec866c 100644
--- a/briar-core/src/org/briarproject/forum/ForumPostValidator.java
+++ b/briar-core/src/org/briarproject/forum/ForumPostValidator.java
@@ -22,6 +22,7 @@ import java.util.Collection;
 import java.util.Collections;
 
 import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
+import static org.briarproject.api.forum.ForumPostFactory.SIGNING_LABEL_POST;
 import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
@@ -68,7 +69,8 @@ class ForumPostValidator extends BdfMessageValidator {
 		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), parent,
 				authorList, forumPostBody);
 		try {
-			clientHelper.verifySignature(sig, author.getPublicKey(), signed);
+			clientHelper.verifySignature(SIGNING_LABEL_POST, sig,
+					author.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
diff --git a/briar-core/src/org/briarproject/introduction/IntroduceeManager.java b/briar-core/src/org/briarproject/introduction/IntroduceeManager.java
index 450fc023a39a017b21354abeabb101b2d082e181..e0227b6bc4f436820bd75407aa8169b2fd0a9bd6 100644
--- a/briar-core/src/org/briarproject/introduction/IntroduceeManager.java
+++ b/briar-core/src/org/briarproject/introduction/IntroduceeManager.java
@@ -13,7 +13,6 @@ import org.briarproject.api.crypto.KeyParser;
 import org.briarproject.api.crypto.PrivateKey;
 import org.briarproject.api.crypto.PublicKey;
 import org.briarproject.api.crypto.SecretKey;
-import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfList;
 import org.briarproject.api.db.DatabaseComponent;
@@ -88,11 +87,13 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
 import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ABORT;
 import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
 import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
+import static org.briarproject.api.introduction.IntroductionManager.CLIENT_ID;
 
 class IntroduceeManager {
 
 	private static final Logger LOG =
 			Logger.getLogger(IntroduceeManager.class.getName());
+	static final String SIGNING_LABEL_RESPONSE = CLIENT_ID + "/RESPONSE";
 
 	private final MessageSender messageSender;
 	private final DatabaseComponent db;
@@ -453,12 +454,8 @@ class IntroduceeManager {
 		localState.put(MAC_KEY, theirMacKey.getBytes());
 
 		// Sign our nonce with our long-term identity public key
-		Signature signature = cryptoComponent.getSignature();
-		KeyParser sigParser = cryptoComponent.getSignatureKeyParser();
-		PrivateKey privKey = sigParser.parsePrivateKey(author.getPrivateKey());
-		signature.initSign(privKey);
-		signature.update(ourNonce);
-		byte[] sig = signature.sign();
+		byte[] sig = cryptoComponent
+				.sign(SIGNING_LABEL_RESPONSE, ourNonce, author.getPrivateKey());
 
 		// Calculate a MAC over identity public key, ephemeral public key,
 		// transport properties and timestamp.
@@ -479,16 +476,10 @@ class IntroduceeManager {
 			throws FormatException, GeneralSecurityException {
 		byte[] nonce = localState.getRaw(NONCE);
 		byte[] sig = localState.getRaw(SIGNATURE);
-		byte[] keyBytes = localState.getRaw(PUBLIC_KEY);
+		byte[] key = localState.getRaw(PUBLIC_KEY);
 
-		// Parse the public key
-		KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
-		PublicKey key = keyParser.parsePublicKey(keyBytes);
 		// Verify the signature
-		Signature signature = cryptoComponent.getSignature();
-		signature.initVerify(key);
-		signature.update(nonce);
-		if (!signature.verify(sig)) {
+		if (!cryptoComponent.verify(SIGNING_LABEL_RESPONSE, nonce, key, sig)) {
 			LOG.warning("Invalid nonce signature in ACK");
 			throw new GeneralSecurityException();
 		}
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
index aeb478001b91dc4fe04aaaacc3da460664082f33..71d6ebbeefad49bef35ae9b46d8305983a027f3d 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageFactoryImpl.java
@@ -51,8 +51,8 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
 			int type = JOIN.getInt();
 			BdfList toSign = BdfList.of(groupId, timestamp, type,
 					member.getName(), member.getPublicKey(), invite);
-			byte[] memberSignature =
-					clientHelper.sign(toSign, member.getPrivateKey());
+			byte[] memberSignature = clientHelper
+					.sign(SIGNING_LABEL_JOIN, toSign, member.getPrivateKey());
 
 			// Compose the message
 			BdfList body =
@@ -78,8 +78,8 @@ class GroupMessageFactoryImpl implements GroupMessageFactory {
 			BdfList toSign = BdfList.of(groupId, timestamp, type,
 					author.getName(), author.getPublicKey(), parentId,
 					previousMsgId, content);
-			byte[] signature =
-					clientHelper.sign(toSign, author.getPrivateKey());
+			byte[] signature = clientHelper
+					.sign(SIGNING_LABEL_POST, toSign, author.getPrivateKey());
 
 			// Compose the message
 			BdfList body =
diff --git a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
index a80f1ce4b58db6e0cc03ff7a1f17bd3adeab9547..7e27d74685bdf55eb9fb23d27981b029d97796ad 100644
--- a/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/GroupMessageValidator.java
@@ -27,9 +27,12 @@ import java.util.Collection;
 import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
+import static org.briarproject.api.privategroup.GroupMessageFactory.SIGNING_LABEL_JOIN;
+import static org.briarproject.api.privategroup.GroupMessageFactory.SIGNING_LABEL_POST;
 import static org.briarproject.api.privategroup.MessageType.JOIN;
 import static org.briarproject.api.privategroup.MessageType.POST;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
+import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
 import static org.briarproject.privategroup.GroupConstants.KEY_INITIAL_JOIN_MSG;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_ID;
 import static org.briarproject.privategroup.GroupConstants.KEY_MEMBER_NAME;
@@ -130,7 +133,7 @@ class GroupMessageValidator extends BdfMessageValidator {
 					.createInviteToken(creator.getId(), member.getId(),
 							pg.getId(), inviteTimestamp);
 			try {
-				clientHelper.verifySignature(creatorSignature,
+				clientHelper.verifySignature(SIGNING_LABEL_INVITE, creatorSignature,
 						creator.getPublicKey(), token);
 			} catch (GeneralSecurityException e) {
 				throw new InvalidMessageException(e);
@@ -146,8 +149,8 @@ class GroupMessageValidator extends BdfMessageValidator {
 		BdfList signed = BdfList.of(g.getId(), m.getTimestamp(), JOIN.getInt(),
 				member.getName(), member.getPublicKey(), invite);
 		try {
-			clientHelper.verifySignature(memberSignature, member.getPublicKey(),
-					signed);
+			clientHelper.verifySignature(SIGNING_LABEL_JOIN, memberSignature,
+					member.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
@@ -189,8 +192,8 @@ class GroupMessageValidator extends BdfMessageValidator {
 				member.getName(), member.getPublicKey(), parentId,
 				previousMessageId, content);
 		try {
-			clientHelper
-					.verifySignature(signature, member.getPublicKey(), signed);
+			clientHelper.verifySignature(SIGNING_LABEL_POST, signature,
+					member.getPublicKey(), signed);
 		} catch (GeneralSecurityException e) {
 			throw new InvalidMessageException(e);
 		}
diff --git a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java
index 3e9b8c88c67aaab47b7d9ab8c56e491c4ae93109..502d4f0761aa8ecff8f9b83d7d64ec2a652b0ec0 100644
--- a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java
+++ b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationFactoryImpl.java
@@ -36,7 +36,7 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
 		BdfList token = createInviteToken(creatorId, memberId, privateGroupId,
 				timestamp);
 		try {
-			return clientHelper.sign(token, privateKey);
+			return clientHelper.sign(SIGNING_LABEL_INVITE, token, privateKey);
 		} catch (GeneralSecurityException e) {
 			throw new IllegalArgumentException(e);
 		} catch (FormatException e) {
@@ -50,7 +50,6 @@ class GroupInvitationFactoryImpl implements GroupInvitationFactory {
 		Group contactGroup = contactGroupFactory.createContactGroup(CLIENT_ID,
 				creatorId, memberId);
 		return BdfList.of(
-				0, // TODO: Replace with a namespaced string
 				timestamp,
 				contactGroup.getId(),
 				privateGroupId
diff --git a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java
index 15de431499b54f4c3abd969a6626ccf348d392cb..175a593c49903013cadde62cedfbb37433e37489 100644
--- a/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java
+++ b/briar-core/src/org/briarproject/privategroup/invitation/GroupInvitationValidator.java
@@ -31,6 +31,7 @@ import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH
 import static org.briarproject.api.privategroup.PrivateGroupConstants.GROUP_SALT_LENGTH;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
 import static org.briarproject.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
+import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
 import static org.briarproject.privategroup.invitation.MessageType.ABORT;
 import static org.briarproject.privategroup.invitation.MessageType.INVITE;
 import static org.briarproject.privategroup.invitation.MessageType.JOIN;
@@ -96,13 +97,13 @@ class GroupInvitationValidator extends BdfMessageValidator {
 				groupName, creator, salt);
 		// Verify the signature
 		BdfList signed = BdfList.of(
-				INVITE.getValue(),
 				m.getTimestamp(),
 				m.getGroupId(),
 				privateGroup.getId()
 		);
 		try {
-			clientHelper.verifySignature(signature, creatorPublicKey, signed);
+			clientHelper.verifySignature(SIGNING_LABEL_INVITE, signature,
+					creatorPublicKey, signed);
 		} catch (GeneralSecurityException e) {
 			throw new FormatException();
 		}
diff --git a/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java b/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
index 08943fc9f163aff322f79df52869069711be0b4c..306b082a6bf55333d6345140a161f0fa76c6a438 100644
--- a/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
+++ b/briar-tests/src/org/briarproject/blogs/BlogPostValidatorTest.java
@@ -38,6 +38,8 @@ import static org.briarproject.api.blogs.BlogConstants.KEY_ORIGINAL_PARENT_MSG_I
 import static org.briarproject.api.blogs.BlogConstants.KEY_PARENT_MSG_ID;
 import static org.briarproject.api.blogs.BlogConstants.KEY_PUBLIC_KEY;
 import static org.briarproject.api.blogs.BlogConstants.KEY_READ;
+import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_COMMENT;
+import static org.briarproject.api.blogs.BlogPostFactory.SIGNING_LABEL_POST;
 import static org.briarproject.api.blogs.MessageType.COMMENT;
 import static org.briarproject.api.blogs.MessageType.POST;
 import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
@@ -101,7 +103,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 
 		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), body);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_POST, signed, sigBytes);
 		final BdfDictionary result =
 				validator.validateMessage(message, group, m).getDictionary();
 
@@ -143,7 +145,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), comment,
 						pOriginalId, currentId);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
 		final BdfDictionary result =
 				validator.validateMessage(message, group, m).getDictionary();
 
@@ -170,7 +172,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), null,
 						originalId, currentId);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
 		final BdfDictionary result =
 				validator.validateMessage(message, group, m).getDictionary();
 
@@ -189,7 +191,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 
 		BdfList signed =
 				BdfList.of(blog.getId(), message.getTimestamp(), body);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_POST, signed, sigBytes);
 
 		final BdfList originalList = BdfList.of(POST.getInt(), body, sigBytes);
 		final byte[] originalBody = TestUtils.getRandomBytes(42);
@@ -228,7 +230,7 @@ public class BlogPostValidatorTest extends BriarTestCase {
 
 		BdfList signed = BdfList.of(blog.getId(), message.getTimestamp(),
 				comment, originalId, oldId);
-		expectCrypto(signed, sigBytes);
+		expectCrypto(SIGNING_LABEL_COMMENT, signed, sigBytes);
 
 		final BdfList originalList = BdfList.of(COMMENT.getInt(), comment,
 				originalId, oldId, sigBytes);
@@ -256,13 +258,13 @@ public class BlogPostValidatorTest extends BriarTestCase {
 		context.assertIsSatisfied();
 	}
 
-	private void expectCrypto(final BdfList signed, final byte[] sig)
-			throws IOException, GeneralSecurityException {
+	private void expectCrypto(final String label, final BdfList signed,
+			final byte[] sig) throws IOException, GeneralSecurityException {
 		context.checking(new Expectations() {{
 			oneOf(blogFactory).parseBlog(group);
 			will(returnValue(blog));
 			oneOf(clientHelper)
-					.verifySignature(sig, author.getPublicKey(), signed);
+					.verifySignature(label, sig, author.getPublicKey(), signed);
 		}});
 	}
 
diff --git a/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
index 73e17616a1f842d4cd64d30e290bc2e8df957a09..ccea806ff2037885b5580e02eb341da66816fb71 100644
--- a/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
+++ b/briar-tests/src/org/briarproject/clients/ClientHelperImplTest.java
@@ -1,13 +1,10 @@
 package org.briarproject.clients;
 
 import org.briarproject.BriarTestCase;
+import org.briarproject.TestUtils;
 import org.briarproject.api.FormatException;
 import org.briarproject.api.clients.ClientHelper;
 import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.crypto.KeyParser;
-import org.briarproject.api.crypto.PrivateKey;
-import org.briarproject.api.crypto.PublicKey;
-import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfEntry;
 import org.briarproject.api.data.BdfList;
@@ -57,8 +54,6 @@ public class ClientHelperImplTest extends BriarTestCase {
 			context.mock(MetadataEncoder.class);
 	private final CryptoComponent cryptoComponent =
 			context.mock(CryptoComponent.class);
-	private final KeyParser keyParser = context.mock(KeyParser.class);
-	private final Signature signature = context.mock(Signature.class);
 	private final ClientHelper clientHelper;
 
 	private final GroupId groupId = new GroupId(getRandomId());
@@ -70,6 +65,7 @@ public class ClientHelperImplTest extends BriarTestCase {
 			new Message(messageId, groupId, timestamp, rawMessage);
 	private final Metadata metadata = new Metadata();
 	private final BdfList list = BdfList.of("Sign this!", getRandomBytes(42));
+	private final String label = TestUtils.getRandomString(5);
 
 	public ClientHelperImplTest() {
 		clientHelper =
@@ -284,72 +280,46 @@ public class ClientHelperImplTest extends BriarTestCase {
 
 	@Test
 	public void testSign() throws Exception {
-		final byte[] privateKeyBytes = getRandomBytes(42);
-		final PrivateKey privateKey = context.mock(PrivateKey.class);
+		final byte[] privateKey = getRandomBytes(42);
 		final byte[] signed = getRandomBytes(42);
 
 		final byte[] bytes = expectToByteArray(list);
 		context.checking(new Expectations() {{
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
-			oneOf(cryptoComponent).getSignatureKeyParser();
-			will(returnValue(keyParser));
-			oneOf(keyParser).parsePrivateKey(privateKeyBytes);
-			will(returnValue(privateKey));
-			oneOf(signature).initSign(privateKey);
-			oneOf(signature).update(bytes);
-			oneOf(signature).sign();
+			oneOf(cryptoComponent).sign(label, bytes, privateKey);
 			will(returnValue(signed));
 		}});
 
-		assertArrayEquals(signed, clientHelper.sign(list, privateKeyBytes));
+		assertArrayEquals(signed, clientHelper.sign(label, list, privateKey));
 		context.assertIsSatisfied();
 	}
 
 	@Test
 	public void testVerifySignature() throws Exception {
-		final PublicKey publicKey = context.mock(PublicKey.class);
-		final byte[] publicKeyBytes = getRandomBytes(42);
-
+		final byte[] publicKey = getRandomBytes(42);
 		final byte[] bytes = expectToByteArray(list);
+
 		context.checking(new Expectations() {{
-			oneOf(cryptoComponent).getSignatureKeyParser();
-			will(returnValue(keyParser));
-			oneOf(keyParser).parsePublicKey(publicKeyBytes);
-			will(returnValue(publicKey));
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
-			oneOf(signature).initVerify(publicKey);
-			oneOf(signature).update(bytes);
-			oneOf(signature).verify(rawMessage);
+			oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
 			will(returnValue(true));
 		}});
 
-		clientHelper.verifySignature(rawMessage, publicKeyBytes, list);
+		clientHelper.verifySignature(label, rawMessage, publicKey, list);
 		context.assertIsSatisfied();
 	}
 
 	@Test
 	public void testVerifyWrongSignature() throws Exception {
-		final PublicKey publicKey = context.mock(PublicKey.class);
-		final byte[] publicKeyBytes = getRandomBytes(42);
-
+		final byte[] publicKey = getRandomBytes(42);
 		final byte[] bytes = expectToByteArray(list);
+
 		context.checking(new Expectations() {{
-			oneOf(cryptoComponent).getSignatureKeyParser();
-			will(returnValue(keyParser));
-			oneOf(keyParser).parsePublicKey(publicKeyBytes);
-			will(returnValue(publicKey));
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
-			oneOf(signature).initVerify(publicKey);
-			oneOf(signature).update(bytes);
-			oneOf(signature).verify(rawMessage);
+			oneOf(cryptoComponent).verify(label, bytes, publicKey, rawMessage);
 			will(returnValue(false));
 		}});
 
 		try {
-			clientHelper.verifySignature(rawMessage, publicKeyBytes, list);
+			clientHelper
+					.verifySignature(label, rawMessage, publicKey, list);
 			fail();
 		} catch (GeneralSecurityException e) {
 			// expected
diff --git a/briar-tests/src/org/briarproject/crypto/KeyEncodingAndParsingTest.java b/briar-tests/src/org/briarproject/crypto/KeyEncodingAndParsingTest.java
index d35da2762e08cc1f24e735a486d236162f2abc8b..b7abf54fb45abfe18a4d6648a2dd0e4301a3c551 100644
--- a/briar-tests/src/org/briarproject/crypto/KeyEncodingAndParsingTest.java
+++ b/briar-tests/src/org/briarproject/crypto/KeyEncodingAndParsingTest.java
@@ -7,7 +7,6 @@ import org.briarproject.api.crypto.KeyPair;
 import org.briarproject.api.crypto.KeyParser;
 import org.briarproject.api.crypto.PrivateKey;
 import org.briarproject.api.crypto.PublicKey;
-import org.briarproject.api.crypto.Signature;
 import org.junit.Test;
 
 import java.security.GeneralSecurityException;
@@ -102,15 +101,13 @@ public class KeyEncodingAndParsingTest extends BriarTestCase {
 
 	@Test
 	public void testSignatureLength() throws Exception {
-		Signature sig = crypto.getSignature();
 		// Generate 10 signature key pairs
 		for (int i = 0; i < 10; i++) {
 			KeyPair keyPair = crypto.generateSignatureKeyPair();
+			byte[] key = keyPair.getPrivate().getEncoded();
 			// Sign some random data and check the length of the signature
 			byte[] toBeSigned = TestUtils.getRandomBytes(1234);
-			sig.initSign(keyPair.getPrivate());
-			sig.update(toBeSigned);
-			byte[] signature = sig.sign();
+			byte[] signature = crypto.sign("label", toBeSigned, key);
 			assertTrue(signature.length <= MAX_SIGNATURE_LENGTH);
 		}
 	}
diff --git a/briar-tests/src/org/briarproject/crypto/MacTest.java b/briar-tests/src/org/briarproject/crypto/MacTest.java
index 336d90952ca4c5dd990a72dddcb67862b902e630..c63fa66c299d864df7d875d051dfbfbe6dc9956a 100644
--- a/briar-tests/src/org/briarproject/crypto/MacTest.java
+++ b/briar-tests/src/org/briarproject/crypto/MacTest.java
@@ -16,18 +16,17 @@ public class MacTest extends BriarTestCase {
 
 	private final CryptoComponent crypto;
 
+	private final SecretKey k = TestUtils.getSecretKey();
+	private final byte[] inputBytes = TestUtils.getRandomBytes(123);
+	private final byte[] inputBytes1 = TestUtils.getRandomBytes(234);
+	private final byte[] inputBytes2 = new byte[0];
+
 	public MacTest() {
 		crypto = new CryptoComponentImpl(new TestSeedProvider());
 	}
 
 	@Test
 	public void testIdenticalKeysAndInputsProduceIdenticalMacs() {
-		// Generate a random key and some random input
-		byte[] keyBytes = TestUtils.getRandomBytes(SecretKey.LENGTH);
-		SecretKey k = new SecretKey(keyBytes);
-		byte[] inputBytes = TestUtils.getRandomBytes(123);
-		byte[] inputBytes1 = TestUtils.getRandomBytes(234);
-		byte[] inputBytes2 = new byte[0];
 		// Calculate the MAC twice - the results should be identical
 		byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
 		byte[] mac1 = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
@@ -36,14 +35,8 @@ public class MacTest extends BriarTestCase {
 
 	@Test
 	public void testDifferentKeysProduceDifferentMacs() {
-		// Generate two random keys and some random input
-		byte[] keyBytes = TestUtils.getRandomBytes(SecretKey.LENGTH);
-		SecretKey k = new SecretKey(keyBytes);
-		byte[] keyBytes1 = TestUtils.getRandomBytes(SecretKey.LENGTH);
-		SecretKey k1 = new SecretKey(keyBytes1);
-		byte[] inputBytes = TestUtils.getRandomBytes(123);
-		byte[] inputBytes1 = TestUtils.getRandomBytes(234);
-		byte[] inputBytes2 = new byte[0];
+		// Generate second random key
+		SecretKey k1 = TestUtils.getSecretKey();
 		// Calculate the MAC with each key - the results should be different
 		byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
 		byte[] mac1 = crypto.mac(k1, inputBytes, inputBytes1, inputBytes2);
@@ -52,16 +45,11 @@ public class MacTest extends BriarTestCase {
 
 	@Test
 	public void testDifferentInputsProduceDifferentMacs() {
-		// Generate a random key and some random input
-		byte[] keyBytes = TestUtils.getRandomBytes(SecretKey.LENGTH);
-		SecretKey k = new SecretKey(keyBytes);
-		byte[] inputBytes = TestUtils.getRandomBytes(123);
-		byte[] inputBytes1 = TestUtils.getRandomBytes(234);
-		byte[] inputBytes2 = new byte[0];
 		// Calculate the MAC with the inputs in different orders - the results
 		// should be different
 		byte[] mac = crypto.mac(k, inputBytes, inputBytes1, inputBytes2);
 		byte[] mac1 = crypto.mac(k, inputBytes2, inputBytes1, inputBytes);
 		assertFalse(Arrays.equals(mac, mac1));
 	}
+
 }
diff --git a/briar-tests/src/org/briarproject/crypto/SignatureTest.java b/briar-tests/src/org/briarproject/crypto/SignatureTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..36d718540867bfe7eeec612db75605eef8c81861
--- /dev/null
+++ b/briar-tests/src/org/briarproject/crypto/SignatureTest.java
@@ -0,0 +1,109 @@
+package org.briarproject.crypto;
+
+import org.briarproject.BriarTestCase;
+import org.briarproject.TestSeedProvider;
+import org.briarproject.TestUtils;
+import org.briarproject.api.crypto.CryptoComponent;
+import org.briarproject.api.crypto.KeyPair;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class SignatureTest extends BriarTestCase {
+
+	private final CryptoComponent crypto;
+
+	private final byte[] publicKey, privateKey;
+	private final String label = TestUtils.getRandomString(42);
+	private final byte[] inputBytes = TestUtils.getRandomBytes(123);
+
+	public SignatureTest() {
+		crypto = new CryptoComponentImpl(new TestSeedProvider());
+		KeyPair k = crypto.generateSignatureKeyPair();
+		publicKey = k.getPublic().getEncoded();
+		privateKey = k.getPrivate().getEncoded();
+	}
+
+	@Test
+	public void testIdenticalKeysAndInputsProduceIdenticalSignatures()
+			throws Exception {
+		// Calculate the Signature twice - the results should be identical
+		byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
+		byte[] sig2 = crypto.sign(label, inputBytes, privateKey);
+		assertArrayEquals(sig1, sig2);
+	}
+
+	@Test
+	public void testDifferentKeysProduceDifferentSignatures() throws Exception {
+		// Generate second private key
+		KeyPair k2 = crypto.generateSignatureKeyPair();
+		byte[] privateKey2 = k2.getPrivate().getEncoded();
+		// Calculate the signature with each key
+		byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
+		byte[] sig2 = crypto.sign(label, inputBytes, privateKey2);
+		assertFalse(Arrays.equals(sig1, sig2));
+	}
+
+	@Test
+	public void testDifferentInputsProduceDifferentSignatures()
+			throws Exception {
+		// Generate a second input
+		byte[] inputBytes2 = TestUtils.getRandomBytes(123);
+		// Calculate the signature with different inputs
+		// the results should be different
+		byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
+		byte[] sig2 = crypto.sign(label, inputBytes2, privateKey);
+		assertFalse(Arrays.equals(sig1, sig2));
+	}
+
+	@Test
+	public void testDifferentLabelsProduceDifferentSignatures()
+			throws Exception {
+		// Generate a second label
+		String label2 = TestUtils.getRandomString(42);
+		// Calculate the signature with different inputs
+		// the results should be different
+		byte[] sig1 = crypto.sign(label, inputBytes, privateKey);
+		byte[] sig2 = crypto.sign(label2, inputBytes, privateKey);
+		assertFalse(Arrays.equals(sig1, sig2));
+	}
+
+	@Test
+	public void testSignatureVerification() throws Exception {
+		byte[] sig = crypto.sign(label, inputBytes, privateKey);
+		assertTrue(crypto.verify(label, inputBytes, publicKey, sig));
+	}
+
+	@Test
+	public void testDifferentKeyFailsVerification() throws Exception {
+		// Generate second private key
+		KeyPair k2 = crypto.generateSignatureKeyPair();
+		byte[] privateKey2 = k2.getPrivate().getEncoded();
+		// calculate the signature with different key, should fail to verify
+		byte[] sig = crypto.sign(label, inputBytes, privateKey2);
+		assertFalse(crypto.verify(label, inputBytes, publicKey, sig));
+	}
+
+	@Test
+	public void testDifferentInputFailsVerification() throws Exception {
+		// Generate a second input
+		byte[] inputBytes2 = TestUtils.getRandomBytes(123);
+		// calculate the signature with different input, should fail to verify
+		byte[] sig = crypto.sign(label, inputBytes, privateKey);
+		assertFalse(crypto.verify(label, inputBytes2, publicKey, sig));
+	}
+
+	@Test
+	public void testDifferentLabelFailsVerification() throws Exception {
+		// Generate a second label
+		String label2 = TestUtils.getRandomString(42);
+		// calculate the signature with different label, should fail to verify
+		byte[] sig = crypto.sign(label, inputBytes, privateKey);
+		assertFalse(crypto.verify(label2, inputBytes, publicKey, sig));
+	}
+
+}
diff --git a/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java b/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java
index 17e5c1266dca74739f381392f5deb9815fa34dfb..af048f6f1a7e68d14d7fb9d07f7fb489bd4179fd 100644
--- a/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java
+++ b/briar-tests/src/org/briarproject/forum/ForumPostValidatorTest.java
@@ -19,6 +19,7 @@ import java.security.GeneralSecurityException;
 import java.util.Collection;
 
 import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
+import static org.briarproject.api.forum.ForumPostFactory.SIGNING_LABEL_POST;
 import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
 import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
@@ -70,8 +71,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithoutParent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithoutParent);
 		}});
 
 		ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -179,8 +180,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(shortAuthorName, authorPublicKey);
 			will(returnValue(shortNameAuthor));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithShortNameAuthor);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithShortNameAuthor);
 		}});
 
 		ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -267,8 +268,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithShortContent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithShortContent);
 		}});
 
 		ForumPostValidator v = new ForumPostValidator(authorFactory,
@@ -342,8 +343,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithParent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithParent);
 			will(throwException(new FormatException()));
 		}});
 
@@ -359,8 +360,8 @@ public class ForumPostValidatorTest extends ValidatorTestCase {
 		context.checking(new Expectations() {{
 			oneOf(authorFactory).createAuthor(authorName, authorPublicKey);
 			will(returnValue(author));
-			oneOf(clientHelper).verifySignature(signature, authorPublicKey,
-					signedWithParent);
+			oneOf(clientHelper).verifySignature(SIGNING_LABEL_POST, signature,
+					authorPublicKey, signedWithParent);
 			will(throwException(new GeneralSecurityException()));
 		}});
 
diff --git a/briar-tests/src/org/briarproject/introduction/IntroduceeManagerTest.java b/briar-tests/src/org/briarproject/introduction/IntroduceeManagerTest.java
index 2ac86ea65f61ba6fc18d0404f953d7706416808f..c33b308c665e0f5e9156f5277eb893607d15b300 100644
--- a/briar-tests/src/org/briarproject/introduction/IntroduceeManagerTest.java
+++ b/briar-tests/src/org/briarproject/introduction/IntroduceeManagerTest.java
@@ -10,10 +10,7 @@ import org.briarproject.api.contact.Contact;
 import org.briarproject.api.contact.ContactId;
 import org.briarproject.api.contact.ContactManager;
 import org.briarproject.api.crypto.CryptoComponent;
-import org.briarproject.api.crypto.KeyParser;
-import org.briarproject.api.crypto.PublicKey;
 import org.briarproject.api.crypto.SecretKey;
-import org.briarproject.api.crypto.Signature;
 import org.briarproject.api.data.BdfDictionary;
 import org.briarproject.api.data.BdfEntry;
 import org.briarproject.api.data.BdfList;
@@ -77,6 +74,7 @@ import static org.briarproject.api.introduction.IntroductionConstants.TYPE_ACK;
 import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
 import static org.briarproject.api.introduction.IntroductionConstants.TYPE_RESPONSE;
 import static org.briarproject.api.sync.SyncConstants.MESSAGE_HEADER_LENGTH;
+import static org.briarproject.introduction.IntroduceeManager.SIGNING_LABEL_RESPONSE;
 import static org.hamcrest.Matchers.array;
 import static org.hamcrest.Matchers.samePropertyValuesAs;
 import static org.junit.Assert.assertFalse;
@@ -91,11 +89,8 @@ public class IntroduceeManagerTest extends BriarTestCase {
 	private final CryptoComponent cryptoComponent;
 	private final ClientHelper clientHelper;
 	private final IntroductionGroupFactory introductionGroupFactory;
-	private final MessageSender messageSender;
-	private final TransportPropertyManager transportPropertyManager;
 	private final AuthorFactory authorFactory;
 	private final ContactManager contactManager;
-	private final IdentityManager identityManager;
 	private final Clock clock;
 	private final Contact introducer;
 	private final Contact introducee1;
@@ -105,24 +100,24 @@ public class IntroduceeManagerTest extends BriarTestCase {
 	private final Transaction txn;
 	private final long time = 42L;
 	private final Message localStateMessage;
-	private final ClientId clientId;
 	private final SessionId sessionId;
 	private final Message message1;
 
 	public IntroduceeManagerTest() {
 		context = new Mockery();
 		context.setImposteriser(ClassImposteriser.INSTANCE);
-		messageSender = context.mock(MessageSender.class);
+		MessageSender messageSender = context.mock(MessageSender.class);
 		db = context.mock(DatabaseComponent.class);
 		cryptoComponent = context.mock(CryptoComponent.class);
 		clientHelper = context.mock(ClientHelper.class);
 		clock = context.mock(Clock.class);
 		introductionGroupFactory =
 				context.mock(IntroductionGroupFactory.class);
-		transportPropertyManager = context.mock(TransportPropertyManager.class);
+		TransportPropertyManager transportPropertyManager =
+				context.mock(TransportPropertyManager.class);
 		authorFactory = context.mock(AuthorFactory.class);
 		contactManager = context.mock(ContactManager.class);
-		identityManager = context.mock(IdentityManager.class);
+		IdentityManager identityManager = context.mock(IdentityManager.class);
 
 		introduceeManager = new IntroduceeManager(messageSender, db,
 				clientHelper, clock, cryptoComponent, transportPropertyManager,
@@ -152,7 +147,7 @@ public class IntroduceeManagerTest extends BriarTestCase {
 		introducee2 =
 				new Contact(contactId2, author2, localAuthorId, true, true);
 
-		clientId = IntroductionManagerImpl.CLIENT_ID;
+		ClientId clientId = IntroductionManagerImpl.CLIENT_ID;
 		localGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
 				clientId, new byte[0]);
 		introductionGroup1 = new Group(new GroupId(TestUtils.getRandomId()),
@@ -270,20 +265,9 @@ public class IntroduceeManagerTest extends BriarTestCase {
 				new BdfEntry(SIGNATURE, sig)
 		);
 
-		final KeyParser keyParser = context.mock(KeyParser.class);
-		final PublicKey publicKey = context.mock(PublicKey.class);
-		final Signature signature = context.mock(Signature.class);
 		context.checking(new Expectations() {{
-			oneOf(cryptoComponent).getSignatureKeyParser();
-			will(returnValue(keyParser));
-			oneOf(keyParser)
-					.parsePublicKey(introducee2.getAuthor().getPublicKey());
-			will(returnValue(publicKey));
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
-			oneOf(signature).initVerify(publicKey);
-			oneOf(signature).update(nonce);
-			oneOf(signature).verify(sig);
+			oneOf(cryptoComponent).verify(SIGNING_LABEL_RESPONSE, nonce,
+					introducee2.getAuthor().getPublicKey(), sig);
 			will(returnValue(false));
 		}});
 
@@ -311,19 +295,9 @@ public class IntroduceeManagerTest extends BriarTestCase {
 		state.put(NONCE, nonce);
 		state.put(SIGNATURE, sig);
 
-		final KeyParser keyParser = context.mock(KeyParser.class);
-		final Signature signature = context.mock(Signature.class);
-		final PublicKey publicKey = context.mock(PublicKey.class);
 		context.checking(new Expectations() {{
-			oneOf(cryptoComponent).getSignatureKeyParser();
-			will(returnValue(keyParser));
-			oneOf(keyParser).parsePublicKey(publicKeyBytes);
-			will(returnValue(publicKey));
-			oneOf(cryptoComponent).getSignature();
-			will(returnValue(signature));
-			oneOf(signature).initVerify(publicKey);
-			oneOf(signature).update(nonce);
-			oneOf(signature).verify(sig);
+			oneOf(cryptoComponent).verify(SIGNING_LABEL_RESPONSE, nonce,
+					publicKeyBytes, sig);
 			will(returnValue(true));
 		}});
 		introduceeManager.verifySignature(state);