Howto base64 decode with C/C++ and OpenSSL

Someone asked for an example of decoding with OpenSSL on the Howto base64 encode with C/C++ and OpenSSL post. So here it is:

#include <string.h>

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

char *unbase64(unsigned char *input, int length);

int main(int argc, char **argv)
{
  char *output = unbase64("WU9ZTyEA\n\0", strlen("WU9ZTyEA\n\0"));
  printf("Unbase64: *%s*\n", output);
  free(output);
}

char *unbase64(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;
}

Tags: , ,

9 thoughts on “Howto base64 decode with C/C++ and OpenSSL

  1. saleem

    Hi,
    Thanks for the code.

    Could you please share some code, on how to decode base64 encode DER format to X509.

    Any help will be great.

    Thanks,
    Saleem

  2. G

    Used this for a project of mine and had a bit of trouble getting it to work. If anyone is tearing their hair out wondering why no errors are being reported but no data is being delivered, try changing

    bmem = BIO_push(b64, bmem);

    to

    BIO_push(b64, bmem);

  3. carson Post author

    @Carlo

    Can you provide more information? I've tested to make sure it works with the latest version of OpenSSL.

  4. Heavy

    Hi
    I had some trouble with the openssl base 64 de/encoding too. This was because openssl does no 'pure' base64 encoding by default but uses the pem standard base64 encoding scheme ( this is why adding the lines from roxas will make it work in the most cases because the newlines are ignored ) but if you want to decode base64 that is used inside a certificate or mail it will be encoded after the pem standard which requires a newline after 64 bytes of base64 data. This makes encoding a bit tricky because you have to calculate the lenght of the encoded data yourself ( or i yust did not find the propper openssl routine to do that ;)

    However here is some code i wrote that does the job. Feel free to use it ..

    size_t MPSCalculateBase64Size(size_t inLen,int iLineSize)
    {
    double dInLen = inLen;
    double dLineSize = iLineSize;
    double dAdd = 0.0;
    double dLen = 0.0;
    size_t iResult = 0;

    double dModIn = fmod( dInLen,3.0);

    if( dModIn )
    dLen = ( ((( dInLen + 3.0 ) – fmod( dInLen, 3.0 )) / 3.0 ) * 4.0 );
    else
    dLen = ( ( dInLen / 3.0 ) * 4.0 ) ;

    dAdd = dLen;

    // If line size is != 0 the we must add the newlines to our resulting
    // data length
    if( iLineSize != 0)
    {
    // Add the newlines required after the pem/base64 standard
    // (after 64 chars)
    dAdd += ( dLen / dLineSize );

    // Check for incomplete line which is although terminated with a
    // \n (PEM Base64).
    double dModLine = fmod(dLen,dLineSize);
    if( dModLine )
    {
    dAdd += 1.0;
    }
    }

    iResult = (size_t) dAdd;
    return iResult;
    }

    int MPSBase64EncodeStr(const MPSCharType* ptrDataIn,size_t inLen,MPSStringType& strResult,size_t& stResultSize)
    {
    if(! ptrDataIn)
    return MPS_ERR_PARAM;

    BIO *bio = NULL;
    BIO *b64 = NULL;

    b64 = BIO_new(BIO_f_base64());
    bio = BIO_new( BIO_s_mem() );
    bio = BIO_push(b64, bio);

    size_t rLen = BIO_write(bio, ptrDataIn, (int) inLen);
    BIO_flush(bio);

    char* ptrOut = NULL;
    BIO_get_mem_data(bio, &ptrOut);

    if(ptrOut)
    {
    // Calculate line size
    std::string strData(ptrOut);
    size_t lineLen = strData.find("\n");
    if(lineLen == (size_t) MPSStringType::npos)
    {
    lineLen = 0;
    }

    int size = (int) MPSCalculateBase64Size( inLen, (int)lineLen ) ;

    strResult.insert(0,ptrOut,size);
    stResultSize = size;
    }

    BIO_free_all(bio);

    return MPS_OK;
    }

    int MPSBase64DecodeStr(const MPSCharType* ptrDataIn,size_t inLen,MPSStringType& strResult,size_t& stResultSize)
    {
    if(! ptrDataIn)
    return MPS_ERR_PARAM;

    BIO* b64 = NULL;
    BIO* bmem = NULL;
    char *buffer = (char *) malloc(inLen);
    if(buffer)
    {
    memset(buffer, 0, inLen);
    b64 = BIO_new(BIO_f_base64());
    bmem = BIO_new_mem_buf((void*) ptrDataIn, (int) inLen);
    bmem = BIO_push(b64, bmem);

    size_t rLen = BIO_read(bmem, buffer,(int) inLen);

    strResult.insert(0, (const char*) buffer, rLen);
    stResultSize = rLen;

    BIO_free_all(bmem);
    delete [] buffer,buffer = NULL;

    return MPS_OK;
    }
    return MPS_ERR;

    }

  5. Heavy

    I like to add that doing that :

    // Calculate line size
    std::string strData(ptrOut);
    size_t lineLen = strData.find("\n");
    if(lineLen == (size_t) MPSStringType::npos)

    is a verry dangerous thing which will probably crash under a lot of circumstances. So it is a lot safer to just input the line len into the function.It can differ between 64 = PEM newline padding,and 72 for some other standard i cant recall right now X). Although there are some type mißmatches i forgot to clean out.
    The input types i used where:
    typedef std::string MPSStringType and
    typedef char MPSCharType

    Have fun ;)

  6. felipe

    Thank you roxaz, that worked. I was able to write to a memory bio, encoding it as base64, but I wasn't able to read from a base 64 encoded memory bio, decoding it.

    the line
    BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
    is only necessary when reading, I just didnt understand what it does, but it worked anyway =p

Leave a Reply

Your email address will not be published. Required fields are marked *