Skip to content

Commit

Permalink
separate AES/RSA keys deletion when something goes wrong
Browse files Browse the repository at this point in the history
  • Loading branch information
lbalmaceda committed Jan 24, 2019
1 parent d878ec0 commit af1fbe5
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -168,14 +168,15 @@ KeyStore.PrivateKeyEntry getRSAKeyEntry() throws CryptoException, IncompatibleDe
} catch (IOException | UnrecoverableEntryException e) {
/*
* Any of this exceptions mean the old key pair is somehow corrupted.
* We can delete it and let the user retry the operation.
* We can delete both the RSA and the AES keys and let the user retry the operation.
*
* - IOException:
* Thrown when there is an I/O or format problem with the keystore data.
* - UnrecoverableEntryException:
* Thrown when the key cannot be recovered. Probably because it was invalidated by a Lock Screen change.
*/
deleteKeys();
deleteRSAKeys();
deleteAESKeys();
throw new CryptoException("The existing RSA key pair could not be recovered and has been deleted. " +
"This occasionally happens when the Lock Screen settings are changed. You can safely retry this operation.", e);
}
Expand Down Expand Up @@ -208,13 +209,11 @@ private KeyStore.PrivateKeyEntry getKeyEntryCompat(KeyStore keyStore) throws Key
}

/**
* Removes the AES and RSA keys generated in a previous execution.
* Removes the RSA keys generated in a previous execution.
* Used when we want the next call to {@link #encrypt(byte[])} or {@link #decrypt(byte[])}
* to recreate the keys.
*/
private void deleteKeys() {
storage.remove(KEY_ALIAS);
storage.remove(KEY_IV_ALIAS);
private void deleteRSAKeys() {
try {
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEY_STORE);
keyStore.load(null);
Expand All @@ -225,6 +224,16 @@ private void deleteKeys() {
}
}

/**
* Removes the AES keys generated in a previous execution.
* Used when we want the next call to {@link #encrypt(byte[])} or {@link #decrypt(byte[])}
* to recreate the keys.
*/
private void deleteAESKeys() {
storage.remove(KEY_ALIAS);
storage.remove(KEY_IV_ALIAS);
}

/**
* Decrypts the given input using a generated RSA Private Key.
* Used to decrypt the AES key for later usage.
Expand All @@ -241,7 +250,7 @@ byte[] RSADecrypt(byte[] encryptedInput) throws IncompatibleDeviceException, Cry
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
cipher.init(Cipher.DECRYPT_MODE, privateKey);
return cipher.doFinal(encryptedInput);
} catch (NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException | InvalidKeyException e) {
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
/*
* This exceptions are safe to be ignored:
*
Expand All @@ -252,15 +261,24 @@ byte[] RSADecrypt(byte[] encryptedInput) throws IncompatibleDeviceException, Cry
* implements it. Was introduced in API 1.
* - InvalidKeyException:
* Thrown if the given key is inappropriate for initializing this cipher.
*
* Read more in https://developer.android.com/reference/javax/crypto/Cipher
*/
Log.e(TAG, "The device can't decrypt input using a RSA Key.", e);
throw new IncompatibleDeviceException(e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
/*
* Any of this exceptions mean the encrypted input is somehow corrupted and cannot be recovered.
* Delete the AES keys since those originated the input.
*
* - IllegalBlockSizeException:
* Thrown only on encrypt mode.
* - BadPaddingException:
* Thrown if the input doesn't contain the proper padding bytes.
*
* Read more in https://developer.android.com/reference/javax/crypto/Cipher
*/
Log.e(TAG, "The device can't decrypt input using a RSA Key.", e);
throw new IncompatibleDeviceException(e);
deleteAESKeys();
throw new CryptoException("The RSA encrypted input is corrupted and cannot be recovered. Please discard it.", e);
}
}

Expand All @@ -280,7 +298,7 @@ byte[] RSAEncrypt(byte[] decryptedInput) throws IncompatibleDeviceException, Cry
Cipher cipher = Cipher.getInstance(RSA_TRANSFORMATION);
cipher.init(Cipher.ENCRYPT_MODE, certificate);
return cipher.doFinal(decryptedInput);
} catch (NoSuchAlgorithmException | IllegalBlockSizeException | BadPaddingException | NoSuchPaddingException | InvalidKeyException e) {
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
/*
* This exceptions are safe to be ignored:
*
Expand All @@ -291,15 +309,23 @@ byte[] RSAEncrypt(byte[] decryptedInput) throws IncompatibleDeviceException, Cry
* implements it. Was introduced in API 1.
* - InvalidKeyException:
* Thrown if the given key is inappropriate for initializing this cipher.
* - IllegalBlockSizeException:
* Thrown if no padding has been requested and the length is not multiple of block size.
* - BadPaddingException:
* Thrown only on decrypt mode.
*
* Read more in https://developer.android.com/reference/javax/crypto/Cipher
*/
Log.e(TAG, "The device can't encrypt input using a RSA Key.", e);
throw new IncompatibleDeviceException(e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
/*
* They really should not be thrown at all since padding is requested in the transformation.
* Delete the AES keys since those originated the input.
*
* - IllegalBlockSizeException:
* Thrown if no padding has been requested and the length is not multiple of block size.
* - BadPaddingException:
* Thrown only on decrypt mode.
*/
deleteAESKeys();
throw new CryptoException("The RSA decrypted input is invalid.", e);
}
}

Expand Down Expand Up @@ -335,9 +361,6 @@ byte[] getAESKey() throws IncompatibleDeviceException, CryptoException {
* - NoSuchAlgorithmException:
* Thrown if the Algorithm implementation is not available. AES was introduced in API 1
*
* However if any of this exceptions happens to be thrown (OEMs often change their Android distribution source code),
* all the checks performed in this class wouldn't matter and the device would not be compatible at all with it.
*
* Read more in https://developer.android.com/reference/javax/crypto/KeyGenerator
*/
Log.e(TAG, "Error while creating the AES key.", e);
Expand Down Expand Up @@ -367,7 +390,7 @@ public byte[] decrypt(byte[] encryptedInput) throws CryptoException, Incompatibl
byte[] iv = Base64.decode(encodedIV, Base64.DEFAULT);
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
return cipher.doFinal(encryptedInput);
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | BadPaddingException | IllegalBlockSizeException e) {
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException e) {
/*
* This exceptions are safe to be ignored:
*
Expand All @@ -380,15 +403,20 @@ public byte[] decrypt(byte[] encryptedInput) throws CryptoException, Incompatibl
* Thrown if the given key is inappropriate for initializing this cipher.
* - InvalidAlgorithmParameterException:
* If the IV parameter is null.
* - BadPaddingException:
* Thrown if the input doesn't contain the proper padding bytes. In this case, if the input contains padding.
* - IllegalBlockSizeException:
* Thrown only on encrypt mode.
*
* Read more in https://developer.android.com/reference/javax/crypto/Cipher
*/
Log.e(TAG, "Error while decrypting the input.", e);
throw new IncompatibleDeviceException(e);
} catch (BadPaddingException | IllegalBlockSizeException e) {
/*
* Any of this exceptions mean the encrypted input is somehow corrupted and cannot be recovered.
* - BadPaddingException:
* Thrown if the input doesn't contain the proper padding bytes. In this case, if the input contains padding.
* - IllegalBlockSizeException:
* Thrown only on encrypt mode.
*/
throw new CryptoException("The AES encrypted input is corrupted and cannot be recovered. Please discard it.", e);
}
}

Expand All @@ -411,7 +439,7 @@ public byte[] encrypt(byte[] decryptedInput) throws CryptoException, Incompatibl
//Save IV for Decrypt stage
storage.store(KEY_IV_ALIAS, new String(encodedIV));
return encrypted;
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException | BadPaddingException | IllegalBlockSizeException e) {
} catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) {
/*
* This exceptions are safe to be ignored:
*
Expand All @@ -426,13 +454,19 @@ public byte[] encrypt(byte[] decryptedInput) throws CryptoException, Incompatibl
* If the IV parameter is null.
* - BadPaddingException:
* Thrown only on decrypt mode.
* - IllegalBlockSizeException:
* Thrown if no padding has been requested and the length is not multiple of block size.
*
* Read more in https://developer.android.com/reference/javax/crypto/Cipher
*/
Log.e(TAG, "Error while encrypting the input.", e);
throw new IncompatibleDeviceException(e);
} catch (IllegalBlockSizeException | BadPaddingException e) {
/*
* - IllegalBlockSizeException:
* Thrown if no padding has been requested and the length is not multiple of block size.
* - BadPaddingException:
* Thrown only on decrypt mode.
*/
throw new CryptoException("The AES decrypted input is invalid.", e);
}
}

Expand Down
Loading

0 comments on commit af1fbe5

Please sign in to comment.