Base64 Encode & Decode in C

Encode or decode below — it runs locally in your browser — then grab the canonical C code. C has no standard Base64 routine, so you either roll a small lookup table or link against OpenSSL. Both approaches are below.

INPUT
OUTPUT
Type or paste to encode / decode

Encode a string

Read the input three bytes (24 bits) at a time, split into four 6-bit values, and index a 64-character alphabet. The trailing 1 or 2 bytes are padded with =:

#include <stdint.h>
#include <stdlib.h>

static const char b64_table[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789+/";

/* Returns a malloc'd, null-terminated Base64 string. Caller must free(). */
char *base64_encode(const uint8_t *in, size_t len) {
    size_t out_len = 4 * ((len + 2) / 3);     /* ceil(len/3) * 4 */
    char *out = malloc(out_len + 1);          /* +1 for '\0'     */
    if (!out) return NULL;

    size_t i, j;
    for (i = 0, j = 0; i + 2 < len; i += 3) {
        uint32_t n = ((uint32_t)in[i] << 16)
                   | ((uint32_t)in[i + 1] << 8)
                   |  (uint32_t)in[i + 2];
        out[j++] = b64_table[(n >> 18) & 0x3F];
        out[j++] = b64_table[(n >> 12) & 0x3F];
        out[j++] = b64_table[(n >>  6) & 0x3F];
        out[j++] = b64_table[ n        & 0x3F];
    }

    size_t rem = len - i;                     /* trailing 1 or 2 bytes */
    if (rem == 1) {
        uint32_t n = (uint32_t)in[i] << 16;
        out[j++] = b64_table[(n >> 18) & 0x3F];
        out[j++] = b64_table[(n >> 12) & 0x3F];
        out[j++] = '=';
        out[j++] = '=';
    } else if (rem == 2) {
        uint32_t n = ((uint32_t)in[i] << 16) | ((uint32_t)in[i + 1] << 8);
        out[j++] = b64_table[(n >> 18) & 0x3F];
        out[j++] = b64_table[(n >> 12) & 0x3F];
        out[j++] = b64_table[(n >>  6) & 0x3F];
        out[j++] = '=';
    }

    out[j] = '\0';
    return out;
}

Tip: C has no standard Base64 — use a lookup table or OpenSSL. Always size the output buffer as 4 * ((n + 2) / 3) + 1, and read input as uint8_t so high-bit bytes don't go negative.

Using OpenSSL (EVP)

If your project already links libcrypto, reuse its well-tested one-shot encoder and decoder instead of maintaining your own:

#include <openssl/evp.h>
#include <stdlib.h>
#include <string.h>

char *openssl_b64_encode(const unsigned char *in, int len) {
    char *out = malloc(4 * ((len + 2) / 3) + 1);
    if (!out) return NULL;
    /* Writes the data plus a trailing '\0'. */
    EVP_EncodeBlock((unsigned char *)out, in, len);
    return out;
}

/* Returns decoded length. NOTE: EVP_DecodeBlock returns a multiple of 3 —
 * subtract the number of trailing '=' yourself to drop padding bytes. */
int openssl_b64_decode(const char *in, unsigned char *out) {
    int len = (int)strlen(in);
    int n = EVP_DecodeBlock(out, (const unsigned char *)in, len);
    if (n < 0) return -1;
    if (len >= 2 && in[len - 1] == '=') n--;
    if (len >= 2 && in[len - 2] == '=') n--;
    return n;
}

URL-safe variant

For JWTs, URLs, and filenames, swap the last two alphabet characters — + and / become - and _ (RFC 4648 §5). The encoder is otherwise identical; just point it at the new table:

static const char b64_url_table[] =
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "abcdefghijklmnopqrstuvwxyz"
    "0123456789-_";

/* Pass url_safe = 1 to emit the RFC 4648 URL-safe alphabet. */
char *base64_encode_ex(const uint8_t *in, size_t len, int url_safe) {
    const char *table = url_safe ? b64_url_table : b64_table;
    /* ...identical loop, but index `table` instead of `b64_table`... */
    /* (omit '=' padding entirely for unpadded URL-safe output) */
}

Frequently asked questions

Does C have a built-in Base64 function?

No. The C standard library (stdio.h, stdlib.h, string.h, etc.) has no Base64 routines. You write your own lookup-table implementation or link a library such as OpenSSL (EVP_EncodeBlock) or libb64.

How do I Base64 encode a string in C?

Define a 64-character alphabet, read three bytes at a time, and map each 6-bit group to a character. The base64_encode function above returns a malloc'd string you must free().

How do I size the output buffer?

Use 4 * ((len + 2) / 3) + 1 bytes — exact, including padding, plus 1 for the null terminator. Decoded length is at most (encoded_len / 4) * 3.

Should I use OpenSSL or a custom implementation?

If you already link libcrypto, use EVP_EncodeBlock/EVP_DecodeBlock. For embedded or dependency-free code, a small lookup table is portable.

Need image, file, or URL-safe modes?

The main base64.dev tool handles text, images, files, and URL-safe Base64 with auto-detect.

Open base64.dev →