已经确定加密算法必须是AES,密钥为256位.
但是,我想知道我们应该使用哪种AES实现和/或“模式”.我已经阅读了一些名为GCM模式的内容,我们已经对它进行了一些测试(使用BouncyCastle / SpongyCastle),但我并不完全清楚AES-GCM的用途是什么以及与普通相比它“购买”了什么AES – 以及是否需要考虑任何权衡.
以下是我们所关注/要求/问题的列表:
>填充:我们需要加密的数据并不总是128位的倍数,因此AES实现/模式应该添加填充,但仅在必要时添加.
我的印象是,一个简单的AES实现,例如由javax.crypto.Cipher提供的,不会这样做,但初始测试表明它确实如此.所以我猜测填充要求本身没有理由采用GCM而不是“普通”AES.那是对的吗?
>身份验证:我们需要一种万无一失的方法来检测是否发生了数据损坏.但是,理想情况下,我们还希望检测何时使用不正确的密钥进行解密.因此,我们希望能够区分这两种情况.我最初考虑GCM的原因是由于这个Stackoverflow question,其中一个响应者似乎暗示使用AES-GCM可以实现这种区分,尽管他没有提供详细的解释(更不用说代码)了.
>最小化开销:我们需要限制加密数据的存储和传输开销.因此,我们希望知道特定AES实现/模式的选择是否以及在何种程度上影响开销量.
>加密/解密性能:虽然这不是主要问题,但我们想知道特定AES实现/模式的选择在多大程度上影响加密和解密性能,无论是cpu时间还是内存占用.
提前感谢任何建议,澄清和/或代码示例.
编辑:德尔南有帮助地指出,没有“普通AES”这样的东西.所以澄清一下,我的意思是使用Java的内置AES支持.
像这样:Cipher localCipher = Cipher.getInstance(“AES”);
解决方法
GCM是经过身份验证的加密模式.它一次性为您提供机密性(加密),完整性和身份验证(MAC).
到目前为止,正常的操作模式是ECB(这是Java的默认设置),CBC,CTR,OFB和其他一些操作模式.他们都只提供加密.如果没有诚信,保密本身很少有用;一个人必须以特别的方式将这些经典模式与完整性检查结合起来.由于密码学难以正确,因此这种组合通常是不安全的,比必要的慢或甚至两者兼而有之.
密码学家已经(最近)创建了经过身份验证的加密模式来解决该问题. GCM是最成功的之一:它已经被NIST选中,它高效,它是专利免费的,它可以携带额外的认证数据(即,数据保持清晰,但您可以验证其真实性) .有关其他模式的说明,请参阅this excellent article of Matthew Green.
引起您的关注:
>填充:默认情况下,Java使用PKCS#7填充.这是有效的,但它经常容易受到padding oracle attacks的影响,最好用MAC击败.GCM嵌入已经是MAC(称为GMAC).
>身份验证:AES-GCM仅接受一个AES密钥作为输入,而不是密码.它将告诉您AES密钥是否错误或有效负载是否已被篡改,但这些条件被视为一个.相反,您应该考虑使用适当的密钥派生算法(如PBKDF2或bcrypt)从密码中派生AES密钥.我认为始终无法判断密码是否错误或者有效负载是否已被修改,因为验证前者所需的数据总是会被破坏.您可以加密一个小的已知字符串(使用ECB AES),一起发送,并使用它来验证密码是否正确.
>最小化开销:在一天结束时,如果您需要身份验证,所有模式都会导致相同的开销(大约10-20个字节).除非您使用非常小的有效负载,否则可以忽略这一点.
>性能:GCM非常好,因为它是一种在线模式(无需缓冲整个有效载荷,因此内存较少),可并行化,并且每个明文块需要一次AES操作和一次Galois乘法.像ECB这样的经典模式更快(每个块仅一个AES操作),但是 – 再次 – 您还必须考虑完整性逻辑,这可能最终比GMAC慢.