Encryption Basics in Java
Encryption turns readable data, called plaintext, into unreadable data, called ciphertext, using a key. Only someone with the right key can turn it back, which is decryption. Unlike hashing, encryption is two-way. There are two families. Symmetric encryption uses one secret key for both directions. Asymmetric encryption uses a pair of keys: a public key that anyone may have, and a private key that is kept secret. In Java, both are done with the class javax.crypto.Cipher. In this lesson, you will learn the difference between the two families, encrypt with AES and RSA, and see how real systems combine them.
Symmetric vs Asymmetric
| Point | Symmetric | Asymmetric |
|---|---|---|
| Keys | One shared secret key | A public key and a private key |
| Encrypt with | The secret key | The receiver's public key |
| Decrypt with | The same secret key | The receiver's private key |
| Speed | Very fast | Much slower |
| Data size | Any size | Only small amounts |
| Main problem | Both sides must already share the key safely | None for sharing: the public key can be published |
| Algorithms | AES, ChaCha20 | RSA, elliptic curves |
| Used for | Files, databases, network traffic | Exchanging keys, digital signatures |
The Classes
| Class | Description |
|---|---|
Cipher | It is used to encrypt and decrypt. |
KeyGenerator | It creates a symmetric key, such as an AES key. |
KeyPairGenerator | It creates a public and private key pair. |
SecretKey, PublicKey, PrivateKey | The key types. |
GCMParameterSpec | It carries the initialization vector and tag length for AES-GCM. |
A cipher is requested by a transformation string: algorithm/mode/padding, for example AES/GCM/NoPadding.
Example: AES and RSA
import java.nio.charset.StandardCharsets;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;
import javax.crypto.AEADBadTagException;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.GCMParameterSpec;
public class Encrypt {
public static void main(String[] args) throws Exception {
byte[] message = "Navika's marks: 91"
.getBytes(StandardCharsets.UTF_8);
// ---- Symmetric: one secret key, AES in GCM mode ----
KeyGenerator keyGen = KeyGenerator.getInstance("AES");
keyGen.init(256);
SecretKey key = keyGen.generateKey();
byte[] iv = new byte[12];
new SecureRandom().nextBytes(iv);
Cipher aes = Cipher.getInstance("AES/GCM/NoPadding");
aes.init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv));
byte[] encrypted = aes.doFinal(message);
System.out.println("AES: " + message.length + " bytes in, "
+ encrypted.length + " bytes out (16 are the tag)");
aes.init(Cipher.DECRYPT_MODE, key, new GCMParameterSpec(128, iv));
String back = new String(aes.doFinal(encrypted),
StandardCharsets.UTF_8);
System.out.println("Decrypted: " + back);
// Change one bit of the encrypted data: decryption refuses it.
encrypted[0] ^= 1;
try {
aes.init(Cipher.DECRYPT_MODE, key,
new GCMParameterSpec(128, iv));
aes.doFinal(encrypted);
} catch (AEADBadTagException e) {
System.out.println("Tampered data rejected: "
+ e.getClass().getSimpleName());
}
// ---- Asymmetric: a public key and a private key, RSA ----
KeyPairGenerator pairGen = KeyPairGenerator.getInstance("RSA");
pairGen.initialize(2048);
KeyPair pair = pairGen.generateKeyPair();
Cipher rsa = Cipher.getInstance(
"RSA/ECB/OAEPWithSHA-256AndMGF1Padding");
rsa.init(Cipher.ENCRYPT_MODE, pair.getPublic());
byte[] sealed = rsa.doFinal(message);
System.out.println("RSA: " + sealed.length + " bytes out");
rsa.init(Cipher.DECRYPT_MODE, pair.getPrivate());
System.out.println("Decrypted with the private key: "
+ new String(rsa.doFinal(sealed), StandardCharsets.UTF_8));
}
}AES: 18 bytes in, 34 bytes out (16 are the tag)
Decrypted: Navika's marks: 91
Tampered data rejected: AEADBadTagException
RSA: 256 bytes out
Decrypted with the private key: Navika's marks: 91Explanation:
- A 256-bit AES key is generated with
KeyGenerator. - The IV (initialization vector) is 12 random bytes. It makes the same message encrypt differently each time. It isn't secret, and it is stored or sent along with the ciphertext, but it must never be reused with the same key.
AES/GCM/NoPaddingis AES in GCM mode. GCM is authenticated encryption: besides hiding the data, it adds a 16-byte tag that detects any change. That is why 18 bytes became 34.- Decryption needs the same key and the same IV.
- Tampering: one bit of the ciphertext is flipped. Decryption throws
AEADBadTagExceptioninstead of returning damaged data. Modes without authentication would silently return garbage. - RSA: a 2048-bit key pair is generated. The message is encrypted with the public key and decrypted with the private key. The output is 256 bytes, the size of the key, however short the message is. OAEP is the secure padding scheme for RSA.
How They Work Together
RSA can only encrypt a small message, and it is slow. AES needs both sides to share a key. Real systems use each for what it does well. This is called hybrid encryption, and it is how HTTPS works:
- Generate a random AES key for this message or session.
- Encrypt the data with that AES key.
- Encrypt the AES key with the receiver's public key (or agree on it with a key exchange).
- Send both. The receiver uses the private key to recover the AES key, then decrypts the data.
Choices That Matter
| Choice | Use | Avoid |
|---|---|---|
| Symmetric algorithm | AES with 128 or 256-bit keys | DES, 3DES, RC4 |
| AES mode | GCM | ECB, which reveals patterns: equal blocks encrypt to equal blocks |
| RSA key size | 2048 bits or more | 1024 bits |
| RSA padding | OAEP | PKCS#1 v1.5 for new designs, and "no padding" |
| Keys and IVs | From SecureRandom | From Random, from passwords directly, or hard-coded |
Cipher.getInstance("AES") alone means AES/ECB/PKCS5Padding, the insecure mode. Always write the full transformation.
Common Mistakes
Using ECB mode, often by writing just "AES". It leaks patterns in the data.
Reusing an IV with the same key in GCM. That breaks its security completely. Generate a fresh random IV for every message.
Hard-coding keys in source code. Anyone with the code has the key. Keep keys in a key store or a secrets manager.
Confusing encoding with encryption. Base64 is not encryption; anyone can decode it.
Encrypting passwords. Passwords should be hashed with a slow function, not encrypted (see "Storing Passwords Safely in Java").
Interview Corner
What is the difference between symmetric and asymmetric encryption?
Symmetric encryption uses the same secret key to encrypt and decrypt; it is fast and suited to large data, but the key must be shared securely. AES is the standard. Asymmetric encryption uses a key pair: data encrypted with the public key can only be decrypted with the private key; it is much slower and limited to small data, but solves key distribution and enables digital signatures. RSA and elliptic-curve algorithms are examples. Systems such as TLS combine both: asymmetric cryptography to establish a shared key, then symmetric encryption for the data.
How do you encrypt data in Java?
With javax.crypto.Cipher. For AES-GCM: create a key with KeyGenerator, generate a random 12-byte IV with SecureRandom, call Cipher.getInstance("AES/GCM/NoPadding"), init(Cipher.ENCRYPT_MODE, key, new GCMParameterSpec(128, iv)) and doFinal(plaintext). Store the IV with the ciphertext. Decryption uses DECRYPT_MODE with the same key and IV and throws AEADBadTagException if the data was altered.
Practice
- Easy: Encrypt the same message twice with the same key but two different IVs, and show that the two ciphertexts differ.
- Harder: Write
encrypt(key, text)that returns the IV followed by the ciphertext in one byte array, anddecrypt(key, bytes)that splits them again.
Key Takeaways
Was this tutorial helpful?