Odd behaviour of DESede Cipher
Dear all,
i am having a problem with DESede and I mayb somebody of you can give me a hint.
This is my Code.
publicclass CipherTest{
privatefinalstaticint ENCRYPT_MODE = Cipher.ENCRYPT_MODE;
privatefinalstaticint DECRYPT_MODE = Cipher.DECRYPT_MODE;
privatestaticfinal String CIPHER_3DES ="DESede/ECB/PKCS5Padding";
privatestatic String ALGORITHM_3DES ="DESede";
/**
* @param args
*/
publicstaticvoid main(String[] args){
String key ="012345678901234567890123";
String value ="13245678:konstant:20061214";
System.out.println(value.length());
String b64encResult;
try{
BASE64Encoder encoder =new BASE64Encoder();
System.out.println(value);
String b64value = encoder.encodeBuffer(value.getBytes());
System.out.println(b64value);
b64encResult = encryptValue(b64value, key);
String decResult = decryptValue(b64encResult, key);
BASE64Decoder decoder =new BASE64Decoder();
System.out.println(decResult);
String result =new String(decoder.decodeBuffer(decResult));
System.out.println(result);
}catch (Exception e){
e.printStackTrace();
}
}
publicstatic String encryptValue(final String value,final String key)throws Exception{
return doCrypt2(ENCRYPT_MODE, value, key, CIPHER_3DES, ALGORITHM_3DES);
}
publicstatic String decryptValue(final String value,final String key)throws Exception{
return doCrypt2(DECRYPT_MODE, value, key, CIPHER_3DES, ALGORITHM_3DES);
}
privatestatic String doCrypt2(finalint signal,final String value,final String key,final String crypAlg,final String cryptStd)throws Exception{
String decValue =null;
byte[] theKey = key.getBytes();
SecretKeySpec ks =new SecretKeySpec(theKey,cryptStd);
Cipher cf = Cipher.getInstance(crypAlg);
cf.init(signal,ks);
byte[] theCph = cf.doFinal(value.getBytes());
decValue =new String(theCph);
return decValue;
}
}
The programm will fail with a BadPaddingException.
Changing the value from
String value = "13245678:konstant:20061214";
to
String value = "13245678:konstan:20061214";
the programm will run fine.
Can somebody tell me what i'm doing wrong?
Thanks
Poncho
# 1
Your problem is in these lines -byte[] theCph = cf.doFinal(value.getBytes());
decValue = new String(theCph);
return decValue;
Another example of the number 1 problem people have when doing encryption. If you search this forum you will find dozens of example.
One cannot reliably reversibly convert arrays of random bytes to a string using
decValue = new String(theCph);
and bytes obtained from encryption are pretty random.
Keep the result as a byte[] array but if this is not possible then use Base64 or Hex encoding.
# 2
Ok, first I see that it make no sense in my case to encode to Base64 BEFORE
using the DESede encoding.
It has to be done AFTER. ;-)
Second i guess that you mean, that a byte-array can interpreted differently when cast to a String or when given as a paramater to a String constructor.
So it would be better to use it all the time as byte[].
Just one more question, before I start changing everything:
Base64Encoder.encode ( i mean the sun.misc.BASE64Encoder ) is returning a String as result.
Isn't that a problem?
Regards
Poncho
# 3
> Just one more question, before I start changing
> everything:
> Base64Encoder.encode ( i mean the
> sun.misc.BASE64Encoder ) is returning a String as
> result.
> Isn't that a problem?
No! Why should it be? Strings contain UNICODE characters and Base64 strings contain only a subset of the UNICODE character set. Base64 strings can be reliably converted back to bytes.
# 4
Hm I really don't get it.
I have changed my code to the following:
import java.util.Arrays;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class CipherTest {
private final static int ENCRYPT_MODE = Cipher.ENCRYPT_MODE;
private final static int DECRYPT_MODE = Cipher.DECRYPT_MODE;
private static final String CIPHER_3DES = "DESede/ECB/PKCS5Padding";
private static String ALGORITHM_3DES = "DESede";
/**
* @param args
*/
public static void main(String[] args) {
String key = "012345678901234567890123";
String value = "13245678:konstan:20061214";
System.out.println(value.length());
byte[] b64encResult;
try {
BASE64Encoder encoder = new BASE64Encoder();
BASE64Decoder decoder = new BASE64Decoder();
System.out.println("String="+value);
b64encResult = encryptValue(value, key);
System.out.print("encoded byte[ ]");
System.out.println(b64encResult);
String b64value = encoder.encodeBuffer(b64encResult);
System.out.println("BASE64="+b64value);
byte[] result = decoder.decodeBuffer(b64value);
if(Arrays.equals(b64encResult, result)){
System.out.println("both byte[ ] are identical!!!");
}
System.out.print("encoded byte[ ]");
System.out.println(result);
byte[] decResult = decryptValue(result, key);
System.out.println("original value" + decResult);
} catch (Exception e) {
e.printStackTrace();
}
}
public static byte[] encryptValue(final String value,final String key) throws Exception{
return doCrypt2(ENCRYPT_MODE, value.getBytes(), key, CIPHER_3DES, ALGORITHM_3DES);
}
public static byte[] decryptValue(final byte[] value,final String key) throws Exception {
return doCrypt2(DECRYPT_MODE, value, key, CIPHER_3DES, ALGORITHM_3DES);
}
private static byte[] doCrypt2(final int signal, final byte[] value,final String key,final String crypAlg,final String cryptStd) throws Exception {
byte[] theKey = key.getBytes();
SecretKeySpec ks = new SecretKeySpec(theKey,cryptStd);
Cipher cf = Cipher.getInstance(crypAlg);
cf.init(signal,ks);
return cf.doFinal(value);
}
}
Can it be the key? Is it mandatory to have it as a byte array, too?
Regards
Poncho
Message was edited by:
Poncho1975
# 5
The name of your variable 'b64encResult' indicates that "you don't get it" but you are still basically doing it right.
Just printing out a reference to an array as in
System.out.println(b64encResult);
does not print the content of the array, just a pseudo reference to the array. If you want to print the content then use
Arrays.toString(...) or Base64 encode it or Hex encode it.
Symetric algorthm keys are essentially just arrays of bytes and your conversion of a String to key bytes using
byte[] theKey = key.getBytes();
will work much of the time BUT the key you get will depend on the default character encoding of your platform and Locale. Key are best specified as byte arrays so that there can be no encoding problem.
Of course you can always specify the key as a Base64 (or Hex) encoded String and then convert to a byte array before use. This approach is reliable.