但我的问题是,这个回调函数是在执行“SSL_accept”函数之后执行的,但是我必须在使用“SSL_new”命令之前选择并使用适当的证书,这是在执行 SSL_accept 之前的方式。
当你启动你的服务器时,你提供一个默认的SSL_CTX
. 这用于非 SNI 客户端,如 SSLv3 客户端和不使用 SNI 的 TLS 客户端(如 Windows XP)。这是必需的,因为在这种情况下不会调用回调。
下面是一些使用 OpenSSL 的s_client
. 要模拟非 SNI 客户端以便get_ssl_servername_cb
不调用您的客户端,请发出:
openssl s_client -connect localhost:8443 -ssl3
# 在 TLSv1 中添加的 SNI
openssl s_client -connect localhost:8443 -tls1
# Windows XP 客户端
要模拟 SNI 客户端以便调用您的客户端,请get_ssl_servername_cb
发出:
openssl s_client -connect localhost:8443 -tls1 -servername localhost
您还可以通过添加来避免证书验证错误-CAfile
。这是来自我的一个测试脚本(用于测试 DSS/DSA 证书localhost
):
printf "GET / HTTP/1.1\r\n\r\n" | /usr/local/ssl/bin/openssl s_client \
-connect localhost:8443 -tls1 -servername localhost \
-CAfile pki/signing-dss-cert.pem
所以我的问题是,如何为 SNI 使用“SSL_CTX_set_tlsext_servername_callback”函数?
请参阅 OpenSSL 源代码<openssl dir>/apps/s_server.c
;或查看如何在 C 或 C++ 中在 OpenSSL 上实现服务器名称指示(SNI)?.
在您的get_ssl_servername_cb
(set with SSL_CTX_set_tlsext_servername_callback
) 中,您检查服务器名称。出现以下两种情况之一:您已经有一个SSL_CTX
for 服务器名称,或者您需要创建一个SSL_CTX
for 服务器名称。
一旦你SSL_CTX
从缓存中获取或创建一个新的SSL_CTX
,你就可以SSL_set_SSL_CTX
在上下文中使用交换。OpenSSL 源文件中有一个在新上下文中交换的示例。请参阅s_server.c
(in <openssl dir>/apps/s_server.c
) 的代码。追随ctx2
,
这是我的一个项目中的样子。IsDomainInDefaultCert
确定请求的服务器名称是否由默认服务器证书提供。如果没有,GetServerContext
则获取所需的SSL_CTX
. GetServerContext
从应用级缓存中提取所需的证书;或创建它并将其放在应用程序级缓存中(GetServerContext
还断言一个引用计数,SSL_CTX
因此 OpenSSL 库不会从应用程序下删除它)。
static int ServerNameCallback(SSL *ssl, int *ad, void *arg)
{
UNUSED(ad);
UNUSED(arg);
ASSERT(ssl);
if (ssl == NULL)
return SSL_TLSEXT_ERR_NOACK;
const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
ASSERT(servername && servername[0]);
if (!servername || servername[0] == '\0')
return SSL_TLSEXT_ERR_NOACK;
/* Does the default cert already handle this domain? */
if (IsDomainInDefCert(servername))
return SSL_TLSEXT_ERR_OK;
/* Need a new certificate for this domain */
SSL_CTX* ctx = GetServerContext(servername);
ASSERT(ctx != NULL);
if (ctx == NULL)
return SSL_TLSEXT_ERR_NOACK;
/* Useless return value */
SSL_CTX* v = SSL_set_SSL_CTX(ssl, ctx);
ASSERT(v == ctx);
if (v != ctx)
return SSL_TLSEXT_ERR_NOACK;
return SSL_TLSEXT_ERR_OK;
}
在上面的代码中,ad
和arg
是未使用的参数。我不知道有什么ad
用,因为我不使用它。arg
可用于将上下文传递给回调。我也不使用它们arg
,但s_server.c
使用它来打印一些调试信息(这arg
是一个指向BIO
绑定到stderr
(和其他一些),IIRC 的 s 的指针)。
为了完整起见,SSL_CTX
引用计数并且可以重复使用。新创建SSL_CTX
的计数为 1,它被委托给 OpenSSL 内部缓存机制。当您将SSL_CTX
传递给SSL
对象时,计数将增加到 2。当SSL
对象调用SSL_CTX_free
时SSL_CTX
,该函数将减少引用计数。如果上下文过期并且引用计数为 1,则 OpenSSL 库将从其内部缓存中删除它。