如何编码和解码 Base45
- 在 输入 · 文本 中输入或粘贴文本。Base45 字符串会显示在 输出 · Base45(RFC 9285) 中。
- 对于压缩后的字节等二进制数据,请先把 从 设为 Hex 或 Base64。
- 要解码,请在方向切换中点击 Base45(RFC 9285)→ 文本,然后粘贴字符串。如果结果是二进制数据(健康证书就是如此),请在 到 中选择 Hex 或 Base64。
- 复制输出 会完整保留空格。保存字节 会把解码后的字节下载为文件。
Base45 的工作原理
RFC 9285 每次取输入的两个字节。每对字节是一个 0 到 65535 之间的数 n,写成三个 45 进制数字 c、d、e,满足 n = c + d × 45 + e × 45²,低位在前。最后剩下的单个字节变成两个数字。字母表就是按顺序排列的二维码字母数字字符集:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:
RFC 中的示例,本工具全部可以复现:
| 输入 | Base45 |
|---|---|
AB | BB8 |
Hello!! | %69 VD92EX0 |
base-45 | UJCLQE7W581 |
ietf! | QED8WEX0 |
解码时会检查两件宽松的解码器可能跳过的事:长度为 3n + 1 的字符串不可能由任何输入生成;值超过 65535 的分组(例如 GGW 或 ZZZ)超出了范围。两种情况都会报错。
为什么 Base45 适合二维码
纸面上看,Base45 似乎很浪费:每 2 个字节用 3 个字符,开销 50%,比 Base64 的 33% 还多。差别在于二维码存储文本的方式。在字母数字模式下,二维码把两个这样的字符装进 11 个比特。因此三个 Base45 字符占用 16.5 比特,承载 16 比特数据,开销约为 3%。
Base64 无法使用字母数字模式,因为它需要小写字母。它只能使用每个字符 8 比特的字节模式,在二维码中的开销为 33%。对于相同的数据,Base45 生成的二维码更小,或者能容纳更多内容。
解码欧盟数字新冠证书(HC1:)
欧盟数字新冠证书的二维码中是一个以 HC1: 开头的字符串。载荷是分层构建的,本页处理其中一层:
- 去掉
HC1:前缀。H、C、1和:都是有效的 Base45 字符,所以如果保留前缀,它会被当作数据解码,后面的内容全部出错。 - 在这里对剩余部分做 Base45 解码,并把 到 设为 Base64。
- 解压结果。它是 zlib 数据,以字节
78开头(Base64 中为eJ或eN)。把 Base64 粘贴到 gzip 与 zlib 解码器中。 - 解析 CBOR。解压后的字节是 CBOR 格式的 COSE_Sign1 结构:一个头部、包含证书字段的载荷,以及一个签名。这需要 CBOR 解码器,本站没有提供。
解码可以看到内容,但不会验证签名。要检查证书是否真实,需要签发国的公钥和 COSE 验证工具。
在 Python 中使用 Base45
Python 标准库中没有 Base45 模块。下面的编码器直接按照 RFC 实现:
ALPHABET = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
def b45encode(data: bytes) -> str:
out = []
for i in range(0, len(data) - 1, 2):
n = data[i] * 256 + data[i + 1]
c, n = n % 45, n // 45
d, e = n % 45, n // 45
out += ALPHABET[c], ALPHABET[d], ALPHABET[e]
if len(data) % 2:
c, d = data[-1] % 45, data[-1] // 45
out += ALPHABET[c], ALPHABET[d]
return "".join(out)
b45encode(b"ietf!") # 'QED8WEX0'
PyPI 上的 base45 包提供了编码和解码两个方向。更多 Python 字节处理见 Python 页面。
常见问题
为什么 Base45 输出中有空格?
空格是二维码字母数字字符集中的 45 个字符之一,所以它在 Base45 中是一个普通的数字,值为 36。Hello!! 编码为 %69 VD92EX0。复制时请保留空格,也不要让表单或 shell 把它们去掉。
能在这里解码欧盟数字新冠证书吗?
可以完成一部分。本页负责 Base45 这一步。之后的数据是签名 COSE 结构中经过 zlib 压缩的 CBOR,需要先解压再用 CBOR 解码器处理;具体步骤见本页。
Base45 可以放进 URL 吗?
不行。空格、%、+ 和 / 都在字母表中,而它们在 URL 中都有特殊含义。请对字符串做百分号编码,或者在 URL 中使用 Base64URL。
为什么我的 Base45 字符串被拒绝?
有效的 Base45 长度为 3n 或 3n + 2 个字符,且每个 3 字符分组的值不能超过 65535。长度为 3n + 1,或出现 GGW 这样的分组,都会报错。常见原因是残留了 HC1: 前缀。