Проблема при использовании отпечатка пальца Android: IV требуется при расшифровке. Используйте IvParameterSpec или AlgorithmParameters для его предоставления.

Я следую примеру ConfirmCredential Android, представленному Google, но он показывает только, как шифровать данные. Когда я пытаюсь расшифровать его, я получаю исключение:

java.security.InvalidKeyException: IV required when decrypting. Use IvParameterSpec or AlgorithmParameters to provide it.

Я использую следующий код:

String transforation = KeyProperties.KEY_ALGORITHM_AES + "/" + KeyProperties.BLOCK_MODE_CBC + "/" + KeyProperties.ENCRYPTION_PADDING_PKCS7;

KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
keyStore.load(null);
SecretKey secretKey = (SecretKey) keyStore.getKey(KEY_NAME, null);

// encrypt
Cipher cipher = Cipher.getInstance(transforation);
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
String encriptedPassword = cipher.doFinal("Some Password".getBytes("UTF-8"));

// decrypt
cipher = Cipher.getInstance(transforation);
cipher.init(Cipher.DECRYPT_MODE, secretKey);
String password = new String(cipher.doFinal(encriptedPassword), "UTF-8"));

Исключение выбрано в строке:

cipher.init(Cipher.DECRYPT_MODE, secretKey);

Любая идея о том, что является правильным способом дешифрования в этом случае?

Ответ 1

В основном вам нужно передать IvParameterSpec для режима расшифровки, тогда как вы не должны вручную устанавливать его для режима шифрования.

Итак, вот что я сделал, и все получилось хорошо:

private initCipher(int mode) {
    try {
        byte[] iv;
        mCipher = Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
                + KeyProperties.BLOCK_MODE_CBC + "/"
                + KeyProperties.ENCRYPTION_PADDING_PKCS7);
        IvParameterSpec ivParams;
        if(mode == Cipher.ENCRYPT_MODE) {
            mCipher.init(mode, generateKey());
            ivParams = mCipher.getParameters().getParameterSpec(IvParameterSpec.class);
            iv = ivParams.getIV();
            fos = getContext().openFileOutput(IV_FILE, Context.MODE_PRIVATE);
            fos.write(iv);
            fos.close();
        }
        else {
            key = (SecretKey)keyStore.getKey(KEY_NAME, null);
            File file = new File(getContext().getFilesDir()+"/"+IV_FILE);
            int fileSize = (int)file.length();
            iv = new byte[fileSize];
            FileInputStream fis = getContext().openFileInput(IV_FILE);
            fis.read(iv, 0, fileSize);
            fis.close();
            ivParams = new IvParameterSpec(iv);
            mCipher.init(mode, key, ivParams);
        }
        mCryptoObject = new FingerprintManager.CryptoObject(mCipher);
    } catch(....)
}

Вы также можете кодировать данные IV с помощью Base64 и сохранять их в общих настройках вместо сохранения в файле.

В любом случае, если вы использовали новую функцию полной резервной копии для Android M, имейте в виду, что этим данным следует НЕ разрешать резервное копирование/восстановление, поскольку это неверно, когда пользователь переустанавливает приложение или установить приложение на другое устройство.

Ответ 2

Я создал проблему в проекте github для образца, предоставленного google (ссылка на вопрос здесь). Ответ, который я получил, заключается в том, что я должен использовать IV, который был сгенерирован, когда значение было зашифровано. (так же, как в решении, предоставляемом @Qianqian)

cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(encryptCipher.getIV()));
byte[] decryptedBytes = cipher.doFinal(encryptedBytes);

Я создал пример приложения, который показывает, как это сделать. Он доступен на github, здесь.

Надеюсь, что это кому-то полезно.

Ответ 3

Как насчет этого,

mCipher.init(Cipher.DECRYPT_MODE, key, mCipher.getParameters());

Если вы используете образец кода из репо Google, над этой линией будет работать, также обратите внимание, что для каждого mChiper.init нам нужно выполнить аутентификацию с помощью отпечатка пальца один раз, это означает, что требуется два файла auth отпечатка, один для шифрования и один для дешифрования.

https://github.com/googlesamples/android-FingerprintDialog