React Router
Cryptography

哈希函数

深入理解哈希函数的原理、算法和应用

哈希函数

哈希函数是密码学中的重要工具,它将任意长度的数据转换为固定长度的哈希值,具有单向性、抗碰撞性和雪崩效应等特性。

哈希函数的基本概念

哈希函数(Hash Function)是一种将任意长度的输入数据映射为固定长度输出的数学函数。

输入数据 → 哈希函数 → 哈希值(固定长度)

哈希函数的特性

  1. 确定性:相同输入总是产生相同输出
  2. 单向性:无法从哈希值反推原始数据
  3. 抗碰撞性:很难找到两个不同的输入产生相同的哈希值
  4. 雪崩效应:输入的微小变化导致输出的巨大变化

经典哈希算法

1. MD5(Message Digest Algorithm 5)

// MD5 哈希函数(简化实现)
function md5(message) {
  // MD5 使用 4 个 32 位寄存器
  let a = 0x67452301;
  let b = 0xEFCDAB89;
  let c = 0x98BADCFE;
  let d = 0x10325476;
  
  // 消息填充
  const paddedMessage = padMessage(message);
  
  // 处理 512 位块
  for (let i = 0; i < paddedMessage.length; i += 64) {
    const block = paddedMessage.slice(i, i + 64);
    processBlock(block, a, b, c, d);
  }
  
  return toHex(a) + toHex(b) + toHex(c) + toHex(d);
}

// 消息填充
function padMessage(message) {
  const bitLength = message.length * 8;
  let padded = message + String.fromCharCode(0x80);
  
  // 填充到 448 位(mod 512)
  while ((padded.length * 8) % 512 !== 448) {
    padded += String.fromCharCode(0x00);
  }
  
  // 添加原始长度(64位)
  padded += String.fromCharCode(bitLength & 0xFF);
  for (let i = 1; i < 8; i++) {
    padded += String.fromCharCode((bitLength >>> (i * 8)) & 0xFF);
  }
  
  return padded;
}

// 转换为十六进制
function toHex(num) {
  return ('00000000' + num.toString(16)).slice(-8);
}

注意:MD5 已被认为不安全,不推荐用于安全应用。

2. SHA-1(Secure Hash Algorithm 1)

// SHA-1 哈希函数(简化实现)
function sha1(message) {
  // SHA-1 使用 5 个 32 位寄存器
  let h0 = 0x67452301;
  let h1 = 0xEFCDAB89;
  let h2 = 0x98BADCFE;
  let h3 = 0x10325476;
  let h4 = 0xC3D2E1F0;
  
  // 消息填充
  const paddedMessage = padMessage(message);
  
  // 处理 512 位块
  for (let i = 0; i < paddedMessage.length; i += 64) {
    const block = paddedMessage.slice(i, i + 64);
    processBlock(block, h0, h1, h2, h3, h4);
  }
  
  return toHex(h0) + toHex(h1) + toHex(h2) + toHex(h3) + toHex(h4);
}

注意:SHA-1 也已被认为不安全。

3. SHA-256(Secure Hash Algorithm 256)

// SHA-256 哈希函数(简化实现)
function sha256(message) {
  // SHA-256 初始哈希值
  const H = [
    0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
    0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
  ];
  
  // SHA-256 常量
  const K = [
    0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
    0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
    // ... 更多常量
  ];
  
  // 消息填充
  const paddedMessage = padMessage(message);
  
  // 处理 512 位块
  for (let i = 0; i < paddedMessage.length; i += 64) {
    const block = paddedMessage.slice(i, i + 64);
    processBlock(block, H, K);
  }
  
  return H.map(h => toHex(h)).join('');
}

// 右旋转函数
function rightRotate(value, amount) {
  return (value >>> amount) | (value << (32 - amount));
}

// SHA-256 压缩函数
function processBlock(block, H, K) {
  // 准备消息调度
  const W = new Array(64);
  
  // 前16个字来自块
  for (let i = 0; i < 16; i++) {
    W[i] = (block[i * 4] << 24) | (block[i * 4 + 1] << 16) | 
           (block[i * 4 + 2] << 8) | block[i * 4 + 3];
  }
  
  // 扩展其余48个字
  for (let i = 16; i < 64; i++) {
    const s0 = rightRotate(W[i-15], 7) ^ rightRotate(W[i-15], 18) ^ (W[i-15] >>> 3);
    const s1 = rightRotate(W[i-2], 17) ^ rightRotate(W[i-2], 19) ^ (W[i-2] >>> 10);
    W[i] = (W[i-16] + s0 + W[i-7] + s1) >>> 0;
  }
  
  // 初始化工作变量
  let a = H[0], b = H[1], c = H[2], d = H[3];
  let e = H[4], f = H[5], g = H[6], h = H[7];
  
  // 主循环
  for (let i = 0; i < 64; i++) {
    const S1 = rightRotate(e, 6) ^ rightRotate(e, 11) ^ rightRotate(e, 25);
    const ch = (e & f) ^ (~e & g);
    const temp1 = (h + S1 + ch + K[i] + W[i]) >>> 0;
    const S0 = rightRotate(a, 2) ^ rightRotate(a, 13) ^ rightRotate(a, 22);
    const maj = (a & b) ^ (a & c) ^ (b & c);
    const temp2 = (S0 + maj) >>> 0;
    
    h = g; g = f; f = e; e = (d + temp1) >>> 0;
    d = c; c = b; b = a; a = (temp1 + temp2) >>> 0;
  }
  
  // 更新哈希值
  H[0] = (H[0] + a) >>> 0; H[1] = (H[1] + b) >>> 0;
  H[2] = (H[2] + c) >>> 0; H[3] = (H[3] + d) >>> 0;
  H[4] = (H[4] + e) >>> 0; H[5] = (H[5] + f) >>> 0;
  H[6] = (H[6] + g) >>> 0; H[7] = (H[7] + h) >>> 0;
}

现代哈希算法

1. SHA-3(Keccak)

SHA-3 使用海绵结构(Sponge Construction),与 SHA-2 家族完全不同。

// SHA-3 海绵结构(简化版)
class Sponge {
  constructor(rate, capacity) {
    this.rate = rate;
    this.capacity = capacity;
    this.state = new Array(rate + capacity).fill(0);
  }
  
  // 吸收阶段
  absorb(input) {
    const paddedInput = this.pad(input);
    
    for (let i = 0; i < paddedInput.length; i += this.rate) {
      const block = paddedInput.slice(i, i + this.rate);
      
      // XOR 块到状态
      for (let j = 0; j < this.rate; j++) {
        this.state[j] ^= block[j] || 0;
      }
      
      // 应用置换函数
      this.permute();
    }
  }
  
  // 挤压阶段
  squeeze(length) {
    const output = [];
    
    while (output.length < length) {
      // 输出状态的前 rate 位
      for (let i = 0; i < this.rate && output.length < length; i++) {
        output.push(this.state[i]);
      }
      
      // 应用置换函数
      this.permute();
    }
    
    return output.slice(0, length);
  }
  
  // 置换函数(简化版)
  permute() {
    // 这里应该实现 Keccak-f 置换
    // 简化处理,实际需要复杂的位操作
    for (let i = 0; i < this.state.length; i++) {
      this.state[i] = (this.state[i] * 7 + 13) % 256;
    }
  }
  
  // 填充函数
  pad(input) {
    return input.concat([0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80]);
  }
}

// SHA-3-256 实现
function sha3_256(message) {
  const sponge = new Sponge(1088, 512); // SHA-3-256 参数
  sponge.absorb(message);
  const hash = sponge.squeeze(32);
  return hash.map(b => ('0' + b.toString(16)).slice(-2)).join('');
}

2. BLAKE2

BLAKE2 是 BLAKE 的改进版本,速度更快,安全性更高。

// BLAKE2b 哈希函数(简化版)
function blake2b(message, key = '', outputLength = 64) {
  // BLAKE2b 初始向量
  const IV = [
    0x6a09e667f3bcc908, 0xbb67ae8584caa73b,
    0x3c6ef372fe94f82b, 0xa54ff53a5f1d36f1,
    0x510e527fade682d1, 0x9b05688c2b3e6c1f,
    0x1f83d9abfb41bd6b, 0x5be0cd19137e2179
  ];
  
  // 初始化状态
  const state = [...IV];
  
  // XOR 密钥长度
  state[0] ^= outputLength;
  state[4] ^= key.length;
  
  // 处理消息
  const paddedMessage = padMessage(message);
  
  for (let i = 0; i < paddedMessage.length; i += 128) {
    const block = paddedMessage.slice(i, i + 128);
    compress(block, state, false);
  }
  
  // 最终压缩
  const finalBlock = new Array(128).fill(0);
  compress(finalBlock, state, true);
  
  // 返回哈希值
  return state.slice(0, outputLength / 8)
    .map(h => h.toString(16).padStart(16, '0'))
    .join('');
}

// BLAKE2b 压缩函数(简化版)
function compress(block, state, final) {
  // 这里应该实现完整的 BLAKE2b 压缩函数
  // 包括 12 轮的 G 函数调用
  // 简化处理
  for (let i = 0; i < 8; i++) {
    state[i] = (state[i] + block[i * 8]) >>> 0;
  }
}

哈希函数的应用

1. 密码存储

// 安全的密码哈希
class PasswordHasher {
  constructor() {
    this.saltLength = 32;
    this.iterations = 100000;
  }
  
  // 哈希密码
  hashPassword(password) {
    const salt = crypto.randomBytes(this.saltLength);
    const hash = this.pbkdf2(password, salt, this.iterations);
    
    return {
      hash: hash.toString('hex'),
      salt: salt.toString('hex'),
      iterations: this.iterations
    };
  }
  
  // 验证密码
  verifyPassword(password, storedHash, storedSalt, iterations) {
    const salt = Buffer.from(storedSalt, 'hex');
    const hash = this.pbkdf2(password, salt, iterations);
    
    return crypto.timingSafeEqual(hash, Buffer.from(storedHash, 'hex'));
  }
  
  // PBKDF2 实现
  pbkdf2(password, salt, iterations) {
    let u = crypto.createHmac('sha256', password)
      .update(salt)
      .update(Buffer.from([0, 0, 0, 1]))
      .digest();
    
    let result = u;
    
    for (let i = 1; i < iterations; i++) {
      u = crypto.createHmac('sha256', password).update(u).digest();
      
      for (let j = 0; j < result.length; j++) {
        result[j] ^= u[j];
      }
    }
    
    return result;
  }
}

// 使用示例
const hasher = new PasswordHasher();
const password = "mySecurePassword123";

const hashed = hasher.hashPassword(password);
console.log(`哈希: ${hashed.hash}`);
console.log(`盐值: ${hashed.salt}`);

const isValid = hasher.verifyPassword(password, hashed.hash, hashed.salt, hashed.iterations);
console.log(`验证结果: ${isValid}`);

2. 数字签名

// 数字签名系统
class DigitalSignature {
  constructor() {
    this.rsaKeys = generateRSAKeys();
  }
  
  // 签名消息
  sign(message) {
    // 1. 计算消息哈希
    const hash = sha256(message);
    
    // 2. 使用私钥签名哈希
    const signature = rsaSign(hash, this.rsaKeys.privateKey);
    
    return {
      message: message,
      signature: signature,
      algorithm: 'RSA-SHA256'
    };
  }
  
  // 验证签名
  verify(signedMessage) {
    // 1. 计算消息哈希
    const hash = sha256(signedMessage.message);
    
    // 2. 使用公钥验证签名
    const decryptedHash = rsaVerify(signedMessage.signature, this.rsaKeys.publicKey);
    
    // 3. 比较哈希值
    return hash === decryptedHash;
  }
}

3. 区块链和默克尔树

// 默克尔树实现
class MerkleTree {
  constructor(leaves) {
    this.leaves = leaves.map(leaf => sha256(leaf));
    this.tree = this.buildTree();
  }
  
  // 构建默克尔树
  buildTree() {
    let level = this.leaves;
    const tree = [level];
    
    while (level.length > 1) {
      const nextLevel = [];
      
      for (let i = 0; i < level.length; i += 2) {
        const left = level[i];
        const right = i + 1 < level.length ? level[i + 1] : left;
        const parent = sha256(left + right);
        nextLevel.push(parent);
      }
      
      level = nextLevel;
      tree.push(level);
    }
    
    return tree;
  }
  
  // 获取根哈希
  getRoot() {
    return this.tree[this.tree.length - 1][0];
  }
  
  // 生成证明
  generateProof(leafIndex) {
    const proof = [];
    let index = leafIndex;
    
    for (let level = 0; level < this.tree.length - 1; level++) {
      const isRightNode = index % 2 === 1;
      const siblingIndex = isRightNode ? index - 1 : index + 1;
      
      if (siblingIndex < this.tree[level].length) {
        proof.push({
          hash: this.tree[level][siblingIndex],
          isRight: isRightNode
        });
      }
      
      index = Math.floor(index / 2);
    }
    
    return proof;
  }
  
  // 验证证明
  verifyProof(leaf, proof, root) {
    let hash = sha256(leaf);
    
    for (const proofElement of proof) {
      if (proofElement.isRight) {
        hash = sha256(hash + proofElement.hash);
      } else {
        hash = sha256(proofElement.hash + hash);
      }
    }
    
    return hash === root;
  }
}

// 使用示例
const data = ["block1", "block2", "block3", "block4"];
const merkleTree = new MerkleTree(data);

console.log(`根哈希: ${merkleTree.getRoot()}`);

const proof = merkleTree.generateProof(1);
const isValid = merkleTree.verifyProof("block2", proof, merkleTree.getRoot());
console.log(`证明验证: ${isValid}`);

4. 文件完整性验证

// 文件哈希计算
class FileHasher {
  constructor() {
    this.chunkSize = 64 * 1024; // 64KB 块
  }
  
  // 计算文件哈希
  async hashFile(file) {
    const reader = new FileReader();
    const chunks = [];
    
    return new Promise((resolve, reject) => {
      reader.onload = (e) => {
        const arrayBuffer = e.target.result;
        const hash = this.hashBuffer(arrayBuffer);
        resolve(hash);
      };
      
      reader.onerror = reject;
      reader.readAsArrayBuffer(file);
    });
  }
  
  // 计算缓冲区哈希
  hashBuffer(buffer) {
    const uint8Array = new Uint8Array(buffer);
    const hash = sha256(Array.from(uint8Array));
    return hash;
  }
  
  // 分块计算大文件哈希
  async hashLargeFile(file) {
    const chunks = [];
    let offset = 0;
    
    while (offset < file.size) {
      const chunk = file.slice(offset, offset + this.chunkSize);
      const chunkHash = await this.hashFile(chunk);
      chunks.push(chunkHash);
      offset += this.chunkSize;
    }
    
    // 计算所有块哈希的哈希
    return sha256(chunks.join(''));
  }
}

哈希函数的攻击

1. 碰撞攻击

// 生日攻击(简化版)
function birthdayAttack(hashFunction, hashLength) {
  const hashSpace = Math.pow(2, hashLength);
  const expectedCollision = Math.sqrt(Math.PI * hashSpace / 2);
  
  console.log(`哈希空间大小: 2^${hashLength}`);
  console.log(`预期碰撞点: ${expectedCollision}`);
  
  return expectedCollision;
}

// 示例
birthdayAttack(sha256, 256);

2. 彩虹表攻击

// 彩虹表生成(简化版)
class RainbowTable {
  constructor() {
    this.table = new Map();
  }
  
  // 生成彩虹表
  generateTable(plaintexts, hashFunction) {
    for (const plaintext of plaintexts) {
      const hash = hashFunction(plaintext);
      this.table.set(hash, plaintext);
    }
  }
  
  // 查找哈希
  lookup(hash) {
    return this.table.get(hash);
  }
}

最佳实践

  1. 选择安全的算法:使用 SHA-256、SHA-3 或 BLAKE2
  2. 使用盐值:为密码哈希添加随机盐值
  3. 多次迭代:使用 PBKDF2、bcrypt 或 Argon2
  4. 验证完整性:使用 HMAC 进行消息认证
  5. 定期更新:关注算法安全性的最新研究
  6. 避免弱哈希:不使用 MD5、SHA-1 等已破解的算法

哈希函数是密码学的基础工具,理解其原理和应用对于构建安全的系统至关重要。