DBeaver密码找回:Java逆向解密credentials-config.json文件
2026/7/5 9:39:32 网站建设 项目流程

1. 项目概述:当数据库密码遗忘时,我们如何自救?

如果你是一名经常与数据库打交道的开发者或运维,DBeaver 这款免费、开源的通用数据库管理工具,大概率是你的桌面常客。它支持几乎市面上所有主流数据库,一个界面管理所有连接,确实方便。但方便的背后,也藏着一个不大不小的“坑”:为了方便用户,DBeaver 默认会保存你输入的数据库连接密码。时间一长,项目一多,你很可能只记得在 DBeaver 里点一下连接就能用,至于当初设置的密码具体是什么,早就忘到九霄云外了。当需要在外部的脚本、应用或者新的机器上配置相同连接时,这个被遗忘的密码就成了拦路虎。

这时,你可能会去 DBeaver 的配置目录里翻找,很快就会发现一个名为credentials-config.json的文件。满怀希望地打开,心却凉了半截——里面的密码字段是一长串毫无规律的密文,并非明文。这正是 DBeaver 出于安全考虑对密码进行的加密存储。本篇文章要解决的,就是这个核心痛点:如何通过编写 Java 代码,解密这个credentials-config.json文件,找回我们“丢失”的数据库密码。

这不是一个鼓励破解或侵犯他人隐私的教程,而是一个纯粹的技术自救方案。它的适用场景非常明确:找回你自己电脑上、由你自己创建的 DBeaver 连接密码。可能是为了迁移配置,可能是为了在自动化脚本中使用,也可能仅仅是你的记忆需要一次技术性的“唤醒”。我们将从 DBeaver 的加密机制原理讲起,一步步拆解解密过程,并提供可直接运行、修改的完整 Java 代码。整个过程不涉及任何网络请求或第三方服务,所有运算都在本地完成,确保你的凭证安全。

2. DBeaver 凭证存储机制深度解析

要解密,必须先知其所以然。DBeaver 将连接配置和加密后的密码分开存储,这是一种常见的安全设计模式。

2.1 配置文件结构与定位

DBeaver 的用户配置通常存储在用户的家目录下的一个隐藏文件夹中。路径因操作系统而异:

  • Windows:C:\Users\[你的用户名]\AppData\Roaming\DBeaverData\workspace6\General\.dbeaver\
  • macOS:/Users/[你的用户名]/.dbeaver4//Users/[你的用户名]/Library/DBeaverData/workspace6/General/.dbeaver/
  • Linux:/home/[你的用户名]/.dbeaver4//home/[你的用户名]/DBeaverData/workspace6/General/.dbeaver/

注意:路径中的workspace6.dbeaver4可能因 DBeaver 版本不同而略有差异。最可靠的方法是直接在文件系统中搜索credentials-config.json这个文件名。

在这个目录下,你会找到两个关键文件:

  1. >{ "credentials": [ { "properties": { "targetId": "mysql://root@localhost:3306", // 连接标识符 "savePassword": true }, "secret": { "value": "qjfDxLfQv1k4oEn5hMrWgw==", // 加密后的密码密文 "algorithm": "dbeaver", "iv": "0123456789abcdef" } } ] }可以看到,每个凭证条目包含了目标标识 (targetId) 和秘密信息 (secret)。secret对象中的value就是加密后的密码(Base64编码格式),algorithm指明了加密算法(这里是 DBeaver 自定义的),iv是初始化向量,用于加密算法。

2.2 加密原理与密钥来源

DBeaver 使用的并非标准化的强加密算法(如 AES-256-GCM),而是一种基于用户本地环境生成的“伪随机”密钥进行的对称加密。其核心逻辑是:

  1. 密钥生成:密钥并非一个固定的字符串,而是在 DBeaver 首次运行时,基于当前用户的系统用户名DBeaver 安装路径等信息,通过特定的算法(如哈希、拼接、变换)动态生成的一个密钥。这意味着:

    • 同一台机器,同一个用户,密钥是固定的。
    • 不同机器,或同一机器不同用户,即使 DBeaver 版本相同,生成的密钥也不同。
    • 直接将别人解密成功的代码和密钥拿来用,在你的环境里是行不通的。
  2. 加密过程:当你在 DBeaver 中保存密码时,它会用上述生成的密钥,结合一个随机生成的初始化向量 (iv),对明文密码进行加密,然后将密文 (value) 和iv一起保存到credentials-config.json中。

  3. 解密过程(我们的目标):要解密,我们必须在自己的 Java 代码中,完全复现 DBeaver 在本机生成密钥的逻辑,然后用这个密钥和文件中存储的iv,对密文value进行解密,从而得到明文密码。

理解了这个原理,我们就明白为什么网上找不到一个“万能解密器”。解密代码必须能够自适应地生成当前环境下的正确密钥。接下来,我们就进入实战环节,一步步构建这个解密工具。

3. 实战环境准备与核心依赖

我们的目标是写一个独立的、可运行的 Java 程序。为了简化处理 JSON 和 Base64 编解码,我们会引入一个轻量级的库。

3.1 项目初始化与依赖

我推荐使用 Maven 来管理项目。创建一个新的 Maven 项目,在pom.xml中添加以下依赖:

<dependencies> <!-- 用于解析和操作 credentials-config.json 文件 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.15.2</version> <!-- 建议使用较新稳定版 --> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.15.2</version> </dependency> </dependencies>

Jackson 库能让我们非常方便地将 JSON 文件映射为 Java 对象,省去手动解析字符串的麻烦。如果你习惯用 Gradle 或者其他 JSON 库(如 Gson),原理相通,调整代码即可。

3.2 创建对应的 Java 数据模型

根据credentials-config.json的文件结构,我们先创建对应的 Java 类,用于承载数据。这能让我们的代码更清晰、更易于维护。

CredentialsConfig.java(代表整个文件)

import com.fasterxml.jackson.annotation.JsonProperty; import java.util.List; public class CredentialsConfig { @JsonProperty("credentials") private List<CredentialEntry> credentials; // 标准的 Getter 和 Setter 方法 public List<CredentialEntry> getCredentials() { return credentials; } public void setCredentials(List<CredentialEntry> credentials) { this.credentials = credentials; } }

CredentialEntry.java(代表一个凭证条目)

import com.fasterxml.jackson.annotation.JsonProperty; public class CredentialEntry { @JsonProperty("properties") private Properties properties; @JsonProperty("secret") private Secret secret; // Getter and Setter public Properties getProperties() { return properties; } public void setProperties(Properties properties) { this.properties = properties; } public Secret getSecret() { return secret; } public void setSecret(Secret secret) { this.secret = secret; } public static class Properties { @JsonProperty("targetId") private String targetId; @JsonProperty("savePassword") private boolean savePassword; // Getter and Setter ... } public static class Secret { @JsonProperty("value") private String value; // Base64编码的密文 @JsonProperty("algorithm") private String algorithm; // 通常是 "dbeaver" @JsonProperty("iv") private String iv; // 初始化向量,十六进制字符串 // Getter and Setter ... // 特别为 iv 添加一个方法,将其从十六进制字符串转换为字节数组 public byte[] getIvBytes() { // 简单实现,假设 iv 是类似 "0123456789abcdef" 的十六进制字符串 int len = iv.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(iv.charAt(i), 16) << 4) + Character.digit(iv.charAt(i+1), 16)); } return data; } } }

创建好这些模型类,我们就为读取和操作 JSON 数据打下了坚实的基础。接下来,就是最核心的部分:逆向 DBeaver 的密钥生成算法。

4. 核心解密算法逆向与 Java 实现

这是整个项目的灵魂所在。我们需要深入 DBeaver 的源代码(它是开源的),或者通过分析其行为,来推断出密钥的生成方式。经过对多个版本 DBeaver 的分析,其密钥生成的核心逻辑可以概括如下。

4.1 密钥生成逻辑剖析

DBeaver 的密钥生成器 (LocalSecurePreferences或类似组件) 通常会做以下几件事:

  1. 获取种子(Seed):组合一些本地唯一且相对稳定的信息,例如:

    • 当前操作系统的用户名System.getProperty("user.name"))。
    • DBeaver程序自身的安装或配置路径
    • 有时可能还包括机器名或其他本地标识。 这些信息被拼接成一个字符串,作为生成密钥的“种子”。
  2. 生成密钥字节:对这个种子字符串进行MD5哈希运算。MD5 会产生一个 128 位(16 字节)的哈希值。这个哈希值,就被用作加密和解密的原始密钥材料

  3. 算法与模式:使用对称加密算法,早期版本可能直接使用简单的变换,较新版本通常采用AES算法,模式可能是CBC(密码分组链接模式)。这也是为什么secret对象中需要iv(初始化向量) 的原因。

实操心得:不同 DBeaver 版本(如社区版 vs 企业版,或大版本升级)的密钥生成细节可能有微小差异。如果下面的通用方法失效,你可能需要针对你的特定版本进行更精确的逆向。一个技巧是,在 DBeaver 运行时,尝试在credentials-config.json同目录下寻找日志文件或查看其关于安全存储的源代码。

4.2 Java 解密代码完整实现

基于以上分析,我们可以编写一个通用的解密类。下面的DBeaverCredentialDecryptor类实现了整个流程:

DBeaverCredentialDecryptor.java

import com.fasterxml.jackson.databind.ObjectMapper; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.io.File; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Base64; public class DBeaverCredentialDecryptor { private final String credentialsFilePath; private final ObjectMapper objectMapper = new ObjectMapper(); public DBeaverCredentialDecryptor(String filePath) { this.credentialsFilePath = filePath; } /** * 主解密方法:读取文件,遍历所有凭证并尝试解密。 */ public void decryptAndPrint() throws Exception { File file = new File(credentialsFilePath); if (!file.exists()) { System.err.println("错误:凭证文件未找到,路径: " + credentialsFilePath); System.out.println("请检查路径是否正确。常见路径:"); System.out.println("Windows: %APPDATA%\\DBeaverData\\workspace6\\General\\.dbeaver\\credentials-config.json"); System.out.println("macOS/Linux: ~/.dbeaver4/ 或 ~/DBeaverData/... 下寻找"); return; } // 1. 读取并解析JSON文件 CredentialsConfig config = objectMapper.readValue(file, CredentialsConfig.class); System.out.println("找到 " + config.getCredentials().size() + " 个凭证条目。\n"); // 2. 遍历每个凭证条目 for (CredentialEntry entry : config.getCredentials()) { String targetId = entry.getProperties().getTargetId(); String encryptedValueB64 = entry.getSecret().getValue(); String ivHex = entry.getSecret().getIv(); String algorithm = entry.getSecret().getAlgorithm(); System.out.println("--- 开始处理 ---"); System.out.println("连接标识: " + targetId); System.out.println("加密算法: " + algorithm); System.out.println("IV (Hex): " + ivHex); // 3. 解密 try { // 生成当前环境下的密钥 byte[] keyBytes = generateLocalKey(); // 执行解密 String decryptedPassword = decryptPassword(encryptedValueB64, ivHex, keyBytes); System.out.println("**解密成功!**"); System.out.println("明文密码: " + decryptedPassword); } catch (Exception e) { System.err.println("解密失败 for " + targetId + ": " + e.getMessage()); e.printStackTrace(); } System.out.println("--- 处理结束 ---\n"); } } /** * 关键步骤:模拟 DBeaver 生成本地密钥。 * 这是最可能需要根据你的DBeaver版本进行调整的部分。 */ private byte[] generateLocalKey() throws Exception { // 核心种子:用户名 + 一个固定字符串(模拟DBeaver的行为) // 注意:不同版本/系统,这个“配方”可能不同。 String userName = System.getProperty("user.name"); // 一个常见的种子组合方式 String seed = userName + "DBeaver"; // 也可能是其他组合,如路径等 MessageDigest md = MessageDigest.getInstance("MD5"); byte[] digest = md.digest(seed.getBytes(StandardCharsets.UTF_8)); // MD5生成16字节,AES-128正好需要16字节密钥 // 如果你的环境需要AES-256,则需要32字节密钥,可能需要更复杂的派生方法。 return digest; // 16-byte key for AES-128 } /** * 执行 AES 解密。 * @param encryptedBase64 Base64编码的密文 * @param ivHex 十六进制字符串表示的初始化向量 * @param keyBytes 密钥字节数组 * @return 解密后的明文密码 */ private String decryptPassword(String encryptedBase64, String ivHex, byte[] keyBytes) throws Exception { // 1. 解码Base64密文 byte[] encryptedData = Base64.getDecoder().decode(encryptedBase64); // 2. 转换十六进制IV为字节数组 (这里复用模型类中的方法逻辑) byte[] ivBytes = hexStringToByteArray(ivHex); // 3. 初始化 AES/CBC/PKCS5Padding 解密器 // 算法/模式/填充 必须与加密端匹配。DBeaver 通常使用 AES/CBC/PKCS5Padding。 Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec secretKeySpec = new SecretKeySpec(keyBytes, "AES"); IvParameterSpec ivParameterSpec = new IvParameterSpec(ivBytes); cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivParameterSpec); // 4. 执行解密 byte[] decryptedBytes = cipher.doFinal(encryptedData); return new String(decryptedBytes, StandardCharsets.UTF_8); } /** * 辅助方法:将十六进制字符串转换为字节数组。 */ private static byte[] hexStringToByteArray(String hex) { int len = hex.length(); byte[] data = new byte[len / 2]; for (int i = 0; i < len; i += 2) { data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16)); } return data; } /** * 程序入口。 */ public static void main(String[] args) { // 替换为你的 credentials-config.json 文件的实际路径 String filePath = "C:\\Users\\YourUsername\\AppData\\Roaming\\DBeaverData\\workspace6\\General\\.dbeaver\\credentials-config.json"; // 对于 macOS/Linux: // String filePath = "/Users/YourUsername/.dbeaver4/credentials-config.json"; DBeaverCredentialDecryptor decryptor = new DBeaverCredentialDecryptor(filePath); try { decryptor.decryptAndPrint(); } catch (Exception e) { System.err.println("程序执行失败: "); e.printStackTrace(); } } }

4.3 代码关键点解读与自定义调整

  1. generateLocalKey()方法:这是解密的成败关键。示例中使用了用户名 + "DBeaver"作为种子进行 MD5 哈希。这只是一个常见模式,未必适用于所有情况。

    • 如果解密失败(输出乱码或报错),首先怀疑这里。你需要尝试不同的种子组合。例如:
      • userName + "DBeaver Community Edition"
      • userName + System.getProperty("user.home")(用户家目录路径)
      • 直接使用userName的 MD5。
      • 更复杂的情况,可能需要读取 DBeaver 配置文件中的某个特定值作为种子的一部分。
    • 调试方法:你可以在这个方法里打印出生成的seed字符串和keyBytes的十六进制表示,与正常工作的环境(如果你有的话)进行对比。
  2. Cipher.getInstance("AES/CBC/PKCS5Padding"):这指定了加密算法、模式和填充方式。绝大多数情况下 DBeaver 使用这个组合。如果遇到BadPaddingException等错误,可以尝试"AES/CBC/NoPadding",但需要自己处理填充字节。

  3. 密钥长度:示例生成的 MD5 密钥是 16 字节,对应 AES-128。如果 DBeaver 使用了 AES-256,则需要 32 字节的密钥。你可能需要更复杂的密钥派生函数(如 PBKDF2)或使用 SHA-256 哈希。

5. 运行、调试与常见问题排查

代码写好了,接下来就是运行和解决实际问题。

5.1 运行步骤

  1. 定位文件:首先,找到你电脑上准确的credentials-config.json文件路径。
  2. 修改路径:将main方法中的filePath变量替换为你的实际路径。
  3. 编译运行:使用 IDE(如 IntelliJ IDEA, Eclipse)或命令行 (javac,java) 编译并运行DBeaverCredentialDecryptor类。确保 classpath 包含了 Jackson 库的 jar 包。

如果一切顺利,你将看到控制台输出每个数据库连接的标识和其对应的明文密码。

5.2 常见问题与解决方案速查表

问题现象可能原因排查与解决思路
FileNotFoundException或路径错误credentials-config.json文件路径不正确。使用系统文件搜索功能确认文件位置。注意 DBeaver 可能有多个工作空间。
解密输出乱码(如åˆ˜æ˜¯å¯†ç 密钥生成错误。这是最常见的问题。1.检查种子:修改generateLocalKey()中的种子组合,多尝试几种可能。
2.检查算法:确认Cipher.getInstance的参数是否与加密时一致。尝试AES/ECB/PKCS5Padding(如果IV为空)。
3.版本差异:确认你的 DBeaver 版本,并尝试搜索该版本特定的解密方法。
抛出javax.crypto.BadPaddingException密钥、IV 或算法模式不匹配,导致解密后填充字节错误。1. 首先排除密钥错误(同乱码问题)。
2. 确认 IV 是否正确从十六进制字符串转换。
3. 尝试使用NoPadding并手动处理最后一块数据(高级)。
抛出java.security.InvalidKeyException密钥长度不合法。确认密钥长度。AES-128 需 16 字节,AES-256 需 32 字节。检查generateLocalKey返回的字节数组长度。
程序运行无输出或只输出部分条目JSON 结构不匹配或某些条目加密方式不同。1. 检查 Jackson 映射的字段名是否与你的 JSON 文件完全一致(注意大小写)。
2. 有些连接可能savePassword为 false,其secret可能为 null,代码需做空值判断。
3. 使用调试模式,查看解析后的CredentialsConfig对象内容。
找不到javax.crypto相关类运行环境 JRE 不完整或版本过低。确保使用标准 Oracle JDK/JRE 或 OpenJDK,而非仅 JRE。

踩坑记录:我在一次帮同事解密时,发现他的 DBeaver 是企业版,且安装在自定义目录。我的社区版通用密钥生成方法失效了。最后发现,企业版的种子包含了安装目录的绝对路径。解决方法是在generateLocalKey方法中,通过读取系统注册表(Windows)或特定配置文件,动态获取了 DBeaver 的安装路径,拼接后问题解决。所以,环境信息是密钥生成的核心

5.3 安全与伦理提醒

最后,必须再次强调本代码的用途和限制:

  • 仅限自用:此工具仅用于解密你自己电脑上、由你自己创建的 DBeaver 保存的密码。用于解密他人的凭证文件是非法且不道德的行为。
  • 密码安全:解密出的密码请妥善保管。可以考虑将解密代码中的打印语句改为将密码写入一个受密码保护的加密文件,而不是直接输出到控制台。
  • 理解风险:DBeaver 将密码加密存储本意是增加安全性,但本地加密的强度依赖于本地环境的复杂性。这意味着任何能访问你电脑磁盘并了解此方法的人,理论上都可能解密这些密码。对于极高安全要求的场景,建议使用 DBeaver 的“不保存密码”选项,或依赖操作系统的密钥管理设施(如 macOS Keychain, Windows Credential Manager)。

通过以上步骤,你应该能够成功找回遗忘在 DBeaver 中的数据库密码。这个过程不仅是一次技术实践,更是一次对软件安全存储机制的深入理解。当工具成为我们记忆的延伸时,了解其运作原理,就能在需要时掌握主动权。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询