自主安全极简密码管理器
大家是不是经常因为密码太多而经常忘记用手机系统自带的密码管理器还会经常乱弹密码修改了密码后自带手机密码器又在乱蹦。这还是次要的除了密码乱蹦问题很担心密码在系统里面泄露。如果你有这些问题那么我们的安全密码存储软件很适合你。用纯html编写只有200行代码可以方便的在任何系统上运行;代码完全透明可方便的进行审查修改;根据所设密码实时计算不需存储任何密码所有文件加密后只有自己能够解开。!DOCTYPE html html head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0, maximum-scale1.0, user-scalableyes title密码库·移动适配版/title style /* 基础样式 */ body { font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Helvetica, Arial, sans-serif; max-width: 600px; margin: 1em auto; padding: 1em; background: #f9f9f9; color: #333; } h1 { font-size: clamp(1.5rem, 6vw, 2rem); text-align: center; margin-bottom: 1rem; } input, textarea, button { font-size: 1rem; padding: 0.8rem; border-radius: 8px; border: 1px solid #ccc; box-sizing: border-box; /* 让宽度包含 padding */ } input, textarea { width: 100%; margin: 0.5rem 0; } textarea { min-height: 200px; font-family: monospace; } button { background-color: #007bff; color: white; border: none; font-weight: 500; cursor: pointer; transition: background 0.2s; margin: 0.2rem 0; width: 100%; /* 手机上按钮占满宽度好点 */ } button:disabled { background-color: #ccc; cursor: not-allowed; } button:hover { background-color: #0056b3; } #keySection { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1.5rem; } #keySection label { font-weight: 600; } .actions { margin-top: 2rem; border-top: 1px solid #ddd; padding-top: 1rem; display: flex; flex-wrap: wrap; gap: 0.5rem; } .actions button { flex: 1 1 auto; /* 让按钮在一行内自动伸缩 */ } .note { font-size: 0.9rem; color: #666; font-style: italic; } /* 针对特别小的屏幕再微调 */ media (max-width: 400px) { body { padding: 0.8em; } button { font-size: 0.9rem; padding: 0.7rem; } } /style /head body h1 密码库移动版/h1 div idkeySection label原始密钥input typepassword idmasterKey placeholder输入你的密钥 //label div styledisplay: flex; gap: 0.5rem; flex-wrap: wrap; button idunlockBtn解锁/button button idlockBtn disabled锁定/button button idsaveBtn保存加密/button /div /div div idcontent styledisplay:none; textarea identriesText placeholder例如{entries:[{site:example.com,username:alice,password:pssw0rd}]}/textarea div styledisplay: flex; gap: 0.5rem; flex-wrap: wrap; button idaddEntryBtn➕ 添加示例条目/button /div p classnote提示如果没有密码库请直接输入内容并点击“保存加密”创建。/p /div div classactions h3 备份与恢复/h3 button idexportBtn导出加密库.json/button button idimportBtn导入加密库.json/button input typefile idimportFile accept.json,application/json styledisplay:none; / /div script // --- 以下 JavaScript 代码和之前完全一样无需修改 --- const STORAGE_KEY encryptedVault; function ab2base64(buf) { return btoa(String.fromCharCode(...new Uint8Array(buf))); } function base642ab(base64) { return Uint8Array.from(atob(base64), c c.charCodeAt(0)).buffer; } async function encryptVault(plaintext, password) { const salt crypto.getRandomValues(new Uint8Array(16)); const iv crypto.getRandomValues(new Uint8Array(12)); const iterations 100000; const baseKey await crypto.subtle.importKey( raw, new TextEncoder().encode(password), { name: PBKDF2 }, false, [deriveKey] ); const key await crypto.subtle.deriveKey( { name: PBKDF2, salt: salt, iterations: iterations, hash: SHA-256 }, baseKey, { name: AES-GCM, length: 256 }, false, [encrypt] ); const encoded new TextEncoder().encode(plaintext); const ciphertext await crypto.subtle.encrypt( { name: AES-GCM, iv: iv }, key, encoded ); return { salt: ab2base64(salt), iv: ab2base64(iv), iterations: iterations, ciphertext: ab2base64(ciphertext) }; } async function decryptVault(encryptedObj, password) { const salt base642ab(encryptedObj.salt); const iv base642ab(encryptedObj.iv); const iterations encryptedObj.iterations; const ciphertext base642ab(encryptedObj.ciphertext); const baseKey await crypto.subtle.importKey( raw, new TextEncoder().encode(password), { name: PBKDF2 }, false, [deriveKey] ); const key await crypto.subtle.deriveKey( { name: PBKDF2, salt: salt, iterations: iterations, hash: SHA-256 }, baseKey, { name: AES-GCM, length: 256 }, false, [decrypt] ); const decrypted await crypto.subtle.decrypt( { name: AES-GCM, iv: iv }, key, ciphertext ); return new TextDecoder().decode(decrypted); } function initUI() { const hasVault localStorage.getItem(STORAGE_KEY) ! null; const contentDiv document.getElementById(content); const unlockBtn document.getElementById(unlockBtn); const lockBtn document.getElementById(lockBtn); const saveBtn document.getElementById(saveBtn); const masterKeyInput document.getElementById(masterKey); if (hasVault) { contentDiv.style.display none; unlockBtn.disabled false; lockBtn.disabled true; saveBtn.disabled true; } else { contentDiv.style.display block; unlockBtn.disabled true; lockBtn.disabled true; saveBtn.disabled false; masterKeyInput.value ; document.getElementById(entriesText).value ; } } document.getElementById(unlockBtn).addEventListener(click, async () { const password document.getElementById(masterKey).value; const encryptedStr localStorage.getItem(STORAGE_KEY); if (!encryptedStr) { alert(没有找到密码库请先创建。); initUI(); return; } try { const encryptedObj JSON.parse(encryptedStr); const plaintext await decryptVault(encryptedObj, password); document.getElementById(entriesText).value plaintext; document.getElementById(content).style.display block; document.getElementById(unlockBtn).disabled true; document.getElementById(lockBtn).disabled false; document.getElementById(saveBtn).disabled false; } catch (e) { alert(解密失败密钥错误或数据损坏。); } }); document.getElementById(saveBtn).addEventListener(click, async () { const password document.getElementById(masterKey).value; if (!password) { alert(请输入原始密钥); return; } const plaintext document.getElementById(entriesText).value; if (!plaintext.trim()) { alert(请输入要加密的内容); return; } try { const encryptedObj await encryptVault(plaintext, password); localStorage.setItem(STORAGE_KEY, JSON.stringify(encryptedObj)); alert(密码库已加密保存。); initUI(); } catch (e) { alert(加密失败 e); } }); document.getElementById(lockBtn).addEventListener(click, () { document.getElementById(masterKey).value ; document.getElementById(entriesText).value ; document.getElementById(content).style.display none; document.getElementById(unlockBtn).disabled false; document.getElementById(lockBtn).disabled true; document.getElementById(saveBtn).disabled true; }); document.getElementById(addEntryBtn).addEventListener(click, () { const current document.getElementById(entriesText).value; const example { entries: [ { site: example.com, username: alice, password: pssw0rd } ] }; if (current.trim() ) { document.getElementById(entriesText).value JSON.stringify(example, null, 2); } else { try { let data JSON.parse(current); if (!data.entries) data.entries []; data.entries.push({ site: new-site.com, username: user, password: newpass }); document.getElementById(entriesText).value JSON.stringify(data, null, 2); } catch { alert(当前内容不是有效的 JSON无法添加条目。); } } }); document.getElementById(exportBtn).addEventListener(click, () { const encryptedStr localStorage.getItem(STORAGE_KEY); if (!encryptedStr) { alert(没有加密库可导出。); return; } const blob new Blob([encryptedStr], { type: application/json }); const url URL.createObjectURL(blob); const a document.createElement(a); a.href url; a.download password-vault-${new Date().toISOString().slice(0,10)}.json; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); }); const fileInput document.getElementById(importFile); document.getElementById(importBtn).addEventListener(click, () { fileInput.click(); }); fileInput.addEventListener(change, (event) { const file event.target.files[0]; if (!file) return; const reader new FileReader(); reader.onload (e) { try { const content e.target.result; const importedData JSON.parse(content); if (!importedData.salt || !importedData.iv || !importedData.iterations || !importedData.ciphertext) { throw new Error(文件格式不正确缺少必要的加密字段); } localStorage.setItem(STORAGE_KEY, content); alert(加密库导入成功。); initUI(); fileInput.value ; } catch (err) { alert(导入失败 err.message); } }; reader.readAsText(file); }); initUI(); /script /body /html欢迎大家使用有任何问题欢迎大家留言讨论。目前主攻边缘端AI本地部署与内网安全。如果您的企业有数据不外传、但需要私有化AI或者离线大模型的需求可以联系我探讨落地。