本文目录导读:

在 PHP 中实现密钥派生,最常用、最安全的方法是使用 PBKDF2(基于密码的密钥派生函数 2)或 Argon2(更现代、推荐用于密码哈希,也可用于派生密钥),以下是几种常见实现方式:
使用 hash_pbkdf2() 函数(推荐)
这是最直接的 PBKDF2 实现,PHP 内置支持:
<?php
function deriveKey($password, $salt, $iterations = 100000, $keyLength = 32) {
$derivedKey = hash_pbkdf2(
'sha256', // 哈希算法
$password, // 原始密码
$salt, // 盐值
$iterations, // 迭代次数
$keyLength, // 输出密钥长度(字节数)
true // 原始二进制输出
);
return $derivedKey;
}
// 使用示例
$password = "my_secure_password";
$salt = random_bytes(16); // 生成安全的随机盐值
$iterations = 100000; // 根据性能调整
$keyLength = 32; // 256位密钥
$derivedKey = deriveKey($password, $salt, $iterations, $keyLength);
// 存储时需保存 salt 和 iterations
echo "Salt (hex): " . bin2hex($salt) . "\n";
echo "Derived Key (hex): " . bin2hex($derivedKey) . "\n";
?>
使用 Argon2(更安全、现代)
PHP 7.2+ 支持 Argon2,主要用于密码哈希,但也可用于派生密钥:
<?php
function deriveKeyWithArgon2($password, $salt, $keyLength = 32) {
// 将盐值编码进选项
$options = [
'memory_cost' => 1 << 17, // 128 MB
'time_cost' => 4,
'threads' => 2,
'salt' => $salt // 自定义盐值
];
// 使用 PASSWORD_ARGON2ID 算法
$hash = password_hash($password, PASSWORD_ARGON2ID, $options);
// 注意:password_hash 输出是包含所有参数的字符串
// 要提取纯密钥部分可能需要额外处理
return $hash;
}
// 或者使用 sodium 扩展(更直接)
function deriveKeyWithSodium($password, $salt, $keyLength = 32) {
// 需要 libsodium 扩展(PHP 7.2+ 默认包含)
$hash = sodium_crypto_pwhash(
$keyLength,
$password,
$salt,
SODIUM_CRYPTO_PWHASH_OPSLIMIT_MODERATE,
SODIUM_CRYPTO_PWHASH_MEMLIMIT_MODERATE
);
return $hash;
}
使用 OpenSSL 的 EVP_BytesToKey
适用于与其他系统兼容的场景(如 OpenSSL 加密):
<?php
function deriveKeyOpenSSL($password, $salt, $keyLength = 32, $ivLength = 16) {
$derived = '';
$total = $keyLength + $ivLength;
$hash = '';
while (strlen($derived) < $total) {
$hash = md5($hash . $password . $salt, true);
$derived .= $hash;
}
$key = substr($derived, 0, $keyLength);
$iv = substr($derived, $keyLength, $ivLength);
return ['key' => $key, 'iv' => $iv];
}
// 使用示例
$result = deriveKeyOpenSSL("password", "salted__" . random_bytes(8));
echo "Key (hex): " . bin2hex($result['key']) . "\n";
echo "IV (hex): " . bin2hex($result['iv']) . "\n";
?>
最佳实践建议
安全性考虑:
- 始终使用足够长的随机盐值(最少 16 字节)
- 迭代次数:根据服务器性能调整,至少 100,000 次
- 密钥长度:根据用途选择(AES-256 需要 32 字节)
- 避免使用 MD5 等弱哈希算法
完整示例(推荐方案):
<?php
class KeyDerivation {
const ALGORITHM = 'sha256';
const ITERATIONS = 100000;
const SALT_LENGTH = 32;
const KEY_LENGTH = 32;
public static function derive($password, $salt = null) {
if ($salt === null) {
$salt = random_bytes(self::SALT_LENGTH);
}
$key = hash_pbkdf2(
self::ALGORITHM,
$password,
$salt,
self::ITERATIONS,
self::KEY_LENGTH,
true
);
return [
'key' => $key,
'salt' => $salt,
'iterations' => self::ITERATIONS
];
}
public static function verify($password, $derivedKey, $salt, $iterations) {
$testKey = hash_pbkdf2(
self::ALGORITHM,
$password,
$salt,
$iterations,
strlen($derivedKey),
true
);
return hash_equals($derivedKey, $testKey);
}
}
// 使用示例
$password = "secure_password_123";
// 派生密钥
$result = KeyDerivation::derive($password);
echo "Salt: " . bin2hex($result['salt']) . "\n";
echo "Key: " . bin2hex($result['key']) . "\n";
// 验证
$isValid = KeyDerivation::verify($password, $result['key'], $result['salt'], $result['iterations']);
echo "验证结果: " . ($isValid ? "成功" : "失败") . "\n";
?>
选择建议
| 场景 | 推荐方法 |
|---|---|
| 一般密钥派生 | hash_pbkdf2() |
| 密码存储 | password_hash() + Argon2 |
| 与现代系统兼容 | Argon2 或 PBKDF2 |
| OpenSSL 互操作 | EVP_BytesToKey |
永远不要使用简单的哈希函数(如 MD5、SHA1)直接派生密钥,始终使用专门的密钥派生函数(KDF)。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播