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.
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.
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.
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.
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; }
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With