加密哈希 #

什么是哈希算法? #

哈希算法(Hash Algorithm)是一种将任意长度的数据映射为固定长度哈希值的算法。

text
┌─────────────────────────────────────────────────────────────┐
│                    哈希算法原理                              │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│   输入数据(任意长度)                                        │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ Hello, World! 这是一个测试字符串...                  │   │
│   └─────────────────────────────────────────────────────┘   │
│                            │                                 │
│                            ▼                                 │
│                    ┌──────────────┐                         │
│                    │  哈希算法     │                         │
│                    └──────────────┘                         │
│                            │                                 │
│                            ▼                                 │
│   输出哈希值(固定长度)                                      │
│   ┌─────────────────────────────────────────────────────┐   │
│   │ a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b    │   │
│   └─────────────────────────────────────────────────────┘   │
│                                                              │
└─────────────────────────────────────────────────────────────┘

哈希算法特点 #

text
┌─────────────────────────────────────────────────────────────┐
│                    哈希算法特点                              │
├─────────────────────────────────────────────────────────────┤
│  ✅ 确定性:相同输入产生相同输出                              │
│  ✅ 快速计算:能够快速生成哈希值                              │
│  ✅ 雪崩效应:输入微小变化导致输出巨大变化                     │
│  ✅ 不可逆:无法从哈希值反推原始数据                          │
│  ✅ 抗碰撞:难以找到产生相同哈希值的不同输入                   │
└─────────────────────────────────────────────────────────────┘

MD5 哈希 #

MD5 简介 #

MD5(Message-Digest Algorithm 5)是一种广泛使用的哈希算法,产生 128 位(16 字节)的哈希值。

javascript
import md5 from './utils/md5';

const hash = md5('hello world');
console.log(hash);

MD5 特点 #

特性 描述
输出长度 128 位(32 个十六进制字符)
分组大小 512 位
安全性 已被破解,不推荐用于安全场景
速度
应用场景 文件校验、数据指纹、非安全哈希

MD5 使用示例 #

javascript
import md5 from './utils/md5';

console.log(md5(''));
console.log(md5('hello'));
console.log(md5('hello world'));
console.log(md5('Hello World'));

const longText = '这是一个很长的文本内容...'.repeat(100);
console.log(md5(longText));

MD5 应用场景 #

1. 文件完整性校验 #

javascript
const calculateFileMD5 = async (file) => {
    const buffer = await file.arrayBuffer();
    const hash = md5(new Uint8Array(buffer));
    return hash;
};

const input = document.querySelector('input[type="file"]');
input.addEventListener('change', async (e) => {
    const file = e.target.files[0];
    const hash = await calculateFileMD5(file);
    console.log(`文件 ${file.name} 的 MD5: ${hash}`);
});

2. 数据缓存键 #

javascript
const getCacheKey = (params) => {
    const str = JSON.stringify(params);
    return md5(str);
};

const cache = new Map();

const fetchData = async (params) => {
    const key = getCacheKey(params);
    
    if (cache.has(key)) {
        return cache.get(key);
    }
    
    const data = await fetch('/api/data', {
        method: 'POST',
        body: JSON.stringify(params)
    }).then(r => r.json());
    
    cache.set(key, data);
    return data;
};

3. 密码存储(不推荐) #

javascript
import md5 from './utils/md5';

const hashPassword = (password, salt) => {
    return md5(password + salt);
};

const salt = 'random-salt-value';
const hashedPassword = hashPassword('user-password', salt);
console.log(hashedPassword);

MD6 哈希 #

MD6 简介 #

MD6 是一种可变输出长度的哈希函数,支持从 1 到 512 位的输出。

javascript
import md6 from './utils/md6';

const hash128 = md6.hash128('hello world');
const hash256 = md6.hash256('hello world');
const hash512 = md6.hash512('hello world');

console.log(hash128.toString());
console.log(hash256.toString());
console.log(hash512.toString());

MD6 实现原理 #

javascript
const MD6 = (function() {
    const w = 64;
    const c = 64;
    const n = 89;
    const r = 80;
    
    const K = [
        0x7311c2812425cfa0n, 0x6432286434158a08n,
        0xb60450e9ef68b7c1n, 0xe8fb23908d9f06f1n,
    ];
    
    function compress(data, outputLen) {
        const input = typeof data === 'string' 
            ? new TextEncoder().encode(data) 
            : data;
        
        const blockSize = 128;
        const padLen = (blockSize - (input.length + 17) % blockSize) % blockSize;
        const padded = new Uint8Array(input.length + 1 + padLen + 16);
        
        padded.set(input);
        padded[input.length] = 0x80;
        
        const view = new DataView(padded.buffer);
        view.setBigUint64(padded.length - 8, BigInt(outputLen), true);
        
        let state = new BigUint64Array(8);
        for (let i = 0; i < 8; i++) {
            state[i] = K[i % K.length];
        }
        
        for (let i = 0; i < padded.length; i += blockSize) {
            const block = new BigUint64Array(16);
            for (let j = 0; j < 16 && i + j * 8 < padded.length; j++) {
                block[j] = view.getBigUint64(i + j * 8, true) || 0n;
            }
            
            for (let round = 0; round < 16; round++) {
                for (let j = 0; j < 8; j++) {
                    state[j] ^= block[(round + j) % 16];
                    state[j] = BigInt.asUintN(64, state[j] * 0x5bd1e995n);
                    state[j] ^= (state[(j + 1) % 8] >> 17n);
                }
            }
        }
        
        const outputBytes = Math.ceil(outputLen / 8);
        const result = new Uint8Array(outputBytes);
        const resultView = new DataView(result.buffer);
        
        for (let i = 0; i < Math.min(8, Math.ceil(outputBytes / 8)); i++) {
            if (i * 8 + 8 <= outputBytes) {
                resultView.setBigUint64(i * 8, state[i], true);
            } else {
                const remaining = outputBytes - i * 8;
                for (let j = 0; j < remaining; j++) {
                    result[i * 8 + j] = Number((state[i] >> BigInt(j * 8)) & 0xffn);
                }
            }
        }
        
        return result;
    }
    
    function hash(message, outputBits = 256) {
        const result = compress(message, outputBits);
        return {
            toString: function() {
                return Array.from(result)
                    .map(b => b.toString(16).padStart(2, '0'))
                    .join('');
            },
            toBytes: function() {
                return result;
            }
        };
    }
    
    return {
        hash: hash,
        hash128: (message) => hash(message, 128),
        hash224: (message) => hash(message, 224),
        hash256: (message) => hash(message, 256),
        hash384: (message) => hash(message, 384),
        hash512: (message) => hash(message, 512)
    };
})();

export default MD6;

MD6 使用示例 #

javascript
import md6 from './utils/md6';

const message = 'hello world';

console.log('MD6-128:', md6.hash128(message).toString());
console.log('MD6-224:', md6.hash224(message).toString());
console.log('MD6-256:', md6.hash256(message).toString());
console.log('MD6-384:', md6.hash384(message).toString());
console.log('MD6-512:', md6.hash512(message).toString());

const customHash = md6.hash(message, 200);
console.log('MD6-200:', customHash.toString());

哈希算法对比 #

特性 MD5 SHA-1 SHA-256 MD6
输出长度 128位 160位 256位 可变(1-512位)
安全性 已破解 已破解 安全 安全
速度 中等 较慢 中等
应用场景 校验、指纹 已不推荐 安全场景 灵活场景

实际应用场景 #

1. 数据去重 #

javascript
import md5 from './utils/md5';

class Deduplication {
    constructor() {
        this.hashes = new Set();
    }
    
    add(data) {
        const hash = md5(JSON.stringify(data));
        if (this.hashes.has(hash)) {
            return false;
        }
        this.hashes.add(hash);
        return true;
    }
    
    has(data) {
        const hash = md5(JSON.stringify(data));
        return this.hashes.has(hash);
    }
}

const dedup = new Deduplication();
console.log(dedup.add({ id: 1, name: 'John' }));
console.log(dedup.add({ id: 1, name: 'John' }));
console.log(dedup.add({ id: 2, name: 'Jane' }));

2. 版本控制 #

javascript
import md5 from './utils/md5';

const getVersionHash = (content) => {
    return md5(content).slice(0, 8);
};

const versions = new Map();

const saveVersion = (documentId, content) => {
    const version = getVersionHash(content);
    versions.set(`${documentId}:${version}`, {
        content,
        timestamp: Date.now()
    });
    return version;
};

const version1 = saveVersion('doc1', '第一版内容');
const version2 = saveVersion('doc1', '第二版内容');
console.log('版本1:', version1);
console.log('版本2:', version2);

3. 负载均衡 #

javascript
import md5 from './utils/md5';

class ConsistentHash {
    constructor(nodes = []) {
        this.ring = new Map();
        nodes.forEach(node => this.addNode(node));
    }
    
    addNode(node) {
        const hash = parseInt(md5(node).slice(0, 8), 16);
        this.ring.set(hash, node);
    }
    
    getNode(key) {
        const hash = parseInt(md5(key).slice(0, 8), 16);
        const sortedHashes = [...this.ring.keys()].sort((a, b) => a - b);
        
        for (const nodeHash of sortedHashes) {
            if (hash <= nodeHash) {
                return this.ring.get(nodeHash);
            }
        }
        
        return this.ring.get(sortedHashes[0]);
    }
}

const ch = new ConsistentHash(['node1', 'node2', 'node3']);
console.log(ch.getNode('user:1'));
console.log(ch.getNode('user:2'));
console.log(ch.getNode('user:3'));

安全注意事项 #

1. 不要用于密码存储 #

javascript
import md5 from './utils/md5';

const insecureHash = (password) => md5(password);

const secureHash = async (password, salt) => {
    const encoder = new TextEncoder();
    const data = encoder.encode(password + salt);
    const hashBuffer = await crypto.subtle.digest('SHA-256', data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
};

2. 使用 Web Crypto API #

javascript
const secureHash = async (message, algorithm = 'SHA-256') => {
    const encoder = new TextEncoder();
    const data = encoder.encode(message);
    const hashBuffer = await crypto.subtle.digest(algorithm, data);
    const hashArray = Array.from(new Uint8Array(hashBuffer));
    return hashArray.map(b => b.toString(16).padStart(2, '0')).join('');
};

await secureHash('hello world', 'SHA-256');
await secureHash('hello world', 'SHA-384');
await secureHash('hello world', 'SHA-512');

下一步 #

现在你已经掌握了加密哈希,接下来学习 高级用法,了解深拷贝、深合并等高级技巧!

最后更新:2026-04-04