1、问题描述
vsftpd 3.0.3升级到3.0.5后,Java ftps连接不成功,报以下错误:
javax.net.ssl.SSLHandshakeException: Remote host terminated the handshake
at java.base/sun.security.ssl.SSLSocketImpl.handleEOF(SSLSocketImpl.java:1715)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1514)
at java.base/sun.security.ssl.SSLSocketImpl.readHandshakeRecord(SSLSocketImpl.java:1421)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:455)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:426)
at org.apache.commons.net.ftp.FTPSClient.sslNegotiation(FTPSClient.java:259)
at org.apache.commons.net.ftp.FTPSClient._connectAction_(FTPSClient.java:205)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:169)
at org.apache.commons.net.SocketClient.connect(SocketClient.java:189)
at com.legrand.common.util.FtpsUtil.ftpLogin(FtpsUtil.java:55)
at com.legrand.common.util.FtpsUtil.main(FtpsUtil.java:301)
Suppressed: java.net.SocketException: 你的主机中的软件中止了一个已建立的连接。
at java.base/sun.nio.ch.NioSocketImpl.implWrite(NioSocketImpl.java:420)
at java.base/sun.nio.ch.NioSocketImpl.write(NioSocketImpl.java:440)
at java.base/sun.nio.ch.NioSocketImpl$2.write(NioSocketImpl.java:826)
at java.base/java.net.Socket$SocketOutputStream.write(Socket.java:1035)
at java.base/sun.security.ssl.SSLSocketOutputRecord.encodeAlert(SSLSocketOutputRecord.java:81)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:407)
at java.base/sun.security.ssl.TransportContext.fatal(TransportContext.java:314)
at java.base/sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:467)
... 7 common frames omitted
Caused by: java.io.EOFException: SSL peer shut down incorrectly
at java.base/sun.security.ssl.SSLSocketInputRecord.read(SSLSocketInputRecord.java:483)
at java.base/sun.security.ssl.SSLSocketInputRecord.readHeader(SSLSocketInputRecord.java:472)
at java.base/sun.security.ssl.SSLSocketInputRecord.decode(SSLSocketInputRecord.java:160)
at java.base/sun.security.ssl.SSLTransport.decode(SSLTransport.java:111)
at java.base/sun.security.ssl.SSLSocketImpl.decode(SSLSocketImpl.java:1506)
... 9 common frames omitted
15:53:04.240 [main] INFO com.legrand.common.util.FtpsUtil - Failed to log in to FTP service: Remote host terminated the handshake
使用FlashFXP连接也不成功,报:TLSv1.2 协商失败,已断开
2、问题排查
2.1 Wireshark抓包
使用Wireshark抓包结果为:
可以看到在ftps连接过程中报错:SSL routines::no ciphers available ,翻译过来就是没有可用的密码。
为什么没有可用的密码,是不是配置文件里没有配?
2.2 vsftpd.conf配置文件检查
打开vsftpd.conf 搜索ciphers,确实没有找到。
# Run in the foreground to keep the container running:
background=NO
# Allow anonymous FTP? (Beware - allowed by default if you comment this out).
anonymous_enable=NO
# Uncomment this to allow local users to log in.
local_enable=YES
## Enable virtual users
guest_enable=YES
## Virtual users will use the same permissions as anonymous
virtual_use_local_privs=YES
# Uncomment this to enable any form of FTP write command.
write_enable=YES
## PAM file name
pam_service_name=vsftpd_virtual
## Home Directory for virtual users
user_sub_token=$USER
local_root=/home/vsftpd/$USER
# You may specify an explicit list of local users to chroot() to their home
# directory. If chroot_local_user is YES, then this list becomes a list of
# users to NOT chroot().
chroot_local_user=YES
# Workaround chroot check.
# See https://www.benscobie.com/fixing-500-oops-vsftpd-refusing-to-run-with-writable-root-inside-chroot/
# and http://serverfault.com/questions/362619/why-is-the-chroot-local-user-of-vsftpd-insecure
allow_writeable_chroot=YES
## Hide ids from user
hide_ids=YES
## Enable logging
# Active les messages de changement de r..pertoire
dirmessage_enable=YES
# Utilisation de log pour les uploads et downloads (par defaut /var/log/vsftpd.log)
xferlog_enable=YES
# Emplacement du fichier de log
xferlog_file=/var/log/vsftpd/vsftpd.log
#xferlog_file=/var/log/vsftpd.log
# Formatage de la log au standard wu-ftpd
xferlog_std_format=YES
# Utilisation de 2 fichiers de log diff..rents (Par d..faut /var/log/xferlog et /var/log/vsftpd.log)
dual_log_enable=YES
## Enable active mode
port_enable=YES
connect_from_port_20=YES
ftp_data_port=20
## Disable seccomp filter sanboxing
seccomp_sandbox=NO
## SSL
ssl_enable=yes
ssl_sslv2=no
ssl_sslv3=no
ssl_tlsv1=yes
allow_anon_ssl=YES
force_anon_data_ssl=YES
force_anon_logins_ssl=YES
force_local_data_ssl=YES
force_local_logins_ssl=YES
rsa_cert_file=/etc/vsftpd/.sslkey/vsftpd.pem
implicit_ssl=NO
require_ssl_reuse=NO
### Variables set at container runtime
pasv_address=
pasv_max_port=26100
pasv_min_port=21100
pasv_addr_resolve=NO
pasv_enable=YES
file_open_mode=0666
local_umask=077
#reverse_lookup_enable=NO
pasv_promiscuous=NO
port_promiscuous=NO
那么是不是vsftpd有对应的配置参数,只是我们没有加上去而已呢?
2.3 vsftpd.conf说明文档
找到vsftpd.conf的说明文档:Manpage of VSFTPD.CONF
果然找到了参数ssl_ciphers,该参数的作用是设置ssl连接通道加密的算法,默认值为:DES-CBC3-SHA
将ssl_ciphers=DES-CBC3-SHA 设置到vsftpd.conf,重启,发现无法启动。
那么我们不禁要问:是不是ssl_ciphers设置不对,ssl_ciphers要设置成什么才合适?
2.4 cipher的介绍
相关文献可以看下openssl官网关于cipher的介绍/docs/manmaster/man1/ciphers.html。也可以在安装了openssl的linux下输入
man ciphers
可以把命令结果输出到文件查看更方便些。
man ciphers > openssl.txt
也就是说 ssl_ciphers可以设置为ALL、HIGH、MEDIUM、LOW等一组密码套件,如:ssl_ciphers=HIGH。也可以设置为如:ssl_ciphers=DHE-DSS-AES128-SHA256,单个密码套件。
3、问题解决
最终选择设置为: ssl_ciphers=HIGH #“高”加密密码套件。这目前指的是密钥长度大于128位的密码,以及一些密钥长度为128位的密码套件。
因为安全性更高,同时避免只设置单个密码套件客户端刚好不支持的情况。
重启vsftpd,是用FlashFXP连接成功,至此问题解决。
4、原因分析
vsftpd3.0.5 默认支持TLSv1.2+
TLS1.2新特性:
1)添加已验证加密支持
2) 添加对HMAC-SHA256密码套件的支持:
3) 删除IDEA和DES密码套件;
4)虽然大部分扩展的实际文档还是在其他地方,但TLS将扩展和协议的主规格说明书进行了集成5)客户端可以使用一种新的扩展( signature algorithms)来通报它愿意接受的散列和签名算法6)当使用TLS1.2套件或者以协商协议是TLS1.2为条件使用之前的套件时,在PRF中使用SHA256替MD5/SHA1组合。
7)允许密码套件定义其自身的PRF:
8) 使用单一散列代替用于数字签名的MD5/SHA1组合。默认使用SHA256,并且密码套件可以指定其自身使用的散列。签名散列算法以往是由协议强制指定,现在是散列函数式签名结构中的一部分而目在实施启用中可以选择最佳算法
9)密码套件可以显式指定Finished消息中的verify data成员的长度
总结:可以看出因为vsftpd3.0.5 默认支持TLSv1.2+,ssl_ciphers默认配置为DES-CBC3-SHA ,而TLSv1.2又删除了DES密码套件,故ftps连接时就报错:SSL routines::no ciphers available ,因此将ssl_ciphers设置为其他可用密码组件就可以了。