我在FreeBSD-8.2中使用OpenSSL 0.9.8q。我的系统上有3个虚拟主机,并希望在一台服务器上实现SNI以服务所有3个虚拟主机。
我每个都有3个单独的证书,在我的ssl-server代码中,我必须以某种方式找出客户端请求的域名,并使用相应的证书文件。为此,我编写了一个名为get_ssl_servername_cb
的函数,并将其作为回调函数传递给SSL_CTX_set_tlsext_servername_callback
。这样,在回调函数中,我可以得到客户端请求的域名。
但我的问题是,这个回调函数在执行SSL_accept
函数后正在执行,但我必须在使用SSL_new
命令之前选择并使用相应的证书,这是执行{之前的方式{1}}。
所以我的问题是,我怎样才能将SSL_accept
函数用于SNI?
答案 0 :(得分:7)
但我的问题是,这个回调函数是在执行" SSL_accept"之后执行的。功能,但我必须在使用" SSL_new"之前选择并使用相应的证书。命令,这是执行SSL_accept之前的方式。
启动服务器时,提供默认SSL_CTX
。这适用于非SNI客户端,如SSLv3客户端和不使用SNI的TLS客户端(如Windows XP)。这是必需的,因为在这种情况下不会调用回调。
以下是一些使用OpenSSL s_client
来解释行为的示例。要模拟非SNI客户端以便{/ 1}} 未被调用,请发出:
get_ssl_servername_cb
#SNI已添加到TLSv1 openssl s_client -connect localhost:8443 -ssl3
#Windows XP客户端要模拟SNI客户端,以便调用openssl s_client -connect localhost:8443 -tls1
,请发出:
get_ssl_servername_cb
您还可以通过添加openssl s_client -connect localhost:8443 -tls1 -servername localhost
来避免证书验证错误。这来自我的一个测试脚本(用于测试-CAfile
上的DSS / DSA证书):
localhost
所以我的问题是,如何使用" SSL_CTX_set_tlsext_servername_callback" SNI的功能?
请参阅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
处的OpenSSL源代码;或者参见How to implement Server Name Indication(SNI) on OpenSSL in C or C++?。
在<openssl dir>/apps/s_server.c
(使用get_ssl_servername_cb
设置)中,检查服务器名称。出现以下两种情况之一:您已经拥有服务器名称的SSL_CTX_set_tlsext_servername_callback
,或者您需要为服务器名称创建SSL_CTX
。
从缓存中获取SSL_CTX
或创建新的SSL_CTX
后,您可以使用SSL_CTX
交换上下文。这是在OpenSSL源文件中交换新上下文的示例。请参阅SSL_set_SSL_CTX
的代码(在s_server.c
中)。按照<openssl dir>/apps/s_server.c
,
这是我的一个项目中的样子。 ctx2
确定默认服务器证书是否提供了所请求的服务器名称。如果没有,IsDomainInDefaultCert
会提取所需的GetServerContext
。 SSL_CTX
从应用级缓存中提取所需的证书;或创建它并将其放入应用程序级缓存中(GetServerContext
还在GetServerContext
上声明一个引用计数,以便OpenSSL库不会从应用程序下删除它。
SSL_CTX
在上面的代码中,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
(以及其他一些),IIRC)。
为了完整性,stderr
被引用计数,可以重复使用。新创建的SSL_CTX
的计数为1,它被委托给OpenSSL内部缓存机制。当您将SSL_CTX
传递给SSL_CTX
对象时,计数增加到2.当SSL
对象在SSL
上调用SSL_CTX_free
时,该函数将减少引用计数。如果上下文已过期且引用计数为1,则OpenSSL库将从其内部缓存中删除它。