首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Python上的SSL问题

Python上的SSL问题
EN

Stack Overflow用户
提问于 2019-10-05 07:46:06
回答 2查看 2.3K关注 0票数 5

我在Python中有一个通过aiosmtp接受(E)SMTP请求的代码,但是由于我在Debian 10上推送了这段代码,所以有一些以前没有出现的错误(而且我的代码没有更改):

SSL:无共享密码()

SSL握手失败协议:传输:<_SelectorSocketTransport fd=11 read=polling write=>

代码语言:javascript
复制
SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1056)
  File "asyncio/sslproto.py", line 625, in _on_handshake_complete
    raise handshake_exc
  File "asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "ssl.py", line 763, in do_handshake
    self._sslobj.do_handshake()

以及:

SSL:关闭通知(_ssl.c:2609)后的KRB5_S_INIT应用程序数据

数据接收协议中的SSL错误:传输:<_SelectorSocketTransport fd=15 read=polling write=>

代码语言:javascript
复制
SSLError: [SSL: KRB5_S_INIT] application data after close notify (_ssl.c:2609)
  File "asyncio/sslproto.py", line 526, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "asyncio/sslproto.py", line 207, in feed_ssldata
    self._sslobj.unwrap()
  File "ssl.py", line 767, in unwrap
    return self._sslobj.shutdown()

我认为这两个问题是相关的。

不幸的是,这两个堆栈跟踪没有显示与我的代码相关的任何内容,这使得我很难更好地了解这是在哪里发生的,并且异常与另一个异常(Python3)无关。

这是我的包裹的版本:

uname -a:Linux my-server 4.19.0-5-amd64 #1 SMP Debian 4.19.37-5+deb10u2 (2019-08-08) x86_64 GNU/Linux

python --版本:Python 3.7.3

pip冷冻

代码语言:javascript
复制
aiomysql==0.0.20
aiosmtpd==1.2
asn1crypto==0.24.0
atpublic==1.0
authres==1.2.0
beanstalkc3==0.4.0
blinker==1.4
certifi==2018.8.24
cffi==1.12.3
chardet==3.0.4
Click==7.0
cloud-init==18.3
configobj==5.0.6
cryptography==2.6.1
distro-info==0.21
dkimpy==0.9.4
dnspython==1.16.0
fail2ban==0.10.2
Flask==1.1.1
idna==2.6
itsdangerous==1.1.0
Jinja2==2.10.1
jsonpatch==1.21
jsonpointer==1.10
jsonschema==2.6.0
MarkupSafe==1.1.0
mysqlclient==1.4.4
oauthlib==2.1.0
psutil==5.6.3
py3dns==3.2.1
pycparser==2.19
PyGObject==3.30.4
pyinotify==0.9.6
PyJWT==1.7.0
PyMySQL==0.9.2
PyNaCl==1.3.0
pyspf==2.0.13
pysrs==1.0.3
python-apt==1.8.4
python-dotenv==0.10.3
PyYAML==3.13
requests==2.21.0
sentry-sdk==0.12.3
six==1.12.0
systemd-python==234
unattended-upgrades==0.1
urllib3==1.24.1
uWSGI==2.0.18
Werkzeug==0.16.0

我相信,如果我的代码有问题,我会在Debian 9和更早的时候出现这个错误,这是我从来没有过的。

我搜索所以和谷歌关于这个错误,但没有找到任何东西。我怀疑特定项目的特定版本(aiosmtpd、异步或python)存在一些问题,但没有任何线索。

我希望你能帮我:)

更新:

我在通讯中增加了密码的追踪。共享密码是:

代码语言:javascript
复制
[[TLS_AES_256_GCM_SHA384, TLSv1.3, 256], [TLS_CHACHA20_POLY1305_SHA256, TLSv1.3, 256], [TLS_AES_128_GCM_SHA256, TLSv1.3, 128], [ECDHE-ECDSA-AES256-GCM-SHA384, TLSv1.2, 256], [ECDHE-RSA-AES256-GCM-SHA384, TLSv1.2, 256], [DHE-RSA-AES256-GCM-SHA384, TLSv1.2, 256], [ECDHE-ECDSA-CHACHA20-POLY1305, TLSv1.2, 256], [ECDHE-RSA-CHACHA20-POLY1305, TLSv1.2, 256], [DHE-RSA-CHACHA20-POLY1305, TLSv1.2, 256], [ECDHE-ECDSA-AES128-GCM-SHA256, TLSv1.2, 128], [ECDHE-RSA-AES128-GCM-SHA256, TLSv1.2, 128]]

套接字的密码是:[ECDHE-RSA-AES256-GCM-SHA384, TLSv1.2, 256],它位于共享密码器中。

更新2

我可以复制错误,但只有在特定的条件下。

在新服务器上,下面是我运行的代码:

代码语言:javascript
复制
import asyncio, logging, sys, signal, ssl
from aiosmtpd.controller import Controller
from aiosmtpd.handlers import Debugging
from aiosmtpd.smtp import SMTP

class ControllerTls(Controller):
    def factory(self):
        context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
        context.load_cert_chain('./certs/certificate.pem', './certs/id_rsa')
        context.load_dh_params('./certs/dhparams.pem')
        return SMTP(
            self.handler,
            tls_context=context
        )


# Temporary outputing errors from mail.log
streamHandler = logging.StreamHandler(sys.stdout)
streamHandler.setFormatter(logging.Formatter('[%(asctime)-15s] (%(levelname)s) - %(message)s'))
streamHandler.setLevel(logging.INFO)

maillog = logging.getLogger('mail.log')
maillog.setLevel(logging.INFO)
maillog.addHandler(streamHandler)

controller = ControllerTls(Debugging(), hostname='0.0.0.0', port=2125)
controller.start()
print('Controller started!')
sig = signal.sigwait([signal.SIGINT, signal.SIGQUIT])
controller.stop()

这是一个基本的脚本,帮助我重现这个问题。

在旧服务器上,我运行以下代码:

代码语言:javascript
复制
import smtplib, ssl, sys

port = 25
if len(sys.argv) == 3:
    port = sys.argv[2]

def com(client, command, *args, **kwargs):
    result = getattr(client, command)(*args, **kwargs)
    if result[0] > 500:
        print('[FATAL] - An error occured!')
        print(result)
        client.quit()
        exit()

context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
context.load_cert_chain('/var/www/towboat/certs/certificate.pem', '/var/www/towboat/certs/id_rsa')
context.set_ciphers('ECDHE-ECDSA-AES256-GCM-SHA384')
client = smtplib.SMTP(sys.argv[1], port=port)
com(client, 'ehlo')
com(client, 'starttls', context=context)
com(client, 'ehlo')
com(client, 'mail', 'contact@improvmx.com')
com(client, 'rcpt', 'cyril@improvmx.com')
com(client, 'quit')

print('All good !')

我称之为:

sendmail.py {ip.of.new.server} 2125

在旧服务器(运行脚本的服务器)上,我得到以下错误:

代码语言:javascript
复制
Controller started!
[2019-10-08 15:57:11,878] (INFO) - Peer: ('ip.of.old.server', 45492)
[2019-10-08 15:57:11,878] (INFO) - ('ip.of.old.server', 45492) handling connection
[2019-10-08 15:57:11,880] (INFO) - ('ip.of.old.server', 45492) Data: b'ehlo {name old server}'
[2019-10-08 15:57:11,883] (INFO) - ('ip.of.old.server', 45492) Data: b'STARTTLS'
[2019-10-08 15:57:11,883] (INFO) - ('ip.of.old.server', 45492) STARTTLS
SSL handshake failed
protocol: <asyncio.sslproto.SSLProtocol object at 0x7f04d33d7d30>
transport: <_SelectorSocketTransport fd=7 read=polling write=<idle, bufsize=0>>
Traceback (most recent call last):
  File "/usr/lib/python3.7/asyncio/sslproto.py", line 625, in _on_handshake_complete
    raise handshake_exc
  File "/usr/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/usr/lib/python3.7/ssl.py", line 763, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1056)
SSL error in data received
protocol: <asyncio.sslproto.SSLProtocol object at 0x7f04d33d7d30>
transport: <_SelectorSocketTransport closing fd=7 read=idle write=<idle, bufsize=0>>
Traceback (most recent call last):
  File "/usr/lib/python3.7/asyncio/sslproto.py", line 526, in data_received
    ssldata, appdata = self._sslpipe.feed_ssldata(data)
  File "/usr/lib/python3.7/asyncio/sslproto.py", line 189, in feed_ssldata
    self._sslobj.do_handshake()
  File "/usr/lib/python3.7/ssl.py", line 763, in do_handshake
    self._sslobj.do_handshake()
ssl.SSLError: [SSL: NO_SHARED_CIPHER] no shared cipher (_ssl.c:1056)
[2019-10-08 15:58:33,909] (INFO) - Connection lost during _handle_client()

超级奇怪的是,如果我在本地机器上复制sendmail脚本,并运行它指向新服务器,我就不再有错误了!

(所以这个问题一定与旧服务器有关?但是为什么新服务器会显示异常呢?!)

如果我切换脚本(测试从新服务器发送电子邮件到旧服务器),它会工作.

EN

回答 2

Stack Overflow用户

发布于 2019-10-10 07:32:31

我认为这就是原因:

v1.1.1d在新服务器上,1.1.0d在旧服务器上

1.1.1行引入了TLSv3和许多其他相当重要的更改-- 请参阅更改日志

正如我在aiosmtpd上看到的那样,您正确地猜到了错误的原因是aiosmtpd。其原因是它支持您至少需要Python3.5,它没有对OpenSSL1.1.1的支持。目前只有python 3.7 (它还没有完全移植到python3.6)支持OpenSSL1.1.1。

由于aiosmtpd的最新版本是1.2 (2018-09-01),所以可以假定(没有看到任何按下),他们还没有实现新的OpenSSL1.1.1 [2018年9月11日],它引入了的主要更改。

除了为aiosmtpd提供公关之外,您唯一的选择是将降级为1.1.0行的最新版本,即当前的1.1.0i

票数 2
EN

Stack Overflow用户

发布于 2019-10-14 01:32:44

首先,我看到密码ECDHE-ECDSA-美学256-GCM- see 384不能工作,因为双方都使用RSA证书(我想知道客户端是否真的使用他的证书进行身份验证,还是只在服务器模式下配置错误)。

如果您在使用OpenSSL 1.1.1的python上运行相同的客户端脚本,服务器将同意TLSv1.3,其中密码套件不能再禁用,因此即使在set_ciphers中也仍然允许。

我想,如果您选择正确的ECDHE-RSA-美学256-GCM-RSA 384或在这方面对“旧”(Openssl 1.1.0) Debian 9没有任何更改,它将连接到新服务器,而不会出现TLSv1.2问题。

也就是说,在新服务器上确定密码套件的旧脚本的问题也与这样一个事实有关: TLSv1.3密码套件不能被禁用,脚本希望它可以禁用测试中的任何密码套件(这就是它的工作方式)。

有些密码套件现在完全没有OpenSSLv1.1.1,但还有一些只是默认禁用的(当前的Python只允许高密码,默认情况下不允许MD5/RC4,当然也不允许SSLv3和更高版本的密码)。

由于python3.6,获得提供的密码列表非常简单(因此不再需要中断的脚本):

代码语言:javascript
复制
root@somehost:~# python3
Python 3.7.3 (default, Oct  7 2019, 12:56:13)
[GCC 8.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import ssl
>>> ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
>>> for cipher in ctx.get_ciphers(): print(cipher['name']+' '+cipher['protocol']) if cipher['auth'] == 'auth-rsa' else None
...
ECDHE-RSA-AES256-GCM-SHA384 TLSv1.2
DHE-RSA-AES256-GCM-SHA384 TLSv1.2
ECDHE-RSA-CHACHA20-POLY1305 TLSv1.2
DHE-RSA-CHACHA20-POLY1305 TLSv1.2
ECDHE-RSA-AES128-GCM-SHA256 TLSv1.2
DHE-RSA-AES128-GCM-SHA256 TLSv1.2
ECDHE-RSA-AES256-SHA384 TLSv1.2
DHE-RSA-AES256-SHA256 TLSv1.2
ECDHE-RSA-AES128-SHA256 TLSv1.2
DHE-RSA-AES128-SHA256 TLSv1.2
ECDHE-RSA-AES256-SHA TLSv1.0
DHE-RSA-AES256-SHA SSLv3
ECDHE-RSA-AES128-SHA TLSv1.0
DHE-RSA-AES128-SHA SSLv3
AES256-GCM-SHA384 TLSv1.2
AES128-GCM-SHA256 TLSv1.2
AES256-SHA256 TLSv1.2
AES128-SHA256 TLSv1.2
AES256-SHA SSLv3
AES128-SHA SSLv3

因此,对于TLSv1.2连接,最低要求是KEX 128-SHA 256(没有ECDHE )或DHE AES128-SHA 256/ECDHE AES128-SHA 256/最小OpenSSL Version 1.0.1 Released 14-March-2012。因此,不支持TLSv1.2的OpenSSL版本应该无法连接。

在生产中,可能会有一些OpenSSLv0.98客户端(至少我知道一些我还需要维护--我认为我已经拥有了在更新的OpenSSL上构建产品所需的所有工具)。如果不是“禁止的”SSLv3,它们只能讨论TLSv1.0。他们至少可以使用显示为SSLv3的套房。

因此,不应该有许多客户端不能使用默认的方案,但是仍然可能有一些客户端被配置成特定的密码套件,从今天的角度来看这些套件是不可接受的,甚至更旧的系统,不太强大的硬件,不同的SSL库供应商,.因此,您需要找到一个示例,该示例实际上无法查看必须允许哪些额外的加密套件--或者可能是不同的身份验证机制。

对于这一讨论中的另一点,如降级OpenSSL,这肯定不是前进的道路。不可能有许多客户端无法与默认设置连接,更不会与OpenSSLv1.1.1仍然提供的所有可用设置连接。如果他们需要更新的话..。

如果您真的需要/需要,您可以编译一个旧的OpenSSL,使其位于一个不同的位置,并将一个Python编译到那个较旧的OpenSSL版本中。也许只为客户端在第二个端口上启动第二个服务器。或者您可以为此运行一个容器,但是您不能降低系统的级别-OpenSSL。

第二个错误,KRB5_S_INIT,确实是一个错误,看起来就像Python异步模块中的一个bug,它是Python7.3引入的。但是,只有当连接不可用时才会发生此错误(因此,由于没有通用密码套件的情况,连接被取消了)。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58246388

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档