How do I base64 encode (decode) in C?

Viewed 366554

I have binary data in an unsigned char variable. I need to convert them to PEM base64 in c. I looked in openssl library but i could not find any function. Does any body have any idea?

19 Answers

But you can also do it in openssl (openssl enc command does it....), look at the BIO_f_base64() function

GNU coreutils has it in lib/base64. It's a little bloated but deals with stuff like EBCDIC. You can also play around on your own, e.g.,

char base64_digit (n) unsigned n; {
  if (n < 10) return n - '0';
  else if (n < 10 + 26) return n - 'a';
  else if (n < 10 + 26 + 26) return n - 'A';
  else assert(0);
  return 0;
}

unsigned char base64_decode_digit(char c) {
  switch (c) {
    case '=' : return 62;
    case '.' : return 63;
    default  :
      if (isdigit(c)) return c - '0';
      else if (islower(c)) return c - 'a' + 10;
      else if (isupper(c)) return c - 'A' + 10 + 26;
      else assert(0);
  }
  return 0xff;
}

unsigned base64_decode(char *s) {
  char *p;
  unsigned n = 0;

  for (p = s; *p; p++)
    n = 64 * n + base64_decode_digit(*p);

  return n;
}

Know ye all persons by these presents that you should not confuse "playing around on your own" with "implementing a standard." Yeesh.

Here's the decoder I've been using for years...

    static const char  table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    static const int   BASE64_INPUT_SIZE = 57;

    BOOL isbase64(char c)
    {
       return c && strchr(table, c) != NULL;
    }

    inline char value(char c)
    {
       const char *p = strchr(table, c);
       if(p) {
          return p-table;
       } else {
          return 0;
       }
    }

    int UnBase64(unsigned char *dest, const unsigned char *src, int srclen)
    {
       *dest = 0;
       if(*src == 0) 
       {
          return 0;
       }
       unsigned char *p = dest;
       do
       {

          char a = value(src[0]);
          char b = value(src[1]);
          char c = value(src[2]);
          char d = value(src[3]);
          *p++ = (a << 2) | (b >> 4);
          *p++ = (b << 4) | (c >> 2);
          *p++ = (c << 6) | d;
          if(!isbase64(src[1])) 
          {
             p -= 2;
             break;
          } 
          else if(!isbase64(src[2])) 
          {
             p -= 2;
             break;
          } 
          else if(!isbase64(src[3])) 
          {
             p--;
             break;
          }
          src += 4;
          while(*src && (*src == 13 || *src == 10)) src++;
       }
       while(srclen-= 4);
       *p = 0;
       return p-dest;
    }

The EVP_EncodeBlock and EVP_DecodeBlock functions make it very easy:

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

char *base64(const unsigned char *input, int length) {
  const int pl = 4*((length+2)/3);
  char *output = calloc(pl+1, 1); //+1 for the terminating null that EVP_EncodeBlock adds on
  const int ol = EVP_EncodeBlock(output, input, length);
  if (ol != pl) { fprintf(stderr, "Whoops, encode predicted %d but we got %d\n", pl, ol); }
  return output;
}

unsigned char *decode64(const char *input, int length) {
  const int pl = 3*length/4;
  unsigned char *output = calloc(pl+1, 1);
  const int ol = EVP_DecodeBlock(output, input, length);
  if (pl != ol) { fprintf(stderr, "Whoops, decode predicted %d but we got %d\n", pl, ol); }
  return output;
}

I wrote one for use with C++, it's very fast, works with streams, free, and open source:

https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/

Feel free to use it if it fits your purpose.

Edit: Added code inline by request.

The performance boost is acieved by using a lookup table for encoding and decoding. _UINT8 is an unsigned char on most OS's.

/** Static Base64 character encoding lookup table */
const char CBase64::encodeCharacterTable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/** Static Base64 character decoding lookup table */
const char CBase64::decodeCharacterTable[256] = {
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
    ,-1,62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-1,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21
    ,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
    ,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
    -1,-1,-1}; 

/*!
\brief Encodes binary data to base 64 character data
\param in The data to encode
\param out The encoded data as characters
*/
void CBase64::Encode(std::istream &in, std::ostringstream &out)
{
    char buff1[3];
    char buff2[4];
    _UINT8 i=0, j;
    while(in.readsome(&buff1[i++], 1))
        if (i==3)
        {
            out << encodeCharacterTable[(buff1[0] & 0xfc) >> 2];
            out << encodeCharacterTable[((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4)];
            out << encodeCharacterTable[((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6)];
            out << encodeCharacterTable[buff1[2] & 0x3f];
            i=0;
        }

    if (--i)
    {
        for(j=i;j<3;j++) buff1[j] = '\0';

        buff2[0] = (buff1[0] & 0xfc) >> 2;
        buff2[1] = ((buff1[0] & 0x03) << 4) + ((buff1[1] & 0xf0) >> 4);
        buff2[2] = ((buff1[1] & 0x0f) << 2) + ((buff1[2] & 0xc0) >> 6);
        buff2[3] = buff1[2] & 0x3f;

        for (j=0;j<(i+1);j++) out << encodeCharacterTable[buff2[j]];

        while(i++<3) out << '=';
    }

}

/*!
\brief Decodes base 64 character data to binary data
\param in The character data to decode
\param out The decoded data
*/
void CBase64::Decode(std::istringstream &in, std::ostream &out)
{
    char buff1[4];
    char buff2[4];
    _UINT8 i=0, j;

    while(in.readsome(&buff2[i], 1) && buff2[i] != '=')
    {
        if (++i==4)
        {
            for (i=0;i!=4;i++)
                buff2[i] = decodeCharacterTable[buff2[i]];

            out << (char)((buff2[0] << 2) + ((buff2[1] & 0x30) >> 4));
            out << (char)(((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2));
            out << (char)(((buff2[2] & 0x3) << 6) + buff2[3]);

            i=0;
        }
    }

    if (i) 
    {
        for (j=i;j<4;j++) buff2[j] = '\0';
        for (j=0;j<4;j++) buff2[j] = decodeCharacterTable[buff2[j]];

        buff1[0] = (buff2[0] << 2) + ((buff2[1] & 0x30) >> 4);
        buff1[1] = ((buff2[1] & 0xf) << 4) + ((buff2[2] & 0x3c) >> 2);
        buff1[2] = ((buff2[2] & 0x3) << 6) + buff2[3];

        for (j=0;j<(i-1); j++) out << (char)buff1[j];
    }
}

This is a decoder that is specifically written to avoid the need for a buffer, by writing directly to a putchar function. This is based on wikibook's implementation https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C

This is not as easy to use as other options above. However, it can be of use in embedded systems, where you want to dump a large file without allocating another large buffer to store the resultant base64 datauri string. (It's a pity that datauri does not let you specify the filename).

void datauriBase64EncodeBufferless(int (*putchar_fcptr)(int), const char* type_strptr, const void* data_buf, const size_t dataLength)
{
  const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  const uint8_t *data = (const uint8_t *)data_buf;
  size_t x = 0;
  uint32_t n = 0;
  int padCount = dataLength % 3;
  uint8_t n0, n1, n2, n3;

  size_t outcount = 0;
  size_t line = 0;

  putchar_fcptr((int)'d');
  putchar_fcptr((int)'a');
  putchar_fcptr((int)'t');
  putchar_fcptr((int)'a');
  putchar_fcptr((int)':');
  outcount += 5;

  while (*type_strptr != '\0')
  {
    putchar_fcptr((int)*type_strptr);
    type_strptr++;
    outcount++;
  }

  putchar_fcptr((int)';');
  putchar_fcptr((int)'b');
  putchar_fcptr((int)'a');
  putchar_fcptr((int)'s');
  putchar_fcptr((int)'e');
  putchar_fcptr((int)'6');
  putchar_fcptr((int)'4');
  putchar_fcptr((int)',');
  outcount += 8;

  /* increment over the length of the string, three characters at a time */
  for (x = 0; x < dataLength; x += 3)
  {
    /* these three 8-bit (ASCII) characters become one 24-bit number */
    n = ((uint32_t)data[x]) << 16; //parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0

    if((x+1) < dataLength)
       n += ((uint32_t)data[x+1]) << 8;//parenthesis needed, compiler depending on flags can do the shifting before conversion to uint32_t, resulting to 0

    if((x+2) < dataLength)
       n += data[x+2];

    /* this 24-bit number gets separated into four 6-bit numbers */
    n0 = (uint8_t)(n >> 18) & 63;
    n1 = (uint8_t)(n >> 12) & 63;
    n2 = (uint8_t)(n >> 6) & 63;
    n3 = (uint8_t)n & 63;

    /*
     * if we have one byte available, then its encoding is spread
     * out over two characters
     */

    putchar_fcptr((int)base64chars[n0]);
    putchar_fcptr((int)base64chars[n1]);
    outcount += 2;

    /*
     * if we have only two bytes available, then their encoding is
     * spread out over three chars
     */
    if((x+1) < dataLength)
    {
      putchar_fcptr((int)base64chars[n2]);
      outcount += 1;
    }

    /*
     * if we have all three bytes available, then their encoding is spread
     * out over four characters
     */
    if((x+2) < dataLength)
    {
      putchar_fcptr((int)base64chars[n3]);
      outcount += 1;
    }

    /* Breaking up the line so it's easier to copy and paste */
    int curr_line = (outcount/80);
    if( curr_line != line )
    {
      line = curr_line;
      putchar_fcptr((int)'\r');
      putchar_fcptr((int)'\n');
    }
  }

  /*
  * create and add padding that is required if we did not have a multiple of 3
  * number of characters available
  */
  if (padCount > 0)
  {
    for (; padCount < 3; padCount++)
    {
      putchar_fcptr((int)'=');
    }
  }

  putchar_fcptr((int)'\r');
  putchar_fcptr((int)'\n');
}

Here is the test

#include <stdio.h>
#include <stdint.h>
#include <string.h>

int main(void)
{
  char str[] = "test";
  datauriBase64EncodeBufferless(putchar, "text/plain;charset=utf-8", str, strlen(str));
  return 0;
}

Expected Output: data:text/plain;charset=utf-8;base64,dGVzdA==

I fix @ryyst answer's bug and this is a url safe version:

    #include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
 
static char encoding_table[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
                                'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
                                'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
                                'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
                                'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',
                                'o', 'p', 'q', 'r', 's', 't', 'u', 'v',
                                'w', 'x', 'y', 'z', '0', '1', '2', '3',
                                '4', '5', '6', '7', '8', '9', '-', '_'};
static char *decoding_table = NULL;
static int mod_table[] = {0, 2, 1};
 
void build_decoding_table() {
 
    decoding_table = malloc(256);
 
    for (int i = 0; i < 64; i++)
        decoding_table[(unsigned char) encoding_table[i]] = i;
}
 
 
void base64_cleanup() {
    free(decoding_table);
} 
 
char *base64_encode(const char *data,
                    size_t input_length,
                    size_t *output_length) {
 
    *output_length = 4 * ((input_length + 2) / 3);
 
    char *encoded_data = malloc(*output_length);
    if (encoded_data == NULL) return NULL;
 
    for (int i = 0, j = 0; i < input_length;) {
 
        uint32_t octet_a = i < input_length ? (unsigned char)data[i++] : 0;
        uint32_t octet_b = i < input_length ? (unsigned char)data[i++] : 0;
        uint32_t octet_c = i < input_length ? (unsigned char)data[i++] : 0;
 
        uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
 
        encoded_data[j++] = encoding_table[(triple >> 3 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 2 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 1 * 6) & 0x3F];
        encoded_data[j++] = encoding_table[(triple >> 0 * 6) & 0x3F];
    }
 
    //int i=0;
    for (int i = 0; i < mod_table[input_length % 3]; i++)
        encoded_data[*output_length - 1 - i] = '=';
    
    *output_length  = *output_length -2 + mod_table[input_length % 3];
    encoded_data[*output_length] =0;

    return encoded_data;
}
 
 
unsigned char *base64_decode(const char *data,
                             size_t input_length,
                             size_t *output_length) {
 
    if (decoding_table == NULL) build_decoding_table();
 
    if (input_length % 4 != 0) return NULL;
 
    *output_length = input_length / 4 * 3;
    if (data[input_length - 1] == '=') (*output_length)--;
    if (data[input_length - 2] == '=') (*output_length)--;
 
    unsigned char *decoded_data = malloc(*output_length);
    if (decoded_data == NULL) return NULL;
 
    for (int i = 0, j = 0; i < input_length;) {
 
        uint32_t sextet_a = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
        uint32_t sextet_b = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
        uint32_t sextet_c = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
        uint32_t sextet_d = data[i] == '=' ? 0 & i++ : decoding_table[data[i++]];
 
        uint32_t triple = (sextet_a << 3 * 6)
        + (sextet_b << 2 * 6)
        + (sextet_c << 1 * 6)
        + (sextet_d << 0 * 6);
 
        if (j < *output_length) decoded_data[j++] = (triple >> 2 * 8) & 0xFF;
        if (j < *output_length) decoded_data[j++] = (triple >> 1 * 8) & 0xFF;
        if (j < *output_length) decoded_data[j++] = (triple >> 0 * 8) & 0xFF;
    }
 
    return decoded_data;
}
 
int main(){
    
    const char * data = "Hello World! 您好!世界!";
    size_t input_size = strlen(data);
    printf("Input size: %ld \n",input_size);
    char * encoded_data = base64_encode(data, input_size, &input_size);
    printf("After size: %ld \n",input_size);
    printf("Encoded Data is: %s \n",encoded_data);
    
    size_t decode_size = strlen(encoded_data);
    printf("Output size: %ld \n",decode_size);
    unsigned char * decoded_data = base64_decode(encoded_data, decode_size, &decode_size);
    printf("After size: %ld \n",decode_size);
    printf("Decoded Data is: %s \n",decoded_data);
    return 0;
}

If you want to find a workable C solution, I believe you need this.
https://github.com/littlstar/b64.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "b64.h"

int
main (void) {
  unsigned char *str = "brian the monkey and bradley the kinkajou are friends";
  char *enc = b64_encode(str, strlen(str));

  printf("%s\n", enc); // YnJpYW4gdGhlIG1vbmtleSBhbmQgYnJhZGxleSB0aGUga2lua2Fqb3UgYXJlIGZyaWVuZHM=

  char *dec = b64_decode(enc, strlen(enc));

  printf("%s\n", dec); // brian the monkey and bradley the kinkajou are friends
  free(enc);
  free(dec);
  return 0;
}

Based on GaspardP's answer, here is simplified version of Jouni Malinen's encoder in C, I made for project I am contributing to :

/* Character list for url-safe base64 encoding */
//char cl[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";

/* Character list for url-unsafe base64 encoding */
char cl[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

/**
 * @brief Encodes s_in into base64 and writes it to s_out.
 *    @param s_in  Pointer to input buffer.
 *    @param s_out Pointer to output buffer.
 * @return Pointer to end of output buffer
 * @usage b64e("ABC",buf);
 */
char *b64e(char *s_in, char *s_out){
   int i=0;
   if (s_in[i]==0) return s_out;
   if (s_in[i+1]==0 || s_in[i+2]==0) {
      *s_out++= b64_cl[ s_in[i] >> 2 ];
      if (s_in[i+1]==0) {
         *s_out++ = b64_cl[ ( ( s_in[i]   & 0b000011 ) << 4 ) ];
      } else
      if (s_in[i+2]==0) {
         *s_out++ = b64_cl[ ( ( s_in[i]   & 0b000011 ) << 4 ) + ( ( s_in[i+1] >> 4 ) & 0b001111 ) ];
         *s_out++ = b64_cl[ ( ( s_in[i+1] & 0b001111 ) << 2 ) ];
      }
      return s_out;
   }
   *s_out++ = b64_cl[    s_in[i] >> 2 ];
   *s_out++ = b64_cl[ ( (s_in[i]   & 0b000011 ) << 4 ) + ( (s_in[i+1] >> 4) & 0b001111 ) ];
   *s_out++ = b64_cl[ ( (s_in[i+1] & 0b001111 ) << 2 ) + ( (s_in[i+2] >> 6) & 0b000011 ) ];
   *s_out++ = b64_cl[ (  s_in[i+2] & 0b111111 ) ];
   return b64e( s_in+3, s_out );
}
Related