I have implemented Encryption / Decryption in Android application.
I have added an Encryption class which has been made a Singleton class.
Part of the code as follows:
public class Encryption {
private SecretKeySpec mKey = null;
private Cipher mCipher = null;
private byte[] mKeyBytes = null;
private AlgorithmParameterSpec mParamSpec = null;
private static Encryption sInstance;
public Encryption() {
byte[] iv = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
mParamSpec = new IvParameterSpec(iv);
mKeyBytes = getMD5(MD5_KEY.getBytes();
mKey = new SecretKeySpec(mKeyBytes, AES_TAG);
try {
mCipher = Cipher.getInstance(TRANSFORMATION_STR);
} catch (NoSuchAlgorithmException e) {
} catch (NoSuchPaddingException e) {
}
}
public static synchronized Encryption getInstance() {
if (sInstance == null) {
sInstance = new Encryption();
}
return sInstance;
}
public String encryptString(String strPwd) {
String strToEncripted = null;
strToEncripted = strPwd;
String result = null;
byte[] input = null;
byte[] cipherText = null;
int ctLength = 0;
try {
input = strToEncripted.getBytes(UTF8_STR);
mCipher.init(Cipher.ENCRYPT_MODE, mKey, mParamSpec);
cipherText = new byte[mCipher.getOutputSize(input.length)];
ctLength = mCipher.update(input, 0, input.length, cipherText, 0);
ctLength += mCipher.doFinal(cipherText, ctLength);
result = Base64.encodeToString(cipherText, Base64.DEFAULT)
.replace(NEWLINE_CHAR, EMPTY_CHAR).trim();
} catch (InvalidKeyException e) {
} catch (UnsupportedEncodingException e) {
} catch (InvalidAlgorithmParameterException e) {
} catch (ShortBufferException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
} catch (IllegalStateException e) {
}
return result;
}
public String decryptstring(byte[] encripted) {
String textDecrypt = "";
byte[] encriptedByteDecode64 = Base64.decode(encripted, Base64.DEFAULT);
byte[] plainText = new byte[mCipher.getOutputSize(encriptedByteDecode64.length)];
int ptLength = 0;
try {
mCipher.init(Cipher.DECRYPT_MODE, mKey, mParamSpec);
ptLength = mCipher.update(encriptedByteDecode64, 0, encriptedByteDecode64.length, plainText, 0);
ptLength += mCipher.doFinal(plainText, ptLength);
textDecrypt = (new String(plainText)).trim();
} catch (InvalidKeyException e) {
} catch (InvalidAlgorithmParameterException e) {
} catch (ShortBufferException e) {
} catch (IllegalBlockSizeException e) {
} catch (BadPaddingException e) {
}
return textDecrypt;
}
private String getMD5(String strKey) {
String key = strKey;
String result = null;
try {
MessageDigest algorithm = MessageDigest.getInstance(MD5_TAG);
algorithm.reset();
algorithm.update(key.getBytes(UTF8_STR));
byte messageDigest[] = algorithm.digest();
StringBuilder hexString = new StringBuilder();
for (int count = 0; count < messageDigest.length; count++) {
String hexaDecimal = Integer.toHexString(0xFF & messageDigest[count]);
while (hexaDecimal.length() < 2)
hexaDecimal = new StringBuilder(ZERO_STR).append(hexaDecimal).toString();
hexString.append(hexaDecimal);
}
result = hexString.toString();
} catch (NoSuchAlgorithmException e) {
} catch (UnsupportedEncodingException e) {
}
return result;
}
}
Using the singleton instance , encryption & decryption of string are implemented & they are working mostly.
Sometimes , though the cipher has been initialised , still its throwing an Exception: java.lang.IllegalStateException: Cipher not initialized
The scenario is mostly when after some time-interval (30 mins), a decryption of string is performed.
Can it be due to the incorrect use of Singleton instance?
Instead of Singleton class , I have tried to encrypt string creating an instance of Encryption class using the new operator , but the problem is I need the same object for decryption , else the java.lang.IllegalStateException: Cipher not initialized
is thrown.
Any suggestions / hints are welcome.
This problem is bound to occur in a multi threaded environment, as it happened to me. The issue is a clash between mCipher.init() and mCipher.doFinal() methods.
Following are the related methods in Cipher class:
public final void init(int opmode, Key key, AlgorithmParameterSpec params) throws InvalidKeyException, InvalidAlgorithmParameterException
{
init(opmode, key, params, JceSecurity.RANDOM);
}
public final void init(int opmode, Key key, AlgorithmParameterSpec params,
SecureRandom random)
throws InvalidKeyException, InvalidAlgorithmParameterException
{
initialized = false;
checkOpmode(opmode);
if (spi != null) {
checkCryptoPerm(spi, key, params);
spi.engineInit(opmode, key, params, random);
} else {
chooseProvider(I_PARAMSPEC, opmode, key, params, null, random);
}
initialized = true;
this.opmode = opmode;
}
public final int doFinal(byte[] output, int outputOffset)
throws IllegalBlockSizeException, ShortBufferException,
BadPaddingException {
checkCipherState();
// Input sanity check
if ((output == null) || (outputOffset < 0)) {
throw new IllegalArgumentException("Bad arguments");
}
chooseFirstProvider();
return spi.engineDoFinal(null, 0, 0, output, outputOffset);
}
private void checkCipherState() {
if (!(this instanceof NullCipher)) {
if (!initialized) {
throw new IllegalStateException("Cipher not initialized");
}
if ((opmode != Cipher.ENCRYPT_MODE) &&
(opmode != Cipher.DECRYPT_MODE)) {
throw new IllegalStateException("Cipher not initialized " +
"for encryption/decryption");
}
}
}
See the behavior of the initialized
variable in a multi threaded environment with two threads executing init() and doFinal(). The Exception returned is not related to the object not being actually initialized but the initialized
variable being set to false
.
I solved my issue by synchronizing my encryptString() and decryptString() methods. Hope you can get some insight by going through the Cipher code.
Make sure you call Cipher.getInstance()
in your encrypt and decrypt methods.
cipher = Cipher.getInstance(TRANSFORMATION_STR);
cipher.init(Cipher.ENCRYPT_MODE, mKey, mParamSpec);
// encrypt code here
getInstance()
method grabs a cipher instance for the current thread to avoid race conditions like the exception you just posted.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With