【问题标题】:Apache FTP Client decide to use SSL or raw FTPApache FTP 客户端决定使用 SSL 或原始 FTP
【发布时间】:2016-09-16 09:47:19
【问题描述】:

在我工作的地方,我们拥有庞大的服务器资产,可以使用使用 FTP 的内部软件来传输文件。正在计划升级所有 FTP 以使用 FTPS 和 SSL 证书。

在撰写此问题时,我正在开展一个项目,以使用 Java/Apache 对我们的文件传输软件(它相当老旧)进行现代化改造。

我使用 Apache 软件编写了一个 FTP 客户端,并且还编写了一个非常相似的 FTPS 客户端。两者都按预期工作。

但是,如果 FTPS 客户端尝试连接到非 FTPS 服务器,则会引发 SSLException。 FTP 客户端在这种情况下工作正常。

最终我想将两个客户端合理化为一个可以管理 FTP 和 FTPS 连接的客户端。

我的问题很简单:

在尝试 SSL 连接之前,有没有办法使用 Apache/Java 检测远程服务器上正在使用的协议?

一个更好的解决方案是模拟 cURL 并尝试 FTPS 并下拉到 FTP。

任何帮助或建议将不胜感激。

代码如下

package jtm.ftp.client;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.GeneralSecurityException;
import java.security.KeyStore;

import javax.net.ssl.KeyManager;
import javax.net.ssl.SSLException;

import jtm.common.JtmConstants;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPReply;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.commons.net.util.TrustManagerUtils;
import org.apache.log4j.Logger;

public class JtmFTPSClient
{
    static Logger log = Logger.getLogger( JtmFTPSClient.class.getName( ) );   

    private static int PORT = JtmConstants.FTP_PORT;

    private static FTPSClient ftps = null;

    private static boolean IS_IMPLICIT = false;


    public static boolean getOrPut( String correlationID, String hostname, String user, String pass, String remoteFile, String localFile, String mode, String direction  )
    {        
        ftps = new FTPSClient( IS_IMPLICIT );       //look at using a constructor here perhaps - see the SWIPE FTPSClient

        boolean ftpsSuccess = false;

        log.debug( correlationID + " FTPS GET initiated" );                   

        try
        {  
            if( setSslConfig( ) == false )
            {
                return false;
            }

            String fileTermName = new File( remoteFile ).getName();                                                          

            /*
             * Configuration Section - source this from the FTPConfig object ** that needs to be made more flexible
             */

            log.info( correlationID + " FTPS Server Address : " + hostname );
            log.info( correlationID + " FTPS Port Number    : " + PORT );
            log.info( correlationID + " FTPS remoteFile     : " + remoteFile );
            log.info( correlationID + " FTPS localFile      : " + localFile );
            log.info( correlationID + " FTPS remoteTermName : " + fileTermName ); 


            // try to connect
            ftps.connect( hostname, PORT );                                                      

            /*
             *  Once connected we need to login to the server via username/password
             */
            if ( !ftps.login( user, pass ) )
            {
                log.error( correlationID + " FTPS Unable to log into : " + hostname + " with supplied credentials" );

                return false;
            }

            int reply = ftps.getReplyCode();                      

            log.debug( correlationID + " FTPS Login Reply Code : " + reply );

            /*
             * http://forus.com/csm/ftps/ trying to SSL the crap out of this client
             */
            ftps.execPBSZ( 0 );
            ftps.execPROT( "P" );

            /*
             *  FTPReply stores a set of constants for FTP reply codes.
             */
            if ( !FTPReply.isPositiveCompletion( reply ) )
            {                
                log.error( correlationID + " FTPS Not a positive reply from " + hostname + " : " + reply);                

                return false;
            }

            log.info( correlationID + " FTPS Logged into to Remote Host : " + hostname );

            ftps.enterLocalPassiveMode();

            log.debug( correlationID + " FTPS Entered Local Passive Mode");


            int xferMode = FTP.BINARY_FILE_TYPE;

            if( mode.equalsIgnoreCase("b") ) {

                xferMode = FTP.BINARY_FILE_TYPE;

            }
            else if( mode.equalsIgnoreCase("t")) {

                xferMode = FTP.ASCII_FILE_TYPE;
            }
            else
            {
                xferMode = FTP.BINARY_FILE_TYPE;
            }

            log.debug(correlationID + " Mode for FTP : " + xferMode );


            // Set the buffer size to cope with larger files
            ftps.setBufferSize( 1024 * 1024 );


            log.debug( correlationID + " FTPS Set Buffer Size to  : " + ftps.getBufferSize( ) );
            log.debug( correlationID + " FTPS Remote system type  : " + ftps.getSystemType( ) );
            log.debug( correlationID + " FTPS Remote directory is : " + ftps.printWorkingDirectory( ) );

            log.info( correlationID  + " FTPS Remote file is " + remoteFile );            

            // Get output stream - This is where the file will be downloaded to

            if(direction.equalsIgnoreCase( "get" ) )
            {
                OutputStream downloadedFile = new FileOutputStream( localFile );

                /*
                 * TODO Check that the remote file exists before download
                 * 
                 * TODO Also a check that a local copy of the file does not already exist. 
                 */                     


                /*
                 *  GET the file from the remote system ( remoteFile, downloadedFile )
                 */
                ftpsSuccess = ftps.retrieveFile( remoteFile, downloadedFile );


                /*
                 *  close output stream
                 */
                downloadedFile.close();


                log.info( correlationID + " FTPS Retrieval Complete for " + remoteFile );
            }
            else if( direction.equalsIgnoreCase("put"))
            {
                FileInputStream file = new FileInputStream( localFile );
                ftpsSuccess = ftps.storeFile(remoteFile, file );

                file.close();
            }


            if( ftpsSuccess == false )
            {
                log.error( correlationID + "FTPS Success is False" );                               
            }
        }
        catch( SSLException e )
        {
            log.error("SSLException caught ", e );

            /*
             * Do we drop down to basic FTP here and try the transfer again? 
             */
            //return JtmFTPClient.get(correlationID, hostname, user, pass, remoteFile, localFile, mode);

            ftpsSuccess = false;            

        }
        catch ( FileNotFoundException e )
        {
            log.error( correlationID + " FileNotFoundException caught ", e );


            ftpsSuccess = false;
        }       
        catch ( GeneralSecurityException e )
        {
            log.error( correlationID + " GeneralSecurityException caught ", e );

            ftpsSuccess = false;
        }
        catch ( IOException e )
        {
            log.error( correlationID + " IOException caught ", e );


            ftpsSuccess = false;
        }   
        finally
        {  
            try
            {
                ftps.logout();
                ftps.disconnect();

            }
            catch ( IOException e )
            {
                log.error(correlationID + " IOException caught closing", e );
            }
        }

        log.debug(correlationID + " FTPS result : " + ftpsSuccess );

        return ftpsSuccess;
    }




    /**
     * A Method that configures the SSL requirements when FTP'ing files to/from secure instances of UTM
     * 
     * @param  isSSL
     * @return boolean
     * @throws IOException
     * @throws GeneralSecurityException
     */
    private static boolean setSslConfig( ) throws IOException, GeneralSecurityException
    {

        String trustStorePath = JtmConstants.TRUST_STORE_PATH;
        String trustStorePass = JtmConstants.TRUST_STORE_PASS;

        String keyStorePath = JtmConstants.KEY_STORE_PATH;
        String keyStorePass = JtmConstants.KEY_STORE_PASS;

        String keyPass = JtmConstants.KEY_PASS;
        String keyAlias = JtmConstants.KEY_ALIAS;       

        boolean isSslRequired = true;


        if ( isSslRequired )
        {
            if ( trustStorePath != null && trustStorePass != null )
            {
                KeyStore ks = KeyStore.getInstance( "JKS" );
                ks.load( new FileInputStream( trustStorePath ), trustStorePass.toCharArray( ) );

                ftps.setTrustManager( TrustManagerUtils.getDefaultTrustManager( ks ) );
            }
            else
            {
                log.error( "Error setting up Trust Store" );
                log.error( "Trust Store path or trust store passord have not been supplied." );

                return false;
            }
        }
        else
        {
            ftps.setTrustManager( TrustManagerUtils.getAcceptAllTrustManager( ) );
        }


        if ( keyStorePath != null && keyStorePass != null )
        {

            File keyFile = new File( keyStorePath );

            KeyManager keyManager;

            if ( keyAlias != null )
            {
                if ( keyPass != null )
                {
                    keyManager = org.apache.commons.net.util.KeyManagerUtils
                            .createClientKeyManager( "JKS", keyFile,
                                    keyStorePass,
                                    keyAlias,
                                    keyPass );
                }
                else
                {
                    keyManager = org.apache.commons.net.util.KeyManagerUtils
                            .createClientKeyManager( keyFile, keyStorePass, keyAlias );                                            
                }
            }
            else
            {
                keyManager = org.apache.commons.net.util.KeyManagerUtils
                        .createClientKeyManager( keyFile, keyStorePass );
            }

            ftps.setKeyManager( keyManager );

            return true;
        }
        else
        {
            log.error( "Error setting up Key Store" );
            log.error( "Key Store path or key store passord have not been supplied." );

            return false;
        }
    }                    
}

【问题讨论】:

  • 是隐式还是显式 TLS/SSL?就自动检测而言,这是一个巨大的差异
  • 隐式总是错误的 - 如果这有帮助吗?
  • 是的 - 我们的想法完全正确!!我们被困住了,因为由于成本/测试和各种原因,许多系统/服务还没有升级到 FTPS。理想情况下,每个人都会升级到 FTPS,这个问题根本不存在。

标签: java apache ssl ftp


【解决方案1】:

不要这样做。你只是假装安全。

如果您允许自动降级到不安全的连接,您可能会被黑客入侵。至少有两种明显的方式:

  • 攻击者只需要将 DNS 查找重定向到恶意的不安全服务器,而您永远不知道自己只是丢失了凭据。

  • 攻击者可以模拟AUTH 命令的失败(发生在连接得到保护之前)。您会自动降级到不安全的连接,再次以纯文本形式向攻击者显示您的凭据。


无论如何,只需尝试FTPSClient,如果使用SSLException 失败,请使用FTPClient

如果您确实需要一个很好的解决方案(无需重新连接)来实现显式 TLS/SSL,请参阅FTPSClient._connectAction_() 是如何实现的。您可以重新实现它以调用基础FTPClient._connectAction_() 并尝试sendCommand(CMD_AUTH, auth),而无需抛出。当然不要调用sslNegotiation(),如果AUTH失败了。

【讨论】:

  • 感谢 Martin,我完全同意并理解所有这些的含义。恐怕我们的手被绑住了。我会在今天晚些时候尝试你的建议,看看我如何处理它们 - 谢谢。
猜你喜欢
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
  • 1970-01-01
  • 2018-07-07
  • 2011-11-13
  • 1970-01-01
  • 1970-01-01
  • 2015-07-20
相关资源
最近更新 更多