使用 Python Paramiko 对 SFTP 服务器进行双因素(密钥和键盘交互)身份验证

Two factor (key and keyboard-interactive) authentication to SFTP server using Python Paramiko

我正在尝试通过 Python 中的安全 SFTP 连接到服务器,但我一直收到身份验证错误。我确定我有正确的详细信息,因为我可以通过 WinSCP 正常连接:

我还查看了 Paramiko 日志文件的输出,它说密码验证失败,但我确定密码是正确的。

下面是我使用的代码,它是从各种 Whosebug 帖子拼凑而成的。知道为什么它无法连接吗?

import paramiko
import pysftp
from base64 import decodebytes

host = '<my server address>'
port = 6671
user = '<my username>'
password = '<my password>'
private_key = '<location of my private key file>'
host_key_data = b"""<contains my host key data>"""
host_key = paramiko.RSAKey(data=decodebytes(host_key_data))
cnopts = pysftp.CnOpts()
cnopts.hostkeys.add(host, 'ssh-rsa', host_key)
pysftp.Connection(host, username=user, password=password, private_key=private_key, port=port, cnopts=cnopts)

日志文件的输出:

DEB [20190626-12:51:16.270] thr=4   paramiko.transport: Kex agreed: diffie-hellman-group-exchange-sha256
DEB [20190626-12:51:16.271] thr=4   paramiko.transport: HostKey agreed: ssh-rsa
DEB [20190626-12:51:16.272] thr=4   paramiko.transport: Cipher agreed: aes128-ctr
DEB [20190626-12:51:16.273] thr=4   paramiko.transport: MAC agreed: hmac-sha1
DEB [20190626-12:51:16.274] thr=4   paramiko.transport: Compression agreed: none
DEB [20190626-12:51:16.774] thr=4   paramiko.transport: Got server p (1024 bits)
DEB [20190626-12:51:17.029] thr=4   paramiko.transport: kex engine KexGexSHA256 specified hash_algo <built-in function openssl_sha256>
DEB [20190626-12:51:17.030] thr=4   paramiko.transport: Switch to new keys ...
DEB [20190626-12:51:17.032] thr=2   paramiko.transport: Host key verified (ssh-rsa)
DEB [20190626-12:51:17.032] thr=2   paramiko.transport: Attempting password auth...
DEB [20190626-12:51:17.531] thr=4   paramiko.transport: userauth is OK
INF [20190626-12:51:17.532] thr=4   paramiko.transport: Auth banner: b'FactSet File Transfer System (FTS)\n'
INF [20190626-12:51:17.767] thr=4   paramiko.transport: Authentication (password) failed.

VBA 使用 WinSCP .NET 程序集的代码,有效:

' Setup session options
Dim mySessionOptions As New SessionOptions
With mySessionOptions
    .Protocol = Protocol_Sftp
    .HostName = "<hostname>"
    .PortNumber = 6671
    .UserName = "<username>"
    .Password = "<password>"
    .SshHostKeyFingerprint = "ssh-rsa 2048 fingerprintkey"
    .SshPrivateKeyPath = "location of private key file"
End With  
! 2019-06-27 17:24:18.691 Authenticating with public key "factset-rsa-key-20170717" from agent
. 2019-06-27 17:24:18.756 Sending Pageant's response
! 2019-06-27 17:24:19.260 Further authentication required
. 2019-06-27 17:24:19.266 Further authentication required
. 2019-06-27 17:24:19.500 Prompt (5, SSH server: Password Authentication, Using keyboard-interactive authentication., Password: )
. 2019-06-27 17:24:19.505 Using stored password.

您正在 WinSCP 中使用双因素身份验证(密钥和 keyboard-interactive)。

恐怕 pysftp 不支持双重身份验证。

直接使用Paramiko,支持multi-factor认证。 pysftp 在内部使用 Paramiko。

实施完整的 keyboard-interactive 身份验证可能比其他身份验证类型复杂一些。但是由于服务器通常使用 keyboard-interactive 身份验证来请求一个固定的密码,一些 SSH 库为这种情况提供了一个快捷方式。 WinSCP .NET 程序集也在您的 VBA 脚本中执行此操作(您指定 Password,尽管 WinSCP 执行 keyboard-interactive 身份验证,而不是密码身份验证)。 Paramiko 也是如此。如果您使用 Paramiko SSHClient.connectpassword 参数,Paramiko 将使用指定的密码响应第一个 keyboard-interactive 身份验证提示。

ssh = paramiko.SSHClient()
# ... 
ssh.connect(host, username=user, port=port, password=password, key_filename=private_key)

您还必须验证服务器的主机密钥(与您在 pysftp 和 WinSCP 中所做的方式相同)。
对于 Paramiko 解决方案,请参阅 Paramiko "Unknown Server"