openssl升级后ssh登录失败( 二 )


使用openssl库编写客户端程序的一般流程如下:先创建套接字,并连接到服务器,然后创建ssl,并绑定套接字,通过调动SSL_connect函数进行握手操作,成功后使用SSL_read和SSL_write进行业务通信 。
代码通常如下:
// 创建套接字,并连接到服务器 SOCKET s = socket(AF_INET, SOCK_STREAM, 0); struct hostent* hst = ::gethostbyname(pszServer); if(NULL == hst) return FALSE; unsigned long addr; struct sockaddr_in sockAddr; memcpy(&addr, hst->h_addr, hst->h_length); sockAddr.sin_family = AF_INET; sockAddr.sin_port = htons(nPort); sockAddr.sin_addr.S_un.S_addr = addr; // 连接到服务器 if(SOCKET_ERROR != ::connect(s, (struct sockaddr *)&sockAddr, sizeof(struct sockaddr_in))) return FALSE; // Openssl库初始化 OpenSSL_add_ssl_algorithms(); SSL_load_error_strings(); SSLeay_add_ssl_algorithms(); ERR_load_BIO_strings(); ctx = SSL_CTX_new(SSLv23_client_method()); // 创建ssl上下文,并绑定套接字 SSL* ssl = SSL_new (m_ctx); SSL_set_fd(m_ssl, s); // 开始ssl握手 int iRet = SSL_connect(m_ssl); if(1 != iRet) return FALSE; // 下面使用SSL_read或SSL_write进行通信 上述代码中SSL_connect函数是最重要的,它内部实现了SSL的握手过程,openssl在内部采用状态机的方式实现了整个握手,代码还是相当的晦涩 。几乎所有的SSL通信失败都是在握手阶段产生的,比如加密套件不匹配,服务器证书没有可靠的签名等 。
openssl中有个s_client命令集合,该命令用于实现客户端的通信,它的实现在s_client.c文件中,在这个庞大代码中有一个不起眼的函数调用,这个函数是SSL_set_tlsext_host_name 。该函数的作用是在客户端在发送ClientHello消息时,将所访问的主机(服务器)名称写入到server name的扩展字段中 。
【openssl升级后ssh登录失败】 if (!noservername && (servername != NULL || dane_tlsa_domain == NULL)) { if (servername == NULL) { if(host == NULL || is_dNS_name(host)) servername = (host == NULL) ? "localhost" : host; } if (servername != NULL && !SSL_set_tlsext_host_name(con, servername)) { BIO_printf(bio_err, "Unable to set TLS servername extension.\n"); ERR_print_errors(bio_err); goto end; } }调用该函数后会在ClientHello的扩展字段中增加一个Server Name字段 。如下图所示:

openssl升级后ssh登录失败

文章插图
我们在客户端编程时,通常不会调用该函数,因为会觉得该字段可有可无,既然已经连接到Host服务器上,为何还要将Host的名称告诉给服务器呢?如果我们不调用SSL_set_tlsext_host_name 函数,难道SSL会话会失败么?实际上即使不调用该函数,也是可以握手成功的,并不影响TLS的正常通信 。
但如果服务器强制校验该字段时,就会导致握手失败,这就好比HTTP协议中请求的HOST字段一样 。那么为什么有的服务器要强制校验该字段呢?如果一台服务器绑定不同的DSN名称(也就是一个IP地址绑定多个域名),这个扩展字段的意义就出来了,服务器会根据不同的域名提供不同的证书 。
举个例子,假设BAT都破产了,他们穷到共用一台服务器的地步,也就是一个IP地址绑定了百度、阿里和腾讯三个公司域名,并且这三家公司都给域名申请了数字证书,此时扩展字段的HOST的作用就体现出来了,openssl服务器端会跟据不同的host名称决定返回哪家公司的数字证书 。这也是Ngnix反向代理的原理,根据不同域名进行不同访问地址映射 。
写到这里,本文也即将结束,也许我们调用了SSL_set_tlsext_host_name函数也无法知道它的最终目的是什么,这也是我写这篇文章的原因之一吧 。
来源:https://www.cnblogs.com/softlee/p/16416574.html

推荐阅读