skip to content

How do you initialize a Cipher for encryption with a raw AES key, and what is SecretKeySpec used for?

level: middleimportance: must knowfreq 65%

answer

  1. init(opmode, key, params) before any use
  2. ENCRYPT_MODE / DECRYPT_MODE
  3. SecretKeySpec wraps raw bytes into a SecretKey
  4. Byte length picks AES-128/192/256 (16/24/32)
  5. Re-init resets the Cipher for reuse

basics

~20 s

After getInstance you call cipher.init(Cipher.ENCRYPT_MODE, key, params). The key is a SecretKey; SecretKeySpec wraps raw bytes (like a 16- or 32-byte array) into a SecretKey for AES. For decryption you use Cipher.DECRYPT_MODE with the same key.

solid answer

~40 s

A Cipher is created stateless and must be initialized before use via init(opmode, key[, params]). The opmode is Cipher.ENCRYPT_MODE or DECRYPT_MODE (also WRAP/UNWRAP). The key implements SecretKey. SecretKeySpec is the adapter that turns raw key material — a byte[] from a KDF, key store, or random generator — into a SecretKey: new SecretKeySpec(keyBytes, "AES"). The byte length sets the AES key size: 16, 24, or 32 bytes for AES-128/192/256. Most modes also need parameters: an IvParameterSpec for CBC/CTR, or a GCMParameterSpec for GCM. You must use the identical key and IV/params to decrypt. init is also where you can reset and reuse a Cipher instance for a new message. A wrong key or wrong length triggers InvalidKeyException; bad params give InvalidAlgorithmParameterException.

code

java · 9 lines
java
byte[] raw = ...;                       // 32 bytes for AES-256, from a KDF/KMS
SecretKey key = new SecretKeySpec(raw, "AES");

Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
byte[] nonce = new byte[12];
SecureRandom.getInstanceStrong().nextBytes(nonce);
GCMParameterSpec spec = new GCMParameterSpec(128, nonce);

cipher.init(Cipher.ENCRYPT_MODE, key, spec);  // ready to encrypt

go deeper

for a junior

Can call init with ENCRYPT_MODE and build a SecretKeySpec from a byte array.

for a middle

Knows opmodes, that byte length picks the AES key size, and when to use SecretKeySpec vs KeyGenerator.

for a senior

Handles IV/GCM params, re-init for reuse, the key exceptions, and never hardcodes key material (KDF/KMS instead).

for a principal

Defines org-wide key provisioning (KMS, rotation, key derivation) and ensures SecretKeySpec is only fed properly managed key material.

## The lifecycle: getInstance then init `Cipher.getInstance(...)` gives you an **uninitialized** engine — it knows the algorithm but has no key. You must call **`init`** before encrypting or decrypting: ```java cipher.init(Cipher.ENCRYPT_MODE, key, params); ``` The first argument, **opmode**, says what the cipher will do: - `Cipher.ENCRYPT_MODE` — plaintext → ciphertext - `Cipher.DECRYPT_MODE` — ciphertext → plaintext - `Cipher.WRAP_MODE` / `UNWRAP_MODE` — encrypt/decrypt *another key* (key wrapping) ## What a SecretKey is, and why SecretKeySpec A **`SecretKey`** is Java's interface for a symmetric key object. You can obtain one two ways: - **Generate** a fresh random key with `KeyGenerator.getInstance("AES")` → `generateKey()`. - **Reconstruct** a key from raw bytes you already have (from a password-based KDF, a database, an env var, a key-management service). For that you use **`SecretKeySpec`**, a lightweight implementation of `SecretKey` that simply wraps a `byte[]`: ```java byte[] raw = ...; // 16, 24, or 32 bytes SecretKey key = new SecretKeySpec(raw, "AES"); ``` The second argument is the algorithm name the key is *for*. The **length of the byte array determines the AES key size**: 16 bytes = AES-128, 24 = AES-192, 32 = AES-256. A wrong length (say 20 bytes) throws **`InvalidKeyException`**. ## Parameters: IV and GCM tag length Most secure modes need extra parameters beyond the key: - **CBC / CTR** need an **IV** (initialization vector) — a per-message value passed as `new IvParameterSpec(ivBytes)` (16 bytes for AES-CBC). - **GCM** needs a `new GCMParameterSpec(tagBits, nonceBytes)` — the nonce/IV (12 bytes recommended) plus the authentication-tag length in bits (typically 128). The IV/nonce must be **unique per message** (random for CBC, never repeated for GCM under the same key) but is **not secret** — you store/transmit it alongside the ciphertext. To decrypt you must supply the **identical key and the same IV/params**. ## Re-init to reuse A `Cipher` is **stateful**: after a full encryption you can call `init` again to reset it for a new operation (new IV!), rather than calling `getInstance` again. It is **not thread-safe**. ## Errors to know - **`InvalidKeyException`** — wrong key length, or restricted key size without the (now-default-on) unlimited policy. - **`InvalidAlgorithmParameterException`** — missing/invalid IV or GCM spec for the chosen mode.

  • How is using SecretKeySpec different from KeyGenerator?
    KeyGenerator produces a fresh random SecretKey from a secure source. SecretKeySpec reconstructs a SecretKey from key bytes you already possess (from a KDF, vault, or storage). Use KeyGenerator to create keys, SecretKeySpec to load existing ones.
  • Does the IV need to be secret?
    No. The IV/nonce must be unique per message (and unpredictable for CBC), but it is not secret — you typically prepend it to the ciphertext. Only the key is secret.

saying these in an interview costs you the question

  • Hardcoding the raw key bytes in source code
  • Assuming SecretKeySpec generates or stretches a key — it only wraps the exact bytes you give it
  • Forgetting that key byte length must be exactly 16/24/32
  • Reusing the same Cipher instance concurrently across threads

context