18

在 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
4

3 回答 3

23

您指出了以下例外情况:

NoSuchPaddingException, NoSuchAlgorithmException
InvalidAlgorithmParameterException, InvalidKeyException,
BadPaddingException, IllegalBlockSizeException

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


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


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

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


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


最后一些注意事项:

  • BadPaddingException并且IllegalBlockSizeException可能Cipher因为没有完全接收到数据,或者因为攻击者弄乱了数据而被抛出;
  • BadPaddingException如果密钥不正确,也可能会抛出。
  • Beware that an exception may be thrown for AES key sizes 192 bit and 256 bit if the unlimited crypto files are not being installed (check the Oracle JavaSE site for more info); you should check if the key size is permitted when the application is started (this is mainly true for old / deprecated versions of Java);
于 2013-03-29T22:30:51.950 回答
3

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

于 2013-03-29T20:08:30.510 回答
-3

处理这个问题的最好方法是创建一个业务异常(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 )

于 2013-03-29T18:43:27.137 回答