我在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
命令之前选择并使用相应的证书,这是执行之前的方式SSL_accept
.
所以我的问题是,我如何使用SSL_CTX_set_tlsext_servername_callback
SNI函数?
但我的问题是,这个回调函数在执行"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
#SNI在TLSv1添加
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
(设置为SSL_CTX_set_tlsext_servername_callback
)中,检查服务器名称.出现以下两种情况之一:您已经拥有SSL_CTX
服务器名称,或者您需要创建SSL_CTX
服务器名称.
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
s stderr
(和其他一些)的指针,IIRC).
为了完整性,SSL_CTX
参考计数,它们可以重复使用.新创建SSL_CTX
的计数为1,委托给OpenSSL内部缓存机制.当您SSL_CTX
将SSL
对象移交给对象时,计数增加到2.当SSL
对象调用SSL_CTX_free
时SSL_CTX
,该函数将减少引用计数.如果上下文已过期且引用计数为1,则OpenSSL库将从其内部缓存中删除它.