Java String Encryption with DES/CBC and MD5 Digest
This guide shows how to:
- Encrypt and decrypt strings using DES in CBC mode with PKCS5 padding
- Generate an MD5 digest for verificasion
- Correctly read and write Base64 ciphertext so decryption succeeds
Note: MD5 is a one-way hash and cannot be decrypted. Use it only for checksums or comparisons. DES is a legacy algorithm; to new systems prefer AES.
DES/CBC encryption and decryption with Base64
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.spec.AlgorithmParameterSpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
public final class LegacyDesCrypto {
// 8-byte IV for DES/CBC
private static final byte[] DEFAULT_IV = new byte[] {
0x22, 0x54, 0x36, 110, 0x40, (byte) 0xAC, (byte) 0xAD, (byte) 0xDF
};
private final AlgorithmParameterSpec ivSpec;
private final Key secretKey;
private final String charset;
public LegacyDesCrypto(String passphrase) throws Exception {
this(passphrase, StandardCharsets.UTF_8.name(), DEFAULT_IV);
}
public LegacyDesCrypto(String passphrase, String charset, byte[] iv) throws Exception {
this.charset = charset;
this.ivSpec = new IvParameterSpec(iv);
// DESKeySpec takes the first 8 bytes; longer keys are truncated by the spec
DESKeySpec spec = new DESKeySpec(passphrase.getBytes(charset));
SecretKeyFactory factory = SecretKeyFactory.getInstance("DES");
this.secretKey = factory.generateSecret(spec);
}
public String encryptToBase64(String plaintext) throws Exception {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(charset));
return Base64.getEncoder().encodeToString(ciphertext);
}
public String decryptFromBase64(String base64Ciphertext) throws Exception {
Cipher cipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, secretKey, ivSpec);
byte[] raw = Base64.getDecoder().decode(base64Ciphertext);
byte[] plaintext = cipher.doFinal(raw);
return new String(plaintext, charset);
}
public static String md5Hex(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
byte[] digest = md.digest(input.getBytes(StandardCharsets.UTF_8));
StringBuilder hex = new StringBuilder(digest.length * 2);
for (byte b : digest) {
hex.append(String.format("%02x", b & 0xff));
}
return hex.toString();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException("MD5 not available", e);
}
}
public static void main(String[] args) throws Exception {
String passphrase = "example passphrase with enough length";
String data = "Sample content to protect";
LegacyDesCrypto crypto = new LegacyDesCrypto(passphrase);
String enc = crypto.encryptToBase64(data);
String dec = crypto.decryptFromBase64(enc);
String md5 = md5Hex(data);
System.out.println("Plaintext: " + data);
System.out.println("Encrypted (Base64): " + enc);
System.out.println("Decrypted: " + dec);
System.out.println("MD5: " + md5);
}
}
- CBC with PKCS5 padding accepts any inpput length; you do not need input sizes to be a multiple of 8
- Base64 is used to encode binary ciphertext as text for storage or transport
Reading and writing Base64 ciphertext with out extra bytes
When persisting the Base64 string, ensure you only read and write the actual characters and avoid padding the buffer with unused space.
Incorrect pattern (converts the entire buffer, including unused positions):
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
File f = new File("C:/ceshi.txt");
try (Reader r = new FileReader(f)) {
char[] buf = new char[1024];
int n = r.read(buf); // may be less than 1024
// WRONG: includes unused characters, breaking Base64
String base64 = new String(buf);
}
Correct pattern (only convert the portion that was read):
import java.io.File;
import java.io.FileReader;
import java.io.Reader;
File f = new File("C:/ceshi.txt");
try (Reader r = new FileReader(f)) {
char[] buf = new char[1024];
int n = r.read(buf);
String base64 = (n > 0) ? new String(buf, 0, n) : "";
}
A simpler approach that avoids manual buffers:
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
String base64 = Files.readString(Path.of("C:/ceshi.txt"), StandardCharsets.US_ASCII).trim();
Decrypting a Base64 ciphertext read from file:
LegacyDesCrypto crypto = new LegacyDesCrypto("example passphrase with enough length");
String base64 = Files.readString(Path.of("C:/ceshi.txt")).trim();
String plaintext = crypto.decryptFromBase64(base64);
System.out.println(plaintext);