Java News Tips Software
Java News Tips Software | Contact | Facebook | Twitter RSS

Data Encryption Decryption using AES Algorithm, Key and Salt with Java Cryptography Extension

In this tutorial we will implement a full data encryption decryption cycle with Java (only data, not file encryption); encrypt some data using a secret key, salt and iterations and decrypt using the same parameters. We are using the Java Cryptography Extension (JCE) for data encryption/decryption operations. This extension is available in Java 1.4.2 and above; you will have to manually download it for older versions (here). Java supports a number of of encryption algorithms, however we will demonstrate only AES algorithm (the Advanced Encryption Standard) usage.

Why should we encrypt data?

Encryption and Decryption are highly important security steps; now there are file and data encryption software as well. Data encryption is the mechanism of converting a message (plain text) into some other text (called ciphertext) so that readers can not understand the original message; however some authorized party can understand that ciphertext using the method called Decryption which converts the ciphertext into original message. As many software applications store sensitive personal data in databases, encryption has become a must. Ideally nobody (including the software developers) should not be able to view these user specific real data.

Simple Data Encryption/Decryption Example with AES

For encryption we must use a secret key along with an algorithm. In the following example we use an algorithm called AES 128 and the bytes of the word "ThisIsASecretKey" as the secret key (the best secret key we found in this world). AES algorithm can use a key of 128 bits (16 bytes * 8); so we selected that key.

package org.kamal.crypto;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher; 
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;

public class SimpleProtector {

    private static final String ALGORITHM = "AES";
    private static final byte[] keyValue = 
        new byte[] { 'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y' };

     public static String encrypt(String valueToEnc) throws Exception {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.ENCRYPT_MODE, key);
        byte[] encValue = c.doFinal(valueToEnc.getBytes());
        String encryptedValue = new BASE64Encoder().encode(encValue);
        return encryptedValue;
    }

    public static String decrypt(String encryptedValue) throws Exception {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.DECRYPT_MODE, key);
        byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedValue);
        byte[] decValue = c.doFinal(decordedValue);
        String decryptedValue = new String(decValue);
        return decryptedValue;
    }

    private static Key generateKey() throws Exception {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);
        // SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        // key = keyFactory.generateSecret(new DESKeySpec(keyValue));
        return key;
    }
}

We use "generateKey()" method to generate a secret key for AES algorithm with a given key. You can change the used algorithm by changing this Key generation; the commented out code shows the use of DES algorithm (Data Encryption Standard). Following is a simple class to test the above implementation.

package org.kamal.crypto;

public class TestSimpleProtector {

    public static void main(String[] args) throws Exception {

        String password = "mypassword";
        String passwordEnc = SimpleProtector.encrypt(password);
        String passwordDec = SimpleProtector.decrypt(passwordEnc);

        System.out.println("Plain Text : " + password);
        System.out.println("Encrypted : " + passwordEnc);
        System.out.println("Decrypted : " + passwordDec);
    }
}

Following is the output we got from above test; so you clearly see that the original text is reproduced after the decryption operation.

Plain Text : mypassword
Encrypted : sBhCap4urE50a/dGuhNgrw==
Decrypted : mypassword

The encrypted value is not simply related to the plain text, so it provides some security to the password.

The Risk with Simple Protector

When you use the above mentioned method to encrypt all passwords in your database, it makes an attacker's task easier. Since the same key is used, if an attacker find a way to get the plain text, the he can use that same method to get all the other plain-texts in the database in minutes.

Use Salt and iterations to improve

To make the attackers job harder we can use two methods; called adding Salt and using Iterations.
A Salt is another plain-text appended to the given plain text, before generating the encrypted value. We use a unique Salt per each plain-text so that the value used to encrypt is getting much stronger making it harder for an attacker to guess with a brute force or dictionary attack. Following class shows the improved version of SimpleProtector class.

package org.kamal.crypto;

import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import sun.misc.*;

public class Protector {

    private static final String ALGORITHM = "AES";
    private static final int ITERATIONS = 2;
    private static final byte[] keyValue = 
        new byte[] { 'T', 'h', 'i', 's', 'I', 's', 'A', 'S', 'e', 'c', 'r', 'e', 't', 'K', 'e', 'y'};

    public static String encrypt(String value, String salt) throws Exception {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);  
        c.init(Cipher.ENCRYPT_MODE, key);
  
        String valueToEnc = null;
        String eValue = value;
        for (int i = 0; i < ITERATIONS; i++) {
            valueToEnc = salt + eValue;
            byte[] encValue = c.doFinal(valueToEnc.getBytes());
            eValue = new BASE64Encoder().encode(encValue);
        }
        return eValue;
    }

    public static String decrypt(String value, String salt) throws Exception {
        Key key = generateKey();
        Cipher c = Cipher.getInstance(ALGORITHM);
        c.init(Cipher.DECRYPT_MODE, key);
  
        String dValue = null;
        String valueToDecrypt = value;
        for (int i = 0; i < ITERATIONS; i++) {
            byte[] decordedValue = new BASE64Decoder().decodeBuffer(valueToDecrypt);
            byte[] decValue = c.doFinal(decordedValue);
            dValue = new String(decValue).substring(salt.length());
            valueToDecrypt = dValue;
        }
        return dValue;
    }

    private static Key generateKey() throws Exception {
        Key key = new SecretKeySpec(keyValue, ALGORITHM);
        // SecretKeyFactory keyFactory = SecretKeyFactory.getInstance(ALGORITHM);
        // key = keyFactory.generateSecret(new DESKeySpec(keyValue));
        return key;
    }
}

You need to store the clear Salt with the stored encrypted value since the salt is required at the decrypt operation. We just used a sentence as the 'salt', but you should use a random letters/numbers for that to avoid any dictionary attacks.

package org.kamal.crypto;

public class TestProtector {

    public static void main(String[] args) throws Exception {
        String password = "mypassword";
        String salt = "this is a simple clear salt";
        String passwordEnc = Protector.encrypt(password, salt);
        String passwordDec = Protector.decrypt(passwordEnc, salt);

        System.out.println("Salt Text : " + salt);
        System.out.println("Plain Text : " + password);
        System.out.println("Encrypted : " + passwordEnc);
        System.out.println("Decrypted : " + passwordDec);
    }
}

The output from the above class looks as follows.

Salt Text : this is a simple clear salt
Plain Text : mypassword
Encrypted : GX8jEr04o6PC+IE+f6DXq+zuRiZ2nsSL+UYxYRh9vK28xt/GrLqnbrP3hkXSW5S3jNgzXkrLSmXm
fwRw+eBMWQJO+8tlSEE9D2Y9qEzowX18s4W81U/D9JL3JX7uJtwp
Decrypted : mypassword

When using Salt, you can store each (clear text) Salt in the database per each encrypted text. It would be better if we pass the secrete key while initializing or instantiating the (Protector) encryption class rather than hard coding it in the class itself (for simplicity we used a hard coded key for this tutorial).

Related: Encrypted vs Hashed Passwords - Which is better?

Labels: , , ,


45 Comments

  1. The examples seem good, but the sun.* packages should not be used. You should have seen the warning when you compiled your examples. I believe the Apache commons has a codec library with Base64 conversion.
  2. Hi Jose,

    Thanks for your comment. Yes, that's correct. We can go with Apache commons jar instead of the sun specific code.
  3. Don't use low level libraries. Several of them are not safe enough, and extending them is usually not good enough. For instance, here you can see a bug on sun's jvm implementation of MessageDigest.isEquals. It is vulnerable to timing attacks, which, as Twitter and 37Signals realized recently, is actually practical over the web.

    This recent google tech talk presentation dives further into security implementation considerations. They are so easy to get wrong that even expert groups get them wrong sometimes.

    In a nutshell: it is better to use high-level libraries like keyczar than trying to roll you own.
  4. Hi metaphysicaldeveloper ,

    Thanks for sharing information with us.
  5. may I ask?

    salt and IV, is it equal?

    and is it using CBC?

    sorry I'm a newbie
  6. hi,
    thanks a lot. this tutorial is really helpful.
    could you please writing a tutorial about RSA algorithm as well in java.
    your explanation is really helpful.
  7. This comment has been removed by a blog administrator.
  8. You have done a marvelous job! I am really inspired with your work.
  9. bestr best bestr

    best
  10. i like this!
  11. Access restriction: The type BASE64Decoder is not accessible due to restriction on required library C:\Program Files\Java\jdk1.6.0_03\jre\lib
    \rt.jar
  12. Harshana,

    Have a look at http://lkamal.blogspot.com/2008/09/eclipse-access-restriction-on-library.html.
  13. Great. Thanks for sharing. It's really important to encrypt data especially if the data is coming from the server. I also use encryption data tools for protection in our office server.

    Data Encryption
  14. hey Kamal,

    I tried your code yesterday, it worked.



    Thanks for sharing.
  15. Excellent example thanks for sharing!
  16. Anonymous Anonymous on June 11, 2012 1:48 PM  
    I think, as a novice speaking, the example shown is great. It is clear, however I wonder what would the apache alternative look like? Again thanks for sharing
  17. Anonymous Anonymous on June 13, 2012 12:11 PM  
    hey what is the package org.kamal.crypto???
  18. @17: That is the package of these java classes of the examples. Hope that is clear with you now?
  19. Instead of 'sun.misc.*', use 'org.apache.commons.codec.binary.Base64' to avoid access restriction or following message while compiling: "BASE64Encoder is Sun proprietary API and may be removed in a future release"
  20. Anonymous Anonymous on June 22, 2012 10:29 AM  
    teri maa ki kamal bc.... package tera baap upload krega
  21. There is also a javax package that can be used instead of the non-portable sun library:

    import javax.xml.bind.DatatypeConverter;
    //...
    eValue = DatatypeConverter.printBase64Binary(encValue);
    //...
    byte[] decordedValue = DatatypeConverter.parseBase64Binary(valueToDecrypt)
  22. @20 - Would you mind putting this in English...

    @21 Fadookie - Thanks for pointing this out.
  23. Anonymous Anonymous on July 24, 2012 11:30 PM  
    Superb!!
    Thanks a lot for sharing!
  24. Anonymous Anonymous on August 07, 2012 2:59 PM  
    Having messed around with encryption examples in java on the web for a few hours getting no where, but intrigued with the many errors, this was the article that gave me some working code to expand on.
    From there I was able to investigate iv vectors and such esoteric stuff.
    Thanks, nice simple example, also informative.

    Ian
  25. Anonymous Anonymous on August 21, 2012 11:30 PM  
    Thanks you very much for your code. It helps a lot.

    Rafael
  26. Anonymous Anonymous on October 04, 2012 5:50 PM  
    Newbie question - if the salt is appended to the plaintext, why is it required for decryption? Surely you an still decrypt and what you get is the plaintext concatenated with the salt?

    As I say I may just be being really dense here...

    Thanks

    Annoyamous
  27. Anonymous Anonymous on October 15, 2012 9:01 PM  
    Dear sir,

    I tried to encrypt my password but it shows error on this line BASE64Encoder().encode(encValue); their is a restriction in rt.jar. could u plz tell me what should i do to remove it.
  28. Thanks a lot Kamal.. It worked .. Pls provide comments on each line so that we can understand the meaning of Each Line easily..
  29. Anonymous Anonymous on January 28, 2013 5:53 PM  
    How Can I Use your Code for Decrypt a key ?!

    Can you explain me please :(
  30. kamal sir i get error like this ..


    abi@abi-laptop:~$ javac TestProtector.java
    TestProtector.java:8: cannot find symbol
    symbol : variable Protector
    location: class org.kama.crypto.TestProtector
    String passwordEnc = Protector.encrypt(password, salt);
    ^
    TestProtector.java:9: cannot find symbol
    symbol : variable Protector
    location: class org.kama.crypto.TestProtector
    String passwordDec = Protector.decrypt(passwordEnc, salt);
    ^
    2 errors

  31. am a ubuntu user
  32. Anonymous Anonymous on February 09, 2013 2:07 PM  
    Hi Kamal

    Thnx for this great introduction! I used it to realize a secure communication between an Android Application and a Glassfish Server. Therefore I had to choose another Base64-lib (Base64Coder) which runs on both systems.
    Now the issue: If I run it on a simple Java Desktop Machine it works fine, but my both systems are producing different AES-Codes even the sourcecode is the same. I think that the generateKey()-Method returns different values. What Do you think? Is there a possibility to set a static Key?
  33. @32 I am not quite sure what the exact issue is. But as you may be already aware Android runs a Dalvik VM rather than a JRE. So that may be the reason for your issue.
  34. Can i have this on C or C++??
  35. This comment has been removed by a blog administrator.
  36. @30 & @31 Seems you have compiled the class inside a package without going through the package.

    Please have a look at this where I have compiled classes inside packages.
  37. This comment has been removed by a blog administrator.
  38. This comment has been removed by a blog administrator.
  39. This comment has been removed by a blog administrator.
  40. Anonymous Anonymous on December 04, 2013 5:03 AM  
    This comment has been removed by a blog administrator.
  41. Anonymous Anonymous on December 20, 2013 9:30 AM  
    This comment has been removed by a blog administrator.
  42. Anonymous Anonymous on March 09, 2014 7:26 PM  
    This comment has been removed by a blog administrator.
  43. Thank you very much
  44. Thanks!! Your code has worked for me too. However I am going through another issue where I have to encrypt using Java and decryption will happen using C#. Could you please guide on the decryption part.
    1. Is this possible?
    2. There are two inputs required in C# i.e. Key and IV
    3. what will be the value of IV even if I assign the same value of Key as used in Java program
    Appreciate your help.
  45. Anonymous Anonymous on March 31, 2015 9:20 AM  

    Thanks extremely practical. Will share website with my friends.
ABOUT AUTHOR
Page Views :
Email :
PREVIOUS ARTICLES
Select Month:
TOP
Free counter and web stats