How to convert Base64 to Base32
- Paste the Base64 into Input · Base64. URL-safe characters (
-,_), missing padding and line breaks are fine. - Read the result in Output · Base32 (RFC 4648).
- For an
otpauth://link or an authenticator app, untick Padding (=) under the output. - Click Copy output. Base32 (RFC 4648) → Base64 in the direction toggle converts the other way, as does the Base32 to Base64 page.
The bytes in the middle do not change. A 20-byte key in, a 20-byte key out, as the Bytes counter under the tool shows.
Examples
| Base64 | Base32 | Bytes |
|---|---|---|
iEwO+V01Llzjb4MxBUtgEuCNW8o= | RBGA56K5GUXFZY3PQMYQKS3ACLQI2W6K | 20 |
iEwO-V01Llzjb4MxBUtgEuCNW8o | RBGA56K5GUXFZY3PQMYQKS3ACLQI2W6K | 20, URL-safe and unpadded input |
MTIzNDU2Nzg5MDEyMzQ1Njc4OTA= | GEZDGNBVGY3TQOJQGEZDGNBVGY3TQOJQ | 20, the RFC 6238 test secret |
SGVsbG8h3q2+7w== | JBSWY3DPEHPK3PXP | 10 |
The two strings in the first rows look nothing alike, and that is normal: Base64 uses 6 bits per character and Base32 uses 5, so the characters never line up.
The mistake to avoid is encoding the Base64 characters as text. Pasting iEwO+V01Llzjb4MxBUtgEuCNW8o= into a text-to-Base32 encoder gives NFCXOTZLKYYDCTDM…, which is valid Base32 of the 28 characters themselves. Authenticator codes from it will never match the server.
Setting up TOTP from a Base64 secret
Server code often stores the shared secret as Base64, or generates it with a command like this:
openssl rand -base64 20
To enroll it in an authenticator app, convert it here, then build the key URI:
otpauth://totp/Example:alice@example.com?secret=RBGA56K5GUXFZY3PQMYQKS3ACLQI2W6K&issuer=Example
algorithm, digits and period can be added as parameters; without them, apps assume SHA-1, 6 digits and 30 seconds. Turn the link into a QR code with any local QR tool, or let the user type the Base32 secret by hand. The secret is the same bytes your server feeds into HMAC, so codes match without changing the server.
If you need a new secret rather than an existing one, generate 20 random bytes as Base64 with the random Base64 generator (Custom preset) and convert them here.
Lengths for common key sizes
| Key | Base64 | Base32, no padding | Base32 with = |
|---|---|---|---|
| 10 bytes (80 bits) | 16 | 16 | 16 |
| 16 bytes (128 bits) | 24 | 26 | 32 |
| 20 bytes (160 bits) | 28 | 32 | 32 |
| 32 bytes (256 bits) | 44 | 52 | 56 |
Sizes that are a multiple of 5 bytes, like 10 and 20, give Base32 with no padding at all.
The same conversion in Python
import base64
secret_b64 = "iEwO+V01Llzjb4MxBUtgEuCNW8o="
base64.b32encode(base64.b64decode(secret_b64)).decode().rstrip("=")
# 'RBGA56K5GUXFZY3PQMYQKS3ACLQI2W6K'
For URL-safe input use base64.urlsafe_b64decode, and add = padding first if it was stripped. More on that in Base64 in Python.
FAQ
Why do authenticator apps need Base32 instead of Base64?
The otpauth:// key URI format, which apps read from QR codes, defines the secret as Base32. It also has to be typeable when the QR code cannot be scanned, and Base32 has no +, / or mixed case to get wrong.
Should the Base32 secret have = padding?
Not in an otpauth:// link: the key URI format says padding should be omitted. Untick Padding (=). A 20-byte secret is 32 characters and needs no padding anyway.
How long should a TOTP secret be?
RFC 4226 requires at least 128 bits and recommends 160 bits, which is 20 bytes: 28 Base64 characters or 32 Base32 characters.
Is converting a secret to Base32 a security risk?
The encoding adds no protection and removes none; anyone with the string in either form can generate your codes. The risk is where you paste it. This page converts it in the browser and its Content Security Policy blocks network requests other than Cloudflare’s page-view count.