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
|
|||
|
})();
|
|||
|
*/
|