A Base45 decoder converts a string written in the 45-character alphabet of RFC 9285 back into bytes. Every 3 characters become 2 bytes.
Example
This is the test vector from RFC 9285:
%69 VD92EX0Hello!!Note that the space in the input is part of the data. In Base45 a space is a valid character, not a separator.
What is Base45?
QR codes have an alphanumeric mode that can only store 45 characters: 0–9, A–Z, space and $ % * + - . / :. Characters in that mode take fewer bits each than in byte mode, so the same data fits in a smaller code. Base45 was designed to fit binary data into exactly that character set.
The encoding takes 2 bytes at a time, treats them as a number from 0 to 65535, and writes that number in base 45 using three characters, least significant first. A single trailing byte becomes two characters.
The best-known use is the EU Digital COVID Certificate. Its QR code text starts with HC1:, followed by Base45. The decoded bytes are zlib-compressed, and inside is a COSE-signed CBOR document. So decoding a certificate takes three steps: Base45 decode, inflate with zlib, then parse CBOR.
How to use the Base45 decoder
- Paste the Base45 text. If it starts with a prefix like
HC1:, remove the prefix first. - Read the result. Short test strings decode to text; real certificate payloads decode to binary.
- For a certificate, check the hex view: zlib data starts with
78. Copy the hex into the Gzip / zlib decompressor with Input is: Hex and Format: zlib / deflate to unpack it.
Decode Base45 in code
A = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:"
def b45decode(s: str) -> bytes:
out = bytearray()
for i in range(0, len(s), 3):
chunk = [A.index(c) for c in s[i:i + 3]]
n = sum(v * 45 ** k for k, v in enumerate(chunk))
out += n.to_bytes(2 if len(chunk) == 3 else 1, "big")
return bytes(out)
print(b45decode("%69 VD92EX0").decode()) # Hello!!Base45 at a glance
| Specification | RFC 9285 |
| Alphabet | 0–9 A–Z, space and $ % * + - . / : |
| Ratio | 3 characters per 2 bytes |
| Main use | QR codes in alphanumeric mode, EU Digital COVID Certificates |
Frequently asked questions
Why use Base45 instead of Base64 in a QR code?
Base64 needs lowercase letters, which forces the QR code into byte mode. Base45 stays inside the alphanumeric mode, which packs characters more tightly, so the same data produces a smaller, easier-to-scan code.
How do I read an EU COVID certificate?
Remove the HC1: prefix, decode the rest as Base45, inflate the result with zlib, and then parse the CBOR/COSE structure. This tool handles the first step and the gzip tool handles the second. The CBOR step needs a CBOR decoder.
Is the space character really part of the data?
Yes. The space is character number 36 in the Base45 alphabet. Removing or collapsing spaces changes the data, so paste the string exactly as it is.
Last reviewed · found a mistake? contact@decoder.tools · view as markdown