Mixing asymmetric and symmetric encryption with HMAC hash verification .NET

Introduction

In this post we looked at how to combine symmetric and asymmetric encryption in the same project to increase messaging security. Symmetric encrypt is quick but has the problem of distributing the public key. Asymmetric encryption solves the public key distribution issue but is in turn a lot slower. We can combine the two techniques where a one-time symmetric public key, also called a session key, is encrypted with the asymmetric public key so that it can be decrypted by the receiver who has access to the asymmetric private key.

The solution is OK so far, it is very difficult to find the right keys involved. Still we can do better and add some message verification. We want to be sure that the message hasn’t been tampered with on its way to us. The sender can hash the encrypted message and send it along with the other necessary properties to the receiver. The receiver can compute the hash on their side and verify whether the two are equal. We looked at a couple of hashing techniques in this post but those do not involve any cryptographic key. That means that an attacker can change the message, recalculate the hash and attach it to the message. The password-salted hash algorithm HMAC sounds like a better option. We looked at HMACs in .NET in this post and we’ll reuse what we learnt there.

The goal of this post is to build upon the mixed encryption demo solution we built previously and add HMAC hashing to the picture.

HMACs

Let’s start with a a quick recap and see what HMACs or HashMacs are.

A hash or message digest helps us verify whether the message has been tampered with by a third party after it was sent to us. Hashing algorithms that don’t involve any secret key, such as MD5 and the SHA family have a drawback when used in messaging. An attacker can intercept the message, alter it, recalculate the hash and attach it to the message. The receiver can’t be sure that the message hasn’t been tampered with during the transition.

We can however take a step further and add an extra layer of security onto our hashes. After all a message and its hash could originate from anyone. How can we verify the authenticity of the message as well? That’s where Hashed Message Authentication Codes, also called HashMACs or HMACs enter the picture.

Suppose that you get the following message with its hash: “Let’s meet at 7pm in front of the cinema, John”. You examine the hash and come to the conclusion that the message hasn’t been modified. Then you get another message: “Let’s meet at 6pm instead.” Hmmm, OK, that’s unusual, John normally doesn’t change his mind so quickly, did the message really come from him?

HMACs are also hashes but they are given an extra round of calculation using a random hash key. If both parties have access to this key then the message can be verified and authenticated using the agreed key and hash algorithm. If the receiver calculates a HashMac which is different from the one sent along with the message then authentication fails.

In the extended demo we’ll use the one-time symmetric public key that the sender generates to calculate the hash of the message. Recall that the symmetric key is encrypted. The receiver will decrypt the symmetric key using their asymmetric private key and then calculate the HMAC of the message. If the recalculated hash matches the hash sent by the Sender then the message should be fine.

Make sure you understand how HMACs are applied in .NET. The post referenced above gives you a good starting point.

Demo

For the demo we’ll build upon the test application we started building in this and this post. We’re at a stage where a Sender sends a message to the Receiver and the communication is based on symmetric and asymmetric encryption for increased security. We’ll add HashMac to the mix for message verification.

We’ll first extend the EncryptedMessage class with a property for the HMAC:

public class EncryptedMessage
{
	public EncryptedMessage(string symmetricKeyEncryptedBase64, string initializationVectorBase64
		, string cipherTextBase64, Guid asymmetricKeyId, string secretMessageHmacBase64)
	{
		SymmetricKeyEncryptedBase64 = symmetricKeyEncryptedBase64;
		InitializationVectorBase64 = initializationVectorBase64;
		CipherTextBase64 = cipherTextBase64;
		AsymmetricKeyId = asymmetricKeyId;
		SecretMessageHmacBase64 = secretMessageHmacBase64;
	}

	public string SymmetricKeyEncryptedBase64 { get; }
	public string InitializationVectorBase64 { get; }		
	public string CipherTextBase64 { get; }
	public Guid AsymmetricKeyId { get; }
	public string SecretMessageHmacBase64 { get; }
}

SecretMessageSender has a Start method to start the communication process. We’re adding a new piece of code and apply the HMACSHA256 class with the one-time symmetric key. We compute the hash of the cipher text and attach its base 64 version to the EncryptedMessage object as follows:

public void Start()
{
	int defaultSymmetricKeySize = 256;
	AsymmetricPublicKey oneTimeAsymmetricPublicKey = _secretMessageReceiver.GenerateOneTimeAsymmetricPublicKey();
	SymmetricEncryptionResult symmetricEncryptionOfSecretMessage = 
		_symmetricEncryptionService.Encrypt(_extremelyConfidentialMessage, defaultSymmetricKeySize);
	string symmetricKeyBase64 = Convert.ToBase64String(symmetricEncryptionOfSecretMessage.SymmetricKey);
	string ivBase64 = Convert.ToBase64String(symmetricEncryptionOfSecretMessage.IV);

	HMACSHA256 hmac = new HMACSHA256(symmetricEncryptionOfSecretMessage.SymmetricKey);
	byte[] hmacHash = hmac.ComputeHash(symmetricEncryptionOfSecretMessage.Cipher);
	string hmacHashBase64 = Convert.ToBase64String(hmacHash);

	AsymmetricEncryptionResult asymmetricallyEncryptedSymmetricKeyResult =
		_xmlBasedAsymmetricEncryptionService.EncryptWithPublicKeyXml(symmetricKeyBase64, oneTimeAsymmetricPublicKey.PublicKeyXml.ToString());
			
	EncryptedMessage encryptedMessage = new EncryptedMessage(asymmetricallyEncryptedSymmetricKeyResult.EncryptedAsBase64
		, ivBase64, symmetricEncryptionOfSecretMessage.CipherBase64, oneTimeAsymmetricPublicKey.PublicKeyId, hmacHashBase64);
			
	_secretMessageReceiver.ProcessIncomingMessage(encryptedMessage);
}

Lastly we extend the ProcessIncomingMessage method of SecretMessageReceiver. The receiver will decrypt the symmetric key and apply it to the HMACSHA256 object. They will then calculate the hash of the cipher text and compare it to the hash supplied by the Sender. So in fact both the Sender and the Receiver apply the same hashing technique. The SequenceEqual LINQ extension provides a comparison function for enumerables such as a byte array. If the two hashes do not match then we throw an exception, otherwise continue the code execution.

Here’s the extended ProcessIncomingMessage method:

public void ProcessIncomingMessage(EncryptedMessage encryptedMessage)
{
	if (_successfulKeyPairResults.ContainsKey(encryptedMessage.AsymmetricKeyId))
	{
		byte[] encryptedSymmetricKey = Convert.FromBase64String(encryptedMessage.SymmetricKeyEncryptedBase64);
		AsymmetricDecryptionResult decryptSymmetricKey = _xmlBasedAsymmetricEncryptionService.DecryptWithFullKeyXml
			(encryptedSymmetricKey,
			_successfulKeyPairResults[encryptedMessage.AsymmetricKeyId].PublicPrivateKeyPairXml);				

		if (decryptSymmetricKey.Success)
		{
			string symmetricKeyBase64 = decryptSymmetricKey.DecryptedMessage;
			byte[] cipherText = Convert.FromBase64String(encryptedMessage.CipherTextBase64);

			byte[] hmacInMessage = Convert.FromBase64String(encryptedMessage.SecretMessageHmacBase64);
			HMACSHA256 hmacSha256 = new HMACSHA256(Convert.FromBase64String(decryptSymmetricKey.DecryptedMessage));
			byte[] hmacToCheckAgainst = hmacSha256.ComputeHash(cipherText);
			bool hmacsEqual = hmacInMessage.SequenceEqual(hmacToCheckAgainst);
			if (!hmacsEqual)
			{
				throw new CryptographicException("Message hash mismatch!!!!!");
			}

			byte[] iv = Convert.FromBase64String(encryptedMessage.InitializationVectorBase64);
			byte[] symmetricKey = Convert.FromBase64String(symmetricKeyBase64);
			string secretMessage = _symmetricEncryptionService.Decrypt(cipherText
				, symmetricKey, iv);
			Console.WriteLine($"Secret message receiver got the following message: {secretMessage}");
			_successfulKeyPairResults.Remove(encryptedMessage.AsymmetricKeyId);
		}
		else
		{
			throw new CryptographicException(decryptSymmetricKey.ExceptionMessage);
		}
	}
	else
	{
		throw new ArgumentException("No such key id found");
	}
}

You can start the demo like before and it should execute without any exception.

You can view the list of posts on Security and Cryptography here.

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

2 Responses to Mixing asymmetric and symmetric encryption with HMAC hash verification .NET

  1. asetbelajar says:

    i’am Sorry sir, here I want to ask about what is the difference of the type of exchange, direct, fanout and topic.?

    On Tue, Nov 15, 2016 at 1:15 PM, Exercises in .NET with Andras Nemes wrote:

    > Andras Nemes posted: “Introduction In this post we looked at how to > combine symmetric and asymmetric encryption in the same project to increase > messaging security. Symmetric encrypt is quick but has the problem of > distributing the public key. Asymmetric encryption solves th” >

Leave a comment

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.