热门标签 | HotTags
当前位置:  开发笔记 > 后端 > 正文

openssl3.0加密算法库编程精要02–加密一段数据吧!

 2.1让我们从Provider谈起相对于老版本的Openssl,新的Openssl3.0+的一个关键改变是提出了一个Provider的概念,我将它命名为安全服务提供者。安

 


  2.1 让我们从 Provider 谈起

  相对于老版本的 Openssl,新的 Openssl3.0+ 的一个关键改变是提出了一个 Provider 的概念,我将它命名为安全服务提供者。

安全服务提供者集成了相应的算法实现,用户可以采用编程的方式或者在配置文件中指定的方式来使用这些安全服务。Openssl 提

供了 5 中不同的标准安全服务,而且我们也可以按照一定的规范自己实现安全服务相关的算法,然后通过 Openssl 提供的顶层的

API 来向用户提供安全服务,用户无需再像以前那样调用具体的底层的 API。

  Openssl 内置的 5 种安全服务分别为:

  (1)Default Provider -- 默认安全服务,包含了大多数通用的、主流的算法实现;

  (2)Base Provider -- 基本安全服务,包含了一小部分算法的密钥编解码实现;

  (3)FIPS Provider -- FIPS(联邦信息处理标准) 安全服务,包含了根据FIPS 140-2标准验证的算法实现,这个是美国政府的

    标准;

  (4)Legacy Provider -- 历史遗留算法的安全服务,包含了一些相对过时的、已于当前证明不安全的算法,如 MD2、RC4 等;

  (5)Null Provider -- 空安全服务,如果你不想让 Default Provider 被自动加载,可以手动加载这个空安全服务替代它。

 


  2.2 library context -- 库上下文的概念

  除了安全服务, Openssl 还提供了一个 Library Context (库上下文)的概念。

  库上下文可以被看做是一个安全服务执行操作的作用域,每一个安全服务都被限定在自己的库上下文作用域进行操作。对于复杂

的应用来说,有可能要同时用到不同的安全服务,库上下文可以保证不同配置的安全服务同时存在且互不干扰。

  库上下文在 Openssl 中被指定为 OSSL_LIB_CTX  类型。应用程序在使用 Openssl 的过程中如果没有显式创建 OSSL_LIB_CTX

对象,那么就需要使用 “default” 类型的库上下文。大多数的 Openssl API 都会带有一个 OSSL_LIB_CTX 类型的参数,我们可以显式

指定,也可以传 NULL 来使用 Openssl 提供的 “default” 库上下文。

   新版的 Openssl 默认库上下文会在需要时自动被创建,所以我们不需要再像使用旧版 Openssl 那样手动初始化所有的算法,而且

在应用退出的时候,默认的库上下文也会自动销毁,无需我们手动操作。

 


  2.3 获取算法

  那么到底如何才能使用 Openssl 提供的算法呢?我们首先从 Openssl 对外提供的头文件中,找到 provider.h 和 evp.h。provider.h

声明了安全服务相关的一系列操作,evp.h 则包含了算法相关的高层 API。然后我们创建一个 main.c,代码如下所示(读者应从 main

函数读起):

#include
#include
#include
#include "trace/trace.h"
#define SUCCESS 0 /* 成功 */
#define FAILED -1 /* 失败 */
#define ALGO_NAME "SM4-ECB"
/* 定义缓冲区长度 */
#define DATA_BUF_LEN 128
/* 全局缓冲区 */
static unsigned char DATA_BUF[DATA_BUF_LEN] = { 0 };
/* 定义二进制数据块 */
struct BIN_DATA {
unsigned char *data; /* 数据首地址 */
int len; /* 数据长度(字节) */
};
/* 加密数据 */
int encrypt_data(EVP_CIPHER_CTX *ctx, const struct BIN_DATA *in, struct BIN_DATA *enc)
{
EVP_CIPHER *cipher = NULL;
int ret = FAILED;
unsigned char key[16] = "123456788765432";
int padlen = 0;
/* 这里仅做简单的判断,实际的生产环境要对各种边界条件做细致的检查,确保应用程序的可靠性 */
if (!ctx || !in || !enc) {
return FAILED;
}
/* 获取算法 */
cipher = EVP_CIPHER_fetch(NULL, ALGO_NAME, NULL);
if (!cipher) {
return FAILED;
}
/* 加密初始化 */
if (EVP_EncryptInit_ex(ctx, cipher, NULL, key, NULL) != 1) {
goto end;
}
/* 加密 */
if (EVP_EncryptUpdate(ctx, enc->data, &enc->len, in->data, in->len) != 1) {
goto end;
}
/* 加密结束,处理一些需要填充的数据 */
if (EVP_EncryptFinal_ex(ctx, enc->data + enc->len, &padlen) != 1) {
goto end;
}
/* 计算最终长度 */
enc->len += padlen;
ret = SUCCESS;
end:
EVP_CIPHER_free(cipher);
return ret;
}
/* 解密数据 */
int decrypt_data(EVP_CIPHER_CTX *ctx, const struct BIN_DATA *enc, struct BIN_DATA *dec)
{
EVP_CIPHER *cipher= NULL;
int ret = FAILED;
unsigned char key[16] = "123456788765432";
int padlen = 0;
if (!ctx || !enc || !dec) {
return FAILED;
}
cipher = EVP_CIPHER_fetch(NULL, ALGO_NAME, NULL);
if (!cipher) {
return FAILED;
}
if (EVP_DecryptInit_ex(ctx, cipher, NULL, key, NULL) != 1) {
goto end;
}
if (EVP_DecryptUpdate(ctx, dec->data, &dec->len, enc->data, enc->len) != 1) {
goto end;
}
if (EVP_DecryptFinal_ex(ctx, dec->data + dec->len, &padlen) != 1) {
goto end;
}
dec->len += padlen;
ret = SUCCESS;
end:
EVP_CIPHER_free(cipher);
return ret;
}
/**
* 采用 sm4 算法加密一段字符串
*/
int main(int argc, char *argv[])
{
int ret = FAILED;
char data[] = "1234567890";
int len = sizeof(data);
/* 原文 */
struct BIN_DATA in = {
(unsigned char *)data,
len
};
/* 加密缓冲区 */
struct BIN_DATA enc = {
DATA_BUF,
DATA_BUF_LEN / 2
};
/* 解密缓冲区 */
struct BIN_DATA dec = {
DATA_BUF + DATA_BUF_LEN / 2,
DATA_BUF_LEN / 2
};
/* 加载 default provider */
OSSL_PROVIDER *prov = OSSL_PROVIDER_load(NULL, "default");
/* 创建上下文 */
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
/* 打印原文数据 */
TRACE_BIN("原文数据", in.data, in.len);
/* 加密数据 */
ret = encrypt_data(ctx, &in, &enc);
if (ret != SUCCESS) {
TRACE("加密失败!\n");
goto end;
}
/* 打印加密数据 */
TRACE_BIN("加密数据", enc.data, enc.len);
/* 解密数据 */
ret = decrypt_data(ctx, &enc, &dec);
if (ret != SUCCESS) {
TRACE("解密失败!\n");
goto end;
}
/* 打印解密数据 */
TRACE_BIN("解密数据", dec.data, dec.len);
ret = SUCCESS;
end:
if (ctx) {
EVP_CIPHER_CTX_free(ctx);
}
/* 卸载默认 default provider */
OSSL_PROVIDER_unload(prov);
return ret;
}  

  trace/trace.h 是笔者实现的一个用于打印日志的模块,内容如下:

#ifndef __TRACE_H__
#define __TRACE_H__
#include
/* 打印日志 */
#define TRACE(log, ...) printf((log), ##__VA_ARGS__)
/* 打印二进制数据 */
#define TRACE_BIN(desc, pt, len) trace_bin((desc), (pt), (len))
/* 打印二进制数据 */
void trace_bin(const char *desc, const void *data, int len);
#endif /* __TRACE_H__ */

  注:trace_bin 函数用于打印二进制数据块,读者感兴趣可以自己实现,原理非常简单,这里不再展示。

  如果用户仅使用 “default” 这一个安全服务,则不需要手动调用 OSSL_PROVIDER_load 和 OSSL_PROVIDER_unload,Openssl

会自动为我们加载它。

   以上程序的最终结果如下所示:

原文数据 size:11
------------------------+------------------------
31 32 33 34 35 36 37 38 | 39 30 00
------------------------+------------------------
加密数据 size:16
------------------------+------------------------
34 b2 cf 92 94 08 c4 10 | 7b ea 27 61 cb 77 8f 64
------------------------+------------------------
解密数据 size:11
------------------------+------------------------
31 32 33 34 35 36 37 38 | 39 30 00
------------------------+------------------------

  2.4 总结

  综上,我们会发现 openssl 的算法基本都是一样的形式:

  (1)创建上下文;

  (2)获取算法;

  (3)调用 EVP_***Init

  (4)调用 EVP_***Update

  (5)调用 EVP_***Final

  (6)销毁算法和上下文;

  所以,我们如果想要使用诸如 AES、或者 3DES 等加密算法的话,只需要将算法名称和相应的密钥长度略作调整即可。如果用户

想要获取相应的算法信息,如密钥长度或者加密的分组长度,在获取算法实现后,调用 EVP_CIPHER_get_key_length 来获取密钥长度,

调用EVP_CIPHER_get_block_size 来获取分组长度,Openssl 专门为对称算法提供了很多功能性的函数,这里我准备放在下一章节阐述

  以上便是本章所有内容。

 

  参考资料:

  1.Openssl 官方文档 -- https://www.openssl.org/docs/

  2.Openssl Wiki -- https://wiki.openssl.org/index.php/Main_Page

 

  



推荐阅读
  • macOS Big Sur全新设计大版本更新,10+个值得关注的新功能
    本文介绍了Apple发布的新一代操作系统macOS Big Sur,该系统采用全新的界面设计,包括图标、应用界面、程序坞和菜单栏等方面的变化。新系统还增加了通知中心、桌面小组件、强化的Safari浏览器以及隐私保护等多项功能。文章指出,macOS Big Sur的设计与iPadOS越来越接近,结合了去年iPadOS对鼠标的完善等功能。 ... [详细]
  • 本文分享了一位Android开发者多年来对于Android开发所需掌握的技能的笔记,包括架构师基础、高级UI开源框架、Android Framework开发、性能优化、音视频精编源码解析、Flutter学习进阶、微信小程序开发以及百大框架源码解读等方面的知识。文章强调了技术栈和布局的重要性,鼓励开发者做好学习规划和技术布局,以提升自己的竞争力和市场价值。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 恶意软件分析的最佳编程语言及其应用
    本文介绍了学习恶意软件分析和逆向工程领域时最适合的编程语言,并重点讨论了Python的优点。Python是一种解释型、多用途的语言,具有可读性高、可快速开发、易于学习的特点。作者分享了在本地恶意软件分析中使用Python的经验,包括快速复制恶意软件组件以更好地理解其工作。此外,作者还提到了Python的跨平台优势,使得在不同操作系统上运行代码变得更加方便。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • 无线认证设置故障排除方法及注意事项
    本文介绍了解决无线认证设置故障的方法和注意事项,包括检查无线路由器工作状态、关闭手机休眠状态下的网络设置、重启路由器、更改认证类型、恢复出厂设置和手机网络设置等。通过这些方法,可以解决无线认证设置可能出现的问题,确保无线网络正常连接和上网。同时,还提供了一些注意事项,以便用户在进行无线认证设置时能够正确操作。 ... [详细]
  • 图解redis的持久化存储机制RDB和AOF的原理和优缺点
    本文通过图解的方式介绍了redis的持久化存储机制RDB和AOF的原理和优缺点。RDB是将redis内存中的数据保存为快照文件,恢复速度较快但不支持拉链式快照。AOF是将操作日志保存到磁盘,实时存储数据但恢复速度较慢。文章详细分析了两种机制的优缺点,帮助读者更好地理解redis的持久化存储策略。 ... [详细]
  • Win10 64位旗舰版的优势及特点详解
    本文详细介绍了Win10 64位旗舰版的优势及特点,包括更安全的源安装盘、永久激活方式、稳定性和硬件驱动的集成,以及人性化的维护工具和分区功能。通过阅读本文,您将了解到Win10 64位旗舰版相比其他版本的优势和特点。 ... [详细]
  • 集成电路企业在进行跨隔离网数据交换时面临着安全性问题,传统的数据交换方式存在安全性堪忧、效率低下等问题。本文以《Ftrans跨网文件安全交换系统》为例,介绍了如何通过丰富的审批流程来满足企业的合规要求,保障数据交换的安全性。 ... [详细]
  • 如何提高PHP编程技能及推荐高级教程
    本文介绍了如何提高PHP编程技能的方法,推荐了一些高级教程。学习任何一种编程语言都需要长期的坚持和不懈的努力,本文提醒读者要有足够的耐心和时间投入。通过实践操作学习,可以更好地理解和掌握PHP语言的特异性,特别是单引号和双引号的用法。同时,本文也指出了只走马观花看整体而不深入学习的学习方式无法真正掌握这门语言,建议读者要从整体来考虑局部,培养大局观。最后,本文提醒读者完成一个像模像样的网站需要付出更多的努力和实践。 ... [详细]
  • 2021最新总结网易/腾讯/CVTE/字节面经分享(附答案解析)
    本文分享作者在2021年面试网易、腾讯、CVTE和字节等大型互联网企业的经历和问题,包括稳定性设计、数据库优化、分布式锁的设计等内容。同时提供了大厂最新面试真题笔记,并附带答案解析。 ... [详细]
  • 玩转直播系列之消息模块演进(3)
    一、背景即时消息(IM)系统是直播系统重要的组成部分,一个稳定的,有容错的,灵活的,支持高并发的消息模块是影响直播系统用户体验的重要因素。IM长连接服务在直播系统有发挥着举足轻重的 ... [详细]
author-avatar
LKD2008_561
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有