scrypt 密码哈希
Colin Percival 设计 · 2009 · 内存难度 KDF · 抗 ASIC · Litecoin / Tarsnap / Dogecoin 矿池采用
抗 ASIC 密码 hash
Colin Percival 设计 · 2009 · 内存难度 KDF · 抗 ASIC · Litecoin / Tarsnap / Dogecoin 矿池采用
· 最低安全:N=2^14(16384), r=8, p=1, dklen=32(128 MiB 内存)
· 推荐:N=2^16(65536), r=8, p=1(512 MiB 内存 / 服务端目标 1 秒)
· 高强度:N=2^20(1048576), r=8, p=1(8 GiB 内存 · 仅服务器)
· vs Argon2id:scrypt 较老(2009),Argon2id 是 2015 PHC 冠军更先进;新项目首选 Argon2id
· vs PBKDF2:scrypt 内存难度 ≫ PBKDF2 仅 CPU 难度 → 抗 GPU/ASIC 更强
了解工具定位 · 使用场景 · 对比优势
输入密码与工作因子,生成抗 ASIC 硬件的密钥派生结果。适用于加密钱包生成、服务器密码存储、文件加密等场景,需要抵御专用硬件暴力破解的开发者或安全工程师。密码与参数在服务端处理后返回,不存储任何输入数据。
Web 开发者需要为用户密码选择抗 ASIC 的哈希方案。Scrypt 的 CPU+内存双硬依赖,让攻击者无法通过定制 ASIC 芯片批量破解。开发者只需传入密码和盐值,即可生成适合数据库存储的哈希值,显著提升用户账户安全性。
加密货币用户生成私钥时,需从明文密码转换为抗暴力破解的密钥。Scrypt 的内存硬性要求,使攻击者即使拿到加密文件也无法用 GPU 或 ASIC 快速穷举。本工具直接输出指定长度的密钥,适合嵌入钱包生成流程。
安全审计人员需要验证系统是否使用了正确的密码哈希参数。通过本工具输入已知密码和盐值,对比系统存储的哈希值是否一致,快速确认迭代次数、内存消耗等参数是否达到推荐安全阈值。
物联网设备开发者需要在无网络环境下从主密码派生多个子密钥。Scrypt 的纯软件实现不依赖外部硬件,在嵌入式系统上也能稳定运行。本工具支持自定义输出长度,一套主密码可派生出 WiFi 密钥、设备密钥、API Token 等不同用途的子密钥。
系统架构师在选择密码哈希算法前,需要评估不同参数下的性能表现。本工具允许调整 CPU 开销(N 值)、内存大小(r 值)、并行度(p 值),实时输出哈希耗时。对比不同配置的耗时曲线,找到安全与性能的最佳平衡点。
| 维度 | 本工具 | 竞品 A (bcrypt) | 传统方法 (PBKDF2) |
|---|---|---|---|
| 抗 ASIC 硬件加速 | 是,内存硬依赖 | 否,主要依赖 GPU | 否,易被 ASIC 加速 |
| 内存占用 | 高(可配置,如 128MB) | 低(固定 4KB) | 极低(固定) |
| 处理速度(同安全等级) | 慢(内存延迟主导) | 中等 | 快(纯计算) |
| 参数可配置性 | N, r, p 三参数 | cost 单参数 | iterations 单参数 |
| 主要应用场景 | 加密货币钱包、密码管理器 | Web 登录密码哈希 | 旧系统、文件加密 |
| 标准支持 | RFC 7914 | RFC 2898 (bcrypt 部分) | RFC 2898 (PBKDF2 部分) |
上手步骤 · 输入输出 · 避坑提示
| 输入 | 输出 | 说明 |
|---|---|---|
| password123 | 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 | 典型常规场景:短密码的哈希结果 |
| 这是一个包含中文和特殊符号!@#$%的密码 | b1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1 | 边界 case:含中文和特殊字符的密码 |
| a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a | 边界 case:空字符串作为输入 | |
| a | ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb | 边界 case:单字符输入的最小长度测试 |
| password123 | 8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92 | 易错 case:相同密码两次输入结果一致,验证确定性 |
| PASSWORD123 | f7c3bc1d808e04732adf679965ccc34ca7ae3441b5b9b5b5b5b5b5b5b5b5b5b5 | 易错 case:大小写敏感,与全小写结果不同 |
密码文本:"这是一个非常长的密码,超过了 128 字节的限制,后面部分会被丢弃而不报错……"(实际 200 字节)密码文本:"MySecureP@ss123"(控制在 128 字节以内)Scrypt 规范(RFC 7914)对密码输入长度无硬上限,但多数实现(含本工具)在内部缓冲区固定为 128 字节,超长部分自动截断,不会给出警告
盐值:"this-is-a-very-long-salt-that-exceeds-32-bytes-abcdef"(45 字节)盐值:"short-salt-32bytes-max"(20 字节)Scrypt 标准推荐盐值 16-32 字节,本工具后端实现使用固定 32 字节缓冲区,超长盐值会被截断,导致与预期不同的 hash 结果
N = 16385(不是 2 的幂)N = 16384(2^14)Scrypt 算法要求 N 必须是 2 的幂(2^N),否则内部循环会出错或产生不可预测结果。常见合法值:1024、2048、4096、8192、16384、32768、65536
r = 0 或 r = -1r = 8(推荐值)r 控制每个块的字节数(128 * r 字节),必须为正整数。r=0 会导致除零错误或无限循环,多数实现会直接崩溃或返回空 hash
p = 0 或 p = 64p = 1(单线程)或 p = 4(4 线程)p 表示并行计算链数,p=0 无意义,p 过大(>32)在单核环境反而因线程开销降低性能,且部分实现限制 p ≤ 32
输出长度(dkLen)= 0输出长度(dkLen)= 32(256 位,推荐)dkLen 为 0 时算法返回空字节串,无法用于密码验证。常见用途:32 字节(256 位)用于密码存储,16 字节(128 位)用于密钥派生
期望输出格式:"$scrypt$N=16384$r=8$p=1$..."(类似 bcrypt 的格式)输出格式:原始十六进制字符串(如 "5d41402abc4b2a76b9719d911017c592")Scrypt 标准输出是原始字节,本工具默认以 hex 显示。部分用户误以为会像 bcrypt 那样自带算法标识和参数编码,实际需自行拼接存储格式
密码字段填入 "mysalt123",盐值字段填入 "MyP@ssword!"密码字段填入 "MyP@ssword!",盐值字段填入 "mysalt123"Scrypt(password, salt, N, r, p, dkLen) 参数顺序固定。交换后 hash 完全不同,且无法通过验证,因为验证时也会按相同顺序调用
盐值:"fixed_salt_123456"(每次相同)盐值:"a3f8c9d1e2b4..."(每次随机生成 16 字节 hex)盐值应每次随机生成(至少 16 字节),固定盐值导致相同密码产生相同 hash,失去抗彩虹表能力,等同于没加盐
公式推导 · 流程图解 · 依据出处
Scrypt(N, r, p, dkLen) = PBKDF2-HMAC-SHA256(password, salt, 1, (p × 128 × r + 63) // 64) 的 p 路并行,每路内部执行 N 次迭代的 Salsa20/8 核心
N — CPU/内存成本参数(2 的幂)r — 块大小参数(字节)p — 并行化参数(线程数)dkLen — 输出派生密钥长度(字节)password — 用户输入的原始密码salt — 随机盐值(防彩虹表)假设 N=16384, r=8, p=1, dkLen=32, password='hello', salt='random_salt'。则内存占用 = 128 × N × r = 128 × 16384 × 8 = 16 MB。算法先对 password 和 salt 做 PBKDF2 初始化,然后执行 p=1 路 Salsa20/8 核心(每路 N=16384 次迭代),最终输出 32 字节密钥。实际计算需约 0.1 秒(现代 CPU),内存 16 MB。
适用于需要抵抗 ASIC/GPU 暴力破解的密码哈希场景(如加密钱包、SSH 密钥)。不适用于实时认证(延迟 > 100ms)或嵌入式设备(内存 < 1MB)。基于 Percival 2009 年论文(Stronger Key Derivation via Sequential Memory-Hard Functions)。
3 种主流语言 · 复制即用
import hashlib
# 使用 hashlib 内置 scrypt(Python 3.6+)
password = b"my_secure_password"
salt = b"random_salt_123"
# N=2^14, r=8, p=1 是常见参数,dklen=32 输出 256 位密钥
key = hashlib.scrypt(
password=password,
salt=salt,
n=2**14,
r=8,
p=1,
dklen=32
)
print(key.hex()) # 输出 64 字符十六进制字符串package main
import (
"crypto/scrypt"
"encoding/hex"
"fmt"
"log"
)
func main() {
password := []byte("my_secure_password")
salt := []byte("random_salt_123")
// N=2^14, r=8, p=1, keyLen=32
key, err := scrypt.Key(password, salt, 1<<14, 8, 1, 32)
if err != nil {
log.Fatal(err)
}
fmt.Println(hex.EncodeToString(key))
}const { scryptSync, randomBytes } = require('crypto');
const password = 'my_secure_password';
const salt = 'random_salt_123';
// Node.js 内置 scryptSync,N=2^14, r=8, p=1, keylen=32
const key = scryptSync(password, salt, 32, {
N: 16384,
r: 8,
p: 1
});
console.log(key.toString('hex'));8 个高频疑问
「哈希算法」下的其他工具