Tengo datos binarios en una variable de caracteres sin signo. Necesito convertirlos a PEM base64 en c. Busqué en la biblioteca openssl pero no pude encontrar ninguna función. ¿Alguien tiene alguna idea?
Puede omitir la "dependencia" de libm y math.h, así como la necesidad de operaciones de coma flotante (que son lentas en algún hardware), al usar *output_length = ((input_length - 1) / 3) * 4 + 4;al principio de base64_encode.
Fabian Henze
9
Me doy cuenta de que "no hay verificación de errores", pero especialmente note que aunque la tabla de decodificación en el decodificador es una matriz de 256, dado que char está firmado en la mayoría de las arquitecturas, realmente está indexando de -128 a 127. Cualquier carácter con el valor alto el conjunto de bits hará que lea fuera de la memoria asignada. Obligar a la búsqueda de datos a ser un carácter sin signo lo borra. Todavía obtienes basura en busca de basura, pero no segfault.
bitmusher
1
Tiene un problema de matriz fuera de límites en build_decoding_table. encoding_table[64]para encoding_table[255]no existir
bobobobo
3
La decodificación tampoco maneja la situación en la que falta el relleno "=". Junto con todos los demás errores, una implementación bastante mala.
Lothar
56
Sé que esta pregunta es bastante antigua, pero me confundía la cantidad de soluciones proporcionadas, cada una de ellas afirmaba ser más rápida y mejor. Arme un proyecto en github para comparar los codificadores y decodificadores 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 ++, se puede transferir fácilmente a C. También se realizaron pruebas con Visual Studio 2015. Si alguien quiere actualizar esta respuesta con resultados de clang / GCC, sé mi invitado.
(La solución de René Nyffenegger, acreditada en otra respuesta a esta pregunta, aparece aquí como adp_gmbh).
Aquí está el de Jouni Malinen que modifiqué ligeramente para devolver un 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>staticconstunsignedchar 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(constunsignedchar*src,size_t len){unsignedchar*out,*pos;constunsignedchar*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 =(unsignedchar*)&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:
staticconstint 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(constvoid* data,constsize_t len){unsignedchar* p =(unsignedchar*)data;int pad = len >0&&(len %4|| p[len -1]=='=');constsize_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;}
Realmente no creo que std :: string y el resto de funciones que usaste sean parte de ANSI C. La pregunta que pide el código C y etiquetada C, obtiene la respuesta más votada en C ++.
SF.
44
Citando a mí mismo "No me he limitado a los algoritmos de C: si una implementación funciona bien en C ++, se puede transferir fácilmente a C". Agregue otro char* outStrparámetro y escriba en ese búfer en lugar de devolver un std::stringsi lo desea, es trivial. Antes de publicar esto, ya había dos respuestas de C ++ con votos a favor aquí.
GaspardP
Si uno quiere una solución que funcione bien para decodificar y codificar sin tener que tomar el código de dos lugares, elegiría la versión apache para C y la solución de polfosol para C ++
DaedalusAlpha
@GaspardP ¿Se puede usar la decodificación de Polfosol en la codificación de Jouni?
Sam Thomas
33
Pero también puedes hacerlo en openssl (el openssl enccomando lo hace ...), mira la BIO_f_base64()función
Parece que el OP ya está utilizando OpenSSL por alguna otra razón, por lo que esta es probablemente la mejor manera de hacerlo.
joshk0
18
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 (constvoid*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 (constvoid*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 la línea "Agrega un terminador nulo" aparece un error de AddressSanitizer que indica que la escritura desborda el montón en 1 byte.
bparker
Gracias, he corregido el error, además de realizar pruebas exhaustivas con cadenas de bytes aleatorios de tamaño aleatorio para garantizar que el código funcione como se anuncia. :)
schulwitz
1
¡BONITO! Lo compilé concc -o base base.c -lssl -lcrypto . Sin errores. Produjo esta salida:Original character string is: Base64 encode this string! Base-64 encoded string is: QmFzZTY0IGVuY29kZSB0aGlzIHN0cmluZyE= Base-64 decoded string is: Base64 encode this string!
clearlight
@schulwitz Tengo un archivo que está codificado como una cadena usando Python, pero cuando decodifico la cadena usando su función e intento escribir el resultado decodificado en un archivo (en C) no obtengo el mismo archivo. La cadena codificada es correcta. `` `const unsigned char * jarFile =" <archivo codificado> "; int main () {print_version (); ARCHIVO * fp; char * out = base64decode (jarFile, strlen (jarFile)); fp = fopen ("archivo.jar", "wb"); if (fp == NULL) {printf ("Error al abrir archivo"); retorno 1; } fwrite (out, sizeof (out), 1, fp); fclose (fp); gratis (fuera); devuelve 0; } ``
Sam Thomas
1
@SamThomas Usar strlen funciona en mi ejemplo porque hice una cadena donde solo existe un terminador nulo (y está al final de la cadena). Consulte: tutorialspoint.com/cprogramming/c_strings.htm La lectura en jarFile con strlen fallará, porque es probable que exista un terminador nulo en el medio de su archivo binario, desordenando el valor bytes_to_decode. Consulte: stackoverflow.com/questions/24596189/… Encuentre el tamaño de su archivo de una manera diferente: stackoverflow.com/questions/238603/…
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
Todos los trabajadores son esenciales
16
libb64 tiene las API C y C ++. Es liviano y quizás la implementación pública más rápida disponible. También es una biblioteca de codificación base64 independiente y dedicada, que puede ser agradable si no necesita todas las otras cosas que provienen del uso de una biblioteca más grande como OpenSSL o glib.
Nota sobre libb64: BUFFERSIZE se define en un archivo make, por lo que si no usa make / cmake, deberá definirlo manualmente en los archivos de encabezado para que se compile. Works / brevemente probado VS2012
Tom
3
Como dijo Tom: #define BUFFERSIZE 16777216puede reemplazar a 65536 si necesita un búfer más pequeño.
jyz
1
¡Tener cuidado! Después de una hora de depuración, descubrí que libb64 asume que charestá firmado en el sistema de destino ... Esto es un problema ya que base64_decode_valuepodría devolver un número negativo que luego se convierte en char.
Si bien este enlace puede responder la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace como referencia. Las respuestas de solo enlace pueden volverse inválidas si la página vinculada cambia.
Todos los trabajadores son esenciales
14
GNU coreutils lo tiene en lib / base64. Está un poco hinchado pero se ocupa de cosas como EBCDIC. También puedes jugar solo, por ejemplo,
char base64_digit (n)unsigned n;{if(n <10)return n -'0';elseif(n <10+26)return n -'a';elseif(n <10+26+26)return n -'A';else assert(0);return0;}unsignedchar base64_decode_digit(char c){switch(c){case'=':return62;case'.':return63;default:if(isdigit(c))return c -'0';elseif(islower(c))return c -'a'+10;elseif(isupper(c))return c -'A'+10+26;else assert(0);}return0xff;}unsigned base64_decode(char*s){char*p;unsigned n =0;for(p = s;*p; p++)
n =64* n + base64_decode_digit(*p);return n;}
Conozca a todas las personas con estos regalos que no debe confundir "jugar por su cuenta" con "implementar un estándar". Yeesh
Además, '+'es 62 y '/'es 63 en PEM base64 según lo solicitado por OP. Aquí hay una lista de variantes de codificación base64 . No veo una variante de codificación base64 con el orden de los caracteres que usa. Pero la matemática detrás del algoritmo es correcta.
Patrick
2
Como ya se dijo: tenga cuidado, este algoritmo no es compatible con la base común64
Cerber
¿Qué pasa con la codificación?
Geremia
14
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:
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>staticconst std::string base64_chars ="ABCDEFGHIJKLMNOPQRSTUVWXYZ""abcdefghijklmnopqrstuvwxyz""0123456789+/";staticinlinebool is_base64(unsignedchar c){return(isalnum(c)||(c =='+')||(c =='/'));}
std::string base64_encode(unsignedcharconst* bytes_to_encode,unsignedint in_len){
std::string ret;int i =0;int j =0;unsignedchar char_array_3[3];unsignedchar 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;unsignedchar 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;}
Es solo una operación muy simple que asegura que el búfer de destino esté configurado en NULL en caso de que la persona que llama no lo haya hecho antes de la llamada, y si tal vez la decodificación fallara, el búfer devuelto sería de longitud cero. No dije que depuré, rastreé y perfilé esta rutina, es solo una que he estado usando durante años. :) Cuando lo miro ahora, realmente no necesita estar allí, así que, ¿por qué no lo llamamos un "ejercicio para el lector"? jeje .. Tal vez solo lo edite. ¡Gracias por mencionarlo!
LarryF
3
su UnBase64función puede comprometer la memoria después del búfer de destino, si ese búfer es del tamaño exacto requerido para decodificar la cadena codificada en base 64. Tomemos, por ejemplo, el caso simple en el que intenta decodificar la siguiente cadena codificada en base 64 "BQ ==", en un solo BYTE, es decir unsigned char Result = 0; UnBase64(&Result, "BQ==", 4); , ¡dañará la pila!
Mike Dinescu
3
Sí, causó un error desagradable en nuestra aplicación. No lo recomiendo
Harald Maassen
Hola Larry, gracias por compartir tu código. ¡Es muy útil!
Federico
4
En caso de que las personas necesiten una solución de c ++, pongo esta solución OpenSSL (tanto para codificar como para decodificar). Tendrá que vincular con la biblioteca "crypto" (que es OpenSSL). Esto se ha verificado en busca de fugas con valgrind (aunque podría agregar un código adicional de verificación de errores para hacerlo un poco mejor; sé que al menos la función de escritura debe 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;}
BIO_free_all necesita especificar la cabeza, no la cola, de su cadena biológica (es decir, el filtro base64). Su implementación actual tiene una pérdida de memoria.
schulwitz
@schulwitz ¿Qué línea tiene la fuga? Bio_free_all libera toda la cadena.
Homer6
4
Escribí uno para usar con C ++, es muy rápido, funciona con transmisiones, gratis y de código abierto:
Siéntase libre de usarlo si se ajusta a su propósito.
Editar: Código agregado en línea por solicitud.
El aumento de rendimiento se logra mediante el uso de una tabla de búsqueda para codificar y decodificar. _UINT8es un unsigned charen la mayoría de los sistemas operativos.
/** Static Base64 character encoding lookup table */constcharCBase64::encodeCharacterTable[65]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";/** Static Base64 character decoding lookup table */constcharCBase64::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
*/voidCBase64::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
*/voidCBase64::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];}}
@cpburnz He agregado un ejemplo en línea ahora y un comentario de por qué es rápido, gracias.
2
Una pequeña mejora en el código de ryyst (que obtuvo la mayoría de los votos) es no usar una tabla de decodificación asignada dinámicamente, sino una tabla precomputada const estática. 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 gratis (decoding_table), uno debería tener decoding_table = NULL, de lo contrario, llamar accidentalmente a base64_decode después de base64_cleanup () se bloqueará o provocará 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 el uso de punteros por completo: 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:
constchar 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','+','/'};unsignedchar decoding_table[256];for(int i =0; i <256; i++)
decoding_table[i]='\0';for(int i =0; i <64; i++)
decoding_table[(unsignedchar)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();
Esto no es tan fácil de usar como otras opciones anteriores. Sin embargo, puede ser útil en sistemas integrados, donde desea volcar un archivo grande sin asignar otro búfer grande para almacenar la cadena de datauri base64 resultante. (Es una pena que datauri no le permita especificar el nombre del archivo).
void datauriBase64EncodeBufferless(int(*putchar_fcptr)(int),constchar* type_strptr,constvoid* data_buf,constsize_t dataLength){constchar base64chars[]="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";constuint8_t*data =(constuint8_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 0if((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 0if((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');}
Esta solución se basa en la respuesta schulwitz (codificación / decodificación usando OpenSSL), pero es para C ++ (bueno, la pregunta original era sobre C, pero ya hay otras respuestas de C ++ aquí) y utiliza la comprobación de errores (por lo que es más seguro de usar) :
#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_memif(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 resultchar*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;}}
Tenga en cuenta que base64_decode devuelve una cadena vacía, si la entrada es una secuencia base64 incorrecta (openssl funciona de esa manera).
hm ... el uso de la biblioteca openssl para decodificar / codificar base64 requiere más líneas de código que la implementación directa (la mejor respuesta en esta pregunta) ...
anton_rh
-2
Aquí hay una versión optimizada del codificador para la respuesta aceptada, que también admite salto de línea para MIME y otros protocolos (se puede aplicar una optimización similar al decodificador):
char*base64_encode(constunsignedchar*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 charschar*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 tripletfor(; 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 bytesif(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)'=';}}
Respuestas:
Aquí está el que estoy usando:
Tenga en cuenta que esto no verifica los errores durante la decodificación: los datos codificados que no sean de base 64 se procesarán.
fuente
*output_length = ((input_length - 1) / 3) * 4 + 4;
al principio de base64_encode.build_decoding_table
.encoding_table[64]
paraencoding_table[255]
no existirSé que esta pregunta es bastante antigua, pero me confundía la cantidad de soluciones proporcionadas, cada una de ellas afirmaba ser más rápida y mejor. Arme un proyecto en github para comparar los codificadores y decodificadores 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 ++, se puede transferir fácilmente a C. También se realizaron pruebas con Visual Studio 2015. Si alguien quiere actualizar esta respuesta con resultados de clang / GCC, sé mi invitado.
CODIFICADORES MÁS RÁPIDOS: Las dos implementaciones de codificador más rápidas que encontré fueron las de 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 .
Este es el momento (en microsegundos) para codificar 32K de datos utilizando los diferentes algoritmos que he probado hasta ahora:
(La solución de René Nyffenegger, acreditada en otra respuesta a esta pregunta, aparece aquí como adp_gmbh).
Aquí está el de Jouni Malinen que modifiqué ligeramente para devolver un std :: string:
DECODIFICADORES MÁS RÁPIDOS: Aquí están los resultados de la decodificación y debo admitir que estoy un poco sorprendido:
El fragmento de Polfosol del fragmento de decodificación base64 en c ++ es el más rápido en un factor de casi 2x.
Aquí está el código en aras de la integridad:
fuente
char* outStr
parámetro y escriba en ese búfer en lugar de devolver unstd::string
si lo desea, es trivial. Antes de publicar esto, ya había dos respuestas de C ++ con votos a favor aquí.Pero también puedes hacerlo en openssl (el
openssl enc
comando lo hace ...), mira laBIO_f_base64()
funciónfuente
Aquí está mi solución usando OpenSSL.
fuente
cc -o base base.c -lssl -lcrypto
. Sin errores. Produjo esta salida:Original character string is: Base64 encode this string! Base-64 encoded string is: QmFzZTY0IGVuY29kZSB0aGlzIHN0cmluZyE= Base-64 decoded string is: Base64 encode this string!
glib tiene funciones para la codificación base64: https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html
fuente
libb64 tiene las API C y C ++. Es liviano y quizás la implementación pública más rápida disponible. También es una biblioteca de codificación base64 independiente y dedicada, que puede ser agradable si no necesita todas las otras cosas que provienen del uso de una biblioteca más grande como OpenSSL o glib.
fuente
#define BUFFERSIZE 16777216
puede reemplazar a 65536 si necesita un búfer más pequeño.char
está firmado en el sistema de destino ... Esto es un problema ya quebase64_decode_value
podría devolver un número negativo que luego se convierte en char.GNU coreutils lo tiene en lib / base64. Está un poco hinchado pero se ocupa de cosas como EBCDIC. También puedes jugar solo, por ejemplo,
Conozca a todas las personas con estos regalos que no debe confundir "jugar por su cuenta" con "implementar un estándar". Yeesh
fuente
'+'
es 62 y'/'
es 63 en PEM base64 según lo solicitado por OP. Aquí hay una lista de variantes de codificación base64 . No veo una variante de codificación base64 con el orden de los caracteres que usa. Pero la matemática detrás del algoritmo es correcta.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.h
Uso
fuente
Aquí está el decodificador que he estado usando durante años ...
fuente
UnBase64
función puede comprometer la memoria después del búfer de destino, si ese búfer es del tamaño exacto requerido para decodificar la cadena codificada en base 64. Tomemos, por ejemplo, el caso simple en el que intenta decodificar la siguiente cadena codificada en base 64 "BQ ==", en un solo BYTE, es decirunsigned char Result = 0; UnBase64(&Result, "BQ==", 4);
, ¡dañará la pila!En caso de que las personas necesiten una solución de c ++, pongo esta solución OpenSSL (tanto para codificar como para decodificar). Tendrá que vincular con la biblioteca "crypto" (que es OpenSSL). Esto se ha verificado en busca de fugas con valgrind (aunque podría agregar un código adicional de verificación de errores para hacerlo un poco mejor; sé que al menos la función de escritura debe verificar el valor de retorno).
fuente
Escribí uno para usar con C ++, es muy rápido, funciona con transmisiones, gratis y de código abierto:
https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/
Siéntase libre de usarlo si se ajusta a su propósito.
Editar: Código agregado en línea por solicitud.
El aumento de rendimiento se logra mediante el uso de una tabla de búsqueda para codificar y decodificar.
_UINT8
es ununsigned char
en la mayoría de los sistemas operativos.fuente
Una pequeña mejora en el código de ryyst (que obtuvo la mayoría de los votos) es no usar una tabla de decodificación asignada dinámicamente, sino una tabla precomputada const estática. 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 gratis (decoding_table), uno debería tener decoding_table = NULL, de lo contrario, llamar accidentalmente a base64_decode después de base64_cleanup () se bloqueará o provocará 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 el uso de punteros por completo: 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:
y el código modificado que estoy usando es:
fuente
Este es un decodificador que está escrito específicamente para evitar la necesidad de un buffer, escribiendo directamente en una función putchar. Esto se basa en la implementación de wikibook https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C
Esto no es tan fácil de usar como otras opciones anteriores. Sin embargo, puede ser útil en sistemas integrados, donde desea volcar un archivo grande sin asignar otro búfer grande para almacenar la cadena de datauri base64 resultante. (Es una pena que datauri no le permita especificar el nombre del archivo).
Aqui esta la prueba
Rendimiento esperado:
data:text/plain;charset=utf-8;base64,dGVzdA==
fuente
Las funciones
EVP_EncodeBlock
y loEVP_DecodeBlock
hacen muy fácil:fuente
Esta solución se basa en la respuesta schulwitz (codificación / decodificación usando OpenSSL), pero es para C ++ (bueno, la pregunta original era sobre C, pero ya hay otras respuestas de C ++ aquí) y utiliza la comprobación de errores (por lo que es más seguro de usar) :
Tenga en cuenta que base64_decode devuelve una cadena vacía, si la entrada es una secuencia base64 incorrecta (openssl funciona de esa manera).
fuente
Aquí hay una versión optimizada del codificador para la respuesta aceptada, que también admite salto de línea para MIME y otros protocolos (se puede aplicar una optimización similar al decodificador):
fuente