AES128-CMAC算法详解与实现
创作时间:
作者:
@小白创作中心
AES128-CMAC算法详解与实现
引用
CSDN
1.
https://blog.csdn.net/xiaohuanxiong_/article/details/139493386
CMAC(Cipher-based MAC)是一种基于密码的MAC算法,它基于块密码算法(如AES)和一个密钥来生成认证码。本文将详细介绍CMAC的工作原理,并提供基于Python和C/C++的验证代码示例。
简介
CMAC(Cipher-based MAC),基于密码的MAC,是一种基于密码的MAC算法,它基于块密码算法(如AES)和一个密钥来生成认证码。CMAC是一种对称密钥加密算法,通常与对称密钥算法(如AES)结合使用,以提供消息的完整性和真实性验证。本文主要用于安全算法验证(基于AES),故有些名词可能不太准确,具体算法可参考 RFC 4493。
(以上来自维基百科:CMAC1或CMAC2)
CMAC的工作原理
- 初始化:CMAC使用一个固定长度的密钥来初始化。密钥的长度通常与底层的对称加密算法(如AES)相关联。
- 分块处理:首先,将消息分成多个固定长度的块。如果消息长度不是块大小的倍数,则可以使用填充来将其填充到合适的大小。
- 生成子密钥:根据初始密钥生成用于加密的子密钥。通常,CMAC使用两个不同的子密钥,分别用于生成左右两个分支的子密钥。
- 生成MAC:
- 左分支:将消息的每个块与左分支的子密钥进行加密。对于最后一个块,如果长度不够,则使用填充。
- 右分支:将左分支的结果进行异或运算,然后再与右分支的子密钥进行加密。
- 结果:将右分支的加密结果截取指定长度作为最终的认证码。
- 认证:将消息的认证码与生成的MAC进行比较。如果两者相匹配,则消息未被篡改,认证成功。
代号(Char) 含义(Meaning)
b 加密块的位长(bit)
K 用于AES的密钥
K1 子密钥1,用于左分支
K2 子密钥2,用于右分支
M 消息
M_i 消息块i
CMAC示例
基于Python的验证代码
需要安装Crypto库:
from Crypto.Cipher import AES
from Crypto.Hash import CMAC
from binascii import hexlify, unhexlify
key = unhexlify('0102030405060708090a0b0c0d0e0f10')
seed = unhexlify('100f0e0d0c0b0a090807060504030201')
mac = CMAC.new(key, seed, ciphermod=AES)
print("AES_CMAC:", mac.hexdigest())
# result : 5becb7b36a0c7e019e9caf10f3971b00
基于C/C++的验证代码
#include "aes.h"
#include "windows.h"
#include <stdio.h>
#include <stdint.h>
#include <string.h>
void AES_128(const uint8_t *key, const uint8_t *input, uint8_t *output)
{
int a = 16;
aes_encrypt_ecb(key, 16, input, 16, output, &a);
}
const unsigned char const_Rb[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x87};
const unsigned char const_Zero[16] = {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
void xor_128(unsigned char *a, const unsigned char *b, unsigned char *out)
{
int i;
for (i = 0; i < 16; i++)
{
out[i] = a[i] ^ b[i];
}
}
void print_hex(char *str, unsigned char *buf, int len)
{
int i;
for (i = 0; i < len; i++)
{
if ((i % 16) == 0 && i != 0)
printf(str);
printf("%02x", buf[i]);
if ((i % 4) == 3)
printf(" ");
if ((i % 16) == 15)
printf("\n");
}
if ((i % 16) != 0)
printf("\n");
}
void print128(unsigned char *bytes)
{
int j;
for (j = 0; j < 16; j++)
{
printf("%02x", bytes[j]);
if ((j % 4) == 3)
printf(" ");
}
}
void leftshift_onebit(unsigned char *input, unsigned char *output)
{
int i;
unsigned char overflow = 0;
for (i = 15; i >= 0; i--)
{
output[i] = input[i] << 1;
output[i] |= overflow;
overflow = (input[i] & 0x80) ? 1 : 0;
}
return;
}
void MAC_GenSubKey(unsigned char *key, unsigned char *K1, unsigned char *K2)
{
unsigned char L[16] = {
0,
};
unsigned char Z[16] = {
0,
};
unsigned char tmp[16] = {
0,
};
int i;
for (i = 0; i < 16; i++)
Z[i] = 0;
AES_128(key, Z, L);
if ((L[0] & 0x80) == 0)
{
leftshift_onebit(L, K1);
}
else
{
leftshift_onebit(L, tmp);
xor_128(tmp, const_Rb, K1);
}
if ((K1[0] & 0x80) == 0)
{
leftshift_onebit(K1, K2);
}
else
{
leftshift_onebit(K1, tmp);
xor_128(tmp, const_Rb, K2);
}
printf("\nLeft:\n");
for (int j = 0; j < 15; j++)
{
printf("%02x", K1[j]);
}
printf("\nRight:\n");
for (int j = 0; j < 15; j++)
{
printf("%02x", K1[j]);
}
printf("\n");
}
void padding(unsigned char *lastb, unsigned char *pad, int length)
{
int j;
for (j = 0; j < 16; j++)
{
if (j < length)
{
pad[j] = lastb[j];
}
else if (j == length)
{
pad[j] = 0x80;
}
else
{
pad[j] = 0x00;
}
}
}
void AES_CMAC(unsigned char *key, unsigned char *input, int length,
unsigned char *mac)
{
unsigned char X[16], Y[16], M_last[16], padded[16];
unsigned char K1[16], K2[16];
int n, i, flag;
MAC_GenSubKey(key, K1, K2);
n = (length + 15) / 16;
if (n == 0)
{
n = 1;
flag = 0;
}
else
{
if ((length % 16) == 0)
{
flag = 1;
}
else
{
flag = 0;
}
}
if (flag)
{
xor_128(&input[16 * (n - 1)], K1, M_last);
}
else
{
padding(&input[16 * (n - 1)], padded, length % 16);
xor_128(padded, K2, M_last);
}
for (i = 0; i < 16; i++)
X[i] = 0;
for (i = 0; i < n - 1; i++)
{
xor_128(X, &input[16 * i], Y);
AES_128(key, Y, X);
}
xor_128(X, M_last, Y);
AES_128(key, Y, X);
for (i = 0; i < 16; i++)
{
mac[i] = X[i];
}
}
int main()
{
unsigned char L[16], K1[16], K2[16], T[16], TT[12];
unsigned char M[16] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10};
unsigned char key[16] = {
0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01};
printf("--------------------------------------------------\n");
printf("key:\n");
for (int i = 0; i < 15; i++)
{
printf("%02x", key[i]);
}
printf("\n");
MAC_GenSubKey(key, K1, K2);
printf("M ");
print_hex(" ", M, 16);
AES_CMAC(key, M, 16, T);
printf("AES_CMAC ");
print128(T);
printf("\n");
printf("--------------------------------------------------\n");
system("pause");
return 0;
}
示例数据
以下数据可供参考,用于验证算法准确性:
消息(Seed/Message) | 密钥(Key) | 结果(Result/Token) |
|---|---|---|
100f0e0d0c0b0a090807060504030201 | 0102030405060708090a0b0c0d0e0f10 | 5becb7b36a0c7e019e9caf10f3971b00 |
0102030405060708090a0b0c0d0e0f10 | 100f0e0d0c0b0a090807060504030201 | 95c6652305da28e31d6a7ab99dfd2998 |
66B0CF31F56AC16ABF4610DF87A1AE20 | 3D2E6DE2A12517BAC5B31BBD0E7E3B54 | 0d4de052272cc4f56e2a4fbc8dcfa931 |
热门推荐
柳州龙潭公园:春季赏紫荆花必打卡!
双十一囤货必备:80后白领的黄苦荞茶养生法
黄苦荞茶安全饮用指南:专家揭秘
猫咪打喷嚏,是感冒还是过敏?一文教你轻松分辨
猫咪打喷嚏引发的家庭健康危机
幼儿园大班下学期家长会:如何玩转热身游戏?
幼儿园大班下学期家长会:做好幼小衔接,助力孩子成长
盛唐密盒:一场文旅融合的创新实践
大唐不夜城《贞观之治》最新演出时间揭晓!
燃情冰雪季!江西七星岭滑雪场赛事预告及游玩攻略
Alex教练教你安全滑雪初体验!
庐山与武功山:江西最佳滑雪胜地推荐
冬日限定快乐!江西铜鼓七星岭滑雪场全攻略
AI技术如何改变我们的未来生活?
李白诗歌中的道家哲学:你读懂了吗?
李白《将进酒》:“君不见黄河之水天上来”的艺术魅力与思想内涵
李白在长安:诗与远方的碰撞
中国地图在地理教学中的创新应用与效果研究
从裴秀到《康熙皇舆全览图》:中国古地图的文化传承
你的洗衣机甩桶漏水了吗?来看看这些妙招!
爱情的真谛:在日常生活中表达爱意
城市市容整治,你我共同参与
医生解答:吃栗子真的能滋养头发吗?
板栗的功效:解锁健康宝藏,探秘小小果实中的营养奇迹!
从新宿御苑到清水寺:日本经典赏樱路线全攻略
用iPhone拍出惊艳樱花照,你get了吗?
腰背痛的克星是“燕子飞”?这些人,小心越做越严重
封神,乱成了一锅粥
秋冬洗衣健康大揭秘:洗衣机藏污纳垢竟如此可怕!
洗衣机维修攻略:小白也能搞定!