Tuesday, September 09, 2008

Java Triple DES example

I had to do this recently, and the examples that I found through Google seemed a little lacking. So a pointer to others:

import static org.junit.Assert.assertEquals;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;
import org.apache.commons.codec.binary.Hex;
import org.junit.Test;
/**
* Test that our encryption is working as per the 3rd party spec we're working to.
*
* @author jabley
*
*/
public class TripleDESEncryptionTest {
/*
* Hopefully the documentation that you're using can give you some suitable test data to fill in
* these constants.
*/
private static final String PLAIN_TEXT = "Some-plain-text-message-to-be-symetrically-encrypted";
private static final String CIPHER_TEXT = "someCipherText";
private static final String SHARED_KEY = "Some-Shared-Secret";
/**
* Test that we are correctly encrypting the content as per their spec.
*
* @throws Exception
* if there was a problem
*/
@Test
public void encryptionWorksOkUsingJCE() throws Exception {
String algorithm = "DESede";
String transformation = "DESede/CBC/PKCS5Padding";
byte[] keyValue = Hex.decodeHex(SHARED_KEY.toCharArray());
DESedeKeySpec keySpec = new DESedeKeySpec(keyValue);
/* Initialization Vector of 8 bytes set to zero. */
IvParameterSpec iv = new IvParameterSpec(new byte[8]);
SecretKey key = SecretKeyFactory.getInstance(algorithm).generateSecret(keySpec);
Cipher encrypter = Cipher.getInstance(transformation);
encrypter.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] input = PLAIN_TEXT.getBytes("UTF-8");
byte[] encrypted = encrypter.doFinal(input);
assertEquals("Ensure that we have encrypted the token correctly", CIPHER_TEXT, new String(Hex
.encodeHex(encrypted)).toUpperCase());
Cipher decrypter = Cipher.getInstance(transformation);
decrypter.init(Cipher.DECRYPT_MODE, key, iv);
byte[] decrypted = decrypter.doFinal(Hex.decodeHex(CIPHER_TEXT.toCharArray()));
assertEquals(PLAIN_TEXT, new String(decrypted, "UTF-8"));
}
}
view raw gistfile1.pde hosted with ❤ by GitHub


UPDATE

Changed to use a gist on github. That will probably dilute the search ranking of this post (which is reasonably popular) but gives me nicer formatting!

15 comments:

Anonymous said...

Very Helpful! Thanks James.

Anand Koppal said...

thanks a lot..it was great

Tahir Akram said...

Hi;

Will you please post the import section of this code too. Like I am getting compilation exception for these PLAIN_TEXT Hex CIPHER_TEXT SHARED_KEY.

James Abley said...

@Tahir: Hex is from Apache Commons Codec.

The constants will vary according to your needs. SHARED_KEY is what you and the 3rd party that you are exchanging data with have agreed. PLAIN_TEXT and CIPHER_TEXT are hopefully tests that have been similarly agreed / defined in sample documentation. If you don't have any, then I would suggest defining some, so that with the common shared key, the String "Hello World" results in the cipher text ????.

tieuduong said...

I must try to encrypt a password: 888888 by 3DES.
Key is a String like : 256EE615CB64085E0E1386895E5E6BC28A621F7C314016DC
and then after encrypting password with this key is : D2C9AA1103116499
But I search and try many times, but don't have any associated with it. Can u help me ?

Glenn Grabbe said...

Great job dude! This was exactly what I was looking for.

James Abley said...

@jundat: I get a different result. In your case, do you know whether you should be using a particular initialization vector?

Vijay said...

@James - It was a very helpful one. Here is the complete class for the same. (just adding upto the example).

import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.spec.InvalidKeySpecException;

import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESedeKeySpec;
import javax.crypto.spec.IvParameterSpec;

public class CryptoUtil {

private Cipher ecipher;
private Cipher dcipher;
private String algorithm = "DESede";
private String transformation = "DESede/CBC/PKCS5Padding";
private String keyPhrase = "123456789012345678901234"; //your keyphrase 24 bit
private SecretKey key;
private IvParameterSpec iv;

private static CryptoUtil cryptoUtil;

public static CryptoUtil getInstance() { //Singleton
if (cryptoUtil == null)
cryptoUtil = new CryptoUtil();

return cryptoUtil;
}

private CryptoUtil() {
try {
DESedeKeySpec keySpec = new DESedeKeySpec(keyPhrase.getBytes());
key = SecretKeyFactory.getInstance(algorithm).generateSecret(keySpec);
iv = new IvParameterSpec(new byte[8]);
ecipher = Cipher.getInstance(transformation);
dcipher = Cipher.getInstance(transformation);
ecipher.init(Cipher.ENCRYPT_MODE, key, iv);
dcipher.init(Cipher.DECRYPT_MODE, key, iv);

} catch (InvalidKeySpecException e) {
} catch (javax.crypto.NoSuchPaddingException e) {
} catch (java.security.NoSuchAlgorithmException e) {
} catch (java.security.InvalidKeyException e) {
} catch (InvalidAlgorithmParameterException e) {
}
}


@SuppressWarnings({"restriction", "hiding"})
public String encrypt(String str) {
try {
// Encode the string into bytes using utf-8
byte[] utf8 = str.getBytes("UTF8");

// Encrypt
byte[] enc = ecipher.doFinal(utf8);

// Encode bytes to base64 to get a string
return new sun.misc.BASE64Encoder().encode(enc);
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (java.io.IOException e) {
}
return null;
}

@SuppressWarnings("restriction")
public String decrypt(String str) {
try {
// Decode base64 to get bytes
byte[] dec = new sun.misc.BASE64Decoder().decodeBuffer(str);

// Decrypt
byte[] utf8 = dcipher.doFinal(dec);

// Decode using utf-8
return new String(utf8, "UTF8");
} catch (javax.crypto.BadPaddingException e) {
} catch (IllegalBlockSizeException e) {
} catch (UnsupportedEncodingException e) {
} catch (java.io.IOException e) {
}
return null;
}
}

James Abley said...

@Vijay - I'll probably update this post to use github for better code formatting. You might want to watch out for the sun.misc imports that you're using though.

Oleksandr said...

In Windows there is algId ALG_SID_3DES_112
Is it the same algorithm as "DESede" in Java?
I think not.. Could you help me with algorithm id in Java that is exactly the same as ALG_SID_3DES_112?

Thanks

James Abley said...

@Oleksandr: does the code as is not work on Windows for you? I don't have access to a Windows machine myself, but I think it should work?

Santhros ibn Shinu said...

@Jim:

Many thanks, really.

I feel so thankful, that if you had the Starbucks plugin, I would give you a coffee, pal.

sudha said...

HI;

I am facing some problem while encoding and decoding the data. The requirement is Triple DES algorithm in CBC mode and right padding with ‘0’. I was developed the Code based in the CBC algorithm base.
My Code :
private static Key genarateKey() throws Exception {
final Key key;
final String UNICODE_FORMAT = "UTF8";
final String DESEDE_ENCRYPTION_SCHEME = "DESede";
final String myEncryptionKey = "CF54F07BF324646BC5D3D62A651B21D20BD322EBAE238985";
key = SecretKeyFactory.getInstance(DESEDE_ENCRYPTION_SCHEME).generateSecret(new DESedeKeySpec(myEncryptionKey.getBytes(UNICODE_FORMAT)));
return key;
}
public static byte[] encryptData(String message,byte cipherIv[]) throws Exception {
final Key key = genarateKey();
Cipher cipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
//final IvParameterSpec iv = new IvParameterSpec(new byte[8]);
final IvParameterSpec iv = new IvParameterSpec(cipherIv);
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
byte[] bytesData = message.getBytes("UTF8");
byte[] raw = cipher.doFinal(bytesData);
/*BASE64Encoder encoder = new BASE64Encoder();
String encryptData = encoder.encode(raw);
return encryptData;*/
return raw;
}
public static void main(String args[]) throws Exception{
String dec = "2#2#purchase##1#1";
System.out.println("Before padding:"+dec);
int count = dec.length()%8;
if(count != 0 ){
while(count != 8)
{
dec=dec+"0";
count++;
}
}
System.out.println("After padding:"+dec);
int j = 0;
byte [] cipherIv = new byte[8];
for(int i =0;i<dec.length()/8;i++){
String blockMsg = dec.substring(j, (j+=8)-1);
cipherIv = TripleDSE.encryptData(blockMsg,cipherIv);
System.out.println("Encrypted data:"+cipherIv);
}
String hexValue = new String(Hex.encodeHex(cipherIv));
System.out.println("Hexadecimal Value:"+hexValue);
}
Please give the Solution for this.

madhuram said...

Hi I am new to Cryptography, I have question in triple des(3des): encryption is done in Oracle function DBMS_OBFUSCATION_TOOLKIT.DES3Encrypt , is that possible I can decrypt same in using java API.
I have only key and encrypted password to decrypt. I have tried couple of examples, when I decrypt in java , Ii am getting different value.
Is that possible if something is encrypted in oracle and can be decrypt in java

ThawZinToe said...

I wanna doing 3DES using keying option 1.What bits this program used and what keying option it used?please reply me.thanks u.