100 lines
2.5 KiB
JavaScript
100 lines
2.5 KiB
JavaScript
|
||
// 挂载到 window 对象的方法
|
||
window.passwordUtils = {
|
||
/**
|
||
* 哈希密码(返回 Promise)
|
||
* @param {string} password - 明文密码
|
||
* @returns {Promise<string>} Base64 格式的盐值+哈希值
|
||
*/
|
||
hashPassword: async function(password) {
|
||
const encoder = new TextEncoder();
|
||
|
||
// 生成随机盐值 (16字节)
|
||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||
|
||
// 配置 PBKDF2 参数
|
||
const keyMaterial = await crypto.subtle.importKey(
|
||
'raw',
|
||
encoder.encode(password),
|
||
{ name: 'PBKDF2' },
|
||
false,
|
||
['deriveBits']
|
||
);
|
||
|
||
// 生成哈希
|
||
const derivedKey = await crypto.subtle.deriveBits(
|
||
{
|
||
name: 'PBKDF2',
|
||
salt: salt,
|
||
iterations: 100000,
|
||
hash: 'SHA-256'
|
||
},
|
||
keyMaterial,
|
||
256 // 输出长度
|
||
);
|
||
|
||
// 合并盐值+哈希值并转为 Base64
|
||
const hashArray = new Uint8Array(derivedKey);
|
||
const combined = new Uint8Array(salt.length + hashArray.length);
|
||
combined.set(salt);
|
||
combined.set(hashArray, salt.length);
|
||
|
||
return btoa(String.fromCharCode(...combined));
|
||
},
|
||
|
||
/**
|
||
* 验证密码(返回 Promise)
|
||
* @param {string} password - 待验证密码
|
||
* @param {string} storedHash - 存储的 Base64 哈希值
|
||
* @returns {Promise<boolean>} 是否匹配
|
||
*/
|
||
verifyPassword: async function(password, storedHash) {
|
||
const encoder = new TextEncoder();
|
||
|
||
// 解码 Base64 获取盐值和原哈希
|
||
const decoded = Uint8Array.from(atob(storedHash), c => c.charCodeAt(0));
|
||
const salt = decoded.slice(0, 16);
|
||
const originalHash = decoded.slice(16);
|
||
|
||
// 重新计算哈希
|
||
const keyMaterial = await crypto.subtle.importKey(
|
||
'raw',
|
||
encoder.encode(password),
|
||
{ name: 'PBKDF2' },
|
||
false,
|
||
['deriveBits']
|
||
);
|
||
|
||
const newHash = await crypto.subtle.deriveBits(
|
||
{
|
||
name: 'PBKDF2',
|
||
salt: salt,
|
||
iterations: 100000,
|
||
hash: 'SHA-256'
|
||
},
|
||
keyMaterial,
|
||
256
|
||
);
|
||
|
||
// 比较哈希值
|
||
const newHashArray = new Uint8Array(newHash);
|
||
return newHashArray.length === originalHash.length &&
|
||
newHashArray.every((val, i) => val === originalHash[i]);
|
||
}
|
||
};
|
||
|
||
// 控制台测试示例(可直接复制到控制台)
|
||
/*
|
||
(async () => {
|
||
// 哈希测试
|
||
const hash = await window.passwordUtils.hashPassword('mypassword');
|
||
console.log('Hashed:', hash);
|
||
|
||
// 验证测试
|
||
const isValid = await window.passwordUtils.verifyPassword('mypassword', hash);
|
||
console.log('Verify result:', isValid); // 应输出 true
|
||
|
||
const isInvalid = await window.passwordUtils.verifyPassword('wrongpass', hash);
|
||
console.log('Wrong password test:', isInvalid); // 应输出 false
|
||
})();
|
||
*/ |