AES Encryption Given Final Block Not Properly Padded - java

I'm trying to create a class that will allow me to encrypt and decrypt strings using the AES algorithm. I'm using the exception from http://aesencryption.net/#Java-aes-encryption-example but have modified the code to suit my needs.
This is my Main.java:
public class Main {
public static void main(String[] args) {
AES256 aes = new AES256();
aes.setKey("Secret Key");
String enc = "";
enc = aes.encrypt("qwertyuiopasdfgh");
System.out.println(enc);
System.out.println(aes.decrypt(enc));
}
}
And this is my AES256.java:
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
public class AES256 {
private SecretKeySpec secretKey;
private byte[] key;
public void setKey(String key) {
MessageDigest sha = null;
try {
this.key = key.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
this.key = sha.digest(this.key);
this.key = Arrays.copyOf(this.key, 16);
secretKey = new SecretKeySpec(this.key, "AES");
} catch (UnsupportedEncodingException | NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
public String getSecretKey() {
return secretKey.toString();
}
public String getKey() {
return new String(key);
}
public String encrypt(String string) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getMimeEncoder().encodeToString(string.getBytes());
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException e) {
e.printStackTrace();
}
return null;
}
public String decrypt(String string) {
try {
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getMimeDecoder().decode(string.getBytes())));
} catch (InvalidKeyException | NoSuchAlgorithmException | NoSuchPaddingException | IllegalBlockSizeException | BadPaddingException e) {
e.printStackTrace();
}
return null;
}
}
This is the error that is being thrown:
javax.crypto.BadPaddingException: Given final block not properly padded
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:966)
at com.sun.crypto.provider.CipherCore.doFinal(CipherCore.java:824)
at com.sun.crypto.provider.AESCipher.engineDoFinal(AESCipher.java:436)
at javax.crypto.Cipher.doFinal(Cipher.java:2121)
at AES256.decrypt(AES256.java:55)
at Main.main(Main.java:13)
Does anybody know what is causing this error?

You return the original string in its base64-encoded form:
return Base64.getMimeEncoder().encodeToString(string.getBytes());
You'd want to use the cipher in there as well:
return Base64.getMimeEncoder().encodeToString(cipher.doFinal(string.getBytes()));
Independent of that, when depolying own crypto please be aware of the impacts of cipher modes, padding, etc. For example the ECB mode you're using will produce the same ciphertext from the same plaintext, e.g. the ciphertext might lead hints about the original text, as in the famous encrypted tux image:
Image Copyright: All uses are permitted provided that Larry Ewing, the owner of the original image, who requires that you mention him, his email address, lewing#isc.tamu.edu, and The GIMP, according to http://www.isc.tamu.edu/~lewing/linux/.
For more details on that, see Wikipedia's article about block cipher modes.

Related

Cannot access application.properties property in AttributeConverter implementation

I am trying to access my encrypt key stored in application.properties and set it as SECRET property in my AttributeEncryptor.
This is the class:
package com.nimesia.sweetvillas.encryptors;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.spec.SecretKeySpec;
import javax.persistence.AttributeConverter;
import java.security.InvalidKeyException;
import java.security.Key;
import java.util.Base64;
#Component
public class AttributeEncryptor implements AttributeConverter<String, String> {
private static final String AES = "AES";
#Value("${datasource.encryptkey}")
private String SECRET;
private final Key key;
private final Cipher cipher;
public AttributeEncryptor() throws Exception {
key = new SecretKeySpec(SECRET.getBytes(), AES);
cipher = Cipher.getInstance(AES);
}
#Override
public String convertToDatabaseColumn(String attribute) {
try {
cipher.init(Cipher.ENCRYPT_MODE, key);
return Base64.getEncoder().encodeToString(cipher.doFinal(attribute.getBytes()));
} catch (IllegalBlockSizeException | BadPaddingException | InvalidKeyException e) {
throw new IllegalStateException(e);
}
}
#Override
public String convertToEntityAttribute(String dbData) {
try {
cipher.init(Cipher.DECRYPT_MODE, key);
return new String(cipher.doFinal(Base64.getDecoder().decode(dbData)));
} catch (InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
throw new IllegalStateException(e);
}
}
}
datasource.encryptkey is in application.properties.
I have tried to access it from a controller and it worked. But when I try and use it in here it gives me a NullPointerException.
Hope I was clear.
Thanks in advance
The class is created before the property is set!
You should add it to the constructor instead:
private final String SECRET;
private final Key key;
private final Cipher cipher;
public AttributeEncryptor(#Value("${datasource.encryptkey}") String secret) throws Exception {
SECRET = secret;
key = new SecretKeySpec(SECRET.getBytes(), AES);
cipher = Cipher.getInstance(AES);
}

java.security.InvalidKeyException: Parameters missing

I need to store the encrypted password in DB without storing any key or salt. And I want to make sure it's safer as well even though it's a two-way encryption. So after googling a bit, I have created a sample program to test it. My idea is to create a custom JPA AttributeConverter class to manage this.
Following is the program:
import java.security.Key;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
public class Crypto {
private static final String _algorithm = "AES";
private static final String _password = "_pasword*";
private static final String _salt = "_salt*";
private static final String _keygen_spec = "PBKDF2WithHmacSHA1";
private static final String _cipher_spec = "AES/CBC/PKCS5Padding";
public static String encrypt(String data) throws Exception {
Key key = getKey();
System.out.println(key.toString());
Cipher cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = cipher.doFinal(data.getBytes());
String encryptedValue = Base64.getEncoder().encodeToString(encVal);
System.out.println("Encrypted value of "+data+": "+encryptedValue);
return encryptedValue;
}
public static void decrypt(String encryptedData) throws Exception {
Key key = getKey();
System.out.println(key.toString());
Cipher cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = Base64.getDecoder().decode(encryptedData);
byte[] decValue = cipher.doFinal(decordedValue);
String decryptedValue = new String(decValue);
System.out.println("Decrypted value of "+encryptedData+": "+decryptedValue);
}
private static Key getKey() throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(_keygen_spec);
KeySpec spec = new PBEKeySpec(_password.toCharArray(), _salt.getBytes(), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), _algorithm);
return secret;
}
public static void main(String []str) throws Exception {
String value = encrypt("India#123");
decrypt(value);
}
}
But its throwing the following exception:
javax.crypto.spec.SecretKeySpec#17111
Encrypted value of India#123: iAv1fvjMnJqilg90rGztXA==
javax.crypto.spec.SecretKeySpec#17111
Exception in thread "main" java.security.InvalidKeyException: Parameters missing
at com.sun.crypto.provider.CipherCore.init(CipherCore.java:469)
at com.sun.crypto.provider.AESCipher.engineInit(AESCipher.java:313)
at javax.crypto.Cipher.implInit(Cipher.java:802)
at javax.crypto.Cipher.chooseProvider(Cipher.java:864)
at javax.crypto.Cipher.init(Cipher.java:1249)
at javax.crypto.Cipher.init(Cipher.java:1186)
at org.lp.test.Crypto.decrypt(Crypto.java:37)
at org.lp.test.Crypto.main(Crypto.java:54)
I am not able to figure this out.
I have rectified the exception based on #Luke Park's answer I have created a JPA AttributeConverter as below:
import java.security.Key;
import java.security.spec.KeySpec;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
#Converter(autoApply=true)
public class CryptoJPAConverter implements AttributeConverter<String, String> {
private static final String _algorithm = "AES";
private static final String _password = "_pasword*";
private static final String _salt = "_salt*";
private static final String _keygen_spec = "PBKDF2WithHmacSHA1";
private static final String _cipher_spec = "AES/ECB/PKCS5Padding";
#Override
public String convertToDatabaseColumn(String clearText) {
Key key;
Cipher cipher;
try {
key = getKey();
cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = cipher.doFinal(clearText.getBytes());
String encryptedValue = Base64.getEncoder().encodeToString(encVal);
return encryptedValue;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
#Override
public String convertToEntityAttribute(String encryptedText) {
Key key;
try {
key = getKey();
Cipher cipher = Cipher.getInstance(_cipher_spec);
cipher.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = Base64.getDecoder().decode(encryptedText);
byte[] decValue = cipher.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static Key getKey() throws Exception {
SecretKeyFactory factory = SecretKeyFactory.getInstance(_keygen_spec);
KeySpec spec = new PBEKeySpec(_password.toCharArray(), _salt.getBytes(), 65536, 128);
SecretKey tmp = factory.generateSecret(spec);
SecretKey secret = new SecretKeySpec(tmp.getEncoded(), _algorithm);
return secret;
}
}
I have used the two way encryption because I need to pass the password as clear text to Java mail client.
Suggestion and comments are welcome
You are using AES/CBC/PKCS5Padding but you are not passing an IV to your cipher.init calls.
CBC mode requires a random IV for each encryption operation, you should generate this using SecureRandom each time you encrypt, and pass the value in as an IvParameterSpec. You'll need the same IV for decryption. It is common to prepend the IV to the ciphertext and retrieve when required.
On a seperate note, encrypting passwords is really quite a terrible idea. The fact that you have to ask this question at all somewhat proves that you aren't in a position to be making security-related decisions. Do yourself and your project a favour and hash your passwords instead. PBKDF2 and bcrypt are both decent ways of doing so.

What is the difference between These method below? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 5 years ago.
Improve this question
i am learning encryption in java and used these two tutorials yet This from Code2learn
and This from howtodoinjava.com
when i try to encrypt hello from these example code both gives different encrypted strings, i have tried a lot to find that what is the main difference between these examples that is causing different encrypted strings of same passwords as both are using AES ..
Code from code 2 learn
package nomad;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import javax.crypto.*;
import sun.misc.*;
public class AESencrp {
private static final String ALGO = "AES";
private static final byte[] keyValue =
new byte[] { 'T', 'h', 'e', 'B', 'e', 's', 't',
'S', 'e', 'c', 'r','e', 't', 'K', 'e', 'y' };
public static String encrypt(String Data) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.ENCRYPT_MODE, key);
byte[] encVal = c.doFinal(Data.getBytes());
String encryptedValue = new BASE64Encoder().encode(encVal);
return encryptedValue;
}
public static String decrypt(String encryptedData) throws Exception {
Key key = generateKey();
Cipher c = Cipher.getInstance(ALGO);
c.init(Cipher.DECRYPT_MODE, key);
byte[] decordedValue = new BASE64Decoder().decodeBuffer(encryptedData);
byte[] decValue = c.doFinal(decordedValue);
String decryptedValue = new String(decValue);
return decryptedValue;
}
private static Key generateKey() throws Exception {
Key key = new SecretKeySpec(keyValue, ALGO);
return key;
}
}
package nomad;
public class Checker {
public static void main(String[] args) throws Exception {
String password = "mypassword";
String passwordEnc = AESencrp.encrypt(password);
String passwordDec = AESencrp.decrypt(passwordEnc);
System.out.println("Plain Text : " + password);
System.out.println("Encrypted Text : " + passwordEnc);
System.out.println("Decrypted Text : " + passwordDec);
}
}
Code for how to do in java
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
public class AES {
private static SecretKeySpec secretKey;
private static byte[] key;
public static void setKey(String myKey)
{
MessageDigest sha = null;
try {
key = myKey.getBytes("UTF-8");
sha = MessageDigest.getInstance("SHA-1");
key = sha.digest(key);
key = Arrays.copyOf(key, 16);
secretKey = new SecretKeySpec(key, "AES");
}
catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public static String encrypt(String strToEncrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToEncrypt.getBytes("UTF-8")));
}
catch (Exception e)
{
System.out.println("Error while encrypting: " + e.toString());
}
return null;
}
public static String decrypt(String strToDecrypt, String secret)
{
try
{
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5PADDING");
cipher.init(Cipher.DECRYPT_MODE, secretKey);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDecrypt)));
}
catch (Exception e)
{
System.out.println("Error while decrypting: " + e.toString());
}
return null;
}
}
public static void main(String[] args)
{
final String secretKey = "ssshhhhhhhhhhh!!!!";
String originalString = "howtodoinjava.com";
String encryptedString = AES.encrypt(originalString, secretKey) ;
String decryptedString = AES.decrypt(encryptedString, secretKey) ;
System.out.println(originalString);
System.out.println(encryptedString);
System.out.println(decryptedString);
}
I have added the codes as one of the comments says that external links are not accessible by everyone .
Keep in mind that secretkeys and passowrd are the same in my code.
Adding my own code..
name it as code 1
AES Class
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.Base64;
public class AES {
public static SecretKeySpec secretKeySpec;
private static byte[] key;
public static String setKey(String myKey) throws Exception {
MessageDigest sha=null;
key =myKey.getBytes("UTF-8");
sha=MessageDigest.getInstance("SHA-1");
key=sha.digest(key);
key= Arrays.copyOf(key,16);
secretKeySpec=new SecretKeySpec(key,"AES");
return myKey;
}
public static String encrypt(String strToencrypt, String secret) throws Exception {
String s= setKey(secret);
System.out.println(s);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(strToencrypt.getBytes("UTF-8")));
}
public static String decryt(String strToDec , String secret) throws Exception {
setKey(secret);
Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE,secretKeySpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(strToDec)));
}
}
Main Class of above AES Class
public class Main {
public static void main(String[] args) throws Exception {
AES aes = new AES();
String ss=null;
String sd=null;
ss=aes.encrypt("hello","TheBestSecretKey");
sd=aes.decryt(ss,"TheBestSecretKey");
System.out.println("The Encrypted == " + ss);
System.out.println("The Decrypted == " + sd);
}
}
Another Code
Name It as Code 2
package sample;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;
import javax.crypto.*;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Base64;
public class Main {
private static final String ALGO ="AES";
private static String string = "TheBestSecretKey";
private static byte[] key ;
private static SecretKeySpec secretKeySpec;
public static String aesEncrypt(String en) throws Exception {
Key key = new SecretKeySpec(string.getBytes(),ALGO);
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.ENCRYPT_MODE,key);
byte[] encValue =c.doFinal(en.getBytes("UTF-8"));
String encryptedValue= new BASE64Encoder().encode(encValue);
return encryptedValue;
}
public static String aesDecrypt(String De) throws Exception{
Key key = new SecretKeySpec(string.getBytes(),"AES");
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE,key);
// return new String(c.doFinal(Base64.getDecoder().decode(De)));
byte[]decodedVlue=new BASE64Decoder().decodeBuffer(De);
byte[] decValue = c.doFinal(decodedVlue);
String deccryptedValue = new String(decValue);
return deccryptedValue;
}
public static void main(String[] args) throws Exception {
String password = "hello";
String passEnc= Main.aesEncrypt(password);
System.out.println(passEnc);
String passDec = Main.aesDecrypt(passEnc);
System.out.println(passDec);
}
}
here are the codes
now i am also posting encrypted strings
using SecretSpecKey TheBestSecretKey and hello as the string to be encrypted
CODE 1 UlQiIs/K0EwcSKbWMjcT1g==
CODE 2 hHDBo1dJYj5RcjdFA6BBfw==

ECB and CBC AES output is equal in Java

I've played around with the Java AES En/Decryption and used different cyper modes for this. Namely I use CBC and ECB. As ECB is considered to be weak, I wanted to go with CBC.
I assumed the output of the encrypted texts ob cbc and ecb are different, but they are equal. How is this possible?
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import javax.crypto.Cipher;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.binary.Hex;
import com.instana.backend.common.exception.InstanaException;
public class AESTest {
private static String pwd = "etjrgp9user9fu3984h1&(/&%$ยง";
public static void main(String[] args) throws Exception {
System.out.println("UNSECURE WITH ECB:");
String ecbEncrypt = encrypt("YOLO", cypher(Cipher.ENCRYPT_MODE, "AES"));
System.out.println("Encrypted: " + ecbEncrypt);
String ebcDecrypt = decrypt(ecbEncrypt, cypher(Cipher.DECRYPT_MODE, "AES"));
System.out.println("Decrypted: " + ebcDecrypt);
System.out.println("=====================================");
System.out.println("SECURE WITH CBC");
String cbcEncrypt = encrypt("YOLO", cypher(Cipher.ENCRYPT_MODE, "AES/CBC/PKCS5Padding"));
System.out.println("Encrypted: " + cbcEncrypt);
String cbcDecrypt = decrypt(cbcEncrypt, cypher(Cipher.DECRYPT_MODE, "AES/CBC/PKCS5Padding"));
System.out.println("Decrypted: " + cbcDecrypt);
System.out.println("=====================================");
System.out.println("Decrypting CBC with ECB");
}
public static String encrypt(String superDuperSecret, Cipher cipher) throws IOException {
try {
byte[] encrypted = cipher.doFinal(superDuperSecret.getBytes("UTF-8"));
return new String(new Hex().encode(encrypted));
} catch (Exception e) {
throw new InstanaException("Encryption of token failed.", e);
}
}
public static String decrypt(String superDuperSecret, Cipher cipher) {
try {
byte[] encrypted1 = new Hex().decode(superDuperSecret.getBytes("UTF-8"));
return new String(cipher.doFinal(encrypted1));
} catch (Exception e) {
throw new InstanaException("Encrypted text could not be decrypted.", e);
}
}
private static Cipher cypher(int mode, String method)
throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, InvalidKeySpecException,
InvalidAlgorithmParameterException {
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
KeySpec spec = new PBEKeySpec(pwd.toCharArray(), pwd.getBytes(), 128, 128);
SecretKey tmp = skf.generateSecret(spec);
SecretKey key = new SecretKeySpec(tmp.getEncoded(), "AES");
Cipher cipher = Cipher.getInstance(method);
if(method.contains("CBC")) {
byte[] ivByte = new byte[cipher.getBlockSize()];
IvParameterSpec ivParamsSpec = new IvParameterSpec(ivByte);
cipher.init(mode, key, ivParamsSpec);
}else{
cipher.init(mode, key);
}
return cipher;
}
}
Since you're passing an empty IV (you never put anything inside your ivByte), the operations performed for the first block are identical regardless of the mode being used. Encrypting a longer payload would result in the second block being chained to the first block in the case of CBC and the following blocks would be different between ECB/CBC.
You should pass a non-empty IV when using CBC mode, so the first block will be xorred with the IV, resulting in different encrypted values starting from the first block.

Cipher initialization error when running test

I wrote a utility program to do encryption and decryption using AES algorithm. Regular program works fine but when I run test with same method I am getting Cipher initialization error on doFinal method.
I did some research and some suggest to put init and doFinal in a synchronized block. I did that and still getting same exception.
I also updated US_export_policy.jar and local_policy.jar in jre7/lib/security folder as suggested by some forum. Still getting same issue.
What could be wrong in the code?
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.Hex;
import org.apache.log4j.Logger;
public class CipherUtil {
private static Logger log = Logger.getLogger(CipherUtil.class);
private static final String SECRET_KEY = "000102030405060708090A0B0C0D0E0F";
private Cipher cipher;
private SecretKeySpec secretKeySpec;
private static CipherUtil cipherUtil;
private CipherUtil() {
try {
cipher = Cipher.getInstance("AES");
} catch (NoSuchAlgorithmException | NoSuchPaddingException ex) {
log.error(ex);
}
byte[] key = null;
try {
key = Hex.decodeHex(SECRET_KEY.toCharArray());
} catch (DecoderException ex) {
log.error(ex);
}
secretKeySpec = new SecretKeySpec(key, "AES");
}
public static synchronized CipherUtil getCipherUtilObject() {
if (cipherUtil == null) {
cipherUtil = new CipherUtil();
}
return cipherUtil;
}
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
public String encrypt(String plainText) {
if (plainText == null)
return null;
String encryptedText = null;
byte[] encrypted = null;
synchronized (cipher) {
try {
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
} catch (InvalidKeyException e) {
log.error(e.getMessage());
}
}
synchronized (cipher) {
try {
encrypted = cipher.doFinal(plainText.getBytes("UTF-8"));
encryptedText = new String(Base64.encodeBase64(encrypted));
} catch (IllegalBlockSizeException | BadPaddingException
| UnsupportedEncodingException e) {
log.error(e.getMessage());
}
}
return encryptedText;
}
public synchronized String decrypt(String encryptedText) {
if (encryptedText == null)
return null;
byte[] toDecrypt = null;
byte[] original = null;
String decryptedText = null;
synchronized (cipher) {
try {
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
} catch (InvalidKeyException e) {
log.error(e.getMessage());
}
}
toDecrypt = Base64.decodeBase64(encryptedText);
synchronized (cipher) {
try {
original = cipher.doFinal(toDecrypt);
} catch (IllegalBlockSizeException | BadPaddingException e) {
log.error(e.getMessage());
}
}
try {
decryptedText = new String(original, "UTF-8");
} catch (UnsupportedEncodingException e) {
log.error(e.getMessage());
}
return decryptedText;
}
}
and the test class:
import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsEqual.equalTo;
import static org.hamcrest.core.IsNot.not;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
public class CipherTest {
CipherUtil cipherUtil;
#Before
public void setUp() {
cipherUtil = CipherUtil.getCipherUtilObject();
}
#Test
public void testEncryptDecrypt() {
String plainText = "Secret Message";
String encryptedText = cipherUtil.encrypt(plainText);
assertThat(encryptedText, not(equalTo(plainText)));
String decryptedText = cipherUtil.decrypt(encryptedText);
assertThat(decryptedText, is(equalTo(plainText)));
assertThat(encryptedText, not(equalTo(decryptedText)));
}
}
and finally this is the exception:
java.lang.IllegalStateException: Cipher not initialized
at javax.crypto.Cipher.checkCipherState(Cipher.java:1672)
at javax.crypto.Cipher.doFinal(Cipher.java:2079)
at com.testapp.util.CipherUtil.encrypt(CipherUtil.java:67)
at com.testapp.util.CipherTest.testEncryptDecrypt(CipherTest.java:23)
The code ran fine on my machine. Note that your encrypt method is not synchronized, so running this in a threaded environment will make it fail. In general you should have one Cipher instance per thread. Cipher contains state between method calls, so just synchronizing access to the method calls themselves will fail from time to time.

Categories

Resources