I have binary data in an unsigned char variable. I need to convert them to PEM base64 in c. I looked in openssl library but i could not find any function. Does any body have
This is a decoder that is specifically written to avoid the need for a buffer, by writing directly to a putchar function. This is based on wikibook's implementation https://en.wikibooks.org/wiki/Algorithm_Implementation/Miscellaneous/Base64#C
This is not as easy to use as other options above. However, it can be of use in embedded systems, where you want to dump a large file without allocating another large buffer to store the resultant base64 datauri string. (It's a pity that datauri does not let you specify the filename).
void datauriBase64EncodeBufferless(int (*putchar_fcptr)(int), const char* type_strptr, const void* data_buf, const size_t dataLength)
{
const char base64chars[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
const uint8_t *data = (const uint8_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 0
if((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 0
if((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');
}
Here is the test
#include <stdio.h>
#include <stdint.h>
#include <string.h>
int main(void)
{
char str[] = "test";
datauriBase64EncodeBufferless(putchar, "text/plain;charset=utf-8", str, strlen(str));
return 0;
}
Expected Output: data:text/plain;charset=utf-8;base64,dGVzdA==
I needed C++ implementation working on std::string. None of answers satisfied my needs, I needed simple two-function solution for encoding and decoding, but I was too lazy to write my own code, so I found this:
http://www.adp-gmbh.ch/cpp/common/base64.html
Credits for code go to René Nyffenegger.
Putting the code below in case the site goes down:
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 rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.h"
#include <iostream>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char 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;
unsigned char 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;
}
base64.h
#include <string>
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);
Usage
const std::string s = "test";
std::string encoded = base64_encode(reinterpret_cast<const unsigned char*>(s.c_str()), s.length());
std::string decoded = base64_decode(encoded);
I wrote one for use with C++, it's very fast, works with streams, free, and open source:
https://tmplusplus.svn.sourceforge.net/svnroot/tmplusplus/trunk/src/
Feel free to use it if it fits your purpose.
Edit: Added code inline by request.
The performance boost is acieved by using a lookup table for encoding and decoding. _UINT8
is an unsigned char
on most OS's.
/** Static Base64 character encoding lookup table */
const char CBase64::encodeCharacterTable[65] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
/** Static Base64 character decoding lookup table */
const char CBase64::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
*/
void CBase64::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
*/
void CBase64::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];
}
}
The EVP_EncodeBlock and EVP_DecodeBlock functions make it very easy:
#include <stdio.h>
#include <stdlib.h>
#include <openssl/evp.h>
char *base64(const unsigned char *input, int length) {
const int pl = 4*((length+2)/3);
char *output = calloc(pl+1, 1); //+1 for the terminating null that EVP_EncodeBlock adds on
const int ol = EVP_EncodeBlock(output, input, length);
if (ol != pl) { fprintf(stderr, "Whoops, encode predicted %d but we got %d\n", pl, ol); }
return output;
}
unsigned char *decode64(const char *input, int length) {
const int pl = 3*length/4;
unsigned char *output = calloc(pl+1, 1);
const int ol = EVP_DecodeBlock(output, input, length);
if (pl != ol) { fprintf(stderr, "Whoops, decode predicted %d but we got %d\n", pl, ol); }
return output;
}
Here's the decoder I've been using for years...
static const char table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
static const int BASE64_INPUT_SIZE = 57;
BOOL isbase64(char c)
{
return c && strchr(table, c) != NULL;
}
inline char value(char c)
{
const char *p = strchr(table, c);
if(p) {
return p-table;
} else {
return 0;
}
}
int UnBase64(unsigned char *dest, const unsigned char *src, int srclen)
{
*dest = 0;
if(*src == 0)
{
return 0;
}
unsigned char *p = dest;
do
{
char a = value(src[0]);
char b = value(src[1]);
char c = value(src[2]);
char d = value(src[3]);
*p++ = (a << 2) | (b >> 4);
*p++ = (b << 4) | (c >> 2);
*p++ = (c << 6) | d;
if(!isbase64(src[1]))
{
p -= 2;
break;
}
else if(!isbase64(src[2]))
{
p -= 2;
break;
}
else if(!isbase64(src[3]))
{
p--;
break;
}
src += 4;
while(*src && (*src == 13 || *src == 10)) src++;
}
while(srclen-= 4);
*p = 0;
return p-dest;
}