Apache FTP客户端决定使用SSL或原始FTP

时间:2016-09-16 09:47:20

标签: java apache ssl ftp

在我工作的地方,我们拥有庞大的服务器区域,可以使用使用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;
        }
    }                    
}

1 个答案:

答案 0 :(得分:3)

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

如果您允许自动降级到不安全的连接,有很多方法可以被黑客入侵。至少有两个明显的方法:

  • 攻击者只需将DNS查找重定向到恶意的不安全服务器,您就不会知道您刚刚丢失了凭据。

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

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

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