Joplin 同步项序列化格式与端到端加密(E2EE)报文结构深度解析——基于 syncTargetSnapshots 加密快照实例
发布时间:2026/9/11 8:10:54 作者:尧图编辑部 阅读量:1,286
报文结构深度解析——基于 syncTargetSnapshots 加密快照实例)
Joplin 同步项序列化格式与端到端加密E2EE报文结构深度解析——基于 syncTargetSnapshots 加密快照实例【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin本文以 Joplin 仓库packages/app-cli/tests/support/syncTargetSnapshots/1/e2ee/目录下真实加密同步快照如49774b5e3e0e460aa70308a43c94cebc.md为样本逐字段拆解 Joplin 笔记在同步目标sync target中的序列化表示以及端到端加密E2EE启用后的密文信封格式JED010000头 AES-CCM 参数。读者将掌握 Joplin 同步项的存储布局、普通与加密快照的差异、主密钥Master Key项的生成规律并理解这些快照如何被用于同步测试与 Joplin Server 的PUT /api/items上传流程。一、快照文件在仓库中的定位与作用syncTargetSnapshots是 Joplin 测试体系中的同步目标快照目录存放经过真实同步流程产出的、某个 sync version 下某类同步目标的完整目录结构快照。其布局为packages/app-cli/tests/support/syncTargetSnapshots/ └── 1/ # sync version 1 ├── normal/ # 未启用 E2EE 的同步快照 │ ├── 0e8d296dbef34588b0de060630ad2582.md # 普通笔记 note2 │ ├── 36ad2ccddc2542a9a5c41a1fbde269b1.md # 笔记本 subFolder2 │ └── ... └── e2ee/ # 启用 E2EE 后的同步快照 ├── 49774b5e3e0e460aa70308a43c94cebc.md # 加密笔记 ├── c24138199f5b403fa3e9b8b4f22685c5.md # 主密钥项type_ 9 └── ...注意49774b5e3e0e460aa70308a43c94cebc.md这类文件并不是普通 Markdown 笔记而是 Joplin 内部以 Markdown 文件形式落盘的序列化同步项。文件名即该项的 ID正文头部是被序列化的属性字段Joplin note metadataJoplin 称之为 note metadata 格式这与普通用户笔记文件的标题第一行 正文格式是两套体系。快照中每个文件对应同步目标里存储的一个 item文件名即items.jop_id。快照的生成流程源码依据快照并非手工维护而是由 packages/lib/testing/syncTargetUtils.ts 中的main()程序化生成调用setupDatabaseAndSynchronizer(1)与switchClient(1)初始化测试数据库通过createTestData(testData)在本地按预定义的笔记树结构创建数据——该结构包含folder1/subFolder1、folder1/subFolder2/note1含附件 resource 与标签 tag1、note2、note3、folder2、folder3/note5等节点若目标是e2ee类型则先setEncryptionEnabled(true)并loadEncryptionMasterKey()随后执行synchronizerStart()与synchronizer().start()完成一次真实同步使本地数据加密后上传到同步目录syncDir最后把同步目录整体复制到snapshotBaseDir/{syncVersion}/{syncTargetType}即supportDir/syncTargetSnapshots/1/e2ee。由此可知e2ee 快照是启用加密 执行完整同步后同步目标侧的真实落盘形态是理解 Joplin 同步协议与加密传输格式的一手素材。测试中可通过deploySyncTargetSnapshot(syncTargetType, syncVersion)将快照复制回同步目录用于验证客户端能否正确拉取并还原数据。二、未加密快照normal 序列化格式基线要读懂加密快照先看同版本normal/目录下的明文形态。以普通笔记 0e8d296dbef34588b0de060630ad2582.md 为例note2 id: 0e8d296dbef34588b0de060630ad2582 parent_id: 36ad2ccddc2542a9a5c41a1fbde269b1 created_time: 2020-07-25T10:36:57.409Z updated_time: 2020-07-25T10:36:57.409Z is_conflict: 0 latitude: 0.00000000 longitude: 0.00000000 altitude: 0.0000 author: source_url: is_todo: 0 todo_due: 0 todo_completed: 0 source: joplin source_application: net.cozic.joplintest-cli application_data: order: 1595673417409 user_created_time: 2020-07-25T10:36:57.409Z user_updated_time: 2020-07-25T10:36:57.409Z encryption_cipher_text: encryption_applied: 0 markup_language: 1 is_shared: 0 type_: 1序列化格式的规则是首行为标题空行后为key: value形式的属性块。以笔记本项 36ad2ccddc2542a9a5c41a1fbde269b1.mdsubFolder2type_ 2为例其属性更精简但同样遵循title / id / created_time / ... / parent_id / is_shared / type_的顺序。各字段含义如下其中type_的类型码与 packages/lib/BaseModel.ts 中的ModelType映射一致字段说明id客户端生成的全局唯一 ID对应 Joplin Server 的items.jop_idparent_id父笔记本 ID用于维护笔记树层级created_time/updated_time服务器同步时认可的创建/更新时间戳user_created_time/user_updated_time用户实际操作产生的时间戳is_conflict是否为冲突笔记0/1latitude/longitude/altitude笔记地理位置author/source_url来源作者与原始 URLis_todo/todo_due/todo_completed待办标记与截止时间source/source_application数据来源与产生应用的标识order排序号毫秒时间戳encryption_cipher_text加密后的内容未加密时为空encryption_applied是否已应用加密0/1markup_languageMarkup 语言标识1 为 Markdownis_shared是否共享type_项类型码1Note2Folder4Resource9MasterKey 等三、加密快照逐字段剖析49774b5e3e0e460aa70308a43c94cebc.md这是本次分析的核心样本即启用 E2EE 后一条笔记在同步目标中的完整落盘内容id: 49774b5e3e0e460aa70308a43c94cebc mime: filename: created_time: updated_time: 2020-07-25T10:37:00.276Z user_created_time: user_updated_time: file_extension: encryption_cipher_text: JED0100002205c24138199f5b403fa3e9b8b4f22685c5000308{...} encryption_applied: 1 encryption_blob_encrypted: size: is_shared: type_: 4与 normal 快照对比可观察到几个关键差异encryption_applied: 1明确标记该条目的内容已被加密明文正文标题行 属性块消失正文与标题、parent_id、时间戳等属性被整体加密进了encryption_cipher_text因此快照只剩元数据骨架这正是加密后同步目标上无法看到任何笔记明文的直接证据也正是 Joplin 隐私保护privacy-focused的落点type_: 4该 ID 前缀49774b5e...对应测试数据testData中subFolder2/note1的附件resource。结合 packages/lib/testing/syncTargetUtils.ts 中createTestData对resource: true节点执行shim.attachFileToNote(note, supportDir/photo.jpg)可知type 4 即 Resource资源/附件类型mime、filename、file_extension、size字段正是资源文件本身属性的占位此处同样被清空说明已纳入加密上下文。说明加密快照目录中的笔记项与资源项均呈现元数据骨架 密文字段形态不同的只是保留的字段集合笔记保留时间戳类字段资源保留mime/filename/size等type_码用于区分二者。四、JED010000加密信封Joplin E2EE 的密文结构encryption_cipher_text的值以固定前缀JED010000开头这是 Joplin 加密数据的版本化格式头。拆解样本值JED010000 2205 c24138199f5b403fa3e9b8b4f22685c5 000308 {iv:...,v:1,iter:101,ks:128,ts:64, mode:ccm,adata:,cipher:aes, salt:O2duAuTVjV4,ct:...}JED010000Joplin Encrypted Data 版本标识2205元数据长度信息c24138199f5b403fa3e9b8b4f22685c5为 32 个十六进制字符正好 32 字节即 22050x089D 的二进制长度编码前的十六进制表示……实际为固定 32 字符的 16 进制长度头c24138199f5b403fa3e9b8b4f22685c5主密钥Master Key的 ID——加密这条笔记所用密钥的主键引用。注意它与 e2ee 目录下的主密钥项文件 c24138199f5b403fa3e9b8b4f22685c5.md 同名印证数据项通过主密钥 ID 关联其加密密钥的设计000308内部子结构标识末尾 JSON 为加密算法参数密文信封参数参数样本值含义v1加密格式版本iter101PBKDF2 迭代次数派生加密密钥ks128密钥长度bitts64GCM/CCM 认证标签长度bitmodeccmAES 分组模式CCM带认证adata关联数据AEAD 附加认证数据cipheraes底层密码算法saltO2duAuTVjV4PBKDF2 盐值Base64ivtYDqKe/kszjggCifj1KUAg初始化向量Base64cti/meCIxFgfziCMlirvSU4Bq/...密文Base64该信封结构与 packages/lib/services/e2ee/EncryptionService.ts 中encryption_method: EncryptionMethod.Custom及encryption_method字段的写入逻辑对应——Joplin 在加密时按{iv, v, iter, ks, ts, mode, adata, cipher, salt, ct}结构序列化参数解密时由decrypt(model.encryption_method, password, model.content)依据这些参数还原明文见同文件L322-L328对EncryptionMethod.SJCL2与 Custom 分支的处理。五、主密钥项type_ 9 的加密元数据e2ee 目录下还有一个特殊项 c24138199f5b403fa3e9b8b4f22685c5.mdid: c24138199f5b403fa3e9b8b4f22685c5 created_time: 2020-07-25T10:37:00.483Z updated_time: 2020-07-25T10:37:00.483Z source_application: net.cozic.joplintest-cli encryption_method: 4 checksum: content: {iv:qukPmj886S4Y8nyT9z/WFA,v:1,iter:10000,ks:256,ts:64, mode:ccm,adata:,cipher:aes,salt:FTTpwryRSrM,ct:...} type_: 9要点type_: 9对应ModelType.MasterKey见 packages/lib/BaseModel.ts即主密钥项与笔记/资源项不同主密钥的内容直接放在content字段而非encryption_cipher_text——因为它本身承载的是加密后的主密钥其明文只能由用户口令派生出的密钥解开注意参数差异主密钥项使用iter: 10000、ks: 256而数据项笔记/资源使用iter: 101、ks: 128。这体现了 Joplin 的分层密钥设计口令派生密钥KDF保护主密钥高强度 PBKDF2 迭代主密钥再派生数据加密密钥保护具体笔记内容。encryption_method: 4与 packages/lib/services/e2ee/types.ts 中的encryption_method?: number字段呼应是区分不同加密实现的版本号。六、快照如何被消费同步测试与 Joplin Server 上传测试侧packages/lib/testing/syncTargetUtils.ts 中的deploySyncTargetSnapshot(syncTargetType, syncVersion)将快照整体复制回同步目录const sourceDir ${snapshotBaseDir}/${syncVersion}/${syncTargetType}; await fs.remove(syncDir); await fs.copy(sourceDir, syncDir);其作用是在测试中快速预置一个已知状态的同步目标从而验证客户端在不同场景明文快照、加密快照下能否正确读取、解密并重建本地数据。加密快照配合checkTestData可断言解密后note1的附件、tag1关联等结构与testData完全一致。Server 侧readme/dev/spec/server_items.md 明确说明向 Joplin Server 上传条目即调用PUT /api/items携带的正是上述序列化 item服务端路由位于packages/server/src/routes/api/items.ts随后调用models.item().saveFromRawContent完成主体处理若是二进制资源resource内容原样存入数据库若是普通 item则反序列化并抽取parent_id、type_等属性单独落库jop_*字段如jop_id、jop_parent_id等其余部分重新序列化为 JSON 存入items.content字段客户端生成的 ID 记为items.jop_id不保证全局唯一服务端另有自增items.id内容字段可能按配置存于数据库items.content、S3 或文件系统因此所有内容读写必须经由ItemModel提供的工具函数。快照中的每个.md文件如49774b5e3e0e460aa70308a43c94cebc.md正是文档所述的serialized Joplin item 示例可作为理解saveFromRawContent输入格式的对照物。七、实践验证如何在本地复现一份加密快照如果想亲眼生成同构快照可在仓库packages/app-cli测试环境中按以下思路操作仓库为只读下述为运行/查看方式阅读 packages/lib/testing/syncTargetUtils.ts 的main(syncTargetType)入口其参数限定为normal或e2ee执行时传入e2ee程序会自动完成建库 → 建测试数据createTestData→ 启用加密并加载主密钥setEncryptionEnabled(true)loadEncryptionMasterKey()→ 触发完整同步 → 将同步目录复制到syncTargetSnapshots/1/e2ee生成完毕后对照normal/与e2ee/两个目录即可直观对比加密前后同步项形态差异并用第三节的表头验证JED010000信封参数。通过该流程可以确认快照不是虚构数据而是 Joplin 同步引擎在启用 E2EE 后真实产生的磁盘形态任何一次同步测试都可以复现。八、小结Joplin 同步项以标题 属性块的序列化文本落盘type_区分 Note1/Folder2/Resource4/MasterKey9等类型类型码定义见 packages/lib/BaseModel.ts启用 E2EE 后正文与关键属性整体加密进encryption_cipher_text以JED010000头 AES-CCM/PBKDF2 参数信封承载encryption_applied: 1标记加密状态主密钥项type_ 9承载口令派生的加密主密钥与数据项形成分层密钥体系解密逻辑可在 packages/lib/services/e2ee/EncryptionService.ts 中追踪这些快照由 packages/lib/testing/syncTargetUtils.ts 生成既用于同步兼容性测试也是 readme/dev/spec/server_items.md 所描述的 Joplin ServerPUT /api/items上传协议的输入样例。围绕packages/app-cli/tests/support/syncTargetSnapshots/1/e2ee/49774b5e3e0e460aa70308a43c94cebc.md这一加密快照样本本文完成了从同步项序列化格式到JED010000 加密信封再到主密钥分层设计与 Server 上传链路的完整解读可作为阅读 Joplin 同步与加密模块源码packages/lib/services/synchronizer/、packages/lib/services/e2ee/、packages/server/src/routes/api/items.ts的入门索引。【免费下载链接】joplinJoplin - the privacy-focused note taking app with sync capabilities for Windows, macOS, Linux, Android and iOS.项目地址: https://gitcode.com/GitHub_Trending/jo/joplin创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考