【基于AES-128计算CMAC值】

前言

简单使用Qt来开发一款小工具,实现计算一个二进制文件的CMAC值,用户通过选择一个二进制文件,然后输入密钥key值,来计算CMAC值,本篇文章会将实现的代码给出,也会简单的去介绍一下什么是AES加密和CAMC。接下来直接进入正题。

AES

AES(Advanced Encryption Standard)是一种对称加密算法,被广泛应用于保护敏感数据的安全性。AES的设计目标是提供高度的安全性,并且在各种设备上都能高效运行。
AES算法有三个固定的密钥长度:128比特、192比特和256比特。其中,AES-128使用128比特(16字节)的密钥长度。下面是AES-128的一些关键特点:
1、密钥长度: AES-128使用128比特(16字节)的密钥,这是算法的基本安全性级别。密钥的长度直接影响到算法的安全性,较长的密钥一般能提供更高的安全性,但也会增加计算复杂度。
2、分组长度: AES-128将数据分成128比特(16字节)的块进行加密。这是AES算法中的固定分组长度。
3、轮数: AES-128使用10轮的加密迭代。每一轮都涉及不同的运算,包括替代字节、行移位、列混淆和轮密钥加。这些运算共同增强了算法的安全性。
4、替代字节(SubBytes): 在替代字节阶段,每个字节都会被替换为一个预定义的字节,通过S盒(Substitution Box)进行替换。这一步增加了算法的非线性性,提高了安全性。
5、行移位(ShiftRows): 在行移位阶段,AES-128中的每一行都会按照固定的规则进行循环左移。这个步骤有助于增加数据的混淆度。
6、列混淆(MixColumns): 列混淆是一个线性变换,通过将每一列与一个固定矩阵相乘,增加了算法的复杂性。
7、轮密钥加(AddRoundKey): 在每一轮中,轮密钥都会与数据块进行按位异或的操作。轮密钥是从主密钥扩展而来,通过一系列密钥扩展算法生成。

CMAC

CMAC(Cipher-based Message Authentication Code)是一种基于密码的消息认证码算法。它是一种确定性的密钥相关函数,用于计算消息的认证码以验证消息的完整性和真实性。CMAC是从CBC-MAC(Cipher Block Chaining Message Authentication Code)演变而来的,但在设计上解决了一些安全性问题。
CMAC主要有两个阶段:生成和验证。
生成阶段:
输入消息和密钥。
使用加密算法对消息进行处理,产生一个固定长度的认证码。
验证阶段:
输入消息、密钥和认证码。
使用加密算法对消息进行处理,产生一个新的认证码。
将生成的认证码与输入的认证码进行比较。如果两者匹配,消息被验证为完整和真实。
CMAC提供了一种安全的方式来对消息进行认证,而不需要使用数字签名。它适用于各种应用,如网络通信、文件传输等,以确保消息在传输过程中没有被篡改。

CMAC的一种具体实现是使用AES(Advanced Encryption Standard)算法,因此在某些上下文中,CMAC也被称为AES-CMAC。

实现

openssl

OpenSSL 是一个开源的密码学工具包,提供了一系列的加密算法和安全通信协议的实现。它支持多种操作系统,包括类Unix系统(如Linux和BSD)以及Windows。以下是 OpenSSL 的一些主要特点和用法:
加密算法: OpenSSL 提供了一系列加密算法,包括对称加密算法(如AES、DES、3DES)、非对称加密算法(如RSA、DSA、ECDSA)、哈希函数(如MD5、SHA-1、SHA-256)等。这些算法可以用于数据加密、数字签名、消息认证码等。
SSL/TLS 协议: OpenSSL 实现了 SSL(Secure Sockets Layer)和 TLS(Transport Layer Security)协议,用于安全的网络通信。它支持多个协议版本,包括 SSLv2、SSLv3、TLSv1、TLSv1.1、TLSv1.2、TLSv1.3。SSL/TLS 协议用于在客户端和服务器之间建立加密通信通道,确保数据的机密性和完整性。
命令行工具: OpenSSL 提供了一些命令行工具,其中最常用的是 openssl 命令。通过 openssl 命令,用户可以执行各种操作,如生成密钥对、创建数字证书、进行加密解密操作等。

calculateFileCMAC

当用户在界面上选择文件后会执行此函数。
参数:
key:密钥,长度为 AES_BLOCK_SIZE。
filePath:需要计算 CMAC 的文件路径。
操作步骤:
1、打开文件,读取文件中的所有数据。
2、如果文件数据的长度小于 64KB,进行填充,填充数据为 0xFF。
3、调用 calculateCMAC 函数计算文件的 CMAC,并返回结果。

calculateCMAC

将文件数据读出后会执行此函数
参数:
key:密钥,长度为 AES_BLOCK_SIZE(一般为 16 字节)。
data:需要计算 CMAC 的数据。
操作步骤:
检查密钥长度是否正确,如果不正确则输出错误信息并返回空 QByteArray。
创建一个 CMAC 上下文(CMAC_CTX)。
对输入的数据进行字节序翻转(每四个字节翻转一次)。
使用 OpenSSL 函数 CMAC_Init 初始化 CMAC 上下文,指定使用 AES-128-CBC 算法。
使用 CMAC_Update 更新 CMAC 上下文,将数据添加到计算中。
使用 CMAC_Final 完成 CMAC 计算,得到最终的认证码。
将认证码转换为 QByteArray 类型的结果。
释放 CMAC 上下文。
文件处理:
根据原始文件路径创建新的文件路径,将文件复制到新路径。
在新文件中追加计算得到的 CMAC。
将计算得到的 CMAC 以十六进制形式显示在界面的文本编辑框中。
创建一个新文件,将翻转后的数据写入新文件中。

关键代码

calculateFileCMAC:

QByteArray cmacopenssl::calculateFileCMAC(const QByteArray &key, const QString &filePath)
{
    
    
    QByteArray result;

    // Read the content of the file
    QFile file(filePath);
    if (!file.open(QIODevice::ReadOnly)) {
    
    
        qDebug() << "Failed to open file:" << file.errorString();
        return result;
    }
    //qint64 fileSize = file.size();
    QByteArray fileData = file.readAll();
    file.close();
    //fileData = QByteArray::fromHex("aaaaaaaaaa");
    // Calculate CMAC for the file data
    // Check the length of the fileData
    qint64 dataSize = fileData.size();
    qint64 paddingSize = 64 * 1024 - dataSize;

    // Check if padding is needed
    if (paddingSize > 0) {
    
    
        QByteArray paddingData(paddingSize, '\xff');
        //fileData.append(paddingData);
    }
    //qDebug()<<fileData.toHex();
    qDebug()<<"size:  "<<fileData.size();
    qDebug()<<fileData.toHex();
    result = calculateCMAC(key, fileData);
    return result;
}

calculateCMAC:

QByteArray cmacopenssl::calculateCMAC(const QByteArray &key, const QByteArray &data)
{
    
    
    QByteArray result;
    // Check key length
    if (key.length() != AES_BLOCK_SIZE) {
    
    
        qDebug() << "Key length should be " << AES_BLOCK_SIZE << " bytes.";
        return result;
    }
    // Create a CMAC context
    CMAC_CTX *ctx = CMAC_CTX_new();
    // Reverse the endianness of every four bytes in the input data
    QByteArray reversedData = data;
    qDebug()<<reversedData.toHex();
    for (int i = 0; i < reversedData.size(); i += 4) {
    
    
        // Ensure that we have at least four bytes to convert
        if (i + 3 < reversedData.size()) {
    
    
            char temp = reversedData[i];
            reversedData[i] = reversedData[i + 3];
            reversedData[i + 3] = temp;

            temp = reversedData[i + 1];
            reversedData[i + 1] = reversedData[i + 2];
            reversedData[i + 2] = temp;
        }
    }
    // Initialize CMAC context with the key
    if (CMAC_Init(ctx, key.constData(), key.length(), EVP_aes_128_cbc(), nullptr) != 1) {
    
    
        qDebug() << "CMAC_Init failed.";
        CMAC_CTX_free(ctx);
        return result;
    }
    //Update CMAC context with the data
    if (CMAC_Update(ctx, data.constData(), data.length()) != 1) {
    
    
        qDebug() << "CMAC_Update failed.";
        CMAC_CTX_free(ctx);
        return result;
    }

    // Finalize CMAC calculation
    unsigned char mac[AES_BLOCK_SIZE];
    size_t macLength;
    if (CMAC_Final(ctx, mac, &macLength) != 1) {
    
    
        qDebug() << "CMAC_Final failed.";
        CMAC_CTX_free(ctx);
        return result;
    }
    // Convert the result to QByteArray
    result = QByteArray(reinterpret_cast<char*>(mac), macLength);
    // Free the CMAC context
    CMAC_CTX_free(ctx);
    QFileInfo fileInfo(filePath);
    QString newFilePath = fileInfo.path() + "/" + fileInfo.baseName() + ".security." + fileInfo.suffix();
    if (!QFile::copy(filePath, newFilePath)) {
    
    
            qDebug() << "Failed to create a copy of the original file:" << filePath;
    }
    QFile copiedFile(newFilePath);
    if (!copiedFile.open(QIODevice::Append)) {
    
    
        qDebug() << "Failed to open copied file for appending:" << newFilePath;
    }
    // Append the result to the copied file
    copiedFile.write(result);
    copiedFile.close();
    qDebug() << "CMAC result appended to the copied file and saved to:" << newFilePath;
    ui->textEdit->setText(result.toHex());
    QFileInfo fileInf(filePath);
       QString newFile = fileInf.path() + "/" + fileInf.baseName() + "_reversedData." + fileInf.suffix();
       QFile reversedDataFile(newFile);
       if (reversedDataFile.open(QIODevice::WriteOnly)) {
    
    
           reversedDataFile.write(reversedData);
           reversedDataFile.close();
           qDebug() << "Reversed data written to the new file:" << newFilePath;
       } else {
    
    
           qDebug() << "Failed to open the new file for writing:" << newFilePath;
       }
    return result;
}

PS:在线求一个对加密算法熟悉的大佬

想咨询一些问题

猜你喜欢

转载自blog.csdn.net/a1379292747/article/details/134424619