C++ 通过CryptoPP计算Hash值

Crypto++ (CryptoPP) 是一个用于密码学和加密的 C++ 库。它是一个开源项目,提供了大量的密码学算法和功能,包括对称加密、非对称加密、哈希函数、消息认证码 (MAC)、数字签名等。Crypto++ 的目标是提供高性能和可靠的密码学工具,以满足软件开发中对安全性的需求。

该库包含了许多常见的密码学算法,如AES、DES、RSA、DSA、SHA等,使开发者能够轻松地在他们的应用程序中实现安全性和加密功能。Crypto++ 是以面向对象的方式设计的,因此它的使用通常涉及使用类和对象来表示不同的密码学概念和算法。

Crypto++ 提供了许多特性,包括多平台支持(Windows、Linux、macOS等)、容易使用的 API、高性能的实现、丰富的文档和社区支持。在使用 Crypto++ 之前,你需要确保正确地配置和链接 Crypto++ 库到你的项目中。

编译Crypto库

目前Crypto库的最新版本为8.90,读者可自行下载对应的库源代码,下载好以后使用Visual Studio工具打开源文件中的cryptest.sln文件。

  • 源码下载地址:https://www.cryptopp.com/release890.html

打开以后选中调试菜单中的属性页面,此时将运行库修改为多线程/MT模式,否则虽可以编译通过但这个库却无法被正常使用,此处是一个坑。

此时选中解决方案,并直接点击重新编译库,这个过程可能需要等待一段时间,更具设备的配置而不同读者可在最底部看到输出进度;

当编译成功以后,读者可以来到cryptopp890\Win32\Output\Release目录下,该目录下的则是编译成功后的lib库文件,可以将这3个文件全部保存在新建的lib文件夹内。

接着在cryptopp890文件夹下直接搜索所有的*.h头文件,并放入到新建的include文件夹内,此时我们就有了最新版本的开发工具包了。

使用该库也很容易,只需要包含Include与Lib库文件即可,如下图所示配置;

使用MD5算法

MD5(Message Digest Algorithm 5)是一种常见的哈希函数,用于产生128位的散列值(通常以32位的十六进制数表示)。MD5广泛用于检查数据完整性、数字签名、密码存储等领域。

以下是 MD5 算法的基本概述:

  1. 输入处理: MD5 接受任意长度的输入,但输出是固定长度的128位。输入被划分为512位的块,每个块包含16个32位的字。
  2. 填充: 如果输入的位数不是512的倍数,就需要填充数据,使其长度满足这个条件。填充是通过在消息的末尾添加一个’1’和零比特,然后添加一个表示原始消息长度的64位整数来完成的。
  3. 初始化: MD5 有四个32位的寄存器(A、B、C、D),初始化为特定的常数。这些寄存器将在处理每个消息块时进行更新。
  4. 处理块: 对于每个512位块,MD5 执行64个操作轮次。每个轮次都使用一个非线性函数,一个常量和一个消息块的子集。这些轮次通过循环结构连接起来。
  5. 输出: MD5 的输出是四个32位字的级联,通常以32位的十六进制数表示。这四个字的顺序是 A、B、C、D。

MD5 算法的设计目标是产生一个唯一的(或极其难以相同)散列值,以便在密码存储、数字签名和数据完整性检查等场景中使用。然而,由于MD5存在一些安全性问题,特别是其易受碰撞攻击的漏洞,现在不再被推荐用于安全性要求较高的场景。对于安全性要求较高的应用,推荐使用更强大和安全的哈希函数,如SHA-256或SHA-3。

如下这段代码中涉及到一些特殊的类,这里将分别介绍功能;

  • FileSource: 用于从文件中读取数据。

  • StringSource: 用于从字符串或二进制数据中读取数据。

  • HashFilter: 表示一个用于计算哈希的过滤器。它接受一个哈希函数作为参数,这里是 md5

  • md5: 用于计算输入数据的 MD5 哈希值。

  • HexEncoder: 用于将二进制数据编码为十六进制表示。

  • StringSink(dst 或 digest): 用于将数据写入字符串。在这里,它将最终的哈希值以十六进制字符串的形式写入到 dstdigest 中。

#include <Windows.h>
#include <iostream>#include <md5.h>
#include <files.h>
#include <hex.h>using namespace std;
using namespace CryptoPP;#pragma comment(lib,"cryptlib.lib")int main(int argc, char* argv[])
{// 定义MD5类MD5 md5;// 计算字符串MD5string src = "Hello World";string dst;StringSource(src, true, new HashFilter(md5, new HexEncoder(new StringSink(dst))));std::cout << "字符串hash = " << dst << std::endl;// 计算字节数组MD5string digest;BYTE pData[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };DWORD dwDataSize = sizeof(pData);StringSource(pData, dwDataSize, true, new HashFilter(md5, new HexEncoder(new StringSink(digest))));std::cout << "数组长度 = " << dwDataSize << std::endl;std::cout << "数组hash = " << digest << std::endl;system("pause");return 0;
}

运行后则可分别输出字符串与数组的MD5值,如下图所示;

如果需要从文件中读取则需要使用FileSource类,在计算MD5之前先将文件读入内存在进行计算,如下所示;

#include <Windows.h>
#include <iostream>#include <md5.h>
#include <files.h>
#include <hex.h>using namespace std;
using namespace CryptoPP;#pragma comment(lib,"cryptlib.lib")// 计算文件MD5
string CalMD5ByFile(char *pszFileName)
{string value;MD5 md5;FileSource(pszFileName, true, new HashFilter(md5, new HexEncoder(new StringSink(value))));return value;
}// 计算数据MD5
string CalMD5ByMemory(PBYTE pData, DWORD dwDataSize)
{string value;MD5 md5;StringSource(pData, dwDataSize, true, new HashFilter(md5, new HexEncoder(new StringSink(value))));return value;
}int main(int argc, char* argv[])
{// 定义MD5类MD5 md5;// 计算文件的MD5string md51 = CalMD5ByFile("d://lyshark.exe");printf("md5 = %s\n", md51.c_str());// 计算文件内存的MD5HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);// 取文件长度DWORD dwFileSize2 = GetFileSize(hFile2, NULL);BYTE *pData2 = new BYTE[dwFileSize2];// 读文件到内存ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);// 计算MD5string md52 = CalMD5ByMemory(pData2, dwFileSize2);printf("md5 = %s\n", md52.c_str());system("pause");return 0;
}

如下图所示,是计算后得到的文件的MD5值;

使用CRC32算法

CRC32(Cyclic Redundancy Check,循环冗余校验)是一种广泛用于数据校验的错误检测算法。它基于多项式除法,在计算机领域中常用于检测数据传输或存储过程中的错误。

以下是CRC32算法的基本概述:

  1. 多项式选择: CRC32使用一个32位的二进制多项式,通常表示为一个32位的二进制数。这个多项式在CRC计算中充当除数。
  2. 数据处理: 要计算CRC32,首先需要将数据按位划分成块,每个块的长度等于多项式的次数。通常,CRC32使用字节为单位进行处理。
  3. 初始值: CRC32计算开始前,需要初始化一个32位的寄存器为一个特定的初始值,通常为全1或全0。
  4. 除法运算: 对于每个数据块,将它与32位的寄存器中的值进行异或操作。然后,将寄存器中的值右移一位,再与多项式进行异或操作。这个过程重复进行,直到所有数据块都被处理完。
  5. 最终值: 在处理完所有数据块后,寄存器中的值就是CRC32的最终校验值。
  6. 校验值附加: 通常,CRC32的结果会附加在原始数据的末尾,形成一个带有校验值的完整数据块。

CRC32广泛应用于文件传输、存储系统、以太网通信等领域,用于检测数据传输中的错误。由于其简单性和高效性,CRC32在实际应用中被广泛采用。然而,需要注意的是,CRC32主要用于错误检测而非安全性,不适用于对恶意操作的防范。在一些对安全性要求较高的场景中,其他更强大的校验算法可能更为合适。

crc32算法的使用只需要包含<crc.h>头文件,并将程序内的MD5类改为CRC32即可,其他的无任何差异,代码如下所示;

#include <Windows.h>
#include <iostream>#include <crc.h>
#include <files.h>
#include <hex.h>using namespace std;
using namespace CryptoPP;#pragma comment(lib,"cryptlib.lib")// 计算文件CRC32
string CalCRCByFile(char *pszFileName)
{string value;CRC32 crc;FileSource(pszFileName, true, new HashFilter(crc, new HexEncoder(new StringSink(value))));return value;
}// 计算数据CRC32
string CalCRCByMemory(PBYTE pData, DWORD dwDataSize)
{string value;CRC32 crc;StringSource(pData, dwDataSize, true, new HashFilter(crc, new HexEncoder(new StringSink(value))));return value;
}int main(int argc, char* argv[])
{// 定义CRC32类CRC32 crc32;// 计算文件的MD5string crc = CalCRCByFile("d://lyshark.exe");printf("crc32 = %s\n", crc.c_str());// 计算文件内存的crcHANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);// 取文件长度DWORD dwFileSize2 = GetFileSize(hFile2, NULL);BYTE *pData2 = new BYTE[dwFileSize2];// 读文件到内存ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);// 计算crcstring crc2 = CalCRCByMemory(pData2, dwFileSize2);printf("crc32 = %s\n", crc2.c_str());system("pause");return 0;
}

程序运行后将会计算文件的CRC32值,如下图所示;

使用SHA1算法

SHA-1(Secure Hash Algorithm 1)是一种常见的哈希函数,用于生成160位的散列值。与MD5类似,SHA-1也被广泛用于数字签名、数据完整性验证等领域。然而,由于SHA-1存在一些安全性漏洞,特别是对碰撞攻击的脆弱性,因此在对安全性要求较高的应用中,不再推荐使用SHA-1,而是转向使用更安全的哈希算法,如SHA-256或SHA-3。

以下是SHA-1算法的基本概述:

  1. 输入处理: SHA-1同样接受任意长度的输入,但输出为160位。输入被划分为512位的块,每个块包含16个32位字。
  2. 填充: 与MD5类似,如果输入长度不是512的倍数,需要对输入进行填充,使其满足条件。填充的方式是在消息的末尾添加一个’1’和零比特,然后添加一个64位整数,表示原始消息长度。
  3. 初始化: SHA-1有五个32位的寄存器(A、B、C、D、E),初始化为特定的常数。这些寄存器将在处理每个消息块时进行更新。
  4. 处理块: SHA-1的处理方式类似于MD5,但使用了不同的非线性函数和常量。每个消息块经过80个操作轮次,其中包括迭代、位运算和条件操作。
  5. 输出: SHA-1的输出是五个32位字的级联,通常以40位的十六进制数表示。这五个字的顺序是A、B、C、D、E。

由于SHA-1存在安全性问题,特别是在2017年被证明对碰撞攻击不再是安全的,因此已经不再被推荐用于安全性要求较高的应用。取而代之的是,SHA-256和SHA-3等更安全的哈希算法,它们提供更长的输出长度和更强的抗碰撞能力。

与MD5的计算方法一致,SHA系列计算方式只需引入<sha.h>系列头文件,并使用SHA1 sha1;类进行计算即可,如下代码所示;

#include <Windows.h>
#include <iostream>#include <sha.h>
#include <files.h>
#include <hex.h>using namespace std;
using namespace CryptoPP;#pragma comment(lib,"cryptlib.lib")// 计算文件SHA1
string CalSHA1ByFile(char *pszFileName)
{string value;SHA1 sha1;FileSource(pszFileName, true, new HashFilter(sha1, new HexEncoder(new StringSink(value))));return value;
}// 计算数据SHA1
string CalSHA1ByMemory(PBYTE pData, DWORD dwDataSize)
{string value;SHA1 sha1;StringSource(pData, dwDataSize, true, new HashFilter(sha1, new HexEncoder(new StringSink(value))));return value;
}int main(int argc, char* argv[])
{// 定义SHA类SHA1 sha1;// 计算文件的sha1string sha11 = CalSHA1ByFile("d://lyshark.exe");printf("sha1 = %s\n", sha11.c_str());// 计算文件内存的sha1HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);// 取文件长度DWORD dwFileSize2 = GetFileSize(hFile2, NULL);BYTE *pData2 = new BYTE[dwFileSize2];// 读文件到内存ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);// 计算sha1string sha12 = CalSHA1ByMemory(pData2, dwFileSize2);printf("sha1 = %s\n", sha12.c_str());system("pause");return 0;
}

sha1计算结果如下图所示;

使用SHA256算法

SHA-256(Secure Hash Algorithm 256-bit)是SHA-2(Secure Hash Algorithm 2)家族中的一种哈希函数,用于生成256位的散列值。SHA-256是目前广泛应用于各种安全领域的强大哈希算法,包括数字签名、证书签名、数据完整性验证等。SHA-256提供了更高的安全性,相对于之前的SHA-1和MD5来说更为强大。

以下是SHA-256算法的基本概述:

  1. 输入处理: SHA-256同样接受任意长度的输入,但输出为256位。输入被划分为512位的块,每个块包含16个32位字。
  2. 填充: 与SHA-1和MD5相似,如果输入长度不是512的倍数,需要对输入进行填充,以满足条件。填充的方式包括添加一个’1’和零比特,然后添加一个64位整数,表示原始消息长度。
  3. 初始化: SHA-256有八个32位的寄存器(A、B、C、D、E、F、G、H),初始化为特定的常数。这些寄存器将在处理每个消息块时进行更新。
  4. 处理块: SHA-256的处理方式包括64个操作轮次,每个轮次使用一个非线性函数、常量和消息块的子集。这些轮次通过循环结构连接起来。
  5. 输出: SHA-256的输出是八个32位字的级联,通常以64位的十六进制数表示。这八个字的顺序是A、B、C、D、E、F、G、H。

SHA-256相对于SHA-1和MD5提供了更高的抗碰撞能力和更强的安全性,使其成为当前广泛使用的哈希算法之一。然而,随着计算能力的增强,一些专家逐渐倾向于使用更长的哈希算法,如SHA-3,以适应未来更高的安全性需求。

代码调用上与sha1保持一致,Sha256同样只需要少量的更改,只要掌握了这个规律,那么则可以完成其他算法的调用,代码如下所示;

#include <Windows.h>
#include <iostream>#include <sha.h>
#include <files.h>
#include <hex.h>using namespace std;
using namespace CryptoPP;#pragma comment(lib,"cryptlib.lib")// 计算文件SHA1
string CalSHA1ByFile(char *pszFileName)
{string value;SHA256 sha256;FileSource(pszFileName, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));return value;
}// 计算数据SHA1
string CalSHA1ByMemory(PBYTE pData, DWORD dwDataSize)
{string value;SHA256 sha256;StringSource(pData, dwDataSize, true, new HashFilter(sha256, new HexEncoder(new StringSink(value))));return value;
}int main(int argc, char* argv[])
{// 定义SHA类SHA256 sha256;// 计算文件的sha256string sha11 = CalSHA1ByFile("d://lyshark.exe");printf("sha256 = %s\n", sha11.c_str());// 计算文件内存的sha256HANDLE hFile2 = CreateFile("d://lyshark.exe", GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_ARCHIVE, NULL);// 取文件长度DWORD dwFileSize2 = GetFileSize(hFile2, NULL);BYTE *pData2 = new BYTE[dwFileSize2];// 读文件到内存ReadFile(hFile2, pData2, dwFileSize2, NULL, NULL);// 计算sha256string sha12 = CalSHA1ByMemory(pData2, dwFileSize2);printf("sha256 = %s\n", sha12.c_str());system("pause");return 0;
}

运行后则可输出文件的sha256值,如下图所示;

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于UDP的网络聊天室

客户端 #include <myhead.h> //定义存储信息结构体 typedef struct _MSG {char code; //操作码&#xff1a;L表示登录C表示群聊S表示系统消息S表示退出char name[128]; char txt[256];}msg_t;//定义保存客户端网络信息的链表 typedef struct _ADDR {struct sockaddr_i…

玄学调参实践篇 | 深度学习模型 + 预训练模型 + 大模型LLM

&#x1f60d; 这篇主要简单记录一些调参实践&#xff0c;无聊时会不定期更新~ 文章目录 0、学习率与batch_size判断1、Epoch数判断2、判断模型架构是否有问题3、大模型 - 计算量、模型、和数据大小的关系4、大模型调参相关论文经验总结5、训练时模型的保存 0、学习率与batch_s…

Spring不再支持Java8了

在今天新建模块的时候发现了没有java8的选项了&#xff0c;结果一查发现在11月24日&#xff0c;Spring不再支持8了&#xff0c;这可怎么办呢&#xff1f;我们可以设置来源为阿里云https://start.aliyun.com/ 。 java8没了 设置URL为阿里云的地址

c++——string字符串____迭代器.范围for.修改遍历容量操作

在成为大人的路上喘口气. 目录 &#x1f393;标准库类型string &#x1f393;定义和初始化string对象 &#x1f4bb;string类对象的常见构造 &#x1f4bb;string类对象的不常见构造 &#x1f4bb;读写string对象 &#x1f393; string类对象的修改操作 &#x1f4…

爬虫http代理有什么用处?怎么高效使用HTTP代理?

在进行网络爬虫工作时&#xff0c;我们有时会遇到一些限制&#xff0c;比如访问频率限制、IP被封等问题。这时&#xff0c;使用HTTP代理可以有效地解决这些问题&#xff0c;提高爬虫的工作效率。本文将介绍爬虫HTTP代理的用处以及如何高效地使用HTTP代理。 一、爬虫HTTP代理的用…

【数据结构】单链表---C语言版

【数据结构】单链表---C语言版 一、顺序表的缺陷二、链表的概念和结构1.概念&#xff1a; 三、链表的分类四、链表的实现1.头文件&#xff1a;SList.h2.链表函数&#xff1a;SList.c3.测试函数&#xff1a;test.c 五、链表应用OJ题1.移除链表元素&#xff08;1&#xff09;题目…

京东数据产品推荐-京东数据挖掘-京东平台2023年10月滑雪装备销售数据分析

如今&#xff0c;滑雪正成为新一代年轻人的新兴娱乐方式&#xff0c;借助北京冬奥会带来的发展机遇&#xff0c;我国冰雪经济已逐渐实现从小众竞技运动到大众时尚生活方式的升级。由此也带动滑雪相关生意的增长&#xff0c;从滑雪服靴到周边设备&#xff0c;样样都需要消费者掏…

微信小程序 scrollview 滚动到指定位置

在微信小程序中&#xff0c;实现 ScrollView 滚动到指定位置有多种方法&#xff0c;下面将介绍三种主要的实现方式。 一、使用scroll-top属性实现滚动 通过设置 scroll-view 组件的 scroll-top 属性&#xff0c;我们可以实现滚动到指定位置。以下是具体实现方式&#xff1a; …

基于STM32单片机的智能家居系统设计(论文+源码)

1.系统设计 基于STM32单片机的智能家居系统设计与实现的具体任务&#xff1a; &#xff08;1&#xff09;可以实现风扇、窗帘、空调、灯光的开关控制&#xff1b; &#xff08;2&#xff09;具有语音识别功能&#xff0c;可以通过语音控制家电&#xff1b; &#xff08;3&a…

Win中Redis部署与配置

1.下载msi版本 下载传送门 2.双击next-->next安装安装 3.密码配置以及开机自启 在配置文件中配置相应配置进行配置密码以及端口和ip port 6379指定 Redis 监听端口&#xff0c;默认端口为 6379&#xff0c;作者在自己的一篇博文中解释了为什么选用 6379 作为默认端口&…

计算机网络 一到二章 PPT 复习

啥币老师要隔段时间测试&#xff0c;我只能说坐胡狗吧旁边 第一章 这nm真的会考&#xff0c;我是绷不住的 这nm有五种&#xff0c;我一直以为只有三种 广播帧在后面的学习中经常遇到 虽然老师在上课的过程中并没有太过强调TCP/IP的连接和断开&#xff0c;但我必须强调一下&…

全新付费进群系统源码 完整版教程

首先准备域名和服务器 安装环境&#xff1a;Nginx1.18 MySQL 5.6 php7.2 安装扩展sg11 伪静态thikphp 后台域名/admin账号admin密码123456 代理域名/daili账号admin密码123456 一、环境配置 二、建站上传源代码解压 上传数据库配置数据库信息 三、登入管理后台 后台域名/ad…

基于yolov2深度学习网络的打电话行为检测系统matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1、YOLOv2网络原理 4.2、基于YOLOv2的打电话行为检测 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 matlab2022a 3.部分核心程序 .................................…

人工智能-优化算法之动量法

对于嘈杂的梯度&#xff0c;我们在选择学习率需要格外谨慎。 如果衰减速度太快&#xff0c;收敛就会停滞。 相反&#xff0c;如果太宽松&#xff0c;我们可能无法收敛到最优解。 泄漏平均值 小批量随机梯度下降作为加速计算的手段。 它也有很好的副作用&#xff0c;即平均梯度…

Java---抽象类讲解

文章目录 1. 抽象类概述2. 抽象类特点3. 抽象类的成员特点4. 抽象类猫狗应用 1. 抽象类概述 在Java中&#xff0c;一个没有方法体的方法应该定义为抽象方法&#xff1b;而类中如果有抽象方法&#xff0c;该类必须定义为抽象类。 2. 抽象类特点 1. 抽象类和抽象方法必须使用abst…

C语言进阶指南(14)(部分字符串库函数及其模拟实现)

欢迎来到博主的专栏——C语言进阶指南 博主id&#xff1a;reverie_ly 文章目录 1、strlen&#xff08;&#xff09;——字符串长度计算函数自定义strlen函数的实现 2、strcpy——字符串拷贝函数strcpy的模拟实现 3.strcat——字符串追加函数strcat的模拟实现 4、strcmp——字符…

【计算机毕业设计】nodejs+vue音乐播放器系统 微信小程序83g3s

本系统的设计与实现共包含12个表:分别是配置文件信息表&#xff0c;音乐列表评论表信息表&#xff0c;音乐论坛信息表&#xff0c;歌手介绍信息表&#xff0c;音乐资讯信息表&#xff0c;收藏表信息表&#xff0c;token表信息表&#xff0c;用户表信息表&#xff0c;音乐类型信…

selenium使用记录

本文记录python环境下使用selenium的一些步骤 Step1&#xff1a;安装并配置驱动 pip install selenium # 使用pip在对应python中安装selenium包为了让selenium能调用指定的浏览器&#xff0c;需要下载对应浏览器的驱动程序&#xff08;这里以edge为例子&#xff09; #Firefo…

DockerCompose修改某个服务的配置(添加或编辑端口号映射)后如何重启单个服务使其生效

场景 docker-compose入门以及部署SpringBootVueRedisMysql(前后端分离项目)以若依前后端分离版为例&#xff1a; docker-compose入门以及部署SpringBootVueRedisMysql(前后端分离项目)以若依前后端分离版为例_docker-compose部署java mysql redis-CSDN博客 上面讲了docker c…

centos7-docker安装与使用

文章目录 一、docker简介1.1docker应用场景1.2docker的优点1.2.1快速&#xff0c;一致地交付应用程序1.2.2响应式部署和扩展1.2.3在同一硬件上运行更多工作负载 1.2docker的架构 二、docker的安装2.1新系统的环境搭建2.1.1更换yum源 2.2安装docker与卸载2.2.1yum安装docker2.2.…