聊聊AES

说起加密,通常分为对称加密和非对称加密,所谓对称加密中的对称,指的是加密和解密使用的是同一个密钥,如此说来什么是非对称就不用我多做解释了。对称加密相对于非对称加密而言,优点是速度快,缺点是安全性相对低一点,不过只要能保证密钥不泄露,其安全性还是有保证的,所以在实际项目中,对称加密的使用非常广泛。

目前最流行的对称加密标准是 AES。需要说明的是:AES 是一个标准,而不是一个算法,实际上背后的算法是 Rijndael,二者很容易混淆,比如很多人会搞不清楚 AES256 和 Rijndael256 有什么不同,甚至会认为是一个东西。其实 AES256 中的 256 指的是密钥的长度是 256 位,而 Rijndael256 中的 256 指的是分组大小是 256 位,更进一步说明的话,因为 AES 的分组大小是固定的 128 位,所以我们可以认为 AES256 等同于密钥长度是 256 位的 Rijndael128,听着有点绕,推荐阅读「 AES 简介 」:

AES

了解了 AES 密钥之后,再说一下填充的概念。引用「 漫画解读:什么是AES算法 」中的描述:在对明文加密的时候,并不是把整个明文一股脑加密成一整段密文,而是把明文拆分成一个个等长的明文块,这些明文块经过加密器的复杂处理,生成一个个独立的密文块,再把这些密文块拼接在一起,就是最终的加密结果。但是这里涉及到一个问题:假如一段明文长度是 192 位,如果按每128 位一个明文块来拆分的话,那么最后一个明文块只有 64 位,不足128 位,此时就需要填充,那为了补齐 128 位而额外填充的 64 位数据具体是什么内容呢,答案就是字节长度: 64 位换算成字节就是 8 字节,所以额外填充的 8 字节的具体内容都是 0x08。再说一个例子,如果明文长度是 128 位,按每 128 位一个明文块来拆分的话,恰好是一个完整的块,此时还需要填充么?答案是需要,仍然需要填充一个完整块的长度!为什么呢?因为加密前要填充,解密后要去掉填充,如果没有填充,解密后最后一个字节恰好是 0x01,那么不方便判断这个 0x01 是实际的数据还是之前填充的数据。实际使用中有很多填充标准,其中最常见的是 PKCS#5 和 PKCS#7,它们的主要区别在于块大小的定义上: PKCS#5 中的块特指长度是 64 位(也就是 8 字节),而 PKCS#7 中的块没有特指某个长度,一般是 128 位(也就是 16 字节),可以说 PKCS#5 是 PKCS#7 的一个特例。

了解了 AES 密钥和填充两个概念后,还需要了解一下模式的概念,不过鉴于实际使用 AES 的时候,多数时候采用的都是 CBC 模式,本文就不详细展开讨论此概念了,但是需要说明的是 CBC 模式中有一个 iv (初始化向量)的概念,乍一看上去它好像是另一个密钥,实际上它并不是 Key,可以把它理解成我们使用 md5 时的 salt,通过对不同的数据使用不同的 salt,可以避免遭遇彩虹表之类的暴力破解,iv 的作用亦如此,更多说明可以参考: Why IV does not have to be secret yet has to be random

下面我通过一个例子来加深一下大家学习的印象:OpenSSL 缺省会执行填充,那么它执行的是 PKCS#5 还是 PKCS#7 呢?我们不妨做个试验来验证一下:

shell> echo -n a \
    | openssl enc -e \
        -aes-256-cbc \
        -K 3132333435363738313233343536373831323334353637383132333435363738 \
        -iv 31323334353637383132333435363738 \
    | openssl enc -d \
        -aes-256-cbc \
        -K 3132333435363738313233343536373831323334353637383132333435363738 \
        -iv 31323334353637383132333435363738 \
        -nopad \
    | xxd

00000000: 610f 0f0f 0f0f 0f0f 0f0f 0f0f 0f0f 0f0f  a...............

填充加密后在解密的时候不去掉填充(nopad),这样数填充了多少个字节就能确定答案,如上明文数据是「a」(0x61),填充数据是 15 个 0x0f,所以我们可知块大小是 16 个字节(不是 8 个字节,所以不是 PKCS#5),所以是 PKCS#7。

最后需要说明的是,上面那些一大长串的东西是什么玩意?这是因为实际上 OpenSSL 在命令行上使用时,K 和 iv 传递的都是十六进制的字符串:

shell> echo -n 12345678123456781234567812345678 | xxd -p
3132333435363738313233343536373831323334353637383132333435363738

shell> echo -n 1234567812345678 | xxd -p
31323334353637383132333435363738

也就是说,真正的 K 是「12345678123456781234567812345678」,32 个字节,也就是 256 位;真正的 iv 是「1234567812345678」,16 个字节,也就是 128 位,均符合 AES256 的标准要求。怎么样,看完本文,你理解了 AES 没有?

欢迎关注我们的公众号

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章