How HMAC Works, Step-by-Step Explanation with Examples

Oleksandr Volkov
9 min readDec 17, 2023

HMAC (Hash Massage Authentication Code) is an approach for creating digital signatures using different hash algorithms like MD5, SHA1, SHA256, SHA512, etc…

Why do we need HMAC?

Short answer — for digital signatures (DA) which give us data Integrity and authentication. This is a unified approach for applying a digital signature with a private key. Let’s take a quick look at how hashing-based DA works (This part is optional and you can skip it).

Let’s consider a scenario where Alice wants to securely send a message to Bob, ensuring that the message remains unchanged during transmission. Simultaneously, Bob needs to verify that the message indeed originated from Alice. To achieve this, Alice decides to enhance security by incorporating a secret key into the hashed message. For a more in-depth understanding of this process, refer to the details on how Digital Authentication (DA) works.

In practice, Alice combines the secret key with the hashed message (secret key + hashed message) and sends the resulting value along with the original message. However, a challenge arises when Bob receives the message and aims to verify its authenticity and integrity. To do this, Bob replicates Alice’s operation by taking the hashed message, adding the secret key (hashed message + secret key) (as you see Bob uses a different order!), and then comparing it with the value sent by Alice. If these values match, it confirms that the message indeed originated from Alice and remains unchanged. In this situation, even if the message hasn’t been altered by an attacker, Bob ends up with a different outcome. This occurs because Bob doesn’t follow the same order as Alice (“HelloBob” is not equal “BobHello”).

Here HMAC comes to us — it defined a specific algorithm to combine message hash and secret key. Alice and Bob use the same algorithm and always get the same result.

HMAC uses a payload and secret key (one private key). If you are interested in the option of a private and public key, look at another algorithm RSASSA.

Overview

I will consider the creation of HMAC using an example of SHA1 because it is quite short. First of all, we need to define the block size of our hashing algorithm, for SHA1 it is 512 bits (64 bytes). I’m going to show it in HEX, not binary because it takes up less space and is easier to understand, but there’s no problem converting HEX to binary.

A quick reminder of what HEX is: computers understand only 0 and 1, so to be able to write numbers or letters, we combine several 0 and 1 into a bit (8 characters — 01101010). Bits may be combined into bytes (1 byte is 8 bits). HEX is a 16-byte form of writing bytes, which can be in the range from 0 to FF (0 to 255 for the decimal system), here is an ASCII to HEX table, you can check if I have correctly converted the text to HEX encoding, or you can use an online calculator. The hexadecimal representation can be recognized by the prefix “x” before the number, for example, x4B, x6579. In other words, HEX is a convenient form for representing bytes.

A step-by-step diagram of HMAC:

A step-by-step diagram of HMAC
Taken from here and adapted according to the need.

Our key will be short, but in reality, the standard recommends using a key length equal to or greater than the length of the algorithm’s bits:

A key of the same size as the hash output (for instance, 256 bits for "HS256") or larger MUST be used with this algorithm.

For example, 256-bits equals 32 ASCII characters.

Calculation

Here, we will step-by-step explain the operation of HMAC according to the above scheme. Remember, all spaces added only for readability
We will consider three cases: when the key length < block size, when the key length = block size, and when the key length > block size.

Terms:
K0 — key wich length = block size.
ipad — Inner pad; The constant which equals x36 repeated as many times as block size in bytes (for SHA1 64 bytes).
opad — Outer pad; The constant which equals x5C repeated as many times as block size in bytes (for SHA1 64 bytes).
H — A hashing function that takes a string of any size as an argument and returns a hashed string of a fixed size.
^ — symbol of XOR operation.
|| — append/concatination. Concatenation of two strings.

Spaces in HEX notation are added only for readability.

Example: 1 key length < block size

Input data:
Message: “Hello” → x48656C6C 6F
Secret Key: “Key” → x4B6579

Constants for our case hash algorithm (SHA1) with block size 64 bytes:
ipad (in HEX): x36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 (x36 repeated 64 time)
opad (in HEX): x5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C (x5C repeated 64 time)

Steps of creating HMAC (you could use the calculator to verify the result):

  1. If the key length < block size, we need to add 0s until the key length equals the block size. In our case, the key length is 3 bytes, and the block size is 64 bytes. So new key is K0 = 4B657900 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  2. We need to perform the operation K0 XOR ipad
    How to make XOR operation you can see here or use the calculator.
    K0 XOR ipad = 7D534F36 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636
  3. Now, we append the message to K0 XOR ipad
    (K0 XOR ipad) || message = 7D534F36 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 48656C6C 6F
  4. We put the result of the previous operation into the SHA-1 hash.
    Please note that you need to specify to the calculator that your data is in HEX (use this calculator). Also, you should remove all spaces (remember spaces are added here for readability but should not be included when hashing). In other words, you should input into the hash function:
    7D534F3636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363636363648656C6C6F
    sha1(K0 XOR ipad || message) = 8e32567d57353a91515458d65b4cc7802450172c
  5. We need to perform the operation K0 XOR opad.
    K0 XOR opad = 1739255C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C
  6. Append to the result of the previous operation the wich hash obtained in step 4.
    (K0 XOR opad) || (sha1(K0 XOR ipad || message)) =
    1739255C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 8E32567D 57353A91 515458D6 5B4CC780 2450172C
  7. Put the result of step 6 into the SHA-1 hash.
    HMAC = 173ac40fb6ac57cc7524594c523bea1bdd54836a

Example 2: key length = block size

Input data:
Message: “Hello” → x48656C6C 6F
Secret Key: “4q72JHgX89z3BkFMt6cwQxL1rD28jpN5UfVhIZYPbCSeuGovRaWmA0sD9ECtX7Jf” → x34713732 4A486758 38397A33 426B464D 74366377 51784C31 72443238 6A704E35 55665668 495A5950 62435365 75476F76 5261576D 41307344 39454374 58374A66

Constants for our case hash algorithm (SHA1) with block size 64 bytes:
ipad (in HEX): x36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 (x36 repeated 64 time)
opad (in HEX): x5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C (x5C repeated 64 time)

Steps of creating HMAC (you could use the calculator to verify the result):

  1. If the key length = block size, then we do nothing. In our case, with a key length of 64 bytes, we proceed with no additional action.
    So new key is K0 = 34713732 4A486758 38397A33 426B464D 74366377 51784C31 72443238 6A704E35 55665668 495A5950 62435365 75476F76 5261576D 41307344 39454374 58374A66
  2. We need to perform the operation K0 XOR ipad.
    How to make XOR operation you can see here or use the calculator.
    K0 XOR ipad = 02470104 7C7E516E 0E0F4C05 745D707B 42005541 674E7A07 4472040E 5C467803 6350605E 7F6C6F66 54756553 43715940 6457615B 77064572 0F737542 6E017C50
  3. Now, append the message (K0 XOR ipad || message) to K0 XOR ipad.
    (K0 XOR ipad) || message = 02470104 7C7E516E 0E0F4C05 745D707B 42005541 674E7A07 4472040E 5C467803 6350605E 7F6C6F66 54756553 43715940 6457615B 77064572 0F737542 6E017C50 48656C6C 6F
  4. We put the result of the previous operation into the SHA-1 hash.
    Please note that you need to specify to the calculator that your data is in HEX (use this calculator). Also, you should remove all spaces (remember spaces are added here for readability but should not be included when hashing). In other words, you should input into the hash function: 024701047C7E516E0E0F4C05745D707B42005541674E7A074472040E5C4678036350605E7F6C6F6654756553437159406457615B770645720F7375426E017C5048656C6C6F
    sha1(K0 XOR ipad || message) =
    5fa962fde86832f2d5935b8be0229987e510f99f
  5. Perform the operation K0 XOR opad.
    K0 XOR opad = 682D6B6E 16143B04 6465266F 1E371A11 286A3F2B 0D24106D 2E186E64 362C1269 093A0A34 1506050C 3E1F0F39 291B332A 0E3D0B31 1D6C2F18 65191F28 046B163A
  6. Append to the result of the previous operation the wich hash obtained in step 4.
    (K0 XOR opad) || (sha1(K0 XOR ipad || message)) = 682D6B6E 16143B04 6465266F 1E371A11 286A3F2B 0D24106D 2E186E64 362C1269 093A0A34 1506050C 3E1F0F39 291B332A 0E3D0B31 1D6C2F18 65191F28 046B163A 5FA962FD E86832F2 D5935B8B E0229987 E510F99F
  7. Put the result of step 6 into the SHA-1 hash.
    HMAC = 62e1eaf2a7075bceb8e0022ae7d3e3d6f7271609

Example 3: key length > block size

Input data:
Message: “Hello” → x48656C6C 6F
Secret Key: “Y0S5INaG35isu0FJNlEPQeC5V9VCb5jPQ6cVBVVTKRov0Un7Wv6kDsVzfTdx5djqg9bQakXf3vxf5IU1sOnjZoUzKu” → x59305335 494E6147 33356973 7530464A 4E6C4550 51654335 56395643 62356A50 51366356 42565654 4B526F76 30556E37 5776366B 4473567A 66546478 35646A71 67396251 616B5866 33767866 35495531 734F6E6A 5A6F557A 4B75

Constants for our case hash algorithm (SHA1) with block size 64 bytes:
ipad (in HEX): x36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 (x36 repeated 64 time)
opad (in HEX): x5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C (x5C repeated 64 time)

Steps of creating HMAC (you could use the calculator to verify the result):

  1. If the key length > block size, we first hash the key, and then add 0s until its length becomes 64 bytes. In our case, with a key length of 90 bytes, we perform SHA1(Key). SHA1(Key) = 20e82c4d0379ee74fbe125e3b5be8b3f634a06e7.
    Currently, the length of SHA1(Key) is 20 bytes, so we add 0s until the length becomes 64 bytes.
    So K0 = 20E82C4D 0379EE74 FBE125E3 B5BE8B3F 634A06E7 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
  2. We need to perform the operation K0 XOR ipad.
    How to make XOR operation you can see here or use the calculator.
    K0 XOR ipad =
    16DE1A7B 354FD842 CDD713D5 8388BD09 557C30D1 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636
  3. Now, append the message (K0 XOR ipad || message) to K0 XOR ipad.
    (K0 XOR ipad) || message = 16DE1A7B 354FD842 CDD713D5 8388BD09 557C30D1 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 36363636 48656C6C 6F
  4. We put the result of the previous operation into the SHA-1 hash.
    Please note that you need to specify to the calculator that your data is in HEX (use this calculator). Also, you should remove all spaces (remember spaces are added here for readability but should not be included when hashing). In other words, you should input into the hash function:
    16de1a7b354fd842cdd713d58388bd09557c30d1363636363636363636363636363636363636363636363636363636363636363636363636363636363636363648656c6c6f
    sha1(K0 XOR ipad || message) = a79535b0b1fd1d5aff741fc0e1bbd2c627076c60
  5. Perform the operation K0 XOR opad.
    K0 XOR opad = 7CB47011 5F25B228 A7BD79BF E9E2D763 3F165ABB 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C
  6. Append to the result of the previous operation the wich hash obtained in step 4.
    (K0 XOR opad) || (sha1(K0 XOR ipad || message)) =7CB47011 5F25B228 A7BD79BF E9E2D763 3F165ABB 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C 5C5C5C5C A79535B0 B1FD1D5A FF741FC0 E1BBD2C6 27076C60
  7. Put the result of step 6 into the SHA-1 hash.
    HMAC = 45fac385c1a6c3404593b8943c3d1da70da0594b

Conclusion

As we can see, there is no magic, only hash and XOR operations. However, if you want to reproduce the calculations on your own, be careful when hashing to ensure that the input type is in HEX if you are entering HEX specifically.

Input type for hash algorithm is HEX

Useful links

  1. XOR operation — https://xor.pw/#
  2. Make hash — https://emn178.github.io/online-tools/sha1.html
  3. Convert ASCII to HEX — https://www.rapidtables.com/convert/number/ascii-to-hex.html
  4. HMAC calculator — https://asecuritysite.com/mac/hmac

Stackademic

Thank you for reading until the end. Before you go:

  • Please consider clapping and following the writer! 👏
  • Follow us on Twitter(X), LinkedIn, and YouTube.
  • Visit Stackademic.com to find out more about how we are democratizing free programming education around the world.

--

--