OpenSSL 3.0 版的构成如下图所示:

openssl android so 下载 openssl 3.0.0_ide


        从图中可以看出,OpenSSL 包含以下组成部分:应用(例如具有证书颁发功能的 CA)、常见服务(包括:X.509 证书创建与解析、ASN.1 编解码、BIO、EVP 等)、协议实现(包括 TLS、DTLS、TS(时间戳)、OCSP 等)、核心及多个提供者(一般包含四个提供者:默认提供者、FIPS 提供者、引擎提供者、遗留提供者。第三方可以自行实现更多的提供者)。

        为了编译和安装 OpenSSL 3.0,应参考发行版软件包中的 INSTALL.md 文件。对于不同的平台,应参考与其对应的 NOTES 文件。在编译 OpenSSL 3.0 源码时,提供了多种不同的打包选择,比如可以生成一个单一的 libcrypto 库文件,并在其中包含剔除 FIPS 提供者后剩下的内容。还可以将所有的多个提供者打包成相互隔离的动态加载模块,在运行时可配置将要注册和使用哪一个动态加载模块。

        编译 OpenSSL 源码后生成的文件集合,如下图所示:

openssl android so 下载 openssl 3.0.0_OpenSSL_02


        从图中可以看出,在对 OpenSSL 代码进行编译后,将得到以下文件:

1)名为 openssl 的可执行程序,能够在命令行界面下运行;

2)libssl 库文件,实现了 TLS 等网络协议;

3)libcrypto 库文件,实现了常见服务、核心、默认提供者以及遗留 API;

4)FIPS 模块,充当 FIPS 提供者,是按照 FIPS 140-3《密码模块安全要求》开发的,包含密码算法实现、自检等功能的集合;

5)遗留模块,充当遗留提供者,实现了已被废弃的密码算法。

6)引擎,充当引擎提供者。它不是 OpenSSL 的一部分,而是一般由硬件厂商开发、供 OpenSSL 调用的模块,用于访问密码硬件;

7)第三方模块,充当第三方提供者。它不是 OpenSSL 的一部分,而是由第三方开发、供 OpenSSL 调用的模块,用于调用第三方自行实现的密码运算等功能。

   其中 FIPS 模块、遗留模块、引擎、第三方模块都是供核心调用的动态加载模块。

        FIPS 模块提供以下服务:
1)显示状态:如果“运行”状态被激活,获取状态的 API 接口函数将返回 1,否则返回 0;
2)密码服务:实现各种密码运算,例如加解密、数字签名与验签、计算消息认证码(MAC);
3)自检(按需 POST):OpenSSL 3.0 版中有一个 API 函数 FIPS_self_test( ),用于进行自检。只有在其他密码学服务未运行时,才能调用这个 API 函数做自检;
4)关键安全参数及密钥清零(CSP / Key Zeroization):当密码运算完成后,对运算过程中保存敏感数据的存储空间做置零操作,这样就擦除了内存中的敏感数据。

        FIPS 模块有以下特点:
1)FIPS 模块只支持动态加载,不支持静态链接;
2)在 FIPS 模块中,不允许生成 1024 比特及更短长度的 RSA 密钥,因为这种密钥长度已不再安全;
3)尽管 FIPS 标准规定一个 FIPS 模块可以调用另一个 FIPS 模块,但在 OpenSSL 3.0 中,出于简化问题的考虑,不允许同时调用多个 FIPS 模块。比如对于 EVP_DigestSign( ) 这个计算数字签名的函数,它会同时用到签名算法和摘要算法,OpenSSL 3.0 版中禁止这两个算法的实现来自不同的 FIPS 模块,要求算法实现都必须来自同一个 FIPS 模块;
4)在构建(Build)时,使用参数 -DFIPS_MODE 来构建 FIPS 提供者相关的文件,因为在源码中使用 #ifdef FIPS_MODE 宏定义限定了与 FIPS 有关的代码。

        对于提供者,有以下几点需要说明:
1)所有的算法都是由提供者实现的。核心一开始未加载任何提供者,因此核心此时还不能调用任何具体的算法实现。在加载提供者后,核心才能够查询提供者中包含的算法实现。如果在第一次调用“获取函数”(fetch function)时,没有加载任何提供者,内建的默认提供者将被自动加载。“核心 API” (Core API) 将保持稳定,但提供者部分的实现代码可能会经常进行更新。
2)在 UNIX 类型的操作系统中,提供者动态模块以 .so 文件形式出现;在 Windows 操作系统中,提供者动态模块以 .dll 文件形式出现。加载哪一个提供者,可以由应用程序指定,或通过一个配置文件来指定,还可以通过命令行参数来指定。例如对于OpenSSL 3.0 版提供的命令行程序,可以通过加上以下形式的命令参数来指定提供者文件所在的路径:
openssl  -provider_path  path_name
        还可以通过加上以下形式的命令参数指定提供者文件名:
openssl  -provider  provider_name
        如果在命令参数中同时包含 -provider_path 和 -provider,-provider_path 参数一定要位于 -provider 参数位置之前。因为只有先指定了提供者文件所在的路径,接下来才能找到提供者文件。
3)每一个提供者模块必须包含如下形式的“提供者模块入口点”(Provider Module Entry Point)函数:
    int OSSL_provider_init(const OSSL_PROVIDER *provider,
                                         const OSSL_DISPATCH *in,
                                         const OSSL_DISPATCH **out,
                                         void **provider_ctx);
函数参数说明:
provider  —— 是一个指向提供者对象的句柄,可以唯一确定一个提供者的身份标识;
in            —— 是一个函数列表,核心将这些函数传递给提供者;
out          —— 是一个提供者内部包含函数的列表,提供者将它回传给核心;
provider_ctx  —— 是一个可选的对象,由提供者创建,用于安全地存储一些自用的数据。