Openssl数据安全传输平台011:base64的使用

文章目录

  • 1 base64
    • 1.1 概念
    • 1.2 应用场景
  • 2 base64 算法 (重要)
  • 3 openssl 中base64的使用
    • 3.1 BIO 操作
    • 3.2 base64 编码 -> bio链的写操作
    • 3.3 base64 解码 -> bio链的读操作

1 base64

1.1 概念

Base64是一种基于64个可打印字符表示二进制数据的表示方法。在Base64中的可打印字符包括字母A-Za-z、数字0-9,这样共有62个字符,加上**+/**, 共64个字符.

  • 普通的文本数据也可以使用base64进行编解码
  • Base64编解码的过程是可逆的
  • Base64不能当做加密算法来使用

1.2 应用场景

在计算机中任何数据都是按ascii码存储的,而ascii码的128~255之间的值是不可见字符。 而在网络上交换数据时,比如说从A地传到B地,往往要经过多个路由设备,由于不同的设备对字符的处理方式有一些不同,这样那些不可见字符就有可能被处理错误,这是不利于传输的。所以就先把数据先做一个Base64编码,统统变成可见字符,这样出错的可能性就大降低了。

  • base64 应用

    • 它可用来作为电子邮件的传输编码。
      • 附件: 图片/音频/视频-> 二进制
      • 邮件传输协议只支持 ASCII字符传递,因此如果要传输二进制文件:图片、视频是无法实现的。
    • Http协议
      • HTTP协议要求请求行和请求头都必须是ASCII编码
    • 数据库数据读写 - blob
      • blob格式: - 文件、音频、视频、图片
      • blob格式的数据中有一些特殊的数据:
        • 中文
          • 写之前进行编码
          • 读出来之后解码
        • 二进制数据

2 base64 算法 (重要)

  • 把3个8位字节(3*8=24)转化为4个6位的字节(4*6=24)
    • 假设有一个字符串, 需要对这个字符串分组, 每3个字节为一组, 分成N组
    • 将每一组的3个字节拆分, 拆成4个字节, 每个字节有6bit
  • 在6位的前面补两个0,形成8位一个字节的形式
    • 每个组就从3个字节变成了4个字节
    • 结论: base64编码之后的字符串变大了,
  • 如果剩下的字符不足3个字节,则用0填充,输出字符使用’=‘,因此编码后输出的文本末尾可能会出现1或2个’=', 表示补了多少字节,解码的时候,会自动去掉。
  • 数据通过base64编码, 编码之后的数据边长了还是变短了?
    - 变长了
    - 每三个字节一组, 编码之后每组增加一个字节

  • 编码之后的数据末尾有可能有=, 这个=是什么意思?
    - =代表0, 0是给最后一组补位用的, 最后一组缺几个字节就补几个0
    - 在解码的时候根据=的个数去掉对应的字节数

在这里插入图片描述Base64 将 8 位为一个单元的字节数据,拆分为 6 位为一个单元的二进制片段。每一个 6 位单元对应 Base64 索引表中的一个字符。简单举个例子,下图中 M 的 ASCII 码是 77 , 而转换为二进制后前六位二进制对应值为 19,为 Base64 字典中的 T。
在这里插入图片描述

3 openssl 中base64的使用

3.1 BIO 操作

// 创建BIO对象
BIO *BIO_new(const BIO_METHOD *type);

// 封装了base64编码方法的BIO,写的时候进行编码,读的时候解码
BIO_METHOD* BIO_f_base64();

// 封装了内存操作的BIO接口,包括了对内存的读写操作
BIO_METHOD* BIO_s_mem();

// 创建一个内存型的BIO对象
// BIO * bio = BIO_new(BIO_s_mem());
BIO *BIO_new_mem_buf(void *buf, int len);

// 创建一个磁盘文件操作的BIO对象
BIO* BIO_new_file(const char* filename, const char* mode);

// 从BIO接口中读出len字节的数据到buf中。
// 成功就返回真正读出的数据的长度,失败返回0或-1,如果该BIO没有实现本函数则返回-2。
int BIO_read(BIO *b, void *buf, int len);
    - buf: 存储数据的缓冲区地址
    - len: buf的最大容量

// 往BIO中写入长度为len的数据。
// 成功返回真正写入的数据的长度,失败返回0或-1,如果该BIO没有实现本函数则返回-2。
int BIO_write(BIO *b, const void *buf, int len);
    - buf: 要写入的数据, 写入到b对应的设备(内存/磁盘文件)- len: 要写入的数据的长度
    - 
// 将BIO内部缓冲区的数据都写出去, 成功: 1, 失败: 0或-1
int BIO_flush(BIO *b);
    - 在使用BIO_write()进行写操作的时候, 数据有时候在openssl提供的缓存中
    - 将openssl提供的缓存中的数据刷到设备(内存/磁盘文件)// 把参数中名为append的BIO附加到名为b的BIO上,并返回b
// 连接两个bio对象到链表中
// 在链表中的关系: b->append
BIO * BIO_push(BIO *b, BIO *append);
    - b: 要插入到链表中的头结点
    - append: 头结点的后继
    - 
// 把名为b的BIO从一个BIO链中移除并返回下一个BIO,如果没有下一个BIO,那么就返回NULL。
BIO * BIO_pop(BIO *b);

typedef struct buf_mem_st BUF_MEM;
struct buf_mem_st {
    size_t length;              /* current number of bytes */
    char *data;
    size_t max;                 /* size of buffer */
    unsigned long flags;
};

// 该函数也是一个宏定义函数,它将b底层的BUF_MEM结构放在指针pp中。
BUF_MEM* ptr;
long BIO_get_mem_ptr(BIO *b, BUF_MEM **pp);
// 释放整个bio链
void BIO_free_all(BIO *a);

我们举几个简单的例子说明BIO_push和BIO_pop的作用,假设md1、md2是digest类型的BIO,b64是Base64类型的BIO,而f是file类型的BIO,那么如果执行操作,那么就会形成md1-md2-b64-f的BIO链,大家可以看到,在构造完一个BIO后,头一个BIO就代表了整个BIO链,这根链表的概念几乎是一样的

这时候,任何写往md1的数据都会经过md1,md2的摘要(或说hush运算),然后经过base64编码,最后写入文件f。可以看到,构造一条好的BIO链后,操作是非常方便的,你不用再关心具体的事情了,整个BIO链会自动将数据进行指定操作的系列处理。

需要注意的是,如果是读操作,那么数据会从相反的方向传递和处理,对于上面的BIO链,数据会从f文件读出,然后经过base64解码,然后经过md1,md2编码,最后读出。

BIO* md1 = BIO_new();    // md1 hash计算
BIO* md2 = BIO_new();    // md2 hash计算
BIO* b64 = BIO_new();    // base64 编解码
BIO* f = BIO_new();        // 操作磁盘文件

// 组织bio链
// b64->f
BIO_push(b64, f);
// 那么就会形成一个b64-f的链。然后再执行下面的操作:
// md2->b64->f
BIO_push(md2, b64);
// md1->md2->b64->f
BIO_push(md1, md2);

// bIO链
md1->md2->b64->f
// 写数据操作
int BIO_write(md1, "hello, world", 11);
// 数据的处理过程
// 进行md1 的哈希计算 -> md2的哈希计算 -> base64编码 -> 数据被写到磁盘

在这里插入图片描述

3.2 base64 编码 -> bio链的写操作

char* data = "hello, world";

// 创建base64编码的bio对象
BIO* b64 = BIO_new(BIO_f_base64());

// 最终在内存中得到一个base64编码之后的字符串
BIO* mem = BIO_new(BIO_s_mem());

// 将两个bio对象串联, 结构: b64->mem
BIO_push(b64, mem);

// 将要编码的数据写入到bio对象中
BIO_write(b64, data, strlen(data)+1);

// 将数据从bio对象对应的内存中取出 -> char*
BUF_MEM* ptr;

// 数据通过ptr指针传出
long BIO_get_mem_ptr(b64, &ptr);
char* buf = new char[ptr->length];
memcpy(buf, ptr->data, ptr->length);
printf("编码之后的数据: %s\n", buf);

3.3 base64 解码 -> bio链的读操作

// 要解码的数据
char* data = "xxxxxxxxxxxxxxxxxxxx";

// 创建base64解码的bio对象
BIO* b64 = BIO_new(BIO_f_base64());
#if 0
// 存储要解码的数据
BIO* mem = BIO_new(BIO_s_mem());
// 将数据写入到mem对应的内存中
BIO_write(mem, data, strlen(data));
#else
BIO *mem = BIO_new_mem_buf(data, strlen(data));
#endif

// 组织bio链
BIO_push(b64, mem);

// 读数据
char buf[1024];
int BIO_read(b64, buf, 1024);
printf("base64解码的数据: %s\n", buf);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/105587.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

一文5000字从0到1使用Jmeter实现轻量级的接口自动化测试(图文并茂)

接口测试虽然作为版本的一环,但是也是有一套完整的体系,有接口的功能测试、性能测试、安全测试;同时,由于接口的特性,接口的自动化低成本高收益的,使用一些开源工具或一些轻量级的方法,在测试用…

Win10中Pro/E鼠标滚轮不能缩放该怎么办?

Pro/E安装好后,鼠标滚轮不能缩放模型,该怎么办?问题多发生在win8/win10上,新装了PROE,发现滑动鼠标中键不能放大缩小。 彩虹图纸管理软件_图纸管理系统_图纸文档管理软件系统_彩虹EDM【官网】彩虹EDM图纸管理软件系统…

Android 11.0 禁用插入耳机时弹出的保护听力对话框

1.前言 在11.0的系统开发中,在某些产品中会对耳机音量调节过高限制,在调高到最大音量的70%的时候,会弹出音量过高弹出警告,所以产品 开发的需要要求去掉这个音量弹窗警告功能 2.禁用插入耳机时弹出的保护听力对话框的核心类 frameworks\base\packages\SystemUI\src\com\and…

使用docker部署flask接口服务 一

文章目录 一:说明二:dockerfile 参数说明1. 一般常用的 参数,以及它的含义2. 我自己的 dockerfile 三:示例操作1. Gunicorn Gevent启动服务的好处2. 用Gunicorn Gevent的好处:3. Gunicorn Gevent的 使用示例4. 创建…

【SpringCloudNetflix】一图理解Spring Cloud Netflix解决了那些微服务问题?

什么是微服务理解: SpringCloudNetflix解决的问题理解: SpringCloudNetflix核心点: 注册中心:Eureka负载均衡:Ribbon、Feign服务熔断:Hystrix服务降级:Hystrix服务监控:Hystrix Da…

MySQL多表关联on和where速度对比实测谁更快

MySQL多表关联on和where速度对比实测谁更快 背景 今天发现有人在讨论:两张MySQL的数据表按照某一个字段进行关联的时候查询,我们使用on和where哪种查询方式更快。百闻不如一见,我们来亲自测试下。 先说结论 Where、对等查询的join速度基本…

Android Studio新功能-设备镜像Device mirroring-在电脑侧显示手机实时画面并可控制

下载最新的灰测版本-蜥蜴 成功运行到真机后,点击右侧Running Devices选项卡,再点击号 选中当前设备; 非常丝滑同步,在电脑侧也可以顺畅控制真机 该功能大大方便了我们视线保持在显示器上专注开发,并且便于与UI视觉进行…

QT mqtt 在子线程中使用

qtmqtt 在子线程中使用_qt在子线程里mqtt无法new-CSDN博客文章浏览阅读524次。解决问题:QMqttClient - connection not made from another thread在qt中使用多线的qtmqtt客户端发送接收数据_qt在子线程里mqtt无法newhttps://blog.csdn.net/qq_35708970/article/deta…

前端开发技术栈(工具篇):2023深入了解webpack的安装和使用以及核心概念和启动流程(详细) 63.3k stars

目录 Webpack简介 Entry Module Chunk Loader Plugin Output Webpack的启动流程 Webpack的优缺点 Webpack的使用 1. 安装Webpack 2. 创建Webpack配置文件 3. 编写代码 4. 运行Webpack 5. 在HTML中引入打包后的文件 6. 执行编译命令 Webpack其他功能介绍 1. 使…

【Proteus仿真】【STM32单片机】智能语音家居陪护机器人

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真STM32单片机控制器,使用OLED显示模块、红外传感器、蜂鸣器、DS18B20温度传感器,风扇LED、语音识别模块等。 主要功能: 系统运行后,…

“深入探讨操作系统和虚拟化技术“

目录 引言1.操作系统1.1.什么是操作系统1.2.常见操作系统1.3.个人版本和服务器版本的区别1.4.Linux的各个版本 2.安装VMWare虚拟机1.VMWare虚拟机介绍2.VMWare虚拟机安装3.VMWare虚拟机配置 3.安装配置Windows Server 2012 R24.完成电脑远程访问电脑5.服务器环境搭建配置jdk配置…

【k8s】kubeadm安装k8s集群

一、环境部署 master192.168.88.10docker、kubeadm、kubelet、kubectl、flannelnode01192.168.88.20docker、kubeadm、kubelet、kubectl、flannelnode02192.168.88.30docker、kubeadm、kubelet、kubectl、flannelhub.lp.com192.168.88.40 docker、docker-compose harbor-offli…

git合并错分支还原技巧

如果合并错了,但是还没有push,还原的简单粗暴的方式就是把合并后的分支删了。 本文主要讲的是git合并错分支并且已经push了的还原方式 比如:开发分支future_1,合并到项目组共同使用的分支develop上了,并且已经push了&…

2023高频前端面试题-http

1. HTTP有哪些⽅法? HTTP 1.0 标准中,定义了3种请求⽅法:GET、POST、HEAD HTTP 1.1 标准中,新增了请求⽅法:PUT、PATCH、DELETE、OPTIONS、TRACE、CONNECT 2. 各个HTTP方法的具体作用是什么? 方法功能G…

Babylonjs学习笔记(五)——创建PBR材质

书接上回,这里讨论PBR材质!!! // 创建天空盒/* */const createSkyBox (scene:Scene):void>{const envTex CubeTexture.CreateFromPrefilteredData(./env/environment.env,scene)scene.environmentTexture envTex;scene.cre…

C++前缀和算法的应用:从栈中取出 K 个硬币的最大面值和 原理源码测试用例

本文涉及的基础知识点 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 题目 一张桌子上总共有 n 个硬币 栈 。每个栈有 正整数 个带面值的硬币。 每一次操作中,你可以从任意一个栈的 顶部 取出 1 个硬币,从栈中移除…

python excel接口自动化测试框架

前言 前些天写了pytestyamlallure接口自动化测试框架这篇文章。 今天采用Excel继续写一个接口自动化测试框架。 设计流程图 这张图是我的excel接口测试框架的一些设计思路。 首先读取excel文件,得到测试信息,然后通过封装的requests方法&#xff0c…

C++数据结构X篇_21_插入排序(稳定的排序)

文章目录 1. 插入排序原理2. 算法图解3. 核心代码:4. 插入排序整体代码实现 1. 插入排序原理 插入排序是一种最简单直观的排序算法,它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相…

线程间的调度顺序

目录 ♫join和sleep ♫wait ♫notify和notifyAll 我们知道线程是抢占式执行,随机调度的,而这也是诱发线程安全的根本原因。我们虽然无法指定线程之间的调度顺序,但是可以通过JVM提供的一些API让某个线程阻塞,主动放弃CPU&#xf…

【31】c++设计模式——>模板方法模式

模板方法模式通常由以下几个部分组成: 1.抽象基类(Abstract Base Class):抽象基类定义了一个算法的骨架,其中包含了模板方法和一些基本操作方法。模板方法在抽象基类中被声明为虚函数,它定义了算法的流程&…