How do I use 3DES encryption/decryption in Java?
Your code was fine except for the Base 64 encoding bit (which you mentioned was a test), the reason the output may not have made sense is that you were displaying a raw byte array (doing toString() on a byte array returns its internal Java reference, not the String representation of the contents). Here's a version that's just a teeny bit cleaned up and which prints "kyle boon" as the decoded string:
import java.security.MessageDigest;import java.util.Arrays;import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;public class TripleDESTest { public static void main(String[] args) throws Exception { String text = "kyle boon"; byte[] codedtext = new TripleDESTest().encrypt(text); String decodedtext = new TripleDESTest().decrypt(codedtext); System.out.println(codedtext); // this is a byte array, you'll just see a reference to an array System.out.println(decodedtext); // This correctly shows "kyle boon" } public byte[] encrypt(String message) throws Exception { final MessageDigest md = MessageDigest.getInstance("md5"); final byte[] digestOfPassword = md.digest("HG58YZ3CR9" .getBytes("utf-8")); final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24); for (int j = 0, k = 16; j < 8;) { keyBytes[k++] = keyBytes[j++]; } final SecretKey key = new SecretKeySpec(keyBytes, "DESede"); final IvParameterSpec iv = new IvParameterSpec(new byte[8]); final Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); cipher.init(Cipher.ENCRYPT_MODE, key, iv); final byte[] plainTextBytes = message.getBytes("utf-8"); final byte[] cipherText = cipher.doFinal(plainTextBytes); // final String encodedCipherText = new sun.misc.BASE64Encoder() // .encode(cipherText); return cipherText; } public String decrypt(byte[] message) throws Exception { final MessageDigest md = MessageDigest.getInstance("md5"); final byte[] digestOfPassword = md.digest("HG58YZ3CR9" .getBytes("utf-8")); final byte[] keyBytes = Arrays.copyOf(digestOfPassword, 24); for (int j = 0, k = 16; j < 8;) { keyBytes[k++] = keyBytes[j++]; } final SecretKey key = new SecretKeySpec(keyBytes, "DESede"); final IvParameterSpec iv = new IvParameterSpec(new byte[8]); final Cipher decipher = Cipher.getInstance("DESede/CBC/PKCS5Padding"); decipher.init(Cipher.DECRYPT_MODE, key, iv); // final byte[] encData = new // sun.misc.BASE64Decoder().decodeBuffer(message); final byte[] plainText = decipher.doFinal(message); return new String(plainText, "UTF-8"); }}
Here is a solution using the javax.crypto library and the apache commons codec library for encoding and decoding in Base64:
import java.security.spec.KeySpec;import javax.crypto.Cipher;import javax.crypto.SecretKey;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.DESedeKeySpec;import org.apache.commons.codec.binary.Base64;public class TrippleDes { private static final String UNICODE_FORMAT = "UTF8"; public static final String DESEDE_ENCRYPTION_SCHEME = "DESede"; private KeySpec ks; private SecretKeyFactory skf; private Cipher cipher; byte[] arrayBytes; private String myEncryptionKey; private String myEncryptionScheme; SecretKey key; public TrippleDes() throws Exception { myEncryptionKey = "ThisIsSpartaThisIsSparta"; myEncryptionScheme = DESEDE_ENCRYPTION_SCHEME; arrayBytes = myEncryptionKey.getBytes(UNICODE_FORMAT); ks = new DESedeKeySpec(arrayBytes); skf = SecretKeyFactory.getInstance(myEncryptionScheme); cipher = Cipher.getInstance(myEncryptionScheme); key = skf.generateSecret(ks); } public String encrypt(String unencryptedString) { String encryptedString = null; try { cipher.init(Cipher.ENCRYPT_MODE, key); byte[] plainText = unencryptedString.getBytes(UNICODE_FORMAT); byte[] encryptedText = cipher.doFinal(plainText); encryptedString = new String(Base64.encodeBase64(encryptedText)); } catch (Exception e) { e.printStackTrace(); } return encryptedString; } public String decrypt(String encryptedString) { String decryptedText=null; try { cipher.init(Cipher.DECRYPT_MODE, key); byte[] encryptedText = Base64.decodeBase64(encryptedString); byte[] plainText = cipher.doFinal(encryptedText); decryptedText= new String(plainText); } catch (Exception e) { e.printStackTrace(); } return decryptedText; } public static void main(String args []) throws Exception { TrippleDes td= new TrippleDes(); String target="imparator"; String encrypted=td.encrypt(target); String decrypted=td.decrypt(encrypted); System.out.println("String To Encrypt: "+ target); System.out.println("Encrypted String:" + encrypted); System.out.println("Decrypted String:" + decrypted); }}
Running the above program results with the following output:
String To Encrypt: imparatorEncrypted String:FdBNaYWfjpWN9eYghMpbRA==Decrypted String:imparator
I had hard times figuring it out myself and this post helped me to find the right answer for my case. When working with financial messaging as ISO-8583 the 3DES requirements are quite specific, so for my especial case the "DESede/CBC/PKCS5Padding" combinations wasn't solving the problem. After some comparative testing of my results against some 3DES calculators designed for the financial world I found the the value "DESede/ECB/Nopadding" is more suited for the the specific task.
Here is a demo implementation of my TripleDes class (using the Bouncy Castle provider)
import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.Security; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.SecretKey; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; /** * * @author Jose Luis Montes de Oca */ public class TripleDesCipher { private static String TRIPLE_DES_TRANSFORMATION = "DESede/ECB/Nopadding"; private static String ALGORITHM = "DESede"; private static String BOUNCY_CASTLE_PROVIDER = "BC"; private Cipher encrypter; private Cipher decrypter; public TripleDesCipher(byte[] key) throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException { Security.addProvider(new BouncyCastleProvider()); SecretKey keySpec = new SecretKeySpec(key, ALGORITHM); encrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION, BOUNCY_CASTLE_PROVIDER); encrypter.init(Cipher.ENCRYPT_MODE, keySpec); decrypter = Cipher.getInstance(TRIPLE_DES_TRANSFORMATION, BOUNCY_CASTLE_PROVIDER); decrypter.init(Cipher.DECRYPT_MODE, keySpec); } public byte[] encode(byte[] input) throws IllegalBlockSizeException, BadPaddingException { return encrypter.doFinal(input); } public byte[] decode(byte[] input) throws IllegalBlockSizeException, BadPaddingException { return decrypter.doFinal(input); } }