Eu tenho dados binários em uma variável de caracteres não assinada. Eu preciso convertê-los para PEM base64 em c. Eu olhei na biblioteca openssl, mas não consegui encontrar nenhuma função. Alguém tem alguma ideia?
Não faz sentido usar isso se houver uma biblioteca.
Diego Woitasen
11
Você pode pular a libm e math.h "dependência", bem como a necessidade de operações de ponto flutuante (que são lentas em algum hardware), usando *output_length = ((input_length - 1) / 3) * 4 + 4;no início de base64_encode.
Fabian Henze
9
Sei que é "sem verificação de erro", mas observe especialmente que, embora a tabela de decodificação no decodificador seja uma matriz de 256, uma vez que char é assinado na maioria das arquiteturas, você está realmente indexando de -128 a 127. Qualquer caractere com alta O conjunto de bits fará com que você leia fora da memória alocada. Forçar a pesquisa de dados a ser um caractere não assinado limpa isso. Você ainda obtém o lixo para o lixo, mas não faz o segfault.
Bitmusher
1
Você tem um problema fora dos limites da matriz build_decoding_table. encoding_table[64]para encoding_table[255]não existir.
bobobobo
3
A decodificação também não lida com a situação em que o preenchimento "=" está ausente. Juntamente com todos os outros erros, uma implementação bastante ruim.
Lothar
56
Sei que essa pergunta é bastante antiga, mas estava ficando confusa com a quantidade de soluções fornecidas - cada uma delas alegando ser mais rápida e melhor. Eu montei um projeto no github para comparar os codificadores e decodificadores base64: https://github.com/gaspardpetit/base64/
Neste ponto, não me limitei aos algoritmos C - se uma implementação tiver um bom desempenho em C ++, ela pode ser facilmente transportada para C. Também foram realizados testes usando o Visual Studio 2015. Se alguém quiser atualizar esta resposta com resultados de clang / gcc, seja meu convidado.
(A solução de René Nyffenegger, creditada em outra resposta a esta pergunta, está listada aqui como adp_gmbh).
Aqui está o Jouni Malinen que eu modifiquei levemente para retornar um 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;}
DECODERS MAIS RÁPIDOS: Aqui estão os resultados da decodificação e devo admitir que estou um pouco surpreso:
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;}
Eu realmente não acho que std :: string e o restante das funções que você usou fazem parte do ANSI C. A pergunta do código C e da etiqueta C recebe a resposta mais votada em C ++.
SF.
4
Citando a mim mesmo "Não me limitei aos algoritmos C - se uma implementação tiver um bom desempenho em C ++, ela pode ser facilmente suportada em C". Adicione outro char* outStrparâmetro e escreva para esse buffer em vez de retornar um, std::stringse desejar, é simples de fazer. Antes de postar isso, já havia duas respostas em C ++ com upvotes aqui.
precisa saber é o seguinte
Se alguém quiser uma solução que funciona bem tanto para codificação e decodificação sem ter que levar o código de dois lugares que eu iria escolher a versão apache para solução de C e polfosol para C ++
DaedalusAlpha
@GaspardP A decodificação de Polfosol pode ser usada na codificação de Jouni?
Sam Thomas
33
Mas você também pode fazê-lo no openssl ( openssl enccomando o faz ....), veja a BIO_f_base64()função
Parece que o OP já está usando o OpenSSL por algum outro motivo, portanto essa é provavelmente a melhor maneira de fazer isso.
precisa saber é o seguinte
18
Aqui está a minha solução usando o 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.}
Na linha "Adiciona um terminador nulo", recebo um erro AddressSanitizer que a gravação excede o heap em 1 byte.
bparker
Obrigado, corrigi o erro, além de fazer testes extensivos com cadeias de bytes aleatórios de tamanho aleatório para garantir que o código funcione conforme anunciado. :)
Schulwitz
1
LEGAIS! Eu compilei comcc -o base base.c -lssl -lcrypto . Sem erros. Ele produziu esta saída: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 Eu tenho um arquivo que é codificado como uma string usando python, mas quando decodifico a string usando sua função e tento gravar o resultado decodificado em um arquivo (em C), não recebo o mesmo arquivo de volta. A sequência codificada está correta. `` `const unsigned char * jarFile =" <arquivo codificado> "; int main () {versão_versão (); ARQUIVO * fp; char * out = base64decode (jarFile, strlen (jarFile)); fp = fopen ("arquivo.jar", "wb"); if (fp == NULL) {printf ("Falha na abertura do arquivo"); retornar 1; } fwrite (fora, tamanho de (fora), 1, fp); fclose (fp); livre (fora); retornar 0; } `` `
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada.
Todos os trabalhadores têm Essencial
16
A libb64 possui APIs C e C ++. É leve e talvez a implementação mais rápida disponível publicamente. É também uma biblioteca de codificação base64 autônoma dedicada, o que pode ser bom se você não precisar de todas as outras coisas resultantes do uso de uma biblioteca maior, como OpenSSL ou glib.
Nota sobre libb64: BUFFERSIZE é definido em um arquivo make, portanto, se você não usar o make / cmake, precisará defini-lo manualmente nos arquivos de cabeçalho para compilar. Funciona / VS2012 brevemente testado
Tom
3
Como Tom disse: #define BUFFERSIZE 16777216você pode substituir para 65536 se precisar de um buffer menor.
JYZ
1
Cuidado! Após uma hora de depuração, descobri que a libb64 assume que charestá assinado no sistema de destino ... Esse é um problema, pois base64_decode_valuepode retornar um número negativo que é convertido em char.
Noir
Observe que a implementação do sourceforge adiciona novas linhas que não são universalmente suportadas. Um fork do BuLogics no github os remove, e eu geramos uma solicitação de recebimento com base em sua descoberta extremamente útil, @Noir.
alkalinity
Embora esse link possa responder à pergunta, é melhor incluir aqui as partes essenciais da resposta e fornecer o link para referência. As respostas somente para links podem se tornar inválidas se a página vinculada for alterada.
Todos os trabalhadores têm Essencial
14
O coreutils do GNU possui em lib / base64. É um pouco inchado, mas lida com coisas como EBCDIC. Você também pode brincar sozinho, por exemplo,
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;}
Conheça todas as pessoas com esses presentes que você não deve confundir "brincar sozinho" com "implementar um padrão". Yeesh.
Além disso, '+'é 62 e'/' é 63 no PEM base64, conforme solicitado pelo OP. Aqui está uma lista de variantes de codificação base64 . Não vejo uma variante de codificação base64 com a ordem dos caracteres que você usa. Mas a matemática por trás do algoritmo está correta.
28412 Patrick
2
Como já foi dito: tenha cuidado, esse algoritmo não é compatível com o base64 comum
Cerber
E a codificação?
Geremia 30/01
14
Eu precisava da implementação C ++ trabalhando em std :: string . Nenhuma das respostas satisfez minhas necessidades, eu precisava de uma solução simples de duas funções para codificação e decodificação, mas estava com preguiça de escrever meu próprio código, então achei o seguinte:
Os créditos para o código vão para René Nyffenegger.
Colocando o código abaixo no caso de o site cair:
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;}
É apenas uma operação muito simples que garante que o buffer de destino seja definido como NULL, caso o chamador não tenha feito isso antes da chamada e, se a decodificação falhar, o buffer retornado terá tamanho zero. Eu não disse que depurei, rastreiei e criei o perfil dessa rotina, é apenas uma que eu uso há anos. :) Quando eu olho para ele agora, ele realmente não precisa estar lá, então, por que não o chamamos de "exercício para o leitor?" hehe .. Talvez eu apenas edite isso. Obrigado por apontar isso!
LarryF
3
sua UnBase64função pode comprometer a memória após o buffer de destino, se esse buffer tiver o tamanho exato necessário para decodificar a string codificada na base 64. Tomemos, por exemplo, o caso simples em que você tenta decodificar a seguinte sequência codificada de base 64 "BQ ==", em um único BYTE, ou seja unsigned char Result = 0; UnBase64(&Result, "BQ==", 4); , corromperá a pilha!
Mike Dinescu
3
Sim, causou um bug desagradável em nosso aplicativo. Não recomendo.
Harald Maassen
Olá Larry, obrigado por compartilhar seu código. É muito útil!
Federico
4
Caso as pessoas precisem de uma solução c ++, eu montei essa solução OpenSSL (para codificar e decodificar). Você precisará vincular à biblioteca "crypto" (que é OpenSSL). Isso foi verificado quanto a vazamentos com o valgrind (embora você possa adicionar algum código adicional de verificação de erro para torná-lo um pouco melhor - eu sei que pelo menos a função de gravação deve verificar o 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;}
O BIO_free_all precisa especificar a cabeça - e não a cauda - da sua cadeia de bio (ou seja, o base64_filter). Sua implementação atual tem um vazamento de memória.
Schulwitz
@schulwitz Qual linha tem o vazamento? Bio_free_all libera toda a cadeia.
Homer6
4
Eu escrevi um para uso com C ++, é muito rápido, trabalha com fluxos, gratuitos e de código aberto:
Sinta-se livre para usá-lo se ele se adequar ao seu objetivo.
Editar: Adicionado código embutido por solicitação.
O aumento de desempenho é obtido usando uma tabela de pesquisa para codificação e decodificação. _UINT8é um unsigned charna maioria dos sistemas operacionais.
/** 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 Adicionei exemplo inline agora e um comentário do porquê é rápido, obrigado.
2
Uma pequena melhoria no código do ryyst (que obteve o maior número de votos) é não usar a tabela de decodificação alocada dinamicamente, mas a tabela pré-computada const estática. Isso elimina o uso de ponteiro e inicialização da tabela e também evita vazamento de memória se alguém esquecer de limpar a tabela de decodificação com base64_cleanup () (a propósito, em base64_cleanup (), depois de chamar free (decoding_table), deve-se ter decoding_table = NULL, caso contrário, chamar acidentalmente base64_decode após base64_cleanup () travar ou causar comportamento indeterminado). Outra solução poderia ser usar std :: unique_ptr ... mas estou satisfeito em ter const char [256] na pilha e evitar o uso de ponteiros - o código parece mais limpo e mais curto dessa maneira.
A tabela de decodificação é calculada da seguinte maneira:
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();
Este é um decodificador que é escrito especificamente para evitar a necessidade de um buffer, gravando diretamente em uma função putchar. Isso é baseado na implementação do wikibook https://en.wikibooks.org/wiki/Algorithm_Implementation/Misc
Não é tão fácil de usar quanto as outras opções acima. No entanto, pode ser útil em sistemas incorporados, nos quais você deseja despejar um arquivo grande sem alocar outro buffer grande para armazenar a sequência base de dados base64 resultante. (É uma pena que o datauri não permita que você especifique o nome do arquivo).
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 solução é baseada na resposta schulwitz (codificação / decodificação usando OpenSSL), mas é para C ++ (bem, a pergunta original era sobre C, mas já existem outras respostas em C ++ aqui) e usa a verificação de erros (por isso é mais 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;}}
Observe que base64_decode retorna uma string vazia, se a entrada estiver incorreta na sequência base64 (o openssl funciona dessa maneira).
hm ... usar a biblioteca openssl para decodificar / codificar base64 requer mais linhas de código do que a implementação direta (melhor resposta nesta pergunta) ...
anton_rh
-2
Aqui está uma versão otimizada do codificador para a resposta aceita, que também suporta quebra de linha para MIME e outros protocolos (a otimização simlar pode ser aplicada ao 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)'=';}}
Respostas:
Aqui está o que eu estou usando:
Lembre-se de que isso não realiza nenhuma verificação de erro durante a decodificação - os dados codificados que não são de base 64 serão processados.
fonte
*output_length = ((input_length - 1) / 3) * 4 + 4;
no início de base64_encode.build_decoding_table
.encoding_table[64]
paraencoding_table[255]
não existir.Sei que essa pergunta é bastante antiga, mas estava ficando confusa com a quantidade de soluções fornecidas - cada uma delas alegando ser mais rápida e melhor. Eu montei um projeto no github para comparar os codificadores e decodificadores base64: https://github.com/gaspardpetit/base64/
Neste ponto, não me limitei aos algoritmos C - se uma implementação tiver um bom desempenho em C ++, ela pode ser facilmente transportada para C. Também foram realizados testes usando o Visual Studio 2015. Se alguém quiser atualizar esta resposta com resultados de clang / gcc, seja meu convidado.
ENCODERS MAIS RÁPIDOS: As duas implementações de codificador mais rápidas que encontrei foram as de Jouni Malinen em http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c e o Apache em https://opensource.apple .com / source / QuickTimeStreamingServer / QuickTimeStreamingServer-452 / CommonUtilitiesLib / base64.c .
Aqui está o tempo (em microssegundos) para codificar 32K de dados usando os diferentes algoritmos que testei até agora:
(A solução de René Nyffenegger, creditada em outra resposta a esta pergunta, está listada aqui como adp_gmbh).
Aqui está o Jouni Malinen que eu modifiquei levemente para retornar um std :: string:
DECODERS MAIS RÁPIDOS: Aqui estão os resultados da decodificação e devo admitir que estou um pouco surpreso:
O fragmento de Polfosol do fragmento de decodificação base64 em c ++ é o mais rápido por um fator de quase 2x.
Aqui está o código para completar:
fonte
char* outStr
parâmetro e escreva para esse buffer em vez de retornar um,std::string
se desejar, é simples de fazer. Antes de postar isso, já havia duas respostas em C ++ com upvotes aqui.Mas você também pode fazê-lo no openssl (
openssl enc
comando o faz ....), veja aBIO_f_base64()
funçãofonte
Aqui está a minha solução usando o OpenSSL.
fonte
cc -o base base.c -lssl -lcrypto
. Sem erros. Ele produziu esta saída:Original character string is: Base64 encode this string! Base-64 encoded string is: QmFzZTY0IGVuY29kZSB0aGlzIHN0cmluZyE= Base-64 decoded string is: Base64 encode this string!
glib tem funções para codificação base64: https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html
fonte
A libb64 possui APIs C e C ++. É leve e talvez a implementação mais rápida disponível publicamente. É também uma biblioteca de codificação base64 autônoma dedicada, o que pode ser bom se você não precisar de todas as outras coisas resultantes do uso de uma biblioteca maior, como OpenSSL ou glib.
fonte
#define BUFFERSIZE 16777216
você pode substituir para 65536 se precisar de um buffer menor.char
está assinado no sistema de destino ... Esse é um problema, poisbase64_decode_value
pode retornar um número negativo que é convertido em char.O coreutils do GNU possui em lib / base64. É um pouco inchado, mas lida com coisas como EBCDIC. Você também pode brincar sozinho, por exemplo,
Conheça todas as pessoas com esses presentes que você não deve confundir "brincar sozinho" com "implementar um padrão". Yeesh.
fonte
'+'
é 62 e'/'
é 63 no PEM base64, conforme solicitado pelo OP. Aqui está uma lista de variantes de codificação base64 . Não vejo uma variante de codificação base64 com a ordem dos caracteres que você usa. Mas a matemática por trás do algoritmo está correta.Eu precisava da implementação C ++ trabalhando em std :: string . Nenhuma das respostas satisfez minhas necessidades, eu precisava de uma solução simples de duas funções para codificação e decodificação, mas estava com preguiça de escrever meu próprio código, então achei o seguinte:
http://www.adp-gmbh.ch/cpp/common/base64.html
Os créditos para o código vão para René Nyffenegger.
Colocando o código abaixo no caso de o site cair:
base64.cpp
base64.h
Uso
fonte
Aqui está o decodificador que uso há anos ...
fonte
UnBase64
função pode comprometer a memória após o buffer de destino, se esse buffer tiver o tamanho exato necessário para decodificar a string codificada na base 64. Tomemos, por exemplo, o caso simples em que você tenta decodificar a seguinte sequência codificada de base 64 "BQ ==", em um único BYTE, ou sejaunsigned char Result = 0; UnBase64(&Result, "BQ==", 4);
, corromperá a pilha!Caso as pessoas precisem de uma solução c ++, eu montei essa solução OpenSSL (para codificar e decodificar). Você precisará vincular à biblioteca "crypto" (que é OpenSSL). Isso foi verificado quanto a vazamentos com o valgrind (embora você possa adicionar algum código adicional de verificação de erro para torná-lo um pouco melhor - eu sei que pelo menos a função de gravação deve verificar o valor de retorno).
fonte
Eu escrevi um para uso com C ++, é muito rápido, trabalha com fluxos, gratuitos e de código aberto:
https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/
Sinta-se livre para usá-lo se ele se adequar ao seu objetivo.
Editar: Adicionado código embutido por solicitação.
O aumento de desempenho é obtido usando uma tabela de pesquisa para codificação e decodificação.
_UINT8
é umunsigned char
na maioria dos sistemas operacionais.fonte
Uma pequena melhoria no código do ryyst (que obteve o maior número de votos) é não usar a tabela de decodificação alocada dinamicamente, mas a tabela pré-computada const estática. Isso elimina o uso de ponteiro e inicialização da tabela e também evita vazamento de memória se alguém esquecer de limpar a tabela de decodificação com base64_cleanup () (a propósito, em base64_cleanup (), depois de chamar free (decoding_table), deve-se ter decoding_table = NULL, caso contrário, chamar acidentalmente base64_decode após base64_cleanup () travar ou causar comportamento indeterminado). Outra solução poderia ser usar std :: unique_ptr ... mas estou satisfeito em ter const char [256] na pilha e evitar o uso de ponteiros - o código parece mais limpo e mais curto dessa maneira.
A tabela de decodificação é calculada da seguinte maneira:
e o código modificado que estou usando é:
fonte
Este é um decodificador que é escrito especificamente para evitar a necessidade de um buffer, gravando diretamente em uma função putchar. Isso é baseado na implementação do wikibook https://en.wikibooks.org/wiki/Algorithm_Implementation/Misc
Não é tão fácil de usar quanto as outras opções acima. No entanto, pode ser útil em sistemas incorporados, nos quais você deseja despejar um arquivo grande sem alocar outro buffer grande para armazenar a sequência base de dados base64 resultante. (É uma pena que o datauri não permita que você especifique o nome do arquivo).
Aqui está o teste
Saída esperada:
data:text/plain;charset=utf-8;base64,dGVzdA==
fonte
As funções
EVP_EncodeBlock
eEVP_DecodeBlock
facilitam muito:fonte
Esta solução é baseada na resposta schulwitz (codificação / decodificação usando OpenSSL), mas é para C ++ (bem, a pergunta original era sobre C, mas já existem outras respostas em C ++ aqui) e usa a verificação de erros (por isso é mais seguro de usar) :
Observe que base64_decode retorna uma string vazia, se a entrada estiver incorreta na sequência base64 (o openssl funciona dessa maneira).
fonte
Aqui está uma versão otimizada do codificador para a resposta aceita, que também suporta quebra de linha para MIME e outros protocolos (a otimização simlar pode ser aplicada ao decodificador):
fonte