【OpenSSL】OpenSSL实现Base64

这篇具有很好参考价值的文章主要介绍了【OpenSSL】OpenSSL实现Base64。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

Base 64概述和应用场景

概述

Base64就是将二进制数据转换为字符串的一种算法。

应用场景

  • 邮件编码
  • xml或则json存储二进制内容
  • 网页传递数据URL
  • 数据库中以文本形式存放二进制数据
  • 可打印的比特币钱包地址base58Check(hash校验)
  • 网页上可以将图片直接使用Base64表达
  • 公私密钥的文本文件

Base16(16进制)

Base164位, 一个Unicode字符编码需要8位 ,那就需要将一个字符分解成2部分。编码字节的值,对应Base64的值如下对照表:

字节值 Base64编码
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 A
11 B
12 C
13 D
14 E
15 F

从零开始实现Base16编解码

代码如下:

#include <iostream>
using namespace std;

static const char BASE16_ENC_TAB[] = "0123456789ABCDEF";
// '0'~'9'  => 48~57, 'A'~'E' => 65~70

static const char BASE16_DEC_TAB[128] =
{
	-1, // 0
	-1, -1, -1, -1, -1,-1, -1, -1, -1, -1, // 1-10
	-1, -1, -1, -1, -1,-1, -1, -1, -1, -1, // 11-20
	-1, -1, -1, -1, -1,-1, -1, -1, -1, -1, // 21-30
	-1, -1, -1, -1, -1,-1, -1, -1, -1, -1, // 31-40
	-1, -1, -1, -1, -1,-1, -1, 0, 1, 2, // 41-50
	3, 4, 5, 6, 7, 8, 9, -1, -1, -1, // 51-60
	-1, -1, -1, -1, 10, 11, 12, 13, 14, 15, // 61-70 'A'~'F'
};

int Base16Encode(const unsigned char* in, int size, char* out)
{
	for (int i = 0; i < size; i++) 
	{
		//1 一个字节取出高4位和低4位
		char h = (in[i] >> 4) ;  // 以为
		char l = (in[i] & 0x0f); // 0000 1111 //去掉高4位
		// 0~15映射到对应的字符
		out[i * 2] = BASE16_ENC_TAB[h];
		out[i * 2 + 1] = BASE16_ENC_TAB[l];
		
	}
	// base 16转码后空间扩大一倍 4位转成一个字符 1字节转成两个字符
	return size * 2;
}

/**
* 将Base16字符转换成常规字符串
*/
int Base16Decode(const string &in, unsigned char* out) 
{
	// 将两个字符拼成一个字节
	for (int i = 0; i < in.size(); i+=2) 
	{
		unsigned char ch = in[i]; //高位转换的字符
		unsigned char lh = in[i + 1]; // 低位转换的字符
		// 上面拿到的还是个字符, 要转换成原始的数据
		unsigned char h = BASE16_DEC_TAB[ch];
		unsigned char l = BASE16_DEC_TAB[lh];

		//out[i/2] = (h <<4) + l;
		out[i / 2] = h << 4 | l;
	}
	return in.size() / 2;
}

int main()
{
	cout << "Test Base16" << endl;

	const unsigned char data[] = "测试Base16";
	int len = sizeof(data);
	char out1[1024] = { 0 };
	int res = Base16Encode(data, len, out1);
	cout << res << ":" << out1 << endl;

	string code(out1);
	unsigned char out2[1024] = { 0 };
	res = Base16Decode(code, out2);
	cout << res << ":" << out2 << endl;
	return 0;
}

Base64(64进制)

首先查看Base64的值码对应表

字节值 Base64编码 字节值 Base64编码 字节值 Base64编码 字节值 Base64编码
0 A 16 Q 32 g 48 w
1 B 17 R 33 h 49 x
2 C 18 S 34 i 50 y
3 D 19 T 35 j 51 z
4 E 20 U 36 k 52 0
5 F 21 V 37 l 53 1
6 G 22 W 38 m 54 2
7 H 23 X 39 n 55 3
8 I 24 Y 40 o 56 4
9 J 25 Z 41 p 57 5
10 K 26 a 42 q 58 6
11 L 27 b 43 t 59 7
12 M 28 c 44 s 60 8
13 N 29 d 45 t 61 9
14 O 30 e 46 u 62 +
15 P 31 f 47 v 63 /

Base64编码要求把3个8位字节(3*8=24), 之后在6位的前面补两个0, 形成8位一个字节的形式。如果剩下的字符不足3个字节, 则用0填充,输出字符使用=,因此编码后输出的文本末尾可能会出现1或2个=

Open SSL BIO接口

  • BIO包含了多种接口,用于控制在BIO_METHOD中不同实现函数, 包括6种filter型和8种source/sink型应用场景。
  • BIO_new创建一个BIO对象.
  • 数据源:source/sink类型的BIO是数据源BIO_new(BIO_s_mem()),生存内存是数据源对象
  • 过滤:filter BIO就是把数据从一个BIO转换到另外一个BIO或应用接口 BIO_new(BIO_f_base64())
  • BIO链:一个BIO链通常包括一个source BIO和一个或多个filter BIO BIO_push(b64_bio, mem_bio);
  • 写编码, 读解码 BIO_write BIO_read_ex

Open SSL BIO实现Base64编解码

#include <iostream>
#include <openssl/bio.h>
#include <openssl/evp.h>
#include <openssl/buffer.h>
using namespace std;

int Base64Encode(const unsigned char* in, int len, char* out_base64)
{
	if (!in || len < 0 || !out_base64) 
	{
		return 0;
	}

	//创建内存源
	auto mem_bio = BIO_new(BIO_s_mem());
	if (!mem_bio) return 0;
	// base64 filter
	auto b64_bio = BIO_new(BIO_f_base64());//这个接口在头文件 evp.h

	if (!b64_bio) 
	{
		BIO_free(mem_bio); //释放申请成功的空间
		return 0;
	}

	// 形成BIO链表
	//b64-mem
	// 形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果
	BIO_push(b64_bio, mem_bio);  // 2个链表(从链表头部,代表整个链表)
	//write 是编码 3字节 => 4字节 不足3字节补充0 和等于号
	int re = BIO_write(b64_bio, in, len); //将数据写入到链表头
	if (re < 0) 
	{
		// 写入失败, 清空整个链表节点
		BIO_free_all(b64_bio);
		return 0;
	}
	// 刷新缓存, 写入链表的mem
	BIO_flush(b64_bio);

	// 从链表源内存读取
	BUF_MEM* p_data = nullptr; // 需要引入
	BIO_get_mem_ptr(b64_bio, &p_data); //拿到编码数据了
	int out_size = 0;
	if (p_data) 
	{
		memcpy(out_base64, p_data->data, p_data->length);
		out_size = p_data->length;
	}
	//执行完后, 清理空间
	BIO_free_all(b64_bio);
	return out_size;
}

int Base64Decode(const char* in, int len, unsigned char* out_data) 
{
	if (!in || len <= 0 || !out_data)
	{
		return 0;
	}

	// 内存源
	auto mem_bio = BIO_new_mem_buf(in, len);
	if (!mem_bio) 
	{
		return 0;
	}
	// base64 过滤器
	auto b64_bio = BIO_new(BIO_f_base64());
	if (!b64_bio) 
	{
		BIO_free(mem_bio);
		return 0;
	}

	//形成BIO链条
	BIO_push(b64_bio, mem_bio);
	//读取 解码 4字节转3字节
	size_t size = 0;
	BIO_read_ex(b64_bio, out_data, len, &size);
	BIO_free_all(b64_bio);
	return size;
}

int main(int argc, char argv[])
{
	cout << "Test openssl BIO base64" << endl;
	unsigned char data[] = "测试Base64数据";
	int len = sizeof(data);
	char out[1024];
	int ret = Base64Encode(data, len, out);
	if (ret) 
	{
		out[ret] = '\0';
	}
	cout << "base64:" << out << endl;

	
	unsigned char out_data[1024] = { 0 };
	//ret = Base64Decode(out, sizeof(out), out_data);// 这里不能用sizeof()  , 用计算字符长度
	ret = Base64Decode(out, strlen(out), out_data);
	cout << "encode :" << out_data << endl;
}

Open SSLBase64编码换行

Open SSLBase64在做编码操作的时候,默认情况下遇到64字节(不同平台不确定,)的时候就会进行换行操作。
例如将上面的输入内容改得过长.

unsigned char data[] = "测试Base64数据形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果";

执行的编码结果就会出现换行操作, 如下图所示:
openssl base64,ssl
只需要在写入待编码码内容前进行参数设置,就可以使其不换行

	//超过长度不还行
	BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); 

编码方法的整体代码如下:


int Base64Encode(const unsigned char* in, int len, char* out_base64)
{
	if (!in || len < 0 || !out_base64) 
	{
		return 0;
	}

	//创建内存源
	auto mem_bio = BIO_new(BIO_s_mem());
	if (!mem_bio) return 0;
	// base64 filter
	auto b64_bio = BIO_new(BIO_f_base64());//这个接口在头文件 evp.h

	if (!b64_bio) 
	{
		BIO_free(mem_bio); //释放申请成功的空间
		return 0;
	}

	// 形成BIO链表
	//b64-mem
	// 形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果
	BIO_push(b64_bio, mem_bio);  // 2个链表(从链表头部,代表整个链表)

	//超过长度不还行
	BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL); 

	//write 是编码 3字节 => 4字节 不足3字节补充0 和等于号
	//编码数据每64直接会加一个\n换行符号,并且结尾时也有换行符号

	int re = BIO_write(b64_bio, in, len); //将数据写入到链表头
	if (re < 0) 
	{
		// 写入失败, 清空整个链表节点
		BIO_free_all(b64_bio);
		return 0;
	}
	// 刷新缓存, 写入链表的mem
	BIO_flush(b64_bio);

	// 从链表源内存读取
	BUF_MEM* p_data = nullptr; // 需要引入
	BIO_get_mem_ptr(b64_bio, &p_data); //拿到编码数据了
	int out_size = 0;
	if (p_data) 
	{
		memcpy(out_base64, p_data->data, p_data->length);
		out_size = p_data->length;
	}
	//执行完后, 清理空间
	BIO_free_all(b64_bio);
	return out_size;
}

执行结果,如下,编码的内容已经不会再换行了。
openssl base64,ssl
从上可以看出, 如果没有了末尾的换行符号。解码的时候又出现无法解码的问题, 因为解码是按照末尾的\n来执行结尾的。因为如下,如果我再编码内容的后面加上\n即可正确的进行解码操作了.

int main(int argc, char argv[])
{
	cout << "Test openssl BIO base64" << endl;
	unsigned char data[] = "测试Base64数据形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果形成链表, 往base64内存写数据, 进行编码,结果会传递到链表的下一个节点, 到mem中读取结果";
	int len = sizeof(data);
	char out[1024];
	int ret = Base64Encode(data, len, out);
	if (ret) 
	{
		out[ret] = '\0';
	}
	cout << "base64:" << out << endl;
	// 手动加下行结尾的符号
	out[ret] = '\n';
	out[ret+1] = '\0';
	unsigned char out_data[1024] = { 0 };
	//ret = Base64Decode(out, sizeof(out), out_data);// 这里不能用sizeof()  , 用计算字符长度
	ret = Base64Decode(out, strlen(out), out_data);
	cout << "encode :" << out_data << endl;
}

另外一种解决办法就是直接也在解码代码中也加上对换行符号的忽略.


int Base64Decode(const char* in, int len, unsigned char* out_data) 
{
	if (!in || len <= 0 || !out_data)
	{
		return 0;
	}

	// 内存源
	auto mem_bio = BIO_new_mem_buf(in, len);
	if (!mem_bio) 
	{
		return 0;
	}
	// base64 过滤器
	auto b64_bio = BIO_new(BIO_f_base64());
	if (!b64_bio) 
	{
		BIO_free(mem_bio);
		return 0;
	}

	//形成BIO链条
	BIO_push(b64_bio, mem_bio);
	//取消默认读取换行符号做结束的操作
	BIO_set_flags(b64_bio, BIO_FLAGS_BASE64_NO_NL);
	//读取 解码 4字节转3字节
	size_t size = 0;
	BIO_read_ex(b64_bio, out_data, len, &size);
	BIO_free_all(b64_bio);
	return size;
}

执行的结果都可以OK了。
openssl base64,ssl
综上问题, 编解码必须要一致。

Base58

由于Base64的方式存在一些问题,比如+,/在一些传输内容时容易出现特殊字符问题,还有0/O/o, l(小写母L)I(大写字母i)在某些情况下肉眼不易区分。Base58去掉了0(数字0)O(大写字母o)l(小写字母L)I(大写字母i),以及两个特殊字符+,/.一共去掉了6个字符,就剩下了58个字符。文章来源地址https://www.toymoban.com/news/detail-774596.html

字节值 Base58编码 字节值 Base58编码 字节值 Base58编码 字节值 Base58编码
0 1 16 H 32 Z 48 q
1 2 17 J 33 a 49 r
2 3 18 K 34 b 50 s
3 4 19 L 35 c 51 t
4 5 20 M 36 d 52 u
5 6 21 N 37 e 53 v
6 7 22 P 38 f 54 w
7 8 23 Q 39 g 55 x
8 9 24 R 40 h 56 y
9 A 25 S 41 i 57 z
10 B 26 T 42 j
11 C 27 U 43 k
12 D 28 V 44 m
13 E 29 W 45 n
14 F 30 X 46 o
15 G 31 Y 47 p

辗转相除法

  • 两个数的最大公约数等于它们中较小的数和两数只差的最大公约数
  • 欧几里德算法,是求最大公约数的算法
  • 两个数的最大公约数是指同时整除它们的最大正整数。辗转相除法的基本原理是两个数的最大公约数等于它们中较小的数和两数只差的最大公约数。
  • 如果要将1234转换成58进制;
  1. 1234 除以 58 ,商21, 余数为16,查表得H
  2. 用21除以58, 商0, 余数为21, 查表得N
  3. 如果待转的数前面又0直接附加编码1来代表,有多少个就附加多少个。

Base56输出字节数

  • 在编码后字符串中, 是从58个字符中选择,需要表示的位数是 l o g 2 58 log_{2}58 log258, 每一个字母代表的信息是 l o g 2 58 log_{2}58 log258.
  • 输入的字节: (length *8)位。
  • 预留的字符数量就是 ( l e n g t h ∗ 8 ) / l o g 2 58 (length*8)/log_{2}58 length8)/log258

到了这里,关于【OpenSSL】OpenSSL实现Base64的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • base64编码:js实现base64编码的3种方式,多场景下分析使用

    js实现base64编码,前端一般应用场景在与后端接口参数中体现,后端可能需要某个字段是base64编码的字符,这时候就需要用前端的方法进行转换,再作为参数传递到服务端。 js实现base64编码的3种方式 1. 使用base64.js进行转换 获取base64.js,可以直接搜索base64.js下载。推荐使用n

    2024年02月16日
    浏览(50)
  • 前端实现图片转Base64

    废话不多说,直接上代码 我们传入一个图 片地址看看是否成功  但是,一般我们在做这样的需求前,处理的图片肯定不止一张,接下来就要批量处理,但是这个方法是异步,我们要将一个数组内的每个对象的地址都转成base64,才算真正完成我们的功能。前端一般接收到后端的

    2024年02月05日
    浏览(44)
  • 前端实现base64编码处理

    文件上传时,因为base64文件字符串过长后端接收失败的问题。 提示:以下是本篇文章正文内容,下面案例可供参考 Base64是一种用于将二进制数据转换为ASCII字符集中可打印字符的编码方法。它通常用于在不同系统之间传输二进制数据,因为许多系统只支持文本数据的传输。 代

    2024年02月12日
    浏览(35)
  • OpenSSL实现SSL网络通信

    Certainly! Here are the C language programs for a simple OpenSSL client and server that can establish a secure communication channel between them: l inux环境下 OpenSSL Server Program (server.c): OpenSSL Client Program (client.c): To compile and run the programs, you’ll need to make sure you have the OpenSSL library installed on your system. Use the foll

    2024年02月06日
    浏览(47)
  • Java 实现图片转base64

    2024年02月09日
    浏览(44)
  • C/C++实现编解码Base64

    Base64是网络上最常见的用于传输8Bit字节码的编码方式之一,Base64就是一种基于64个可打印字符来表示二进制数据的方法 由于二进制的一些字符在网络协议中属于控制字符,不能直接传送,因此需要用Base64编码之后传输,编码之后传输的是一些很普通的ASCII字符。 Base64常用于邮

    2024年02月12日
    浏览(39)
  • Java实现Base64编码以及原理详解

    使用java.util.Base64类来实现Base64编码。 该类是Java8引入的。 上面就是java实现的代码,使用起来是很方便的。下面来说说Base64编码的原理。 Base64编码的作用:将字符串转为由64个特定字符组成的编码,这种编码方式适用于不支持特定编码的情况。 一、 首先来看,原字符串是“

    2024年02月14日
    浏览(50)
  • BASE64算法原理解析之C#实现

    1. BASE64算法原理: base64编码规则      A.采用64个基本的ASCII码字符对数据进行重新编码     B.将要编码字符串拆分成字节数组,以 3个字节为一组 。 按顺序排列24 位数据 ,    C.把24位数据分成4组,每组6位,每组最高位前补两个0凑足一个字节,         3字节为一组的数据重新

    2024年02月15日
    浏览(44)
  • JAVA实现网络/本地图片转BASE64存储

    网络图片转BASE64 如果是本地图片的话,其实和网络图片相差不多的,主要就是读取图片流的形式变一下 若有问题,可以留言探讨。

    2024年02月13日
    浏览(33)
  • 图片与Base64编码相互转换、优势分析和技术实现

    在Web开发中,图片与Base64编码的相互转换是一个非常实用的技能。图片 Base64编码是将图片文件转换为字符串格式,以便于在网络上传输和存储。本文将详细介绍图片与Base64编码的转换方法,以及图片Base64编码的优势。 图片Base64相互转换 | 一个覆盖广泛主题工具的高效在线平

    2024年01月19日
    浏览(58)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包