Base64 encoding and decoding with OpenSSL

后端 未结 8 2120
天命终不由人
天命终不由人 2020-12-07 19:22

I\'ve been trying to figure out the openssl documentation for base64 decoding and encoding. I found some code snippets below



        
相关标签:
8条回答
  • 2020-12-07 20:00

    Improved TCS answer to remove macros/datastructures

    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;
    }
    

    To write to file

    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;
    }
    

    Base64 encoding from file to file. Many times due to file constraint we have read in chunks of data and do encoding. Below is the 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;
     }
    
    0 讨论(0)
  • 2020-12-07 20:02

    I like mtrw's use of EVP.

    Below is my "modern C++" take on his answer without manual memory allocation (calloc). It will take a std::string but it can easily be overloaded to use raw bytes for example.

    #include <openssl/evp.h>
    
    #include <memory>
    #include <stdexcept>
    #include <vector>
    
    
    auto EncodeBase64(const std::string& to_encode) -> std::string {
      /// @sa https://www.openssl.org/docs/manmaster/man3/EVP_EncodeBlock.html
    
      const auto predicted_len = 4 * ((to_encode.length() + 2) / 3);  // predict output size
    
      const auto output_buffer{std::make_unique<char[]>(predicted_len + 1)};
    
      const std::vector<unsigned char> vec_chars{to_encode.begin(), to_encode.end()};  // convert to_encode into uchar container
    
      const auto output_len = EVP_EncodeBlock(reinterpret_cast<unsigned char*>(output_buffer.get()), vec_chars.data(), static_cast<int>(vec_chars.size()));
    
      if (predicted_len != static_cast<unsigned long>(output_len)) {
        throw std::runtime_error("EncodeBase64 error");
      }
    
      return output_buffer.get();
    }
    
    auto DecodeBase64(const std::string& to_decode) -> std::string {
      /// @sa https://www.openssl.org/docs/manmaster/man3/EVP_DecodeBlock.html
    
      const auto predicted_len = 3 * to_decode.length() / 4;  // predict output size
    
      const auto output_buffer{std::make_unique<char[]>(predicted_len + 1)};
    
      const std::vector<unsigned char> vec_chars{to_decode.begin(), to_decode.end()};  // convert to_decode into uchar container
    
      const auto output_len = EVP_DecodeBlock(reinterpret_cast<unsigned char*>(output_buffer.get()), vec_chars.data(), static_cast<int>(vec_chars.size()));
    
      if (predicted_len != static_cast<unsigned long>(output_len)) {
        throw std::runtime_error("DecodeBase64 error");
      }
    
      return output_buffer.get();
    }
    

    There's probably a cleaner/better way of doing this (I'd like to get rid of reinterpret_cast). You'll also definitely want a try/catch block to deal with the potential exception.

    0 讨论(0)
提交回复
热议问题