OpenSSL

密码学和SSL/TLS工具包

ossl-guide-libraries-introduction

名称

ossl-guide-libraries-introduction - OpenSSL指南:OpenSSL库的介绍

介绍

OpenSSL提供了两个可供应用程序使用的库,分别称为libcryptolibssl

libcrypto库提供了通用密码学的API,例如加密、数字签名、哈希函数等。它还提供对与密码学相关的标准的支持API,例如读取和写入数字证书(也称为X.509证书)。最后,它还提供了各种其他支持API,这些API不直接与密码学相关,但仍然有用且依赖于其他API。例如,“BIO”函数提供了抽象I/O的功能,例如通过文件或网络。

libssl库提供了在网络上两个对等体之间执行安全通信的功能。最重要的是,它实现了对SSL/TLS、DTLS和QUIC标准的支持。

libssl库依赖并使用libcrypto提供的许多功能。任何与libssl链接的应用程序也将与libcrypto链接,并且大多数执行此操作的应用程序将直接使用这两个库提供的API函数。

可以编写仅使用libcrypto功能且根本不与libssl链接的应用程序。

提供程序

除了这两个主要库之外,OpenSSL还带有一组提供程序。

OpenSSL中的提供程序是一个组件,它将算法实现收集在一起(例如,对称加密算法AES的实现)。为了使用算法,您必须至少加载一个包含其实现的提供程序。OpenSSL带有多个提供程序,也可以从第三方获取。

提供程序可以是“内置”的,也可以是单独的可加载模块文件(通常以“.so”或“.dll”结尾,具体取决于平台)。内置提供程序是指已经存在于libcrypto中或应用程序本身直接提供的提供程序。第三方也可以以可加载模块的形式提供提供程序。

如果您没有显式加载提供程序(在程序代码中或通过配置),则将自动加载OpenSSL内置的“默认”提供程序。

有关OpenSSL本身提供的提供程序的说明,请参见下面的"OPENSSL提供程序"

加载和卸载提供程序是一个非常昂贵的操作。它通常只执行一次,在应用程序生命周期的早期,并且这些提供程序在应用程序执行期间保持加载状态。

库上下文

许多OpenSSL API函数使用库上下文。库上下文可以被认为是配置选项生效的“范围”。加载提供程序时,它仅在给定库上下文的范围内加载。这样,复杂应用程序的不同组件可以分别使用不同的库上下文,并加载具有不同配置设置的不同提供程序。

如果应用程序没有显式创建库上下文,则将使用“默认”库上下文。

库上下文由OSSL_LIB_CTX类型表示。许多OpenSSL API函数将库上下文作为参数。应用程序始终可以为此参数传递NULL以仅使用默认库上下文。

第一次需要时会自动创建默认库上下文。这将自动加载任何可用的配置文件,并将初始化OpenSSL以供使用。与早期版本的OpenSSL(在1.1.0之前)不同,无需执行显式初始化步骤。

类似地,当应用程序退出时,默认库上下文将自动销毁。无需执行显式反初始化步骤。

有关库上下文的更多信息,请参见OSSL_LIB_CTX(3)。另请参见"算法获取"在ossl-guide-libcrypto-introduction(7)中

属性查询字符串

在某些情况下,可用的提供程序可能意味着任何给定算法的多个实现都可能可用。例如,OpenSSL FIPS提供程序提供许多相同算法的替代实现,这些算法在OpenSSL默认提供程序中可用。

选择算法实现的过程称为“获取”。当OpenSSL获取要使用的算法时,可以指定“属性查询字符串”来指导选择过程。例如,属性查询字符串“provider=default”可用于强制选择仅考虑默认提供程序中的算法实现。

属性查询字符串可以作为函数的参数显式指定。还可以使用EVP_set_default_properties(3)EVP_default_properties_enable_fips(3)函数为整个库上下文指定默认属性查询字符串。在指定默认属性和函数特定属性的情况下,将组合它们。函数特定属性将在存在冲突时覆盖默认属性。

有关获取的更多信息,请参见"算法获取"在ossl-guide-libcrypto-introduction(7)中。有关属性的更多信息,请参见property(7)

多线程应用程序

只要OpenSSL已构建为支持线程(大多数平台上的默认情况),那么大多数OpenSSL函数在某种意义上都是线程安全的,即可以安全地从多个线程同时调用同一函数。但是,大多数OpenSSL数据结构不是线程安全的。例如,BIO_write(3)BIO_read(3)函数是线程安全的。但是,如果在一个线程中调用BIO_write(),而在另一个线程中调用BIO_read(),则这两个函数都传递相同的BIO对象,则调用BIO_write()将不是线程安全的,因为这两个函数都可能尝试对相同的BIO对象进行更改。

这些规则有一些例外。少量函数根本不是线程安全的。在这种情况下,此限制应在函数的文档中进行说明。类似地,某些数据结构可能是部分或完全线程安全的。例如,始终可以安全地在多个线程中使用OSSL_LIB_CTX

有关OpenSSL线程支持的更详细讨论,请参见openssl-threads(7)

错误处理

大多数OpenSSL函数将提供一个返回值,指示函数是否已成功。始终检查OpenSSL函数的返回值(如果可用)被认为是最佳实践。

大多数返回指针值的函数在发生错误时将返回NULL。

大多数返回整数值的函数将返回正整数以指示成功。其中一些函数将返回0以指示失败。其他函数可能会返回0或负值以指示失败。

某些函数不会失败并且具有void返回类型。还有一些少量函数不符合上述约定(例如,它们可能返回0以指示成功)。

由于上述行为差异,务必检查每个函数的文档,以获取有关如何解释其返回值的信息。

有时需要获取有关失败原因的更多信息(例如,用于调试或记录目的)。许多(但不是全部)函数会将有关失败的更多信息添加到OpenSSL错误堆栈中。通过使用错误堆栈,您可以找到有关错误原因代码/字符串以及发出错误的OpenSSL中的确切文件和源代码行等信息。

OpenSSL提供了一组错误处理函数来查询错误堆栈。有关可用于查询错误数据的函数的信息,请参见ERR_get_error(3)。另请参见ERR_print_errors(3),了解有关一些用于打印错误数据的简单帮助函数的信息。最后,请查看ERR_clear_error(3),了解如何从错误堆栈中清除旧错误。

OPENSSL提供程序

OpenSSL带有一组提供程序。

每个提供程序中可用的算法可能因构建时配置选项而异。openssl-list(1)命令可用于列出当前可用的算法。

openssl-list(1)显示的算法名称可以用作相应获取函数的算法标识符。另请参见下面链接的提供程序特定手册页,以获取有关使用每个提供程序中可用的算法的更多详细信息。

除了OpenSSL提供程序之外,第三方也可以实现提供程序。有关编写提供程序的信息,请参见provider(7)

默认提供程序

默认提供程序作为libcrypto库的一部分内置,并包含所有最常用的算法实现。如果需要(如果加载了其他提供程序并提供了相同算法的实现),则属性查询字符串“provider=default”可用作这些实现的搜索条件。默认提供程序包括下面基本提供程序中的所有功能。

如果您根本不加载任何提供程序,则将自动加载“默认”提供程序。如果您显式加载任何提供程序,则如果需要“默认”提供程序,也需要显式加载它。

参见OSSL_PROVIDER-default(7)

基本提供程序

基本提供程序作为libcrypto库的一部分内置,并包含用于编码和解码OpenSSL密钥的算法实现。如果需要(如果加载了其他提供程序并提供了相同算法的实现),则属性查询字符串“provider=base”可用作这些实现的搜索条件。一些编码和解码算法实现本身不是FIPS算法实现,但支持来自FIPS提供程序的算法,并允许在“FIPS模式”下使用。属性查询字符串“fips=yes”可用于选择此类算法。

参见OSSL_PROVIDER-base(7)

FIPS提供程序

FIPS提供程序是可动态加载的模块,因此必须显式加载,无论是在代码中还是通过OpenSSL配置(请参见config(5))。它包含已根据FIPS标准验证的算法实现。如果需要(如果加载了其他提供程序并提供了相同算法的实现),则属性查询字符串“provider=fips”可用作这些实现的搜索条件。FIPS提供程序中所有已批准的算法实现也可以使用属性“fips=yes”进行选择。FIPS提供程序还可以包含未批准的算法实现,这些实现可以使用属性“fips=no”进行选择。

通常还需要加载"基本提供程序",因为FIPS提供程序不支持密钥的编码或解码。

参见OSSL_PROVIDER-FIPS(7)fips_module(7)

遗留提供程序

传统提供程序是一个动态加载的模块,因此必须显式加载,可以通过代码或 OpenSSL 配置加载(参见 config(5))。它包含被认为不安全的算法实现,或者不再常用,例如 MD2 或 RC4。如果需要(如果其他提供程序已加载并提供相同算法的实现),则属性“provider=legacy”可用作这些实现的搜索条件。

参见 OSSL_PROVIDER-legacy(7)

空提供程序

空提供程序作为 libcrypto 库的一部分内置。它根本不包含任何算法。获取算法时,如果尚未显式加载其他提供程序,则将自动加载默认提供程序。要阻止这种情况发生,可以显式加载空提供程序。

如果您创建了自己的库上下文并希望确保所有 API 调用都已正确传递创建的库上下文,并且没有意外使用默认库上下文,则可以使用此方法。将空提供程序加载到默认库上下文中,以便默认库上下文没有任何算法实现可用。

参见 OSSL_PROVIDER-null(7)

配置

默认情况下,OpenSSL 在首次使用时会加载配置文件。这将在默认库上下文中设置各种配置设置。创建自己的库上下文的应用程序可以选择使用 OSSL_LIB_CTX_load_config(3) 函数使用配置文件对其进行配置。

配置文件可用于自动加载提供程序并设置默认属性查询字符串。

有关 OpenSSL 配置文件格式的信息,请参见 config(5)

库约定

许多获取或设置值的 OpenSSL 函数遵循使用数字01的命名约定,即“get0”、“get1”、“set0”和“set1”。这也可以应用于某些向现有集合添加值的函数,即“add0”和“add1”。

例如,函数

int X509_CRL_add0_revoked(X509_CRL *crl, X509_REVOKED *rev);
int X509_add1_trust_object(X509 *x, const ASN1_OBJECT *obj);

0版本中,对象的拥有权传递给(对于添加或设置)或保留由(对于获取)父对象。例如,在调用上面的 X509_CRL_add0_revoked() 函数后,rev 对象的所有权将传递给crl 对象。因此,在调用此函数后,不应直接释放rev。它将在释放crl时隐式释放。

1版本中,对象的拥有权不会传递给或保留由父对象。而是执行对象的复制或“增加引用计数”。因此,在调用上面的 X509_add1_trust_object() 函数后,应用程序仍将负责在适当的地方释放obj值。

许多 OpenSSL 函数符合CLASSNAME_func_name()形式的命名约定。在此命名约定中,CLASSNAME是 OpenSSL 数据结构的名称(以大写字母表示),函数主要在其上操作。名称的func_name部分通常为小写字母,并指示函数的目的。

演示应用程序

OpenSSL 与一组演示应用程序一起分发,这些应用程序提供了一些有关如何使用各种 API 函数的示例。要查看它们,请从 OpenSSL 网站下载 OpenSSL 源代码 (https://www.openssl.org/source/)。提取您正在使用的 OpenSSL 版本的下载的.tar.gz文件,并查看demos子目录中的各种文件。

子目录中的 Makefile 提供了有关如何构建和运行演示应用程序的说明。

进一步阅读

有关使用libcrypto的更详细介绍,请参见 ossl-guide-libcrypto-introduction(7),有关libssl的更多信息,请参见 ossl-guide-libssl-introduction(7)

另请参见

openssl(1)ssl(7)evp(7)OSSL_LIB_CTX(3)openssl-threads(7)property(7)OSSL_PROVIDER-default(7)OSSL_PROVIDER-base(7)OSSL_PROVIDER-FIPS(7)OSSL_PROVIDER-legacy(7)OSSL_PROVIDER-null(7)openssl-glossary(7)provider(7)

版权所有 2000-2023 OpenSSL 项目作者。保留所有权利。

根据 Apache 许可证 2.0(“许可证”)获得许可。除非符合许可证,否则您不得使用此文件。您可以在源代码分发版中的 LICENSE 文件或 https://www.openssl.org/source/license.html 中获取副本。