上次讲到的单向散列函数只能保证数据传输的完整性,不能提供认证功能,即 Bob 不能确定消息是否来自 Alice,只能确定消息是完整的,没有被篡改。
假设有以下场景,现在 Alice 和 Bob 分别是两家银行:
所以我们除了要关注汇款请求(消息)的“完整性”,还要关注“认证”问题,即“消息是否来自正确的发送者”。下面要介绍的消息认证码即能保证消息的完整性,还能对消息进行认证。
消息认证码(Message Authentication Code)是一种确认完整性并进行认证的技术,简称为MAC。
消息认证码的输入为任意长度的消息和一个发送者与接收者之间共享的密钥,它可以输出固定长度的数据,这个数据称为 MAC 值。
根据任意长度的消息输出固定长度的数据,这一点和单向散列函数很类似。但由于计算 MAC 值必须持有共享密钥,没有共享密钥的人就无法计算 MAC 值,因此消息认证码利用这一性质来完成认证。此外,消息认证码和单向散列函数一样具有雪崩效应,哪怕消息中发生 1 1 1比特的变化,也会导致 MAC 值发生不可区分的改变。
消息认证码有多种实现方式,我们可以先简单的将其理解为:消息认证码是一种与密钥相关联的单向散列函数:
我们仍然假设 Alice 和 Bob 是两家银行,消息认证码的使用步骤如下:
消息认证码的密钥配送问题:由于发送者和接收者之间需要共享密钥,这一点和对称密码很相似。实际上,对称密码的密钥配送问题在消息认证码中同样会发生。要解决该问题,我们需要像对称密码一样使用一些共享密钥的方法,至于使用哪种配送方法,需要根据具体的目的来进行选择。
使用 AES之 类的分组密码可以实现消息认证码。
我们将分组密码的密钥作为消息认证码的共享密钥来使用,并用 CBC 模式将消息全部加密,然后将最后一个密文分组用作 MAC 值。这是因为 CBC 模式的最后一个分组会受到整个消息以及密钥的双重影响,因此可以将它用作消息认证码。
下面我们来看一下使用单向散列函数实现的消息认证码:HMAC。
HMAC是一种使用单向散列函数来构造消息认证码的方法,其中 HMAC 的 H 就是 Hash 的意思。
HMAC 中使用的单向散列函数并不局限于一种,任何高强度的单向散列函数都可以被用于 HMAC。例如用 SHA-256、SHA-512 所构造的 HMAC,分别称为 HMAC-SHA-256、HMAC-SHA-512。
HMAC 的步骤:
00110110
(0x36
)不断循环反复直到分组长度所形成的比特序列,其中 ipad 的 i 是 inner(内部)的意思。01011100
(0x5C
)不断循环反复直到分组长度所形成的比特序列,其中 opad 的 o 是 outer(外部)的意思。通过上述流程我们可以看出,最后得到的 MAC 值,一定是一个和输入的消息以及密钥都相关的长度固定的比特序列。
此外,根据 RFC 2104,HMAC 的数学公式为:
HMAC ( K , m ) = H ( ( K ′ ⊕ o p a d ) ∣ ∣ H ( ( K ′ ⊕ i p a d ) ∣ ∣ m ) ) {\displaystyle {\textit {HMAC}}(K,m)=H{\Bigl (}(K'\oplus opad)\;||\;H{\bigl (}(K'\oplus ipad)\;||\;m{\bigr )}{\Bigr )}} HMAC(K,m)=H((K′⊕opad)∣∣H((K′⊕ipad)∣∣m))
其中:
0x5c5c5c…5c5c
,一段十六进制常量)0x363636…3636
,一段十六进制常量)HMAC的伪代码如下:
function hmac (key, message) {
if (length(key) > blocksize) {
key = hash(key) // keys longer than blocksize are shortened
}
if (length(key) < blocksize) {
// keys shorter than blocksize are zero-padded (where ∥ is concatenation)
key = key ∥ [0x00 * (blocksize - length(key))] // Where * is repetition.
}
o_key_pad = [0x5c * blocksize] ⊕ key // Where blocksize is that of the underlying hash function
i_key_pad = [0x36 * blocksize] ⊕ key // Where ⊕ is exclusive or (XOR)
return hash(o_key_pad ∥ hash(i_key_pad ∥ message)) // Where ∥ is concatenation
}
认证加密(AE或AEAD,Authenticated Encryption,Authenticated Encryption with Associated Data)是一种将对称密码与消息认证码相结合,同时满足机密性、完整性和认证三大功能的机制。常见的AEAD算法有chacha20-poly1305
和aes-256-gcm
。
认证加密的方法:
关于GCM:GCM(Galois/Counter Mode)是一种认证加密方式。GCM使用AES等128比特分组密码的CTR模式,并使用一个反复进行加法和乘法运算的散列函数来计算MAC值。由于CTR模式的本质是对递增计数器值进行加密,因此可通过对若干分组进行并行处理来提高运行速度。此外,由于CTR模式加密与MAC的值计算使用的是相同的密钥,因此在密钥管理方面也更容易。
我们仍然假设 Alice 和 Bob 是两家银行,现在有如下场景:
在这里,Mallory 并没有破解消息认证码,而只是将 Alice 银行的正确 MAC 值保存下来重复利用而已。这种攻击方式称为重放攻击(replay attack):
消息认证码无法解决的问题如下:
为了解决上面这两个问题,我们可以使用数字签名(将在之后介绍)。