如何进行 Z85 编码和解码
- 以 hex 形式把字节粘贴到 输入 · Hex。空格、冒号和
0x前缀都可以接受,所以 hex 转储可以直接粘贴。 - 在 输出 · Z85(ZeroMQ) 中查看 Z85。
- 如果字节数不是 4 的倍数,你会看到一条注明字节数的错误。只有当双方都认可这一扩展时,才勾选 允许最后一块不完整(非标准)。
- 要解码,在方向切换中点击 Z85(ZeroMQ)→ Hex,然后粘贴 Z85 字符串。把 到 设为 Base64 或 文本 可以换种方式查看,或点击 保存字节 得到二进制文件。
规范测试向量
ZeroMQ 的 Z85 规范(ZeroMQ RFC 系列中的 RFC 32)给出了两个测试值。本工具两个都能重现:
| Hex | Z85 |
|---|---|
86 4F D2 6F B5 59 F7 5B | HelloWorld |
8E 0B DD 69 76 28 B9 1D 8F 24 55 87 EE 95 C5 B0 4D 48 96 3F 79 25 98 77 B4 9C D9 06 3A EA D3 B7 | JTKVSB%%)wK0E.X)V>+}o?pNmC{O&4W4b!Ni{Lh6 |
第一个展示了分块结构:8 个字节,两个块,十个字符。第二个是一个 32 字节的密钥及其 40 字符的编码,也就是你在 CurveZMQ 配置中看到的长度。
一些边界值:四个零字节是 00000,四个 ff 字节是 %nSc0。Z85 对全零块没有简写,所以一串零仍然是每块五个字符。
ZeroMQ 为什么使用 Z85
CurveZMQ 密钥是 32 个随机字节。ZeroMQ 需要一种方式,把它们作为普通字符串放进配置文件、命令行和源代码中。Z85 就是为此设计的:
- 它比原始字节大 25%,而 Base64 是 33%,所以 32 字节的密钥是 40 个字符,而不是 44 个。
- 它的字母表中没有
"、'、\或反引号,所以密钥可以直接放进 C、Python 或 shell 的字符串字面量中,无需转义。它也避开了,和;。 - 数字和字母排在前面(
0-9 a-z A-Z),后面是 23 个标点字符:.-:+=^!/*?&<>()[]{}@%$#。
它仍然包含 <、> 和 &,所以放在 XML 或 HTML 中的 Z85 需要转义。在 JSON 中则不需要。
在 Python 中使用 Z85
Z85 和 Python 的 b85encode 使用相同的算法,只有字母表不同,所以做一次字符转换就能互相转换:
import base64
B85 = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~"
Z85 = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#"
TO_Z85 = str.maketrans(B85, Z85)
FROM_Z85 = str.maketrans(Z85, B85)
def z85encode(data: bytes) -> str:
if len(data) % 4:
raise ValueError("Z85 needs a multiple of 4 bytes")
return base64.b85encode(data).decode().translate(TO_Z85)
def z85decode(text: str) -> bytes:
return base64.b85decode(text.translate(FROM_Z85))
z85encode(bytes.fromhex("864FD26FB559F75B")) # 'HelloWorld'
如果你已经依赖 PyZMQ,zmq.utils.z85.encode 和 decode 能做同样的事。在 C 中,libzmq 提供 zmq_z85_encode 和 zmq_z85_decode。Python 字节处理的更多内容见 Python 页面。
常见问题
- “Z85 以 4 字节为一块进行编码,而数据有 N 个字节”。 输入没有按块对齐。检查是否复制了完整的密钥;显示为 31 字节的 32 字节密钥丢了一对 hex 数字。
- “Z85 需要 5 个字符一组;剩余 N 个”。 Z85 字符串被截断了,或者有多余的字符。CurveZMQ 密钥必须正好是 40 个字符。
- 字符串能解码,但密钥不能用。 它可能是 Ascii85 或 Git Base85,它们的长度规则相同,但字母表不同,所以同样的字符会解码出不同的字节。到 Base85 页面 试试,把 从 设为另一种变体。
常见问题
为什么 Z85 要求字节数是 4 的倍数?
规范只为完整的 4 字节块定义了 Z85,这样解码器可以确信每 5 个字符就是 4 个字节。其他长度的数据必须由应用程序自行填充或封装。如果仍想按 Ascii85 的方式编码末尾部分,勾选 允许最后一块不完整(非标准)。
Z85 格式的 CurveZMQ 密钥有多长?
40 个字符。CurveZMQ 的公钥和私钥都是 32 字节,32 / 4 × 5 = 40。这就是 zmq_curve_keypair() 返回的长度,在 C 中还要加一个结尾的空字符。
Z85 和 Ascii85 一样吗?
算法相同,字母表不同。两者都把 4 个字节写成 5 位 85 进制数字,但 Z85 把数字和字母排在前面,避开了引号和反斜杠,也没有 z 简写或 <~ ~> 定界符。
如何在 Python 中进行 Z85 编码?
标准库没有 Z85,但 base64.b85encode 使用相同的算法,所以转换它的输出字母表就能得到 Z85。本页的代码片段就是这样做的。PyZMQ 也自带 zmq.utils.z85。