我正在尝试通过FTP TLS显式传输文件到主机,但不知道如何处理证书。事务的WinSCP日志显示已处理TLS协商并验证证书。但我的Python脚本无法做到这一点。我知道证书指纹和密码,但不知道如何实现它。
我的剧本:
import ftplib
import ssl
def main():
ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1_2)
ftps = ftplib.FTP_TLS(context=ctx)
try:
ftps.set_debuglevel(2)
print(ftps.connect(host))
ftps.auth()
print(ftps.login(username,password))
print("1")
ftps.prot_p()
print("2")
#ftps.cwd('/')
print("3")
print (ftps.retrlines('LIST'))
print("4")
ftps.storbinary("STOR test.csv", open('C:\\test.csv', 'rb'))
print("5")
except Exception as ex:
print("error: ")
print(ex)
ftps.close()
input('Hit <ENTER> to close...')
if __name__ == "__main__":
main()
这是我的输出,最后一行是Python错误:
*get* '220 Microsoft FTP Service\n'
*resp* '220 Microsoft FTP Service'
220 Microsoft FTP Service
*cmd* 'AUTH TLS'
*put* 'AUTH TLS\r\n'
*get* '234 AUTH command ok. Expecting TLS Negotiation.\n'
*resp* '234 AUTH command ok. Expecting TLS Negotiation.'
*cmd* 'USER xxx'
*put* 'USER xxx\r\n'
*get* '331 Password required\n'
*resp* '331 Password required'
*cmd* 'PASS ********'
*put* 'PASS ********\r\n'
*get* '230 User logged in.\n'
*resp* '230 User logged in.'
230 User logged in.
1
*cmd* 'PBSZ 0'
*put* 'PBSZ 0\r\n'
*get* '200 PBSZ command successful.\n'
*resp* '200 PBSZ command successful.'
*cmd* 'PROT P'
*put* 'PROT P\r\n'
*get* '200 PROT command successful.\n'
*resp* '200 PROT command successful.'
2
3
*cmd* 'TYPE A'
*put* 'TYPE A\r\n'
*get* '200 Type set to A.\n'
*resp* '200 Type set to A.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (x,x,x,x,4,4).\n'
*resp* '227 Entering Passive Mode (x,x,x,x,4,4).'
*cmd* 'LIST'
*put* 'LIST\r\n'
*get* '125 Data connection already open; Transfer starting.\n'
*resp* '125 Data connection already open; Transfer starting.'
*get* '226 Transfer complete.\n'
*resp* '226 Transfer complete.'
226 Transfer complete.
4
*cmd* 'TYPE I'
*put* 'TYPE I\r\n'
*get* '200 Type set to I.\n'
*resp* '200 Type set to I.'
*cmd* 'PASV'
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (x,x,x,x,4,3).\n'
*resp* '227 Entering Passive Mode (x,x,x,x,4,3).'
*cmd* 'STOR test.csv'
*put* 'STOR test.csv\r\n'
*get* '125 Data connection already open; Transfer starting.\n'
*resp* '125 Data connection already open; Transfer starting.'
error:
The read operation timed out
答案 0 :(得分:3)
事务的WinSCP日志显示已处理TLS协商并验证证书。但我的Python脚本无法做到这一点。我知道证书指纹和密码,但不知道如何实现它。
您认为,您的Python脚本在TLS协商中不会失败。
客户端使用AUTH TLS
命令从普通连接升级到TLS,服务器接受该命令,可以在日志中看到:
*put* 'AUTH TLS\r\n'
*get* '234 AUTH command ok. Expecting TLS Negotiation.\n'
之后,完成TLS协商(即TLS握手)。如果握手失败,客户端将中止。但握手成功,以便客户端可以继续使用更多命令,该命令也会被服务器接受:
*put* 'USER xxx\r\n'
*get* '331 Password required\n'
因此,TLS协商没有问题。这也不是数据传输本身的问题,可以看出客户端成功地从服务器传输数据,即目录列表:
*put* 'PASV\r\n'
*get* '227 Entering Passive Mode (x,x,x,x,4,4).\n'
*put* 'LIST\r\n'
*get* '125 Data connection already open; Transfer starting.\n'
*get* '226 Transfer complete.\n'
仅在将文件传输到服务器时失败,因为服务器未在预期时间内回复:
*put* 'STOR test.csv\r\n'
*get* '125 Data connection already open; Transfer starting.\n'
error:
The read operation timed out
目前还不清楚为什么服务器无法响应。一个可能的原因可能是,在服务器成功响应之前,某个应用程序(如防病毒)需要处理该文件,并且此处理需要的时间太长。
答案 1 :(得分:2)
问题结果是在传输文件后解开套接字。更具体地说,shutdown()方法似乎等待服务器查看是否可以关闭套接字。在这种情况下,服务器不回答。该文件确实已传输,但套接字问题会在收到“文件传输完成”响应之前抛出错误。
似乎是这样(将文件发送到服务器): 1.建立FTP TLS 2.客户发出STOR命令 3.创建安全套接字 4.文件已传输 5.插座被打开并丢弃。 6.从服务器收到“传输完成”响应。
但是第5步的问题阻止了6发生。
我不确定这是不是一个bug。一些研究表明,套接字可以关闭而不是关闭,服务器可能无法响应套接字关闭请求。
我的解决方案是修改我的本地python库来处理它。
答案 2 :(得分:0)
<强>注释强>:
ssl.create_default_context()
会导致错误:“[SSL:CERTIFICATE_VERIFY_FAILED]”
ssl.create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None)
为给定目的返回具有默认设置的新SSLContext对象。cafile,capath,cadata表示可信任证书验证的可选CA证书,如SSLContext.load_verify_locations()中所示。如果所有三个都是None,则此函数可以选择信任系统的默认CA证书。
使用如下:
ctx = ssl.create_default_context(Purpose.CLIENT_AUTH)
问题:不知道如何处理证书
使用ctx = ssl.load_cert_chain(certfile, keyfile=None, password=None)
,
你已经有context=ctx
。
FTP子类,它根据RFC 4217中的描述向FTP添加TLS支持
context=
是一个ssl.SSLContext对象,它允许将SSL配置选项证书和私钥捆绑到一个(可能是长期存在的)结构中。
keyfile=
和certfile=
是上下文的传统替代方案 - 它们可以指向用于SSL连接的PEM格式的私钥和证书链文件。
自版本3.6以来已弃用:
密钥文件和证书文件已弃用,支持context
。请改用ssl.SSLContext.load_cert_chain(),或者让ssl.create_default_context()为您选择系统的可信CA证书。