前言

在上一篇文章中我们讲了如何使用 CMake 搭建 OpenSSL 环境,并打印了 OpenSSL 的版本。

今天要使用 OpenSSL 进行 Base64 编解码。

Base64

Base64 是一种基于64个可打印字符来表示二进制数据的编码方法。

它最初是为电子邮件系统设计的,因为早期的网络和邮件传输协议不能可靠地处理非文本数据(如图像、音频文件等)。

通过将这些二进制数据转换成ASCII字符串格式,可以确保它们在网络上传输时不会被破坏或修改。

Base64 由大写字母A-Z、小写字母a-z、数字0-9以及符号"+“和”/"。

64 = 2^6,所以用 6 位就能容纳,但是一个字节是 8 位,它们的最小公倍数是 24,所以每 3 个字节(3 * 8 = 4 * 6)可以分成一组,用 4 个字符可以表示。

如果不足 24 位则用 0 补齐。

使用

上面简单讲了一下 Base64 的原理和出现的原因,接下来看一下如何使用 OpenSSL 进行编解码。

如何搭建 OpenSSLCMake OpenSSL 环境搭建中已经讲过了,不会的可以前去查看

编码

我们直接看代码如何编写,先来导入一下头文件

1
2
3
4
5
#include <iostream>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
using namespace std;

要使用 Base64 编码,我们需要用到 OpenSSLBIO 接口。

BIO(Basic Input Output) 接口是用于实现输入输出操作的一套抽象层。它提供了一种灵活的方法来处理各种类型的I/O,包括文件、套接字、内存缓冲区等,并且支持过滤器(filters),这些过滤器可以在数据传递给最终目的地之前对其进行处理。

BIO 使用的流程一般是:

  1. 创建 BIO 对象 比如: BIO_new
  2. 配置算法/过滤器 比如:加解密,Base64,摘要算法
  3. 执行 IO 操作 使用 BIO_readBIO_write 来进行写入或读取数据。
  4. 清理创建的对象,释放内存
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// 创建 BIO 对象
auto bio_mem = BIO_new(BIO_s_mem());
if (bio_mem == nullptr) {
    return 0;
}

// 添加 Base64 过滤器
auto bio_b64 = BIO_new(BIO_f_base64());
if (bio_b64 == nullptr) {
    BIO_free(bio_mem);
    return 0;
}

// 把内存和 Base64 编码算法关联起来
BIO_push(bio_b64, bio_mem);

string data = "OpenSSL Base64 编码";

// 写入数据
int re = BIO_write(bio_b64, reinterpret_cast<unsigned char*>(data.data()), data.size());
if (re <= 0) {
    BIO_free_all(bio_b64);
    return 0;
}

// 刷新缓存,写入到 mem
BIO_flush(bio_b64);
BIO_set_flags(bio_b64, BIO_FLAGS_BASE64_NO_NL); // 不进行换行,不设置一些情况下会解码失败

string encoded_data;
BUF_MEM *encoded_mem = 0;
BIO_get_mem_ptr(bio_b64, &encoded_mem); // 读到 buffer 里
if (encoded_mem) {
    encoded_data = string(encoded_mem->data, encoded_mem->length);
}
BIO_free_all(bio_b64);
cout << "Base64 编码: " << encoded_data << endl;

输入如下:

1
Base64 编码: T3BlblNTTCBCYXNlNjQg57yW56CB

解码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
string encoded_data = "T3BlblNTTCBCYXNlNjQg57yW56CB";
auto bio_mem_decode = BIO_new_mem_buf(encoded_data.data(), encoded_data.size());
if (bio_mem_decode == nullptr) {
    return 0;
}
auto bio_b64_decode = BIO_new(BIO_f_base64());
if (bio_b64_decode == nullptr) {
    BIO_free(bio_mem_decode);
    return 0;
}

BIO_push(bio_b64_decode, bio_mem_decode);
BIO_set_flags(bio_b64_decode, BIO_FLAGS_BASE64_NO_NL); // 不进行换行,不设置一些情况下会解码失败

int capcity = 1024;
string decode_data(capcity, 0);

size_t size = 0;
BIO_read_ex(bio_b64_decode, decode_data.data(), capcity, &size);
BIO_free_all(bio_b64_decode);

decode_data.resize(size);
cout << "Base64 解码: " << decode_data << endl;

结果如下:

1
Base64 解码: OpenSSL Base64 编码

总结

Base64 编码还是比较简单的, OpenSSL 使用起来比其它语言标准库的要复杂一些。