crypto
NAME
crypto - OpenSSL 加密库
SYNOPSIS
有关详细信息,请参阅各个手册页。
DESCRIPTION
OpenSSL 加密库 ( libcrypto
) 实现了各种 Internet 标准中使用的各种加密算法。该库提供的服务由 TLS 和 CMS 的 OpenSSL 实现使用,并且还用于实现许多其他第三方产品和协议。
其功能包括对称加密、公钥加密、密钥协商、证书处理、加密散列函数、加密伪随机数生成器、消息认证码 (MAC)、密钥派生函数 (KDF) 和各种实用程序。
算法
SHA256 摘要或 AES 加密等加密原语在 OpenSSL 中称为“算法”。每个算法可能有多种可供使用的实现。例如,RSA 算法可作为适合一般用途的“默认”实现,以及已根据 FIPS 标准进行验证的“fips”实现,适用于重要的情况。第三方也有可能添加额外的实现,例如在硬件安全模块 (HSM) 中。
运营
不同的算法可以按其目的分组在一起。例如,有用于加密的算法,以及用于消化数据的不同算法。这些不同的组在 OpenSSL 中称为“操作”。每个操作都有一组不同的与其关联的功能。例如,要使用 AES(或任何其他加密算法)执行加密操作,您可以使用EVP_EncryptInit(3)页上详细介绍的加密函数。或者要使用 SHA256 执行摘要操作,则可以使用EVP_DigestInit(3)页面上的摘要函数。
供应商
OpenSSL 中的提供程序是一个将算法实现收集在一起的组件。为了使用一种算法,您必须至少加载一个包含该算法实现的提供程序。OpenSSL 附带了许多提供商,它们也可以从第三方获得。如果您没有显式加载提供程序(无论是在程序代码中还是通过配置),则将自动加载 OpenSSL 内置“默认”提供程序。
图书馆环境
库上下文可以被认为是配置选项生效的“范围”。当加载提供程序时,它仅在给定库上下文的范围内加载。通过这种方式,复杂应用程序的不同组件可以各自使用不同的库上下文,并让不同的提供程序加载不同的配置设置。
如果应用程序没有显式创建库上下文,则将使用“默认”库上下文。
库上下文由OSSL_LIB_CTX类型 表示。许多 OpenSSL API 函数将库上下文作为参数。应用程序始终可以为此参数 传递NULL ,以仅使用默认库上下文。
默认库上下文会在第一次需要时自动创建。这将自动加载任何可用的配置文件并初始化 OpenSSL 以供使用。与 OpenSSL 的早期版本(1.1.0 之前)不同,不需要执行显式的初始化步骤。
同样,当应用程序退出时,默认库上下文会自动销毁。不需要采取明确的反初始化步骤。
有关库上下文的更多信息, 请参阅OSSL_LIB_CTX(3) 。另请参阅“算法获取”。
多线程应用程序
只要 OpenSSL 构建时支持线程(大多数平台上的默认情况),那么大多数 OpenSSL函数都是线程安全的,因为同时从多个线程调用同一函数是安全的。然而,大多数 OpenSSL数据结构都不是线程安全的。例如,BIO_write(3)和BIO_read(3)函数是线程安全的。然而,从一个线程调用 BIO_write() 而在另一个线程中调用 BIO_read() 则不是线程安全的,其中两个函数都传递同一个BIO对象,因为它们都可能尝试对同一个BIO对象进行更改。
这些规则也有例外。少数函数根本不是线程安全的。在这种情况下,应在该函数的文档中注明此限制。类似地,某些数据结构可能是部分或完全线程安全的。例如,在多个线程中 使用OSSL_LIB_CTX是安全的。
有关 OpenSSL 线程支持的更详细讨论, 请参阅openssl-threads(7) 。
ALGORITHM FETCHING
为了使用算法,必须首先“获取”算法的实现。获取是查看可用实现、应用选择标准(通过属性查询字符串)并最终选择将使用的实现的过程。
OpenSSL 支持两种类型的获取 - 显式获取和隐式获取。
属性查询字符串
获取算法时,可以指定属性查询字符串来指导选择过程。例如,属性查询字符串“provider=default”可用于强制选择仅考虑默认提供程序中的算法实现。
属性查询字符串可以显式指定为函数的参数。还可以使用EVP_set_default_properties(3)或EVP_default_properties_enable_fips(3)函数为整个库上下文指定默认属性查询字符串。如果指定了默认属性和特定于功能的属性,则将它们组合起来。如果存在冲突,函数特定属性将覆盖默认属性。
有关属性的更多信息, 请参阅属性(7) 。
显式抓取
OpenSSL 库的用户从不直接向提供商查询算法实现。相反,各种 OpenSSL API 通常具有执行该工作的显式获取函数,并且它们将适当的算法对象返回给用户。这些函数通常具有名称APINAME_fetch
,其中APINAME
是操作的名称。例如,EVP_MD_fetch(3)可用于显式获取摘要算法实现。用户负责在不再需要时
释放从APINAME_fetch
函数返回的对象。APINAME_free
这些获取函数遵循相当常见的模式,其中传递三个参数:
- The library context
-
更详细的描述请 参见OSSL_LIB_CTX(3) 。这可能是 NULL,表示默认(全局)库上下文或用户创建的上下文。获取函数只会考虑在此库上下文中加载的提供程序(请参阅OSSL_PROVIDER_load(3) )。如果此库上下文中没有加载提供程序,则将加载默认提供程序作为后备(请参阅OSSL_PROVIDER-default(7))。
- An identifier
-
对于所有当前实现的获取函数,这是算法名称。
- A property query string
-
用于指导算法实现选择的属性查询字符串。
然后,获取的算法实现可以与使用它们的其他不同函数一起使用。例如,EVP_DigestInit_ex(3)函数将EVP_MD对象作为参数,该对象可能是从先前调用EVP_MD_fetch(3)返回的。
隐式抓取
OpenSSL 有许多函数返回没有关联实现的算法对象,例如EVP_sha256(3)、EVP_aes_128_cbc(3)、EVP_get_cipherbyname(3)或EVP_get_digestbyname(3)。这些是为了与 3.0 版本之前的 OpenSSL 兼容而存在,其中显式获取不可用。
当它们与EVP_DigestInit_ex(3)或EVP_CipherInit_ex(3)等函数一起使用时,将使用默认搜索条件隐式获取要使用的实际实现。
在某些情况下,当提供 NULL 算法参数时,也可能会发生隐式获取。在这种情况下,使用默认搜索条件和与其使用上下文一致的算法名称隐式获取算法实现。
围绕EVP_PKEY_CTX和EVP_PKEY(3)的函数(例如EVP_DigestSignInit(3)等)都隐式获取实现。由于这些函数涉及操作类型(例如EVP_SIGNATURE(3))和EVP_PKEY (3)的EVP_KEYMGMT( 3),因此它们尝试以下操作:
-
给定存储在EVP_PKEY_CTX 中的库上下文和属性字符串,从任何提供程序获取操作类型实现。
如果操作类型实现的提供程序与EVP_PKEY(3)的EVP_KEYMGMT(3)实现的提供程序不同,请尝试在与操作类型实现相同的提供程序中获取EVP_KEYMGMT(3)实现并导出EVP_PKEY( 3)它(有效地制作原始密钥的临时副本)。
如果此步骤中的任何操作失败,则使用下一步作为后备。
-
作为后备方案,尝试从与原始EVP_PKEY(3)的EVP_KEYMGMT(3)相同的提供程序获取操作类型实现,仍使用EVP_PKEY_CTX中的属性字符串。
表现
如果多次执行相同的操作,则建议最初使用“显式获取”预取算法一次,然后将此创建的对象传递给当前使用“隐式获取”的任何操作。请参阅“在应用程序中使用算法”中的显式获取示例。
在 OpenSSL 3.0 之前,直接使用常量方法表(例如 EVP_sha256())来访问方法。如果将这些便利函数之一传递给操作,则固定方法将被忽略,并且仅使用名称来从提供程序内部获取方法。
如果未将预取对象传递给操作,则任何隐式获取都将使用内部缓存的预取对象,但仍然比直接传递预取对象慢。
通过提供程序获取提供了更大的灵活性,但它比旧方法慢,因为它必须在所有加载的提供程序中搜索算法,然后使用提供程序提供的方法填充方法表。OpenSSL 在内部在第一次获取时缓存类似的算法(因此加载摘要会缓存所有摘要)。
可以使用以下方法进行预取:
- EVP_MD_fetch(3)
- EVP_CIPHER_fetch(3)
- EVP_KDF_fetch(3)
- EVP_MAC_fetch(3)
- EVP_KEM_fetch(3)
- OSSL_ENCODER_fetch(3)
- OSSL_DECODER_fetch(3)
- EVP_RAND_fetch(3)
执行操作时内部使用以下方法:
请参阅OSSL_PROVIDER-default(7)、 <OSSL_PROVIDER-fips(7)> 和 <OSSL_PROVIDER-legacy(7)> 以获取可获取的算法名称列表。
FETCHING EXAMPLES
以下部分提供了一系列获取算法实现的示例。
在默认上下文中获取 SHA2-256 的任何可用实现。请注意,某些算法有别名。所以“SHA256”和“SHA2-256”是同义词:
EVP_MD *md = EVP_MD_fetch(NULL, "SHA2-256", NULL);
...
EVP_MD_free(md);
在默认上下文中获取 AES-128-CBC 的任何可用实现:
EVP_CIPHER *cipher = EVP_CIPHER_fetch(NULL, "AES-128-CBC", NULL);
...
EVP_CIPHER_free(cipher);
从默认上下文中的默认提供程序获取 SHA2-256 的实现:
EVP_MD *md = EVP_MD_fetch(NULL, "SHA2-256", "provider=default");
...
EVP_MD_free(md);
获取不是来自默认上下文中的默认提供程序的 SHA2-256 实现:
EVP_MD *md = EVP_MD_fetch(NULL, "SHA2-256", "provider!=default");
...
EVP_MD_free(md);
从指定上下文中的默认提供程序获取 SHA2-256 的实现:
EVP_MD *md = EVP_MD_fetch(ctx, "SHA2-256", "provider=default");
...
EVP_MD_free(md);
将遗留提供程序加载到默认上下文中,然后从中获取 WHIRLPOOL 的实现:
/* This only needs to be done once - usually at application start up */
OSSL_PROVIDER *legacy = OSSL_PROVIDER_load(NULL, "legacy");
EVP_MD *md = EVP_MD_fetch(NULL, "WHIRLPOOL", "provider=legacy");
...
EVP_MD_free(md);
请注意,在上面的示例中,属性字符串“provider=legacy”是可选的,因为假设没有加载其他提供程序,“whirlpool”算法的唯一实现是在“legacy”提供程序中。另请注意,如果除了其他提供程序之外还需要默认提供程序,则应显式加载默认提供程序:
/* This only needs to be done once - usually at application start up */
OSSL_PROVIDER *legacy = OSSL_PROVIDER_load(NULL, "legacy");
OSSL_PROVIDER *default = OSSL_PROVIDER_load(NULL, "default");
EVP_MD *md_whirlpool = EVP_MD_fetch(NULL, "whirlpool", NULL);
EVP_MD *md_sha256 = EVP_MD_fetch(NULL, "SHA2-256", NULL);
...
EVP_MD_free(md_whirlpool);
EVP_MD_free(md_sha256);
OPENSSL PROVIDERS
OpenSSL 附带了一组提供程序。
每个提供程序中可用的算法可能因构建时配置选项而异。openssl -list(1)命令可用于列出当前可用的算法。
openssl-list(1) 中显示的算法名称可用作适当获取函数的算法标识符。另请参阅下面链接的特定于提供程序的手册页,了解有关使用每个提供程序中可用算法的更多详细信息。
除了 OpenSSL 提供商之外,第三方也可以实施提供商。有关编写提供程序的信息,请参阅提供程序(7)。
默认提供商
默认提供程序作为libcrypto库 的一部分内置,并包含所有最常用的算法实现。如果需要(如果加载其他提供程序并提供相同算法的实现),属性查询字符串“provider=default”可以用作这些实现的搜索条件。默认提供程序包括下面基本提供程序中的所有功能。
如果您根本不加载任何提供程序,那么将自动加载“默认”提供程序。如果您显式加载任何提供程序,那么“默认”提供程序也需要在需要时显式加载。
基地提供商
基本提供程序作为libcrypto库 的一部分内置,并包含用于 OpenSSL 密钥编码和解码的算法实现。如果需要(如果加载了其他提供程序并提供相同算法的实现),属性查询字符串“provider=base”可以用作这些实现的搜索条件。某些编码和解码算法实现本身不是 FIPS 算法实现,但支持 FIPS 提供商的算法,并允许在“FIPS 模式”下使用。属性查询字符串“fips=yes”可用于选择此类算法。
FIPS 提供商
FIPS 提供程序是一个动态可加载模块,因此必须以代码或通过 OpenSSL 配置显式加载(请参阅config(5))。它包含已根据 FIPS 140-2 标准验证的算法实现。如果需要(如果加载其他提供程序并提供相同算法的实现),属性查询字符串“provider=fips”可以用作这些实现的搜索条件。FIPS 提供程序中所有批准的算法实现也可以通过属性“fips=yes”进行选择。FIPS 提供程序还可能包含未经批准的算法实现,可以使用属性“fips=no”来选择这些算法。
请参阅OSSL_PROVIDER-FIPS(7)和fips_module(7)。
遗留提供商
旧版提供程序是一个可动态加载的模块,因此必须以代码或通过 OpenSSL 配置显式加载(请参阅config(5))。它包含被认为不安全或不再常用的算法实现,例如 MD2 或 RC4。如果需要(如果加载了其他提供程序并提供相同算法的实现),属性“provider=legacy”可以用作这些实现的搜索标准。
空提供商
null 提供程序作为libcrypto库 的一部分内置。它根本不包含任何算法。获取算法时,如果没有显式加载其他提供程序,将自动加载默认提供程序。为了防止这种情况发生,您可以显式加载空提供程序。
USING ALGORITHMS IN APPLICATIONS
通过使用“EVP”API,应用程序可以使用加密算法。加密、摘要、消息认证码等各种操作中的每一个都有一组可以调用的 EVP 函数调用来使用它们。有关更多详细信息,请参阅evp(7)页面。
其中大多数都遵循共同的模式。首先创建一个“上下文”对象。例如,对于摘要操作,您将使用EVP_MD_CTX,对于加密/解密操作,您将使用EVP_CIPHER_CTX。然后,该操作通过“init”函数进行初始化,可供使用 - 可以选择传入一组参数(使用 OSSL_PARAM (3)类型)来配置操作的行为方式。接下来的数据通过一系列“更新”调用输入到操作中。该操作使用“最终”调用来完成,该调用通常会提供某种输出。最后,上下文被清理并释放。
下面显示了使用 SHA256 执行此过程以摘要数据的完整示例。其他操作如加解密、签名、消息认证码等的过程类似。
#include <stdio.h>
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/err.h>
int main(void)
{
EVP_MD_CTX *ctx = NULL;
EVP_MD *sha256 = NULL;
const unsigned char msg[] = {
0x00, 0x01, 0x02, 0x03
};
unsigned int len = 0;
unsigned char *outdigest = NULL;
int ret = 1;
/* Create a context for the digest operation */
ctx = EVP_MD_CTX_new();
if (ctx == NULL)
goto err;
/*
* Fetch the SHA256 algorithm implementation for doing the digest. We're
* using the "default" library context here (first NULL parameter), and
* we're not supplying any particular search criteria for our SHA256
* implementation (second NULL parameter). Any SHA256 implementation will
* do.
* In a larger application this fetch would just be done once, and could
* be used for multiple calls to other operations such as EVP_DigestInit_ex().
*/
sha256 = EVP_MD_fetch(NULL, "SHA256", NULL);
if (sha256 == NULL)
goto err;
/* Initialise the digest operation */
if (!EVP_DigestInit_ex(ctx, sha256, NULL))
goto err;
/*
* Pass the message to be digested. This can be passed in over multiple
* EVP_DigestUpdate calls if necessary
*/
if (!EVP_DigestUpdate(ctx, msg, sizeof(msg)))
goto err;
/* Allocate the output buffer */
outdigest = OPENSSL_malloc(EVP_MD_get_size(sha256));
if (outdigest == NULL)
goto err;
/* Now calculate the digest itself */
if (!EVP_DigestFinal_ex(ctx, outdigest, &len))
goto err;
/* Print out the digest result */
BIO_dump_fp(stdout, outdigest, len);
ret = 0;
err:
/* Clean up all the resources we allocated */
OPENSSL_free(outdigest);
EVP_MD_free(sha256);
EVP_MD_CTX_free(ctx);
if (ret != 0)
ERR_print_errors_fp(stderr);
return ret;
}
CONFIGURATION
默认情况下,OpenSSL 在首次使用时会加载配置文件。这将在默认库上下文中设置各种配置设置。创建自己的库上下文的应用程序可以选择使用OSSL_LIB_CTX_load_config(3)函数使用配置文件来配置它们。
配置文件可用于自动加载提供程序并设置默认属性查询字符串。
有关 OpenSSL 配置文件格式的信息,请参阅config(5)。
ENCODING AND DECODING KEYS
许多算法需要使用密钥。可以使用 EVP API 动态生成密钥(例如,请参阅EVP_PKEY_Q_keygen(3))。然而,通常需要在某些外部格式(例如 PEM 或 DER)之间保存或加载密钥(或其相关参数)(请参阅openssl-glossary(7))。OpenSSL 使用编码器和解码器来执行此任务。
编码器和解码器只是算法实现,就像 OpenSSL 中任何其他算法实现一样。它们由提供商实施。OpenSSL 编码器和解码器在默认提供程序中可用。它们也在基础提供者中重复。
有关编码器的信息,请参阅OSSL_ENCODER_CTX_new_for_pkey(3)。有关解码器的信息,请参阅OSSL_DECODER_CTX_new_for_pkey(3)。
LIBRARY CONVENTIONS
许多“获取”或“设置”值的 OpenSSL 函数都遵循使用数字0和1 的命名约定,即“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值。
SEE ALSO
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)
COPYRIGHT
版权所有 2000-2023 OpenSSL 项目作者。版权所有。
根据 Apache License 2.0(“许可证”)获得许可。除非遵守许可证,否则您不得使用此文件。您可以在源代码分发版或/source/license.html的 LICENSE 文件中获取副本。