You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

求基于OpenSSL库的Retail MAC算法C语言实现示例

Hey there! 我懂你现在的困扰——想用C语言结合OpenSSL实现Retail MAC的校验和计算,却找不到现成的示例代码对吧?别着急,我给你整理了一份完整的实现,注释都写得很明白,你跟着一步步来就行~

Retail MAC 校验和计算的OpenSSL实现(C语言)

首先得说下,Retail MAC(也就是ANSI X9.19 MAC)是一种基于DES算法的消息认证码,核心逻辑是对数据分块后做加密+异或的迭代处理。下面的代码用OpenSSL的EVP通用加密接口实现,兼容性更好,也更符合OpenSSL的最佳实践。

完整代码示例

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/err.h>

// 处理Retail MAC的核心函数
// 参数说明:
// key: DES密钥(8字节,含奇偶校验位)
// key_len: 密钥长度(必须为8)
// data: 待计算MAC的原始数据
// data_len: 原始数据长度
// mac: 输出MAC值的缓冲区
// mac_len: 输入为缓冲区最大长度,输出为实际生成的MAC长度(通常取4或8字节)
int compute_retail_mac(const unsigned char *key, size_t key_len,
                       const unsigned char *data, size_t data_len,
                       unsigned char *mac, size_t *mac_len) {
    EVP_CIPHER_CTX *ctx = NULL;
    int ret = -1;
    size_t block_size = EVP_CIPHER_block_size(EVP_des_ecb());
    unsigned char current_block[8] = {0};
    unsigned char encrypted_block[8] = {0};
    int out_len;

    // 校验输入参数合法性
    if (!key || key_len != 8 || !data || !mac || !mac_len || *mac_len < 4) {
        fprintf(stderr, "Invalid input parameters\n");
        goto cleanup;
    }

    // 初始化OpenSSL错误处理(可选,但建议加上)
    OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS, NULL);
    ERR_load_crypto_strings();

    // 创建并初始化加密上下文
    ctx = EVP_CIPHER_CTX_new();
    if (!ctx) {
        fprintf(stderr, "Failed to create EVP_CIPHER_CTX\n");
        goto cleanup;
    }

    // 初始化DES-ECB加密模式(Retail MAC用ECB模式)
    if (EVP_EncryptInit_ex(ctx, EVP_des_ecb(), NULL, key, NULL) != 1) {
        fprintf(stderr, "Failed to init encryption context\n");
        ERR_print_errors_fp(stderr);
        goto cleanup;
    }
    // 关闭填充(我们自己处理Retail MAC的填充规则)
    EVP_CIPHER_CTX_set_padding(ctx, 0);

    // 第一步:处理数据分块和填充
    size_t total_blocks = (data_len + block_size - 1) / block_size;
    // 如果数据长度刚好是块大小的整数倍,需要额外加一个全0块
    if (data_len % block_size == 0) {
        total_blocks++;
    }

    // 初始化当前块为全0
    memset(current_block, 0, sizeof(current_block));

    // 迭代处理每个数据块
    for (size_t i = 0; i < total_blocks; i++) {
        unsigned char input_block[8] = {0};
        size_t copy_len = (i < total_blocks - 1) ? block_size : (data_len % block_size);
        if (copy_len > 0) {
            memcpy(input_block, data + i * block_size, copy_len);
        }

        // 当前块和输入块异或
        for (int j = 0; j < block_size; j++) {
            current_block[j] ^= input_block[j];
        }

        // 加密异或后的块
        if (EVP_EncryptUpdate(ctx, encrypted_block, &out_len, current_block, block_size) != 1) {
            fprintf(stderr, "Encryption failed\n");
            ERR_print_errors_fp(stderr);
            goto cleanup;
        }
        // ECB模式下out_len应该等于block_size
        if (out_len != block_size) {
            fprintf(stderr, "Unexpected encryption output length\n");
            goto cleanup;
        }

        // 将加密结果作为下一轮的当前块
        memcpy(current_block, encrypted_block, block_size);
    }

    // 最终的MAC取前N字节(通常是4或8字节,这里默认取4字节,可按需调整)
    size_t output_len = *mac_len > 8 ? 8 : *mac_len;
    memcpy(mac, current_block, output_len);
    *mac_len = output_len;
    ret = 0;

cleanup:
    // 释放资源
    if (ctx) {
        EVP_CIPHER_CTX_free(ctx);
    }
    ERR_free_strings();
    return ret;
}

// 测试主函数
int main() {
    // 示例DES密钥(8字节,注意DES密钥的奇偶校验位,这里用测试值)
    unsigned char key[8] = {0x12, 0x34, 0x56, 0x78, 0x90, 0xAB, 0xCD, 0xEF};
    // 示例待计算MAC的数据
    unsigned char data[] = "Hello Retail MAC Test";
    size_t data_len = strlen((char *)data);
    // 存储MAC的缓冲区
    unsigned char mac[8] = {0};
    size_t mac_len = 4; // 取4字节MAC

    if (compute_retail_mac(key, sizeof(key), data, data_len, mac, &mac_len) == 0) {
        printf("Computed Retail MAC (4 bytes): ");
        for (size_t i = 0; i < mac_len; i++) {
            printf("%02X ", mac[i]);
        }
        printf("\n");
    } else {
        fprintf(stderr, "Failed to compute Retail MAC\n");
        return 1;
    }

    return 0;
}

关键说明和注意事项

  • 编译命令:编译时需要链接OpenSSL的crypto库,命令如下:
    gcc -o retail_mac retail_mac.c -lcrypto
    
  • 密钥要求:Retail MAC通常使用单DES,密钥必须是8字节(包含1字节的奇偶校验位,OpenSSL会自动处理校验)。如果需要3DES版本,只需把EVP_des_ecb()换成EVP_des_ede3_ecb(),同时密钥长度改为24字节。
  • 填充规则:严格遵循ANSI X9.19标准:
    • 如果数据长度不是8字节的整数倍,用0x00填充到8字节;
    • 如果数据长度刚好是8字节的整数倍,额外添加一个全0x00的8字节块。
  • MAC长度:标准的Retail MAC可以是4字节或8字节,代码里默认取4字节,你可以通过修改mac_len参数调整。

运行测试

编译后运行程序,会输出示例数据的4字节Retail MAC值,你可以用其他工具验证结果是否正确。

内容的提问来源于stack exchange,提问作者GI PP

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:53:23