关于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())); }}这个例子只讲了一个分块的状况,在理论的场景中可能波及多个分块,而多个分块进行尝试扭转一个密文分组理论会影响两个明文分组,要求一直在雷同地位的向前的密文分组进行变换猜想,十分耗时。 ...

May 23, 2023 · 4 min · jiezi

关于aes:CryptoJs-使用-AES-CBC-加解密数据

AES CBC 模式废话不多说,间接上代码,加密步骤为: 约定 key, 生成本次加密须要的 iv数据体 cbc 加密 encData数据体 base64 编码 encDataBase64ed计算数据体的 hmac{iv, mac, encDataBase64ed} stringify = payloadpayload base64编码传输。payloadBase64ed<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <title>AesCBC加解密</title> <style> * { box-sizing: border-box; } input, textarea { width: 100%; padding: 10px 20px; border-radius: 10px; } </style></head><body><div style="padding: 0 20px"> <div> <h4>Key</h4> <input type="text" id="key" placeholder="请输出约定的Key"> </div> <div> <h4>密文</h4> <textarea name="encData" id="encData" rows="10" oninput="encDataDec()"></textarea> </div> <div> <h4>明文</h4> <textarea name="decData" id="decData" rows="10" oninput="decDataEnc()"></textarea> </div></div><script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.9-1/crypto-js.js"></script><script> function encDataDec() { let key = document.getElementById("key").value if (key.trim() === "") { alert("请输出约定的Key") return; } let encDataDom = document.getElementById("encData") let decDataDom = document.getElementById("decData") if (encDataDom.value === "") { decDataDom.value = "" return } try { decDataDom.value = AesCBCDecrypt(encDataDom.value, key) } catch (err) { decDataDom.value = err.toString() } } function decDataEnc() { let key = document.getElementById("key").value if (key.trim() === "") { alert("请输出约定的Key") return; } let encDataDom = document.getElementById("encData") let decDataDom = document.getElementById("decData") if (decDataDom.value === "") { encDataDom.value = "" return } try { encDataDom.value = AesCBCEncrypt(decDataDom.value, key) } catch (err) { encDataDom.value = err.toString() } } function AesCBCEncrypt(bizData, key) { let iv = "te21".repeat(4)//16位随机iv let waKey = CryptoJS.enc.Utf8.parse(key) let waIv = CryptoJS.enc.Utf8.parse(iv) let bizDataEnc = CryptoJS.AES.encrypt(bizData, waKey, { iv: waIv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) let bizDataEncBase64 = CryptoJS.enc.Base64.stringify(bizDataEnc.ciphertext) let ivBase64 = CryptoJS.enc.Base64.stringify(waIv) let mac = CryptoJS.HmacSHA256(ivBase64 + bizDataEncBase64, waKey).toString() let payloadJson = { iv: ivBase64, mac: mac, value: bizDataEncBase64 } return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(JSON.stringify(payloadJson))) } function AesCBCDecrypt(payloadEnc, key) { let payloadJson = JSON.parse(CryptoJS.enc.Utf8.stringify(CryptoJS.enc.Base64.parse(payloadEnc))) let waKey = CryptoJS.enc.Utf8.parse(key) let waIv = CryptoJS.enc.Base64.parse(payloadJson.iv) let bizData = CryptoJS.AES.decrypt(payloadJson.value, waKey, { iv: waIv, mode: CryptoJS.mode.CBC, padding: CryptoJS.pad.Pkcs7 }) return CryptoJS.enc.Utf8.stringify(bizData) }</script></body></html>

April 24, 2023 · 2 min · jiezi

关于aes:AES加解密使用总结

AESAES, 高级加密规范, 是采纳区块加密的一种规范, 又称Rijndael加密法. 严格上来讲, AES和Rijndael又不是齐全一样, AES的区块长度固定为128比特, 秘钥长度能够是128, 192或者256. Rijndael加密法能够反对更大范畴的区块和密钥长度, Rijndael应用的密钥和区块长度均能够是128,192或256比特. AES是对称加密最风行的算法之一. 咱们不去探讨具体的AES的实现, 因为其中要使用到大量的高等数学常识, 单纯的理解AES流程其实也没什么意义(没有数学根底难以了解), 所以咱们明天着重来总结一些应用过程中的小点. 加密模式 - ECB, CBC当然了分组明码的加密模式不仅仅是ECB和CBC这两种, 其余的咱们暂不波及. 下面说的AES是一种区块加密的规范, 那加密模式其实能够了解为解决不同区块的形式和分割. ECBECB能够看做最简略的模式, 须要加密的数据依照区块的大小分为N个块, 并对每个块独立的进行加密 此种办法的毛病在于同样的明文块会被加密成雷同的密文块, 因而, 在某些场合, 这种办法不能提供严格的数据保密性. 通过上面图示例子大家就很容易明确了 CBC咱们的我的项目中应用的就是这种模式, 在CBC模式中, 每个明文块与前一个块的加密后果进行异或后, 在进行加密, 所以每个块的加密都依赖后面块的加密后果的, 同时为了保障第一个块的加密, 在第一个块中须要引入初始化向量iv. CBC是最罕用的模式. 他的毛病是加密过程只能是串行的, 无奈并行, 因为每个块的加密要依赖到前一个块的加密后果, 同时在加密的时候明文中的轻微扭转, 会导致前面所有的密文块都发生变化. 但此种模式也是有长处的, 在解密的过程中, 每个块的解密依赖上一个块的加密后果, 所以咱们要解密一个块的时候, 只须要把他后面一个块也一起读取, 就能够实现本块的解密, 所以这个过程是能够并行操作的. 填充模式AES加密每个块blockSize是128比特, 那如果咱们要加密的数据不是128比特的倍数, 就会存在最初一个分块有余128比特, 那这个块怎么解决, 就用到了填充模式. 上面是罕用的填充模式. PKCS7 / PKCS5PKCS7可用于填充的块大小为1-255比特, 填充形式也很容易了解, 应用需填充长度的数值paddingSize 所示意的ASCII码 paddingChar = chr(paddingSize)对数据进行冗余填充. (前面有解释) ...

December 14, 2021 · 1 min · jiezi

关于aes:AESjs加密解密与C的相互转换

AES简介AES, Advanced Encryption Standard,其实是一套规范:FIPS 197,而咱们所说的AES算法其实是Rijndael算法。 NIST (National INstitute of Standards and Technology) 在1997年9月12日公开征集更高效更平安的代替DES加密算法,第一轮共有15种算法入选,其中5种算法入围了决赛,别离是MARS,RC6,Rijndael,Serpent和Twofish。又通过3年的验证、评测及公众探讨之后Rijndael算法最终入选。 Rijndael算法Rijndael算法是由比利时学者Joan Daemen和Vincent Rijmen所提出的,算法的名字就由两位作者的名字组合而成。Rijndael的劣势在于集安全性、性能、效率、可实现性及灵活性与一体。 背景因为在HTTP中参数都是通过URL或者Body进行传输的,那么就存在信息的裸露问题,这时候很多敏感的信息就须要进行加密,避免敏感信息泄露。 具体实现1、服务端加密/解密public class DecryptStringAES{ /// <summary> /// AES加密算法 /// </summary> /// <param name="input">明文字符串</param> /// <returns>字符串</returns> public static string EncryptByAES(string input) { if (string.IsNullOrWhiteSpace(input)) { return input; } using (RijndaelManaged rijndaelManaged = new RijndaelManaged()) { rijndaelManaged.Mode = CipherMode.CBC; rijndaelManaged.Padding = PaddingMode.PKCS7; rijndaelManaged.FeedbackSize = 128; rijndaelManaged.Key = Encoding.UTF8.GetBytes(Decrypt.Key); rijndaelManaged.IV = Encoding.UTF8.GetBytes(Decrypt.AES_IV); ICryptoTransform encryptor = rijndaelManaged.CreateEncryptor(rijndaelManaged.Key, rijndaelManaged.IV); using (MemoryStream msEncrypt = new MemoryStream()) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write)) { using (StreamWriter swEncrypt = new StreamWriter(csEncrypt)) { swEncrypt.Write(input); } byte[] bytes = msEncrypt.ToArray(); return Convert.ToBase64String(bytes); } } } } /// <summary> /// AES解密 /// </summary> /// <param name="input">密文字节数组</param> /// <returns>返回解密后的字符串</returns> public static string DecryptByAES(string input) { if (string.IsNullOrWhiteSpace(input)) { return input; } var buffer = Convert.FromBase64String(input); using (RijndaelManaged rijndaelManaged = new RijndaelManaged()) { rijndaelManaged.Mode = CipherMode.CBC; rijndaelManaged.Padding = PaddingMode.PKCS7; rijndaelManaged.FeedbackSize = 128; rijndaelManaged.Key = Encoding.UTF8.GetBytes(Decrypt.Key); rijndaelManaged.IV = Encoding.UTF8.GetBytes(Decrypt.AES_IV); ICryptoTransform decryptor = rijndaelManaged.CreateDecryptor(rijndaelManaged.Key, rijndaelManaged.IV); using (MemoryStream msEncrypt = new MemoryStream(buffer)) { using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srEncrypt = new StreamReader(csEncrypt)) { return srEncrypt.ReadToEnd(); } } } } } }2、 客户端(JS)应用包 ...

May 12, 2021 · 2 min · jiezi

关于aes:Go-加密解密算法总结

前言加密解密在理论开发中利用比拟宽泛,罕用加解密分为:“对称式”、“非对称式”和”数字签名“。 对称式:对称加密(也叫私钥加密)指加密和解密应用雷同密钥的加密算法。具体算法次要有DES算法,3DES算法,TDEA算法,Blowfish算法,RC5算法,IDEA算法。 非对称加密(公钥加密):指加密和解密应用不同密钥的加密算法,也称为公私钥加密。具体算法次要有RSA、Elgamal、背包算法、Rabin、D-H、ECC(椭圆曲线加密算法)。 数字签名:数字签名是非对称密钥加密技术与数字摘要技术的利用。次要算法有md5、hmac、sha1等。 以下介绍golang语言次要的加密解密算法实现。 md5MD5信息摘要算法是一种被宽泛应用的明码散列函数,能够产生出一个128位(16进制,32个字符)的散列值(hash value),用于确保信息传输残缺统一。 func GetMd5String(s string) string { h := md5.New() h.Write([]byte(s)) return hex.EncodeToString(h.Sum(nil))}hmacHMAC是密钥相干的哈希运算音讯认证码(Hash-based Message Authentication Code)的缩写, 它通过一个规范算法,在计算哈希的过程中,把key混入计算过程中。 和咱们自定义的加salt算法不同,Hmac算法针对所有哈希算法都通用,无论是MD5还是SHA-1。采纳Hmac代替咱们本人的salt算法,能够使程序算法更标准化,也更平安。 示例 //key随便设置 data 要加密数据func Hmac(key, data string) string { hash:= hmac.New(md5.New, []byte(key)) // 创立对应的md5哈希加密算法 hash.Write([]byte(data)) return hex.EncodeToString(hash.Sum([]byte("")))}func HmacSha256(key, data string) string { hash:= hmac.New(sha256.New, []byte(key)) //创立对应的sha256哈希加密算法 hash.Write([]byte(data)) return hex.EncodeToString(hash.Sum([]byte("")))}sha1SHA-1能够生成一个被称为音讯摘要的160位(20字节)散列值,散列值通常的出现模式为40个十六进制数。 func Sha1(data string) string { sha1 := sha1.New() sha1.Write([]byte(data)) return hex.EncodeToString(sha1.Sum([]byte("")))}AES密码学中的高级加密规范(Advanced Encryption Standard,AES),又称Rijndael加密法,是美国联邦政府采纳的一种区块加密规范。这个规范用来代替原先的DES(Data Encryption Standard),曾经被多方剖析且广为全世界所应用。AES中常见的有三种解决方案,别离为AES-128、AES-192和AES-256。如果采纳真正的128位加密技术甚至256位加密技术,蛮力攻打要取得成功须要消耗相当长的工夫。 ...

September 23, 2020 · 5 min · jiezi