Python pysftp/paramiko连接SFTP报no acceptable ciphers错误如何解决
问题根因
从提供的paramiko调试日志可直接定位两个核心问题:
- 依赖库版本兼容缺陷:你使用的
pysftp==0.2.9已停止维护超过6年,存在多处传参与适配bug:按位置传递的加密算法列表无法正确注入到底层paramiko的Transport实例、默认硬编码的加密算法列表过旧、无法自动加载ED25519格式的私钥。从调试日志可见,你手动传入的aes-ctr、aes-cbc类算法完全没有出现在客户端发起协商的加密列表中,证明参数传递完全失效。 - 服务端配置查询方式错误:你执行
ssh -Q cipher查询到的是服务器本地SSH客户端编译支持的所有加密算法,不是sshd服务实际运行时允许的加密算法列表,查询结果和服务端真实配置不匹配,导致你手动指定的算法可能根本不在服务端允许范围内。
排查思路
按优先级依次验证:
- 登录Rocky Linux服务器,执行
sshd -T | grep ciphers获取sshd服务实际启用的加密算法列表,确保客户端配置的算法和该列表存在至少一个交集 - 确认本地私钥格式为标准OpenSSH格式,而非Putty生成的.ppk格式,若为ppk格式需先转换为OpenSSH格式
- 确认服务器端
/home/pmartin/.ssh/authorized_keys文件权限为600,所属用户和用户组均为pmartin,避免sshd因权限过宽拒绝密钥认证 - 若服务器开启SELinux,执行
restorecon -Rv /home/pmartin/.ssh修复.ssh目录的安全上下文 - 确认Windows本地运行Python的用户对私钥文件有读权限
解决方案
优先选择方案1,无兼容层bug,稳定性最高。
方案1:弃用pysftp,直接基于paramiko实现
pysftp本身只是paramiko的薄封装,停更后已无法适配新版OpenSSH和paramiko的特性,直接使用paramiko可完全控制连接参数,避免中间层bug。
可直接运行的示例代码:
import paramiko import logging # 开启调试日志,连接异常时可直接看到协商过程 logging.basicConfig(level=logging.DEBUG) paramiko.common.logging.basicConfig(level=paramiko.common.DEBUG) # 连接配置 host = 'monServeur.domaine.fr' port = 22 username = 'pmartin' # Windows路径使用原始字符串避免转义问题 pkey_path = r'C:\Users\pmartin\.ssh\myPrivateKey' passphrase = 'mypassphrase' # 配置全量兼容加密算法列表,确保和服务端有交集 ciphers = [ 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'chacha20-poly1305@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr', 'aes256-cbc', 'aes192-cbc', 'aes128-cbc' ] # 初始化传输通道,手动指定加密算法 transport = paramiko.Transport((host, port)) transport.get_security_options().ciphers = ciphers # 加载带口令的私钥,根据你的私钥类型选择对应的类:RSA密钥用RSAKey,ED25519密钥用Ed25519Key private_key = paramiko.Ed25519Key.from_private_key_file(pkey_path, password=passphrase) # private_key = paramiko.RSAKey.from_private_key_file(pkey_path, password=passphrase) # 建立连接 transport.connect(username=username, pkey=private_key) # 初始化SFTP客户端执行业务操作 sftp = paramiko.SFTPClient.from_transport(transport) sftp.chdir('/home/pmartin') for entry in sftp.listdir_attr(): print(entry.filename) # 释放资源 sftp.close() transport.close()
方案2:保留pysftp依赖,修复传参逻辑
如果必须使用pysftp,需要放弃位置传参,全部使用关键字传参,同时手动加载私钥、通过CnOpts配置加密算法:
import pysftp import paramiko host = 'monServeur.domaine.fr' port = 22 username = 'pmartin' pkey_path = r'C:\Users\pmartin\.ssh\myPrivateKey' passphrase = 'mypassphrase' # 兼容加密算法列表 ciphers = [ 'aes256-gcm@openssh.com', 'aes128-gcm@openssh.com', 'chacha20-poly1305@openssh.com', 'aes256-ctr', 'aes192-ctr', 'aes128-ctr' ] # 手动加载私钥,适配RSA/ED25519格式 key = paramiko.Ed25519Key.from_private_key_file(pkey_path, password=passphrase) # key = paramiko.RSAKey.from_private_key_file(pkey_path, password=passphrase) # 配置连接参数 cnopts = pysftp.CnOpts() cnopts.ciphers = ciphers with pysftp.Connection( host=host, port=port, username=username, private_key=key, private_key_pass=passphrase, cnopts=cnopts ) as sftp: with sftp.cwd('/home/pmartin'): for entry in sftp.listdir_attr(): print(entry.filename)
内容的提问来源于stack exchange,提问作者Olivier MM
相关产品推荐
相关产品推荐

