Learn how to use Base64 in programming languages: C#, C++, C, Java
, ,

Base64 in C: Beginner’s Guide to Encoding & Decoding

Unfortunately, the C language does not support Base64 encoding by default, so there are two solutions: use an external library or implement it yourself. Choose either one, examples of both are shown in this article.

What is Base64?

Base64 is used to convert binary data into a textual representation that is universally supported in digital communication. This encoding scheme turns binary data into a string of ASCII characters, allowing it to be transmitted over text channels. Base64 facilitates data sharing without the possibility of special characters causing problems, making it a crucial tool for data exchange across multiple technologies.

What is C Programming Language?

C is a fundamental programming language that impacted the creation of many others. It is well-known for its ease of use, efficiency, and portability, making it an excellent choice for system programming, embedded systems, and low-level operations. C has a simple syntax and permits direct manipulation of hardware resources, making it ideal for jobs that require fine control over system resources.

Base64 Encoding and Decoding in C

Base64 encoding and decoding are not incorporated into the C programming language. Developers, on the other hand, can easily add this feature by utilizing external libraries. One such library is OpenSSL, which offers complete support for cryptography and data encoding activities, including Base64 operations.

Within the OpenSSL library, functions such as BIO_f_base64() and BIO_read() allow for efficient and secure Base64 encoding and decoding of binary data. These functions are available in the openssl/bio.h header file and provide developers with flexible tools to handle Base64 operations.

Because the C programming language lacks built-in Base64 support, developers must either implement such operations manually or use other libraries. OpenSSL is simply one of several methods for implementing Base64 encoding and decoding.

How to Add Library to C?

The specific methods to include OpenSSL in your C project vary depending on your operating system and development environment. If you’re working with Windows and Visual Studio Code, for example, you can install/compile OpenSSL as a static library that will be included in your C project. Install the OpenSSL development package (e.g., libssl-dev on Debian, Ubuntu, and similar distributions) and use the -I flag to specify the path to the OpenSSL folder when compiling with gcc to compile a.c file with OpenSSL includes.

Example of Base64 Encoding in C

This C code explains how to encode data in base64 format using the OpenSSL package.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
 
char* base64_encode(const unsigned char* data, size_t input_length) {
    BIO *bio, *b64;
    FILE* stream;
    size_t output_length = 4 * ((input_length + 2) / 3);
 
    char *encoded_data = (char *)malloc(output_length);
    if (encoded_data == NULL) return NULL;
 
    stream = fmemopen(encoded_data, output_length, "w");
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new_fp(stream, BIO_NOCLOSE);
    bio = BIO_push(b64, bio);
 
    BIO_write(bio, data, input_length);
    BIO_flush(bio);
    BIO_free_all(bio);
 
    fclose(stream);
    return encoded_data;
}
 
int main() {
    const char* original_text = "B64Encode.com";
    size_t original_length = strlen(original_text);
 
    char* encoded_text = base64_encode((const unsigned char*)original_text, original_length);
    printf("Original Text: %s\nEncoded Text: %s\n", original_text, encoded_text);
 
    free(encoded_text);
    return 0;
}

The base64_encode function takes as input a pointer to the data to be encoded and the length of the data in bytes. It returns a pointer to a newly allocated buffer containing the base64-encoded data.

The function first calculates the length of the output buffer required to hold the base64-encoded data. It then allocates memory for the buffer using malloc. If memory allocation fails, the function returns NULL.

Next, the function creates a FILE stream using fmemopen, which associates the stream with the output buffer. It then creates a new BIO object for base64 encoding using BIO_new and BIO_f_base64. A second BIO object is created for writing to the FILE stream using BIO_new_fp. The two BIO objects are then chained together using BIO_push, with the base64 encoding BIO object at the top of the chain.

The function then writes the input data to the top of the BIO chain using BIO_write. As the data passes through the chain, it is base64-encoded by the first BIO object and written to the output buffer by the second BIO object. The function then flushes any remaining data in the BIO chain using BIO_flush, frees all resources associated with the BIO objects using BIO_free_all, and closes the FILE stream using fclose.

The main function demonstrates how to use the base64_encode function. It defines a string containing some text, calculates its length, and calls base64_encode to encode it in base64 format. The original and encoded text are then printed to standard output using printf. Finally, memory allocated for the encoded text is freed using free.

Example of Base64 Decoding in C

This C code demonstrates how to decode data from base64 format using the OpenSSL library.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
 
unsigned char* base64_decode(const char* encoded_data, size_t* output_length) {
    BIO *bio, *b64;
    size_t input_length = strlen(encoded_data);
 
    *output_length = (input_length * 3) / 4;
    unsigned char* decoded_data = (unsigned char*)malloc(*output_length);
    if (decoded_data == NULL) return NULL;
 
    bio = BIO_new_mem_buf(encoded_data, -1);
    b64 = BIO_new(BIO_f_base64());
    bio = BIO_push(b64, bio);
 
    *output_length = BIO_read(bio, decoded_data, input_length);
    BIO_free_all(bio);
 
    return decoded_data;
}
 
int main() {
    const char* encoded_text = "QjY0RW5jb2RlLmNvbQ==";
    size_t decoded_length = 0;
 
    unsigned char* decoded_data = base64_decode(encoded_text, &decoded_length);
    printf("Encoded Text: %s\nDecoded Text: %.*s\n", encoded_text, (int)decoded_length, decoded_data);
 
    free(decoded_data);
    return 0;
}

The base64_decode function takes as input a pointer to the base64-encoded data and a pointer to a size_t variable that will be used to store the length of the decoded data in bytes. It returns a pointer to a newly allocated buffer containing the decoded data.

The function first calculates the length of the input data using strlen and uses this value to estimate the maximum length of the output buffer required to hold the decoded data. It then allocates memory for the buffer using malloc. If memory allocation fails, the function returns NULL.

Next, the function creates a new BIO object for reading from a memory buffer using BIO_new_mem_buf and associates it with the input data. It then creates a new BIO object for base64 decoding using BIO_new and BIO_f_base64. The two BIO objects are then chained together using BIO_push, with the base64 decoding BIO object at the top of the chain.

The function then reads from the top of the BIO chain using BIO_read. As data is read from the chain, it is base64-decoded by the first BIO object and stored in the output buffer. The actual number of bytes read is stored in the variable pointed to by the output_length parameter. The function then frees all resources associated with the BIO objects using BIO_free_all.

The main function demonstrates how to use the base64_decode function. It defines a string containing some base64-encoded text and calls base64_decode to decode it. The encoded and decoded text are then printed to standard output using printf. Finally, memory allocated for the decoded data is freed using free.

Implementing Base64 Encoding Algorithm in Pseudocode

If you don’t want to use an external library, you can try to implement Base64 encoding and decoding yourself. The good news is that Base64 is not a very complicated method, so it is not too difficult to program.

Because Base64 encoding isn’t tied to a particular programming language, it can be employed in any programming language. The example below demonstrates how to apply a technique that isn’t reliant on a specific programming language for implementing Base64 encoding.

If you want more details, this article explains Base64 in more detail.

Here’s an example of a language-agnostic algorithm in pseudocode:

function base64_encode(input)
    // The base character set
    const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
 
    // The length of the input
    let input_length = length(input)
 
    // The output
    let output = ""
 
    // Process the input in 3-byte blocks
    for i from 0 to input_length - 1 step 3
        // The value of the block
        let block_value = (input[i] << 16) + (input[i + 1] << 8) + input[i + 2]
 
        // Encode the block into 4 characters
        for j from 0 to 3
            let index = (block_value >> ((3 - j) * 6)) & 0x3F
            output += BASE64_CHARS[index]
        end for
    end for
 
    // Pad the output length with '=' characters if necessary
    let padding = input_length % 3
    if padding > 0
        for i from 0 to (3 - padding)
            output[output.length - i - 1] = '='
        end for
    end if
 
    return output
end function

Here is an explanation of the code, broken down into a list:

  1. The binary data to be encoded is passed as an input parameter to the base64_encode function.
  2. The basic character set for the encoding, which consists of 64 characters, is designated by the constant BASE64_CHARS.
  3. The length of the input data is used to calculate the input_length variable.
  4. The output variable, which will hold the result of the encoding, is initially set to an empty string.
  5. A for loop that iterates from 0 to input_length – 1 with a step of 3 processes the input data in 3-byte blocks.
  6. The first byte is shifted left by 16 bits for each block, the second byte is shifted left by 8 bits, and the third byte is added to determine the block_value variable for each block.
  7. A second for loop that iterates from 0 to 3 is then used to encode the block into 4 characters.
  8. For each character, an index is calculated by shifting the block_value to the right by a multiple of 6 bits and masking it with 0x3F.
  9. The character at this index in the BASE64_CHARS constant is then appended to the output string.
  10. After all blocks have been processed, the output length is padded with ‘=’ characters if necessary.
  11. To accomplish this, the padding is determined by dividing the input_length by 3.
  12. A for loop iterates from 0 to (3 – padding) and replaces the final characters of the output string with ‘=‘ characters if padding is higher than 0.
  13. The function then returns the result that was encoded and saved in the output variable.

Implementing Base64 Decoding Algorithm in Pseudocode

Now let’s look at decoding independently of the programming language.

function base64_decode(input)
    // The base character set
    const BASE64_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
 
    // The length of the input
    let input_length = length(input)
 
    // The output
    let output = []
 
    // Process the input in 4-character blocks
    for i from 0 to input_length - 1 step 4
        // The value of the block
        let block_value = (index_of(BASE64_CHARS, input[i]) << 18) + (index_of(BASE64_CHARS, input[i + 1]) << 12) + (index_of(BASE64_CHARS, input[i + 2]) << 6) + index_of(BASE64_CHARS, input[i + 3])
 
        // Decode the block into 3 bytes
        for j from 0 to 2
            let byte = (block_value >> ((2 - j) * 8)) & 0xFF
            output.append(byte)
        end for
    end for
 
    // Remove any padding bytes from the output
    let padding = count(input, '=')
    if padding > 0
        output = output[0:output.length - padding]
    end if
 
    return output
end function
  • The base64_decode function takes an input parameter, which is the string of characters to be decoded.
  • The BASE64_CHARS constant is defined as the base character set for the decoding, which consists of 64 characters.
  • The length of the input string is used to calculate the input_length variable.
  • The output variable, which will hold the result of the decoding, is initialized as an empty array.
  • The input data is processed in 4-character blocks using a for loop that iterates from 0 to input_length - 1 with a step of 4.
  • For each block, the block_value variable is calculated by shifting the index of each character in the BASE64_CHARS constant to the left by a multiple of 6 bits and adding them together.
  • Then, a second for loop iterating from 0 to 2 decodes the block into 3 bytes.
  • By moving the block_value to the right by a multiple of 8 bits and masking it with 0xFF, a value is computed for each byte. This value is then appended to the output array.
  • If necessary, any padding bytes are taken out of the output once all blocks have been processed. The count function is used to determine how many padding characters (‘=‘) are present in the input, and then the appropriate number of bytes are subtracted from the end of the output array.
  • Finally, the function returns the decoded result stored in the output array.