【Reverse-Crypto】深入逆向-密码篇-MD5算法详解以及代码编写
字数 906 2025-08-22 12:22:54

MD5算法详解与实现指南

1. MD5算法概述

MD5(Message Digest Algorithm 5)是一种广泛使用的密码散列函数,产生128位(16字节)的哈希值,通常表示为32个十六进制字符。

核心特性

  • 固定输出:无论输入多长,输出总是128位
  • 不可逆性:无法从哈希值反推原始数据
  • 抗碰撞性:难以找到两个不同输入产生相同哈希值
  • 雪崩效应:输入微小变化导致输出巨大差异

应用场景

  • 文件完整性校验
  • 数字签名
  • 密码存储(需加盐)
  • 数据指纹生成

2. MD5算法处理步骤

完整处理流程

  1. 消息填充(Padding)
  2. 分组(Grouping)
  3. 缓冲区初始化
  4. 迭代处理
  5. 输出结果

3. 详细实现解析

3.1 消息填充

目的:将输入长度填充至模512等于448的位数(即N×512+448)

填充规则

  1. 首先附加一个"1"位
  2. 然后附加足够多的"0"位
  3. 最后附加64位的原始消息长度(小端序)
void MD5::padding(vector<unsigned char>& data) {
    uint64_t nBits = data.size() * 8;
    size_t paddingBits = 0;
    
    if (nBits % 512 >= 448) {
        paddingBits = 512 - nBits % 512 % 448;
    } else {
        paddingBits = 448 - nBits % 512;
    }
    
    // 先填充1位"1"
    data = Tools::left_shift(data, 1);
    data[data.size() - 1] ^= 0x1;
    paddingBits -= 1;
    
    // 再填充剩余"0"位
    data = Tools::left_shift(data, paddingBits);
    
    // 最后附加原始长度(64位小端序)
    vector<uint8_t> MessageSize = Tools::uint64ToVector(Tools::swapEndian64(nBits));
    data.insert(data.end(), MessageSize.begin(), MessageSize.end());
}

3.2 分组处理

将填充后的数据按512位(64字节)分组,每组再划分为16个32位字(小端序存储)。

vector<vector<uint32_t>> MD5::blockText(vector<uint8_t>& data) {
    size_t groupCount = data.size() * 8 / 512;
    vector<vector<uint32_t>> groups(groupCount);
    
    for (size_t i = 0; i < groupCount; i++) {
        groups[i] = vector<uint32_t>(16, 0);
        for (size_t j = 0; j < 16; j++) {
            for (size_t k = 0; k < 4; k++) {
                groups[i][j] |= (uint32_t)data[i*64 + j*4 + k] << ((3-k)*8);
            }
        }
    }
    return groups;
}

3.3 缓冲区初始化

使用4个32位幻数初始化哈希缓冲区:

const uint32_t A = 0x67452301;
const uint32_t B = 0xEFCDAB89;
const uint32_t C = 0x98BADCFE;
const uint32_t D = 0x10325476;

3.4 迭代处理

MD5使用四轮循环处理,每轮16次操作,共64次操作。每轮使用不同的非线性函数:

非线性函数定义

#define F(x, y, z) (((x) & (y)) | ((~(x)) & (z)))
#define G(x, y, z) (((x) & (z)) | ((y) & (~(z))))
#define H(x, y, z) ((x) ^ (y) ^ (z))
#define I(x, y, z) (y ^ (x | ~z))

四轮处理函数

uint32_t MD5::FF(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t t, uint32_t shift) {
    return b + Tools::rotate_left(a + F(b, c, d) + M + t, shift);
}

uint32_t MD5::GG(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t t, uint32_t shift) {
    return b + Tools::rotate_left(a + G(b, c, d) + M + t, shift);
}

uint32_t MD5::HH(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t t, uint32_t shift) {
    return b + Tools::rotate_left(a + H(b, c, d) + M + t, shift);
}

uint32_t MD5::II(uint32_t a, uint32_t b, uint32_t c, uint32_t d, uint32_t M, uint32_t t, uint32_t shift) {
    return b + Tools::rotate_left(a + I(b, c, d) + M + t, shift);
}

循环左移函数

uint32_t Tools::rotate_left(uint32_t value, uint8_t shift) {
    const int bits = 32;
    shift %= bits;
    return (value << shift) | (value >> (bits - shift));
}

完整迭代处理

void MD5::transByIterator(vector<uint32_t>& v, uint32_t& A, uint32_t& B, uint32_t& C, uint32_t& D) {
    // Round 1 (FF)
    A = FF(A, B, C, D, v[0],  0xd76aa478, 7);
    D = FF(D, A, B, C, v[1],  0xe8c7b756, 12);
    // ... 共16次FF操作
    
    // Round 2 (GG)
    A = GG(A, B, C, D, v[1],  0xf61e2562, 5);
    D = GG(D, A, B, C, v[6],  0xc040b340, 9);
    // ... 共16次GG操作
    
    // Round 3 (HH)
    A = HH(A, B, C, D, v[5],  0xfffa3942, 4);
    D = HH(D, A, B, C, v[8],  0x8771f681, 11);
    // ... 共16次HH操作
    
    // Round 4 (II)
    A = II(A, B, C, D, v[0],  0xf4292244, 6);
    D = II(D, A, B, C, v[7],  0x432aff97, 10);
    // ... 共16次II操作
}

3.5 输出处理

将最终的四轮处理结果转换为大端序并拼接:

vector<uint32_t> MD5::update1(vector<vector<uint32_t>>& groups) {
    vector<uint32_t> res = {A, B, C, D};
    
    // 小端序转换
    for (size_t i = 0; i < groups.size(); i++) {
        encode(groups[i]);
    }
    
    // 分组处理
    size_t i = 0;
    while (i < groups.size()) {
        uint32_t tmp_A = res[0];
        uint32_t tmp_B = res[1];
        uint32_t tmp_C = res[2];
        uint32_t tmp_D = res[3];
        
        transByIterator(groups[i], tmp_A, tmp_B, tmp_C, tmp_D);
        
        res[0] += tmp_A;
        res[1] += tmp_B;
        res[2] += tmp_C;
        res[3] += tmp_D;
        i++;
    }
    
    // 转回大端序
    decode(res);
    return res;
}

4. 优化实现

使用预定义的常量数组简化代码:

常量定义

// 位移矩阵
const uint8_t shiftBits[4][4] = {
    {7, 12, 17, 22},
    {5, 9, 14, 20},
    {4, 11, 16, 23},
    {6, 10, 15, 21}
};

// 伪随机数矩阵
const uint32_t pseudoRandom[4][16] = {
    {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 
     0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,
     0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
     0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821},
    // ... 其他三轮的伪随机数
};

// 消息分组索引矩阵
const uint8_t groupsIndex[4][16] = {
    {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15},
    {1,6,11,0,5,10,15,4,9,14,3,8,13,2,7,12},
    {5,8,11,14,1,4,7,10,13,0,3,6,9,12,15,2},
    {0,7,14,5,12,3,10,1,8,15,6,13,4,11,2,9}
};

优化后的迭代处理

void MD5::transByRange(vector<uint32_t>& v, vector<uint32_t>& res) {
    using RoundFunction = uint32_t (MD5::*)(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t);
    RoundFunction roundFunctions[] = {&MD5::FF, &MD5::GG, &MD5::HH, &MD5::II};
    
    for (size_t i = 0; i < 4; i++) {
        const uint32_t* pseudo = pseudoRandom[i];
        const uint8_t* shift = shiftBits[i];
        const uint8_t* groupIndex = groupsIndex[i];
        
        for (size_t k = 16; k > 0; k--) {
            res[k%4] = (this->*roundFunctions[i])(
                res[k%4], res[(k+1)%4], res[(k+2)%4], res[(k+3)%4],
                v[groupIndex[16-k]], pseudo[16-k], shift[(16-k)%4]
            );
        }
    }
}

5. 辅助工具函数

字节序转换

// 64位字节序转换
uint64_t Tools::swapEndian64(uint64_t x) {
    return ((x >> 56) & 0xff) | ((x >> 40) & 0xff00) | 
           ((x >> 24) & 0xff0000) | ((x >> 8) & 0xff000000) |
           ((x << 8) & 0xff00000000) | ((x << 24) & 0xff0000000000) |
           ((x << 40) & 0xff000000000000) | ((x << 56) & 0xff00000000000000);
}

// uint64转vector
vector<unsigned char> Tools::uint64ToVector(uint64_t value) {
    vector<uint8_t> vec(8);
    for (size_t i = 0; i < 8; ++i) {
        vec[i] = static_cast<uint8_t>((value >> ((7-i)*8)) & 0xFF);
    }
    return vec;
}

左移操作

vector<unsigned char> Tools::left_shift(vector<unsigned char> vec, size_t shift) {
    if (shift == 0) return vec;
    
    size_t byte_shift = shift / 8;
    size_t bit_shift = shift % 8;
    
    vector<unsigned char> result(vec.size() + byte_shift, 0);
    
    for (size_t i = 0; i < vec.size(); ++i) {
        result[i] |= (vec[i] << bit_shift);
        if (i + 1 < vec.size()) {
            result[i] |= (vec[i+1] >> (8 - bit_shift));
        }
    }
    
    if (vec[0] >> (8 - bit_shift)) {
        result.insert(result.begin(), 1);
        result[0] = (vec[0] >> (8 - bit_shift));
    }
    
    return result;
}

6. 安全注意事项

虽然MD5曾广泛使用,但现已发现以下安全问题:

  1. 碰撞攻击:可以构造两个不同输入产生相同哈希值
  2. 彩虹表攻击:使用预先计算的哈希表反向查找
  3. 速度过快:不利于密码存储(应使用bcrypt/PBKDF2等)

建议

  • 文件校验等非安全场景仍可使用
  • 密码存储必须加盐并使用更安全的算法
  • 安全敏感场景应使用SHA-2或SHA-3系列算法

7. 完整实现示例

string MD5::hash(const string& input) {
    // 1. 数据准备
    vector<uint8_t> plaintext(input.begin(), input.end());
    
    // 2. 填充
    padding(plaintext);
    
    // 3. 分组
    vector<vector<uint32_t>> groups = blockText(plaintext);
    
    // 4. 哈希计算
    vector<uint32_t> res = updateRange(groups);
    
    // 5. 格式化输出
    stringstream ss;
    for (size_t i = 0; i < res.size(); i++) {
        ss << hex << setw(8) << setfill('0') << res[i];
    }
    
    return ss.str();
}

通过本指南,您应该能够全面理解MD5算法的原理和实现细节,并能够根据需要实现自己的MD5哈希函数。记住在实际应用中要考虑算法的安全性限制。

MD5算法详解与实现指南 1. MD5算法概述 MD5(Message Digest Algorithm 5)是一种广泛使用的密码散列函数,产生128位(16字节)的哈希值,通常表示为32个十六进制字符。 核心特性 固定输出 :无论输入多长,输出总是128位 不可逆性 :无法从哈希值反推原始数据 抗碰撞性 :难以找到两个不同输入产生相同哈希值 雪崩效应 :输入微小变化导致输出巨大差异 应用场景 文件完整性校验 数字签名 密码存储(需加盐) 数据指纹生成 2. MD5算法处理步骤 完整处理流程 消息填充(Padding) 分组(Grouping) 缓冲区初始化 迭代处理 输出结果 3. 详细实现解析 3.1 消息填充 目的 :将输入长度填充至模512等于448的位数(即N×512+448) 填充规则 : 首先附加一个"1"位 然后附加足够多的"0"位 最后附加64位的原始消息长度(小端序) 3.2 分组处理 将填充后的数据按512位(64字节)分组,每组再划分为16个32位字(小端序存储)。 3.3 缓冲区初始化 使用4个32位幻数初始化哈希缓冲区: 3.4 迭代处理 MD5使用四轮循环处理,每轮16次操作,共64次操作。每轮使用不同的非线性函数: 非线性函数定义 四轮处理函数 循环左移函数 完整迭代处理 3.5 输出处理 将最终的四轮处理结果转换为大端序并拼接: 4. 优化实现 使用预定义的常量数组简化代码: 常量定义 优化后的迭代处理 5. 辅助工具函数 字节序转换 左移操作 6. 安全注意事项 虽然MD5曾广泛使用,但现已发现以下安全问题: 碰撞攻击 :可以构造两个不同输入产生相同哈希值 彩虹表攻击 :使用预先计算的哈希表反向查找 速度过快 :不利于密码存储(应使用bcrypt/PBKDF2等) 建议 : 文件校验等非安全场景仍可使用 密码存储必须加盐并使用更安全的算法 安全敏感场景应使用SHA-2或SHA-3系列算法 7. 完整实现示例 通过本指南,您应该能够全面理解MD5算法的原理和实现细节,并能够根据需要实现自己的MD5哈希函数。记住在实际应用中要考虑算法的安全性限制。