Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Base64 encoding and decoding with OpenSSL

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

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

This only seems to work for single line strings such as "Start", the moment I introduce complex strings with newlines and spaces etc it fails horribly.

It doesn't even have to be openssl, a simple class or set of functions that do the same thing would be fine, theres a very complicated build process for the solution and I am trying to avoid having to go in there and make multiple changes. The only reason I went for openssl is because the solution is already compiled with the libraries.

like image 877
Bernard Avatar asked Mar 13 '11 07:03

Bernard


People also ask

What is OpenSSL Base64?

OpenSSL base64-encodes the hashed and signed policy statement. tr replaces characters that are not valid in URL query string parameters with characters that are valid. For code examples that demonstrate creating a signature in several programming languages see Code examples for creating a signature for a signed URL.

How do you decode a Base64 encoded string in Unix?

To decode with base64 you need to use the --decode flag. With encoded string, you can pipe an echo command into base64 as you did to encode it. Using the example encoding shown above, let's decode it back into its original form. Provided your encoding was not corrupted the output should be your original string.

How do I install Base64 on Windows?

To install, just copy the file base64.exe to a folder on your Windows PATH, for example C:\Windows . You may need administrator permissions to do this. We recommend you set up a C:\Bin directory for files like this.


Video Answer


2 Answers

Personally, I find the OpenSSL API to be so incredibly painful to use, I avoid it unless the cost of avoiding it is extremely high. I find it quite upsetting that it has become the standard API in the crypto world.

I was feeling bored, and I wrote you one in C++. This one should even handle the edge cases that can cause security problems, like, for example, encoding a string that results in integer overflow because it's too large.

I have done some unit testing on it, so it should work.

#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; } 
like image 95
Omnifarious Avatar answered Oct 04 '22 10:10

Omnifarious


Rather than using the BIO_ interface it's much easier to use the EVP_ interface. For instance:

#include <iostream> #include <stdlib.h> #include <openssl/evp.h>  char *base64(const unsigned char *input, int length) {   const auto pl = 4*((length+2)/3);   auto output = reinterpret_cast<char *>(calloc(pl+1, 1)); //+1 for the terminating null that EVP_EncodeBlock adds on   const auto ol = EVP_EncodeBlock(reinterpret_cast<unsigned char *>(output), input, length);   if (pl != ol) { std::cerr << "Whoops, encode predicted " << pl << " but we got " << ol << "\n"; }   return output; }  unsigned char *decode64(const char *input, int length) {   const auto pl = 3*length/4;   auto output = reinterpret_cast<unsigned char *>(calloc(pl+1, 1));   const auto ol = EVP_DecodeBlock(output, reinterpret_cast<const unsigned char *>(input), length);   if (pl != ol) { std::cerr << "Whoops, decode predicted " << pl << " but we got " << ol << "\n"; }   return output; } 

The EVP functions include a streaming interface too, see the man page.

like image 29
mtrw Avatar answered Oct 04 '22 10:10

mtrw