一、MD5算法介绍
MD5(Message-Digest Algorithm 5)是一种广泛使用的密码散列函数,它可以将任意长度的数据(消息)计算出一个128位的固定长度的散列值(消息摘要)。MD5加密的原理如下:
1.1数据填充:
将原始消息填充至64位的整数倍。填充方式是在消息末尾添加1个1,然后添加若干个0,直到消息长度等于448 bits (mod 512)。
1.2追加数据长度:
将原始消息的长度(以bits为单位)以64位的形式添加到上一步的结果之后。
1.3MD寄存器初始化:
MD5算法使用4个32位的寄存器A、B、C、D来存储中间结果,初始值分别为:
A = 0x67452301
B = 0xefcdab89
C = 0x98badcfe
D = 0x10325476
1.4四轮循环计算:
对填充和追加长度后的消息进行4轮非线性运算,每轮使用不同的布尔函数。每轮都包含16个步骤,每个步骤都更新寄存器A、B、C、D的值。
1.5输出:
经过4轮运算后,4个寄存器的值即为最终的128位MD5散列值。
MD5算法的优点是计算速度快,广泛应用于文件校验、数字签名等场景。但是它存在一些安全隐患,如存在碰撞攻击,即不同的消息可能计算出相同的摘要
1.6示例
假设我们有一个字符串"Hello, world!"作为输入消息。步骤如下:
(1)原始数据填充:
原始消息长度为 13 bytes (104 bits),填充 1 个 1 和 55 个 0 (占 64 bits),得到 168 bits,
(2)数据长度追加:
最后添加原始消息长度 104 bits,得到总长度 272 bits
(3)初始化寄存器:
A = 0x67452301
B = 0xefcdab89
C = 0x98badcfe
D = 0x10325476
(4)四轮循环计算:
进行4轮非线性运算,每轮 16 步,共 64 步。这个过程比较复杂,涉及到一些位运算和布尔函数,就不详细展开了。
最终得到 MD5 散列值:
A = 0xe10adc3e
B = 0x5771cf43
C = 0x4b0455e5
D = 0x1d8a9e34
综合 A、B、C、D 的值,得到最终的 MD5 散列值为:e10adc3e5771cf434b0455e51d8a9e34
这个 128 位的散列值就是对原始消息"Hello,world!"进行 MD5 加密后得到的结果。需要注意的是,MD5 算法是确定性的,对同样的输入消息,计算出的 MD5 值永远是相同的。
二、获取嵌入式设备的唯一编号
在linux开发板中,/proc/cpuinfo是一个在Linux系统中提供CPU相关信息的特殊文件。当我们查看这个文件时,可以获取到丰富的CPU信息,其中Serial代表当前开发板的CPU唯一编号,因此我们只要获取得到该编号,并对该编号进行加密,理论上使得某些可执行文件只可以在该开发板运行。
下列为获取该编号的API,直接调用获取返回结果即为CPU编号:
char* get_cpu_serial() // 获取 CPU 序列号的函数
{
FILE *file;
char line[256];
char *serial = NULL;
file = fopen("/proc/cpuinfo", "r"); // 打开 /proc/cpuinfo 文件
if (file == NULL)
{
perror("Error opening /proc/cpuinfo");
return NULL;
}
// 逐行读取文件内容
while (fgets(line, sizeof(line), file) != NULL)
{
if (strncmp(line, "Serial", strlen("Serial")) == 0) // 比较每行的前缀是否是 "Serial"
{
line[strcspn(line, "\n")] = '\0'; // 移除行尾的换行符
char *colon_pos = strchr(line, ':'); // 查找行中的冒号位置
if (colon_pos != NULL)
{
char *serial_start = colon_pos + 1;
while (*serial_start == ' ') serial_start++; // 跳过冒号和后面的所有空格,定位到实际的序列号开始位置
serial = (char*)malloc(strlen(serial_start) + 1);
strcpy(serial, serial_start); // 分配内存并复制序列号字符串
char *end = serial + strlen(serial) - 1;
while (end > serial && *end == ' ') end--;
*(end + 1) = '\0'; // 去除末尾的空格,分配结束标志
break;
}
}
}
fclose(file);
return serial; // 返回序列号字符串
}
三、MD5算法相关库的安装
openssl库的安装
(1)openssl官网下载最新安装包即可。https://openssl-library.org/source/
(2)tar -zxvf openssl-1.1.1c.tar.gz
(3)cd openssl-1.1.1s
(4)./config
(5)make
(6)sudo make install
(7)openssl -version提示:error while loading shared libraries: libssl.so.3: cannot open shared object file
原因:没有把安装的ssl中libssl.so.3链接到/usr/lib中导致找不到
解决:
(1)whereis openssl首先确定我们是安装了openssl
(2)sudo find / -name libssl.so.3
(3)sudo find / -name libcrypto.so.3
(4)openssl依赖于ssl与crypto。
使用sudo find / -name libssl.so.3
与sudo find / -name libcrypto.so.3查看ssl与crypto所在目录。
(5)在/usr/lib生成二者软连接:
sudo ln -s /usr/local/lib64/libssl.so.3 /usr/lib/libssl.so.3
sudo ln -s /usr/local/lib64/libcrypto.so.3 /usr/lib/libcrypto.so.3
(6)openssl -version查看版本信息
四、使用MD5算法对CPU编号进行加密
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <openssl/md5.h>
char* get_cpu_serial() // 获取 CPU 序列号的函数
{
FILE *file;
char line[256];
char *serial = NULL;
file = fopen("/proc/cpuinfo", "r"); // 打开 /proc/cpuinfo 文件
if (file == NULL)
{
perror("Error opening /proc/cpuinfo");
return NULL;
}
// 逐行读取文件内容
while (fgets(line, sizeof(line), file) != NULL)
{
if (strncmp(line, "Serial", strlen("Serial")) == 0) // 比较每行的前缀是否是 "Serial"
{
line[strcspn(line, "\n")] = '\0'; // 移除行尾的换行符
char *colon_pos = strchr(line, ':'); // 查找行中的冒号位置
if (colon_pos != NULL)
{
char *serial_start = colon_pos + 1;
while (*serial_start == ' ') serial_start++; // 跳过冒号和后面的所有空格,定位到实际的序列号开始位置
serial = (char*)malloc(strlen(serial_start) + 1);
strcpy(serial, serial_start); // 分配内存并复制序列号字符串
char *end = serial + strlen(serial) - 1;
while (end > serial && *end == ' ') end--;
*(end + 1) = '\0'; // 去除末尾的空格,分配结束标志
break;
}
}
}
fclose(file);
return serial; // 返回序列号字符串
}
void md5_encrypt(const char *input, char *output) {
unsigned char digest[MD5_DIGEST_LENGTH];
MD5_CTX ctx;
MD5_Init(&ctx);
MD5_Update(&ctx, input, strlen(input));
MD5_Final(digest, &ctx);
for (int i = 0; i < MD5_DIGEST_LENGTH; i++) {
sprintf(output + (i * 2), "%02x", digest[i]);
}
}
int main() {
char* cpu_serial = get_cpu_serial();
char encode_cpu_serial[MD5_DIGEST_LENGTH * 2 + 1];
md5_encrypt(cpu_serial, encode_cpu_serial);
printf("Original data: %s\n", cpu_serial);
printf("Encrypted MD5 hash: %s\n", encode_cpu_serial);
return 0;
}
gcc test.c -o test -lssl -lcrypto编译运行即可。这里我是在虚拟机运行的,虚拟机没有Serial选项,我是对其他字段进行加密的。