I was trying to generate a 32byte base64 string using openssl, but it does not always produce 32 byte string and sometimes the output is garbled and not displayed correctly
#include <openssl/rand.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int Base64Encode(const unsigned char* buffer, unsigned char** b64text) { //Encodes a binary safe base 64 string
BIO *bio, *b64;
BUF_MEM *bufferPtr;
b64 = BIO_new(BIO_f_base64());
bio = BIO_new(BIO_s_mem());
bio = BIO_push(b64, bio);
BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line
BIO_write(bio, buffer, strlen(buffer));
BIO_flush(bio);
BIO_get_mem_ptr(bio, &bufferPtr);
BIO_set_close(bio, BIO_NOCLOSE);
BIO_free_all(bio);
*b64text=bufferPtr->data;
return (0); //success
}
int main() {
unsigned char buffer[35], *base64EncodeOutput;
int ret = RAND_bytes(buffer, 32);
buffer[32]='\0'; // Null terminate
(void)Base64Encode(buffer, &base64EncodeOutput);
(void)printf("Return value of the operation was: %d\n%s\n", ret, base64EncodeOutput);
return EXIT_SUCCESS;
}
Compiling and running with gcc rand_str.c -lcrypto && ./a.out | tail -1
, sometimes produces something like:
I6YaDVSRPw5Ux+2paY4u4ToMKtZXQoBj`�
And sometimes the output is not even 32 bytes in length.
My goal is to replicate what this command does:
openssl rand -base64 32
What do I need to do differently?
BIO_write(bio, buffer, strlen(buffer));
NUL is a valid random byte, so that strlen will sometimes return less than the desired value (32).
The `� is due to the base64 buffer not having a NUL terminator, so it's reading out some random garbage. I don't know of any way to force OpenSSL to add a NUL, but you can tell printf where to stop:
(void)printf("Return value of the operation was: %d\n%.44s\n", ret, base64EncodeOutput);
Compiling and running with gcc rand_str.c -lcrypto && ./a.out | tail -1, sometimes produces something like:
I6YaDVSRPw5Ux+2paY4u4ToMKtZXQoBj`�
The BIO's
do not produce C-strings. They are not NULL terminated.
Perform one more BIO_write
and write the NULL character after removing the Base64
BIO from the chain.
Thanks to the solutions posted above, I was able to fix this problem. Here is the result:
#include <openssl/rand.h>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static char* base64_bytes(int size) {
char *buff = malloc(size + 1), *bytes = NULL;
int chunk;
BIO *b64, *out;
BUF_MEM *bptr;
// Create a base64 filter/sink
if ((b64 = BIO_new(BIO_f_base64())) == NULL) {
return NULL;
}
// Create a memory source
if ((out = BIO_new(BIO_s_mem())) == NULL) {
return NULL;
}
// Chain them
out = BIO_push(b64, out);
BIO_set_flags(out, BIO_FLAGS_BASE64_NO_NL);
// Generate random bytes
if (!RAND_bytes(buff, size)) {
return NULL;
}
// Write the bytes
BIO_write(out, buff, size);
BIO_flush(out);
// Now remove the base64 filter
out = BIO_pop(b64);
// Write the null terminating character
BIO_write(out, "\0", 1);
BIO_get_mem_ptr(out, &bptr);
// Allocate memory for the output and copy it to the new location
bytes = malloc(bptr->length);
strncpy(bytes, bptr->data, bptr->length);
// Cleanup
BIO_set_close(out, BIO_CLOSE);
BIO_free_all(out);
free(buff);
return bytes;
}
int main() {
char *b64 = base64_bytes(32);
puts(b64);
free(b64);
return EXIT_SUCCESS;
}
The main issue with my original attempt was that when the null terminator is passed through the base64
filter, it is not recognized as the end of the string, but rather it is converted to base64 which makes the string look the way it did in my original post.
So the suggested solution is to remove the base64 filter from the BIO
after writing the original string, this then leaves just the out
BIO. Since the out
BIO is simply a sink, we can write pretty much anything to it and it does not modify it's input. So this gives the opportunity to write a null terminator into the underlying buffer and retrieve the complete string later.