BoshConnect:从 ConnectBot 到全功能 SSH/SFTP 客户端的开发实战
基于开源项目 ConnectBot,用 Kotlin + Jetpack Compose 从零打造一款支持加密备份、云同步、GitHub 备份的 Android SSH/SFTP 客户端。本文记录整个开发过程中的架构设计、踩坑经验和技术决策。
为什么要做这个项目
作为一个经常管理多台 VPS 的开发者,我需要一个好用的 Android SSH 客户端。现有的方案各有问题:
- Termius:功能强大但收费,免费版限制多
- JuiceSSH:界面陈旧,Compose 时代的产品长那样说不过去
- ConnectBot:开源老牌,但 UI 还停留在 Android 5 时代
于是我决定:基于 ConnectBot 的 SSH 内核,用现代 Android 技术栈重新打造一个。
项目取名 BoshConnect,开源在 GitHub。
技术栈选型
| 层级 | 选择 | 理由 |
|---|---|---|
| 语言 | Kotlin | Android 官方推荐,类型安全 |
| UI | Jetpack Compose + Material 3 | 声明式 UI,开发效率高 |
| 架构 | MVVM + StateFlow | Google 推荐架构,响应式 |
| DI | Hilt | 标准 Android DI,与 Compose 集成好 |
| 数据库 | Room | 类型安全的 SQLite 封装 |
| SSH | ConnectBot sshlib | 成熟稳定的 SSH 实现 |
| SFTP | JSch | 老牌 Java SSH 库 |
| 加密 | PBKDF2 + AES-256-GCM | 业界标准,无需额外依赖 |
架构设计
整个项目分为四层:
UI Layer (Compose Screens + ViewModels)
↓
Domain Layer (SettingsManager · CloudSyncApi · CryptoManager)
↓
Data Layer (Room DB · ConnectBot Service · SSH Transport)
↓
Crypto Layer (SessionKeyHolder → FieldCrypto → AES-256-GCM)
关键决策:去掉登录页
最初版本有主密码登录页,用户需要输入密码才能进入。但经过实际使用发现:
- 手机本身有锁屏,App 再加一层密码是多余的安全感
- 多端同步时密码管理复杂,需要 salt 同步、密钥派生一致
- 用户反馈:每次打开都要输密码太麻烦
最终方案: - 设备密钥自动生成:随机 256-bit 密钥,持久化存储,用户无感知 - 备份密码独立:只在备份/恢复时使用,与设备密钥无关
// 自动生成设备密钥
val deviceKey = ByteArray(32).also { SecureRandom().nextBytes(it) }
cryptoManager.saveDeviceKey(deviceKey)
SessionKeyHolder.set(deviceKey)密钥体系设计
设备密钥 (自动生成) → 加密数据库字段 (密码/私钥)
备份密码 (用户设置) → 加密备份文件 + GitHub 备份
云同步密码 (服务器账号) → 加密云端数据
三个密钥完全独立,互不影响。这样做的好处:
- 本地数据自动加密,用户无需操作
- 备份文件用独立密码保护,跨设备恢复只需密码
- 云同步用服务器账号密码,天然支持多端
功能实现
1. SSH 终端
保留了 ConnectBot 的终端内核,但 UI 完全重写:
- 快捷键栏常驻(Ctrl/Esc/Tab/方向键/功能键)
- Compose 实现,支持深色模式
- 光标自动避让手机键盘
2. SFTP 文件管理
用 JSch 的 ChannelSftp 直接实现,不依赖
ConnectBot 的旧代码:
- 远程目录浏览(面包屑导航)
- 上传/下载(进度条 + 取消)
- 删除/重命名/权限修改
- Android 10+ 分区存储兼容
3. 加密备份
备份文件格式(BackupEnvelope):
{
"format": "boshconnect_backup_v1",
"encrypted": true,
"payload": "AES-GCM 加密的 JSON",
"salt": "base64 编码的 PBKDF2 salt",
"createdAt": 1743638400000
}关键设计:备份文件内嵌 salt。这样任何设备只要有备份密码,就能派生相同的密钥来解密。
4. GitHub 备份
原理很简单:
// 备份
PUT https://api.github.com/repos/{owner}/{repo}/contents/boshconnect/backup_20260403.enc
Authorization: token {PAT}
Body: { message, content: base64(encrypted_data) }
// 恢复
GET https://api.github.com/repos/{owner}/{repo}/contents/boshconnect/
→ 下载最新文件 → 输入备份密码 → 解密 → 写入本地优势:零服务器维护、GitHub 免费私有仓库、天然版本控制、安全性高。
5. 云同步(SbSSH Server)
自建 FastAPI 服务端,端到端加密:注册/登录 → JWT 认证 → 数据加密后上传,服务端只存密文。
踩坑记录
1. Kotlin 版本兼容
ConnectBot 的 termlib 库编译用了 Kotlin 2.3.0,但项目用的是 2.0.21。直接导致 metadata 版本不匹配。最终回退到 2.0.21,在 termlib 依赖上排除 Kotlin stdlib。
2. Google Maven TLS 握手失败
服务器通过代理访问 dl.google.com 时 TLS 握手失败。解决:所有 build.gradle 加阿里云 Maven 镜像。
3. 多端同步密钥不一致
早期版本每个设备生成自己的 salt,导致不同设备派生出不同的密钥。解决:登录时从服务器获取 salt,用服务器的 salt 派生密钥。
4. Android 14 图标显示问题
Adaptive Icon 的 XML 矢量图和 PNG 混合使用导致异常。最终用 Python PIL 生成 PNG,通过 layer-list drawable 包装。
总结
这个项目的核心价值:复用而不是重写(SSH 内核用 ConnectBot)+ 现代技术栈(Compose + Material 3)+ 安全优先(字段级加密、独立备份密码)+ 用户友好(去掉登录页、GitHub 备份零配置)。
本文由博客助手大龙虾整理。