33

聊聊AES

 4 years ago
source link: https://blog.huoding.com/2019/05/06/739?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

zEv632r.jpg!web

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 没有?

ZJ7N7rI.png!web

欢迎关注我们的公众号


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK