Navicat 密码一旦保存, 再次编辑就没法查看密码是什么了, 包括之前可以使用的星号密码查看器也看不到密码, 我们可以通过导出链接文件, 然后通过解密被加密的密码来获取密码原文.

导出加密密码

打开 Navicat – 文件 – 导出连接
image.png|758

接下来的弹窗里, 勾选导出密码, 然后导出成 ncx 文件
image.png

用文本编辑器打开 connections.ncx 文件, 复制 password 的值, 这个就是加密后的密码.

使用 PHP 解密

电脑没有 PHP 环境, 所以选择使用 Docker 来运行 PHP, 操作如下:

准备解密脚本

vim navicat-decode-password.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
<?php

namespace FatSmallTools;

class NavicatPassword
{
protected $version = 0;
protected $aesKey = 'libcckeylibcckey';
protected $aesIv = 'libcciv libcciv ';
protected $blowString = '3DC5CA39';
protected $blowKey = null;
protected $blowIv = null;

public function __construct($version = 12)
{
$this->version = $version;
$this->blowKey = sha1('3DC5CA39', true);
$this->blowIv = hex2bin('d9c7c3c8870d64bd');
}

public function encrypt($string)
{
$result = FALSE;
switch ($this->version) {
case 11:
$result = $this->encryptEleven($string);
break;
case 12:
$result = $this->encryptTwelve($string);
break;
default:
break;
}

return $result;
}

protected function encryptEleven($string)
{
$round = intval(floor(strlen($string) / 8));
$leftLength = strlen($string) % 8;
$result = '';
$currentVector = $this->blowIv;

for ($i = 0; $i < $round; $i++) {
$temp = $this->encryptBlock($this->xorBytes(substr($string, 8 * $i, 8), $currentVector));
$currentVector = $this->xorBytes($currentVector, $temp);
$result .= $temp;
}

if ($leftLength) {
$currentVector = $this->encryptBlock($currentVector);
$result .= $this->xorBytes(substr($string, 8 * $i, $leftLength), $currentVector);
}

return strtoupper(bin2hex($result));
}

protected function encryptBlock($block)
{
return openssl_encrypt($block, 'BF-ECB', $this->blowKey, OPENSSL_RAW_DATA|OPENSSL_NO_PADDING);
}

protected function decryptBlock($block)
{
return openssl_decrypt($block, 'BF-ECB', $this->blowKey, OPENSSL_RAW_DATA|OPENSSL_NO_PADDING);
}

protected function xorBytes($str1, $str2)
{
$result = '';
for ($i = 0; $i < strlen($str1); $i++) {
$result .= chr(ord($str1[$i]) ^ ord($str2[$i]));
}

return $result;
}

protected function encryptTwelve($string)
{
$result = openssl_encrypt($string, 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv);
return strtoupper(bin2hex($result));
}

public function decrypt($string)
{
$result = FALSE;
switch ($this->version) {
case 11:
$result = $this->decryptEleven($string);
break;
case 12:
$result = $this->decryptTwelve($string);
break;
default:
break;
}

return $result;
}

protected function decryptEleven($upperString)
{
$string = hex2bin(strtolower($upperString));

$round = intval(floor(strlen($string) / 8));
$leftLength = strlen($string) % 8;
$result = '';
$currentVector = $this->blowIv;

for ($i = 0; $i < $round; $i++) {
$encryptedBlock = substr($string, 8 * $i, 8);
$temp = $this->xorBytes($this->decryptBlock($encryptedBlock), $currentVector);
$currentVector = $this->xorBytes($currentVector, $encryptedBlock);
$result .= $temp;
}

if ($leftLength) {
$currentVector = $this->encryptBlock($currentVector);
$result .= $this->xorBytes(substr($string, 8 * $i, $leftLength), $currentVector);
}

return $result;
}

protected function decryptTwelve($upperString)
{
$string = hex2bin(strtolower($upperString));
return openssl_decrypt($string, 'AES-128-CBC', $this->aesKey, OPENSSL_RAW_DATA, $this->aesIv);
}
}


use FatSmallTools\NavicatPassword;

// 需要指定版本,11 适用于11版本或以下.
// 12 适用于12版本和以上, 个人测试16版本可以正常解密.
$navicatPassword = new NavicatPassword(12);
//$navicatPassword = new NavicatPassword(11);

//解密, 从命令行参数读取
$encrypted_password = $_SERVER['argv'][1];
$decode = $navicatPassword->decrypt($encrypted_password);
echo $decode."\n";

使用 Docker 版 PHP 来执行脚本

执行 php 脚本,使用 cli 版本的镜像即可. 这里选择 php:8.3.1-cli

1
docker run --rm -v "$(pwd)":/data php:8.3.1-cli php /data/navicat-decode-password.php "F0FC4E94542FF60596CC6FFA9F5F68D1"

image.png

密码会打印在控制台上.

优化版脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/bin/bash

list=$(grep "<Connection ConnectionName=" connections.ncx)

IFS=$'\n' # 指定分隔符为换行
for i in ${list[*]};
do
name=$(echo "$i" | awk -F 'ConnectionName="' '{print $2}' | awk -F '"' '{print $1}')
user=$(echo "$i" | awk -F 'UserName="' '{print $2}' | awk -F '"' '{print $1}')
host=$(echo "$i" | awk -F 'Host="' '{print $2}' | awk -F '"' '{print $1}')
port=$(echo "$i" | awk -F 'Port="' '{print $2}' | awk -F '"' '{print $1}')
en_password=$(echo "$i" | awk -F 'Password="' '{print $2}' | awk -F '" ' '{print $1}')
password=$(docker run --rm -v "$(pwd)":/data php:8.3.1-cli php /data/navicat-decode-password.php "$en_password")

echo "$name"
echo "地址: $host"
echo "端口: $port"
echo "用户: $user"
echo "密码: $password"
echo ----------------------------------------------------------------------------------------
done

脚本会读取 connections.ncx 文件,并按照格式将内容输出:

image.png