C# · Course navigation

C#: message authentication with HMAC-SHA256

Compute a keyed tag so a receiver can detect tampering, and compare tags safely.

On this page
Before you start

C# bytes, encodings, and the System.Security.Cryptography namespace.

  • Compute a keyed tag so a receiver can detect tampering, and compare tags safely.
  • Predict the sample output and explain the state changes.
  • Complete the practice task and check the boundary cases.

The concept

A plain hash lets anyone recompute a digest after changing a message. An HMAC mixes a secret key into the hash, so only someone who knows the key can produce a valid tag. The receiver recomputes the tag with the shared key and compares it with the one received. If they match, the message is authentic and unmodified.

The example uses the HMAC-SHA256 test vector from RFC 4231 with key Jefe. CryptographicOperations.FixedTimeEquals compares two byte arrays in time that does not depend on where they first differ, which prevents timing attacks. An ordinary equality check can leak how many bytes matched.

Code example

C#
using System;
using System.Security.Cryptography;
using System.Text;

class Program {
  static void Main() {
    byte[] key = Encoding.UTF8.GetBytes("Jefe");
    byte[] message = Encoding.UTF8.GetBytes("what do ya want for nothing?");
    byte[] tampered = Encoding.UTF8.GetBytes("what do ya want for something?");

    using var hmac = new HMACSHA256(key);
    byte[] tag = hmac.ComputeHash(message);
    Console.WriteLine(Convert.ToHexString(tag).ToLowerInvariant());

    byte[] again = hmac.ComputeHash(message);
    Console.WriteLine("same message valid: " + CryptographicOperations.FixedTimeEquals(tag, again));
    byte[] forged = hmac.ComputeHash(tampered);
    Console.WriteLine("tampered message valid: " + CryptographicOperations.FixedTimeEquals(tag, forged));
  }
}
Output
5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
same message valid: True
tampered message valid: False

Follow the code

Open the walkthrough to inspect the example one step at a time.

Show guided walkthrough
Guided walkthroughReady
1using System;
2using System.Security.Cryptography;
3using System.Text;
4
5class Program {
6 static void Main() {
7 byte[] key = Encoding.UTF8.GetBytes("Jefe");
8 byte[] message = Encoding.UTF8.GetBytes("what do ya want for nothing?");
9 byte[] tampered = Encoding.UTF8.GetBytes("what do ya want for something?");
10
11 using var hmac = new HMACSHA256(key);
12 byte[] tag = hmac.ComputeHash(message);
13 Console.WriteLine(Convert.ToHexString(tag).ToLowerInvariant());
14
15 byte[] again = hmac.ComputeHash(message);
16 Console.WriteLine("same message valid: " + CryptographicOperations.FixedTimeEquals(tag, again));
17 byte[] forged = hmac.ComputeHash(tampered);
18 Console.WriteLine("tampered message valid: " + CryptographicOperations.FixedTimeEquals(tag, forged));
19 }
20}

Follow the example

Start to inspect the source, values, and output. These prepared steps explain the published sample; grouped loop steps describe multiple iterations.

View the complete dry-run table
Prepared trace of the published program. Some rows group repeated iterations.
Step / lineWhat happensState after stepOutput so far
Create an HMAC engine bound to the secret key.—
—
The tag depends on both the message and the key.tag = 32 bytes
—
This matches the published RFC 4231 test value.tag = 32 bytes
5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
The receiver recomputes the tag and compares in constant time.tag = 32 bytes
5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
same message valid: True
Changing the text changes the tag, so the forgery is detected.tag = 32 bytes
5bdcc146bf60754e6a042426089575c75a003f089d2739839dec58b964ec3843
same message valid: True
tampered message valid: False

Behind the scenes

How C# processes this code

C# compiles to code for the .NET runtime, which invokes the program entry point.

Cost and common mistakes

O(m) time for an m-byte message.

  • The key must be secret and long enough. A short, guessable key like Jefe is only acceptable in a published test vector.
  • An HMAC proves authenticity and integrity, not confidentiality: the message is still readable.
  • Include a nonce or timestamp in the signed data if replaying an old valid message would be harmful.

Try it yourself

Why not use SHA256(key + message) instead of HMAC?

Reveal answer and reasoning

Hashes built this way are vulnerable to length-extension attacks. HMAC is designed to avoid them.

Further reading