处理 Java 加密异常

这是非常基本的代码段,在Java中处理加密/解密时很常见。

final Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
cipher.init(Cipher.ENCRYPT_MODE, key, iv);
cipher.doFinal(*something*);

仅这三行就可能会引发六个异常,我不确定处理它们的最干净(就代码可读性而言)方法是什么。尝试使用六个捕获子句对我来说真的像一种气味。

在处理此类对象时,是否有微模式或最佳实践,我显然缺少?

编辑

对不起,我想我没有很好地解释自己。我的问题实际上并不是要避免 try\catch 子句,而是是否有一种常见的方法来处理类似的情况。

加密例外是

NoSuchPaddingException, NoSuchAlgorithmException
InvalidAlgorithmParameterException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException

答案 1

您指出了以下例外情况:

NoSuchPaddingException, NoSuchAlgorithmException
InvalidAlgorithmParameterException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException

现在所有这些都是GeneralSecurityException的,所以很容易抓住它们。但是看看用例,你可能不想这样做。


如果您查看异常的原因,那么您会发现这些异常中的任何一个 - 除了最后两个 - 仅在生成算法或键的实现时才被抛出。我认为,一旦您测试了应用程序,这些值或多或少地保持静态,这是合理的。因此,例如,抛出一个 IllegalStateException 是合乎逻辑的。 是不需要引发(在方法签名中)或 catch 的运行时异常。当然,您应该将安全异常作为异常的原因IllegalStateException


现在最后两个例外,BadPaddingExceptionTravilBlockSizeException是不同的。它们依赖于实际的密文,因此它们依赖于算法的输入。现在,通常情况下,在将输入输入馈送到密码实例(启动解密)之前,应始终验证输入的完整性,例如,通过首先验证 HMAC 校验和)。因此,从这个意义上说,您仍然可以摆脱运行时异常。如果您不单独检查完整性,那么您应该不要转换为 .相反,您可以让用户处理异常,也可以将其作为特定于用例的异常重新抛出。RuntimeException

如果你通过(重新)抛出它来处理它,那么应该了解明文预言机攻击,如填充预言机攻击。对于CBC模式下的填充oracle攻击:如果攻击者可以尝试让您多次解密密文并收到解密失败(或未失败)的指示,那么他们可以在不破坏密码的情况下检索消息的明文。因此,在可以处理身份验证标记的 16 个额外字节的情况下,应首选经过身份验证的模式(如 GCM 模式)。BadPaddingException


最好使用单独的/块来构建和初始化以及解密本身。您还可以在处理 GeneralSecurityException 之前捕获异常。从 Java 7 开始,您也可以使用多捕获语句(例如 )。trycatchCipherBadPaddingExceptionIllegalBlockSizeExceptioncatch(final BadPaddingException | IllegalBlockSizeException e)


最后一些注意事项:

  • BadPaddingExceptionTragalBlockSizeException可能是因为数据没有完全接收而被抛出,但也可能是因为攻击者弄乱了数据;Cipher
  • 如果键不正确,也可能引发 BadPaddingException
  • 请注意,如果未安装无限制的加密文件,则可能会为AES密钥大小192位和256位引发异常(请查看Oracle JavaSE站点以获取更多信息);您应该检查应用程序启动时是否允许密钥大小(这主要适用于Java的旧/不推荐使用版本)。较新版本的Java不需要这些无限制的加密文件。

答案 2

如果你愿意失去一些特殊性,所有的Crypto例外都扩展了GeneralSecurityException,你可以抓住它。