2026/6/1

scrypt

scrypt 是一種password-based key derivation演算法。是加拿大計算機科學家暨計算機安全研究人員 Colin Percival 於2009年所發明的密鑰派生函數,當初設計用在他所創立的Tarsnap服務上。設計時考慮到大規模的客製硬體攻擊而刻意設計需要大量記憶體運算,可防止 GPU/ASIC 大規模破解。2016年,scrypt 演算法發佈在RFC 7914。scrypt的簡化版被用在數個密碼貨幣的工作量證明(Proof-of-Work)上。

scrypt需要使用大量記憶體的原因來自於產生大量 pseudorandom 資料作為演算法計算的基礎。一旦這些資料被產生後,演算法將會以偽隨機性的順序讀取這些資料產生結果。因此最直接的實做方式將會需要大量記憶體將這些資料儲存在記憶體內供演算法計算。由於偽隨機性資料是透過演算法產生,在實作上也可以在需要存取時再計算以降低記憶體使用量。但由於計算成本很高,這個實作方法將大幅降低演算法的速度。

主要的參數:

透過 r, p 參數,調整記憶體使用量

參數 說明
N 迭代次數,耗費 CPU 與時間成本(必須是 2 的次方)
r 記憶體使用量(block size)
p 並行度(parallelism)
salt 隨機值,避免彩虹表攻擊
dkLen 輸出 hash 長度,通常是 32 bytes / 64 bytes

java sample

pom.xml

        <!-- scrypt -->
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk18on</artifactId>
            <version>1.82</version>
        </dependency>

ScryptBouncyCastleExample.java

import org.bouncycastle.crypto.generators.SCrypt;
import java.security.SecureRandom;
import java.util.Base64;

public class ScryptBouncyCastleExample {

    // scrypt 參數
    private static final int N = 16384; // CPU cost
    private static final int r = 8;     // Memory cost
    private static final int p = 1;     // Parallelism
    private static final int KEY_LENGTH = 32;

    // 產生隨機 salt
    public static byte[] generateSalt() {
        byte[] salt = new byte[16];
        new SecureRandom().nextBytes(salt);
        return salt;
    }

    // Hash 密碼
    public static String hashPassword(String password, byte[] salt) {
        byte[] hash = SCrypt.generate(password.getBytes(), salt, N, r, p, KEY_LENGTH);
        return Base64.getEncoder().encodeToString(hash);
    }

    // 驗證密碼
    public static boolean verifyPassword(String password, byte[] salt, String storedHash) {
        String hashToVerify = hashPassword(password, salt);
        return hashToVerify.equals(storedHash);
    }

    public static void main(String[] args) {
        String password = "MyPassword123!";

        // 產生 salt
        byte[] salt = generateSalt();

        // 生成 hash
        String hashed = hashPassword(password, salt);
        System.out.println("Salt (Base64): " + Base64.getEncoder().encodeToString(salt));
        System.out.println("Hashed password: " + hashed);

        // 驗證
        boolean match = verifyPassword(password, salt, hashed);
        System.out.println("Password match? " + match);

        boolean wrong = verifyPassword("wrongPassword", salt, hashed);
        System.out.println("Password match with wrong password? " + wrong);
    }
}

執行結果

Salt (Base64): +VF7o5rmURY++GxWmKARhw==
Hashed password: PON5gVuqeiOM4MDszfY4DHBjBacpMdObu07WhdTaoyo=
Password match? true
Password match with wrong password? false

References

scrypt - 維基百科,自由的百科全書