How do I base64 encode (decode) in C?

后端 未结 17 1496
孤城傲影
孤城傲影 2020-11-22 06:27

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

相关标签:
17条回答
  • 2020-11-22 06:40

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

    0 讨论(0)
  • 2020-11-22 06:42

    Here is an optimized version of encoder for the accepted answer, that also supports line-breaking for MIME and other protocols (simlar optimization can be applied to the decoder):

     char *base64_encode(const unsigned char *data,
                        size_t input_length,
                        size_t *output_length,
                        bool addLineBreaks)
    
        *output_length = 4 * ((input_length + 2) / 3);
        if (addLineBreaks) *output_length += *output_length / 38; //  CRLF after each 76 chars
    
        char *encoded_data = malloc(*output_length);
        if (encoded_data == NULL) return NULL;
    
        UInt32 octet_a;
        UInt32 octet_b;
        UInt32 octet_c;
        UInt32 triple;
        int lineCount = 0;
        int sizeMod = size - (size % 3); // check if there is a partial triplet
        // adding all octet triplets, before partial last triplet
        for (; offset < sizeMod; ) 
        {
            octet_a = data[offset++];
            octet_b = data[offset++];
            octet_c = data[offset++];
    
            triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
    
            encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
            encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
            encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
            encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];
            if (addLineBreaks)
            {
                if (++lineCount == 19)
                {
                    encoded_data[mBufferPos++] = 13;
                    encoded_data[mBufferPos++] = 10;
                    lineCount = 0;
                }
            }
        }
    
        // last bytes
        if (sizeMod < size)
        {
            octet_a = data[offset++]; // first octect always added
            octet_b = offset < size ? data[offset++] : (UInt32)0; // conditional 2nd octet
            octet_c = (UInt32)0; // last character is definitely padded
    
            triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c;
    
            encoded_data[mBufferPos++] = encoding_table[(triple >> 3 * 6) & 0x3F];
            encoded_data[mBufferPos++] = encoding_table[(triple >> 2 * 6) & 0x3F];
            encoded_data[mBufferPos++] = encoding_table[(triple >> 1 * 6) & 0x3F];
            encoded_data[mBufferPos++] = encoding_table[(triple >> 0 * 6) & 0x3F];
    
            // add padding '='
            sizeMod = size % 3; 
            // last character is definitely padded
            encoded_data[mBufferPos - 1] = (byte)'=';
            if (sizeMod == 1) encoded_data[mBufferPos - 2] = (byte)'=';
        }
     }
    
    0 讨论(0)
  • 2020-11-22 06:43

    I know this question is quite old, but I was getting confused by the amount of solutions provided - each one of them claiming to be faster and better. I put together a project on github to compare the base64 encoders and decoders: https://github.com/gaspardpetit/base64/

    At this point, I have not limited myself to C algorithms - if one implementation performs well in C++, it can easily be backported to C. Also tests were conducted using Visual Studio 2015. If somebody wants to update this answer with results from clang/gcc, be my guest.

    FASTEST ENCODERS: The two fastest encoder implementations I found were Jouni Malinen's at http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c and the Apache at https://opensource.apple.com/source/QuickTimeStreamingServer/QuickTimeStreamingServer-452/CommonUtilitiesLib/base64.c.

    Here is the time (in microseconds) to encode 32K of data using the different algorithms I have tested up to now:

    jounimalinen                25.1544
    apache                      25.5309
    NibbleAndAHalf              38.4165
    internetsoftwareconsortium  48.2879
    polfosol                    48.7955
    wikibooks_org_c             51.9659
    gnome                       74.8188
    elegantdice                 118.899
    libb64                      120.601
    manuelmartinez              120.801
    arduino                     126.262
    daedalusalpha               126.473
    CppCodec                    151.866
    wikibooks_org_cpp           343.2
    adp_gmbh                    381.523
    LihO                        406.693
    libcurl                     3246.39
    user152949                  4828.21
    

    (René Nyffenegger's solution, credited in another answer to this question, is listed here as adp_gmbh).

    Here is the one from Jouni Malinen that I slightly modified to return a std::string:

    /*
    * Base64 encoding/decoding (RFC1341)
    * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
    *
    * This software may be distributed under the terms of the BSD license.
    * See README for more details.
    */
    
    // 2016-12-12 - Gaspard Petit : Slightly modified to return a std::string 
    // instead of a buffer allocated with malloc.
    
    #include <string>
    
    static const unsigned char base64_table[65] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    
    /**
    * base64_encode - Base64 encode
    * @src: Data to be encoded
    * @len: Length of the data to be encoded
    * @out_len: Pointer to output length variable, or %NULL if not used
    * Returns: Allocated buffer of out_len bytes of encoded data,
    * or empty string on failure
    */
    std::string base64_encode(const unsigned char *src, size_t len)
    {
        unsigned char *out, *pos;
        const unsigned char *end, *in;
    
        size_t olen;
    
        olen = 4*((len + 2) / 3); /* 3-byte blocks to 4-byte */
    
        if (olen < len)
            return std::string(); /* integer overflow */
    
        std::string outStr;
        outStr.resize(olen);
        out = (unsigned char*)&outStr[0];
    
        end = src + len;
        in = src;
        pos = out;
        while (end - in >= 3) {
            *pos++ = base64_table[in[0] >> 2];
            *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
            *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
            *pos++ = base64_table[in[2] & 0x3f];
            in += 3;
        }
    
        if (end - in) {
            *pos++ = base64_table[in[0] >> 2];
            if (end - in == 1) {
                *pos++ = base64_table[(in[0] & 0x03) << 4];
                *pos++ = '=';
            }
            else {
                *pos++ = base64_table[((in[0] & 0x03) << 4) |
                    (in[1] >> 4)];
                *pos++ = base64_table[(in[1] & 0x0f) << 2];
            }
            *pos++ = '=';
        }
    
        return outStr;
    }
    

    FASTEST DECODERS: Here are the decoding results and I must admit that I am a bit surprised:

    polfosol                    45.2335
    wikibooks_org_c             74.7347
    apache                      77.1438
    libb64                      100.332
    gnome                       114.511
    manuelmartinez              126.579
    elegantdice                 138.514
    daedalusalpha               151.561
    jounimalinen                206.163
    arduino                     335.95
    wikibooks_org_cpp           350.437
    CppCodec                    526.187
    internetsoftwareconsortium  862.833
    libcurl                     1280.27
    LihO                        1852.4
    adp_gmbh                    1934.43
    user152949                  5332.87
    

    Polfosol's snippet from base64 decode snippet in c++ is the fastest by a factor of almost 2x.

    Here is the code for the sake of completeness:

    static const int B64index[256] = { 0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
    0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0, 62, 63, 62, 62, 63, 52, 53, 54, 55,
    56, 57, 58, 59, 60, 61,  0,  0,  0,  0,  0,  0,  0,  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,  0,
    0,  0,  0, 63,  0, 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 };
    
    std::string b64decode(const void* data, const size_t len)
    {
        unsigned char* p = (unsigned char*)data;
        int pad = len > 0 && (len % 4 || p[len - 1] == '=');
        const size_t L = ((len + 3) / 4 - pad) * 4;
        std::string str(L / 4 * 3 + pad, '\0');
    
        for (size_t i = 0, j = 0; i < L; i += 4)
        {
            int n = B64index[p[i]] << 18 | B64index[p[i + 1]] << 12 | B64index[p[i + 2]] << 6 | B64index[p[i + 3]];
            str[j++] = n >> 16;
            str[j++] = n >> 8 & 0xFF;
            str[j++] = n & 0xFF;
        }
        if (pad)
        {
            int n = B64index[p[L]] << 18 | B64index[p[L + 1]] << 12;
            str[str.size() - 1] = n >> 16;
    
            if (len > L + 2 && p[L + 2] != '=')
            {
                n |= B64index[p[L + 2]] << 6;
                str.push_back(n >> 8 & 0xFF);
            }
        }
        return str;
    }
    
    0 讨论(0)
  • 2020-11-22 06:46

    Here's the one I'm using:

    #include <stdint.h>
    #include <stdlib.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};
    
    
    char *base64_encode(const unsigned 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];
        }
    
        for (int i = 0; i < mod_table[input_length % 3]; i++)
            encoded_data[*output_length - 1 - i] = '=';
    
        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;
    }
    
    
    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);
    }
    

    Keep in mind that this doesn't do any error-checking while decoding - non base 64 encoded data will get processed.

    0 讨论(0)
  • 2020-11-22 06:48

    glib has functions for base64 encoding: https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html

    0 讨论(0)
  • 2020-11-22 06:48

    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.

    0 讨论(0)
提交回复
热议问题