AES-CMAC使用了高级加密标准作为组分。为了产生一个消息认证码,CMAC需要一个密钥,消息message及消息的长度length作为输入,输出是消息认证码。
AES-CMAC的核心是CBC-MAC。对于待加密消息M,应用CBC-MAC算法。在CMAC操作中有两种情况:
如果输入消息长度等于Block的整数倍,最后的Block M_n需要先于K1异或再进行处理;
如果输入的消息长度不等于Block的整数倍,最后的Block M_n需要补齐到一个Block的大小,与K2异或,再进行处理。上一次处理的结果将成为下一次处理的输入。
子密钥生成算法:
输入密钥K,输出子密钥K1与K2。过程如下:
①用密钥K对全零输入块加密得到L
②K1通过如下方式导出:
如果L的最高有效位为0,则K1:=L左移1位;否则,K1等于const_Rb与左移1位的L异或。
③K2通过如下方式导出:
如果K1的最高有效位为0,则K2:=K1左移1位;否则,K2等于const_Rb与左移1位的K1异或。
根据参考文献[1]javafr_RFC_4493给出的说明及测试实例,编写AES_CMAC类。在main()函数中加入取消注释宏定义AES_CMAC_TEST,进行AES-CMAC测试。
待测试消息是:
M= 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
密钥是:
K= 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
注意,测试中我们不是把M作为输入,而是截取其一部分作为输入消息,以测试AES_CMAC类处理不同长度的消息的正确性。测试结果如图:
对照参考文献[1]:
结果符合预期。
aes_cmac.h:
/************************************************************************/
/*AES_CMAC: Use AES-128
/*Author: chenweiliang
/*Version: 1.0
/*Reference: javafr_RFC_4493
/************************************************************************/
#pragma once
#include "aes.h"
class AES_CMAC
{
public:
//key: 128-bit key
AES_CMAC(const byte key[]);
virtual ~AES_CMAC();
static const byte const_Rb[16];
static const byte const_Zero[16];
//msg: message to be authenticated
//len: length of the message in bytes
//mac: message authentication code
void getCMAC(byte *msg, int len, byte *mac);
//Copy K1 to key1
void getK1(byte *key1);
//copy K2 to key2
void getK2(byte *key2);
protected:
private:
AES mAes_128;
//128-bit key
byte mK[16];
//128-bit first subkey
byte mK1[16];
//128-bit second subkey
byte mK2[16];
//Utils function
void leftShiftOneBit(byte *input_128,byte *output_128);
void xor128(const byte *a, const byte *b, byte *out);
void generateSubKey();
//lastb: last block of the intput message
//pad: last block after padding
//length: length of last block before padding
void padding(byte *lastb,byte *pad,int length);
};
aes_cmac.cpp:
/************************************************************************/
/*AES_CMAC: Use AES-128
/*Author: chenweiliang
/*Version: 1.0
/*Reference: javafr_RFC_4493
/************************************************************************/
#include "aes_cmac.h"
const byte AES_CMAC::const_Rb[16] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x87
};
const byte AES_CMAC::const_Zero[16] = {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
};
void AES_CMAC::leftShiftOneBit(byte *input_128, byte *output_128)
{
byte overflow = 0x00;
for (int i = 15; i >= 0; i--){
output_128[i] = input_128[i] << 1;
output_128[i] |= overflow;
overflow = (input_128[i] & 0x80) ? 0x01 : 0x00;
}
return;
}
void AES_CMAC::xor128(const byte *a, const byte *b, byte *out)
{
for (int i = 0; i < 16; i++){
out[i] = a[i] ^ b[i];
}
}
AES_CMAC::AES_CMAC(const byte key[])
{
for (int i = 0; i < 16; i++){
mK[i] = key[i];
}
mAes_128 = AES(mK,KEYLENGTH::KEY_LENGTH_16BYTES);
generateSubKey();
}
void AES_CMAC::generateSubKey()
{
//for output of AES-128 applied to 0^128
byte L[16];
//0^128
byte Z[16];
byte temp[16];
for (int i = 0; i < 16; i++){
Z[i] = 0;
}
mAes_128.encrypt(Z,L);
//If MSB(L)==0,then mK1=L<<1
if ((L[0] & 0x80) == 0){
leftShiftOneBit(L, mK1);
}
//mK1=(L<<1)xor(Rb)
else{
leftShiftOneBit(L, temp);
xor128(temp, const_Rb, mK1);
}
if ((mK1[0] & 0x80) == 0){
leftShiftOneBit(mK1, mK2);
}
else{
leftShiftOneBit(mK1, temp);
xor128(temp, const_Rb, mK2);
}
return;
}
void AES_CMAC::padding(byte *lastb, byte *pad, int length)
{
for (int i = 0; i < 16; i++){
if (i < length){
pad[i] = lastb[i];
}
else if (i == length){
pad[i] = 0x80;
}
else{
pad[i] = 0x00;
}
}
}
void AES_CMAC::getCMAC(byte *msg, int len, byte *mac)
{
byte X[16];
byte Y[16];
byte msgLast[16];
byte padded[16];
//the number of blocks to be processed
int n;
//the number of bytes of the last block
int r;
//denoting last block is complete or not
bool isComplete;
n = (len + 15) / 16;
if (n == 0){
n = 1;
isComplete = false;
}
else{
if ((len % 16) == 0){
isComplete = true;
}
else{
isComplete = false;
}
}
if (isComplete){
xor128(&msg[16*(n-1)], mK1, msgLast);
}
else{
padding(&msg[16*(n-1)], padded, len%16);
xor128(padded, mK2, msgLast);
}
for (int i = 0; i < 16; i++){
X[i] = 0;
}
for (int i = 0; i < n - 1; i++){
//Y=(X)xor(meg_i)
xor128(X, &msg[16 * i], Y);
//X=AES(key,Y)
mAes_128.encrypt(Y, X);
}
xor128(X, msgLast, Y);
mAes_128.encrypt(Y, X);
for (int i = 0; i < 16; i++){
mac[i] = X[i];
}
}
void AES_CMAC::getK1(byte *key1)
{
for (int i = 0; i < 16; i++){
key1[i] = mK1[i];
}
}
void AES_CMAC::getK2(byte *key2)
{
for (int i = 0; i < 16; i++){
key2[i] = mK2[i];
}
}
AES_CMAC::~AES_CMAC()
{
}
main函数:
/************************************************************************/
/*Main: Test AES,CMAC
/*Author: chenweiliang
/*Version: 1.0
/*Note: To test AES-128,please comment the macro definition
/* "#define AES_CMAC_TEST" and uncomment it for CMAC testing
/************************************************************************/
#include "aes.h"
#include "aes_cmac.h"
#ifndef AES_CMAC_TEST
#define AES_CMAC_TEST
#endif // !AES_CMAC_TEST
#include <string>
void print128(unsigned char state[16]){
for (int i = 0; i < 16; i++){
printf("%02hhx", state[i]);
if (i % 8 == 7){
printf(" ");
}
}
printf("\n");
}
void print512(unsigned char data[64]){
for (int i = 0; i < 64; i++){
printf("%02hhx", data[i]);
if (i % 8 == 7){
printf(" ");
}
if (i % 32 == 31){
printf("\n");
}
}
printf("\n");
}
void printn(unsigned char data[], int n)
{
for (int i = 0; i < n; i++){
printf("%02hhx", data[i]);
if (i % 8 == 7){
printf(" ");
}
if (i % 16 ==15&&i!=n-1){
printf("\n ");
}
else if (i == n - 1){
printf("\n");
}
}
}
using namespace std;
int main()
{
#ifdef AES_CMAC_TEST
unsigned char K1[16], K2[16], mac[16], TT[12];
unsigned char M[64] = {
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
};
unsigned char key[16] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};
AES_CMAC aes_cmac = AES_CMAC(key);
printf("AES-CMAC Test\n");
printf("-------------------------------------------------------------------\n");
printf("SubKeyGeneration\n");
printf("K ");
print128(key);
printf("K1 ");
aes_cmac.getK1(K1);
print128(K1);
printf("K2 ");
aes_cmac.getK2(K2);
print128(K2);
printf("-------------------------------------------------------------------\n");
printf("\n-------------------------------------------------------------------\n");
aes_cmac.getCMAC(M, 0, mac);
printf("Example 1: len=0\n");
printf("M <Empty String>\n");
printf("AES-CMAC: ");
print128(mac);
printf("-------------------------------------------------------------------\n");
aes_cmac.getCMAC(M, 16, mac);
printf("Example 2: len=16\n");
printf("M ");
printn(M, 16);
printf("AES-CMAC: ");
print128(mac);
printf("-------------------------------------------------------------------\n");
aes_cmac.getCMAC(M, 40, mac);
printf("Example 3: len=40\n");
printf("M ");
printn(M, 40);
printf("AES-CMAC: ");
print128(mac);
printf("-------------------------------------------------------------------\n");
aes_cmac.getCMAC(M, 64, mac);
printf("Example 4: len=64\n");
printf("M ");
printn(M, 64);
printf("AES-CMAC: ");
print128(mac);
printf("-------------------------------------------------------------------\n");
#else
unsigned char input[16] =
{
0x01, 0x23, 0x45, 0x67,
0x89, 0xab, 0xcd, 0xef,
0xfe, 0xdc, 0xba, 0x98,
0x76, 0x54, 0x32, 0x10
};
unsigned char key[] =
{
0x0f, 0x15, 0x71, 0xc9,
0x47, 0xd9, 0xe8, 0x59,
0x0c, 0xb7, 0xad, 0xd6,
0xaf, 0x7f, 0x67, 0x98
};
printf("AES Test\n");
AES aes = AES(key, KEYLENGTH::KEY_LENGTH_16BYTES);
printf("Key length: 128\n");
printf("key: ");
print128(key);
printf("Message: ");
print128(input);
byte out[16];
aes.encrypt(input, out);
printf("ciphertext: ");
print128(out);
aes.decrypt(out,out);
printf("plaintext: ");
print128(out);
#endif // AES_CMAC_TEST
return 0;
}
[1] javafr_RFC_4493
[2]AES加密学习笔记http://blog.csdn.net/whycadi/article/details/6917394
[3]AES加密算法http://www.cnblogs.com/mingcn/archive/2010/10/31/aes_c.html
文章浏览阅读6.3k次,点赞2次,收藏15次。Python黑帽编程-ARP之一http://blog.csdn.net/supercooly/article/details/53956494参考文章:https://zhuanlan.zhihu.com/p/24645819参考文章:http://bbs.ichunqiu.com/thread-13429-1-1.html一、ARP协议中文名称:地址解析协议_python you must specify a valid interface name.
文章浏览阅读329次。此帖记录一下SOLO环境配置的过程以及一些问题和解决办法。_深度学习solo算法必须要安装mmcv吗
文章浏览阅读239次。泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。模板的格式template<typename T1, typename T2,…,typename Tn>返回值类型 函数名(参数列表){}模板分为类模板和函数模板模板它本身并不是函数,是编译..._模版是复用代码的一中机制,利用模版可以进行与类型无关的程序设计
文章浏览阅读9.9k次。如何将访问的接口去掉token验证项目应用:springboot oath2完成此操作需要修改两个模块的application.yml文件修改,一个是getway网关的yml文件,另一个则是接口所在模块的yml文件_不通微服务开发阶段怎样取消sa_token验证
文章浏览阅读1.8w次。今天用非root账号,用sudo 命令 添加了两个账号,然后重启服务···可以正常取出东西,但是不能提交···报的错就是 : 不能打开文件“/var/svn/svnrepos/db/txn-current-lock”: 权限不够查了半天 找到了解决办法,原文链接:http://blog.sina.com.cn/s/blog_7139569d0100woar.htm_svn cant open file
文章浏览阅读2.4w次,点赞11次,收藏44次。原文地址:https://www.bitecho.net/microsoft-visual-studio-2019.html#respondVisual Studio(简称VS)是微软公司的开发工具包系列产品,包括了整个软件生命周期中所需要的大部分工具,如UML工具、代码管控工具、集成开发环境(IDE)等等,并且几乎适用于所有开发平台。微软于4月3日正式发布了Visual Studio 2019正式版,在IDE、性能、常规调试、源控制和Team管理器、编程语言、Web技术、Xamarin移动..._microsoft vcredist 2019
文章浏览阅读2.7k次,点赞6次,收藏6次。1.闲言 在正式的学习之前,我喜欢先放飞一下自我。我觉得技术就是用来聊的,找个酒馆,找些大神,咱们听着音乐一起聊起来。所以我特别希望能把自己的微博写的口语化,就像玩一样。就像古代那些说书人一样,萧远山和慕容博相视一笑,王图霸业,血海深仇,尽归尘土。这是我向往的一种表达方式,但是我现在还达不到那个境界,只能尽力而为吧。2.YOLOV2 1.十个改造点 yolov1提升了目标检测的速度,但是在MAP方面却掉了上去。所以说铁打的大神,流水的模型..._yolov9
文章浏览阅读1.1w次,点赞8次,收藏5次。在二次开发微信,首先要成为微信开发者,在微信开发者平台注册,还要注册一个微信公众号,注册过程很简单,唯一的要求就是要绑定银行卡的微信账号扫一下二维码.然后就能注册成功微信公众号了. 在注册微信公众号之后有两种模式,一个是编辑模式,还有一个就是开发者模式.编辑模式其实也是一个后台操作的过程,在编辑模式下基本上一些基本的功能都能实现,像关键字回复,被关注的时候回复,还有订阅号每天能够推送一条信息给关注_微信 电脑端 二次开发
文章浏览阅读456次。管理生产的操作要求 内容系列: 此内容是#在系列的一部分#: 管理生产操作要求 https://www.ibm.com/developerworks/library/?series_title_by=manage+operational+requirements+for+production 请继续关注本系列中的其他内容。 该内容是该系列的一部分: 管理生产的操作需..._构建测评用例
文章浏览阅读282次。Android 获取当前日期 时间很方便 直接调用Calendar类即可定义变量private int mHour; private int mMinute; private int mYear; private int mMonth; private int mDay;调用如下 final Calendar c..._直接调用calendar类年份
文章浏览阅读145次。据说这个题很容易考,嘻嘻嘻,我自己做做。。。每做一个题 它还有变形,我又要开始思考。。。。。所有算法题里面我最讨厌 矩阵 矩阵,数学都没有搞明白矩阵,你还要我写代码。。。说明我今年变聪明了。。。。我做题都是非常简单思路,大道至简。就是如果这三个数之和刚好与target相等就直接输出,如果不是 就看范围。找到距离这个target最近的两个数。min,max最后用绝对值比较一下就可以了..._最接近的三数之和 时间复杂度
文章浏览阅读710次。经典算法研究系列:十、从头到尾彻底理解傅里叶变换算法、上作者:July、dznlong 二零一一年二月二十日推荐阅读:The Scientist and Engineer's Guide to Digital Signal Processing,By Steven W. Smith, Ph.D。此书地址:http://www.dspguide.com/pdfbook.htm_求序列的5点dft