使用SNI在一个盒子中提供多个域

时间:2014-03-13 08:48:18

标签: c openssl sni

我在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?

1 个答案:

答案 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会提取所需的GetServerContextSSL_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库将从其内部缓存中删除它。

相关问题