J'ai des données binaires dans une variable char non signée. Je dois les convertir en PEM base64 dans c. J'ai regardé dans la bibliothèque openssl mais je n'ai trouvé aucune fonction. Quelqu'un at-il une idée?
Cela n'a aucun sens d'utiliser ceci s'il y a une bibliothèque.
Diego Woitasen
11
Vous pouvez ignorer les "dépendances" libm et math.h ainsi que le besoin d'opérations en virgule flottante (qui sont lentes sur certains matériels), en utilisant *output_length = ((input_length - 1) / 3) * 4 + 4;au début de base64_encode.
Fabian Henze
9
Je me rends compte qu'il n'y a "pas de vérification d'erreur", mais surtout remarquez que bien que la table de décodage dans le décodeur soit un tableau de 256, puisque char est signé sur la plupart des architectures, vous indexez vraiment de -128 à 127. Tout caractère avec le haut bit set vous fera lire en dehors de la mémoire allouée. Forcer la recherche de données à être un caractère non signé efface cela. Vous sortez toujours des ordures pour les ordures, mais vous ne serez pas en panne.
bitmusher
1
Vous avez un problème de tableau hors limites dans build_decoding_table. encoding_table[64]pour encoding_table[255]n'existent pas.
bobobobo
3
Le décodage ne gère pas non plus la situation où le remplissage "=" est manquant. Avec toutes les autres erreurs, une très mauvaise implémentation.
Lothar
56
Je sais que cette question est assez ancienne, mais je devenais confus par la quantité de solutions proposées - chacune d'elles prétendant être plus rapide et meilleure. J'ai monté un projet sur github pour comparer les encodeurs et décodeurs base64: https://github.com/gaspardpetit/base64/
À ce stade, je ne me suis pas limité aux algorithmes C - si une implémentation fonctionne bien en C ++, elle peut facilement être rétroportée vers C. Des tests ont également été effectués à l'aide de Visual Studio 2015. Si quelqu'un veut mettre à jour cette réponse avec les résultats de clang / gcc, soyez mon invité.
(La solution de René Nyffenegger, créditée dans une autre réponse à cette question, est répertoriée ici sous le nom adp_gmbh).
Voici celui de Jouni Malinen que j'ai légèrement modifié pour renvoyer 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;}
DÉCODEUR LES PLUS RAPIDES: Voici les résultats du décodage et je dois avouer que je suis un peu surpris:
L'extrait de Polfosol de l'extrait de code de décodage base64 en c ++ est le plus rapide d'un facteur presque 2x.
Voici le code par souci d'exhaustivité:
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;}
Je ne pense vraiment pas que std :: string et le reste des fonctions que vous avez utilisées font partie de l'ANSI C.
SF.
4
En me citant "Je ne me suis pas limité aux algorithmes C - si une implémentation fonctionne bien en C ++, elle peut facilement être rétroportée en C". Ajoutez un autre char* outStrparamètre et écrivez dans ce tampon au lieu de renvoyer un std::stringsi vous le souhaitez, c'est trivial à faire. Avant de publier ceci, il y avait déjà deux réponses C ++ avec des votes positifs ici.
GaspardP
Si l'on veut une solution qui fonctionne bien pour le décodage et l'encodage sans avoir à prendre du code à deux endroits, je choisirais la version apache pour C et la solution de polfosol pour C ++
DaedalusAlpha
@GaspardP Le décodage de Polfosol peut-il être utilisé sur l'encodage de Jouni?
Sam Thomas
33
Mais vous pouvez aussi le faire dans openssl (la openssl enccommande le fait ....), regardez la BIO_f_base64()fonction
Il semble que l'OP utilise déjà OpenSSL pour une autre raison, c'est donc probablement la meilleure façon de procéder.
joshk0
18
Voici ma solution utilisant 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.}
Sur la ligne «Ajoute un terminateur nul», j'obtiens une erreur AddressSanitizer indiquant que l'écriture déborde le tas de 1 octet.
bparker
Merci, j'ai corrigé l'erreur, en plus de faire des tests approfondis avec des chaînes de taille aléatoire d'octets aléatoires pour m'assurer que le code fonctionne comme annoncé. :)
schulwitz
1
AGRÉABLE! Je l'ai compilé aveccc -o base base.c -lssl -lcrypto . Aucune erreur. Il a produit cette sortie: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 J'ai un fichier qui est encodé sous forme de chaîne en utilisant python, mais quand je décode la chaîne en utilisant votre fonction et que j'essaye d'écrire le résultat décodé dans un fichier (en C), je ne récupère pas le même fichier. La chaîne codée est correcte. `` `const unsigned char * jarFile =" <fichier encodé> "; int main () {print_version (); FICHIER * fp; char * out = base64decode (jarFile, strlen (jarFile)); fp = fopen ("fichier.jar", "wb"); if (fp == NULL) {printf ("L'ouverture du fichier a échoué"); return 1; } fwrite (out, sizeof (out), 1, fp); fclose (fp); gratuit (sortie); return 0; } `` ``
Sam Thomas
1
@SamThomas L'utilisation de strlen fonctionne dans mon exemple car j'ai créé une chaîne où un seul terminateur nul existe (et c'est à la fin de la chaîne). Voir: tutorialspoint.com/cprogramming/c_strings.htm La lecture dans jarFile avec strlen échouera, car un terminateur nul existe probablement au milieu de votre fichier binaire, perturbant la valeur bytes_to_decode. Voir: stackoverflow.com/questions/24596189/… Trouvez la taille de votre fichier d'une manière différente: stackoverflow.com/questions/238603/…
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses aux liens uniquement peuvent devenir invalides si la page liée change.
Tous les travailleurs sont essentiels
16
libb64 a à la fois des API C et C ++. Il est léger et peut-être l'implémentation publique la plus rapide. C'est aussi une bibliothèque d'encodage base64 autonome dédiée, ce qui peut être utile si vous n'avez pas besoin de tous les autres éléments provenant de l'utilisation d'une bibliothèque plus grande telle que OpenSSL ou glib.
Note sur la libb64: BUFFERSIZE est défini dans un fichier make, donc si vous n'utilisez pas make / cmake, vous devrez le définir manuellement dans les fichiers d'en-tête pour qu'il se compile. Fonctionne / brièvement testé VS2012
Tom
3
Comme Tom l'a dit: #define BUFFERSIZE 16777216vous pouvez remplacer par 65536 si vous avez besoin d'un tampon plus petit.
jyz
1
Il faut se méfier! Après une heure de débogage, j'ai compris que libb64 suppose qu'il charest signé sur le système cible ... C'est un problème car il base64_decode_valuepourrait renvoyer un nombre négatif qui est ensuite converti en char.
Noir
Notez que l'implémentation de sourceforge ajoute des nouvelles lignes qui ne sont pas universellement prises en charge. Un fork de BuLogics sur github les supprime, et j'ai généré une pull request basée sur votre découverte extrêmement utile, @Noir.
alcalinité
Bien que ce lien puisse répondre à la question, il est préférable d'inclure les parties essentielles de la réponse ici et de fournir le lien pour référence. Les réponses aux liens uniquement peuvent devenir invalides si la page liée change.
Tous les travailleurs sont essentiels
14
GNU coreutils l' a dans lib / base64. C'est un peu gonflé mais traite de trucs comme EBCDIC. Vous pouvez également jouer seul, par exemple
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;}
Sachez toutes les personnes par ces cadeaux que vous ne devez pas confondre «jouer seul» avec «mettre en œuvre une norme». Yeesh.
Aussi, '+'est 62 et '/'est 63 dans PEM base64 comme demandé par OP. Voici une liste de variantes d'encodage base64 . Je ne vois pas de variante d'encodage base64 avec l'ordre des caractères que vous utilisez. Mais le calcul derrière l'algorithme est correct.
Patrick
2
Comme déjà dit: attention cet algorithme n'est pas compatible avec la base64 commune
Cerber
Qu'en est-il de l'encodage?
Geremia
14
J'avais besoin d'une implémentation C ++ fonctionnant sur std :: string . Aucune des réponses ne répondait à mes besoins, j'avais besoin d'une solution simple à deux fonctions pour l'encodage et le décodage, mais j'étais trop paresseux pour écrire mon propre code, alors j'ai trouvé ceci:
Mettre le code ci-dessous en cas de panne du site:
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;}
C'est juste une opération très simple qui s'assure que le tampon dest est défini sur NULL au cas où l'appelant ne le ferait pas avant l'appel, et si peut-être le décodage échouait, le tampon retourné serait de longueur nulle. Je n'ai pas dit que j'avais débogué, tracé et profilé cette routine, c'est juste celle que j'utilise depuis des années. :) Quand je le regarde maintenant, il n'a vraiment pas besoin d'être là, alors pourquoi ne pas l'appeler un «exercice pour le lecteur»? hehe .. Peut-être que je vais simplement le modifier. Merci de l'avoir signalé!
LarryF
3
votre UnBase64fonction peut compromettre la mémoire après le tampon dest, si ce tampon a la taille exacte requise pour décoder la chaîne encodée en base 64. Prenons par exemple le cas simple où vous essayez de décoder la chaîne suivante encodée en base 64 "BQ ==", en un seul BYTE c'est-à-dire que unsigned char Result = 0; UnBase64(&Result, "BQ==", 4); cela corrompra la pile!
Mike Dinescu
3
Ouais, a causé un bug désagréable dans notre application. Je ne recommande pas.
Harald Maassen
Salut Larry, merci de partager votre code. C'est très utile!
Federico
4
Au cas où les gens auraient besoin d'une solution C ++, je mets cette solution OpenSSL ensemble (à la fois pour l'encodage et le décodage). Vous devrez créer un lien avec la bibliothèque "crypto" (qui est OpenSSL). Cela a été vérifié pour les fuites avec valgrind (bien que vous puissiez ajouter un code de vérification d'erreur supplémentaire pour le rendre un peu meilleur - je sais au moins que la fonction d'écriture devrait vérifier la valeur de retour).
#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 doit spécifier la tête - pas la queue - de votre chaîne bio (c'est-à-dire le base64_filter). Votre implémentation actuelle a une fuite de mémoire.
schulwitz
@schulwitz Quelle ligne a la fuite? Bio_free_all libère toute la chaîne.
Homer6
4
J'en ai écrit un pour une utilisation avec C ++, il est très rapide, fonctionne avec des flux, gratuit et open source:
N'hésitez pas à l'utiliser si cela correspond à votre objectif.
Edit: Ajout de code en ligne sur demande.
L'amélioration des performances est obtenue en utilisant une table de recherche pour l'encodage et le décodage. _UINT8est un unsigned charsur la plupart des systèmes d'exploitation.
/** 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 J'ai ajouté un exemple en ligne maintenant et un commentaire expliquant pourquoi c'est rapide, merci.
2
Une petite amélioration du code de ryyst (qui a obtenu le plus de votes) est de ne pas utiliser de table de décodage allouée dynamiquement mais plutôt de table précalculée statique const. Cela élimine l'utilisation du pointeur et l'initialisation de la table, et évite également les fuites de mémoire si l'on oublie de nettoyer la table de décodage avec base64_cleanup () (d'ailleurs, dans base64_cleanup (), après avoir appelé free (decoding_table), on devrait avoir decoding_table = NULL, sinon appeler accidentellement base64_decode après base64_cleanup () plantera ou provoquera un comportement indéterminé). Une autre solution pourrait être d'utiliser std :: unique_ptr ... mais je suis satisfait d'avoir simplement const char [256] sur la pile et d'éviter d'utiliser des pointeurs tous ensemble - le code semble plus propre et plus court de cette façon.
La table de décodage est calculée comme suit:
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();
Ce n'est pas aussi facile à utiliser que les autres options ci-dessus. Cependant, il peut être utile dans les systèmes embarqués, où vous souhaitez vider un fichier volumineux sans allouer un autre grand tampon pour stocker la chaîne datauri base64 résultante. (C'est dommage que datauri ne vous laisse pas spécifier le nom du fichier).
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');}
Cette solution est basée sur la réponse schulwitz (encodage / décodage avec OpenSSL), mais c'est pour C ++ (enfin, la question originale concernait C, mais il y a déjà une autre réponse C ++ ici) et elle utilise la vérification des erreurs (donc c'est plus sûr à utiliser) :
#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;}}
Notez que base64_decode renvoie une chaîne vide, si l'entrée est une séquence base64 incorrecte (openssl fonctionne de cette manière).
hm ... l'utilisation de la bibliothèque openssl pour le décodage / encodage base64 prend plus de lignes de code qu'une implémentation directe (meilleure réponse à cette question) ...
anton_rh
-2
Voici une version optimisée du codeur pour la réponse acceptée, qui prend également en charge les sauts de ligne pour MIME et d'autres protocoles (une optimisation similaire peut être appliquée au décodeur):
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)'=';}}
Réponses:
Voici celui que j'utilise:
Gardez à l'esprit que cela ne vérifie pas les erreurs lors du décodage - les données non codées en base 64 seront traitées.
la source
*output_length = ((input_length - 1) / 3) * 4 + 4;
au début de base64_encode.build_decoding_table
.encoding_table[64]
pourencoding_table[255]
n'existent pas.Je sais que cette question est assez ancienne, mais je devenais confus par la quantité de solutions proposées - chacune d'elles prétendant être plus rapide et meilleure. J'ai monté un projet sur github pour comparer les encodeurs et décodeurs base64: https://github.com/gaspardpetit/base64/
À ce stade, je ne me suis pas limité aux algorithmes C - si une implémentation fonctionne bien en C ++, elle peut facilement être rétroportée vers C. Des tests ont également été effectués à l'aide de Visual Studio 2015. Si quelqu'un veut mettre à jour cette réponse avec les résultats de clang / gcc, soyez mon invité.
ENCODEUR LES PLUS RAPIDES: Les deux implémentations d'encodeurs les plus rapides que j'ai trouvées étaient celles de Jouni Malinen sur http://web.mit.edu/freebsd/head/contrib/wpa/src/utils/base64.c et l'Apache sur https://opensource.apple .com / source / QuickTimeStreamingServer / QuickTimeStreamingServer-452 / CommonUtilitiesLib / base64.c .
Voici le temps (en microsecondes) pour encoder 32K de données en utilisant les différents algorithmes que j'ai testés jusqu'à présent:
(La solution de René Nyffenegger, créditée dans une autre réponse à cette question, est répertoriée ici sous le nom adp_gmbh).
Voici celui de Jouni Malinen que j'ai légèrement modifié pour renvoyer un std :: string:
DÉCODEUR LES PLUS RAPIDES: Voici les résultats du décodage et je dois avouer que je suis un peu surpris:
L'extrait de Polfosol de l'extrait de code de décodage base64 en c ++ est le plus rapide d'un facteur presque 2x.
Voici le code par souci d'exhaustivité:
la source
char* outStr
paramètre et écrivez dans ce tampon au lieu de renvoyer unstd::string
si vous le souhaitez, c'est trivial à faire. Avant de publier ceci, il y avait déjà deux réponses C ++ avec des votes positifs ici.Mais vous pouvez aussi le faire dans openssl (la
openssl enc
commande le fait ....), regardez laBIO_f_base64()
fonctionla source
Voici ma solution utilisant OpenSSL.
la source
cc -o base base.c -lssl -lcrypto
. Aucune erreur. Il a produit cette sortie:Original character string is: Base64 encode this string! Base-64 encoded string is: QmFzZTY0IGVuY29kZSB0aGlzIHN0cmluZyE= Base-64 decoded string is: Base64 encode this string!
glib a des fonctions pour l'encodage base64: https://developer.gnome.org/glib/stable/glib-Base64-Encoding.html
la source
libb64 a à la fois des API C et C ++. Il est léger et peut-être l'implémentation publique la plus rapide. C'est aussi une bibliothèque d'encodage base64 autonome dédiée, ce qui peut être utile si vous n'avez pas besoin de tous les autres éléments provenant de l'utilisation d'une bibliothèque plus grande telle que OpenSSL ou glib.
la source
#define BUFFERSIZE 16777216
vous pouvez remplacer par 65536 si vous avez besoin d'un tampon plus petit.char
est signé sur le système cible ... C'est un problème car ilbase64_decode_value
pourrait renvoyer un nombre négatif qui est ensuite converti en char.GNU coreutils l' a dans lib / base64. C'est un peu gonflé mais traite de trucs comme EBCDIC. Vous pouvez également jouer seul, par exemple
Sachez toutes les personnes par ces cadeaux que vous ne devez pas confondre «jouer seul» avec «mettre en œuvre une norme». Yeesh.
la source
'+'
est 62 et'/'
est 63 dans PEM base64 comme demandé par OP. Voici une liste de variantes d'encodage base64 . Je ne vois pas de variante d'encodage base64 avec l'ordre des caractères que vous utilisez. Mais le calcul derrière l'algorithme est correct.J'avais besoin d'une implémentation C ++ fonctionnant sur std :: string . Aucune des réponses ne répondait à mes besoins, j'avais besoin d'une solution simple à deux fonctions pour l'encodage et le décodage, mais j'étais trop paresseux pour écrire mon propre code, alors j'ai trouvé ceci:
http://www.adp-gmbh.ch/cpp/common/base64.html
Les crédits pour le code vont à René Nyffenegger.
Mettre le code ci-dessous en cas de panne du site:
base64.cpp
base64.h
Usage
la source
Voici le décodeur que j'utilise depuis des années ...
la source
UnBase64
fonction peut compromettre la mémoire après le tampon dest, si ce tampon a la taille exacte requise pour décoder la chaîne encodée en base 64. Prenons par exemple le cas simple où vous essayez de décoder la chaîne suivante encodée en base 64 "BQ ==", en un seul BYTE c'est-à-dire queunsigned char Result = 0; UnBase64(&Result, "BQ==", 4);
cela corrompra la pile!Au cas où les gens auraient besoin d'une solution C ++, je mets cette solution OpenSSL ensemble (à la fois pour l'encodage et le décodage). Vous devrez créer un lien avec la bibliothèque "crypto" (qui est OpenSSL). Cela a été vérifié pour les fuites avec valgrind (bien que vous puissiez ajouter un code de vérification d'erreur supplémentaire pour le rendre un peu meilleur - je sais au moins que la fonction d'écriture devrait vérifier la valeur de retour).
la source
J'en ai écrit un pour une utilisation avec C ++, il est très rapide, fonctionne avec des flux, gratuit et open source:
https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/
N'hésitez pas à l'utiliser si cela correspond à votre objectif.
Edit: Ajout de code en ligne sur demande.
L'amélioration des performances est obtenue en utilisant une table de recherche pour l'encodage et le décodage.
_UINT8
est ununsigned char
sur la plupart des systèmes d'exploitation.la source
Une petite amélioration du code de ryyst (qui a obtenu le plus de votes) est de ne pas utiliser de table de décodage allouée dynamiquement mais plutôt de table précalculée statique const. Cela élimine l'utilisation du pointeur et l'initialisation de la table, et évite également les fuites de mémoire si l'on oublie de nettoyer la table de décodage avec base64_cleanup () (d'ailleurs, dans base64_cleanup (), après avoir appelé free (decoding_table), on devrait avoir decoding_table = NULL, sinon appeler accidentellement base64_decode après base64_cleanup () plantera ou provoquera un comportement indéterminé). Une autre solution pourrait être d'utiliser std :: unique_ptr ... mais je suis satisfait d'avoir simplement const char [256] sur la pile et d'éviter d'utiliser des pointeurs tous ensemble - le code semble plus propre et plus court de cette façon.
La table de décodage est calculée comme suit:
et le code modifié que j'utilise est:
la source
Il s'agit d'un décodeur spécialement écrit pour éviter d'avoir besoin d'un tampon, en écrivant directement dans une fonction putchar. Ceci est basé sur l'implémentation de wikibook https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C
Ce n'est pas aussi facile à utiliser que les autres options ci-dessus. Cependant, il peut être utile dans les systèmes embarqués, où vous souhaitez vider un fichier volumineux sans allouer un autre grand tampon pour stocker la chaîne datauri base64 résultante. (C'est dommage que datauri ne vous laisse pas spécifier le nom du fichier).
Voici le test
Production attendue:
data:text/plain;charset=utf-8;base64,dGVzdA==
la source
Les fonctions
EVP_EncodeBlock
et leEVP_DecodeBlock
rendent très simple:la source
Cette solution est basée sur la réponse schulwitz (encodage / décodage avec OpenSSL), mais c'est pour C ++ (enfin, la question originale concernait C, mais il y a déjà une autre réponse C ++ ici) et elle utilise la vérification des erreurs (donc c'est plus sûr à utiliser) :
Notez que base64_decode renvoie une chaîne vide, si l'entrée est une séquence base64 incorrecte (openssl fonctionne de cette manière).
la source
Voici une version optimisée du codeur pour la réponse acceptée, qui prend également en charge les sauts de ligne pour MIME et d'autres protocoles (une optimisation similaire peut être appliquée au décodeur):
la source