У меня есть двоичные данные в переменной без знака. Мне нужно преобразовать их в PEM base64 в c. Я посмотрел в библиотеке openssl, но не нашел никакой функции. Есть ли у кого-нибудь идеи?
Нет смысла использовать это, если есть библиотека.
Diego Woitasen
11
Вы можете пропустить "зависимости" libm и math.h, а также необходимость операций с плавающей запятой (которые медленны на некоторых аппаратных средствах), используя *output_length = ((input_length - 1) / 3) * 4 + 4;в начале base64_encode.
Fabian Henze
9
Я понимаю, что это «без проверки ошибок», но особенно замечаю, что, хотя таблица декодирования в декодере представляет собой массив из 256, поскольку char подписан на большинстве архитектур, вы действительно индексируете от -128 до 127. Любой символ с высоким установленный бит заставит вас читать за пределами выделенной памяти. Принуждение поиска данных к беззнаковому символу очищает это. Вы все еще получаете мусор вместо мусора, но вы не будете работать с ошибкой.
bitmusher
1
У вас есть проблема с выходом за пределы массива в build_decoding_table. encoding_table[64]чтобы encoding_table[255]не существует.
bobobobo
3
Декодирование также не справляется с ситуацией, когда отсутствует дополнение "=". Вместе со всеми другими ошибками довольно плохая реализация.
Lothar
56
Я знаю, что этот вопрос довольно старый, но меня смущало количество предоставленных решений - каждое из них утверждало, что оно быстрее и лучше. Я собрал проект на github для сравнения кодировщиков и декодеров base64: https://github.com/gaspardpetit/base64/
На этом этапе я не ограничивал себя алгоритмами C - если одна реализация хорошо работает на C ++, ее можно легко перенести на C. Также тесты проводились с использованием Visual Studio 2015. Если кто-то хочет обновить этот ответ результатами из clang / gcc, будь моим гостем.
(Решение Рене Ниффенеггера, указанное в другом ответе на этот вопрос, указано здесь как adp_gmbh).
Вот тот, что от Йоуни Малинена, который я немного изменил, чтобы вернуть std :: string:
/*
* Base64 encoding/decoding (RFC1341)
* Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/// 2016-12-12 - Gaspard Petit : Slightly modified to return a std::string // instead of a buffer allocated with malloc.#include<string>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;}
САМЫЕ БЫСТРЫЕ ДЕКОДЕРЫ: Вот результаты декодирования, и я должен признать, что немного удивлен:
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;}
Я действительно не думаю, что std :: string и остальные функции, которые вы использовали, являются частью ANSI C. Вопрос, запрашивающий код C и помеченный C, получает наибольшее количество голосов в C ++.
SF.
4
Цитируя себя: «Я не ограничивался алгоритмами C - если одна реализация хорошо работает на C ++, ее можно легко перенести на C». Добавьте еще один char* outStrпараметр и напишите в этот буфер вместо того, чтобы возвращать, std::stringесли хотите, это тривиально. До того, как я опубликовал это, здесь уже было два ответа C ++ с положительными голосами.
GaspardP 05
Если кому-то нужно решение, которое хорошо работает как для декодирования, так и для кодирования без необходимости брать код из двух мест, я бы выбрал версию apache для C и решение polfosol для C ++
DaedalusAlpha
@GaspardP Можно ли использовать декодирование Polfosol в кодировке Jouni?
Сэм Томас
33
Но вы также можете сделать это в openssl ( openssl encкоманда делает это ....), посмотрите на BIO_f_base64()функцию
Похоже, что OP уже использует OpenSSL по какой-то другой причине, так что это, вероятно, лучший способ сделать это.
joshk0
18
Вот мое решение с использованием 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.}
В строке «Добавляет нуль-терминатор» я получаю ошибку AddressSanitizer, что при записи происходит переполнение кучи на 1 байт.
bparker
Спасибо, я исправил ошибку, а также провел обширное тестирование со строками случайного размера случайных байтов, чтобы убедиться, что код работает так, как рекламируется. :)
Schulwitz
1
ОТЛИЧНО! Я скомпилировал его с помощьюcc -o base base.c -lssl -lcrypto . Нет ошибок Он произвел такой вывод: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 У меня есть файл, который закодирован как строка с использованием python, но когда я декодирую строку с помощью вашей функции и пытаюсь записать декодированный результат в файл (в C), я не получаю тот же файл обратно. Закодированная строка верна. `` `const unsigned char * jarFile =" <закодированный файл> "; int main () {print_version (); FILE * fp; char * out = base64decode (jarFile, strlen (jarFile)); fp = fopen ("file.jar", "wb"); if (fp == NULL) {printf ("Ошибка открытия файла"); возврат 1; } fwrite (out, sizeof (out), 1, fp); fclose (FP); бесплатно (уходит); вернуть 0; } `` `
Сэм Томас
1
@SamThomas Использование strlen работает в моем примере, потому что я создал строку, в которой существует только один нулевой терминатор (и он находится в конце строки). См.: Tutorialspoint.com/cprogramming/c_strings.htm Чтение jarFile с помощью strlen завершится ошибкой, потому что, вероятно, в середине вашего двоичного файла есть нулевой терминатор, испорченный значением bytes_to_decode. См .: stackoverflow.com/questions/24596189/… Определите размер файла другим способом: stackoverflow.com/questions/238603/…
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится.
Все рабочие важны
16
libb64 имеет API C и C ++. Это легкая и, пожалуй, самая быстрая общедоступная реализация. Это также выделенная автономная библиотека кодирования base64, которая может быть приятной, если вам не нужны все остальные вещи, которые возникают при использовании более крупной библиотеки, такой как OpenSSL или glib.
Примечание для libb64: BUFFERSIZE определяется в файле make, поэтому, если вы не используете make / cmake, вам нужно будет вручную определить его в файлах заголовков, чтобы он скомпилировался. Работает / кратко протестирован VS2012
Tom
3
Как сказал Том: #define BUFFERSIZE 16777216вы можете заменить на 65536, если вам нужен меньший буфер.
jyz
1
Осторожно! После часа отладки я понял, что libb64 предполагает, что charон подписан в целевой системе ... Это проблема, поскольку base64_decode_valueможет возвращать отрицательное число, которое затем преобразуется в char.
Noir
Обратите внимание, что реализация sourceforge добавляет новые строки, которые не поддерживаются повсеместно. Вилка по BuLogics на GitHub удаляет их, и я генерироваться запрос тянуть на основе ваших чрезвычайно полезным нахождения, @Noir.
щелочность
Хотя эта ссылка может дать ответ на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если ссылка на страницу изменится.
Все рабочие важны
14
GNU coreutils имеет его в lib / base64. Он немного раздут, но работает с такими вещами, как EBCDIC. Вы также можете поиграть самостоятельно, например,
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;}
По этим подаркам узнайте всех, что вам не следует путать «игру в одиночку» с «внедрением стандарта». Иш.
Кроме того, '+'это 62 и '/'63 в PEM base64, как этого требует OP. Вот список вариантов кодировки base64 . Я не вижу варианта кодировки base64 с порядком используемых вами символов. Но математика, лежащая в основе алгоритма, верна.
Патрик
2
Как уже было сказано: будьте осторожны, этот алгоритм несовместим с общим base64
Cerber
А как насчет кодирования?
Geremia
14
Мне нужна была реализация C ++, работающая над std :: string . Ни один из ответов не удовлетворил мои потребности, мне нужно было простое двухфункциональное решение для кодирования и декодирования, но мне было лень писать собственный код, поэтому я нашел следующее:
Введем приведенный ниже код на случай, если сайт отключится:
base64.cpp
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/#include"base64.h"#include<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;}
Это очень простая операция, которая гарантирует, что буфер dest установлен в NULL на случай, если вызывающий объект не сделал этого перед вызовом, и если, возможно, декодирование не удалось, возвращаемый буфер будет нулевой длины. Я не сказал, что отлаживал, отслеживал и профилировал эту процедуру, это всего лишь одна из тех процедур, которые я использовал в течение многих лет. :) Когда я смотрю на это сейчас, на самом деле это не обязательно, так почему бы нам не назвать это «упражнением для читателя»? хе-хе .. Может просто отредактирую. Спасибо за указание на это!
LarryF
3
ваша UnBase64функция может скомпрометировать память после буфера назначения, если этот буфер имеет точный размер, необходимый для декодирования строки в кодировке base 64. Возьмем, к примеру, простой случай, когда вы пытаетесь декодировать следующую строку в кодировке base 64 «BQ ==» в один БАЙТ, т.е. unsigned char Result = 0; UnBase64(&Result, "BQ==", 4); это повредит стек!
Майк Динеску
3
Да, вызвала неприятную ошибку в нашем приложении. Не рекомендую
Харальд Маассен
Привет, Ларри, спасибо, что поделился кодом. Это очень полезно!
Федерико
4
Если людям нужно решение на C ++, я объединяю это решение OpenSSL (как для кодирования, так и для декодирования). Вам нужно будет установить связь с «криптографической» библиотекой (то есть OpenSSL). Это было проверено на утечки с помощью valgrind (хотя вы могли бы добавить дополнительный код проверки ошибок, чтобы сделать его немного лучше - я знаю, по крайней мере, функция записи должна проверять возвращаемое значение).
#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 необходимо указать начальную, а не хвостовую часть вашей био-цепочки (то есть base64_filter). В вашей текущей реализации есть утечка памяти.
Schulwitz
@schulwitz В какой линии течь? Bio_free_all освобождает всю цепочку.
Homer6
4
Я написал один для использования с C ++, он очень быстрый, работает с потоками, бесплатно и с открытым исходным кодом:
Не стесняйтесь использовать его, если он соответствует вашим целям.
Изменить: добавлен код, встроенный по запросу.
Повышение производительности достигается за счет использования таблицы поиска для кодирования и декодирования. _UINT8есть в unsigned charбольшинстве ОС.
/** 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 Сейчас я добавил встроенный пример и комментарий, почему это быстро, спасибо.
2
Небольшое улучшение кода от ryyst (получившего наибольшее количество голосов) заключается в том, что он не использует динамически распределенную таблицу декодирования, а, скорее, предварительно вычисляет статическую константную таблицу. Это исключает использование указателя и инициализацию таблицы, а также предотвращает утечку памяти, если кто-то забывает очистить таблицу декодирования с помощью base64_cleanup () (кстати, в base64_cleanup () после вызова free (decoding_table) нужно иметь decoding_table = NULL, иначе случайный вызов base64_decode после base64_cleanup () приведет к сбою или неопределенному поведению). Другим решением может быть использование std :: unique_ptr ... но я доволен тем, что в стеке есть const char [256], и вообще избегаю использования указателей - в этом случае код выглядит чище и короче.
Таблица декодирования рассчитывается следующим образом:
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();
Это не так просто использовать, как другие варианты, указанные выше. Однако его можно использовать во встроенных системах, где вы хотите выгрузить большой файл без выделения другого большого буфера для хранения результирующей строки данных base64. (Жалко, что datauri не позволяет указать имя файла).
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');}
Это решение основано на ответе Шульвица (кодирование / декодирование с использованием OpenSSL), но оно предназначено для C ++ (ну, исходный вопрос был о C, но здесь уже есть другие ответы C ++) и использует проверку ошибок (так что безопаснее использовать) :
#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;}}
Обратите внимание, что base64_decode возвращает пустую строку, если введена неправильная последовательность base64 (openssl работает таким образом).
хм ... использование библиотеки openssl для декодирования / кодирования base64 требует больше строк кода, чем прямая реализация (лучший ответ в этом вопросе) ...
anton_rh
-2
Вот оптимизированная версия кодировщика для принятого ответа, которая также поддерживает разрыв строки для MIME и других протоколов (к декодеру может применяться упрощенная оптимизация):
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)'=';}}
Ответы:
Вот тот, который я использую:
Имейте в виду, что это не выполняет никакой проверки ошибок при декодировании - данные в кодировке, отличной от base 64, будут обрабатываться.
источник
*output_length = ((input_length - 1) / 3) * 4 + 4;
в начале base64_encode.build_decoding_table
.encoding_table[64]
чтобыencoding_table[255]
не существует.Я знаю, что этот вопрос довольно старый, но меня смущало количество предоставленных решений - каждое из них утверждало, что оно быстрее и лучше. Я собрал проект на github для сравнения кодировщиков и декодеров base64: https://github.com/gaspardpetit/base64/
На этом этапе я не ограничивал себя алгоритмами C - если одна реализация хорошо работает на C ++, ее можно легко перенести на C. Также тесты проводились с использованием Visual Studio 2015. Если кто-то хочет обновить этот ответ результатами из clang / gcc, будь моим гостем.
САМЫЕ БЫСТРЫЕ КОДЕРЫ: Двумя самыми быстрыми реализациями кодировщика, которые я нашел, были Jouni Malinen по адресу http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c и Apache по адресу https://opensource.apple .com / источник / QuickTimeStreamingServer / QuickTimeStreamingServer-452 / CommonUtilitiesLib / base64.c .
Вот время (в микросекундах) для кодирования 32K данных с использованием различных алгоритмов, которые я тестировал до сих пор:
(Решение Рене Ниффенеггера, указанное в другом ответе на этот вопрос, указано здесь как adp_gmbh).
Вот тот, что от Йоуни Малинена, который я немного изменил, чтобы вернуть std :: string:
САМЫЕ БЫСТРЫЕ ДЕКОДЕРЫ: Вот результаты декодирования, и я должен признать, что немного удивлен:
Фрагмент кода Polfosol из фрагмента кода декодирования base64 на C ++ является самым быстрым почти в 2 раза.
Вот код для полноты:
источник
char* outStr
параметр и напишите в этот буфер вместо того, чтобы возвращать,std::string
если хотите, это тривиально. До того, как я опубликовал это, здесь уже было два ответа C ++ с положительными голосами.Но вы также можете сделать это в openssl (
openssl enc
команда делает это ....), посмотрите наBIO_f_base64()
функциюисточник
Вот мое решение с использованием OpenSSL.
источник
cc -o base base.c -lssl -lcrypto
. Нет ошибок Он произвел такой вывод:Original character string is: Base64 encode this string! Base-64 encoded string is: QmFzZTY0IGVuY29kZSB0aGlzIHN0cmluZyE= Base-64 decoded string is: Base64 encode this string!
glib имеет функции для кодирования base64: https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html
источник
libb64 имеет API C и C ++. Это легкая и, пожалуй, самая быстрая общедоступная реализация. Это также выделенная автономная библиотека кодирования base64, которая может быть приятной, если вам не нужны все остальные вещи, которые возникают при использовании более крупной библиотеки, такой как OpenSSL или glib.
источник
#define BUFFERSIZE 16777216
вы можете заменить на 65536, если вам нужен меньший буфер.char
он подписан в целевой системе ... Это проблема, посколькуbase64_decode_value
может возвращать отрицательное число, которое затем преобразуется в char.GNU coreutils имеет его в lib / base64. Он немного раздут, но работает с такими вещами, как EBCDIC. Вы также можете поиграть самостоятельно, например,
По этим подаркам узнайте всех, что вам не следует путать «игру в одиночку» с «внедрением стандарта». Иш.
источник
'+'
это 62 и'/'
63 в PEM base64, как этого требует OP. Вот список вариантов кодировки base64 . Я не вижу варианта кодировки base64 с порядком используемых вами символов. Но математика, лежащая в основе алгоритма, верна.Мне нужна была реализация C ++, работающая над std :: string . Ни один из ответов не удовлетворил мои потребности, мне нужно было простое двухфункциональное решение для кодирования и декодирования, но мне было лень писать собственный код, поэтому я нашел следующее:
http://www.adp-gmbh.ch/cpp/common/base64.html
Кредиты на код принадлежат Рене Ниффенеггеру.
Введем приведенный ниже код на случай, если сайт отключится:
base64.cpp
base64.h
использование
источник
Вот декодер, которым я пользуюсь много лет ...
источник
UnBase64
функция может скомпрометировать память после буфера назначения, если этот буфер имеет точный размер, необходимый для декодирования строки в кодировке base 64. Возьмем, к примеру, простой случай, когда вы пытаетесь декодировать следующую строку в кодировке base 64 «BQ ==» в один БАЙТ, т.е.unsigned char Result = 0; UnBase64(&Result, "BQ==", 4);
это повредит стек!Если людям нужно решение на C ++, я объединяю это решение OpenSSL (как для кодирования, так и для декодирования). Вам нужно будет установить связь с «криптографической» библиотекой (то есть OpenSSL). Это было проверено на утечки с помощью valgrind (хотя вы могли бы добавить дополнительный код проверки ошибок, чтобы сделать его немного лучше - я знаю, по крайней мере, функция записи должна проверять возвращаемое значение).
источник
Я написал один для использования с C ++, он очень быстрый, работает с потоками, бесплатно и с открытым исходным кодом:
https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/
Не стесняйтесь использовать его, если он соответствует вашим целям.
Изменить: добавлен код, встроенный по запросу.
Повышение производительности достигается за счет использования таблицы поиска для кодирования и декодирования.
_UINT8
есть вunsigned char
большинстве ОС.источник
Небольшое улучшение кода от ryyst (получившего наибольшее количество голосов) заключается в том, что он не использует динамически распределенную таблицу декодирования, а, скорее, предварительно вычисляет статическую константную таблицу. Это исключает использование указателя и инициализацию таблицы, а также предотвращает утечку памяти, если кто-то забывает очистить таблицу декодирования с помощью base64_cleanup () (кстати, в base64_cleanup () после вызова free (decoding_table) нужно иметь decoding_table = NULL, иначе случайный вызов base64_decode после base64_cleanup () приведет к сбою или неопределенному поведению). Другим решением может быть использование std :: unique_ptr ... но я доволен тем, что в стеке есть const char [256], и вообще избегаю использования указателей - в этом случае код выглядит чище и короче.
Таблица декодирования рассчитывается следующим образом:
и модифицированный код, который я использую:
источник
Это декодер, который специально написан, чтобы избежать необходимости в буфере, путем записи непосредственно в функцию putchar. Это основано на реализации викибука https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C
Это не так просто использовать, как другие варианты, указанные выше. Однако его можно использовать во встроенных системах, где вы хотите выгрузить большой файл без выделения другого большого буфера для хранения результирующей строки данных base64. (Жалко, что datauri не позволяет указать имя файла).
Вот тест
Ожидаемый результат:
data:text/plain;charset=utf-8;base64,dGVzdA==
источник
EVP_EncodeBlock
ИEVP_DecodeBlock
функции делают его очень легко:источник
Это решение основано на ответе Шульвица (кодирование / декодирование с использованием OpenSSL), но оно предназначено для C ++ (ну, исходный вопрос был о C, но здесь уже есть другие ответы C ++) и использует проверку ошибок (так что безопаснее использовать) :
Обратите внимание, что base64_decode возвращает пустую строку, если введена неправильная последовательность base64 (openssl работает таким образом).
источник
Вот оптимизированная версия кодировщика для принятого ответа, которая также поддерживает разрыв строки для MIME и других протоколов (к декодеру может применяться упрощенная оптимизация):
источник