online - ¿Cómo codifico(descifrar) base64 en C?
https codebeautify org base64 decode (14)
Tengo datos binarios en una variable char sin signo. Necesito convertirlos a PEM base64 en c. Busqué en la biblioteca de openssl pero no pude encontrar ninguna función. ¿Alguien tiene alguna idea?
Aquí está el decodificador que he estado usando durante años ...
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;
}
Aquí está el que estoy usando:
#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);
}
Tenga en cuenta que esto no hace ninguna comprobación de errores durante la decodificación: los datos codificados que no sean base 64 serán procesados.
Aquí está mi solución usando OpenSSL.
/* A BASE-64 ENCODER AND DECODER USING OPENSSL */
#include <openssl/pem.h>
#include <string.h> //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 (i.e. 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 (i.e. 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.
}
En caso de que las personas necesiten una solución de c ++, pongo esta solución OpenSSL junta (tanto para codificar como decodificar). Tendrá que enlazar con la biblioteca "crypto" (que es OpenSSL). Esto se ha verificado para detectar fugas con valgrind (aunque podría agregar algún código adicional de comprobación de errores para hacerlo un poco mejor, sé que al menos la función de escritura debería verificar el valor de retorno).
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <stdlib.h>
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;
}
Escribí uno para usar con C ++, es muy rápido, funciona con transmisiones, libres y de código abierto:
https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/
Siéntase libre de usarlo si se adapta a su propósito.
Editar: código añadido en línea por solicitud.
El aumento de rendimiento se logra utilizando una tabla de búsqueda para codificar y decodificar. _UINT8
es un unsigned char
en la mayoría de los SO.
/** 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];
}
}
Necesitaba la implementación de C ++ trabajando en std :: string . Ninguna de las respuestas satisfizo mis necesidades, necesitaba una solución simple de dos funciones para codificar y decodificar, pero era demasiado vago para escribir mi propio código, así que encontré esto:
http://www.adp-gmbh.ch/cpp/common/base64.html
Los créditos para el código van a René Nyffenegger.
Poner el código a continuación en caso de que el sitio se caiga:
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 [email protected]
*/
#include "base64.h"
#include <iostream>
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 <string>
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<const unsigned char*>(s.c_str()), s.length());
std::string decoded = base64_decode(encoded);
Pero también puede hacerlo en openssl (el comando openssl enc
hace ...), observe la función BIO_f_base64()
Sé que esta pregunta es bastante antigua, pero estaba confundida por la cantidad de soluciones proporcionadas, cada una de ellas afirmando ser más rápida y mejor. Organicé un proyecto en github para comparar los codificadores y decoders base64: https://github.com/gaspardpetit/base64/
En este punto, no me he limitado a los algoritmos de C: si una implementación funciona bien en C ++, puede transferirse fácilmente a C. También se realizaron pruebas con Visual Studio 2015. Si alguien quiere actualizar esta respuesta con los resultados de clang / gcc, se mi invitado.
CODIFICADORES MÁS RÁPIDOS: Las dos implementaciones de codificadores más rápidas que encontré fueron Jouni Malinen en http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c y Apache en https://opensource.apple.com/source/QuickTimeStreamingServer/QuickTimeStreamingServer-452/CommonUtilitiesLib/base64.c .
Aquí está el tiempo (en microsegundos) para codificar 32K de datos usando los diferentes algoritmos que he probado hasta ahora:
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 solución de René Nyffenegger, acreditada en otra respuesta a esta pregunta, figura aquí como adp_gmbh).
Aquí está el de Jouni Malinen que modifiqué ligeramente para devolver una std :: string:
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <[email protected]>
*
* 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;
}
DECODIFICADORES MÁS RÁPIDOS: Aquí están los resultados de la decodificación y debo admitir que estoy un poco sorprendido:
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
El fragmento de Polfosol del fragmento de decodificación base64 en c ++ es el más rápido por un factor de casi 2x.
Aquí está el código por el bien de la compleción:
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;
}
Una pequeña mejora en el código de ryyst (que obtuvo la mayoría de los votos) es no usar la tabla de decodificación asignada dinámicamente sino más bien la tabla estática const precomputada. Esto elimina el uso del puntero y la inicialización de la tabla, y también evita la pérdida de memoria si se olvida limpiar la tabla de decodificación con base64_cleanup () (por cierto, en base64_cleanup (), después de llamar a free (decoding_table), uno debería tener decoding_table = NULL, de lo contrario, accidentalmente se llama a base64_decode después de que base64_cleanup () se bloquee o cause un comportamiento indeterminado). Otra solución podría ser usar std :: unique_ptr ... pero estoy satisfecho con tener const char [256] en la pila y evitar usar punteros alltogether - el código se ve más limpio y más corto de esta manera.
La tabla de decodificación se calcula de la siguiente manera:
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();
y el código modificado que estoy usando es:
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;
};
glib tiene funciones para la codificación base64: https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html
GNU coreutils lo tiene en lib / base64. Está un poco hinchado pero trata cosas como EBCDIC. También puede jugar por su cuenta, por ejemplo,
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;
}
Conozcan a todas las personas con estos regalos que no deben confundir "jugando por su cuenta" con "implementar un estándar". Yeesh.
Aquí hay una versión optimizada del codificador para la respuesta aceptada, que también admite el corte de líneas para MIME y otros protocolos (la optimización simlar se puede aplicar al decodificador):
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)''='';
}
}
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 <openssl/bio.h>
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<char> 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).