[{"content":"和 Sun 公司开发的众多奇迹产品一样，Java 是二十一世纪以来第一个最伟大的语言。 自 2006 年 Sun 宣布将 Java 及 HotSpot 开源并形成 OpenJDK 项目以来，我们看到了无数的由各厂商和社区自定义的 OpenJDK 发行版和衍生版的诞生，甚至出现了几个 HotSpot 以外其他的虚拟机实现。\n因此，官方（Oracle）发行的 Java 并不是唯一的选择。 你可以在 whichjdk.com 上找到几款著名 OpenJDK 发行版的介绍（仅供参考，因为我不认可作者的部分观点:-）。\n作为指南，我将介绍几个重要概念，并且着重推荐几个你应该尝试的 OpenJDK 发行版。\n封面来自 whichjdk.com\n重要概念 Java 和 OpenJDK 有什么区别？ 简单来说：Java 是这门编程语言的名字，同时也是 Oracle 拥有的商标。 OpenJDK 是受官方支持的一款开源且自由的 Java 实现。\n因为 Java 及其著名的咖啡杯图标是 Oracle 拥有的商标，所以其他人无法在未经 Oracle 许可的情况下在自己的产品上使用 Java 这个名字。 因此，只有 Oracle 发布的官方的非开源 Oracle JDK 才会使用 Java 这个名字，而其他基于 OpenJDK 衍生的 Java 产品则通常使用 OpenJDK 的名字，并使用著名的尖头企鹅图标，尽管他们都是基于 Java 平台的软件。\nJVM、JRE、JDK、Java FX 分别是什么 简单来说：逐级包含关系。JVM 是程序执行的核心虚拟机，JRE 是在 JVM 之外包含 Java 平台运行库及标准库，JDK 是在 JRE 之外包含开发工具和编译器。\nJVM 通常不单独分发，因此最小可用的 Java 发行形式是 JRE。 实际上，非开发者用户只需安装 JRE 即可，并且省下 70% 的空间，不过对于当代计算机来说，这些空间并不重要。\n更详细的介绍可参见： https://www.geeksforgeeks.org/java/differences-jdk-jre-jvm/\nJava FX 是 Java 最新一代的跨平台 UI 框架，支持现代的前端开发工作流以及硬件加速图形渲染。部分基于 Java 的现代桌面应用基于其实现（如 HMCL），并且也要求运行环境安装。 类似于 OpenJDK，开源的 Java FX 对应项目是 OpenJFX。 与先前两代技术 AWT 和 Swing 不同，Java FX 不包含在 Java 标准库中，当前也通常不随 Java 发行版一同分发。 因此，Java FX 有时需要手动安装，或者也可以下载内置 Java FX 的 Java 发行版。\n我需要哪些 Java 大版本？ 对于 99% 的使用情况，你只需要安装 Java 的 LTS 版本。这些版本更通用，并且更稳定。\n这意味着，你需要的版本最多可能是：Java 8、11、17、21、25。\n旧版（小于等于 1.12.2）的 Minecraft 可能只能在 Java 8 上运行。1.16 及以后的 Minecraft 要求 Java 17 及以上。1.21 及以后的 Minecraft 要求 Java 21 及以上。\n然而，经过我在部分整合包上的测试，要求 Java 17 的 Minecraft 版本通常也在 Java 21 上正常运行。因此我只在电脑上安装了 Java 8 和 Java 21。\n截至发稿时，Java 25 还未发布，因此暂不讨论。后续我可能会转到 Java 25，并不再使用 Java 21。更新总是更好。\n我需要更新 Java 小版本吗？ 简单来说：要。并且很重要。\nJava 的大版本主要包含功能更新，包括最新的语言特性和虚拟机特性。小版本主要包含安全性补丁和性能改进，未及时更新这些安全补丁可能会为计算机带来风险。 比如旧版的 Java 8 就不支持 TLS 1.3 协议，对 HTTPS 性能和安全性造成实际影响。\nJava 版本号格式在历史上变更过很多次，当前我们需要使用的有两种。一种是 Java 8 在使用的风格，一种是最新风格。\n对于 Java 8：\n使用 update 版本号。比如 Java 8, Update 51，即 Java 8 的第 51 个小版本更新，有时也写作 8u51 或 1.8.0_51。8u51 是一个 2015 年发布的很旧的版本，我们将在下文再次提及它。\n较新的版本有时 update 版本号是三位数，如 211。211 并不代表第 211 个小版本更新，而是第 21 个小版本更新的 1 号更新。第三位数字通常具有三个变种，分别为以下含义：\n关键安全更新（PSU） - 经过测试的安全性更新。 修正包更新（PSU） - 功能性更新/调整。 紧急更新 - 通常为修补前两类更新的缺陷而紧急更新。 然而，由于他们的版本号约定极其混乱，有时难以分辨😅。总之数字越大的越新（\n对于最新的 Java：使用语义化版本号。比如 Java 21.0.8。\n想要同时使用多种 OpenJDK 发行版？ 尝试使用 SDKMAN!。\n推荐的 Java 发行版 Azul Zing (Azul Platform Prime) Azul 是最著名的专业自定义 OpenJDK 厂商之一，一直是 Oracle 产品的有力竞争对手。其中，Azul Zing 是他们的王牌产品。 这个 OpenJDK 分支提供了他们自研的基于 LLVM 的 Falcon JIT 编译器、无停顿的 C4 垃圾收集器以及基于 JIT 缓存和 profile 引导的 ReadyNow 高速冷启动解决方案。\nAzul Zing 是目前我最偏好的 OpenJDK，并且已经在我的服务器上运行过多个整合包周目，性能优异，暂未遇到问题。 缺点是目前只支持 Linux，并且是一个商业解决方案，仅限其流式构建对“评估和开发用途”免费。 因此，如果你是模组开发者或者私有服务器运维，可以考虑使用这个 OpenJDK。如果你正在运营一个商业化服务器，请不要使用它以避免潜在的法律问题。\n你可以在其官网上下载 Azul Zing，也可以通过他们官方的软件包仓库及指引在各 Linux 发行版上使用包管理器安装。\n安装过程很简单，但是你可能需要添加额外的 JVM 参数才能发挥其全部功能。具体请参阅文档。\n它是高度自定义的 JDK。\nAzul Zulu (Azul Platform Core) Azul Zulu 是 Azul 的免费生产就绪级 OpenJDK，具有稳定的更新支持、Java FX选项和可视化安装包的下载选项。 因此，Azul Zulu 对于普通用户来说安装和更新体验都十分友好，是官方 Java 的最佳替代品。你可以在下面的页面找到其下载链接：\nhttps://www.azul.com/downloads/?package=jdk-fx#zulu\nWindows 用户下载时请先将鼠标移至“Download”按钮，然后点选下拉框里的“.msi”，即可视化安装包。 推荐的 Java 版本见上方介绍。 建议下载 JDK FX，因为 FX 意为附带 Java FX，但是他们部分 JRE FX 不提供 msi 安装包😅，故作此推荐。\n不过 Azul Zulu 虽然几乎是 OpenJDK 的源码，其源代码并不直接公开，这是其一缺点。 然而，由于它是基于 GPLv2 代码的项目，因此你可以通过电子邮件的方式向 Azul 申请获得其源代码的下载链接。\nGraalVM 由于 Oracle 宣布 GraalVM 将专注于 Java 以外的语言（Python、JS）并不再发布新的 GraalVM for JDK，GraalVM 作为 Java 发行版已正式坠机🤡。 详见官方通知。\n在此之前，Oracle 已将 GraalVM CE Java 代码捐赠给 OpenJDK，因此部分优化已经或即将加入 OpenJDK。\n该节原文可能不再具有参考价值、过期并已折叠，展开以阅读：\n既然 Azul 有这么杀手锏的产品，作为 Java 本家的 Oracle 自然也有好东西应敌。 Oracle GraalVM 是一个使用即时 (JIT) 编译器加速 Java 和 JVM 应用性能的高性能 JDK。它能够降低应用延迟，通过缩短垃圾回收时间提高峰值吞吐量。它同时支持 Linux、Windows、macOS 等多平台。\n基于闭源 Oracle JDK 的 Oracle GraaVM 可在其官网下载： https://www.graalvm.org/downloads/\n他们还有一个开源的 GraalVM Community Edition，不过其性能会比 Oracle 版稍弱一些。\nGraalVM 相比普通的 OpenJDK 来说，拥有一个重写的 JIT 内核。这个 JIT 实现使用 Java 编写并实现自举，是对世纪初更新至今的基于 C++ 编写的 HotSpot Server Mode C2 JIT 的一个替换。 Graal JIT 的表现迅速追平了 C2，并且在现代 Java 语法、激进优化策略方面的支持更好。\nGraal JIT 同时也被反向引入了 OpenJDK 中，但目前属于实验性 VM 功能，且需要附带 Graal Compiler 的 OpenJDK 发行版才能使用。 在受支持的发行版上添加下列参数即可切换到 Graal JIT：-XX:+UnlockExperimentalVMOptions -XX:+UseGraalJIT\n此外，GraalVM Native Image 可提前 (AOT) 编译 Java 字节码，生成可近乎瞬时启动且仅占用极少内存资源的本机可执行文件。 Java 的 AOT 编译并不是什么新概念，且至少已有20年研究历史，世界上运行最广的 Java AOT 编译器就是 Android Runtime，在数十亿生产环境设备已部署十余年之久。 在我来看，这是 Java 在云原生时代的一次补票尝试，然而对于游戏来说，启动时间并不重要，而且 Minecraft 也不能直接使用 GraalVM 进行 AOT 编译，所以我们暂时略过这一点。\n它是高度自定义的 JDK。\nAdoptium Temurin Eclipse Adoptium 是 Eclipse 基金会旗下的顶级项目，先前是 AdoptOpenJDK。一直以来，他们都在提供基于社区的开源生产就绪级 OpenJDK 构建，具有多项认证。它也曾是我的第一选择。\n当你想要一个最标准的 OpenJDK 的时候，你需要的就是 Adoptium Temurin。它一度是 OpenJDK 发行版的代名词。\n推荐的 Java 版本见上方介绍。你可以在这里找到其下载链接：\nhttps://adoptium.net/temurin/releases\n其它 OpenJDK 一些具有亮点的 OpenJDK：\nAmazon Corretto: 声称具有综合性能和安全性提升。使用了他们自己的密码学库强化加密表现。 Alibaba Dragonwell: 阿里巴巴基于其内部部署的 AJDK 的优化内容开发的开源 OpenJDK。 IBM Semeru Runtime: 即 Eclipse OpenJ9，曾经也在 AdoptOpenJDK 提供。OpenJ9 相比 HotSpot 拥有更优的内存优化，但性能往往不如后者。它是高度自定义的 JDK。 值得一提的是，官方 Minecraft 启动器默认下载的高版本 Java 是 Microsoft Build of OpenJDK。这是一个由 Microsoft 提供的 OpenJDK 构建，猜测是 Microsoft 为了自己的云服务配套以及避免纠纷而提供的服务，没有什么亮点。\n对于大部分 OpenJDK 发行版来说（上面描述为“高度自定义的 JDK”的除外），他们几乎就是上游 OpenJDK 的代码，虽然说有“性能提升”，但通常来说不会相差太大，对于实际游戏的影响更是无需抱太大期望。 因此，选择一个易于安装/更新、功能完整、资质充足的 OpenJDK 发行版可能会更好更重要。\n指北：一些需要立刻注意的事情 应尽量及时更新你的 Java 稍微留心 Java 版本的更新。新的 Java 带来的性能与安全性更新通常对你有益。 计算机软件从来都不只是能用就行。\n8u51：一个 Mojang 留下的坑 官方 Minecraft 启动器默认会为旧版本 Minecraft 下载 Oracle Java 8u51。这一行为也记录在 Minecraft Wiki。\n如上文提到，8u51 是一个相当老的版本，缺失了许多性能和安全更新。 而 Mojang 这么做，可能部分是源于自 8u211 时引入的 Oracle Java SE 许可证更新。\n然而，你作为在这些商业相关许可证范围之外的个人，不必困在这样的大坑里。 因此，跟随上面的介绍，下载并使用一个最新的 OpenJDK 吧~\nTrust Store 老旧导致 HTTPS 连接失败 2025年10月初，Mojang 验证基础设施的网站证书进行了例行更换。这次更换后的新证书需要 TLS 客户端信任 DigiCert Global Root G2 根证书才能完成信任，而老旧的 Java 发行版中（据报道是 8u91 之前的版本）所携带的 Trust Store 中尚未包含该根证书。\n这导致了 Java 8u51 等老版本 Java 用户无法与玩家验证相关接口 sessionserver.mojang.com 建立 HTTPS 连接，致使玩家无法加入多人游戏。上面提到的问题仅在该文首发一个月不到之后就产生了如此严重的服务失败问题\u0026hellip;\n网上的缓解方案多种多样，然而真正的解决方法只有一个——更新你的 Java 版本。保持更新到最新的 Java 小版本，即可修复新证书的信任问题，同时获得性能提升。\n彩蛋 Minecraft 当年爆火，以至于 java.com 上有对 Minecraft 的介绍，尽管只有一句话： https://www.java.com/zh-cn/download/help/minecraft.html\n","date":"2025-09-08T00:00:00Z","image":"https://launium.com/p/2025-09-choose-a-java-distro-for-minecraft/cover_hu_8e574e4fc8289a46.webp","permalink":"https://launium.com/p/2025-09-choose-a-java-distro-for-minecraft/","title":"指南：为 Minecraft 选择一个合适的 Java 发行版"},{"content":"Minecraft Forge 一直以来有一个特殊的行为，他们可能会修改 Minecraft 握手包中的主机名部分。 他们用这个标记来识别一个客户端是否安装了 Forge，因此 Forge 服务器可以在服务器列表上就能向非 Forge 客户端提供警告。这也许是他们想到的实现此功能的唯一方法。\n然而，这样的行为实际上是自行约定的，因为 Mojang 没有定过相关标准。Forge 这样写是因为它能正常工作，且兼容官方服务器（因为官方实现也不对此项进行合法性检查），仅此而已。 由于 Forge 的热门度，所有服务器实现或代理都需要兼容这一协议方言。我知道在技术上讲这并不困难，但不影响它实际上是一团糟的事实。\n截至公元2024年11月5日，wiki.vg 只在其页面介绍了 FML 协议的两个版本。实际上在 FML2 协议后，Forge 还因为各种包括技术层面和非技术层面的原因进行了版本迭代。 而每次迭代后，主机名末尾的标记都会变化。因此，我将记录这些变化。本文会在未来更新以跟进版本。\nFML 标记迭代表 游戏版本 Forge 分支 FML 标记 代码路径 [1.7.x, 1.12.2] Official \\0FML\\0 patches/minecraft/net/minecraft/network/handshake/client/C00Handshake.java.patch [1.13.x, 1.17.x] \\0FML2\\0 src/main/java/net/minecraftforge/fml/network/FMLNetworkConstants.java或 src/main/java/net/minecraftforge/fmllegacy/network/FMLNetworkConstants.java [1.18.x, 1.20.1] \\0FML3\\0 src/main/java/net/minecraftforge/network/NetworkConstants.java [1.20.4, +∞) LexForge \\0FORGE src/main/java/net/minecraftforge/network/NetworkContext.java NeoForge - - 需要注意的是，NeoForge 正式版自游戏版本 1.20.4 开始重构，删除了主机名标记的机制。\n由于 Forge 在 1.20.1 前后经历了分叉，可能为了显示与 FML 作者 cpw 的彻底切割，官方 Forge（或称 LexForge）换掉了 FML 标记，改为 FORGE，并且删去了末尾的一个 \\0。\n简单的处理办法 由于这些标记均以 \\0 开头，并且 \\0 不会出现在合法的主机名中，所以可以直接通过寻找索引的方法分割字符串。\n事实上，Forge 本身和 Velocity 也都使用这种方法。ZBProxy 因此也采用此方法实现。\n其它现代加载器没有这样的标记 目前为止，Fabric 及其下游加载器没有引入这样的特殊标记。目前不清楚其他模组加载器（包括远古版本的 Forge 和其他加载器）是否引入了类似的标记。\n","date":"2024-11-04T00:00:00Z","image":"https://launium.com/p/forge-network-marker/cover_hu_917bd0560e4416d4.webp","permalink":"https://launium.com/p/forge-network-marker/","title":"关于 Forge 使用过的各种主机名标记"},{"content":"ZBProxy 自 2021 年 6 月 20 日第一次提交以来，至今已走过超过三个年头。ZBProxy 的发展见证了我个人技术的成长，这三年来我们也一直坚持更新，不断将它打造为更专业的加速IP最佳选择。\n经过接近一个月的准备，我非常高兴地宣布即将到来的 ZBProxy 3.1 版本。它将是一个大更新，也是一个小更新。大，是因为我完全重写了所有代码，也引入了新的软件结构；小，是因为它近乎完全兼容旧版本的配置，依旧支持一键部署。我也想借此吐槽一些 ZBProxy 项目建项以来的槽点，尽管不少已经在我的个人群内被我提前讨论过了。（笑）\n大更新？ 是的，它是。\n我们引入的更改包括如下：\n重构整个代码，引入基于规则的分流机制 全新的日志格式，支持日志分级 代码趋近于模块化 支持以注册的方法添加自定义功能 迁移文档至 mkdocs ，归档旧的 GitBook 文档 恢复 Release 发布进程。我们将用 Release 发布稳定版本，并继续用 Action 发布开发版本临时构建。 这几乎完成了我最初在群内公布的计划。\n超过三千行代码的重写，每一行都是对新代码结构的思考。引入的新结构能解决一直以来的痛点，比如：\n重载部分的代码晦涩难懂。每次更改都有可能产生新的 bug，并且一些 bug 难以得到解决（比如 #103） 不能做到单入口多出口。以往一个服务只能转发到一个目标，端口无法复用。这难以通过简单更改配置或优化代码做到。 所以我重写了代码，然后把它变得模块化，在原有的基础上设计了配置文件新的部分。现在的 ZBProxy 不仅是一个程序，也是一个 Go 库，可以被其它项目调用。整个 ZBProxy 使用一个结构体管理，没有全局变量，使用 Go context 控制，支持注册自定义规则/嗅探，能满足你的所有想象。你也可以只调用 ZBProxy 的部分功能，以及一些 common 包，减少代码量和重复劳动。\n我还提供了官方的扩展示例： https://github.com/layou233/ZBProxy-plugin-examples\n目前编写了一个公会专属的加速 IP ，使用 ZBProxy 作为依赖，而没有修改任何一行 ZBProxy 本体的代码。 设计较为简陋，是因为我只是拿这个作为一个演示，证明了 ZBProxy 的潜力可以完全替代以前需要魔改才能做到的事情。如果有兴趣，你可以进一步开发，做到你想要的任何功能并部署到生产环境上。\n对于 MC 开发者来说，不需要编写任何一行有关 Minecraft 协议数据解析的代码，因为所有相关明文信息都已经被预读取好并存入 metadata 元数据中，只需访问即可。（需要嗅探）\n更多详见文档的原理——开发页，我会进一步阐述如何使用以及其优点和限制。\n小更新。 关于新设计和旧版本的兼容性，实际上我想了一段时间。最后在模块化之后，这一问题迎刃而解。\n我们保留 Service 的一些旧设计，比如内置的 Minecraft 功能。我们将这些旧功能成为“旧字段”（legacy field）。而使用旧字段的服务成为“旧服务”（legacy service）。\n在识别为旧服务后，ZBProxy 会自动为旧服务生成一个对应的 outbound。实际上，旧服务是新服务和出站的捆绑，能完美兼容以前的功能和配置。 旧服务监听接受到的连接，不会进入路由，而是直接进行对应嗅探后进入内置的出站，与以前的做法效果相同。\n对旧服务的支持恰恰是 ZBProxy 的程序优势。虽然称为“旧”，但我个人中短期内没有计划将其删除，因为它配置起来简单，能满足一些简单的需求，并且实现容易，并不会打乱代码结构。 因此虽然不刻意鼓励，但我依然支持你使用旧服务，尽管我可能只会为旧服务提供有限的功能性更新。\n因此，这也是一个小更新。没有东西被破坏，一切如初。这很好，不是吗？\n一些问题 以下部分摘选自我在群里的发言（\nQ1: ZBProxy 1 和 2 是什么？ A1: ZBProxy 1 是最初的版本，仅实现了基础功能（恰好能跑的 bug）。ZBProxy 2 是对屎山的修改（更大的屎山）。其实 2 并不是一个非常大的升级，只是 1 的微不足道的性能优化，现在我更趋向于把它划为 1.1 或者 1.0.1 ，可是早在两年前就发布了，我也改不了啦。\nQ2: 为什么 3 这么久才发布？ A2: 一是时间因素，二是我希望它足够稳定，三是仍有一些最初设计的功能迟迟没有实现（现在已放弃）。总的来说，前两个问题都是因为 ZBProxy 早期 versioning 不成熟导致。\nQ3: ZBProxy 的 mcprotocol 包适合什么样的使用场景 A3: 适用于性能需求高的开发场景，因为它能实现缓冲区复用、向量化IO（vectorized IO）等十分需要技巧的优化。目前它不是一个完整的 MC 协议框架，没有加密、压缩等支持，只能处理简单的数据包（这对于 ZBProxy 来说足够了），但它确实拥有最佳性能，相比 go-mc 有许多可取之处。\n目前不建议使用它用于对 Minecraft 协议的完整处理，除非你需要性能。go-mc 的接口在不经过修改的前提下可以进行的优化很少，但你也可以自行修改它以提升其性能。\n有人说使用它写出来的代码难以阅读，实际上只是我过于追求优化而把代码写的比较魔幻而已（笑）。实际上它写起来也可以是整齐、优雅的。\nQ4: ZBProxy 3.1 的代码量比以前明显变多了，看不懂！ A4: 这显然是一个伪命题。代码模块化之后，你需要作出一个修改所需要阅读的代码量是减少的，因为你只需要阅读那一个模块的代码，甚至无需通读全文。\n你认为它变得更难理解，是因为你还没有开始阅读它。当你有需要的时候，你会花费精力去理解，而当需要变得没那么重要的时候，你放弃思考，而这是困难的根源。\n","date":"2024-07-19T00:00:00Z","image":"https://launium.com/p/zbproxy-3-1-prerelease/cover_hu_7011ed4b6b3f5a32.webp","permalink":"https://launium.com/p/zbproxy-3-1-prerelease/","title":"ZBProxy 3.1 预发布随感"},{"content":"创建了几个较低金额的赞助项，也可以随意金额赞助。然而赞助了也不会有特别的东西，也许如果你需要帮助或者想催更我的博客，可以用这个来驱使我哦 orz。\n刚注册的号，未认证，打算用这篇文章去认证一下。（\n我是 GitHub@layou233 ，是 ZBProxy、MCPingForAndroid 等项目的作者。本站是我目前唯一的个人网站，用于分享技术和个人动态。\n我的爱发电主页： https://afdian.net/a/launium\n","date":"2024-06-14T00:00:00Z","image":"https://launium.com/p/my-afdian/cover_hu_7bce6405a519c3f9.webp","permalink":"https://launium.com/p/my-afdian/","title":"创建了一个爱发电账号"},{"content":"tl;dr: 下载链接在文末。\n第一次尝试制作了 EPUB 2.0 图文轻小说。使用的是官网下载的 Sigil 当前最新版 2.0.2，跟着 ZC 大佬的视频教程开倍速一步步走很快就做出来了。顺便分享一下 ZC 大佬的视频教程： https://www.bilibili.com/video/BV1CW411r7er/?p=2 。只看这一集就能做出来成色不错的 EPUB。\n使用 Sigil 时最大的感受就是重度依赖正则表达式。从纯文本到 HTML 格式的转换，如果不用正则真的不知道怎么办……不过似乎一些大量重复的工作都可以使用一套万能预设正则表达式，日后我如果继续制作 EPUB 可能会整理然后分享。\n有一点想吐槽的是，Sigil 声称 \u0026ldquo;Full UTF-16 support\u0026rdquo; 结果只是支持导入 UTF-16 的 EPUB 文件（然而这只是作为 XML 解析器的一个基本要求！），保存的 EPUB 一律自动转换为 UTF-8。官方说 UTF-16 大多数情况下没有提升，只是对 CJK 字符的空间略有节省。然而他不考虑一下我们东亚 CJK 用户的感受吗……懒得做 UTF-8 转 UTF-16 的程序，所以文件稍微大一点，不过也不用过意不去啦。另外神奇的是，这个 EPUB 文件通过邮件发送到我的 kindle 后，亚马逊自动帮我把文件大小转换为了 2.8 MB，小了一倍有余，也许就是在字符编码方面做了转换，或者调整了压缩算法……\n本书实在是太太太太太冷门了，我在网上没有找到 EPUB 的分享版本，故在此将拙作放出，各位可以随意下载。请尊重原作、译者、校对和我的劳动哦。QwQ\n本系列共两卷，EPUB 皆已制作完毕，下载链接以及摘要值都在文末，请自取。\n原作者： 赤月ヤモリ 插画： kr 木\n以下是本书第一卷的简介：\n我的初恋对象，古贺胡桃。\n因不像高中生的美貌和性格引起了嫉妒而被孤立了，然后——在她因饱受欺凌差点从屋顶上跳下去之际，我能做的就是——\n「和我做爱吧！」\n「……变态……」\n「也许吧！但是这样好吗？如果死在这里，你人生最后一幕就是“来自变态的告白搭讪”了，这会破坏气氛吧！」\n「……你、你对我做了什么！真是个变态！」\n「这是有原因的。对，因为我是世界上最爱你的！我们结婚吧！！！」\n「诶、诶～～…………？？」\n拼命地呼喊爱，在这个世界上创造你的容身之处！\n由恋爱喜剧史上最笨又直率的主角\u0026amp;沉默易害羞的冷酷JK上演的100%糖度的恋爱喜剧开幕！\n第二卷简介：\n笨蛋情侣二人的热恋之火，通过修学旅行后，燃烧得更加猛烈！\n解决了欺凌问题的贵一和胡桃正式成为了恋人。\n这样的二人，即将迎来青春中屈指可数的大型活动之一，修学旅行！京都的寺社佛阁，久违的游乐园与情绪高涨的胡桃。交往后二人的感情，在旅途中逐渐升温……？！\n但是，胡桃桑的表情有点阴沉，看起来就像，不得不在京都与某个人见面一般……\n直男主人公×高冷美少女的甜蜜青春恋爱喜剧，第二弹！\n===第一卷下载=== sha256sum: 80934459300f05cb0f44f5b279d6f2d1739fde76281057aaee9295b5529cc69b\n===第二卷下载=== sha256sum: d283220c57df1a1b4a865cef6c58e734ac5d0a6fb31f83cd85116e2cb5b4d1ce\n","date":"2024-02-03T00:00:00Z","image":"https://launium.com/p/xxxshiyou-epub/cover_hu_9679079584c8a807.jpg","permalink":"https://launium.com/p/xxxshiyou-epub/","title":"[EPUB] 试着向准备跳下去的同班同学提议「和我XX吧」 简体中文 赤月ヤモリ"},{"content":"Minecraft 在多人游戏中使用混合模式加密（非对称+对称加密）已经十余年了，而这套加密算法在这十余年间本质上从未有过更改，只是数据包格式略有变动。wiki.vg 上对这一加密算法有非常详细的介绍，两年前我开始研究 Minecraft 多人游戏协议时借助 wiki.vg 和 Fnardn 的帮助才粗略地将其理解下来，而后的两年里我在密码学应用领域陆陆续续经历了不少实践，对该协议有了更多的看法，遂意动写下此文。后续可能会对此文进行进一步纠正，还请各路大神在评论区斧正。\n协议概要 以下内容摘自 wiki.vg:\nThe login process is as follows:\nC→S: Handshake with Next State set to 2 (login)\nC→S: Login Start\nS→C: Encryption Request\nClient auth\nC→S: Encryption Response\nServer auth, both enable encryption\nS→C: Set Compression (optional)\nS→C: Login Success\nC→S: Login Acknowledged\n客户端在向服务端发送自己的游戏名（IGN）后，如果服务器启用了正版验证，会立刻向客户端发送加密请求。这个请求通常包含一个服务器 ID，一个以 ASN.1 DER 格式编码的 1024bit RSA 公钥，以及一个随机生成的验证口令。服务器 ID 和验证口令用于双方向 Mojang 服务器进行身份验证时确保会话的唯一性，防止中间人攻击。RSA 公钥则用于进行密钥封装（KEM），以交换一个 AES-128-CFB8 密钥。\n由于与 Mojang 验证服务器的通信基于 HTTPS，故整个加密算法实际上由承载 HTTPS 的 TLSv1.2 或 TLSv1.3 加密通信协议提供信任层面的支持。由于 TLSv1.2 及以上的版本通常认为是密码学安全的，故整个 Minecraft 加密模型理论上没有太大的信任问题。然而，由于此算法发明于十余年前，许多被选用的密码算法都存在破绽，导致加密模型本身存在不安全因素。\n总览全算法，使用到的密码学标准算法有：RSA （PKCS1v1.5 加密，1024位或以上）、AES （128位长度，CFB8 流密码)、SHA-1\n重要因素 RSA 长度过短 wiki.vg 在介绍中强调了以下几句话：\nThe server generates a 1024-bit RSA keypair on startup.\nIt is also possible for a modified or custom server to use a longer RSA key, without breaking official clients.\n服务器在启动时生成一对 1024bit RSA 密钥。这样的设计可能出于两个原因：\nRSA 密钥生成速度很慢。在不同的 CPU 上，一对 1024bit RSA 密钥的生成需要花费5秒到50秒不等。如果是 2048bit RSA 密钥，则需要10秒以上的时间。对于有大量玩家同时连入的情况，这样的开销显然是不切实际的。 在当时（2012），1024bit RSA 的使用仍未被视为不安全。 即使 NIST 已于 2011 年发表文章宣布在 2011 年开始 1024bit RSA 应弃用，且在 December 31, 2013 之后完全停止使用（参考 NIST Special Publication 800-131A），但 Mojang 依然这样做了。\n不过值得庆幸的是，ASN.1 DER 封装格式的使用使其同时能兼容 2048bit 的 RSA 密钥，并且不会破坏各版本的客户端兼容。故升级到 2048bit RSA 密钥实际上没有太大阻力，只是可能出于上面第一条因素的考虑，这方面的更改依旧没有被实施。据实测，Hypixel 依旧使用 1024bit RSA 密钥，但是与 wiki.vg 描述不同的是，每次连接 Hypixel 收到的公钥并不相同。他们可能为每个连接生成了不同的密钥，或者准备了一个密钥池以轮换密钥。\n除 1024bit RSA 已被认为不安全以外，实际上目前大部分非对称加密算法都被认为容易受到量子攻击。NIST 认为当前互联网上的绝大多数流量在数十年后都能被解密，无论是 RSA，NIST ECC 曲线还是 25519 曲线都难逃量子攻击。因此，可以肯定的是，这一协议会在十年内被更新。\nRSA PKCS#1 v1.5 加密不安全？ 在 KEM 过程中，该协议使用 RSA PKCS#1 v1.5 定义的方法对通讯密钥进行加密传递。然而，这又是一个被认为不怎么安全的加密算法。在 Go 官方 RSA 标准库的文档中，就有这样一个贴心的提示：\nWARNING: use of this function to encrypt plaintexts other than session keys is dangerous. Use RSA OAEP in new protocols.\n此算法用于加密除会话密钥意外的内容都是危险的，如果这样做，则考虑选用 RSA OAEP 加密算法——一个复杂但是不容易导致错误使用的填充非对称加密算法。然而，Minecraft 确实使用 PKCS# v1.5 用于加密通讯密钥，以及验证口令，且这两者都是恒定长度。\nRSA PKCS#1 v1.5 并非不安全，只是很容易因为错误的使用或者应用设计而导致不安全。换句话说，取决于使用者。目前来说，我认为 Minecraft 此处的使用是安全的，但仍缺少密码学论证。\nAES-128-CFB8 相对于非对称加密体系，对称加密体系的坑显然就没有这么多且严重。AES 暂时被认为未受到严重量子威胁，并且 128 位的 AES 密钥是安全的（Cloudflare CDN 还将 AES-128-GCM 的优先级设置高于 AES-256-GCM）。AES 是分组密码（block cipher，也称块密码），为了方便在程序中使用，通常配合一种分组密码操作模式算法将其转换为流密码（stream cipher）或者 AEAD 密码。由于当时（2012） AEAD 尚未兴起，Mojang 选择 CFB8 将其作为流密码也是情有可原，甚至还能体现出他们的谨慎。\nCFB8 是CFB 分组密码操作模式的一个变种，在维基上有较为详细的介绍。CFB 和 CTR 是最受欢迎的两种能将块密码转换为流密码的算法，相较于 CTR 在并行加解密方面的极大优势（速度极快），CFB 则提供错误传播（error propagation）方面的优势。在遭遇到部分密文在传输过程中被篡改的情况时，CFB 会使一个块长度之后的所有解密操作都返回完全错误的结果，从而迫使上层应用察觉并报错。而 CTR 则通常只会使出错的密文解密出错，后面的密文则能正常解密，使攻击者对特定内容进行篡改的成功率大大提高。\nCFB8 继承了 CFB 在错误传播方面的优势，并将位宽降至一个字节的大小。相较于 CFB，CFB8 在遭遇单个字节密文遭遇篡改后，后续所有字节的密文都会立即解密为完全错误的结果。乍一看似乎更加安全，实际上 CFB8 这样做带来的开销是巨大的。CFB 能将底层块密码输出的每一个字节用于加密，但 CFB8 无论底层块密码一次性能输出多少个字节，都永远只拿第一个字节用于加密，并使用一种难以优化的变换形式将密文反映到其状态中。这是一种极大的性能浪费。以 AES 为例，AES 块密码一次能加密 16 个字节明文，而 CFB8 只用其第一个，这样粗算，其速度就只相当于 AES 块密码的 1/16。因此，我之前经常抱怨 Mojang 的选择过于保守，选择了一个并不怎么热门且难以优化的流密码算法。我也在今年对 Tnze/go-mc 中的 CFB8 实现进行了两次优化（#256 #265），实现了多场景加速以及解密并行化，在直接使用其框架（bot 包/server 包）时加密速度提升 ~20%，解密速度提升 30%~200%，视 CPU 和内存情况而定。\n再谈谈 AES。AES 是最被广泛使用的对称加密块密码，安全性得到广泛证明，包括电脑、手机、电视甚至智能穿戴设备在内的现代主要在用 CPU 都已添加 AES 硬件加速指令集的支持。Minecraft 所使用的 Java 标准库实现支持 AES 硬件加速。故对 AES 的选择无可挑剔。\nSHA-1 SHA-1 已被认为不安全并且易于破解。同样在上面的那篇 NIST 文章中（NIST Special Publication 800-131A），NIST 同时宣布了对 SHA-1 的弃用。然而，在这篇文章中提到，对于“非数字签名生成应用程序”（non-digital signature generation applications）中，SHA-1 的使用仍然是“可接受”（acceptable）的。在 2022 年底的时候，NIST 又发出倡议，将 December 31, 2030 作为弃用 SHA-1 的最后期限。\n不过，即使 Minecraft 使用 SHA-1 计算验证口令和会话信息的摘要值，它并不是安全模型的一部分。也就是说，Minecraft 中的这个使用场景即为上述的“非数字签名生成应用程序”。生成的摘要值都经过加密传输，仅对客户端、Mojang 服务器以及连接对端（在被中间人攻击时为中间人端）可见。而被摘要的内容中不包含敏感信息，中间人也只能拿到他已经知道的东西，即使被破解了也无需担心（虽然谁都不想被这样做😶）。\n注：此文中的“验证口令”并非“Minecraft 账户访问口令（access token）”，而是该加密协议中的 verify token。\n尽管如此，SHA-1 的使用也应该被替换。对于所有的密码学应用，我都会推荐 SHA-256（实际上是 SHA-2 256）和 BLAKE3。SHA-256 已受到大部分硬件的硬件加速支持，速度快且饱经考验，但可能会遭受长度扩展攻击（length extension attack）。BLAKE3 是一个较新的算法，衍生于 SHA-3，但运算速度极快，据官方说法甚至可以超越 SHA-1 和 MD5 等经典算法，它免疫长度扩展攻击，并且有灵活可自定义的输出长度。\n有趣的是，尽管 SHA-1 被认为不安全，HMAC_SHA-1 的组合仍被认为安全。设计于 2017 年的 Shadowsocks AEAD 协议仍然使用 HKDF_SHA-1 算法来获得一个会话通讯密钥。不过这些仍被认为安全的组合也会在 NIST 的 2030 期限后淘汰，Shadowsocks 2022 Edition 协议中也将密钥派生函数换为了 BLAKE3 KDF。\n如果是我 自嗨环节。如果换做我在现在为 Minecraft 设计一个加密通讯协议，我会怎么做？在此提供一些在这方面的改进参考。\n使用 ECDH 代替 RSA KEM 混合加密体系中，有两个使用非对称加密算法交换对称加密通讯密钥的算法家族。一家是 Minecraft 正在使用的 KEM，另一家是 TLS 主要正在使用的 DH（迪菲-赫尔曼密钥交换，Diffie–Hellman key exchange）。这两者看似做法不同，其实差别不大，毕竟都是为了同一个目的。Cloudflare 博客中有一幅图形象地对比了两类算法运作的差异：【图片出处】\n需要补充的是，Minecraft 通信加密协议中客户端和服务端的身份与此图中 KEM 所描述的正好相反。\nECDH 是使用椭圆曲线加密算法的 DH。相较于 RSA KEM，ECDH 有速度、大小等各方面的优势。目前最流行的 ECDH 算法 X25519，公私钥都只有 32 字节大小，生成私钥的过程仅是生成一组 32 字节的随机数（在实际应用中还需进行简单的三次位修改，以避免更大运算量并保证常量时间运算），密钥交换运算速度远快于 RSA，是非常理想的密钥交换算法。\n在修改中，只需将 1024bit RSA 简单替换为 X25519 即可。同时将验证口令改为随机生成的盐值，使用 BLAKE3 KDF 或者 HKDF_SHA-256 将盐值与 X25519 的输出派生出实际使用的通讯密钥（因为部分密码学家建议将 X25519 的输出经过密钥派生后再使用）。这项更改还能顺便节省每次握手时数十字节的流量开销。\nX25519 也受到量子威胁，在十年后应被更换。目前 NIST 等机构正在开发一个后量子算法 Kyber，预计在 2024 年标准化。不过，Kyber 需要传输的数据量非常大，每次握手都需要传输 800 字节或更多的数据，甚至超越 TCP MSS。尽管 Kyber 比 X25519 更快，但传输大小是它的一个重大痛点。\n使用 AEAD 上面介绍了流密码体系，也提到了 AEAD。当初 Mojang 如此小心谨慎地选择 CFB8 来将安全性落实到每一个字节，要是他们早点选择 GCM 就没这事了。GCM 是一种分组密码操作模式，与 CFB/CTR 不同的是，它将块密码转换为 AEAD 密码。AEAD 密码以一个消息为单位进行加解密，每次被传入加密的内容被视为一整个消息，解密时也需要将一整个消息不多不少地传入。由于 GCM 模式可以被看作是 CTR 模式与 GMAC 的组合，GCM 可以提供类似于 CTR 的并行加解密效率，同时保证消息不被篡改。在有内容被篡改时，函数会直接返回错误，比流密码中的错误扩散处理方式更为优雅。现代的 CPU 也有 SIMD 指令可以实现对 GMAC 的加速，整体加解密性能直逼 CTR，加密时远超 CFB。因此，我会选择使用 AES-128-GCM 用于加密。这也是 TLS 等一众密码协议的首要选择。\n应用 AEAD 密码则需要对加密传输协议进行修改。原先使用的流密码，因为流密码本身没有开销且没有其他限制，只需简单地将输入输出流替换为加密流即可。由于 AEAD 对长度敏感（因为要确保是一整个消息），所以还需要对格式进行修改。Minecraft 通信协议中已有“包”的概念，并且在每个包前面附加上一个 VarInt 编码的包长。我们要做的是将这个包长改为明文发送，然后再将加密后的“数据包”发送出去。接收方需要做的是先明文读入包长，然后再读入包长+AEAD Overhead个字节，将这些内容送入 AEAD 解密函数，即可获得明文。\n你可能怀疑，使用明文发送包长会不会导致不安全性。实际上，TLS 就在这么做。即使不明文发送包长，攻击者也可以通过各种细节得知你实际的包长度，这一点需要很大的代价才能在实践中避免。因此，TLS 就选择不隐藏包长，因为仅仅为这一包长数字单独加密一次或者实现完整的长度混淆机制显得得不偿失，并且没有太大实际意义。至于 Shadowsocks AEAD 以及以后版本为什么会选择对包长进行单独的一次 AEAD 加密，是因为 Shadowsocks 的设计目标就是让传输流量从头到尾看起来都完全随机，不留下数据层面的线索，而明文的包长很显然暴露了它所对应的协议。TLS 和 Minecraft 通讯协议在握手部分都有明文内容，若要辨别它们对应的协议，也无需通过识别明文发送包长这一特征来做到。\n向 Mojang 提议 事实上，我在今年6月的时候就尝试向 Mojang 提议更新他们的加密协议（甚至通讯协议），并且提出了类似上面的建议。出于他们反馈系统的字数限制，当时所写远远没有如上那么详细，但也概括了主要精神。现在，这一反馈贴已经通过审核，欢迎大家前去投票支持我的提议，也可以提出其他建议，这会对 Minecraft 社区的长期发展产生深远的影响！😊\n链接： https://feedback.minecraft.net/hc/en-us/community/posts/17001695944077-Improve-Minecraft-encryption-protocol\n","date":"2023-12-08T00:00:00Z","image":"https://launium.com/p/cryptography-in-minecraft/cover_hu_45d44268d2ba8650.jpg","permalink":"https://launium.com/p/cryptography-in-minecraft/","title":"谈谈 Minecraft 多人游戏的密码学应用"},{"content":"一直以来都希望认真改造一下 launium.com 的首页。以前采用一个非常著名的 HTML5UP 网页模板，但被我手工改的不成样子，背景的随机图片时常加载不出来，按钮部分还存在由于字符串过长爆框的情况。总结之后改进的需求如下：\n全静态网页，这对于速度和成本都很有帮助。 简单的页面和干净的阅读体验，尽量做到 minimalist。 评论系统支持，对 giscus 很感兴趣。 能够迁移原主页的一些链接，方便访问者找到文档入口。 性能，性能，性能⚡ 最后选择了 hugo 作为建站引擎，使用了国人开发的一个 hugo 模板 CaiJimmy/hugo-theme-stack。hugo 使用 Go 编写，渲染速度非常快，同时也和 Go 生态深度融合，初体验不错。CaiJimmy/hugo-theme-stack 主题比较符合我的口味，也有很多国人 blogger 常用的功能，最后选用了它。这里深表对这两个项目的感谢与支持！\n最后用 Google PageSpeed Insights 和原主页做一个对比。这是原主页最后一版的测试结果：\nhttps://pagespeed.web.dev/analysis/https-launium-com/11cgbmigxv?form_factor=mobile\n最新的主页测试结果，每次访问都会重新评估：\nhttps://pagespeed.web.dev/analysis?url=https%3A%2F%2Flaunium.com\n","date":"2023-12-02T00:00:00Z","image":"https://launium.com/p/hello-world/cover_hu_e95a4276bf860a84.jpg","permalink":"https://launium.com/p/hello-world/","title":"Hello World"},{"content":"For more details, check out the documentation.\nBilibili video Tencent video YouTube video Generic video file Your browser doesn't support HTML5 video. Here is a link to the video instead. Gist GitLab Quote Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n― A famous person, The book they wrote Photo by Codioful on Unsplash\n","date":"2023-08-25T00:00:00Z","image":"https://launium.com/p/shortcodes/cover_hu_5667347daefb4230.jpg","permalink":"https://launium.com/p/shortcodes/","title":"Shortcodes"}]