为什么TLS会变慢?

时间:2016-11-14 04:28:51

标签: python performance sockets ssl tls1.2

我应该期望增加SSL / TLS多少开销?

我使用Ubuntu 14.04和OpenSSL 1.0.1f以及Python 3.4.3。

test.py

from datetime import datetime
import socket
import ssl

with socket.socket() as server:
    server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    server.bind(('', 8001))
    server.listen(1)
    while True:
        with server.accept()[0] as client:
            start = datetime.now()
            client = ssl.wrap_socket(client, 'key.pem', 'cert.pem', True)
            end = datetime.now()
            print('{:.0f}ms'.format((end - start).total_seconds() * 1000))
            client.send(b'hello')
            client.shutdown(socket.SHUT_RDWR)

启动服务器

$ openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
$ python3 test.py

并连接

$ for i in $(seq 20); do openssl s_client -cert cert.pem -key key.pem -connect localhost:8001; done 
$ # output shows TLSv1.2 is being used

即使在同一主机上, TLS协商也需要15ms。当我删除TLS时,我测量整个连接,传输和关闭是< 1毫秒。

我在这里没有太多经验,但这似乎比我预期的要慢得多。那是数百万个时钟周期。从洛杉矶到纽约旅行的速度比光慢。

(1)这种表现与我的预期相符吗? (2)导致这种性能的限制因素是什么? (3)我可以更改此代码以更快地进行初始TLS协商吗?

2 个答案:

答案 0 :(得分:4)

在普通TCP的情况下,您只有一次TCP握手(1次RTT),它在操作系统内核中完全处理,以最大限度地减少延迟和资源使用。

对于TLS和您的代码,除了此之外,您还需要一个完整的TLS握手,因为您的代码不会进行会话重用。这意味着2个RTT仅用于握手和每次密钥交换的重加密(影响取决于密码)。在(内核中)TCP关闭之上还有一个额外的RTT用于有序TLS关闭。此外,传输的数据比TCP握手更多,TLS握手完全在用户空间完成,这会导致由调度,读取和写入数据的许多系统调用以及内核和用户空间之间的上下文切换和副本引起的额外延迟。这些系统调用。此外,您每次都会设置一个新的SSL上下文,包括加载证书等,这会增加更多不必要的开销。

所有这些开销都在同一系统上完成两次,因为您在同一系统上有客户端和服务器,并且您还使用客户端证书。 此外,您不会传输非常少的数据(远远少于单独的TLS握手所需的数据),因此您只能测量TLS握手所需的时间,即TLS中最昂贵的部分。而且,由于您有一个单线程客户端和一个单线程服务器,因此每个都可能是速度限制。

换句话说:您的基准测试并未反映常见现实场景中TLS与TCP的实际开销。但是它表明如果以过于简单的方式使用TLS会产生明显的影响,即通常不会通过执行大量TLS的应用程序进行任何优化。

答案 1 :(得分:3)

  • 因为TLS会进行7次TCP访问以建立加密层
  • 因为涉及加密(RSA,DHE,......)

15ms真的没那么多。