Generating a MAC that uses SHA-1

The following example consists of a Sender and a Receiver.

- The Sender creates a message, generates a secret key, generates a mac given the message and the key and writes both the message and the mac to the file “message.ser”. Typically, this would be send to the other party.
A MAC works with a secret key. The weak point is that both the sender and the receiver must know the secret key. In the example, the secret key is stored in a keystore “keystore.kst” in the current directory.

- The Receiver reads “message.ser” and extracts the message and the MAC. To find out whether the message was tampered with, it calculates the MAC again using the message and the secret key that it reads out from “keystore.kst”.

Note that to use the keystore for secret keys, the keystore algorithm “JCEKS” must be used. “JCEKS” is implemented by the security provider that comes with JCE. You can download it here.

I added jce1_2_2.jar and sunjce_provider to my classpath. The code is documented.

Sender.java:

import javax.crypto.spec.*;
import javax.crypto.Mac;
import java.security.*;
import javax.crypto.*;
import sun.misc.*;
import java.io.*;
 
public class Sender
{
   public static void main(String []args) throws Exception {
      // message to send 
      String message = "the sun is green and the grass shines";
 
      // generate a random key and store it in keystore.kst in current directory 
      SecretKey key = generateAndStoreKey();
 
      // generate a MAC for the message, given the secret key
      byte[] mac = generateMAC(message, key);
      
      System.out.println("HMAC-MD5 for message '" + message + "':");
      System.out.println("t" + new BASE64Encoder().encode(mac));
      System.out.println("Writing message+mac to file 'message.ser'");
      
      // writing both the message and the mac to message.ser
      ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("message.ser"));
      oos.writeObject(message);
      oos.writeObject(mac);
      oos.close();
   }
 
   public static SecretKey generateAndStoreKey() throws Exception {
      KeyStore ks = KeyStore.getInstance("JCEKS");
      // initiaze new KeyStore, must do!
      ks.load(null, null);
 
      // generate random key
      SecureRandom sr = new SecureRandom();
      byte[] b = new byte[20];
      sr.nextBytes(b);
      SecretKey key = new SecretKeySpec(b, "HmacSHA1");
 
      // save secret key in our new keystore with alias MySecretKey
      FileOutputStream fos = new FileOutputStream("keystore.kst");
      ks.setKeyEntry("MySecretKey", key, "123456".toCharArray(), null);
      ks.store(fos, "123456".toCharArray());
      fos.close();
 
      return key;
   }
 
   public static byte[] generateMAC(String message, SecretKey key) throws Exception {
      // generate message digest based on key
      Mac mac = Mac.getInstance("HmacMD5");
      mac.init(key);
 
      return mac.doFinal(message.getBytes());
   }
}

Receiver.java:

import javax.crypto.spec.*;
import javax.crypto.Mac;
import java.security.*;
import javax.crypto.*;
import sun.misc.*;
import java.io.*;
 
public class Receiver
{
   public static void main(String []args) throws Exception {
      // read in message and mac from message.ser
      ObjectInputStream ois = new ObjectInputStream(new FileInputStream("message.ser"));
      String message = (String) ois.readObject();
      byte[] mac = (byte[]) ois.readObject();
 
      System.out.println("Original message: " + message);
 
      // get secret key from keystore keystore.kst
      SecretKey key = getSecretKey();
 
      // generate mac again given this key
      byte[] newMac = getMAC(message, key);
 
      // ensure the new mac is equal to the one that was "sent" to us
      if (MessageDigest.isEqual(mac, newMac)) {
         System.out.println("Message was not tampered with!");
      }
      else {
         System.out.println("Message has been tampered with!");
      }
   }
 
   public static byte[] getMAC(String message, SecretKey key) throws Exception {
      // generate message digest based on key
      Mac mac = Mac.getInstance("HmacMD5");
      mac.init(key);
  
      return mac.doFinal(message.getBytes());
   }

   public static SecretKey getSecretKey() throws Exception {
      FileInputStream fis = new FileInputStream("keystore.kst");
      KeyStore ks = KeyStore.getInstance("JCEKS");
      ks.load(fis, "123456".toCharArray());
 
      SecretKey key = (SecretKey) ks.getKey("MySecretKey", "123456".toCharArray());
  
      return key;
   }
}

sample run:

C:mybouncycastlemacsha1>java Sender
HMAC-MD5 for message 'the sun is green and the grass shines':
        XmvNl18rRj9ql4JkS3N8vw==
Writing message+mac to file 'message.ser'
 
C:mybouncycastlemacsha1>java Receiver
Original message: the sun is green and the grass shines
Message was not tampered with!