首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 程式設計入門

Unix 標準加密方法:crypt 與基於 DES 的單向雜湊演進

     編輯:Java 程式設計入門

在 Unix/Linux 系統的早期演進中,密碼儲存安全 是核心課題之一。傳統 Unix 採用基於 **DES (Data Encryption Standard)** 變體的 crypt(3) 函數進行密碼單向雜湊,這套機制雖然在當時具備開創性,但隨著算力提升已暴露嚴重缺陷。本文將梳理 Unix 標準加密的歷史脈絡、技術細節、已知弱點,以及現代替代方案,為理解當代密碼學基礎設施提供歷史視角。

經典 Unix Crypt (DES-based) 機制詳解

核心演算法流程

  1. 輸入: 用戶密碼 (最多取前 8 字元) + 2 字元 Salt (擾亂碼)。
  2. Salt 生成: 從 [a-zA-Z0-9./] 64 字元集隨機選取 2 字元,提供 12-bit 隨機性 (4096 種組合)。
  3. 密碼處理: 密碼轉為 7-bit ASCII,每字元最高位補 0,組成 56-bit 金鑰 (不足 8 字元以 null 填充)。
  4. DES 加密迭代: 以該金鑰加密固定常數區塊 (全零或特定魔數),共進行 25 輪 迭代 (標準 DES 16 輪 + 修改置換表)。
  5. 輸出格式: $salt$hash (傳統格式直接拼接),共 13 字元可列印字串 (2 字元 salt + 11 字元 hash)。

Salt 的關鍵作用

  • 防禦彩虹表/預計算攻擊: 同一密碼配合不同 Salt 產生完全不同雜湊,攻擊者無法預先計算常見密碼字典。
  • 防禦同一密碼識別: 即使兩用戶密碼相同,儲存的雜湊值也不同 (因 Salt 不同),洩露雜湊庫不直接洩露密碼重用情況。

致命弱點:為何它已被淘汰

弱點 影響
密碼長度截斷 (8 字元) 超過 8 字元的密碼直接截斷,極大降低熵值,長密碼無意義。
僅 12-bit Salt (4096 種) 現代 GPU 可在秒級遍歷所有 Salt 組合,大規模並行破解極其高效。
DES 56-bit 金鑰空間過小 專用硬體 (ASIC/FPGA) 可在數天內窮舉;現代 GPU 算力更令暴力破解極其廉價。
固定 25 輪迭代不可配置 無法隨算力增長增加計算成本 (無「工作量因子」機制),違反現代雜湊「可調適緩慢」原則。
不支援 Unicode/多位元組字元 僅處理 7-bit ASCII,國際化場景嚴重受限。

演進時間軸:從 DES Crypt 到現代標準

  1. 传统 DES Crypt (1970s-1990s):crypt(3),/etc/passwd 可讀,極度脆弱。
  2. Shadow Password Suite (1990s):將雜湊移至 /etc/shadow (僅 root 可讀),緩解離線破解風險,但演算法未變。
  3. MD5 Crypt ($1$) (1994, Poul-Henning Kamp):引入可變長度 Salt、任意長度密碼、1000 輪 MD5 迭代。FreeBSD/Linux 採用,曾為主流。
  4. SHA-256/512 Crypt ($5$/$6$) (2007, Ulrich Drepper):基於 SHA-2 家族,支援更長 Salt、更高迭代次數 (預設 5000,可配置),/etc/shadow 現見格式。
  5. bcrypt ($2a$/$2b$/$2y$) (1999, OpenBSD):基於 Blowfish 金鑰擴展,內建昂貴的金鑰設置過程,自適應工作量因子 (cost parameter),專為密碼雜湊設計,**長期業界金標準**。
  6. Argon2 (2015, PHC 冠軍):記憶體硬化 函數,有效抵禦 GPU/ASIC/FPGA 並行攻擊,三種模式 (Argon2d, Argon2i, Argon2id),**現代新專案首選**。
  7. scrypt (2009, Colin Percival):同為記憶體硬化,早期加密貨幣廣泛使用,參數調整較 Argon2 繁瑣。

Java 開發者的實踐指南

在 Java 中處理 Unix 相容密碼驗證或遷移:

1. 驗證傳統 crypt 雜湊 (不建議新系統使用)

// 使用 Bouncy Castle 或 Apache Commons Codec
import org.apache.commons.codec.digest.UnixCrypt;
String hash = UnixCrypt.crypt("userPassword", "salt"); // 產生 DES crypt
boolean valid = UnixCrypt.crypt("inputPassword", storedHash).equals(storedHash);

2. 新系統:強制使用 Argon2id (推薦) 或 bcrypt

// 依賴: org.bouncycastle:bcprov-jdk18on / com.password4j:password4j
// Password4J 範例 (現代高層 API)
import com.password4j.Password;
import com.password4j.Argon2Function;

Argon2Function argon2 = Argon2Function.getInstance(16, 3, 1, 32); // 記憶體, 迭代, 並行度, 雜湊長度
String hash = Password.hash("userPassword").addRandomSalt(16).with(argon2).getResult();

// 驗證
boolean valid = Password.check("inputPassword", storedHash).with(argon2);

3. Spring Security 整合 (生產標準)

@Bean
public PasswordEncoder passwordEncoder() {
    // DelegatingPasswordEncoder 自動識別儲存格式前綴 ({bcrypt}, {argon2}, {SHA-256}...)
    // 新編碼預設使用 BCrypt (強度 10),可改為 Argon2
    String idForEncode = "bcrypt"; 
    Map encoders = Map.of(
        "bcrypt", new BCryptPasswordEncoder(10),
        "argon2", new Argon2PasswordEncoder(16, 3, 1, 32), // 需 Spring Security 5.6+
        "noop", NoOpPasswordEncoder.getInstance() // 僅測試用
    );
    return new DelegatingPasswordEncoder(idForEncode, encoders);
}

總結

Unix 標準加密的歷史,是一部**「算力增長 vs 防禦機制演進」的軍備競賽史**。從固定 25 輪 DES 到可配置迭代的 SHA-2 Crypt,再到記憶體硬化的 Argon2,核心邏輯始終圍繞:**增加攻擊者單次嘗試成本、消除預計算優勢、適應硬體演進**。作為開發者,**請勿在任何新專案中使用傳統 crypt/MD5/SHA-1/單純 SHA-256 作為密碼雜湊**,請擁抱 Argon2id (首選) 或 bcrypt (次選),並配合 DelegatingPasswordEncoder 實現平滑遷移與多格式共存。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved