生成随机n字节Base64字符串

时间:2016-07-13 16:13:32

标签: c string random openssl base64

我试图使用openssl生成一个32byte的base64字符串,但它并不总是产生32字节字符串,有时输出会出现乱码并且无法正确显示

#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;
}

使用gcc rand_str.c -lcrypto && ./a.out | tail -1进行编译和运行,有时会产生类似:

的内容

I6YaDVSRPw5Ux+2paY4u4ToMKtZXQoBj`�

有时输出甚至不是32个字节。

我的目标是复制此命令的作用: openssl rand -base64 32

我需要做些什么不同的事情?

3 个答案:

答案 0 :(得分:2)

BIO_write(bio, buffer, strlen(buffer));

NUL是一个有效的随机字节,因此strlen有时会返回小于所需的值(32)。

`�是由于base64缓冲区没有NUL终结符,所以它读出一些随机垃圾。我不知道有什么方法可以强制OpenSSL添加NUL,但是你可以告诉printf在哪里停止:

(void)printf("Return value of the operation was: %d\n%.44s\n", ret, base64EncodeOutput);

答案 1 :(得分:2)

  

使用gcc编译并运行rand_str.c -lcrypto&amp;&amp; ./a.out | tail -1,有时产生类似的东西:

I6YaDVSRPw5Ux+2paY4u4ToMKtZXQoBj`�

BIO's执行而不是会产生C字符串。它们不是NULL终止。

从链中删除BIO_write BIO后再执行一次Base64并写入NULL字符。

答案 2 :(得分:1)

感谢上面发布的解决方案,我能够解决这个问题。结果如下:

#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;
}

我最初的尝试的主要问题是,当null终结符通过base64过滤器时,它不会被识别为字符串的结尾,而是转换为base64,这使得字符串看起来像它在我原帖中的表现方式。

所以建议的解决方案是在写完原始字符串后从BIO删除base64过滤器,然后只留下out BIO。由于out BIO只是一个接收器,我们可以写任何东西,它不会修改它的输入。因此,这使得有机会将空终止符写入底层缓冲区并稍后检索完整的字符串。