java.lang.IllegalStateException: Cipher not initialized
android, java
Solution
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.
Problem
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.