Retail MAC Calculation in Java

The following java code shows Cryptographic checksum calculated using ISO/IEC 9797-1 MAC algorithm 3 with block cipher DES, zero IV (8 bytes), and ISO/IEC 9797-1 padding method 2. No external library like Bouncy Castle, used.

public byte[] retailMac(byte[] key, byte[] data) {
 int loc = 0;
 byte[] edata;
 // Create Keys
 byte[] key1 = Arrays.copyOf(key, 8);
 byte[] key2 = Arrays.copyOfRange(key, 8, 16);

 // ISO/IEC 9797-1 or ISO 7816d4 Padding for data (adding 80 00 ..)
 byte[] pdata = pad-iso-7816d4(data, 8);

 try {
  SecretKey ka = new SecretKeySpec(key1, "DES");
  Cipher cipherA = Cipher.getInstance("DES/CBC/NoPadding");
  cipherA.init(Cipher.ENCRYPT_MODE, ka, new IvParameterSpec(new byte[8]));

  SecretKey kb = new SecretKeySpec(key2, "DES");
  Cipher cipherB = Cipher.getInstance("DES/CBC/NoPadding");
  cipherB.init(Cipher.DECRYPT_MODE, kb, new IvParameterSpec(new byte[8]));

  // Encrypt block by block with Key-A
  edata = cipherA.doFinal(pdata);

  byte[] x = new byte[8];
  for (loc = 0; loc < pdata.length; loc += 8) {
    System.arraycopy(pdata, loc, x, 0, 8);
    byte[] y = xor-array(edata, x);
    edata = cipherA.doFinal(y);

  // Decrypt the resulting block with Key-B
  edata = cipherB.doFinal(edata);
  // Encrypt the resulting block with Key-A
  edata = cipherA.doFinal(edata);
 } catch (Exception e) {
  return null;
 return edata;

This is not a perfect code, like repeated XOR part can be replaced by single doFinal.

Tagged with: ,
Posted in Uncategorized
6 comments on “Retail MAC Calculation in Java
  1. immaculatearrogance says:

    Hey thanks for sharing the code. Have a couple of doubts –
    1. What is SSC here? You have it in line 21. Should it be there?
    2. What is data? Is it same as the the info for which MAC has to be calculated?
    3. Can you share the algorithm for padit()

    • 1. It should be pdata (Not SSC).
      2. data – It is an argument
      3. Padding is simple. If byte array length is not 8, then pad 80,00, .. to make it exactly 8. If length is already 8, then also add extra pad 80, 00, 00, 00, 00, 00, 00, 00.

  2. Heri says:

    And please can you explain what xor-array should do?

  3. Heri says:

    I think your algo is not 100% correct. In order to obtain the same outcome as the BouncyCastle implementation I had to replace your lines 21-28 with following code:

    byte[] x = new byte[8];
    System.arraycopy(pData, loc, x, 0, 8);

    edata = cipherA.doFinal(x);

    for (loc = 8; loc < pData.length; loc += 8)
    System.arraycopy(pData, loc, x, 0, 8);
    byte[] y = xor_array(edata, x);
    edata = cipherA.doFinal(y);

    (note: first crypto operation is only done on the first block of the input data, the loop starts then with the second block).

    where xor_array is implemented as:

    private byte[] xor_array( byte[] aFirstArray, byte[] aSecondArray)
    byte[] result = new byte[aFirstArray.length];
    for ( int i = 0; i < result.length; i++ )
    result[i] = (byte) ( aFirstArray[i] ^ aSecondArray[i] );
    return result;

  4. Sergio says:


    Thank you very much for this code example.

    I am not exactly sure how to implement the pad-iso-7816d4(data, 8);

    May I ask you to share this code example as well?

    Thank you very much!

    • Need to search the code. But here is the logic with an example:

      dataLen: 44
      blk Size: 8
      padLen: (blkSize – (dataLen % blkSize)) = 4

      Create paddedData = new byte[dataLen + padLen]
      copy data to paddedData
      set paddedData[dataLen] = 0x80, remain 3 bytes will zero by default.

      If padLen is zero, no need for any padding.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: