Skip to content

Instantly share code, notes, and snippets.

@barrysteyn
Last active April 23, 2024 09:50
Show Gist options
  • Star 17 You must be signed in to star a gist
  • Fork 9 You must be signed in to fork a gist
  • Save barrysteyn/4409525 to your computer and use it in GitHub Desktop.
Save barrysteyn/4409525 to your computer and use it in GitHub Desktop.
Base64 Encoding/Decoding with the OpenSSL c api
//Decodes Base64
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <string.h>
#include <stdio.h>
int calcDecodeLength(const char* b64input) { //Calculates the length of a decoded base64 string
int len = strlen(b64input);
int padding = 0;
if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are =
padding = 2;
else if (b64input[len-1] == '=') //last char is =
padding = 1;
return (int)len*0.75 - padding;
}
int Base64Decode(char* b64message, char** buffer) { //Decodes a base64 encoded string
BIO *bio, *b64;
int decodeLen = calcDecodeLength(b64message),
len = 0;
*buffer = (char*)malloc(decodeLen+1);
FILE* stream = fmemopen(b64message, strlen(b64message), "r");
b64 = BIO_new(BIO_f_base64());
bio = BIO_new_fp(stream, BIO_NOCLOSE);
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer
len = BIO_read(bio, *buffer, strlen(b64message));
//Can test here if len == decodeLen - if not, then return an error
(*buffer)[len] = '\0';
BIO_free_all(bio);
fclose(stream);
return (0); //success
}
//Encodes Base64
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <string.h>
#include <stdio.h>
#include <math.h>
int Base64Encode(const char* message, char** buffer) { //Encodes a string to base64
BIO *bio, *b64;
FILE* stream;
int encodedSize = 4*ceil((double)strlen(message)/3);
*buffer = (char *)malloc(encodedSize+1);
stream = fmemopen(*buffer, encodedSize+1, "w");
b64 = BIO_new(BIO_f_base64());
bio = BIO_new_fp(stream, BIO_NOCLOSE);
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
BIO_write(bio, message, strlen(message));
BIO_flush(bio);
BIO_free_all(bio);
fclose(stream);
return (0); //success
}
#include <stdio.h>
int main() {
//Encode To Base64
char* base64EncodeOutput;
Base64Encode("Hello World", &base64EncodeOutput);
printf("Output (base64): %s\n", base64EncodeOutput);
//Decode From Base64
char* base64DecodeOutput;
Base64Decode("SGVsbG8gV29ybGQ=", &base64DecodeOutput);
printf("Output: %s\n", base64DecodeOutput);
return(0);
}
all:
gcc -o base64 Main.c Base64Encode.c Base64Decode.c -lcrypto -lm
@XimingCheng
Copy link

if strlen(b64input) == 0 or 1 calcDecodeLength may be crash

BIO_flush(bio); g++ ouputs “value computed is not used” warnning
should change into (void)BIO_flush(bio);

@sauragrawal
Copy link

this decode do not work when provided with an ssh key to decode

@emctague
Copy link

Helpful note: To make this work across platforms that don't support fmemopen, a BIO_new(BIO_s_mem()) can be used as the output.

Example for encoding (uses C++, but you get the idea) - in this case, rand is the input to be encoded.

    BIO *b64 = BIO_new(BIO_f_base64());
    BIO *bio = BIO_new(BIO_s_mem());
    bio = BIO_push(b64, bio);
    BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
    BIO_write(bio, rand, bytes_amt);
    BIO_flush(bio);

    // Convert the encoded data to a C-string
    BUF_MEM *bptr;
    BIO_get_mem_ptr(bio, &bptr);
    std::string data_str(bptr->data, bptr->length);

@matvore
Copy link

matvore commented Nov 17, 2023

@emctague Thanks for that. I'm surprised you can use BIO_get_mem_ptr on a BIO_f_base64 instance (bio is set to b64 after the BIO_push call). But this works when I try it.

For anyone else, BIO_s_mem and related require including openssl/buffer.h

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment