views:

12955

answers:

2

Every method I write to encode a string in Java using 3DES can't be decrypted back to the original string. Does anyone have a simple code snippet that can just encode and then decode the string back to the original string?

I know I'm making a very silly mistake somewhere in this code. Here's what I've been working with so far:

** note, I am not returning the BASE64 text from the encrypt method, and I am not base64 un-encoding in the decrypt method because I was trying to see if I was making a mistake in the BASE64 part of the puzzle.

public class TripleDESTest {

public static void main(String[] args) {

    String text = "kyle boon";

    byte[] codedtext = new TripleDESTest().encrypt(text);
    String decodedtext  = new TripleDESTest().decrypt(codedtext);

    System.out.println(codedtext);
    System.out.println(decodedtext);
}

public byte[] encrypt(String message)
{
    try
 {
  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;    
    }
    catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
    catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
    catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
    catch (java.security.InvalidKeyException e) { System.out.println("Invalid Key"); }
    catch (BadPaddingException e) { System.out.println("Invalid Key");}
    catch (IllegalBlockSizeException e) { System.out.println("Invalid Key");}
    catch (UnsupportedEncodingException e) { System.out.println("Invalid Key");}

    return null;
}

public String decrypt(byte[] message)
{
    try
 {
  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 plainText.toString();            
    }
    catch (java.security.InvalidAlgorithmParameterException e) { System.out.println("Invalid Algorithm"); }
    catch (javax.crypto.NoSuchPaddingException e) { System.out.println("No Such Padding"); }
    catch (java.security.NoSuchAlgorithmException e) { System.out.println("No Such Algorithm"); }
    catch (java.security.InvalidKeyException e) { System.out.println("Invalid Key"); }
    catch (BadPaddingException e) { System.out.println("Invalid Key");}
    catch (IllegalBlockSizeException e) { System.out.println("Invalid Key");}
    catch (UnsupportedEncodingException e) { System.out.println("Invalid Key");} catch (IOException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
 }

    return null;
}

}

+7  A: 

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");
    }
}
Boris Terzic
A: 

@Mike Sarek: While I agree with you on the high level password storage issue you focus on, there are two small points that I think you've missed:

  1. The question is about 3DES encryption implementation, really. The use case that shows up in the OPs may just be an example, or maybe the application actually needs to decode the password for whatever reason. I think that's why people are voting your answer down :(
  2. If one didn't need to unencode a password (or whatever), and security was goal, then maybe it would be best to directly advocate a hash algorithm (like SHA or (gasp) MD5) in addition to the high level critique.

Anyways, just my 2 pence.

Stu Thompson