#if (NGX_OPENSSL) ngx_ssl_init(log); #endif
objs/ngx_auto_config.h 中
#ifndef NGX_OPENSSL #define NGX_OPENSSL 1 #endif
所以这个条件编译成立
NGX_OPENSSL
是一个宏定义,用于控制与 OpenSSL 相关的功能是否被启用若用户通过
./configure
参数(如--with-http_ssl_module
)显式启用SSL模块,则NGX_OPENSSL
宏会被定义OpenSSL 是一个开源的加密库,广泛用于实现安全通信协议(如 HTTPS、TLS/SSL)以及提供各种加密和解密功能
ngx_ssl_init
声明
在 src/event/ngx_event_openssl.h 中:
ngx_int_t ngx_ssl_init(ngx_log_t *log);
实现
在 src\event\ngx_event_openssl.c 中:
ngx_int_t ngx_ssl_init(ngx_log_t *log) { #if OPENSSL_VERSION_NUMBER >= 0x10100003L if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_init_ssl() failed"); return NGX_ERROR; } /* * OPENSSL_init_ssl() may leave errors in the error queue * while returning success */ ERR_clear_error(); #else OPENSSL_config(NULL); SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #endif #ifndef SSL_OP_NO_COMPRESSION { /* * Disable gzip compression in OpenSSL prior to 1.0.0 version, * this saves about 522K per connection. */ int n; STACK_OF(SSL_COMP) *ssl_comp_methods; ssl_comp_methods = SSL_COMP_get_compression_methods(); n = sk_SSL_COMP_num(ssl_comp_methods); while (n--) { (void) sk_SSL_COMP_pop(ssl_comp_methods); } } #endif ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_connection_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_server_conf_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_session_cache_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_ticket_keys_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_ticket_keys_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_ocsp_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_ocsp_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_certificate_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_certificate_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "SSL_CTX_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_next_certificate_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_next_certificate_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_certificate_name_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_certificate_name_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); return NGX_ERROR; } ngx_ssl_stapling_index = X509_get_ex_new_index(0, NULL, NULL, NULL, NULL); if (ngx_ssl_stapling_index == -1) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "X509_get_ex_new_index() failed"); return NGX_ERROR; } return NGX_OK; }
这段代码是 Nginx 源码中用于初始化 OpenSSL 库的核心函数
ngx_ssl_init
,其主要目的是为 Nginx 的 SSL/TLS 功能提供必要的初始化操作函数的主要功能
ngx_ssl_init
函数的主要任务是:
- 初始化 OpenSSL 库,确保其能够正常工作。
- 配置 SSL/TLS 相关的全局状态。
- 注册自定义扩展索引(ex_data),以便在后续的 SSL/TLS 上下文中存储和访问 Nginx 特定的数据。
主要逻辑分析
(1) OpenSSL 库的初始化
#if OPENSSL_VERSION_NUMBER >= 0x10100003L if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_init_ssl() failed"); return NGX_ERROR; } ERR_clear_error(); #else OPENSSL_config(NULL); SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #endif
条件编译 :
- 如果 OpenSSL 版本号大于等于
0x10100003L
(即 OpenSSL 1.1.0 及以上版本),使用OPENSSL_init_ssl
函数进行初始化。- 对于较老的 OpenSSL 版本(低于 1.1.0),则通过一系列传统函数(如
OPENSSL_config
、SSL_library_init
等)进行初始化。
新版本的初始化 :
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_init_ssl() failed"); return NGX_ERROR; } ERR_clear_error();
- 调用
OPENSSL_init_ssl
初始化 OpenSSL,并加载配置文件。- 即使
OPENSSL_init_ssl
返回成功,也可能在错误队列中留下未处理的错误信息,因此调用ERR_clear_error
清除这些潜在的错误。
旧版本的初始化 :
OPENSSL_config(NULL); SSL_library_init(); SSL_load_error_strings(); OpenSSL_add_all_algorithms();
OPENSSL_config
加载 OpenSSL 配置文件。SSL_library_init
初始化 SSL 库。SSL_load_error_strings
加载错误字符串,方便调试。OpenSSL_add_all_algorithms
注册所有加密算法。
(2) 禁用压缩(针对旧版本 OpenSSL)
#ifndef SSL_OP_NO_COMPRESSION { int n; STACK_OF(SSL_COMP) *ssl_comp_methods; ssl_comp_methods = SSL_COMP_get_compression_methods(); n = sk_SSL_COMP_num(ssl_comp_methods); while (n--) { (void) sk_SSL_COMP_pop(ssl_comp_methods); } } #endif
- 在 OpenSSL 1.0.0 之前的版本中,没有
SSL_OP_NO_COMPRESSION
宏来禁用压缩。- 压缩可能导致内存消耗增加(每连接约 522KB),因此手动禁用压缩:
- 获取当前支持的压缩方法列表。
- 遍历并移除所有压缩方法。
(3) 创建扩展索引
Nginx 使用 OpenSSL 的扩展机制来存储自定义数据。以下是创建的几个扩展索引及其用途:
1.连接级别的扩展索引 :
ngx_ssl_connection_index = SSL_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- 用于在 SSL 连接对象中存储 Nginx 的自定义数据。
2.上下文级别的扩展索引 :
ngx_ssl_server_conf_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
用于在 SSL 上下文对象中存储服务器配置信息
3.会话缓存扩展索引 :
ngx_ssl_session_cache_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL);
- 用于管理 SSL 会话缓存。
4.其他扩展索引 :
ngx_ssl_ticket_keys_index
:用于存储会话票据密钥。
ngx_ssl_ocsp_index
:用于存储 OCSP(在线证书状态协议)相关数据。
ngx_ssl_certificate_index
和ngx_ssl_next_certificate_index
:用于管理证书链。
ngx_ssl_stapling_index
:用于 OCSP Stapling(证书状态绑定)。每个扩展索引的创建都检查返回值是否为
-1
,如果失败则记录错误日志并返回NGX_ERROR
意图
1 兼容性
- 代码通过条件编译兼容了不同版本的 OpenSSL(1.1.0 及以上版本与旧版本)。
- 旧版本中手动禁用压缩的逻辑体现了对性能优化的关注。
2 扩展性
- 使用 OpenSSL 的扩展机制(
SSL_get_ex_new_index
和SSL_CTX_get_ex_new_index
)为 Nginx 提供了灵活的自定义数据存储能力。- 这些扩展索引使得 Nginx 能够在 SSL/TLS 层面上集成更多的功能(如会话缓存、OCSP Stapling 等)。
3 错误处理
- 每个关键步骤都有详细的错误检查和日志记录。
- 例如,
OPENSSL_init_ssl
和SSL_get_ex_new_index
等函数的返回值都被严格验证,确保初始化失败时能够及时发现问题。4 性能优化
- 禁用压缩是为了减少内存消耗,提升性能。
- 通过扩展索引管理自定义数据,避免了全局变量的使用,提高了模块化程度和可维护性。
详解
gcc -E src/event/ngx_event_openssl.c \ -I src/core \ -I src/event \ -I src/event/modules \ -I src/os/unix \ -I objs \ > ngx_event_openssl_preprocessed.c
使用以上命令处理条件编译
在输出文件 ngx_event_openssl_preprocessed.c 中 查找 ngx_ssl_init
ngx_int_t ngx_ssl_init(ngx_log_t *log) { if (OPENSSL_init_ssl( # 148 "src/event/ngx_event_openssl.c" 3 4 0x00000040L # 148 "src/event/ngx_event_openssl.c" , # 148 "src/event/ngx_event_openssl.c" 3 4 ((void *)0) # 148 "src/event/ngx_event_openssl.c" ) == 0) { ngx_ssl_error(2, log, 0, "OPENSSL_init_ssl() failed"); return -1; } ERR_clear_error(); # 189 "src/event/ngx_event_openssl.c" ngx_ssl_connection_index = # 189 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(0, # 189 "src/event/ngx_event_openssl.c" 0 # 189 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 189 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_connection_index == -1) { ngx_ssl_error(2, log, 0, "SSL_get_ex_new_index() failed"); return -1; } ngx_ssl_server_conf_index = # 196 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(1, # 196 "src/event/ngx_event_openssl.c" 0 # 196 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 197 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_server_conf_index == -1) { ngx_ssl_error(2, log, 0, "SSL_CTX_get_ex_new_index() failed"); return -1; } ngx_ssl_session_cache_index = # 204 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(1, # 204 "src/event/ngx_event_openssl.c" 0 # 204 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 205 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_session_cache_index == -1) { ngx_ssl_error(2, log, 0, "SSL_CTX_get_ex_new_index() failed"); return -1; } ngx_ssl_ticket_keys_index = # 212 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(1, # 212 "src/event/ngx_event_openssl.c" 0 # 212 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 213 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_ticket_keys_index == -1) { ngx_ssl_error(2, log, 0, "SSL_CTX_get_ex_new_index() failed"); return -1; } ngx_ssl_ocsp_index = # 220 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(1, # 220 "src/event/ngx_event_openssl.c" 0 # 220 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 220 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_ocsp_index == -1) { ngx_ssl_error(2, log, 0, "SSL_CTX_get_ex_new_index() failed"); return -1; } ngx_ssl_certificate_index = # 227 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(1, # 227 "src/event/ngx_event_openssl.c" 0 # 227 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 228 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_certificate_index == -1) { ngx_ssl_error(2, log, 0, "SSL_CTX_get_ex_new_index() failed"); return -1; } ngx_ssl_next_certificate_index = # 235 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(3, # 235 "src/event/ngx_event_openssl.c" 0 # 235 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 236 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_next_certificate_index == -1) { ngx_ssl_error(2, log, 0, "X509_get_ex_new_index() failed"); return -1; } ngx_ssl_certificate_name_index = # 242 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(3, # 242 "src/event/ngx_event_openssl.c" 0 # 242 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 243 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_certificate_name_index == -1) { ngx_ssl_error(2, log, 0, "X509_get_ex_new_index() failed"); return -1; } ngx_ssl_stapling_index = # 250 "src/event/ngx_event_openssl.c" 3 4 CRYPTO_get_ex_new_index(3, # 250 "src/event/ngx_event_openssl.c" 0 # 250 "src/event/ngx_event_openssl.c" 3 4 , ((void *)0), ((void *)0), ((void *)0), ((void *)0)) # 250 "src/event/ngx_event_openssl.c" ; if (ngx_ssl_stapling_index == -1) { ngx_ssl_error(2, log, 0, "X509_get_ex_new_index() failed"); return -1; } return 0; }
可以看出
新版本的初始化
if (OPENSSL_init_ssl(OPENSSL_INIT_LOAD_CONFIG, NULL) == 0) { ngx_ssl_error(NGX_LOG_ALERT, log, 0, "OPENSSL_init_ssl() failed"); return NGX_ERROR; } ERR_clear_error();
这个分支成立
在 Ubuntu 环境下 使用:
openssl version
也可以查看 openssl 的版本
可以知道 我这里属于 OpenSSL 1.1.0 及以上版本
OPENSSL_init_ssl
在 OpenSSL 1.1.0 及以上版本中,
OPENSSL_init_ssl
函数的声明位于以下头文件中:#include <openssl/ssl.h>
OPENSSL_init_ssl
是 OpenSSL 1.1.0 引入的一个函数,用于初始化 SSL/TLS 库。它的主要作用是替代旧版本中多个独立的初始化函数(如SSL_library_init
、SSL_load_error_strings
等),提供一个统一的接口来完成 SSL/TLS 的初始化工作。函数原型如下:
int OPENSSL_init_ssl(uint64_t opts, const OPENSSL_INIT_SETTINGS *settings);
opts
:
- 这是一个位掩码参数,用于指定初始化选项。
- 常见的选项包括:
OPENSSL_INIT_LOAD_CONFIG
:加载 OpenSSL 配置文件(通常是openssl.cnf
)。OPENSSL_INIT_LOAD_SSL_STRINGS
:加载 SSL/TLS 相关的错误字符串。OPENSSL_INIT_ADD_ALL_CIPHERS
和OPENSSL_INIT_ADD_ALL_DIGESTS
:注册所有加密算法和摘要算法。OPENSSL_INIT_NO_LOAD_CONFIG
:禁用配置文件加载。- 更多选项可以参考 OpenSSL 文档。
settings
:
- 这是一个指向
OPENSSL_INIT_SETTINGS
结构的指针,用于传递更高级的初始化设置。- 在大多数情况下,可以传入
NULL
,表示使用默认设置。2.3 返回值
- 成功时返回
1
。- 失败时返回
0
。需要注意的是,即使
OPENSSL_init_ssl
返回成功,也可能在错误队列中留下未处理的错误信息。因此,通常会在调用后清除错误队列:
ERR_clear_error();
ngx_ssl_error
Ubuntu 下 nginx-1.24.0 源码分析 -ngx_ssl_error 函数-CSDN博客
NGX_ERROR
定义在 src/core/ngx_core.h 中
#define NGX_OK 0 #define NGX_ERROR -1
初始化失败,函数返回 -1
ERR_clear_error
OpenSSL 使用一个线程安全的错误队列来记录操作过程中发生的错误信息。这些错误信息可以通过以下函数访问:
ERR_get_error()
:从错误队列中获取一个错误代码。ERR_peek_error()
:查看错误队列中的第一个错误代码,但不移除它。ERR_clear_error()
:清空当前线程的错误队列。
ERR_clear_error()
的主要作用是清空当前线程的错误队列。具体来说:
- 它会移除所有当前线程中累积的错误信息。
- 调用后,错误队列将变为空状态。
在多线程环境中,每个线程都有独立的错误队列,因此调用
ERR_clear_error()
不会影响其他线程的错误状态
通过调用
ERR_clear_error()
,可以确保错误队列在初始化完成后处于干净的状态。这样,后续的 OpenSSL 操作如果产生错误,错误队列中的信息将是与当前操作直接相关的,而不是之前残留的无关错误
ngx_ssl_connection_index
ngx_ssl_connection_index
是一个全局变量,它的作用是为每个 SSL 连接分配一个唯一的索引值。
这个索引值用于在 OpenSSL 的
SSL
结构体中存储和检索与该连接相关的自定义数据。
ngx_ssl_connection_index
是通过调用SSL_get_ex_new_index()
创建的一个全局索引值
为每个 SSL 连接关联 Nginx 的特定数据 :
- 在 Nginx 中,每个 SSL 连接都需要存储一些与 Nginx 相关的上下文信息(例如连接对象、会话状态等)。
- 通过
ngx_ssl_connection_index
,可以将这些数据存储到 OpenSSL 的SSL
结构体中,并在需要时快速检索。避免全局变量或复杂的数据结构 :
- 如果没有扩展数据机制,Nginx 可能需要维护一个额外的映射表(例如哈希表或链表)来管理 SSL 连接和 Nginx 数据之间的关系。
- 使用
ngx_ssl_connection_index
,可以直接将数据附加到SSL
结构体中,简化了数据管理。
SSL 连接
SSL 连接-CSDN博客
SSL
结构体中的扩展数据数组(ex_data
)不仅仅用于存储用户自定义数据,它还可以被 OpenSSL 内部或其他模块使用。
ngx_ssl_connection_index
是通过SSL_get_ex_new_index()
分配的一个索引值,指向数组中某个特定的位置,而该位置之前的内容可能已经被其他模块或 OpenSSL 内部使用该位置是 用户自定义数据
SSL
结构体中的扩展数据数组是一个固定大小的数组void *ex_data[SSL_MAX_EX_DATA];
SSL_MAX_EX_DATA
:表示数组的最大长度,通常是 32 或更大的一个固定值。- 每个数组元素是一个指针,可以存储任意类型的数据
- 数组的每个槽位由一个整数索引标识。
- 索引值从 0 开始,依次递增。
- 不同的模块或功能可以通过调用
SSL_get_ex_new_index()
分配自己的索引值,并将数据存储到对应的槽位中
SSL_CTX_get_ex_new_index
SSL_CTX_get_ex_new_index()
用于为SSL_CTX
(SSL 上下文)结构体 分配一个扩展数据索引。它的主要作用是:
- 在
SSL_CTX
中存储自定义数据 :例如全局配置、证书链、私钥等。- 支持模块化设计 :允许不同模块将自定义数据附加到
SSL_CTX
中,而无需修改 OpenSSL 的内部实现返回值
- 成功时返回一个非负整数,表示新分配的索引值。
- 失败时返回
-1
。
SSL_get_ex_new_index
的作用
SSL_get_ex_new_index()
用于为SSL
(SSL 连接)结构体 分配一个扩展数据索引。它的主要作用是:
- 在
SSL
中存储自定义数据 :例如每个连接的上下文信息。- 支持连接级别的扩展数据管理 :允许开发者为每个 SSL 连接附加独立的数据
返回值
- 成功时返回一个非负整数,表示新分配的索引值。
- 失败时返回
-1
。
两者的区别
特性
SSL_CTX_get_ex_new_index
SSL_get_ex_new_index
目标结构体
SSL_CTX
(SSL 上下文)
SSL
(SSL 连接)数据范围
全局数据,适用于所有 SSL 连接
每个连接独立的数据
典型用途
存储服务器配置、证书链、会话缓存等全局信息
存储每个连接的上下文信息(如 Nginx 的连接对象)
生命周期
与
SSL_CTX
生命周期一致与单个
SSL
实例的生命周期一致分配的索引值是否共享
不同模块可以分配独立的索引值
不同模块可以分配独立的索引值
SSL_CTX_get_ex_new_index
:用于为SSL_CTX
分配扩展数据索引,适用于存储全局数据(如服务器配置)。SSL_get_ex_new_index
:用于为SSL
分配扩展数据索引,适用于存储每个连接的独立数据(如连接上下文)。- 核心区别 :两者的目标结构体不同,分别对应全局的
SSL_CTX
和每个连接的SSL
。
X509_get_ex_new_index
X509_get_ex_new_index()
是 OpenSSL 提供的一个函数,用于为X509
(证书)结构体 分配扩展数据索引。它的主要作用是:
- 在
X509
结构体中存储自定义数据 :例如与证书相关的上下文信息或元数据。- 支持模块化设计 :允许不同模块将自定义数据附加到
X509
证书对象中,而无需修改 OpenSSL 的内部实现
返回值
- 成功时返回一个非负整数,表示新分配的索引值。
- 失败时返回
-1
。
X509
是 OpenSSL 库中用于表示 数字证书 的核心数据结构。它在 SSL/TLS 协议中扮演着至关重要的角色,主要用于身份验证、加密和信任链的建立
数字证书的核心概念
数字证书是一种电子文档,用于证明某个实体(如服务器或客户端)的身份,并包含该实体的公钥。数字证书通常由受信任的第三方机构(称为 证书颁发机构,CA )签发。
数字证书的主要内容
- 主体信息(Subject) :证书持有者的身份信息(如域名、组织名称等)。
- 公钥(Public Key) :证书持有者的公钥。
- 签发者信息(Issuer) :签发证书的 CA 的身份信息。
- 有效期(Validity Period) :证书的有效时间范围。
- 签名(Signature) :CA 使用其私钥对证书内容进行签名,以确保证书的完整性和真实性
X509
结构体的作用在 OpenSSL 中,
X509
是一个复杂的数据结构,用于表示和操作数字证书。它的主要作用包括以下几个方面:1. 身份验证
- 在 SSL/TLS 握手过程中,服务器会向客户端发送其数字证书(
X509
对象),以证明自己的身份。- 客户端通过验证证书的签名和有效期,确认服务器是否可信。
2. 公钥分发
- 数字证书中包含了服务器的公钥,客户端可以使用该公钥与服务器进行加密通信。
- 例如,在 RSA 密钥交换中,客户端使用服务器的公钥加密预主密钥(Pre-Master Secret)。
3. 建立信任链
- 数字证书通常形成一个信任链(Certificate Chain),从服务器证书到根证书。
X509
提供了操作证书链的功能,例如验证证书链的完整性。4. OCSP 和 CRL 验证
X509
支持在线证书状态协议(OCSP)和证书吊销列表(CRL),用于检查证书是否已被吊销。
数字证书在逻辑上和现实中的证件(如身份证、护照或驾照)非常相似。它们的作用都是用来证明某个实体的身份,并且依赖于一个可信的第三方机构来验证和签发
数字证书与现实证件的核心功能对比
功能
现实中的证件(如身份证)
数字证书
身份证明
证明持证人的身份(如姓名、照片、出生日期等)。
证明某个实体(如服务器或客户端)的身份。
签发机构
由政府或权威机构签发(如公安局)。
由受信任的 CA(证书颁发机构)签发。
有效期
证件有明确的有效期(如 10 年)。
证书有明确的有效期(如 1 年)。
防伪机制
使用防伪技术(如水印、芯片)防止伪造。
使用加密算法(如签名)防止篡改和伪造。
信任链
公众信任签发机构(如政府),从而信任证件。
用户信任 CA,从而信任其签发的证书。
类比:现实中的身份证 vs 数字证书
1. 身份证明
- 现实中的身份证 :
- 身份证上的信息(如姓名、照片、身份证号)用于证明持证人的身份。
- 当你出示身份证时,其他人可以通过检查这些信息确认你是谁。
- 数字证书 :
- 数字证书中的信息(如主体名称、公钥)用于证明某个实体的身份。
- 当服务器向客户端发送证书时,客户端可以通过检查证书内容确认服务器的身份。
2. 签发机构
- 现实中的身份证 :
- 身份证由政府机构(如公安局)签发,公众信任政府,因此也信任身份证的真实性。
- 数字证书 :
- 数字证书由受信任的 CA(如 Let's Encrypt、DigiCert)签发,用户信任 CA,因此也信任其签发的证书。
3. 防伪机制
- 现实中的身份证 :
- 身份证可能包含防伪技术,例如水印、全息图或芯片,以防止伪造。
- 数字证书 :
- 数字证书使用加密算法(如 RSA 或 ECC)生成签名,CA 使用其私钥对证书内容进行签名,确保证书未被篡改。
4. 有效期
- 现实中的身份证 :
- 身份证通常有一个有效期(如 10 年),过期后需要重新申请。
- 数字证书 :
- 数字证书也有一个有效期(如 1 年或 2 年),过期后需要续签或重新申请。
5. 信任链
- 现实中的身份证 :
- 公众信任签发机构(如政府),因此信任其签发的身份证。
- 如果有人伪造身份证,公众可以通过查询政府数据库验证其真实性。
- 数字证书 :
- 用户信任 CA,因此信任其签发的证书。
- 如果证书被篡改或吊销,用户可以通过 CRL(证书吊销列表)或 OCSP(在线证书状态协议)验证其状态。