我在FreeBSD8.2中使用OpenSSL 0.9.8q。我的系统上有3台虚拟主机,并希望实现SNI,以便在一台服务器中为所有3台主机提供服务。
我有三个独立的证书,每个证书一个,在我的ssl服务器代码中,我必须以某种方式找出客户机请求的域名,并在此基础上使用适当的证书文件。为此,我编写了一个名为get_ssl_servername_cb的函数,并将其作为回调函数传递给SSL_CTX_set_tlsext_servername_callback。这样,在回调函数中,我可以获得客户请求的域名。
但是我的问题是,这个回调函数是在执行SSL_accept函数之后执行的,但是在使用SSL_new命令之前,必须选择和使用适当的证书,这是在执行SSL_accept之前。
所以我的问题是,如何使用SSL_CTX_set_tlsext_servername_callback函数进行SNI?
发布于 2014-03-13 10:29:42
但是我的问题是,这个回调函数是在执行" SSL_accept“函数之后执行的,但是在使用"SSL_new”命令之前,必须选择和使用适当的证书,这是在执行SSL_accept之前。
启动服务器时,提供默认的SSL_CTX。这用于非SNI客户端,如不使用SNI(如Windows )的SSLv3客户端和TLS客户端。这是需要的,因为在这种情况下不调用回调。
下面是一些使用OpenSSL的s_client来挠痒行为的示例。若要模拟非SNI客户端以避免调用get_ssl_servername_cb,请发出以下命令:
openssl s_client -connect localhost:8443 -ssl3 # SNI在TLSv1上添加openssl s_client -connect localhost:8443 -tls1 # Windows客户端若要模拟SNI客户端以便调用get_ssl_servername_cb,请发出以下命令:
openssl s_client -connect localhost:8443 -tls1 -servername localhost还可以通过添加-CAfile来避免证书验证错误。这是我的一个测试脚本(用于在localhost上测试DSS/DSA证书):
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 因此,我的问题是,我如何使用"SSL_CTX_set_tlsext_servername_callback“函数的SNI?
请参阅<openssl dir>/apps/s_server.c上的<openssl dir>/apps/s_server.c源代码;或参见How to implement Server Name Indication(SNI) on OpenSSL in C or C++?。
在get_ssl_servername_cb (用SSL_CTX_set_tlsext_servername_callback设置)中,检查服务器名。出现两种情况之一:您已经为服务器名称创建了一个SSL_CTX,或者您需要为服务器的名称创建一个SSL_CTX。
从缓存中获取SSL_CTX或创建新的SSL_CTX之后,就可以在上下文中使用SSL_set_SSL_CTX进行交换了。在OpenSSL源文件中的新上下文中有一个交换的例子。请参阅s_server.c的代码(在<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是指向绑定到stderr (以及其他几个)的BIO的指针)。
为了完整性,SSL_CTX是参考计数,它们可以重复使用.新创建的SSL_CTX的计数为1,它被委托给OpenSSL内部缓存机制。当您将SSL_CTX传递给SSL对象时,计数增加到2。当SSL对象在SSL_CTX上调用SSL_CTX_free时,该函数将减少引用计数。如果上下文过期且引用计数为1,则OpenSSL库将从其内部缓存中删除它。
https://stackoverflow.com/questions/22373332
复制相似问题