Keyboard Shortcuts N Next post
P Previous post
S Save / unsave
R Read aloud
T Toggle theme
/ Focus search
Esc Close panels
🔥
Ready to read...
Cryptography Cryptography Tutorial Java Java and Cryptography Mastery Series — Extended Module 13 - Hash MAC and Checksum

Java Program to Demonstrate Difference Between Salt and IV with Examples

Reviewed & accurate
AI Summary

Introduction

The salt vs iv java tutorial clarifies two random-looking parameters that often confuse cryptography learners. Salt is used in password hashing and key derivation to defeat rainbow tables and precomputation attacks. The initialization vector (IV) is used in symmetric encryption to ensure that the same plaintext encrypts to a different ciphertext every time. Both look like random bytes prepended or appended to the secret, but they serve different cryptographic purposes, have different security requirements, and are used in different stages of the cryptographic pipeline.

For a Java learner the salt vs iv java program below is a chance to see both parameters in action in the same code. We use the JDK's PBKDF2WithHmacSHA256 key derivation to demonstrate salt and javax.crypto AES-GCM encryption to demonstrate IV. The tutorial walks through the conceptual distinction, the security properties, and the practical decision rule for choosing salt versus IV.

What is the Difference Between Salt and IV?

Salt is a random value mixed into a password before hashing. The purpose of salt is to ensure that two users with the same password produce different password hashes. Without salt, an attacker who precomputes a rainbow table of common passwords and their hashes can crack every user's password with a single lookup. With salt, the attacker must recompute the rainbow table for each unique salt value, which defeats the precomputation attack.

The initialization vector (IV) is a random value mixed into an encryption to ensure that the same plaintext encrypts to a different ciphertext. The purpose of IV is to make encryption non-deterministic: an attacker who sees two identical ciphertexts cannot infer that the underlying plaintexts are identical. IV is essential for semantically secure encryption; without it, an attacker can correlate repeated plaintexts across messages.

The two concepts differ in three key ways. First, salt is used in password hashing and key derivation; IV is used in symmetric encryption. Second, salt must be unique per password (typically random, but a counter is also acceptable); IV must be unique per encryption (random or counter) and must never repeat for the same key with some encryption modes (AES-GCM). Third, salt can be public; it is typically stored alongside the password hash. IV is also public; it is typically sent alongside the ciphertext.

How Salt and IV Work Step by Step

The two concepts work in different stages of the cryptographic pipeline.

Step 1: Salt in password hashing. When a user creates a password "hunter2", generate a random 16-byte salt. Compute the password hash as PBKDF2(password, salt, 10000 iterations, SHA-256). Store the salt and the hash together in the user database. When the user logs in, retrieve the salt, recompute the hash with the input password, and compare. Because each user has a different salt, two users with the same password produce different hashes.

Step 2: IV in encryption. When a sender wants to encrypt a plaintext "Hello, world!", generate a random 12-byte IV. Compute the ciphertext as AES-GCM-Encrypt(key, IV, plaintext). Send the IV and the ciphertext together. The receiver uses the IV and key to decrypt. Because each encryption uses a different IV, the same plaintext encrypts to a different ciphertext every time.

Step 3: Salt must be unique per password. The salt's job is to make precomputation impossible. If two users have the same salt, an attacker who cracks one user's password can crack the other user's password with one additional hash lookup. Salt must be unique, but it does not need to be secret.

Step 4: IV must be unique per encryption for the same key. For AES-GCM, IV reuse is catastrophic: reusing an IV with the same key leaks the XOR of two plaintexts and exposes the authentication key. For AES-CBC, IV reuse is less catastrophic but still leaks information about the underlying plaintext.

Step 5: Salt is typically 16 bytes (128 bits); IV for AES-GCM is typically 12 bytes (96 bits) per the NIST recommendation; IV for AES-CBC is 16 bytes (128 bits, equal to the block size).

Java Program to Demonstrate Salt and IV


import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.crypto.spec.IvParameterSpec;
import java.security.SecureRandom;
import java.nio.charset.StandardCharsets;

public class SaltVsIv {

    private static final SecureRandom RNG = new SecureRandom();

    // Generate a random salt of N bytes
    public static byte[] generateSalt(int bytes) {
        byte[] salt = new byte[bytes];
        RNG.nextBytes(salt);
        return salt;
    }

    // Generate a random IV of N bytes
    public static byte[] generateIv(int bytes) {
        byte[] iv = new byte[bytes];
        RNG.nextBytes(iv);
        return iv;
    }

    // Derive a key from a password and salt using PBKDF2
    public static byte[] deriveKey(String password, byte[] salt, int iterations) throws Exception {
        PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt, iterations, 256);
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        return factory.generateSecret(spec).getEncoded();
    }

    // Encrypt with AES-GCM using an IV
    public static byte[] encryptGcm(byte[] key, byte[] iv, byte[] plaintext) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(key, "AES"),
                    new GCMParameterSpec(128, iv));
        return cipher.doFinal(plaintext);
    }

    // Decrypt with AES-GCM using an IV
    public static byte[] decryptGcm(byte[] key, byte[] iv, byte[] ciphertext) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"),
                    new GCMParameterSpec(128, iv));
        return cipher.doFinal(ciphertext);
    }

    public static String toHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b & 0xff));
        }
        return sb.toString();
    }

    public static void main(String[] args) throws Exception {
        String password = "hunter2";

        // Salt: unique per password, defeats rainbow tables
        byte[] salt1 = generateSalt(16);
        byte[] salt2 = generateSalt(16);
        byte[] key1 = deriveKey(password, salt1, 10000);
        byte[] key2 = deriveKey(password, salt2, 10000);

        System.out.println("Password: " + password);
        System.out.println("Salt 1: " + toHex(salt1));
        System.out.println("Derived Key 1: " + toHex(key1));
        System.out.println("Salt 2: " + toHex(salt2));
        System.out.println("Derived Key 2: " + toHex(key2));
        System.out.println("(Same password, different salts -> different derived keys.)");
        System.out.println();

        // IV: unique per encryption, ensures semantic security
        byte[] iv1 = generateIv(12);
        byte[] iv2 = generateIv(12);
        byte[] plaintext = "Hello, AES-GCM!".getBytes(StandardCharsets.UTF_8);
        byte[] ct1 = encryptGcm(key1, iv1, plaintext);
        byte[] ct2 = encryptGcm(key1, iv2, plaintext);

        System.out.println("Plaintext: " + new String(plaintext));
        System.out.println("IV 1: " + toHex(iv1));
        System.out.println("Ciphertext 1: " + toHex(ct1));
        System.out.println("IV 2: " + toHex(iv2));
        System.out.println("Ciphertext 2: " + toHex(ct2));
        System.out.println("(Same plaintext, same key, different IVs -> different ciphertexts.)");
    }
}

Code Explanation Line by Line

The SecureRandom instance is the JDK's cryptographically secure random number generator. It uses the operating system's entropy source (/dev/urandom on Linux, BCryptGenRandom on Windows) to produce unpredictable bytes. Always use SecureRandom for cryptographic salt and IV generation; never use java.util.Random, which is predictable and easy to attack.

The generateSalt and generateIV methods are structurally identical: they generate N random bytes using SecureRandom. The difference is in how the bytes are used. Salt is 16 bytes (the standard PBKDF2 recommendation); IV for AES-GCM is 12 bytes (the NIST-recommended size for AES-GCM, which gives a 96-bit IV that allows 2 to the power 32 encryptions before IV collision becomes a concern).

The deriveKey method uses PBKDF2WithHmacSHA256 to derive a key from a password and salt. The PBEKeySpec constructor takes the password as a char array, the salt as a byte array, the iteration count, and the derived key length in bits. The iteration count (10000 here, but 600000 is the modern recommendation for production) controls how slow the key derivation is, which is the key stretching defense against brute force.

The encryptGcm and decryptGcm methods use the JDK's AES-GCM cipher. The GCMParameterSpec constructor takes the authentication tag length in bits (128 is the maximum and recommended) and the IV as a byte array. The IV must be unique per encryption for the same key; reusing the IV with AES-GCM is a catastrophic security failure.

The main method demonstrates the difference between salt and IV. The salt section shows that two different salts produce two different derived keys from the same password. The IV section shows that two different IVs produce two different ciphertexts from the same plaintext encrypted with the same key. Both salts and IVs are public; they can be stored or transmitted alongside the corresponding secret material.

Sample Input and Output

Password: "hunter2"
Salt 1: 32-character hex string (random)
Derived Key 1: 64-character hex string (different from key 2)
Salt 2: 32-character hex string (different from salt 1)
Derived Key 2: 64-character hex string (different from key 1)
(Same password, different salts -> different derived keys.)

Plaintext: "Hello, AES-GCM!"
IV 1: 24-character hex string (random)
Ciphertext 1: variable-length hex string (ciphertext + 16-byte tag)
IV 2: 24-character hex string (different from IV 1)
Ciphertext 2: variable-length hex string (different from ciphertext 1)
(Same plaintext, same key, different IVs -> different ciphertexts.)

The output shows two demonstrations. The salt demonstration proves that two different salts produce two different derived keys from the same password; an attacker who precomputes a rainbow table for one salt cannot reuse it for the other salt. The IV demonstration proves that two different IVs produce two different ciphertexts from the same plaintext encrypted with the same key; an attacker who sees two identical ciphertexts cannot infer that the underlying plaintexts are identical.

Common Mistakes to Avoid

First, confusing salt with IV is the most common conceptual mistake. Salt is for password hashing; IV is for encryption. The two serve different purposes and are not interchangeable. A password hash with an IV instead of a salt would still be vulnerable to rainbow tables; an encryption with a salt instead of an IV would still leak plaintext correlation.

Second, reusing an IV for AES-GCM with the same key is a catastrophic security failure. IV reuse leaks the XOR of two plaintexts and exposes the authentication key, allowing the attacker to forge tags for arbitrary messages. Always generate a fresh random IV for each AES-GCM encryption.

Third, using a salt shorter than 16 bytes (128 bits) is a security mistake. A short salt has a high probability of collision across users, which means an attacker who cracks one user's password can crack the other user's password with one additional hash lookup. The NIST recommendation is at least 128 bits of salt.

Fourth, storing the salt or IV next to the password hash or ciphertext is correct practice, not a security bug. Salt and IV are both public parameters; their job is to be unique, not secret. Trying to hide them is a waste of effort.

Real-World Applications

Salt is universally used in password storage. The /etc/shadow file on Linux stores salt alongside the bcrypt or SHA-512 hash of each user's password. The Django web framework uses PBKDF2 with a per-user random salt. The Spring SecurityPasswordEncoder interface requires a salt parameter. The bcrypt library automatically generates a 16-byte salt per hash and embeds it in the output string.

IV is universally used in symmetric encryption. Every TLS 1.3 record has a fresh IV derived from the handshake master secret and the record sequence number. Every SSH packet has a fresh IV (or, for AEAD ciphers like ChaCha20-Poly1305, a fresh nonce derived from the packet sequence number). Every encrypted cookie has an IV embedded in the cookie value. Every encrypted database column has an IV stored alongside the ciphertext.

In the Java ecosystem, PBKDF2WithHmacSHA256 is the standard library's built-in salt-based key derivation. AES/GCM/NoPadding is the standard library's built-in IV-based authenticated encryption. The Spring SecurityPasswordEncoder family wraps PBKDF2, bcrypt, and scrypt for password storage. The Apache Tink library by Google provides higher-level wrappers around both salt and IV usage.

The JWT (JSON Web Token) standard uses IV (or the equivalent nonce parameter) for the AES-GCM and ChaCha20-Poly1305 encryption modes. The JOSE library for Java wraps the standard library with a higher-level API that manages IVs and salts automatically. The Tink library by Google also wraps the standard library and enforces correct IV and salt handling.

In password storage, the modern recommendation is to use Argon2id (the 2015 Password Hashing Competition winner) instead of PBKDF2. Argon2id takes both a salt and a memory parameter; the memory parameter is what makes it resistant to GPU-based attacks. Spring Security 5.6+ supports Argon2id via Bouncy Castle. The Apache Tink library also includes Argon2id as a recommended password hash.

Conclusion

Salt and IV are both random-looking parameters that make cryptographic operations non-deterministic, but they serve different purposes. Salt randomizes password hashing to defeat rainbow tables; IV randomizes encryption to defeat plaintext correlation. We used the JDK's PBKDF2 and AES-GCM classes to demonstrate both parameters in the same code, walking through the conceptual distinction, the security requirements, and the practical decision rule. The program is self-contained and runnable on the standard JDK. Next you should study pepper for the additional layer of secret-based password protection, then explore key stretching to see how a slow hash makes brute force password attacks more expensive, and finally study the message digest vs MAC comparison to see how these concepts connect to keyed hashes.

Test Your Knowledge
How did you find this?

Comments

Join the discussion! Sign in with your Google or Blogger account, or comment as Anonymous - no account needed. For quick questions, also reach me on Telegram @cytestch.

Comments