Non-printable character after generating random n-byte Base64 string

后端 未结 3 756
南旧
南旧 2020-12-12 00:39

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<

相关标签:
3条回答
  • 2020-12-12 00:58

    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.

    0 讨论(0)
  • 2020-12-12 01:01

    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.

    0 讨论(0)
  • 2020-12-12 01:19

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

    0 讨论(0)
提交回复
热议问题