关于aes:密码学为什么不推荐在对称加密中使用CBC工作模式
引言这篇文章是我在公司外部分享中一部分内容的具体版本,如题目所言,我会通过文字、代码示例、带你残缺的搞懂为什么咱们不倡议你应用cbc加密模式,用了会导致什么平安问题,即便肯定要用须要留神哪些方面的内容。 注:本文仅从平安角度登程,未思考性能与兼容性等因素工作模式是个啥分组加密的工作模式与具体的分组加密算法没有关系,所以只有应用了cbc模式,不限于AES、DES、3DES等算法都一样存在问题。 以AES-128-CBC为例,能够屏蔽AES算法的外部实现,把AES算法当作一个黑盒,输出明文和密钥返回密文。 因为是分组加密算法,所以对于长的明文,须要依照算法约定的块大小进行分组,AES每一组为16B,不同组之间应用雷同的密钥进行计算的话,会产生一些平安问题,所以为了将分组明码利用到不同的理论利用,NIST定义了若干的工作模式,不同模式对分块的加密解决逻辑会不同,常见的工作模式有: 模式形容ECB(电码本)雷同的密钥分队明文分组进行加密CBC(分组链接)加密算法的输出是上一个密文组和以后明文组的异或CFB(密文反馈)一次解决s位,上一块密文作为下一块加密算法输出,产生伪随机数与明文异或或作为下一单元的密文OFB(输入反馈)相似CFB,仅加密算法的输出是上一次加密的输入,且应用整个分组CTR(技数器)每个明文分组都与一个通过加密的计数器相异或。对每个后续分组计数器递增ECB模式最为简略,假如存在明文分组a、b、c、d 每个分组别离在雷同密钥k进行aes加密后的密文为A、B、C、D,最终明文abcd对应的密文为ABCD,如图所示: ECB模式很简略可能从性能角度讲十分占优,因为分组之间没有关联,能够独立并行计算。但从平安角度来看这种间接将密文分组进行拼接的形式,很可能会被攻击者猜解出明文特色或替换抛弃局部密文块达到明文的替换与截取成果,以下的图十分清晰: <img src="https://9eek-1251521991.cos.ap-chengdu.myqcloud.com/article/img/20230302102403.png" alt="image-20230302102403380" style="zoom:67%;" /> 所以很容易了解ECB也不是举荐应用的工作模式。 CBC有了ECB的前事不忘;后事之师,CBC( Cipher Block Chaining)模式就提出将明文分组先于一个随机值分组IV进行异或且本组的密文又与下一组的明文进行异或的形式,这种形式减少了密文的随机性,防止了ECB的问题,具体过程见图: 加密过程 解释下这个图,存在明文分组a、b、c、d,cbc工作模式是存在执行程序的,即第一个密文分组计算后能力计算第二个分组,第一个明文分组在加密前明文a须要和一个初始分组IV进行异或运算 即 a^IV ,而后再用密钥K进行规范的AES加密,E(a^IV,K) 失去第一组的密文分组A,密文分组A会参加第二组密文的计算,计算过程相似,只不过第二次需将IV替换为A,如此循环,最初失去的密文ABCD即为CBC模式。 解密过程仔细观察CBC的加密过程,须要应用到一个随机分组IV,在规范的加密过程中,IV会被拼接到密文分组中去,假如存在两人甲和乙,甲方给到乙方的密文理论是 (IV)ABCD,乙在拿到密文后提取IV,而后进行下图的解密: 解密过程就是加密过程转变了下方向,注意两个图从abcd到ABCD的箭头方向。第一个密文分组先进行AES解密,失去的两头值咱们计为M_A,M_A再于初始向量IV进行异或失去a,第二个分组反复同样的动作,还是将IV替换为密文分组A,最终可失去明文分组abcd。 CBC有什么问题CBC减少了随机变量IV给密文减少了随机性,增大了密文剖析的难度是不是就平安了呢? 答案当然是不,CBC又引入了新的问题——能够通过扭转密文从而扭转明文。 CBC字节翻转攻打原理解说CBC字节翻转攻打原理非常简单,如图所示: 攻打往往产生在解密过程,黑客通过管制IV和密文分组能够达到批改明文的目标,图中黑客通过替换密文D分组为E分组能够篡改本来明文d为x(可能会波及填充验证,这里先不论),或者同样的情理黑客能够通过管制IV达到批改明文分组a的目标。 举个例子接下来用一个理论例子来演示其原理及危害。 为了保障不便进行原理解说,在加密时会将IV和key写死,防止每次运行的后果不一样。假如存在一个web服务利用,前后端通过Cookie来进行权限校验,cookie的内容为明文admin:0进行AES-128-CBC加密后的密文进行base64编码,数字0代表此时用户的权限为非管理员用户,当admin前面的数字为1时,后端会认为是一名管理员用户。 Cookie内容为:AAAAAAAAAAAAAAAAAAAAAJyycJTyrCtpsXM3jT1uVKU= 此时黑客在晓得校验原理的状况下可利用字节翻转攻打对此服务发动攻打,在不晓得密钥的状况下将cookie明文批改为admin:1,具体过程: AES以16B作为block size进行分块,admin:0在ascii编码下对应的二进制仅为7B,所以在加密时还会对原始明文进行填充直到刚好为16B的整数倍,所以还须要填充9B(填充细节上面再讲),因为CBC还会有IV,所以最终的密文是IV+Cipher,IV16B,cipher16B,总共32B,这里因为只有一个密文分块,所以扭转IV的第7个字节对应明文admin:0数字的地位,或者密文的第7个字节即可扭转明文数字局部的字段,通过一直的尝试,咱们将本来密文IV分组 00改为01,即可胜利翻转明文为1,即cookie明文变为admin:1,从而达到权限晋升的目标。 残缺代码: package com.example.springshiroproject;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.util.ByteSource;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;import java.security.Key;import java.util.Arrays;public class MyTest { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { AesCipherService aesCipherService = new AesCipherService(); // 写死密钥 byte[] key = new byte[128/8]; Arrays.fill(key,(byte) '\0'); // 写死的密钥,客户端及黑客未知 String plainText = "admin:0"; // cookie明文内容 byte[] plainTextBytes = plainText.getBytes(); // 写死IV byte[] iv_bytes = new byte[128/8]; Arrays.fill(iv_bytes, (byte) '\0');//// // 通过反射调用能够自定义IV的AES-128-cbc加密办法(原办法为private) Method encryptWithIV = aesCipherService.getClass().getSuperclass().getSuperclass().getSuperclass().getDeclaredMethod("encrypt",new Class[]{byte[].class, byte[].class,byte[].class,boolean.class}); encryptWithIV.setAccessible(true); ByteSource cipherWithIV = (ByteSource) encryptWithIV.invoke(aesCipherService,new Object[]{plainTextBytes, key,iv_bytes,true}); System.out.println("明文:" + ByteSource.Util.bytes(plainTextBytes).toHex()); // 失常逻辑解密 byte[] cipher = cipherWithIV.getBytes(); System.out.println("原始密文: " + cipherWithIV.toHex()); System.out.println("Cookie内容: " + cipherWithIV.toBase64()); ByteSource decPlain = aesCipherService.decrypt(cipher, key); System.out.println("原始解密后明文:" + new String(decPlain.getBytes())); // 字节翻转攻打 cipher[6] = (byte)0x01; System.out.println("翻转后的密文: " + ByteSource.Util.bytes(cipher).toHex()); System.out.println("翻转后的cookie:"+ ByteSource.Util.bytes(cipher).toBase64()); decPlain = aesCipherService.decrypt(cipher, key); System.out.println("翻转解密后明文:" + new String(decPlain.getBytes())); }}这个例子只讲了一个分块的状况,在理论的场景中可能波及多个分块,而多个分块进行尝试扭转一个密文分组理论会影响两个明文分组,要求一直在雷同地位的向前的密文分组进行变换猜想,十分耗时。 ...