赞
踩
消息认证码(Message Authentication Code) 是一种确认完整性并进行认证的技术,简称 MAC。
使用消息认证码可以确认自己收到的消息是否就是发送者的本意,也就是说可以判断消息是否被篡改,是否有人伪装成发送者发送了这条消息。
消息认证码的输入包括任意长度的消息和一个发送者与接收者之间的共享密钥。输出固定长度的数据,输出的数据就是 MAC 值。
在上个章节我们讲到了单向散列函数,单向散列函数也可以用在检查消息的完整性。那消息认证码和单向散列函数的区别是什么呢?
单向散列函数: 只有一个输入参数,就是消息本身, 只能用在检查消息的完整性。
消息认证码: 有两个输入参数,一个是消息本身,另一个是共享秘钥。所以消息认证码不仅可以确认消息的完整性,还可以确认消息发送者是否持有共同的共享秘钥。
单向散列函数与消息认证码的区别如下图:
消息认证码和单向散列函数的区别就在有没有这个共享密钥上了。所以消息认证码就是利用共享密钥进行认证的。消息认证码和单向散列函数的散列值一样,如果消息发生了 1 比特的变化,MAC 值也会发生变化。所以消息认证码正是用这个性质来进行完整性的。
所以消息认证码可以理解为消息认证码是一种与密钥相关联的单向散列函数。单向散列函数也是实现消息认证码最常见的一种方法,该类方法称为HMAC。
消息认证码可以有其他密码技术构造,常见的消息认证码实现方式有:单向散列函数实现、分组密码实现和认证密码实现。
单向散列函数实现
使用单向散列函数实现的消息认证码称为HMAC。单向散列函数可以检查消息的完整性,但无法验证消息来源的可靠性,所以单向散列算法不能直接应用于消息认证码算法。在HMAC算法中,共享秘钥没有长度限制,输入消息也没有长度限制,但HMAC算法的输入结果长度是固定的。
分组密码实现
分组密码中的CBC模式也可以实现消息认证码算法,典型的实现方法为CBC-MAC。分组密码的秘钥可以作为消息认证码中的共享秘钥,对消息进行加密处理后将最后一个分组加密的结果作为消息认证码。由于CBC模式的最后一个分组结果由整个消息和密钥决定,所以这种方法也可以保证消息的完整性和真实性。
CMAC也是基于分组密码的消息认证码算法,CMAC算法通过共享密钥派生出两个中间密钥K1和K2,其算法的安全性要高于CBC-MAC算法。
认证加密算法实现
认证加密算法是在通信过程中提供数据机密性和完整性的密码算法,可以看做对称加密算法和消息认证码的结合。 认证加密算法的典型实现包括GCM算法和CCM算法,相比单向散列算法和分组密码实现的消息认证码算法,认证加密算法的应用更广泛。
HMAC 是一种使用单向散列函数来构造消息认证码的方法,HMAC 中的 H 是 Hash 的意思。官方文档见 RFC 2104
任何高强度的单向散列函数都可以被用于 HMAC 中,例如 SHA-1、SHA-224、SHA-256、SHA-384、SHA-512 所构造的 HMAC,分别称为 HMAC-SHA1、HMAC-SHA-224、HMAC-SHA-256、HMAC-SHA-384、HMAC-SHA-512。
HMAC 计算 MAC 值步骤如下:
如果密钥比单向散列函数的分组长度要短,就需要在末尾填充 0,使最终长度和单向散列函数分组长度一样。
如果密钥比分组长度要长,则要用单向散列函数求出密钥的散列值,然后把这个散列值作为 HMAC 的密钥。
将填充后的密钥和 ipad 的比特序列进行 XOR 计算。ipad 是 00110110 (16进制的 36)不断循环直到长度和分组长度一样长的比特序列。ipad 的 i 是 inner 内部的意思。
XOR 得到的最终结果是一个和单向散列函数的分组长度相同,且和密钥相关的比特序列。这个序列称为 ipadkey。
把 ipadkey 附加在消息的开头。
把第 3 步的结果输入单向散列函数,计算出散列值。
将填充后的密钥和 opad 的比特序列进行 XOR 计算。opad 是 01011100 (16进制的 5C)不断循环直到长度和分组长度一样长的比特序列。opad 的 o 是 outer 外部的意思。
XOR 得到的最终结果是一个和单向散列函数的分组长度相同,且和密钥相关的比特序列。这个序列称为 opadkey。
把 opadkey 附加在散列值的开头。
把第 6 步的结果输入单向散列函数,计算出散列值。这个散列值即为最终 MAC 值。
最终的 MAC 值一定是一个和输入消息以及密钥都相关的长度固定的比特序列。
HMAC 如果用伪代码表示:
HMAC = hash(opadkey || hash(ipadkey || message))
= hash( (key ⊕ opad) || hash( (key ⊕ ipad) || message) )
opadkey = key ⊕ opad
ipadkey = key ⊕ ipad
key 为密钥,message 为消息,hash 计算为 hash(),A || B 代表 A 放在 B 的前面
具体举一个 HMAC_MD5 的例子:
/*
** Function: hmac_md5
*/
void hmac_md5(text, text_len, key, key_len, digest)
unsigned char* text; /* pointer to data stream */
int text_len; /* length of data stream */
unsigned char* key; /* pointer to authentication key */
int key_len; /* length of authentication key */
caddr_t digest; /* caller digest to be filled in */
{
MD5_CTX context;
unsigned char k_ipad[65]; /* inner padding -
* key XORd with ipad
*/
unsigned char k_opad[65]; /* outer padding -
* key XORd with opad
*/
unsigned char tk[16];
int i;
/* if key is longer than 64 bytes reset it to key=MD5(key) */
if (key_len > 64) {
MD5_CTX tctx;
MD5Init(&tctx);
MD5Update(&tctx, key, key_len);
MD5Final(tk, &tctx);
key = tk;
key_len = 16;
}
/*
* the HMAC_MD5 transform looks like:
*
* MD5(K XOR opad, MD5(K XOR ipad, text))
*
* where K is an n byte key
* ipad is the byte 0x36 repeated 64 times
* opad is the byte 0x5c repeated 64 times
* and text is the data being protected
*/
/* start out by storing key in pads */
bzero( k_ipad, sizeof k_ipad);
bzero( k_opad, sizeof k_opad);
bcopy( key, k_ipad, key_len);
bcopy( key, k_opad, key_len);
/* XOR key with ipad and opad values */
for (i=0; i<64; i++) {
k_ipad[i] ^= 0x36;
k_opad[i] ^= 0x5c;
}
/*
* perform inner MD5
*/
MD5Init(&context); /* init context for 1st
* pass */
MD5Update(&context, k_ipad, 64) /* start with inner pad */
MD5Update(&context, text, text_len); /* then text of datagram */
MD5Final(digest, &context); /* finish up 1st pass */
/*
* perform outer MD5
*/
MD5Init(&context); /* init context for 2nd
* pass */
MD5Update(&context, k_opad, 64); /* start with outer pad */
MD5Update(&context, digest, 16); /* then results of 1st
* hash */
MD5Final(digest, &context); /* finish up 2nd pass */
}
输出
key = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b
key_len = 16 bytes
data = "Hi There"
data_len = 8 bytes
digest = 0x9294727a3638bb1c13f48ef8158bfc9d
key = "Jefe"
data = "what do ya want for nothing?"
data_len = 28 bytes
digest = 0x750c783e6ab0b503eaa86e310a5db738
key = 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
key_len 16 bytes
data = 0xDDDDDDDDDDDDDDDDDDDD...
..DDDDDDDDDDDDDDDDDDDD...
..DDDDDDDDDDDDDDDDDDDD...
..DDDDDDDDDDDDDDDDDDDD...
..DDDDDDDDDDDDDDDDDDDD
data_len = 50 bytes
digest = 0x56be34521d144c88dbb8c733f0e8b3f6
CBC-MAC
CBC-MAC是数据认证算法的扩展实现。CBC-MAC的分组密码算法通常使用对称加密算法AES算法,并且使用全零分组作为初始化向量。但CBC-MAC在使用的时候需要满足一些限制条件,它仅能处理固定长度的消息。对于非固定长度的消息,该算法存在一定的安全隐患,假设给定一个消息分组M, 可以通过CBC-MAC计算得到消息认证码T,由于算法的特性,攻击者可以很容易计算出两个消息分组M‘=M||(M^T)的消息认证码,其结果还是T。为了解决该问题,人们在CBC-MAC基础上设计了CMAC算法。
CMAC
CMAC为了解决非固定长度消息的安全性问题,CMAC会通过密钥扩展出两个子密钥,子密钥会在最后一个消息分组加密前参与运算。CMAC的计算过程主要分为三部分,分别是子密钥生成、消息认证码的生成和验证消息认证码。
CCM是CTR-CBC-MAC的简写,CBC算法计算过程需要使用CTR模式和CBC-MAC算法。 加密和认证时CCM的输入包括明文P、一次性整数N(nonce)、相关数据A和密钥K。输出密文C和认证码T。
CCM认证过程是对格式化后的数据进行CBC-MAC运算,输入得到长度为t的消息认证码Tag,然后使用Ctr0作为计数值对Tag进行AES-CTR加密,输入作为最终的消息认证码T。对明文的加密过程同样使用计数器模式,不同的是计数值从Ctr1开始,该过程无需对明文进行填充。完成上述操作后,将密文和消息认证码作为最终的输出结果。
CCM算法需要对明文进行两次加密处理,第一次使用CBC-MAC计算消息认证码,第二次使用CTR模式加密明文。但由于加密和认证可以同时进行,并且加密过程中所使用的CTR模式也支持并行计算,这些特点使该算法在有相应的硬件支持时性能提高显著。
GCM的整体流程与CCM类似,GCM的计算过程需要使用GHASH算法和GCTR算法。
进行填充。完成上述操作后,将密文和消息认证码作为最终的输出结果。
CCM算法需要对明文进行两次加密处理,第一次使用CBC-MAC计算消息认证码,第二次使用CTR模式加密明文。但由于加密和认证可以同时进行,并且加密过程中所使用的CTR模式也支持并行计算,这些特点使该算法在有相应的硬件支持时性能提高显著。
GCM的整体流程与CCM类似,GCM的计算过程需要使用GHASH算法和GCTR算法。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。