Python显式FTP不协商TLS

时间:2017-08-24 15:17:34

标签: python ssl ftp

我正在尝试通过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

3 个答案:

答案 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

  

class ftplib.FTP_TLS(host=”, user=”, passwd=”, acct=”, keyfile=None, certfile=None, context=None, timeout=None, source_address=None)

     

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证书。