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.

Original source