195 lines
8.8 KiB
HTML
195 lines
8.8 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<meta name="renderer" content="webkit">
|
||
<script>(function(){var t;try{t=localStorage.getItem("hp-theme")}catch(e){}if(!t){t=(window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches)?"dark":"light"}document.documentElement.setAttribute("data-theme",t);})();</script>
|
||
<title>Navicat 密码解密</title>
|
||
<link rel="icon" href="../assets/img/logo.svg">
|
||
<link rel="stylesheet" href="../assets/css/common.css">
|
||
</head>
|
||
<body>
|
||
<div class="func-bar"><div class="wrap func-bar-inner">
|
||
<a class="btn btn-sm js-fb-back" href="index.php" title="返回功能区首页">← 返回功能区首页</a>
|
||
<span class="func-bar-title">Navicat 密码解密</span>
|
||
<a class="btn btn-sm" href="../index.php">首页</a>
|
||
</div></div>
|
||
|
||
<main class="page-main">
|
||
<div class="wrap page-body">
|
||
<div class="panel-card">
|
||
<p class="tip" style="margin-top:0">🔒 全部运算在本机浏览器完成,<b>密文 / 明文不会上传服务器</b>,可放心处理。</p>
|
||
<p class="tip" style="margin-top:4px">打开Navicat,点击左上角<code>文件</code>,点击<code>导出连接</code>选项,必须选择<code>导出密码</code>,然后上传ncx或者自行筛选password即可</p>
|
||
|
||
<div class="field-row" style="align-items:center;margin-top:12px">
|
||
<label class="fl" style="margin:0" for="nvVer">Navicat 版本</label>
|
||
<select id="nvVer">
|
||
<option value="12">12 及以后(AES-128-CBC)</option>
|
||
<option value="11">11 及以前(Blowfish)</option>
|
||
</select>
|
||
<div class="seg" id="nvDir">
|
||
<button type="button" data-dir="dec" class="active">解密</button>
|
||
<button type="button" data-dir="enc">加密</button>
|
||
</div>
|
||
<button type="button" class="btn btn-primary" id="nvRun">执行</button>
|
||
<button type="button" class="btn" id="nvSwap" title="把输出内容放入输入区">输出 → 输入</button>
|
||
<button type="button" class="btn" id="nvClear">清空</button>
|
||
</div>
|
||
<div id="nvMsg"></div>
|
||
</div>
|
||
|
||
<div class="panel-card">
|
||
<h2>解析 .ncx 连接文件</h2>
|
||
<p class="tip">选择 Navicat 导出的 <code>.ncx</code> 连接文件,本工具在本地解析其中全部连接并按规范格式输出;连接密码会用<b>上方所选 Navicat 版本</b>自动解密。文件不会上传服务器。</p>
|
||
<div class="field-row" style="align-items:center;flex-wrap:wrap">
|
||
<input type="file" id="nvFile" accept=".ncx,.xml,text/xml,application/xml" style="flex:1 1 280px;min-width:220px">
|
||
<button type="button" class="btn btn-primary" id="nvParse">解析文件</button>
|
||
<span class="tip" style="align-self:center">选择后自动解析;切换版本后可点“解析文件”重新解密</span>
|
||
</div>
|
||
<div id="nvFileMsg"></div>
|
||
</div>
|
||
|
||
<div class="io-area">
|
||
<div class="io-box">
|
||
<div class="io-label">
|
||
<span>输入区(密文 / 明文,可编辑)</span>
|
||
<button type="button" class="btn btn-sm js-copy" data-target="#nvIn">复制</button>
|
||
</div>
|
||
<textarea id="nvIn" placeholder="解密:粘贴十六进制密文(如 CE3AAB73CBBE383C);加密:输入明文密码" spellcheck="false"></textarea>
|
||
</div>
|
||
<div class="io-box">
|
||
<div class="io-label">
|
||
<span>输出区(可编辑)</span>
|
||
<button type="button" class="btn btn-sm js-copy" data-target="#nvOut">复制</button>
|
||
</div>
|
||
<textarea id="nvOut" placeholder="结果输出到这里,可手动修改后再次使用" spellcheck="false"></textarea>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
|
||
<button type="button" class="theme-fab js-theme-btn" title="切换昼夜模式" aria-label="切换昼夜模式"><span class="js-theme-icon"></span></button>
|
||
<button type="button" class="theme-fab to-top js-to-top" title="返回顶部" aria-label="返回顶部">↑</button>
|
||
<script src="../assets/js/common.js"></script>
|
||
<script src="../assets/js/navicat.js"></script>
|
||
<script>
|
||
(function () {
|
||
'use strict';
|
||
function $(id) { return document.getElementById(id); }
|
||
var verSel = $('nvVer');
|
||
var dirButtons = Array.prototype.slice.call(document.querySelectorAll('#nvDir button'));
|
||
var nvIn = $('nvIn');
|
||
var nvOut = $('nvOut');
|
||
var nvMsg = $('nvMsg');
|
||
|
||
function activeDir() {
|
||
var b = dirButtons.filter(function (x) { return x.classList.contains('active'); })[0];
|
||
return b ? b.getAttribute('data-dir') : 'dec';
|
||
}
|
||
function setMsg(text, isErr) {
|
||
nvMsg.innerHTML = '';
|
||
if (!text) return;
|
||
var d = document.createElement('div');
|
||
d.className = isErr ? 'err-msg' : 'ok-msg';
|
||
d.textContent = text;
|
||
nvMsg.appendChild(d);
|
||
}
|
||
function busy(on) {
|
||
$('nvRun').disabled = on;
|
||
$('nvRun').textContent = on ? '处理中…' : '执行';
|
||
}
|
||
function label(dir) { return dir === 'dec' ? '解密' : '加密'; }
|
||
|
||
function run() {
|
||
var ver = verSel.value;
|
||
var dir = activeDir();
|
||
var text = nvIn.value;
|
||
if (!text) { setMsg('请先输入内容', true); nvIn.focus(); return; }
|
||
var C = window.NavicatCipher;
|
||
if (!C) { setMsg('组件未加载,请刷新页面重试', true); return; }
|
||
busy(true);
|
||
setMsg('正在' + label(dir) + '…', false);
|
||
var p = (dir === 'dec') ? C.decrypt(text, ver) : C.encrypt(text, ver);
|
||
p.then(function (out) {
|
||
nvOut.value = out;
|
||
busy(false);
|
||
if (dir === 'dec' && out && out.indexOf('\uFFFD') >= 0) {
|
||
setMsg('解密完成,但结果含乱码字符:可能是密文与所选版本不匹配,或密文不完整 / 不合法,请核对版本后重试。', true);
|
||
} else {
|
||
setMsg(label(dir) + '完成(Navicat ' + ver + ')', false);
|
||
}
|
||
}).catch(function (e) {
|
||
busy(false);
|
||
setMsg(label(dir) + '失败:' + (e && e.message ? e.message : e), true);
|
||
});
|
||
}
|
||
|
||
dirButtons.forEach(function (b) {
|
||
b.addEventListener('click', function () {
|
||
dirButtons.forEach(function (x) { x.classList.remove('active'); });
|
||
b.classList.add('active');
|
||
nvIn.placeholder = (b.getAttribute('data-dir') === 'dec')
|
||
? '解密:粘贴十六进制密文(如 CE3AAB73CBBE383C)'
|
||
: '加密:输入明文密码';
|
||
});
|
||
});
|
||
$('nvRun').addEventListener('click', run);
|
||
$('nvSwap').addEventListener('click', function () {
|
||
if (nvOut.value) { nvIn.value = nvOut.value; setMsg('已将输出内容回填到输入区', false); }
|
||
});
|
||
$('nvClear').addEventListener('click', function () {
|
||
nvIn.value = ''; nvOut.value = ''; setMsg('', false); nvIn.focus();
|
||
});
|
||
nvIn.addEventListener('keydown', function (ev) {
|
||
if ((ev.ctrlKey || ev.metaKey) && ev.key === 'Enter') { ev.preventDefault(); run(); }
|
||
});
|
||
|
||
/* ---------- .ncx 连接文件解析 ---------- */
|
||
var nvFile = $('nvFile');
|
||
var nvFileMsg = $('nvFileMsg');
|
||
var nvParseBtn = $('nvParse');
|
||
var ncxText = '';
|
||
|
||
function setFileMsg(text, isErr) {
|
||
nvFileMsg.innerHTML = '';
|
||
if (!text) return;
|
||
var d = document.createElement('div');
|
||
d.className = isErr ? 'err-msg' : 'ok-msg';
|
||
d.textContent = text;
|
||
nvFileMsg.appendChild(d);
|
||
}
|
||
function parseBusy(on) {
|
||
nvParseBtn.disabled = on;
|
||
nvParseBtn.textContent = on ? '解析中…' : '解析文件';
|
||
}
|
||
function runNcx() {
|
||
var C = window.NavicatCipher;
|
||
if (!C) { setFileMsg('组件未加载,请刷新页面重试', true); return; }
|
||
if (!ncxText) { setFileMsg('请先选择 .ncx 文件', true); return; }
|
||
parseBusy(true);
|
||
setFileMsg('正在解析并解密…', false);
|
||
C.buildConnectionReport(ncxText, verSel.value).then(function (report) {
|
||
nvOut.value = report;
|
||
parseBusy(false);
|
||
setFileMsg('解析完成,结果已输出到下方输出区', false);
|
||
}).catch(function (e) {
|
||
parseBusy(false);
|
||
setFileMsg('解析失败:' + (e && e.message ? e.message : e), true);
|
||
});
|
||
}
|
||
|
||
nvFile.addEventListener('change', function () {
|
||
var f = nvFile.files && nvFile.files[0];
|
||
if (!f) return;
|
||
var reader = new FileReader();
|
||
reader.onload = function () { ncxText = String(reader.result || ''); setFileMsg('已读取:' + f.name, false); runNcx(); };
|
||
reader.onerror = function () { setFileMsg('文件读取失败', true); };
|
||
reader.readAsText(f, 'UTF-8');
|
||
});
|
||
nvParseBtn.addEventListener('click', runNcx);
|
||
})();
|
||
</script>
|
||
</body>
|
||
</html>
|