How to encode and decode Base45
- Type or paste text into Input · Text. The Base45 string appears in Output · Base45 (RFC 9285).
- For binary data such as compressed bytes, set From to Hex or Base64 first.
- To decode, click Base45 (RFC 9285) → Text in the direction toggle and paste the string. Choose Hex or Base64 under To if the result is binary, which it is for health certificates.
- Copy output keeps spaces intact. Save bytes downloads the decoded bytes as a file.
How Base45 works
RFC 9285 takes the input two bytes at a time. Each pair is a number n from 0 to 65535, written as three base-45 digits c, d, e with n = c + d × 45 + e × 45², least significant first. A final single byte becomes two digits. The alphabet is the QR code alphanumeric set in order:
0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ $%*+-./:
The RFC’s examples, all reproduced by this tool:
| Input | Base45 |
|---|---|
AB | BB8 |
Hello!! | %69 VD92EX0 |
base-45 | UJCLQE7W581 |
ietf! | QED8WEX0 |
Decoding checks two things a lenient decoder might skip: a length of 3n + 1 cannot come from any input, and a group above 65535 (such as GGW or ZZZ) is out of range. Both give an error.
Why Base45 suits QR codes
On paper, Base45 looks wasteful: 3 characters per 2 bytes is 50% overhead, more than Base64’s 33%. The difference lies in how QR codes store text. In alphanumeric mode, a QR code packs two of these 45 characters into 11 bits. Three Base45 characters therefore cost 16.5 bits for 16 bits of data, about 3% overhead.
Base64 cannot use alphanumeric mode, because it needs lower-case letters. It has to go into byte mode at 8 bits per character, which is 33% overhead in the QR code. For the same data, the Base45 QR code is smaller or holds more.
Decoding an EU Digital COVID Certificate (HC1:)
The EU Digital COVID Certificate QR code holds a string that starts with HC1:. The payload is built in layers, and this page handles one of them:
- Remove the
HC1:prefix.H,C,1and:are all valid Base45 characters, so a prefix left in place is decoded as data and breaks everything after it. - Base45 decode the rest here, with To set to Base64.
- Inflate the result. It is zlib data, which starts with the byte
78(eJoreNin Base64). Paste the Base64 into the gzip and zlib decoder. - Parse the CBOR. The inflated bytes are a COSE_Sign1 structure in CBOR: a header, the payload with the certificate fields, and a signature. That needs a CBOR decoder, which this site does not have.
Decoding shows the content, but it does not verify the signature. Checking that a certificate is genuine needs the issuing country’s public key and a COSE verifier.
Base45 in Python
Python has no Base45 module in the standard library. The encoder follows the RFC directly:
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'
The base45 package on PyPI provides both directions. For more Python byte handling see the Python page.
FAQ
Why does Base45 output contain spaces?
Space is one of the 45 characters of the QR alphanumeric set, so it is a normal digit in Base45 with the value 36. Hello!! encodes to %69 VD92EX0. Keep the spaces when you copy, and do not let a form or shell trim them.
Can I decode an EU Digital COVID Certificate here?
Partly. This page does the Base45 step. After that, the data is zlib-compressed CBOR inside a signed COSE structure, which needs an inflate step and a CBOR decoder; the steps are listed on this page.
Is Base45 safe to put in a URL?
No. Space, %, + and / are in the alphabet and all mean something in URLs. Percent-encode the string or use Base64URL for URLs.
Why is my Base45 string rejected?
Valid Base45 has a length of 3n or 3n + 2 characters, and each 3-character group must be at most 65535. A length of 3n + 1, or a group such as GGW, is an error. A leftover HC1: prefix is a common cause.