关于AES加解密中CBC模式的IV初始化向量的安全性问题 admin 2023-09-19 11:57:02 篇首语:本文由小编为大家整理,主要介绍了关于AES加解密中CBC模式的IV初始化向量的安全性问题相关的知识,希望对你有一定的参考价值。 参考技术A 前段时间,在研究HLS的AES加密,由于一个地方电视台的HLS流有AES加密,在查看了相关的加解密方案后发现使用的是简单的AES的CBC模式,在CBC的模式下,会设置一个IV,初始化向量。但是我在解密的时候,使用了一个由于理解错误而产生的一个错误IV居然也能解密视频并进行播放,于是就有了这篇张文章。 虽然有五种加密,但是常用的还是CBC,CBC的全称Cipher Block Chaining ,有点类似于区块链哈,我们先来看下加密方式 上面的图片从左往右看,初始化IV只有在第一个块加密的时候才会用到,而第N个块的加密IV则是用的N-1(N>1)个加密后的二进制数组。 CBC的解密则也是从左往右看,但是加密时IV在解密时候,只会用于对第一个块进行解密,其他块的解密则是使用上一块的加密二进制作为IV进行解密操作。 通过上面的分析就能知道,加密的时候,iv会影响所有数据的加密结果,而解密时,iv只会影响第一个加密块的解密结果,其他块的解密可以直接通过分隔加密数据获取正确是N-1块的IV。 这也就印证了为什么ff能播放我用错误的iv解密出来的视频流数据,因为第一块的大小存储大概是id3 的数据,ff直接丢掉id3的数据,直接解码后面的视频数据 ,ff 应该是识别h264编码头开始解码视频。 那么从这点可以看出,在使用了key的情况下,IV对整个数据的保密性没有太大的作用。 再来说说我是怎么发现这个问题,因为错误的IV只会影响第一个块的数据,我在第一次尝试解密后,直接用ff进行播放,ff能够解码并且播放成功,但是相对于其他未加密的HLS流来说,播放我解密后的数据会有3-5s的延时,这让我很是头疼,后来我就通过 ffplay -v trace 打印播放解密的日志,发现视频数据的id3信息丢失了,那么出问题出在第一个块。然后再次研究m3u8加解密IV的赋值方法,后发来经过多次试验,正确赋值IV,解密出来的数据能够及时播放。再后来,就出现了这篇文章的来由。 由此带来的延展,针对iv在CBC模式下的弱用,AES提供了更多的模式可供选择,详细的可以到wiki上了解。 https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation python实现AES加密目录 1.AES加密 2.需要导入的模块 3.定义好全局变量 4.编写加密函数 5.将密文解密 6.完整代码 1.AES加密 AES的区块长度固定为128 比特,密钥长度则可以是128,192或256比特; AES有多种加密模式,其中CBC分组模式是SSL、IPSec的标准。使用CBC加密模式时需要提前给出一段初始化向量iv,因为明文分组都是根据前一个明文分组加密的,所以第一个明文分组需要用到iv来加密。 2.需要导入的模块 import base64from Crypto.Cipher import AES 3.定义好全局变量 iv = "1234567887654321"key = "miyaoxuyao16ziji"data = "hello world" iv是初始化向量,第一组明文就是用它加密的 key是密钥,这里选择的长度是128比特,所以字符串的长度要固定在16 data就是需要加密的数据 4.编写加密函数 AES的区块固定是128比特,也就是16字节,所以一开始要先判断明文是否小于16字节,如果小于16字节,需要补齐,为此要写一个补齐的函数。 # 将原始的明文用空格填充到16字节def pad(data): pad_data = data for i in range(0,16-len(data)): pad_data = pad_data + " " return pad_data 创建AES加密对象用创建好的加密对象,对明文进行加密把加密好的密文用base64编码把字符串解码成字符串代码: # 将明文用AES加密def AES_en(key, data): # 将长度不足16字节的字符串补齐 if len(data) < 16: data = pad(data) # 创建加密对象 AES_obj = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8")) # 完成加密 AES_en_str = AES_obj.encrypt(data.encode("utf-8")) # 用base64编码一下 AES_en_str = base64.b64encode(AES_en_str) # 最后将密文转化成字符串 AES_en_str = AES_en_str.decode("utf-8") return AES_en_str 5.将密文解密 解密是加密的逆过程,按着加密代码的逆序很容易就能写出 将密文字符串重新编码成bytes类型将base64编码解开创建AES解密对象用解密对象对密文解密将补齐的空格用strip()函数除去将明文解码成字符串 代码: def AES_de(key, data): # 解密过程逆着加密过程写 # 将密文字符串重新编码成二进制形式 data = data.encode("utf-8") # 将base64的编码解开 data = base64.b64decode(data) # 创建解密对象 AES_de_obj = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8")) # 完成解密 AES_de_str = AES_de_obj.decrypt(data) # 去掉补上的空格 AES_de_str = AES_de_str.strip() # 对明文解码 AES_de_str = AES_de_str.decode("utf-8") return AES_de_str 6.完整代码 import base64from Crypto.Cipher import AESiv = "1234567887654321"key = "miyaoxuyao16ziji"data = "hello world"# 将原始的明文用空格填充到16字节def pad(data): pad_data = data for i in range(0,16-len(data)): pad_data = pad_data + " " return pad_data# 将明文用AES加密def AES_en(key, data): # 将长度不足16字节的字符串补齐 if len(data) < 16: data = pad(data) # 创建加密对象 AES_obj = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8")) # 完成加密 AES_en_str = AES_obj.encrypt(data.encode("utf-8")) # 用base64编码一下 AES_en_str = base64.b64encode(AES_en_str) # 最后将密文转化成字符串 AES_en_str = AES_en_str.decode("utf-8") return AES_en_strdef AES_de(key, data): # 解密过程逆着加密过程写 # 将密文字符串重新编码成二进制形式 data = data.encode("utf-8") # 将base64的编码解开 data = base64.b64decode(data) # 创建解密对象 AES_de_obj = AES.new(key.encode("utf-8"), AES.MODE_CBC, iv.encode("utf-8")) # 完成解密 AES_de_str = AES_de_obj.decrypt(data) # 去掉补上的空格 AES_de_str = AES_de_str.strip() # 对明文解码 AES_de_str = AES_de_str.decode("utf-8") return AES_de_strdata = AES_en(key, data)print(data)data = AES_de(key, data)print(data) 以上是关于关于AES加解密中CBC模式的IV初始化向量的安全性问题的主要内容,如果未能解决你的问题,请参考以下文章 Android番外篇 ‘gradlew‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件 vmware vsphere client 提示无法连接到远程服务器(无法连接连接vc,能ping通) 您可能还会对下面的文章感兴趣: 相关文章 浏览器打不开网址提示“ERR_CONNECTION_TIMED_OUT”错误代码的解决方法 如何安装ocx控件 VMware的虚拟机为啥ip地址老是自动变化 vbyone和EDP区别 linux/debian到底怎么重启和关机 苹果平板键盘被弄到上方去了,如何调回正常? 机器学习常用距离度量 如何查看kindle型号