Java Cryptography: A Developer's Guide (2026)
Cryptography is essential for securing data in Java applications. This comprehensive guide explores cryptographic techniques, patterns, and best practices for Java developers.
Pro Tip: Understanding cryptography fundamentals is crucial for implementing secure applications.
Table of Contents
Symmetric Encryption
Note: Symmetric encryption uses the same key for both encryption and decryption.
Symmetric Encryption Implementation
public class SymmetricEncryption {
private static final String ALGORITHM = "AES/GCM/NoPadding";
private static final int GCM_IV_LENGTH = 12;
private static final int GCM_TAG_LENGTH = 16;
private final SecretKey key;
public SymmetricEncryption(byte[] keyBytes) {
this.key = new SecretKeySpec(keyBytes, "AES");
}
public String encrypt(String plaintext) throws Exception {
byte[] iv = generateIV();
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.ENCRYPT_MODE, key, spec);
byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
byte[] combined = new byte[iv.length + encrypted.length];
System.arraycopy(iv, 0, combined, 0, iv.length);
System.arraycopy(encrypted, 0, combined, iv.length, encrypted.length);
return Base64.getEncoder().encodeToString(combined);
}
public String decrypt(String ciphertext) throws Exception {
byte[] combined = Base64.getDecoder().decode(ciphertext);
byte[] iv = new byte[GCM_IV_LENGTH];
byte[] encrypted = new byte[combined.length - GCM_IV_LENGTH];
System.arraycopy(combined, 0, iv, 0, GCM_IV_LENGTH);
System.arraycopy(combined, GCM_IV_LENGTH, encrypted, 0, encrypted.length);
Cipher cipher = Cipher.getInstance(ALGORITHM);
GCMParameterSpec spec = new GCMParameterSpec(GCM_TAG_LENGTH * 8, iv);
cipher.init(Cipher.DECRYPT_MODE, key, spec);
byte[] decrypted = cipher.doFinal(encrypted);
return new String(decrypted, StandardCharsets.UTF_8);
}
private byte[] generateIV() {
byte[] iv = new byte[GCM_IV_LENGTH];
new SecureRandom().nextBytes(iv);
return iv;
}
}
Asymmetric Encryption
Pro Tip: Asymmetric encryption uses different keys for encryption and decryption.
Asymmetric Encryption Implementation
public class AsymmetricEncryption {
private static final String ALGORITHM = "RSA/ECB/OAEPWithSHA-256AndMGF1Padding";
private final PrivateKey privateKey;
private final PublicKey publicKey;
public AsymmetricEncryption(KeyPair keyPair) {
this.privateKey = keyPair.getPrivate();
this.publicKey = keyPair.getPublic();
}
public String encrypt(String plaintext) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, publicKey);
byte[] encrypted = cipher.doFinal(plaintext.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(encrypted);
}
public String decrypt(String ciphertext) throws Exception {
Cipher cipher = Cipher.getInstance(ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
byte[] decrypted = cipher.doFinal(Base64.getDecoder().decode(ciphertext));
return new String(decrypted, StandardCharsets.UTF_8);
}
public static KeyPair generateKeyPair() throws Exception {
KeyPairGenerator generator = KeyPairGenerator.getInstance("RSA");
generator.initialize(4096);
return generator.generateKeyPair();
}
}
Hashing
Note: Hashing is used for data integrity verification and password storage.
Hashing Implementation
public class SecureHashing {
private static final String HASH_ALGORITHM = "SHA-256";
private static final String PBKDF2_ALGORITHM = "PBKDF2WithHmacSHA256";
private static final int ITERATIONS = 65536;
private static final int KEY_LENGTH = 256;
public String hash(String input) throws Exception {
MessageDigest digest = MessageDigest.getInstance(HASH_ALGORITHM);
byte[] hash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(hash);
}
public String hashPassword(String password, byte[] salt) throws Exception {
KeySpec spec = new PBEKeySpec(
password.toCharArray(),
salt,
ITERATIONS,
KEY_LENGTH
);
SecretKeyFactory factory = SecretKeyFactory.getInstance(PBKDF2_ALGORITHM);
byte[] hash = factory.generateSecret(spec).getEncoded();
byte[] combined = new byte[salt.length + hash.length];
System.arraycopy(salt, 0, combined, 0, salt.length);
System.arraycopy(hash, 0, combined, salt.length, hash.length);
return Base64.getEncoder().encodeToString(combined);
}
public boolean verifyPassword(String password, String storedHash) throws Exception {
byte[] combined = Base64.getDecoder().decode(storedHash);
byte[] salt = new byte[16];
byte[] hash = new byte[combined.length - 16];
System.arraycopy(combined, 0, salt, 0, 16);
System.arraycopy(combined, 16, hash, 0, hash.length);
String computedHash = hashPassword(password, salt);
return computedHash.equals(storedHash);
}
}
Digital Signatures
Pro Tip: Digital signatures ensure data authenticity and integrity.
Digital Signature Implementation
public class DigitalSignature {
private static final String SIGNATURE_ALGORITHM = "SHA256withRSA";
private final PrivateKey privateKey;
private final PublicKey publicKey;
public DigitalSignature(KeyPair keyPair) {
this.privateKey = keyPair.getPrivate();
this.publicKey = keyPair.getPublic();
}
public String sign(String data) throws Exception {
Signature signature = Signature.getInstance(SIGNATURE_ALGORITHM);
signature.initSign(privateKey);
signature.update(data.getBytes(StandardCharsets.UTF_8));
return Base64.getEncoder().encodeToString(signature.sign());
}
public boolean verify(String data, String signature) throws Exception {
Signature sig = Signature.getInstance(SIGNATURE_ALGORITHM);
sig.initVerify(publicKey);
sig.update(data.getBytes(StandardCharsets.UTF_8));
return sig.verify(Base64.getDecoder().decode(signature));
}
}
Key Management
Note: Proper key management is crucial for cryptographic security.
Key Management Implementation
public class KeyManager {
private static final String KEYSTORE_TYPE = "PKCS12";
private static final String KEYSTORE_PATH = "keystore.p12";
private static final String KEYSTORE_PASSWORD = "changeit";
private final KeyStore keyStore;
public KeyManager() throws Exception {
this.keyStore = KeyStore.getInstance(KEYSTORE_TYPE);
loadKeyStore();
}
private void loadKeyStore() throws Exception {
File keystoreFile = new File(KEYSTORE_PATH);
if (keystoreFile.exists()) {
try (FileInputStream fis = new FileInputStream(keystoreFile)) {
keyStore.load(fis, KEYSTORE_PASSWORD.toCharArray());
}
} else {
keyStore.load(null, KEYSTORE_PASSWORD.toCharArray());
}
}
public void saveKeyStore() throws Exception {
try (FileOutputStream fos = new FileOutputStream(KEYSTORE_PATH)) {
keyStore.store(fos, KEYSTORE_PASSWORD.toCharArray());
}
}
public void storeKey(String alias, Key key, String password) throws Exception {
keyStore.setKeyEntry(alias, key, password.toCharArray(), null);
saveKeyStore();
}
public Key getKey(String alias, String password) throws Exception {
return keyStore.getKey(alias, password.toCharArray());
}
public void deleteKey(String alias) throws Exception {
keyStore.deleteEntry(alias);
saveKeyStore();
}
}
Best Practices
Pro Tip: Following cryptographic best practices ensures application security.
Cryptographic Best Practices
- Use strong encryption algorithms
- Implement proper key management
- Use secure random number generation
- Implement proper key rotation
- Use appropriate key sizes
- Implement proper error handling
- Use secure key storage
- Implement proper access controls
- Use secure communication protocols
- Implement proper logging
- Use secure configuration management
- Implement proper key backup
- Use secure key exchange
- Implement proper key recovery
- Follow cryptographic standards
Conclusion
Cryptography is essential for securing data in Java applications. By implementing these cryptographic techniques and following best practices, developers can create more secure applications.