Non-printable character after generating random n-

2019-01-20 16:41发布

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?

3条回答
放荡不羁爱自由
2楼-- · 2019-01-20 17:29

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);

查看更多
等我变得足够好
3楼-- · 2019-01-20 17:29

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.

查看更多
太酷不给撩
4楼-- · 2019-01-20 17:37

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.

查看更多
登录 后发表回答