Java using AES 256 and 128 Symmetric-key encryption - java

I am new in cipher technology. I found this code to do Symmetric Encryption.
byte[] key = //... secret sequence of bytes
byte[] dataToSend = ...
Cipher c = Cipher.getInstance("AES");
SecretKeySpec k = new SecretKeySpec(key, "AES");
c.init(Cipher.ENCRYPT_MODE, k);
byte[] encryptedData = c.doFinal(dataToSend);
Its working. Here I can use my own password. And thats what exactly I needed. But I dont know how to do 128 or 256 Symmetric Enctryption.
How can I use 128 and 256 key into my code ?

Whether AES uses 128 or 256 bit mode depends on size of your key, which must be 128 or 256 bits long. Typically you don't use your password as a key, because passwords rarely have exact length as you need. Instead, you derive encryption key from your password by using some key derivation function.
Very simple example: take MD5 of your password to get 128-bit key. If you want 256-bit key, you can use SHA-256 to get 256-bit hash of your password. Key-derivation functions usually run this hashing several hundreds time and use extra salt as well. Check out http://en.wikipedia.org/wiki/Key_derivation_function for details.
Also note: to run encryption stronger than 128-bit you will need to download and install 'Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files 6' from http://www.oracle.com/technetwork/java/javase/downloads/index.html.

The Answer for 128 bit
The following method is to encrypt a string (valueEnc) with AES encryption:
private static final String ALGORITHM = "AES";
public String encrypt(final String valueEnc, final String secKey) {
String encryptedVal = null;
try {
final Key key = generateKeyFromString(secKey);
final Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.ENCRYPT_MODE, key);
final byte[] encValue = c.doFinal(valueEnc.getBytes());
encryptedVal = new BASE64Encoder().encode(encValue);
} catch(Exception ex) {
System.out.println("The Exception is=" + ex);
}
return encryptedVal;
}
The next method will decrypt the AES encrypted string (encryptedVal):
public String decrypt(final String encryptedValue, final String secretKey) {
String decryptedValue = null;
try {
final Key key = generateKeyFromString(secretKey);
final Cipher c = Cipher.getInstance(ALGORITHM);
c.init(Cipher.DECRYPT_MODE, key);
final byte[] decorVal = new BASE64Decoder().decodeBuffer(encryptedValue);
final byte[] decValue = c.doFinal(decorVal);
decryptedValue = new String(decValue);
} catch(Exception ex) {
System.out.println("The Exception is=" + ex);
}
return decryptedValue;
}
The secKey is a 128-bit key, which is encoded in the BASE64Encoder. The BASE64Decoder in the following method generates an appropriate 128-bit key
private Key generateKeyFromString(final String secKey) throws Exception {
final byte[] keyVal = new BASE64Decoder().decodeBuffer(secKey);
final Key key = new SecretKeySpec(keyVal, ALGORITHM);
return key;
}

You can use a simple KeyGenerator object like this:
KeyGenerator generator = KeyGenerator.getInstance("AES/CTR/PKCS5PADDING");
generator.init(128);
SecretKey key = generator.generateKey();
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, key);
...

From Java's docs for Cipher.init(...):
public final void init(int opmode,
Key key)
Throws:
InvalidKeyException - if the given key is inappropriate for initializing
this cipher, or if this cipher is
being initialized for decryption and
requires algorithm parameters that
cannot be determined from the given
key, or if the given key has a keysize
that exceeds the maximum allowable
keysize (as determined from the
configured jurisdiction policy files).
To me, this means that, as Martijn Courteaux said in his comment, you should use a key of 256 bits (i.e. initialize the SecretKeySpec with a byte array containing 32 bytes), and the cipher will accept it and use it, or reject it and throw an exception if its size is not acceptable.
If you get an exception, it's probably because you have not installed the unlimited strength crypto files, (the default JDK install allows 128 bit keys as documented in this crypto spec document). Download unlimited strength crypto package here.

public class CipherUtils
{
private static byte[] key = {
0x74, 0x68, 0x69, 0x73, 0x49, 0x73, 0x41, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x4b, 0x65, 0x79
};//"thisIsASecretKey";
public static String encrypt(String strToEncrypt)
{
try
{
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
final SecretKeySpec secretKey = new SecretKeySpec(key, "AES");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
final String encryptedString = Base64.encodeBase64String(cipher.doFinal(strToEncrypt.getBytes()));
return encryptedString;
}
catch (Exception e)
{
e.printStackTrace();
}
return null;
}
}

Related

Encryption Decryption issue with Cipher in java

Hi I am facing a decryption problem. The decrypted value is not matching the original one.
Here is my logic for encryption :
public byte[] encrypt(String plainText) {
byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
SecureRandom secRandom = new SecureRandom();
secRandom.nextBytes(iv);
Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
}
And this is my logic for Decryption
public String decrypt(byte[] cipherText) {
byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
SecureRandom secRandom = new SecureRandom();
secRandom.nextBytes(iv);
Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), StandardCharsets.UTF_8);
}
Encryption options :
ENCRYPTION_ALGORITHM = "DESede";
ENCRYPTION_INSTANCE = "DESede/CBC/PKCS5Padding";
Integer ENCRYPTION_PARAM_SIZE = 8;
This is how I am trying to verify :
public static void main(String[] args){
Long value = 9123456L;
String strval = value.toString();
byte[] encryptedVal = encrypt(strval);
String decryptedVal = decrypt(encryptedVal);
System.out.println("Original value : " +strval);
System.out.println("Encrypted value : " +encryptedVal.toString());
System.out.println("Decrypted value : " +decryptedVal);
System.out.println("Final value : " +Long.parseLong(decryptedVal));
}
What I need to do here to make it work.
Note : The above code is working fine if I use the below logic without SecureRandom :
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(new byte[ENCRYPTION_PARAM_SIZE]));
Like others have said, your issue is that the IV you are using for encryption is different than the one you are using for decryption. The IV is not sensitive (from a confidentiality point of view) so it can be transmitted alongside the ciphertext. Some applications prepend the IV to the ciphertext while others use more standard formats such as CMS Encrypted Data. Since you are using Java, you can generate CMS Encrypted Data structures using Bouncy Castle's CMSEncryptedDataGenerator class.
There are two other issues with your code:
Choice of cryptographic primitives
Key management
Cryptographic Primitives
The cryptographic transformation you are using is "DESede/CBC/PKCS5Padding". Unless you have a good reason to use DES/3DES, you should consider switching to AES. Additionally, I would recommend using an AEAD such as AES-GCM or AES-GCM-SIV.
Key Management
All the cryptography in the world doesn't mean much if the keys aren't managed correctly. From your code it looks like you are constructing the SecretKeySpec object out of the key's bytes. If you can, try to use an hardware security module (HSM) or a key management system (KMS) where the key never gets exported from the tamper bounds of the device in plaintext format. AWS KMS, Azure Key Vault, and Google KMS all offer very affordable pricing for this. Of course, there are other options as well.
In the decrypt function you generate a random Initialization Vector (IV), so this won't ever work.
You need to store the IV from the encrypt function and provide it as an input to the decrypt function.
Here's an example:
public byte[] encryptAndDecrypt(String plainText) {
byte iv[] = new byte[ENCRYPTION_PARAM_SIZE];
SecureRandom secRandom = new SecureRandom();
secRandom.nextBytes(iv);
Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(iv));
byte[] cipherText=cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
return decrypt(cipherText, iv)
}
public String decrypt(byte[] cipherText, byte[] iv) {
Cipher cipher = Cipher.getInstance(ENCRYPTION_INSTANCE);
SecretKeySpec key = new SecretKeySpec(fixSecret(encryptionKey), ENCRYPTION_ALGORITHM);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(cipherText);
}
Note that by definition the IV should be random but shouldn't be treated as a secret so you can store it as plain data without any protection.
The idea behind the IV is to randomize the cipher text so if you're not using IV, or using a constant IV, and encrypt "X", cipher text is "Y", you could easily reverse the cipher text into plain text, while with random IV the cipher text is different every time.

How to properly recreate SecretKey from string

I'm trying to make an encryption-decryption app. I've got two classes - one with functions to generate the key, encrypt and decrypt, second one for JavaFX GUI. In the GUI class I've got 4 textareas: 1st to write text to encrypt, 2nd for encrypted text, 3rd for the key (String encodedKey = Base64.getEncoder().encodeToString(klucz.getEncoded());) and 4th for decrypted text.
The problem is, I am not able to decrypt the text. I'm trying to recreate the SecretKey like this:
String encodedKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz = new SecretKeySpec(decodedKey, "DESede");
When I encrypt the key looks like this: com.sun.crypto.provider.DESedeKey#4f964d80 and when I try to recreate it: javax.crypto.spec.SecretKeySpec#4f964d80 and I'm getting javax.crypto.IllegalBlockSizeException: Input length must be multiple of 8 when decrypting with padded cipher
Here is my 1st class:
public class Encryption {
public static SecretKey generateKey() throws NoSuchAlgorithmException {
Security.addProvider(new com.sun.crypto.provider.SunJCE());
KeyGenerator keygen = KeyGenerator.getInstance("DESede");
keygen.init(168);
SecretKey klucz = keygen.generateKey();
return klucz;
}
static byte[] encrypt(byte[] plainTextByte, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, klucz);
byte[] encryptedBytes = cipher.doFinal(plainTextByte);
return encryptedBytes;
}
static byte[] decrypt(byte[] encryptedBytes, SecretKey klucz)
throws Exception {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, klucz);
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);
return decryptedBytes;
}
}
edit
btnEncrypt.setOnAction((ActionEvent event) -> {
try {
String plainText = textAreaToEncrypt.getText();
SecretKey klucz = Encryption.generateKey();
byte[] plainTextByte = plainText.getBytes();
byte[] encryptedBytes = Encryption.encrypt(plainTextByte, klucz);
String encryptedText = Base64.getEncoder().encodeToString(encryptedBytes);
textAreaEncryptedText.setText(encryptedText);
byte[] byteKey = klucz.getEncoded();
String stringKey = Base64.getEncoder().encodeToString(byteKey);
textAreaKey.setTextstringKey
} catch (Exception ex) {
ex.printStackTrace();
}
});
btnDecrypt.setOnAction((ActionEvent event) -> {
try {
String stringKey = textAreaKey.getText();
byte[] decodedKey = Base64.getDecoder().decode(encodedKey);
SecretKey klucz2 = new SecretKeySpec(decodedKey, "DESede");
String encryptedText = textAreaEncryptedText.getText();
byte[] encryptedBytes = Base64.getDecoder().decode(encryptedText.getBytes());
byte[] decryptedBytes = Encryption.decrypt(encryptedBytes, klucz2;
String decryptedText = Base64.getEncoder().encodeToString(decryptedBytes);
textAreaDecryptedText.setText(decryptedText);
} catch (Exception ex) {
ex.printStackTrace();
}
});
One of your problems is here:
String encryptedText = new String(encryptedBytes, "UTF8");
Generally, many byte sequences in cipher text are not valid UTF-8–encoded characters. When you try to create a String, this malformed sequences will be replaced with the "replacement character", and then information from the the cipher text is irretrievably lost. When you convert the String back to bytes and try to decrypt it, the corrupt cipher text raises an error.
If you need to represent the cipher text as a character string, use base-64 encoding, just as you do for the key.
The other principal problem is that you are aren't specifying the full transformation. You should specify the "mode" and "padding" of the cipher explicitly, like "DESede/ECB/PKCS5Padding".
The correct mode will depend on your assignment. ECB is generally not secure, but more secure modes add a bit of complexity that may be outside the scope of your assignment. Study your instructions and clarify the requirements with your teacher if necessary.
There are two main issues:
You should not use user entered password as a key (there are difference between them). The key must have specific size depending on the cipher (16 or 24 bytes for 3des)
Direct 3DES (DESede) is a block cipher encrypting 8 bytes at once. To encrypt multiple blocks, there are some methods defined how to do that properly. It is calls Block cipher mode.
For proper encryption you need to take care of a few more things
Creating a key from the password
Let's assume you want to use DESede (3des). The key must have fixed size - 16 or 24 bytes. To properly generate a key from password you should use PBKDF. Some people are sensitive to "must use", however neglecting this step really compromises the encryption security mainly using user-entered passwords.
For 3DES you can use :
int keySize = 16*8;
int iterations = 800000;
char[] password = "password".toCharArray();
SecureRandom random = new SecureRandom();
byte[] salt = random.generateSeed(8);
SecretKeyFactory secKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
KeySpec spec = new PBEKeySpec(password, salt, iterations, keySize);
SecretKey pbeSecretKey = secKeyFactory.generateSecret(spec);
SecretKey desSecret = new SecretKeySpec(pbeSecretKey.getEncoded(), "DESede");
// iv needs to have block size
// we will use the salt for simplification
IvParameterSpec ivParam = new IvParameterSpec(salt);
Cipher cipher = Cipher.getInstance("DESEde/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desSecret, ivParam);
System.out.println("salt: "+Base64.getEncoder().encodeToString(salt));
System.out.println(cipher.getIV().length+" iv: "+Base64.getEncoder().encodeToString(cipher.getIV()));
byte[] ciphertext = cipher.doFinal("plaintext input".getBytes());
System.out.println("encrypted: "+Base64.getEncoder().encodeToString(ciphertext));
if you can ensure that your password has good entropy (is long and random enough) you may be good with a simple hash
MessageDigest dgst = MessageDigest.getInstance("sha-1");
byte[] hash = dgst.digest("some long, complex and random password".getBytes());
byte[] keyBytes = new byte[keySize/8];
System.arraycopy(hash, 0, keyBytes, 0, keySize/8);
SecretKey desSecret = new SecretKeySpec(keyBytes, "DESede");
The salt serves to randomize the output and should be used.
The output of the encryption should be salt | cipthertext | tag (not necessarily in this order, but you will need all of these for proper encryption).
To decrypt the output, you will need to split the output to salt, ciphertext and the tag.
I see zero vectors ( static salt or iv ) very often in examples from StackOverflow, but in many cases it may lead to broken ciphers revelaling key or plaintext.
The initialization vector iv is needed for block chain modes (encrypting longer input than a single block), we could use the salt from the key as well
when having the same size ( 8 bytes in our case). For really secure solution the password salt should be longer.
The tag is an authentication tag, to ensure that nobody has manipulated with the ciphertext. You could use HMAC of the plaintext or ciphertext. It is important you should use different key for HMAC than for encryption. However - I believe in your case your homework will be ok even without the hmac tag

How to encrypt data with AES 256 bit in java?

I have research many source code and find difficulty on how to specify block size in AES java? Does java support until 256 bits? I have search out php source code and it support until 256 bits AES encryption
This is the sample source code for AES encryption. Thanks all for helping me to figure out.
http://aesencryption.net/
PHP supports Rijndael with a 256 block size. AES is a subset of Rijndael with key sizes of 128, 192 and 256 bits and a block size of 128 bits. So saying that PHP supports AES with a blocksize of 256 bit is a contradiction (i.e. incorrect).
Java SE (up to and including Java 9) by Oracle only supports AES with a 128 bit block size and all (3) AES key sizes, although you need the Unlimited Crypto files to use 192 and 256 bit encryption.
To use Rijndael with 256 bit block size you could use Bouncy Castle lightweight API, the different block sizes aren't added to the Bouncy Castle provider either:
new RijndaelEngine(256)
For Android you may want to use Spongy Castle instead.
TRY IT:
public static void main(String[] args) {
String key = "1234567890ABCDEF";
try {
byte[] encrypt = encrypt("hello word",key);
System.out.println(new String(encrypt));
String decrypt = decrypt(encrypt, key);
System.out.println(decrypt);
} catch (Exception ex) {
}
}
public static byte[] encrypt(String message, String key1) throws Exception {
SecretKeySpec key = new SecretKeySpec(key1.getBytes(), "AES");
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
cipher.init(Cipher.ENCRYPT_MODE, key);
return cipher.doFinal(message.getBytes());
}
public static String decrypt(byte[] message, String key1) throws Exception {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding", "SunJCE");
SecretKeySpec key = new SecretKeySpec(key1.getBytes(), "AES");
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decryptedByte = cipher.doFinal(message);
String decryptedText = new String(decryptedByte);
return decryptedText;
}

3des with 2 different keys in java getting null

3des with 2 different keys in java getting null.
import java.security.spec.*;
import javax.crypto.*;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
public class DESedeEncryption {
public static void main(String[] args) {
SecretKey k1 = generateDESkey();
SecretKey k2 = generateDESkey();
String firstEncryption = desEncryption("plaintext", k1);
System.out.println("firstEncryption Value : "+firstEncryption);
String decryption = desDecryption(firstEncryption, k2);
System.out.println("decryption Value : "+decryption);
String secondEncryption = desEncryption(decryption, k1);
System.out.println("secondEncryption Value : "+secondEncryption);
}
public static SecretKey generateDESkey() {
KeyGenerator keyGen = null;
try {
keyGen = KeyGenerator.getInstance("DESede");
} catch (Exception ex) {
}
keyGen.init(112); // key length 56
SecretKey secretKey = keyGen.generateKey();
return secretKey;
}
public static String desEncryption(String strToEncrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, desKey);
BASE64Encoder base64encoder = new BASE64Encoder();
byte[] encryptedText = cipher.doFinal(strToEncrypt.getBytes());
String encryptedString =base64encoder.encode(encryptedText);
return encryptedString;
} catch (Exception ex) {
}
return null;
}
public static String desDecryption(String strToDecrypt, SecretKey desKey) {
try {
Cipher cipher = Cipher.getInstance("DESede/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, desKey);
BASE64Decoder base64decoder = new BASE64Decoder();
byte[] encryptedText = base64decoder.decodeBuffer(strToDecrypt);
byte[] plainText = cipher.doFinal(encryptedText);
String decryptedString= bytes2String(plainText);
return decryptedString;
} catch (Exception ex) {
}
return null;
}
private static String bytes2String(byte[] bytes) {
StringBuffer stringBuffer = new StringBuffer();
for (int i = 0; i <bytes.length; i++) {
stringBuffer.append((char) bytes[i]);
}
return stringBuffer.toString();
}
}
while i'm running the above code i'm getting null values. plz help.
output:
firstEncryption Value : jAihaGgiOzBSFwBWo3gpbw==
decryption Value : null
secondEncryption Value : null
getting error:
firstEncryption Value : ygGPwCllarWvSH8td55j/w==
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:811)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:676)
at com.sun.crypto.provider.DESedeCipher.engineDoFinal(DESedeCipher.java:
294)
at javax.crypto.Cipher.doFinal(Cipher.java:2087)
at DESedeEncryption.desDecryption(DESedeEncryption.java:145)
at DESedeEncryption.main(DESedeEncryption.java:107)
decryption Value : null
java.lang.NullPointerException
at DESedeEncryption.desEncryption(DESedeEncryption.java:130)
at DESedeEncryption.main(DESedeEncryption.java:109)
secondEncryption Value : null
Symmetric ciphers work by encrypting and decrypting with the same key, hence the name symmetric. (And for most modes also the same IV, but the IV doesn't need to be secret.) You're encrypting with one key and decrypting with an independent key which is different with overwhelming probability (i.e. it might the same once a zillion quillion eternities). That won't work.
Perhaps you are confused by the description of Triple-DES also known as 3DES DESede or TDEA. The original DES (or DEA) cipher uses a 56-bit key (in 8 bytes) which was secure in the 1960s but not now. Triple-DES was defined using DES as a building block but with a bundle of 3 keys (k1,k2,k3) which can also be treated as a combined 168-bit key (in 24 bytes); if k3=k1 the key is described as 112-bits although it is still stored as 24 bytes. Your call to KeyGenerator "DESede" .init(112) does exactly that; it generates a 24-byte bundle with k3=k1 and k2 different. For convenience in the past Triple-DES is defined to use single-DES to encrypt with k1, decrypt with k2, and encrypt with k3, and the reverse when decrypting, hence the name DES-EDE or DESede. See http://en.wikipedia.org/wiki/Triple_DES .
If you really want, you can implement Triple-DES yourself in Java using Cipher "DES" by doing E,D,E (or reverse D,E,D if used) and then wrapping the mode around that, see Java Triple DES encryption with 2 different keys . But it's much easier to just use Cipher "DESede", which it does the lot for you, treating DESede like any other symmetric cipher primitive, as answered in that question.
Also, mode ECB is dangerous. It is an exaggeration to say it is always insecure as some people do, but historically very many applications using it designed by non-experts are insecure. Unless you know much more than is evident in your question, or are following (or interfacing to) a design by someone who does, use a better established mode like CBC or CTR.

How to use three keys with triple des(3des) in Java

I found a link in stackoverflow here use-3des-encryption-decryption-in-java,but in fact the method uses only two parameter:HG58YZ3CR9" and the "IvParameterSpec iv = new IvParameterSpec(new byte[8]);"
But the most strong option of triple des could use three different key to encrypt the message.So how to do that? I find a mehond in Cipher, which use "SecureRandom" as another parameter.So is this the right way?
The first method code is below:
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("SHA-1");
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("SHA-1");
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");
}
}
As per this document, simply pass the cipher a key that is 168 bits long.
Keysize must be equal to 112 or 168.
A keysize of 112 will generate a Triple DES key with 2 intermediate keys, and a keysize of 168 will generate a Triple DES key with 3 intermediate keys.
Your code seems to do something questionable to make up for the fact that the output of MD5 is only 128 bits long.
Copy-pasting cryptographic code off the internet will not produce secure applications. Using a static IV compromises several reasons why CBC mode is better than ECB. If you are using a static key, you should probably consider generating random bytes using a secure random number generator instead of deriving the key from a short ASCII string. Also, there is absolutely no reason to use Triple DES instead of AES in new applications.
In principle, the for-next loop to generate the DES ABA key does seem correct. Note that you can provide DESede with a 16 byte key from Java 7 onwards, which amounts to the same thing.
That said, the code you've shown leaves a lot to be desired:
I is not secure:
the key is not generated by a Password Based Key Derivation Function (PBKDF) using the (password?) string
the key is composed of two keys instead of three (using a triple DES or TDEA with an ABA key)
the IV is set to all zero's instead of being randomized
the "password" string is too short
Furthermore the following code mistakes can be seen:
using new sun.misc.BASE64Encoder() which is in the Sun proprietary packages (which can be removed or changed during any upgrade of the runtime)
throwing Exception for platform exceptions and runtime exceptions (not being able to decrypt is handled the same way as not being able to instantiate the Cipher)
requesting 24 bytes instead of 16 within the Arrays.copyOf() call (which seems to return 24 SHA-1 output while there are only 20 bytes)
To generate a 3DES 24 byte (168 bits used) DES ABC key from a password (like) String you should use PBKDF-2. Adding an authentication tag is also very important if man-in-the-middle attacks or padding oracle apply. It would be much secure and much more practical to upgrade to AES if you can control the algorithms being used as well.

Categories