【问题标题】:Non-printable character after generating random n-byte Base64 string生成随机 n 字节 Base64 字符串后的不可打印字符
【发布时间】:2016-11-16 08:17:46
【问题描述】:

我试图使用 openssl 生成一个 32 字节的 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 &amp;&amp; ./a.out | tail -1 编译和运行,有时会产生如下内容:

I6YaDVSRPw5Ux+2paY4u4ToMKtZXQoBj`�

有时输出的长度甚至不是 32 字节。

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


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

【问题讨论】:

    标签: c string random openssl base64


    【解决方案1】:

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

    【讨论】:

    • 感谢您的回复。你能告诉我使用%44s%.44s有什么区别吗?
    • %44s 打印最少 44 个字符,而 %.44s 最多打印 44 个字符。如果字符串短于最小值,则用空格填充;如果长于最大值,则会被截断。
    【解决方案2】:

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

    I6YaDVSRPw5Ux+2paY4u4ToMKtZXQoBj`�
    

    BIO's 不会产生 C 字符串。它们不是以 NULL 结尾的。

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

    【讨论】:

      【解决方案3】:

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

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

      我最初尝试的主要问题是,当空终止符通过base64 过滤器时,它不会被识别为字符串的结尾,而是被转换为base64,这使得字符串看起来像这样在我的原始帖子中确实如此。

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-03-21
        • 1970-01-01
        • 2023-03-17
        • 1970-01-01
        • 1970-01-01
        • 2011-09-26
        • 2017-08-01
        相关资源
        最近更新 更多