• 基于openEuler的OpenSSL编译安装和编程基础


    基于openEuler的OpenSSL编译安装和编程基础

    有关OpenSSL的编译、安装、使用、编程我写过一篇博客Linux下OpenSSL的安装与使用.

    由于学校跟华为合作“智能基座”产教融合协同育人基地项目,我们把学习平台从原来的X64+Ubuntu的架构换成ARM64(鲲鹏)+openEuler的架构。

    ARM64 + openEuler环境可以在华为云购买ECS,同学们使用优惠券注意按下图选项购买:

    特别提醒:做完实验一定要关机,否则优惠券会很快用完!!!

    另外还可以自己购买个树莓派,自己安装openEuler,可以参考这个教程树莓派openEuler安装.

    下面演示基于华为云ECS,鲲鹏+openEuler架构。

    远程登录openEuler,我们看一下openEuler默认有没有安装OpenSSL,如下图所示,openEuler默认已经安装好了OpenSSL:

    当前openEuler系统安装的OpenSSL版本是1.1.1d,现在(2021.04)OpenSSL最新的版本是1.1.1k,还有一个问题,openEuler中没有变成开发需要的头文件。我们需要自己下载OpenSSL的源码编译安装。

    OpenSSL编译安装

    1. 我们去OpenSSL官网下载最新版本OpenSSL 1.1.1k的源码openssl-1.1.1k.tar.gz,然后把代码上传到openEuler云服务器中。
    2. 建立两个文件夹,分别放置OpenSSL的源码和安装路径,记住pwd运行的结果/root/rocopenssl
    mkdir rocopensslsrc rocopenssl
    cd rocopenssl
    pwd 
    
    1. 解压源代码到rocopensslsrc文件夹:
    tar -zxvf openssl-1.1.1k.tar.gz -C rocopensslsrc
    
    1. 配置编译安装路径(/root/rocopenssl),和openEuler系统默认的不同。
    cd rocopensslsrc/openssl-1.1.1k
    ./config --prefix=/root/rocopenssl
    
    1. 编译(make时间稍长,耐心等待),测试、安装,其中测试步骤可选:
    make
    make test
    make install
    
    1. 查看安装后的版本,确定是最新安装的1.1.1k版:
    cd ~/rocopenssl
    ./bin/openssl version
    

    注意用到的库还是旧版本的,这个问题怎么解决留作思考题吧

    1. 大家要注意的是rocopenssl下的include和lib的路径,我们后面编程要用到。
    /root/rocopenssl/include
    /root/rocopenssl/lib
    

    以上步骤可以使用下面的installopenssl.sh脚本完成:

    #! /bin/sh
    
    cd
    
    if [ ! -d rocopenssl ];
    then
        mkdir rocopenssl
        mkdir rocopensslsrc
    fi
    
    if [ ! -f ~/openssl-1.1.1k.tar.gz ]; 
    then
    	wget https://www.openssl.org/source/openssl-1.1.1k.tar.gz
    	tar -zxvf ~/openssl-1.1.1k.tar.gz -C ~/rocopensslsrc
    	
    	cd ~/rocopensslsrc/openssl-1.1.1k
    	./config --prefix=/root/rocopenssl
    	make
        make test
        make install
    	
    	~/rocopenssl/bin/openssl version
    fi
    

    OpenSSL命令的使用

    学会工具第一招,学会查看帮助文档,OpenSSL命令的帮助使用openssl help查看:

    OpenSSL命令下面有很多子命令分为三类:标准命令,消息摘要命令和密码命令。这些命令我们有两种用法,以最简单的产看版本命令为例,我们可以先输入openssl进入OpenSSL的功能界面后输入version,也可以直接在openEuler终端中直接输入openssl version进行查看,在实际应用中我们多采用后一种方法。

    对于子命令的使用帮助,我们可以有两种方式查看openssl help xxsubcmdopenssl xxsubcmd --help,比如用这两种方法查看消息摘要子命令的用法:

    openssl dgst 常用选项(options)有:

    [-md5|-md4|-md2|-sha1|-sha|-mdc2|-ripemd160|-dss1] :指定一种摘要算法

    -out filename:将摘要值保存到指定文件中

    比如我们用国密算法sm3计算besti的摘要,注意子命令的两种用法以及管道的用法:

    besti放到besti.txt文件中,计算文件的摘要:

    如何使用随机数产生对称加密算法的密钥?如何使用国密算法sm4进行对称加解密?如果产生sm2的公私钥对?如果用SM2算法加密解密,签名验签?这些作为思考题吧。

    OpenSSL编程

    密码算法库的功能非常强大,是OpenSSL的基础,它实现了现代密码学大部分主流的密码算法和标准,主要包括公开密钥(非对称)算法、对称加密算法、信息摘要算法、X509 数字证书标准、PKCS12个人信息交换语法标准、PKCS7 加密消息语法标准、OCSP 在线证书状态查询协议、 CRL 证书吊销列表等标准。同时 OpenSSL 还提供了Engine 机制;利用 Engine 可以将加密卡、加密机这样的外部硬件算法模块无缝集成到 OpenSSL 中。密码算法库在 Windows 下编译后其库文件为 libeay32.lib(如果编译成动态库则为 libeay32.dll),在 Linux 编译后其库文件名称为 libcrypto.a。

    现在的版本支持国密算法如SM2,SM3,SM4等算法。

    简单测试

    编写一个测试代码test_openssl.c:

    #include <stdio.h>
    #include <openssl/evp.h>
    
    int main(){
    	
        OpenSSL_add_all_algorithms();
    	
        return 0;
    }
    
    

    然后用下面命令编译:

    gcc -o to test_openssl.c -I /root/rocopenssl/include -L /root/rocopenssl/lib -lcrypto -lpthread
    

    执行./to;echo $?,结果打印0.

    BASE64算法

    OpenSS1在 Evp.h中提供了Base64 编码和解码的函数。Base64 编码就是把二进制数据转换为可见的 ASCIⅡ字符。Base64 解码是相反的过程。Base64编码后的数据方便在 Web 中传输以及可视化的复制和粘贴。

    OpenSSL 进行 Base64 编码的函数主要有EVP_EncodeInit、EVP_EncodeUpdate、EVP_EncodeFinal和 EVP_EncodeBlock。其中 EVP_EncodeInit、EVP_EncodeUpdateEVP_EncodeFinal 适用于处理不定长的大数据,EVP_EncodeBlock 适用于处理短数据。

    OpenSSL 进行 Base64 解码的函数主要有EVP_Decodelnit、EVP_DecodeUpdate、EVP_DecodeFinal和 EVP_DecodeBlock。其中 EVP_DecodeInit、EVP_DecodeUpdateEVP_DecodeFinal适用于处理不定长的大数据,EVP_DecodeBlock 适用于处理短数据。

    上面的函数可以完成消息的编码和解码,它们均定义在 openssl/evp.h 文件中。

    我们要注意的是XXXInit,XXXUpdte,XXXFinal的编程模式。

    我们写一个BASE64的测试代码testbase64.c :

    #include <stdio.h>
    #include <string.h>
    #include <openssl/evp.h>
    #include <openssl/x509.h>
    
    //Base64编码
    void tEVP_Encode()
    {
    	EVP_ENCODE_CTX *ctx;
            ctx = EVP_ENCODE_CTX_new();	//EVP编码结构体
    	unsigned char in[1024];			//输入数据缓冲区
    	int inl;						//输入数据长度
    	char out[2048]={0};				//输出数据缓冲区
    	int outl;						//输出数据长度
    	FILE *infp;						//输入文件句柄
    	FILE *outfp;					//输出文件句柄
    
    	infp = fopen("test.dat","rb");//打开待编码的文件
    	if(infp == NULL)
    	{
    		printf("Open File "Test.dat"  for Read Err.
    ");
    		return;
    	}
    	
    	outfp = fopen("test.txt","w");//打开编码后保存的文件
    	if(outfp == NULL)
    	{
    		printf("Open File "test.txt" For Write Err.
    ");
    		return;
    	}
    	EVP_EncodeInit(ctx);//Base64编码初始化
    	printf("文件"Test.dat" Base64编码后为:
    ");
    	//循环读取原文,并调用EVP_EncodeUpdate计算Base64编码
    	while(1)
    	{
    		inl = fread(in,1,1024,infp);
    		if(inl <= 0)
    			break;
    		EVP_EncodeUpdate(ctx,out,&outl,in,inl);//编码
    		fwrite(out,1,outl,outfp);//输出编码结果到文件
    		printf("%s",out);
    	} 
    	EVP_EncodeFinal(ctx,out,&outl);//完成编码,输出最后的数据。
    	fwrite(out,1,outl,outfp);
    	printf("%s",out);
    	fclose(infp);
    	fclose(outfp);	
    	printf("对文件"Test.dat" Base64编码完成,保存到"test.txt"文件.
    
    
    ");
    }
    
    //Base64解码
    void tEVP_Decode()
    {
    	EVP_ENCODE_CTX *ctx;
            ctx = EVP_ENCODE_CTX_new();			//EVP编码结构体
    	char in[1024];					//输入数据缓冲区
    	int inl;						//输入数据长度
    	unsigned char out[1024];		//输出数据缓冲区
    	int outl;						//输出数据长度
    	FILE *infp;						//输入文件句柄
    	FILE *outfp;					//输出文件句柄
    	
    	infp = fopen("test.txt","r");//打开待解码的文件
    	if(infp == NULL)
    	{
    		printf("Open File "Test.txt"  for Read Err.
    ");
    		return;
    	}
    	outfp = fopen("test-1.dat","wb");//打开解码后保存的文件
    	if(outfp == NULL)
    	{
    		printf("Open File "test-1.txt" For Write Err.
    ");
    		return;
    	}
    	EVP_DecodeInit(ctx);//Base64解码初始化
    	printf("开始对文件"Test.txt" Base64解码...
    
    ");
    	//循环读取原文,并调用EVP_DecodeUpdate进行Base64解码
    	while(1)
    	{
    		inl = fread(in,1,1024,infp);
    		if(inl <= 0)
    			break;
    		EVP_DecodeUpdate(ctx,out,&outl,in,inl);//Base64解码
    		fwrite(out,1,outl,outfp);//输出到文件
    	} 
    	EVP_DecodeFinal(ctx,out,&outl);//完成解码,输出最后的数据。
    	fwrite(out,1,outl,outfp);
    	fclose(infp);
    	fclose(outfp);	
    	printf("对文件"Test.txt" Base64解码完成,保存为"test-1.dat"
    
    
    ");
    	
    }
     
    int main()
    {
     
    	tEVP_Encode();
    	tEVP_Decode();
    	
    	return 0;
    }
    
    

    代码中有中文,编译运行可能出现乱码,使用gcc编译时可以使用如下参数:

    • -finput-charset 指定源文件(保存文件时选择)的编码方式(若不指定,编译器默认是UTF-8)
    • -fexec-charset 指定可执行程序中的字符以什么编码方式来表示,默认是UTF-8

    编译代码的命令是:

    gcc -o testbase64 testbase64.c -I /root/rocopenssl/include -L /root/rocopenssl/lib -lcrypto -lpthread -finput-charset=GBK -fexec-charset=UTF-8
    

    测试中的用到一个test.dat 是个二进制文件,可以用echo命令生成,或者用二进制编辑工具生成。

    执行./testbase64,结果如下图:

    作业

    1. 两人一组
    2. 基于Socket实现TCP通信,一人实现服务器,一人实现客户端
    3. 研究OpenSSL算法,测试对称算法中的SM4,非对称算法中的SM2,Hash算法中的SM3
    4. 选用合适的算法,基于混合密码系统实现对TCP通信进行机密性、完整性保护。
    5. 学有余力者,对系统进行安全性分析和改进。

    参考资料


    欢迎关注“rocedu”微信公众号(手机上长按二维码)

    做中教,做中学,实践中共同进步!

    rocedu



    如果你觉得本文对你有帮助,请点一下左下角的“好文要顶”和“收藏该文


  • 相关阅读:
    Sql server 经典常用函数
    Sql Server 时间格式化
    eval解析JSON中的注意点
    SQL Server 数据库try catch 存储过程
    SQL 添加索引
    sql中的begin catch 。。。end catch 的用法
    常用正则表达式
    css3实现背景渐变
    CacheHelper
    星座运势(等)接口控制器
  • 原文地址:https://www.cnblogs.com/rocedu/p/14617763.html
Copyright © 2020-2023  润新知