OpenSSL解惑2:如何强制选择协议的版本
clq的程序员学前班
OpenSSL 的示例网上很多,不过基本上都是比较基础的例子。看了我们前面的文章的同学一定对微信小程序要限制协议版本的事实印象深刻。我个人也是这样的,编写了示例过后我们就会想,那么在使用 openssl 的过程中是怎样选择协议的版本的呢?
带着这样的疑问我们先来看一个最常见的 openssl 示例,如下:
SSL * ssl;SSL_CTX* ctx;SSL_METHOD * meth;int status; int err;char buf [256]; //建立SSL/*初始化*/SSLeay_add_ssl_algorithms();OpenSSL_add_all_algorithms(); /*生成一个ssl结构*/ meth = SSLv23_client_method(); ctx = SSL_CTX_new(meth); ssl = SSL_new(ctx); /*把建立好的socket和ssl结构联系起来*/ SSL_set_fd(ssl,fd); /*ssl的握手过程*/ SSL_connect(ssl); /*接下来用ssl_write(), ssl_read()代替原有的write(),read()即可*/ /* 数据交换开始,用ssl_write,ssl_read代替write,read */ err = SSL_read(ssl, buf, sizeof(buf) - 1); //如果是SSL_ERROR_WANT_READ,则要重新接收数据 buf[err] = '\0'; printf ("got %d chars:%s\n", err, buf);
这里面要特别注意的是 SSL_set_fd 关联的 socket 必须是 socket 已经连接上网络了,否则后面的 SSL_connect 是不会成功的。因为我们已经在上一篇文章说过了, ssl/tsl 的"连接"不过是双方交换/协商通讯加密密码的过程,和真实的网络连接过程是无关的,所以是要先连接上网络的。
这样设置 socket 后可以就可以很顺利的连接上 163 邮箱的安全接口了(注意端口是 465 而不是传统的 25),运行结果如下:
我们可以看到,163 安全接口的返回和标准 smtp 的返回内容是一样的(当然是指解密后的数据内容)。
那我们模仿微信小程序,强制指定 tls1.2 会有什么结果呢?等等,怎么指定协议版本呢,这是个问题呀。
让我们来看看上面代码中的 SSLv23_client_method 函数,这个函数名本身就很有点奇怪嘛,我们前面的文章并没有说有一个 2.3 版本的 ssl 协议啊。这个函数其实不是指的协议是 2.3 或者 23 版本,而是指先尝试用 sslv3 连接,不行的话就用 sslv2 协议再连接,因为我们前面已经说过了,网络上的 ssl 支持情况是不尽相同的,一般的示例中为了适应面广一点当然是希望 v2、v3 都支持的了,所以我们就明白了,那么指定 v3 协议那就是 SSLv3_client_method 了?是的,您猜对的,还真就是有这个函数。换用这个函数的话确实也是能正确运行的,不过等等,我们前面说的是 tls 啊,虽然它和 sslv3 差不多,但毕竟不同嘛。这个在网上就有点难找了。我们换种思维,到代码里面去找吧,好吧很快看到一个有点象的 DTLSv1_2_client_method 函数,我们换上去。哇!编译正常通过了... 不过悲剧是运行结果是这样的:
我擦... 怎么没有看到熟悉的问好信息啊 ... 出错了吗? 是的,163 邮箱是不能用 DTLS v1.2 的。好吧,您一定以为是 163 的服务器还没升级 ... 其实不是。其实是所有的 dtls 都不行,光看代码无论如何是明白不了这其中的原因的,真正的原因是 dtls 是在 tls 上进行的扩展,虽然 DTLS v1.2 比 TLS v1.2 晚出现,但它们并不兼容,不过不兼容的原因不是因为新旧问题,而因为 DTLS 是用在 udp 协议上的,而 ssl/tls 是基于 tcp 协议的。不了解 udp 协议的同学我实在是解释不下去了,不过简单地来说,udp 协议没有安全传输协议的,是看到 ssl/tls 在 tcp 上的成功而借鉴产生的一个标准。从根本上来说它和 ssl/tls 是两个东西。
好了,现在可以告诉大家了,正确的函数是 TLSv1_2_client_method 运行结果如图,可以看到结果和 SSLv23_client_method 的结果是一模一样的。
这也说明 163 是可以支持最新的 tls1.2 安全协议的。
那么问题又来了,有没有一种自动选择 tls 协议的函数呢?问得好,真的有的,那就是 TLS_client_method。更换这个函数,运行结果会是一样的,因为截图是一样的,我们就不贴了。聪明的同学估计还会问,那有没有自动选择 tls 还是 ssl 的呢? 这个... 要求虽然有点高,不过还是有的,而且就是 TLS_client_method 本身,它可以按最高的 tls 版本先尝试,不行的话就一路向下兼容 ssl。 这在程序内部实现其实并不难,因为我们前面说过了 tls 其实有时间也叫做 ssl3.1 ,实现时一样判断 ssl 版本的高低就行了。
所以在这里我们有一个很重要的东西一定要和大家说清楚,网上示例的 SSLv23_client_method 已经不合适了,现在时代已经进步,在实际的真实程序中应该用 TLS_client_method 来代替这些示例中的代码!
好了,以上就是这一次所要讲的关于 openssl 中强制选择协议版本的方法。