Skip to content

Symmetric Encryption Algorithms

Symmetric encryption algorithms use a single password for both encryption and decryption. For example, the popular compression tools WinZIP and WinRAR use symmetric encryption to secure and decrypt compressed files.

Encryption and Decryption Functions

From a programming perspective, encryption can be viewed as a function that receives a password and plaintext, then outputs ciphertext:

java
secret = encrypt(key, message);

Decryption works oppositely, taking a password and ciphertext to return plaintext:

java
plain = decrypt(key, secret);

Common Symmetric Encryption Algorithms

In software development, commonly used symmetric encryption algorithms include:

AlgorithmKey LengthModePadding Mode
DES56/64 bitsECB/CBC/PCBC/CTR/...NoPadding/PKCS5Padding/...
AES128/192/256 bitsECB/CBC/PCBC/CTR/...NoPadding/PKCS5Padding/PKCS7Padding/...
IDEA128 bitsECBPKCS5Padding/PKCS7Padding/...

Key length directly determines encryption strength, while mode and padding mode can be seen as parameters and format choices for symmetric encryption algorithms. The Java standard library does not implement every mode and padding scheme, but typically we only need to choose from common options.

Note: The DES algorithm is considered insecure today due to its short key length, which can be brute-forced in a short time.

Using AES for Encryption

The AES algorithm is currently the most widely used encryption algorithm. Let's start with encrypting and decrypting using the ECB mode:

java
import java.security.*;
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // Original text:
        String message = "Hello, world!";
        System.out.println("Message: " + message);
        // 128-bit key = 16 bytes Key:
        byte[] key = "1234567890abcdef".getBytes("UTF-8");
        // Encrypt:
        byte[] data = message.getBytes("UTF-8");
        byte[] encrypted = encrypt(key, data);
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
        // Decrypt:
        byte[] decrypted = decrypt(key, encrypted);
        System.out.println("Decrypted: " + new String(decrypted, "UTF-8"));
    }

    // Encrypt:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }

    // Decrypt:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        SecretKey keySpec = new SecretKeySpec(key, "AES");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(input);
    }
}

The symmetric encryption interface provided by the Java standard library is straightforward, and you can follow these steps to implement it:

  1. Obtain a Cipher instance based on the algorithm name/mode/padding mode.
  2. Initialize a SecretKey instance based on the algorithm name, ensuring the key meets the required length.
  3. Use the SecretKey to initialize the Cipher instance, setting the desired encryption or decryption mode.
  4. Provide plaintext or ciphertext to obtain ciphertext or plaintext.

Improving Security with CBC Mode

ECB mode is the simplest AES encryption mode, requiring only a fixed-length key. However, fixed plaintext will always yield fixed ciphertext, which compromises security. A better approach is to use CBC mode, which requires a random number as an IV (Initialization Vector) parameter. This way, the same plaintext will generate different ciphertext each time:

java
import java.security.*;
import java.util.Base64;
import javax.crypto.*;
import javax.crypto.spec.*;

public class Main {
    public static void main(String[] args) throws Exception {
        // Original text:
        String message = "Hello, world!";
        System.out.println("Message: " + message);
        // 256-bit key = 32 bytes Key:
        byte[] key = "1234567890abcdef1234567890abcdef".getBytes("UTF-8");
        // Encrypt:
        byte[] data = message.getBytes("UTF-8");
        byte[] encrypted = encrypt(key, data);
        System.out.println("Encrypted: " + Base64.getEncoder().encodeToString(encrypted));
        // Decrypt:
        byte[] decrypted = decrypt(key, encrypted);
        System.out.println("Decrypted: " + new String(decrypted, "UTF-8"));
    }

    // Encrypt:
    public static byte[] encrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        // CBC mode requires a 16-byte initialization vector:
        SecureRandom sr = SecureRandom.getInstanceStrong();
        byte[] iv = sr.generateSeed(16);
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivps);
        byte[] data = cipher.doFinal(input);
        // IV does not need to be secret; return both IV and ciphertext:
        return join(iv, data);
    }

    // Decrypt:
    public static byte[] decrypt(byte[] key, byte[] input) throws GeneralSecurityException {
        // Split input into IV and ciphertext:
        byte[] iv = new byte[16];
        byte[] data = new byte[input.length - 16];
        System.arraycopy(input, 0, iv, 0, 16);
        System.arraycopy(input, 16, data, 0, data.length);
        // Decrypt:
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
        IvParameterSpec ivps = new IvParameterSpec(iv);
        cipher.init(Cipher.DECRYPT_MODE, keySpec, ivps);
        return cipher.doFinal(data);
    }

    public static byte[] join(byte[] bs1, byte[] bs2) {
        byte[] r = new byte[bs1.length + bs2.length];
        System.arraycopy(bs1, 0, r, 0, bs1.length);
        System.arraycopy(bs2, 0, r, bs1.length, bs2.length);
        return r;
    }
}

In CBC mode, a randomly generated 16-byte IV parameter is required and must be created using SecureRandom. Since an IvParameterSpec instance is added, the initialization method needs to call a specific overloaded method of Cipher that takes an IvParameterSpec.

Summary

  • Symmetric encryption algorithms use the same key for both encryption and decryption, with common algorithms including DES, AES, and IDEA.
  • The key length is determined by algorithm design, with AES supporting key lengths of 128, 192, or 256 bits.
  • When using symmetric encryption algorithms, you must specify the algorithm name, mode, and padding mode.
Symmetric Encryption Algorithms has loaded