DBeaver密码找回:Java逆向解密credentials-config.json文件

📅 2026/7/5 9:39:39
DBeaver密码找回:Java逆向解密credentials-config.json文件
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这个文件名。在这个目录下你会找到两个关键文件>{ credentials: [ { properties: { targetId: mysql://rootlocalhost:3306, // 连接标识符 savePassword: true }, secret: { value: qjfDxLfQv1k4oEn5hMrWgw, // 加密后的密码密文 algorithm: dbeaver, iv: 0123456789abcdef } } ] }可以看到每个凭证条目包含了目标标识 (targetId) 和秘密信息 (secret)。secret对象中的value就是加密后的密码Base64编码格式algorithm指明了加密算法这里是 DBeaver 自定义的iv是初始化向量用于加密算法。2.2 加密原理与密钥来源DBeaver 使用的并非标准化的强加密算法如 AES-256-GCM而是一种基于用户本地环境生成的“伪随机”密钥进行的对称加密。其核心逻辑是密钥生成密钥并非一个固定的字符串而是在 DBeaver 首次运行时基于当前用户的系统用户名和DBeaver 安装路径等信息通过特定的算法如哈希、拼接、变换动态生成的一个密钥。这意味着同一台机器同一个用户密钥是固定的。不同机器或同一机器不同用户即使 DBeaver 版本相同生成的密钥也不同。直接将别人解密成功的代码和密钥拿来用在你的环境里是行不通的。加密过程当你在 DBeaver 中保存密码时它会用上述生成的密钥结合一个随机生成的初始化向量 (iv)对明文密码进行加密然后将密文 (value) 和iv一起保存到credentials-config.json中。解密过程我们的目标要解密我们必须在自己的 Java 代码中完全复现 DBeaver 在本机生成密钥的逻辑然后用这个密钥和文件中存储的iv对密文value进行解密从而得到明文密码。理解了这个原理我们就明白为什么网上找不到一个“万能解密器”。解密代码必须能够自适应地生成当前环境下的正确密钥。接下来我们就进入实战环节一步步构建这个解密工具。3. 实战环境准备与核心依赖我们的目标是写一个独立的、可运行的 Java 程序。为了简化处理 JSON 和 Base64 编解码我们会引入一个轻量级的库。3.1 项目初始化与依赖我推荐使用 Maven 来管理项目。创建一个新的 Maven 项目在pom.xml中添加以下依赖dependencies !-- 用于解析和操作 credentials-config.json 文件 -- dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-databind/artifactId version2.15.2/version !-- 建议使用较新稳定版 -- /dependency dependency groupIdcom.fasterxml.jackson.core/groupId artifactIdjackson-core/artifactId version2.15.2/version /dependency /dependenciesJackson 库能让我们非常方便地将 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 ListCredentialEntry credentials; // 标准的 Getter 和 Setter 方法 public ListCredentialEntry getCredentials() { return credentials; } public void setCredentials(ListCredentialEntry 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(i1), 16)); } return data; } } }创建好这些模型类我们就为读取和操作 JSON 数据打下了坚实的基础。接下来就是最核心的部分逆向 DBeaver 的密钥生成算法。4. 核心解密算法逆向与 Java 实现这是整个项目的灵魂所在。我们需要深入 DBeaver 的源代码它是开源的或者通过分析其行为来推断出密钥的生成方式。经过对多个版本 DBeaver 的分析其密钥生成的核心逻辑可以概括如下。4.1 密钥生成逻辑剖析DBeaver 的密钥生成器 (LocalSecurePreferences或类似组件) 通常会做以下几件事获取种子Seed组合一些本地唯一且相对稳定的信息例如当前操作系统的用户名System.getProperty(user.name)。DBeaver程序自身的安装或配置路径。有时可能还包括机器名或其他本地标识。 这些信息被拼接成一个字符串作为生成密钥的“种子”。生成密钥字节对这个种子字符串进行MD5哈希运算。MD5 会产生一个 128 位16 字节的哈希值。这个哈希值就被用作加密和解密的原始密钥材料。算法与模式使用对称加密算法早期版本可能直接使用简单的变换较新版本通常采用AES算法模式可能是CBC(密码分组链接模式)。这也是为什么secret对象中需要iv(初始化向量) 的原因。实操心得不同 DBeaver 版本如社区版 vs 企业版或大版本升级的密钥生成细节可能有微小差异。如果下面的通用方法失效你可能需要针对你的特定版本进行更精确的逆向。一个技巧是在 DBeaver 运行时尝试在credentials-config.json同目录下寻找日志文件或查看其关于安全存储的源代码。4.2 Java 解密代码完整实现基于以上分析我们可以编写一个通用的解密类。下面的DBeaverCredentialDecryptor类实现了整个流程DBeaverCredentialDecryptor.javaimport 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 代码关键点解读与自定义调整generateLocalKey()方法这是解密的成败关键。示例中使用了用户名 DBeaver作为种子进行 MD5 哈希。这只是一个常见模式未必适用于所有情况。如果解密失败输出乱码或报错首先怀疑这里。你需要尝试不同的种子组合。例如userName DBeaver Community EditionuserName System.getProperty(user.home)(用户家目录路径)直接使用userName的 MD5。更复杂的情况可能需要读取 DBeaver 配置文件中的某个特定值作为种子的一部分。调试方法你可以在这个方法里打印出生成的seed字符串和keyBytes的十六进制表示与正常工作的环境如果你有的话进行对比。Cipher.getInstance(AES/CBC/PKCS5Padding)这指定了加密算法、模式和填充方式。绝大多数情况下 DBeaver 使用这个组合。如果遇到BadPaddingException等错误可以尝试AES/CBC/NoPadding但需要自己处理填充字节。密钥长度示例生成的 MD5 密钥是 16 字节对应 AES-128。如果 DBeaver 使用了 AES-256则需要 32 字节的密钥。你可能需要更复杂的密钥派生函数如 PBKDF2或使用 SHA-256 哈希。5. 运行、调试与常见问题排查代码写好了接下来就是运行和解决实际问题。5.1 运行步骤定位文件首先找到你电脑上准确的credentials-config.json文件路径。修改路径将main方法中的filePath变量替换为你的实际路径。编译运行使用 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 中的数据库密码。这个过程不仅是一次技术实践更是一次对软件安全存储机制的深入理解。当工具成为我们记忆的延伸时了解其运作原理就能在需要时掌握主动权。