Encodage et décodage de Base64 avec OpenSSL

j'ai essayé de comprendre la documentation d'openssl pour le décodage et l'encodage de base64. J'ai trouvé quelques extraits de code ci-dessous

#include <openssl/sha.h>
#include <openssl/hmac.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/buffer.h>

char *base64(const unsigned char *input, int length)
{
  BIO *bmem, *b64;
  BUF_MEM *bptr;

  b64 = BIO_new(BIO_f_base64());
  bmem = BIO_new(BIO_s_mem());
  b64 = BIO_push(b64, bmem);
  BIO_write(b64, input, length);
  BIO_flush(b64);
  BIO_get_mem_ptr(b64, &bptr);

  char *buff = (char *)malloc(bptr->length);
  memcpy(buff, bptr->data, bptr->length-1);
  buff[bptr->length-1] = 0;

  BIO_free_all(b64);

  return buff;
}

char *decode64(unsigned char *input, int length)
{
  BIO *b64, *bmem;

  char *buffer = (char *)malloc(length);
  memset(buffer, 0, length);

  b64 = BIO_new(BIO_f_base64());
  bmem = BIO_new_mem_buf(input, length);
  bmem = BIO_push(b64, bmem);

  BIO_read(bmem, buffer, length);

  BIO_free_all(bmem);

  return buffer;
}

cela ne semble fonctionner que pour les chaînes à une seule ligne comme "Start", au moment où j'introduis des chaînes Complexes avec des newlines et des espaces etc ça échoue horriblement.

il ne doit même pas être openssl, une classe simple ou un ensemble de fonctions qui font la même chose serait très bien, Il ya un processus de construction très compliqué pour la solution et j'essaie d'éviter d'avoir à y aller et de faire de multiples changements. La seule raison pour laquelle j'ai choisi openssl est que la solution est déjà compilée avec les bibliothèques.

19
demandé sur Cœur 2011-03-13 10:25:29

6 réponses

personnellement, je trouve L'API OpenSSL incroyablement douloureuse à utiliser, je l'évite à moins que le coût de l'éviter soit extrêmement élevé. Je trouve assez troublant qu'il soit devenu L'API standard dans le monde de la cryptographie.

Je m'ennuyais, et je t'en ai écrit un en C++. Celui-ci devrait même gérer les cas de bord qui peuvent causer des problèmes de sécurité, comme, par exemple, encoder une chaîne qui résulte en un dépassement d'entier parce qu'il est trop grand.

j'ai fait quelques les tests unitaires sur elle, donc, il faut travailler.

#include <string>
#include <cassert>
#include <limits>
#include <stdexcept>
#include <cctype>

static const char b64_table[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

static const char reverse_table[128] = {
   64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
   64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64,
   64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63,
   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64,
   64,  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, 64, 64, 64, 64, 64,
   64, 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, 64, 64, 64, 64, 64
};

::std::string base64_encode(const ::std::string &bindata)
{
   using ::std::string;
   using ::std::numeric_limits;

   if (bindata.size() > (numeric_limits<string::size_type>::max() / 4u) * 3u) {
      throw ::std::length_error("Converting too large a string to base64.");
   }

   const ::std::size_t binlen = bindata.size();
   // Use = signs so the end is properly padded.
   string retval((((binlen + 2) / 3) * 4), '=');
   ::std::size_t outpos = 0;
   int bits_collected = 0;
   unsigned int accumulator = 0;
   const string::const_iterator binend = bindata.end();

   for (string::const_iterator i = bindata.begin(); i != binend; ++i) {
      accumulator = (accumulator << 8) | (*i & 0xffu);
      bits_collected += 8;
      while (bits_collected >= 6) {
         bits_collected -= 6;
         retval[outpos++] = b64_table[(accumulator >> bits_collected) & 0x3fu];
      }
   }
   if (bits_collected > 0) { // Any trailing bits that are missing.
      assert(bits_collected < 6);
      accumulator <<= 6 - bits_collected;
      retval[outpos++] = b64_table[accumulator & 0x3fu];
   }
   assert(outpos >= (retval.size() - 2));
   assert(outpos <= retval.size());
   return retval;
}

::std::string base64_decode(const ::std::string &ascdata)
{
   using ::std::string;
   string retval;
   const string::const_iterator last = ascdata.end();
   int bits_collected = 0;
   unsigned int accumulator = 0;

   for (string::const_iterator i = ascdata.begin(); i != last; ++i) {
      const int c = *i;
      if (::std::isspace(c) || c == '=') {
         // Skip whitespace and padding. Be liberal in what you accept.
         continue;
      }
      if ((c > 127) || (c < 0) || (reverse_table[c] > 63)) {
         throw ::std::invalid_argument("This contains characters not legal in a base64 encoded string.");
      }
      accumulator = (accumulator << 6) | reverse_table[c];
      bits_collected += 6;
      if (bits_collected >= 8) {
         bits_collected -= 8;
         retval += static_cast<char>((accumulator >> bits_collected) & 0xffu);
      }
   }
   return retval;
}
36
répondu Omnifarious 2017-08-04 19:43:34

voici un exemple D'OpenSSL base64 Encoder / decode que j'ai écrit:

attention, j'ai quelques macros/classes dans le code que j'ai écrit, mais aucune n'est importante pour l'exemple. Il s'agit simplement de quelques wrappers C++ que j'ai écrit:

buffer base64::encode( const buffer& data )
{
    // bio is simply a class that wraps BIO* and it free the BIO in the destructor.

    bio b64(BIO_f_base64()); // create BIO to perform base64
    BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);

    bio mem(BIO_s_mem()); // create BIO that holds the result

    // chain base64 with mem, so writing to b64 will encode base64 and write to mem.
    BIO_push(b64, mem);

    // write data
    bool done = false;
    int res = 0;
    while(!done)
    {
        res = BIO_write(b64, data.data, (int)data.size);

        if(res <= 0) // if failed
        {
            if(BIO_should_retry(b64)){
                continue;
            }
            else // encoding failed
            {
                /* Handle Error!!! */
            }
        }
        else // success!
            done = true;
    }

    BIO_flush(b64);

    // get a pointer to mem's data
    char* dt;
    long len = BIO_get_mem_data(mem, &dt);

    // assign data to output
    std::string s(dt, len);

    return buffer(s.length()+sizeof(char), (byte*)s.c_str());
}
8
répondu TCS 2011-03-16 20:20:52

réponse améliorée de TCS pour supprimer les macros / infrastructures de données

unsigned char *encodeb64mem( unsigned char *data, int len, int *lenoutput )
{
// bio is simply a class that wraps BIO* and it free the BIO in the destructor.

BIO *b64 = BIO_new(BIO_f_base64()); // create BIO to perform base64
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);

BIO *mem = BIO_new(BIO_s_mem()); // create BIO that holds the result

// chain base64 with mem, so writing to b64 will encode base64 and write to mem.
BIO_push(b64, mem);

// write data
bool done = false;
int res = 0;
while(!done)
{
    res = BIO_write(b64, data, len);

    if(res <= 0) // if failed
    {
        if(BIO_should_retry(b64)){
            continue;
        }
        else // encoding failed
        {
            /* Handle Error!!! */
        }
    }
    else // success!
        done = true;
}

BIO_flush(b64);

// get a pointer to mem's data
unsigned char* output;
*lenoutput = BIO_get_mem_data(mem, &output);

// assign data to output
//std::string s(dt, len2);

return output;
}

Pour écrire dans le fichier

int encodeb64(unsigned char* input, const char* filenm, int leni)
{
BIO *b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);

BIO *file = BIO_new_file(filenm, "w");
BIO *mem = BIO_new(BIO_f_buffer());
BIO_push(b64, mem);
BIO_push(mem, file);

// write data
bool done = false;
int res = 0;
while(!done)
{
    res = BIO_write(b64, input, leni);

    if(res <= 0) // if failed
    {
        if(BIO_should_retry(b64)){
            continue;
        }
        else // encoding failed
        {
            /* Handle Error!!! */
        }
    }
    else // success!
        done = true;
}

BIO_flush(b64);
BIO_pop(b64);
BIO_free_all(b64);

    return 0;
}

codage Base64 d'un fichier à l'autre. Plusieurs fois en raison de contraintes de fichier, Nous avons lu dans des morceaux de données et de faire encodage. Ci-dessous est le code.

int encodeb64FromFile(const char* input, const char* outputfilename)
{
BIO *b64 = BIO_new(BIO_f_base64());
BIO_set_flags(b64,BIO_FLAGS_BASE64_NO_NL);
int leni = 3*64;
unsigned char *data[3*64];
BIO *file = BIO_new_file(outputfilename, "w");
BIO *mem = BIO_new(BIO_f_buffer());
BIO_push(b64, mem);
BIO_push(mem, file);

FILE *fp = fopen(input, "rb");
while ((leni = fread(data, 1, sizeof data, fp)) > 0) {
    // write data
    bool done = false;
    int res = 0;
    while(!done)
    {
        res = BIO_write(b64, data, leni);

        if(res <= 0) // if failed
        {
            if(BIO_should_retry(b64)){
                continue;
            }
            else // encoding failed
            {
                /* Handle Error!!! */
            }
        }
        else // success!
            done = true;
    }

 }

 BIO_flush(b64);
BIO_pop(b64);
BIO_free_all(b64);
fclose(fp);

return 0;
 }
3
répondu Satish 2013-06-13 13:23:46

Base64 est vraiment assez simple; vous ne devriez pas avoir de difficulté à trouver n'importe quel nombre d'implémentations via un Google rapide. Par exemple,ici est une implémentation de référence en C de L'Internet Software Consortium, avec des commentaires détaillés expliquant le processus.

les couches d'implémentation d'openssl sont très complexes avec le truc" BIO " qui n'est pas (IMHO) très utile si tout ce que vous faites est le décodage/encodage.

2
répondu David Gelhar 2011-03-13 07:53:46
  #include <openssl/bio.h>

  typedef unsigned char byte;      

  namespace base64 {
    static void Encode(const byte* in, size_t in_len,
                       char** out, size_t* out_len) {
      BIO *buff, *b64f;
      BUF_MEM *ptr;

      b64f = BIO_new(BIO_f_base64());
      buff = BIO_new(BIO_s_mem());
      buff = BIO_push(b64f, buff);

      BIO_set_flags(buff, BIO_FLAGS_BASE64_NO_NL);
      BIO_set_close(buff, BIO_CLOSE);
      BIO_write(buff, in, in_len);
      BIO_flush(buff);

      BIO_get_mem_ptr(buff, &ptr);
      (*out_len) = ptr->length;
      (*out) = (char *) malloc(((*out_len) + 1) * sizeof(char));
      memcpy(*out, ptr->data, (*out_len));
      (*out)[(*out_len)] = '';

      BIO_free_all(buff);
    }

    static void Decode(const char* in, size_t in_len,
                       byte** out, size_t* out_len) {
      BIO *buff, *b64f;

      b64f = BIO_new(BIO_f_base64());
      buff = BIO_new_mem_buf((void *)in, in_len);
      buff = BIO_push(b64f, buff);
      (*out) = (byte *) malloc(in_len * sizeof(char));

      BIO_set_flags(buff, BIO_FLAGS_BASE64_NO_NL);
      BIO_set_close(buff, BIO_CLOSE);
      (*out_len) = BIO_read(buff, (*out), in_len);
      (*out) = (byte *) realloc((void *)(*out), ((*out_len) + 1) * sizeof(byte));
      (*out)[(*out_len)] = '';

      BIO_free_all(buff);
    }
  }
1
répondu fsenart 2015-10-25 15:46:42

cela fonctionne pour moi, et vérifié aucune fuite de mémoire avec valgrind.

#include <openssl/bio.h>
#include <openssl/evp.h>
#include <cstring>
#include <memory>
#include <string>
#include <vector>

#include <iostream>

namespace {
struct BIOFreeAll { void operator()(BIO* p) { BIO_free_all(p); } };
}

std::string Base64Encode(const std::vector<unsigned char>& binary)
{
    std::unique_ptr<BIO,BIOFreeAll> b64(BIO_new(BIO_f_base64()));
    BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
    BIO* sink = BIO_new(BIO_s_mem());
    BIO_push(b64.get(), sink);
    BIO_write(b64.get(), binary.data(), binary.size());
    BIO_flush(b64.get());
    const char* encoded;
    const long len = BIO_get_mem_data(sink, &encoded);
    return std::string(encoded, len);
}

// Assumes no newlines or extra characters in encoded string
std::vector<unsigned char> Base64Decode(const char* encoded)
{
    std::unique_ptr<BIO,BIOFreeAll> b64(BIO_new(BIO_f_base64()));
    BIO_set_flags(b64.get(), BIO_FLAGS_BASE64_NO_NL);
    BIO* source = BIO_new_mem_buf(encoded, -1); // read-only source
    BIO_push(b64.get(), source);
    const int maxlen = strlen(encoded) / 4 * 3 + 1;
    std::vector<unsigned char> decoded(maxlen);
    const int len = BIO_read(b64.get(), decoded.data(), maxlen);
    decoded.resize(len);
    return decoded;
}

int main()
{
    const char* msg = "hello";
    const std::vector<unsigned char> binary(msg, msg+strlen(msg));
    const std::string encoded = Base64Encode(binary);
    std::cout << "encoded = " << encoded << std::endl;
    const std::vector<unsigned char> decoded = Base64Decode(encoded.c_str());
    std::cout << "decoded = ";
    for (unsigned char c : decoded) std::cout << c;
    std::cout << std::endl;
    return 0;
}

Compiler:

g++ -lcrypto main.cc

Sortie:

encoded = aGVsbG8=
decoded = hello
0
répondu Matt 2017-12-26 17:12:15