Java Symmetric Encryption/Decryption With Message Authentication Code

This post demonstrates encrypting/decrypting messages by using symmetric algorithms such as AES. It also demonstrates computing MAC(Message Authentication Code) and encoding/decoding messages with Base64.

Encryption Step Highlights

  • Generate 128 random bit using SecureRandom
  • Encrypt plain text using AES algorithm with a predefined symmetric key and prepend the random bits
  • Calculate the MAC(Message Authentication Code) and append it to the cipher
  • Encode the whole cipher in Base64 for transmission purpose(i.e. via email or web)

Dencryption Step Highlights

  • Decode the received Base64 message back to binary
  • Extract out the random padding, encrypted text and MAC
  • Validate the MAC to ensure data intercity
  • Decrypt the cipher using AES algorithm with the same predefined symmetric key

Note: You may need to install unlimited strength JCE to run this program. The default JCE bundled in Oracle JRE can only support upto 128bit key size.

Please refer to the complete example code below

import java.util.Arrays;

import javax.crypto.Cipher;
import javax.crypto.Mac;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;

import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

public class JavaCipherTest {
  private static final SecureRandom secureRandom = new SecureRandom();
  private static final int RANDOM_BYTES_LENGTH = 16;
  private static final String MAC_ALGORITHM = "HMACSHA256";
  private static final String STRING_ENCODING = "ISO_8859_1";
  private static final String HEX_AES_KEY =
  private static final String HEX_MAC_KEY = "AEB908AA1CEDFFDEA1F255640A05EEF6";

  public static void main(String[] args) {
    String plainText = "seceret message, fatal if compromised";
    System.out.println("Original text: " + plainText);

    JavaCipherTest tester = new JavaCipherTest();
    String encryptedText;
    try {
      encryptedText = tester.encrypt(plainText, HEX_AES_KEY, HEX_MAC_KEY, MAC_ALGORITHM);
      System.out.println("Encrypted text: " + encryptedText);
      String decryptedText = tester.decrypt(encryptedText, HEX_AES_KEY, HEX_MAC_KEY, MAC_ALGORITHM);
      System.out.println("Decrypted text: " + decryptedText);
    } catch (Exception e) {

  public String encrypt(String plainText, String hexAesKey, String hexMacKey, String macAlgorithm)
      throws Exception {
    // compute the cipher
    byte[] decodedHexAesKey = DatatypeConverter.parseHexBinary(hexAesKey);
    SecretKeySpec secretKeySpec = new SecretKeySpec(decodedHexAesKey, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    byte[] paddingBytes = new byte[RANDOM_BYTES_LENGTH];
    cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, new IvParameterSpec(paddingBytes));
    byte[] encryptedText = cipher.doFinal(plainText.getBytes(STRING_ENCODING));

    // Prepend random to the encryptedText
    byte[] paddedCipher = concatByteArrays(paddingBytes, encryptedText);

    // Append message digest
    byte[] digest = computeDigest(paddedCipher, hexMacKey, macAlgorithm);
    byte[] completeText = concatByteArrays(paddedCipher, digest);

    BASE64Encoder base64Encoder = new BASE64Encoder();
    return base64Encoder.encode(completeText);

  public String decrypt(String encryptedText, String hexAesKey, String hexMacKey,
      String macAlgorithm) throws Exception {
    BASE64Decoder base64decoder = new BASE64Decoder();
    int macLength = Mac.getInstance(macAlgorithm).getMacLength();
    byte[] completeBytes = base64decoder.decodeBuffer(encryptedText);
    int macStartIndex = completeBytes.length - macLength;
    byte[] padding = Arrays.copyOfRange(completeBytes, 0, RANDOM_BYTES_LENGTH);
    byte[] paddedCipher = Arrays.copyOfRange(completeBytes, 0, macStartIndex);
    byte[] encryptedBytes = Arrays.copyOfRange(completeBytes, RANDOM_BYTES_LENGTH, macStartIndex);
    byte[] digestBytes = Arrays.copyOfRange(completeBytes, macStartIndex, completeBytes.length);

    byte[] computedDigest = computeDigest(paddedCipher, hexMacKey, macAlgorithm);
    if (!MessageDigest.isEqual(digestBytes, computedDigest)) {
      throw new RuntimeException("Message corrupted");

    byte[] decodedHexAesKey = DatatypeConverter.parseHexBinary(hexAesKey);
    SecretKeySpec keySpec = new SecretKeySpec(decodedHexAesKey, "AES");
    Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
    cipher.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(padding));

    String plaintext = new String(cipher.doFinal(encryptedBytes), STRING_ENCODING);
    return plaintext;

  private byte[] concatByteArrays(byte[] array1, byte[] array2) {
    byte[] result = new byte[array1.length + array2.length];
    System.arraycopy(array1, 0, result, 0, array1.length);
    System.arraycopy(array2, 0, result, array1.length, array2.length);
    return result;

  private byte[] computeDigest(byte[] message, String hexMacKey, String algorithm)
      throws NoSuchAlgorithmException, InvalidKeyException {
    byte[] decodedHexMacKey = DatatypeConverter.parseHexBinary(hexMacKey);
    SecretKeySpec secretKeySpc = new SecretKeySpec(decodedHexMacKey, algorithm);
    Mac mac = Mac.getInstance(algorithm);
    byte[] digest = mac.doFinal(message);
    return digest;

This entry was posted in Java, Programming and tagged , , , , , , , , . Bookmark the permalink.

One Response to Java Symmetric Encryption/Decryption With Message Authentication Code

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 )

Twitter picture

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

Facebook photo

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

Google+ photo

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

Connecting to %s