繁体   English   中英

处理 Java 加密异常

[英]Handling Java crypto exceptions

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

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

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

在使用此类对象时,是否存在我显然遗漏的微模式或最佳实践?

编辑

对不起,我想我没有很好地解释自己。 我的问题并不是要避免使用 try\catch 子句,而是是否有处理类似情况的通用方法。

加密例外是

NoSuchPaddingException, NoSuchAlgorithmException
InvalidAlgorithmParameterException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException

您指出了以下例外情况:

NoSuchPaddingException, NoSuchAlgorithmException
InvalidAlgorithmParameterException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException

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


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


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

如果您通过(重新)抛出BadPaddingException来处理它,那么应该了解明文 oracle 攻击,例如 padding oracle 攻击。 对于 CBC 模式下的 padding oracle 攻击:如果对手可以尝试让您多次解密密文并收到解密失败(或失败)的指示,那么他们可以在不破坏密码的情况下检索消息的明文 出于这个原因,在可以处理身份验证标签的 16 个附加字节的情况下,应该首选 GCM 模式等身份验证模式。


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


最后一些注意事项:

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

如果您愿意失去一些细节,所有 Crypto 异常都扩展GeneralSecurityException ,您可以抓住它。

处理这个问题的最好方法是创建一个业务异常(MyModuleException 或其他东西),然后重新抛出该异常,添加 Crypto 异常以导致部分。 这样,您的方法将只抛出一个异常,而不是六个,这在应用程序的其他层中会更容易管理。

public void myMethod(...) throws MyModuleException {
  try {
    final Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
    cipher.init(Cipher.ENCRYPT_MODE, key, iv);
    cipher.doFinal(*something*);
  } catch(Crypto1Ex ex){
    throw new MyModuleException("something is wrong", ex); //ex added, so it is not lost and visible in stacktraceses
  } catch(Crypto1Ex ex){
    throw new MyModuleException("something is wrong", ex);
  } //etc.
}

在 Java 7 中,您可能会更轻松地处理它(请参阅: http ://docs.oracle.com/javase/7/docs/technotes/guides/language/catch-multiple.html)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM