Come faccio codificare in base64 (decodificare) in C?

Ho dati binari in una variabile char senza segno. Devo convertirli in PEM base64 in c. Ho cercato nella libreria di openssl ma non sono riuscito a trovare alcuna funzione. Qualcuno ha qualche idea?

Ecco quello che sto usando:

#include  #include  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); } 

Tieni presente che questo non esegue alcun controllo degli errori durante la decodifica: i dati codificati non di base 64 verranno elaborati.

So che questa domanda è piuttosto vecchia, ma mi sono confuso dalla quantità di soluzioni fornite – ognuna delle quali afferma di essere più veloce e migliore. Ho messo insieme un progetto su github per confrontare gli encoder e decoder base64: https://github.com/gaspardpetit/base64/

A questo punto, non mi sono limitato agli algoritmi C: se un’implementazione funziona bene in C ++, può essere facilmente trasferita a C. Sono stati eseguiti anche test utilizzando Visual Studio 2015. Se qualcuno desidera aggiornare questa risposta con i risultati di clang / gcc, sii mio ospite.

ENCODER PIÙ VELOCI : Le due implementazioni di encoder più veloci che ho trovato sono state quelle di Jouni Malinen su http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c e Apache su https://opensource.apple .com / source / QuickTimeStreamingServer / QuickTimeStreamingServer-452 / CommonUtilitiesLib / base64.c .

Ecco il tempo (in microsecondi) per codificare 32K di dati utilizzando i diversi algoritmi che ho provato fino ad ora:

 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 

(La soluzione di René Nyffenegger, accreditata in un’altra risposta a questa domanda, è elencata qui come adp_gmbh).

Ecco quello di Jouni Malinen che ho leggermente modificato per restituire uno std :: string:

 /* * Base64 encoding/decoding (RFC1341) * Copyright (c) 2005-2011, Jouni Malinen  * * 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  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; } 

I DECODER PIÙ VELOCI: ecco i risultati della decodifica e devo ammettere che sono un po 'sorpreso:

 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 

Lo snippet di Polfosol da snippet di decodifica in 64 bit in c ++ è il più veloce di un fattore di quasi 2x.

Ecco il codice per motivi di completezza:

 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; } 

Ma puoi anche farlo in openssl (comando openssl enc lo fa ….), guarda la funzione BIO_f_base64()

Mi serviva l’implementazione in C ++ su std :: string . Nessuna delle risposte soddisfaceva le mie esigenze, avevo bisogno di una semplice soluzione a due funzioni per la codifica e la decodifica, ma ero troppo pigro per scrivere il mio codice, quindi ho trovato questo:

http://www.adp-gmbh.ch/cpp/common/base64.html

I crediti per il codice vanno a René Nyffenegger.

Inserendo il codice qui sotto nel caso in cui il sito non funzioni:

base64.cpp

 /* base64.cpp and base64.h Copyright (C) 2004-2008 René Nyffenegger This source code is provided 'as-is', without any express or implied warranty. In no event will the author be held liable for any damages arising from the use of this software. Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions: 1. The origin of this source code must not be misrepresented; you must not claim that you wrote the original source code. If you use this source code in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original source code. 3. This notice may not be removed or altered from any source distribution. René Nyffenegger rene.nyffenegger@adp-gmbh.ch */ #include "base64.h" #include  static const std::string base64_chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "0123456789+/"; static inline bool is_base64(unsigned char c) { return (isalnum(c) || (c == '+') || (c == '/')); } std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) { std::string ret; int i = 0; int j = 0; unsigned char char_array_3[3]; unsigned char char_array_4[4]; while (in_len--) { char_array_3[i++] = *(bytes_to_encode++); if (i == 3) { char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) < < 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) < < 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for(i = 0; (i <4) ; i++) ret += base64_chars[char_array_4[i]]; i = 0; } } if (i) { for(j = i; j < 3; j++) char_array_3[j] = '\0'; char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; char_array_4[1] = ((char_array_3[0] & 0x03) < < 4) + ((char_array_3[1] & 0xf0) >> 4); char_array_4[2] = ((char_array_3[1] & 0x0f) < < 2) + ((char_array_3[2] & 0xc0) >> 6); char_array_4[3] = char_array_3[2] & 0x3f; for (j = 0; (j < i + 1); j++) ret += base64_chars[char_array_4[j]]; while((i++ < 3)) ret += '='; } return ret; } std::string base64_decode(std::string const& encoded_string) { int in_len = encoded_string.size(); int i = 0; int j = 0; int in_ = 0; unsigned char char_array_4[4], char_array_3[3]; std::string ret; while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { char_array_4[i++] = encoded_string[in_]; in_++; if (i ==4) { for (i = 0; i <4; i++) char_array_4[i] = base64_chars.find(char_array_4[i]); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) < < 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) < < 6) + char_array_4[3]; for (i = 0; (i < 3); i++) ret += char_array_3[i]; i = 0; } } if (i) { for (j = i; j <4; j++) char_array_4[j] = 0; for (j = 0; j <4; j++) char_array_4[j] = base64_chars.find(char_array_4[j]); char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); char_array_3[1] = ((char_array_4[1] & 0xf) < < 4) + ((char_array_4[2] & 0x3c) >> 2); char_array_3[2] = ((char_array_4[2] & 0x3) < < 6) + char_array_4[3]; for (j = 0; (j < i - 1); j++) ret += char_array_3[j]; } return ret; } 

base64.h

 #include  std::string base64_encode(unsigned char const* , unsigned int len); std::string base64_decode(std::string const& s); 

uso

 const std::string s = "test"; std::string encoded = base64_encode(reinterpret_cast(s.c_str()), s.length()); std::string decoded = base64_decode(encoded); 

libb64 ha entrambe le API C e C ++. È leggero e forse l’implementazione più rapida disponibile al pubblico. È anche una libreria di codifica stand64 indipendente stand-alone, che può essere utile se non hai bisogno di tutte le altre cose che derivano dall’uso di una libreria più grande come OpenSSL o glib.

Ecco la mia soluzione usando OpenSSL.

 /* A BASE-64 ENCODER AND DECODER USING OPENSSL */ #include  #include  //Only needed for strlen(). char *base64encode (const void *b64_encode_this, int encode_this_many_bytes){ BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO. BUF_MEM *mem_bio_mem_ptr; //Pointer to a "memory BIO" structure holding our base64 data. b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO. mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory sink BIO. BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-sink BIO chain. BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less. BIO_write(b64_bio, b64_encode_this, encode_this_many_bytes); //Records base64 encoded data. BIO_flush(b64_bio); //Flush data. Necessary for b64 encoding, because of pad characters. BIO_get_mem_ptr(mem_bio, &mem_bio_mem_ptr); //Store address of mem_bio's memory structure. BIO_set_close(mem_bio, BIO_NOCLOSE); //Permit access to mem_ptr after BIOs are destroyed. BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (ie the 1st one). BUF_MEM_grow(mem_bio_mem_ptr, (*mem_bio_mem_ptr).length + 1); //Makes space for end null. (*mem_bio_mem_ptr).data[(*mem_bio_mem_ptr).length] = '\0'; //Adds null-terminator to tail. return (*mem_bio_mem_ptr).data; //Returns base-64 encoded data. (See: "buf_mem_st" struct). } char *base64decode (const void *b64_decode_this, int decode_this_many_bytes){ BIO *b64_bio, *mem_bio; //Declares two OpenSSL BIOs: a base64 filter and a memory BIO. char *base64_decoded = calloc( (decode_this_many_bytes*3)/4+1, sizeof(char) ); //+1 = null. b64_bio = BIO_new(BIO_f_base64()); //Initialize our base64 filter BIO. mem_bio = BIO_new(BIO_s_mem()); //Initialize our memory source BIO. BIO_write(mem_bio, b64_decode_this, decode_this_many_bytes); //Base64 data saved in source. BIO_push(b64_bio, mem_bio); //Link the BIOs by creating a filter-source BIO chain. BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines. int decoded_byte_index = 0; //Index where the next base64_decoded byte should be written. while ( 0 < BIO_read(b64_bio, base64_decoded+decoded_byte_index, 1) ){ //Read byte-by-byte. decoded_byte_index++; //Increment the index until read of BIO decoded data is complete. } //Once we're done reading decoded data, BIO_read returns -1 even though there's no error. BIO_free_all(b64_bio); //Destroys all BIOs in chain, starting with b64 (ie the 1st one). return base64_decoded; //Returns base-64 decoded data with trailing null terminator. } /*Here's one way to base64 encode/decode using the base64encode() and base64decode functions.*/ int main(void){ char data_to_encode[] = "Base64 encode this string!"; //The string we will base-64 encode. int bytes_to_encode = strlen(data_to_encode); //Number of bytes in string to base64 encode. char *base64_encoded = base64encode(data_to_encode, bytes_to_encode); //Base-64 encoding. int bytes_to_decode = strlen(base64_encoded); //Number of bytes in string to base64 decode. char *base64_decoded = base64decode(base64_encoded, bytes_to_decode); //Base-64 decoding. printf("Original character string is: %s\n", data_to_encode); //Prints our initial string. printf("Base-64 encoded string is: %s\n", base64_encoded); //Prints base64 encoded string. printf("Base-64 decoded string is: %s\n", base64_decoded); //Prints base64 decoded string. free(base64_encoded); //Frees up the memory holding our base64 encoded data. free(base64_decoded); //Frees up the memory holding our base64 decoded data. } 

GNU coreutils lo ha in lib / base64. È un po ‘gonfio ma si occupa di cose come EBCDIC. Puoi anche giocare da solo, ad es.

 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; } 

Conosci tutte le persone con questi regali che non dovresti confondere "giocando da solo" con "implementare uno standard". Yeesh.

Ecco il decoder che uso da anni …

  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; } 

Ne ho scritto uno per l’uso con C ++, è molto veloce, funziona con stream, free e open source:

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

Sentiti libero di usarlo se si adatta al tuo scopo.

Modifica: Aggiunto codice in linea su richiesta.

L’aumento delle prestazioni viene ottenuto utilizzando una tabella di ricerca per la codifica e la decodifica. _UINT8 è un unsigned char sulla maggior parte dei sistemi operativi.

 /** 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]; } } 

Nel caso in cui le persone necessitino di una soluzione c ++, ho messo insieme questa soluzione OpenSSL (sia per la codifica che per la decodifica). Dovrai collegarti con la libreria “cripto” (che è OpenSSL). Questo è stato verificato per le perdite con valgrind (anche se è ansible aggiungere un codice di controllo degli errori aggiuntivo per renderlo un po ‘migliore – so che almeno la funzione di scrittura dovrebbe controllare il valore di ritorno).

 #include  #include  #include  string base64_encode( const string &str ){ BIO *base64_filter = BIO_new( BIO_f_base64() ); BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL ); BIO *bio = BIO_new( BIO_s_mem() ); BIO_set_flags( bio, BIO_FLAGS_BASE64_NO_NL ); bio = BIO_push( base64_filter, bio ); BIO_write( bio, str.c_str(), str.length() ); BIO_flush( bio ); char *new_data; long bytes_written = BIO_get_mem_data( bio, &new_data ); string result( new_data, bytes_written ); BIO_free_all( bio ); return result; } string base64_decode( const string &str ){ BIO *bio, *base64_filter, *bio_out; char inbuf[512]; int inlen; base64_filter = BIO_new( BIO_f_base64() ); BIO_set_flags( base64_filter, BIO_FLAGS_BASE64_NO_NL ); bio = BIO_new_mem_buf( (void*)str.c_str(), str.length() ); bio = BIO_push( base64_filter, bio ); bio_out = BIO_new( BIO_s_mem() ); while( (inlen = BIO_read(bio, inbuf, 512)) > 0 ){ BIO_write( bio_out, inbuf, inlen ); } BIO_flush( bio_out ); char *new_data; long bytes_written = BIO_get_mem_data( bio_out, &new_data ); string result( new_data, bytes_written ); BIO_free_all( bio ); BIO_free_all( bio_out ); return result; } 

Piccolo miglioramento del codice da ryyst (che ha ottenuto il maggior numero di voti) non è quello di utilizzare una tabella di decodifica allocata dynamicmente, ma piuttosto una tabella precostata const statica. Questo elimina l’uso del puntatore e l’inizializzazione della tabella, ed evita anche perdite di memoria se si dimentica di pulire la tabella di decodifica con base64_cleanup () (a proposito, in base64_cleanup (), dopo aver chiamato libero (decoding_table), si dovrebbe avere decoding_table = NULL, altrimenti chiamare accidentalmente base64_decode dopo base64_cleanup () si bloccherà o causerà un comportamento indeterminato). Un’altra soluzione potrebbe essere l’uso di std :: unique_ptr … ma sono soddisfatto di avere solo const char [256] nello stack ed evitare di usare i puntatori del tutto – il codice sembra più pulito e più corto in questo modo.

La tabella di decodifica è calcasting come segue:

 const 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', '+', '/' }; unsigned char decoding_table[256]; for (int i = 0; i < 256; i++) decoding_table[i] = '\0'; for (int i = 0; i < 64; i++) decoding_table[(unsigned char)encoding_table[i]] = i; for (int i = 0; i < 256; i++) cout << "0x" << (int(decoding_table[i]) < 16 ? "0" : "") << hex << int(decoding_table[i]) << (i != 255 ? "," : "") << ((i+1) % 16 == 0 ? '\n' : '\0'); cin.ignore(); 

e il codice modificato che sto usando è:

  static const 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 const unsigned char decoding_table[256] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x00, 0x00, 0x3f, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; char* base64_encode(const unsigned char *data, size_t input_length, size_t &output_length) { const int mod_table[] = { 0, 2, 1 }; output_length = 4 * ((input_length + 2) / 3); char *encoded_data = (char*)malloc(output_length); if (encoded_data == nullptr) return nullptr; 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 (input_length % 4 != 0) return nullptr; output_length = input_length / 4 * 3; if (data[input_length - 1] == '=') (output_length)--; if (data[input_length - 2] == '=') (output_length)--; unsigned char* decoded_data = (unsigned char*)malloc(output_length); if (decoded_data == nullptr) return nullptr; 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; }; 

This solution is based on schulwitz answer (encoding/decoding using OpenSSL), but it is for C++ (well, original question was about C, but there are already another C++ answers here) and it uses error checking (so it’s safer to use):

 #include  std::string base64_encode(const std::string &input) { BIO *p_bio_b64 = nullptr; BIO *p_bio_mem = nullptr; try { // make chain: p_bio_b64 < --> p_bio_mem p_bio_b64 = BIO_new(BIO_f_base64()); if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); } BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //No newlines every 64 characters or less p_bio_mem = BIO_new(BIO_s_mem()); if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); } BIO_push(p_bio_b64, p_bio_mem); // write input to chain // write sequence: input -->> p_bio_b64 -->> p_bio_mem if (BIO_write(p_bio_b64, input.c_str(), input.size()) < = 0) { throw std::runtime_error("BIO_write failed"); } if (BIO_flush(p_bio_b64) <= 0) { throw std::runtime_error("BIO_flush failed"); } // get result char *p_encoded_data = nullptr; auto encoded_len = BIO_get_mem_data(p_bio_mem, &p_encoded_data); if (!p_encoded_data) { throw std::runtime_error("BIO_get_mem_data failed"); } std::string result(p_encoded_data, encoded_len); // clean BIO_free_all(p_bio_b64); return result; } catch (...) { if (p_bio_b64) { BIO_free_all(p_bio_b64); } throw; } } std::string base64_decode(const std::string &input) { BIO *p_bio_mem = nullptr; BIO *p_bio_b64 = nullptr; try { // make chain: p_bio_b64 <--> p_bio_mem p_bio_b64 = BIO_new(BIO_f_base64()); if (!p_bio_b64) { throw std::runtime_error("BIO_new failed"); } BIO_set_flags(p_bio_b64, BIO_FLAGS_BASE64_NO_NL); //Don't require trailing newlines p_bio_mem = BIO_new_mem_buf((void*)input.c_str(), input.length()); if (!p_bio_mem) { throw std::runtime_error("BIO_new failed"); } BIO_push(p_bio_b64, p_bio_mem); // read result from chain // read sequence (reverse to write): buf < <-- p_bio_b64 <<-- p_bio_mem std::vector buf((input.size()*3/4)+1); std::string result; for (;;) { auto nread = BIO_read(p_bio_b64, buf.data(), buf.size()); if (nread < 0) { throw std::runtime_error("BIO_read failed"); } if (nread == 0) { break; } // eof result.append(buf.data(), nread); } // clean BIO_free_all(p_bio_b64); return result; } catch (...) { if (p_bio_b64) { BIO_free_all(p_bio_b64); } throw; } } 

Note that base64_decode returns empty string, if input is incorrect base64 sequence (openssl works in such way).

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)'='; } }