Issue while using Android fingerprint: IV required

2020-05-23 05:08发布

I'm following the ConfirmCredential Android example provided by Google, but it only shows how to encrypt the data. When I try to decrypt it I get exception:

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

I use the following code:

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

The exception is thrown at line:

cipher.init(Cipher.DECRYPT_MODE, secretKey);

Any idea on what is the proper way to do the decryption in this case?

3条回答
聊天终结者
2楼-- · 2020-05-23 05:24

I created an issue in the github project for the sample provided by google (link to the issue here). The response I got is that I must use the IV that was generated when the value was encrypted. (same as in the solution provided by @Qianqian)

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

I created a sample application that shows how to do this. It is available on github, here.

Hope this is useful to someone.

查看更多
何必那么认真
3楼-- · 2020-05-23 05:39

How about this,

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

If you use the sample code from Google repo, above line will work, also note, for every mChiper.init we need to authenticate with fingerprint once, that means two fingerprint auth is required, one for encryption and one for decryption.

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

查看更多
可以哭但决不认输i
4楼-- · 2020-05-23 05:48

Basically you have to pass the IvParameterSpec for the decrypt mode whereas you should not manually set it for encryption mode.

So here is what I did and it worked well:

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(....)
}

You can also encode the IV data with Base64 and save it under shared preferences instead of saving it under a file.

In either case, If you have employed the new full backup feature for Android M, keep in mind that this data should NOT be allowed to backup/restore since it is invalid when a user reinstall the app or install the app on another device.

查看更多
登录 后发表回答