BIO 不刷新 OpenSSL 中的数据

BIO don't flush data in OpenSSL

我正在使用 BIO 通过 OpenSSL 计算某些文件的摘要。我的代码如下:

#include <stdio.h>
#include <string.h>
#include <openssl/evp.h>
#include <openssl/bio.h>

int main(){

    BIO* bio_infile;
    bio_infile = BIO_new_file("test.txt", "rb");
    
    BIO* bio_md = BIO_new(BIO_f_md());
    BIO_set_md(bio_md, EVP_sha1());

    BIO* bio_outfile;
    bio_outfile = BIO_new_file("dgst.txt","w");
    
    BIO_push(bio_md, bio_outfile);
    BIO_push(bio_infile, bio_md);
    
    BIO_flush(bio_infile);
    BIO_flush(bio_md);
    
    BIO_free(bio_infile); 
    BIO_free(bio_md);
    BIO_free(bio_outfile);
    
    return 0;
}

但是,当我的程序 运行 运行时,我在文件 dgst.txt 中找不到任何内容。

为什么?

BIO 链接机制用于将过滤器 BIO 前置到 source/sink BIO。它不是为连接源 BIO 和接收器 BIO 而设计的。

那么你可以做什么:

  • 创建带有最终 BIO 接收器的 BIO 过滤器链。当向链头写入数据时,输入将遍历每个过滤器,直到到达接收器。

  • 创建具有最终 BIO 来源的 BIO 过滤器链。当从链头读取数据时,读取请求将沿着链向上传递到源。然后,从源读取的数据将通过每个过滤器反向遍历链。

但是你不能做什么:

  • 创建一个 BIO 链,它以源开始并以接收器结束,中间有过滤器。没有将数据从源自动“泵送”到接收器的机制。

一个附加说明:BIO_f_md 是一个过滤器 BIO,它计算通过它的数据的摘要,但它不修改数据。所以 BIO 的输出是原始数据,而不是数据的摘要。在使用 BIO_get_md (reference) 处理所有数据后,可以检索摘要。因此,在您的示例中,即使支持将源链接到接收器,最终的 dgst.txt 也只会包含来自 test.txt.

的原始数据

要修复您的示例,有两种方法:

方法一

使用摘要 BIO 后跟输入文件的来源 BIO 创建一个链。然后我们可以从摘要 BIO 中读取,它在内部从源 BIO 中提取数据。我们读取数据并立即丢弃它,直到处理完整个文件。然后我们可以从摘要 BIO.

中检索摘要
    // Create chain: Digest -> Source File
    BIO* bio_infile;
    bio_infile = BIO_new_file("test.txt", "rb");

    BIO* bio_md = BIO_new(BIO_f_md());
    BIO_set_md(bio_md, EVP_sha1());

    BIO* head = BIO_push(bio_md, bio_infile);

    // Read from head of chain
    std::array<char,1024> buf{};
    int read;

    while ((read = BIO_read(head, buf.data(), buf.size())) > 0) {
      // Ignore buffer, we only want the data to pass throug the digest BIO
    }

    // Read result from digest BIO
    EVP_MD *md;
    BIO_get_md(bio_md, &md);

    std::array<char, EVP_MAX_MD_SIZE> md_buf{};
    int mdlen = BIO_gets(bio_md, md_buf.data(), md_buf.size());

    // Print digest to stdout
    for (int i = 0; i < mdlen; i++) {
      printf("%02x", static_cast<uint8_t>(md_buf[i]));
    }
    printf("\n");

    // Cleanup
    BIO_free_all(head); 

方法二

创建一个带有摘要 BIO 的链,后跟一个用于丢弃数据的特殊接收器 BIO (BIO_s_null)。然后我们从源 BIO 读取输入文件的数据(没有任何链接)并将数据写入摘要 BIO。写入的数据通过摘要 BIO 到达接收器 BIO,接收器将丢弃数据。当所有数据都处理完后,我们可以从摘要BIO.

中检索摘要
    // Create chain: Digest -> Null Sink
    BIO* bio_infile;
    bio_infile = BIO_new_file("test.txt", "rb");

    BIO* bio_md = BIO_new(BIO_f_md());
    BIO_set_md(bio_md, EVP_sha1());

    BIO* bio_null = BIO_new(BIO_s_null());

    BIO* head = BIO_push(bio_md, bio_null);

    // Read from file and write to chain
    std::array<char,1024> buf{};
    int read;

    while ((read = BIO_read(bio_infile, buf.data(), buf.size())) > 0) {
      BIO_write(head, buf.data(), read);
    }

    // Read result from digest BIO
    EVP_MD *md;
    BIO_get_md(bio_md, &md);

    std::array<char, EVP_MAX_MD_SIZE> md_buf{};
    int mdlen = BIO_gets(bio_md, md_buf.data(), md_buf.size());

    // Print digest to stdout
    for (int i = 0; i < mdlen; i++) {
      printf("%02x", static_cast<uint8_t>(md_buf[i]));
    }
    printf("\n");

    // Cleanup
    BIO_free(bio_infile);
    BIO_free_all(head);