引言

哈希算法将任意输入映射为固定长度的摘要。摘要不是唯一标识符、加密结果或真实性证明:有限输出空间必然存在碰撞,低熵输入可以被猜测;如果攻击者能同时替换文件和未认证的摘要,普通校验也无法阻止篡改。

📋 目录

关键要点

  • 安全属性有条件:原像、第二原像和抗碰撞性是不同属性,任何属性都不能让低熵秘密不可猜。
  • 速度取决于用途:SHA-2/SHA-3 面向通用哈希;密码存储需要可调成本、内存硬化或刻意昂贵的 KDF。
  • MD5/SHA-1 属于遗留算法:碰撞弱点使其不适用于对抗性完整性和签名;MD5 仅可在明确记录的非对抗性旧式校验中出现。
  • 应用需要认证边界:摘要只有在期望值来自可信渠道时才能检测意外变化;对抗攻击应使用 MAC、数字签名或认证传输。
  • 哈希与加密:哈希用于验证,而加密用于保密。两者目的不同。
  • 选择算法:按协议、互操作目标和威胁模型选择构造,并在接近生产的硬件上校准密码 KDF 参数。

本文将深入探讨哈希算法的机制、不同类型的算法(如MD5、SHA-1、SHA-256)及其在现代技术中的应用。

常见哈希算法对比

为了帮助您快速了解,下表总结了最常见的哈希算法及其安全状态:

算法 输出长度 (位) 安全状态
MD5 128 不安全
SHA-1 160 不安全
SHA-256 256 安全
SHA-512 512 安全
SHA-3 224, 256, 384, 512 安全

选择原则:根据协议要求、互操作性、密钥边界和威胁模型选择哈希构造;不要把通用哈希替代密码 KDF。

代码示例:如何生成哈希值

下面我们通过代码示例,展示如何在不同编程语言中生成 SHA-256 哈希值。

JavaScript (Node.js)

javascript
import crypto from 'crypto';

function calculateSHA256(input) {
  const hash = crypto.createHash('sha256');
  hash.update(input);
  return hash.digest('hex');
}

const data = '你好, 世界!';
const hash = calculateSHA256(data);
console.log(`SHA-256 哈希值: ${hash}`);

Python

python
import hashlib

def calculate_sha256(input_string):
    sha256_hash = hashlib.sha256()
    sha256_hash.update(input_string.encode('utf-8'))
    return sha256_hash.hexdigest()

data = "你好, 世界!"
hashed_data = calculate_sha256(data)
print(f"SHA-256 哈希值: {hashed_data}")

Java

java
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class HashingExample {
    public static String calculateSHA256(String input) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] encodedhash = digest.digest(input.getBytes(StandardCharsets.UTF_8));
            return bytesToHex(encodedhash);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    private static String bytesToHex(byte[] hash) {
        StringBuilder hexString = new StringBuilder(2 * hash.length);
        for (byte b : hash) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) {
                hexString.append('0');
            }
            hexString.append(hex);
        }
        return hexString.toString();
    }

    public static void main(String[] args) {
        String data = "你好, 世界!";
        String sha256 = calculateSHA256(data);
        System.out.println("SHA-256 哈希值: " + sha256);
    }
}

实际应用

哈希算法在各行各业都有广泛应用:

  • 数据完整性验证:当期望摘要来自可信渠道时,摘要可检测意外变化;对抗攻击应使用 MAC、签名或认证传输。
  • 密码存储:使用 Argon2id、Bcrypt 或 Scrypt 等专用 KDF,配合唯一盐值、校准成本和升级策略;不能直接存 SHA-256。
  • 数字签名:签名方案使用私钥签署消息或其摘要,再用公钥验证;签名不是笼统的“用私钥加密哈希”。
  • 区块链技术:哈希可以链接数据结构,但不可变性和真实性还取决于共识、密钥管理和完整协议。
  • 数据去重:通过存储和比较文件哈希值,云存储服务可以识别和消除重复文件,节省存储空间。

常见问题 (FAQ)

1. 哈希与加密有何不同?

哈希是固定输出映射;高熵输入的恢复可能很难,但候选值仍可被猜测,有限输出空间也必然存在碰撞。加密是使用密钥保护机密性的可逆机制。

2. 为什么 MD5 和 SHA-1 不再安全?

MD5 和 SHA-1 的碰撞攻击已使其不适合签名或对抗性完整性。旧系统中可能仍有非对抗性校验,但期望摘要必须来自可信渠道,不能据此防止恶意替换。

3. 什么是“加盐”(Salting)?

“加盐”是向密码 KDF 提供唯一随机值,并与编码后的参数一起存储。它能阻止预计算表并区分相同密码,但不能单独抵抗弱密码的在线或离线猜测。

4. 如何为我的应用选择合适的哈希算法?

应按协议、互操作目标和威胁模型选择哈希构造。密码存储不要使用 SHA-256/SHA-512/SHA-3,优先使用维护良好的 Argon2id、Bcrypt 或 Scrypt(受约束时使用 PBKDF2),并在生产近似硬件上校准成本和重哈希策略。

5. 哈希值可以被“解密”吗?

哈希没有“解密”操作。攻击者可以猜测候选输入并比较摘要,因此安全性取决于输入熵和构造;密码必须使用慢速 KDF。

结论

哈希算法只是安全构造的组成部分。MD5 和 SHA-1 不适合对抗性碰撞场景;SHA-2 和 SHA-3 是通用哈希家族,密码存储需要独立、可调的 KDF,而认证完整性通常需要 MAC 或数字签名。选择构造时应同时记录参数、密钥管理、验证渠道以及摘要能够证明和不能证明的内容。