android java.lang.IllegalStateException:密码未初始化

m4pnthwp  于 2023-06-28  发布在  Android
关注(0)|答案(5)|浏览(179)

我在Android应用程序中实现了加密/解密。
我添加了一个加密类,它已经成为一个Singleton类。
部分代码如下:

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;
        }
    }

使用单例示例实现了字符串的加密和解密,它们基本上都能正常工作。
有时,虽然密码已经初始化,但仍然会抛出异常:java.lang.IllegalStateException: Cipher not initialized
场景主要是在某个时间间隔(30分钟)之后执行字符串的解密。
这可能是由于Singleton示例的不正确使用造成的吗?
而不是Singleton类,我尝试加密字符串,使用new运算符创建Encryption类的示例,但问题是我需要相同的对象进行解密,否则会抛出**java.lang.IllegalStateException: Cipher not initialized**。
欢迎任何建议/提示。

wydwbb8l

wydwbb8l1#

这个问题在多线程环境中必然会出现,就像我遇到的一样。问题是mCipher.init()和mCipher.doFinal()方法之间的冲突。
以下是Cipher类中的相关方法:

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");
        }
    }
}

查看initialized变量在多线程环境中的行为,其中有两个线程执行init()和doFinal()。返回的Exception与未实际初始化的对象无关,而是与initialized变量设置为false有关。
我通过同步encryptString()和decryptString()方法解决了这个问题。希望你能通过浏览密码得到一些见解。

dw1jzc5e

dw1jzc5e2#

确保在encrypt和decrypt方法中调用了Cipher.getInstance()

cipher = Cipher.getInstance(TRANSFORMATION_STR);
cipher.init(Cipher.ENCRYPT_MODE, mKey, mParamSpec);
// encrypt code here

getInstance()方法为当前线程获取一个密码示例,以避免竞争条件,例如您刚刚发布的异常。

xmq68pz9

xmq68pz93#

decryptstring方法中,调用

byte[] plainText = new byte[mCipher.getOutputSize(encriptedByteDecode64.length)];

在你打电话之前写几行字

mCipher.init(Cipher.DECRYPT_MODE, mKey, mParamSpec);

由于在调用getOutputSize时密码尚未初始化,因此会出现异常。重新排列这些行应该可以解决这个问题。(对我来说是这样)。

s5a0g9ez

s5a0g9ez4#

我也遇到了同样的问题(Cipher not initialized)。我使用的是高加密,对我来说,解决方案是用无限强度版本替换jre/lib/security中的普通策略jar。

sigwle7e

sigwle7e5#

如果你用这个方法

public void initCipher(int encryptionMode) throws GeneralSecurityException {
         getCipher().init(encryptionMode, getKey());
     }

那么你必须立即摆脱这个方法:D.相反,您可以在ConvertToEntity和ConvertToColumn方法中重新初始化密码。例如:

public String convertToDatabaseColumn(Object attribute) {
        Cipher cipher = getCipher();
        cipher.init(Cipher.ENCRYPT_MODE, getKey());
        if (attribute == null)
            return null;
        byte[] bytes = SerializationUtils.serialize(attribute);
        assert bytes != null;
        return Base64.getEncoder().encodeToString(cipher.doFinal(bytes));
    }

我只是想帮你,我也希望你能帮上忙.还有一件事,我使用@sneakyThrows而不是try和catch:D

相关问题