Mastering Cryptography: From Classic Ciphers to Java Encryption Implementations
This article explains fundamental cryptography concepts, traces the evolution from ancient substitution ciphers to modern symmetric and asymmetric algorithms, and provides complete Java code examples for DES, AES, RSA, digital signatures, and hash functions such as MD5 and SHA.
Basic Terminology
Plaintext : Original readable data.
Ciphertext : Data after applying an encryption method; can be transmitted publicly.
Key : Secret value that drives the encryption algorithm.
Encryption method : The algorithm or rule that transforms plaintext into ciphertext.
Historical Overview
Classical cryptography dates back to ancient Egypt (c. 1900 BC) and includes simple substitution ciphers such as the Caesar cipher, where each letter is shifted by a fixed offset (e.g., a shift of 3 turns A→D, B→E). The following image illustrates the Caesar cipher:
Modern cryptography emerged during the World Wars, leading to milestones such as the Enigma machine, Shannon’s information‑theoretic foundations (1949), the Data Encryption Standard (DES, 1977), and the invention of public‑key cryptography by Diffie and Hellman (1976). These developments form the basis of today’s symmetric and asymmetric algorithms.
Symmetric Encryption Algorithms
Symmetric encryption uses the same secret key for both encryption and decryption. Common algorithms include DES, 3DES, AES, Blowfish, RC5, and IDEA, with DES and AES being the most frequently referenced.
Advantages : Public algorithm, low computational cost, fast encryption speed.
Disadvantages : Secure key distribution and management are challenging.
Encryption Process
Encryption: plaintext + key = ciphertext Decryption:
ciphertext - key = plaintextDES (Data Encryption Standard) – Java Example
DES is a 64‑bit block cipher with a 56‑bit effective key length. In Java the JCE API is used as follows:
private static final String DES = "DES";
public static void main(String[] args) throws Exception {
String data = "123 456";
String key = "wang!@#$"; // key must be at least 8 bytes
System.out.println(encrypt(data, key));
System.out.println(decrypt(encrypt(data, key), key));
}
public static String encrypt(String data, String key) throws Exception {
byte[] encrypted = encrypt(data.getBytes(), key.getBytes());
return new sun.misc.BASE64Encoder().encode(encrypted);
}
public static String decrypt(String data, String key) throws Exception {
if (data == null) return null;
byte[] decoded = new sun.misc.BASE64Decoder().decodeBuffer(data);
return new String(decrypt(decoded, key.getBytes()));
}
private static byte[] encrypt(byte[] data, byte[] key) throws Exception {
SecureRandom sr = new SecureRandom();
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory kf = SecretKeyFactory.getInstance(DES);
SecretKey sk = kf.generateSecret(dks);
Cipher cipher = Cipher.getInstance(DES);
cipher.init(Cipher.ENCRYPT_MODE, sk, sr);
return cipher.doFinal(data);
}
private static byte[] decrypt(byte[] data, byte[] key) throws Exception {
SecureRandom sr = new SecureRandom();
DESKeySpec dks = new DESKeySpec(key);
SecretKeyFactory kf = SecretKeyFactory.getInstance(DES);
SecretKey sk = kf.generateSecret(dks);
Cipher cipher = Cipher.getInstance(DES);
cipher.init(Cipher.DECRYPT_MODE, sk, sr);
return cipher.doFinal(data);
}Key notes for DES in Java :
The key length must be at least 8 bytes; excess bytes are ignored (only the first 8 are used).
DESKeySpec internally copies the first 8 bytes, throwing an InvalidKeyException if fewer bytes are provided.
AES (Advanced Encryption Standard) – Java Example
AES is a block cipher with a fixed block size of 128 bits and key sizes of 128, 192, or 256 bits. The following snippet shows AES‑128 in ECB mode with PKCS5 padding:
public static void main(String[] args) throws Exception {
String key = "1234567890123456"; // 16‑byte key for AES‑128
String plain = "buxuewushu";
String encrypted = encrypt(plain, key);
System.out.println("Encrypted: " + encrypted);
String decrypted = decrypt(encrypted, key);
System.out.println("Decrypted: " + decrypted);
}
public static String encrypt(String src, String key) throws Exception {
if (key == null || key.length() != 16) {
throw new IllegalArgumentException("Key must be 16 bytes");
}
SecretKeySpec sk = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, sk);
byte[] encrypted = cipher.doFinal(src.getBytes("UTF-8"));
return java.util.Base64.getEncoder().encodeToString(encrypted);
}
public static String decrypt(String src, String key) throws Exception {
if (key == null || key.length() != 16) {
throw new IllegalArgumentException("Key must be 16 bytes");
}
SecretKeySpec sk = new SecretKeySpec(key.getBytes("UTF-8"), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, sk);
byte[] decoded = java.util.Base64.getDecoder().decode(src);
return new String(cipher.doFinal(decoded), "UTF-8");
}Important points when configuring Cipher in Java:
The transformation string follows the pattern algorithm/mode/padding (e.g., AES/CBC/PKCS5Padding).
If the mode is omitted, the default is ECB; if padding is omitted, the default is PKCS5Padding.
For block ciphers combined with stream modes, the block size must be specified (e.g., DES/CFB8/NoPadding). Cipher supports four operation modes: ENCRYPT_MODE, DECRYPT_MODE, WRAP_MODE, and UNWRAP_MODE.
Asymmetric Encryption Algorithms (RSA)
Asymmetric (public‑key) cryptography uses a key pair: a public key for encryption or signature verification, and a private key for decryption or signing. RSA is the most widely used algorithm.
Key Pair Generation
public static KeyPair buildKeyPair() throws NoSuchAlgorithmException {
int keySize = 2048; // minimum recommended size
KeyPairGenerator kpg = KeyPairGenerator.getInstance("RSA");
kpg.initialize(keySize);
return kpg.generateKeyPair();
}RSA Encryption / Decryption
public static byte[] encrypt(PublicKey pub, String message) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.ENCRYPT_MODE, pub);
return cipher.doFinal(message.getBytes("UTF-8"));
}
public static byte[] decrypt(PrivateKey priv, byte[] encrypted) throws Exception {
Cipher cipher = Cipher.getInstance("RSA");
cipher.init(Cipher.DECRYPT_MODE, priv);
return cipher.doFinal(encrypted);
}Digital Signature (Sign / Verify)
private static String signWithRSA(String content, PrivateKey priv) throws Exception {
Signature sig = Signature.getInstance("SHA1WithRSA");
sig.initSign(priv);
sig.update(content.getBytes("UTF-8"));
return java.util.Base64.getEncoder().encodeToString(sig.sign());
}
private static boolean verifyWithRSA(String content, PublicKey pub, String signature) throws Exception {
Signature sig = Signature.getInstance("SHA1WithRSA");
sig.initVerify(pub);
sig.update(content.getBytes("UTF-8"));
return sig.verify(java.util.Base64.getDecoder().decode(signature));
}Example usage:
public static void main(String[] args) throws Exception {
KeyPair kp = buildKeyPair();
byte[] enc = encrypt(kp.getPublic(), "Sample message");
System.out.println("Encrypted: " + java.util.Base64.getEncoder().encodeToString(enc));
System.out.println("Decrypted: " + new String(decrypt(kp.getPrivate(), enc), "UTF-8"));
String sig = signWithRSA("Sample message", kp.getPrivate());
System.out.println("Signature: " + sig);
System.out.println("Verified: " + verifyWithRSA("Sample message", kp.getPublic(), sig));
}Hash (Message‑Digest) Algorithms
Hash functions produce a fixed‑size fingerprint of arbitrary data without requiring a key. They are used for integrity checks, digital signatures, and as building blocks for other cryptographic protocols. Good hash functions are deterministic, collision‑resistant, and irreversible.
MD5 Example in Java
public static String getMD5(String input) throws Exception {
MessageDigest md = MessageDigest.getInstance("MD5");
md.update(input.getBytes());
byte[] digest = md.digest();
// Convert to hex string
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b & 0xff));
}
return sb.toString();
}Common hash families (non‑exhaustive):
CRC8/16/32 – simple checksums, widely used in communications.
MD2, MD4, MD5 – 128‑bit digests; MD5 is the most common.
SHA‑1, SHA‑256, SHA‑384, SHA‑512 – NIST‑standard hashes with increasing security.
RIPEMD, PANAMA, TIGER, ADLER32 – alternative designs for specific use‑cases.
Key Characteristics of Secure Hashes
Fixed output length regardless of input size.
Different inputs produce different digests with overwhelming probability.
Pre‑image resistance: infeasible to reconstruct the original message from its hash.
Collision resistance: infeasible to find two distinct inputs that yield the same hash.
This summary provides the essential terminology, historical context, and practical Java code for symmetric (DES, AES) and asymmetric (RSA) encryption, as well as for hash functions such as MD5 and SHA families, enabling developers to implement secure cryptographic operations in software.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Code Ape Tech Column
Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
