8

पर्यावरण: मैं स्प्रिंग इंटीग्रेशन 4.1.6 का उपयोग कर 64-बिट विंडोज 7 पर सन जावा जेडीके 1.8.0_60 का उपयोग कर रहा हूं (जो आंतरिक रूप से एफटीपीएस एक्सेस के लिए अपाचे कॉमन्स नेट 3.3 का उपयोग करने लगता है)।उसी टीएलएस सत्र का उपयोग कर डेटा कनेक्शन के साथ एफटीपीएस सर्वर से कैसे कनेक्ट करें?

मैं अपने आवेदन के साथ हमारे क्लाइंट के एफटीपीएस सर्वर से स्वचालित डाउनलोड को एकीकृत करने का प्रयास कर रहा हूं। मैंने मुद्दों के बिना अन्य ग्राहकों के लिए किसी भी परेशानी के बिना स्प्रिंग इंटीग्रेशन का उपयोग करके एसएफटीपी सर्वरों के साथ सफलतापूर्वक ऐसा किया है, लेकिन यह पहली बार है जब क्लाइंट को हमें एफटीपीएस का उपयोग करने की आवश्यकता होती है, और इसे कनेक्ट करने के लिए बहुत परेशानी होती है। मेरे असली एप्लिकेशन में, मैं एक्सएमएल बीन्स का उपयोग करके वसंत एकीकरण को कॉन्फ़िगर कर रहा हूं, यह समझने की कोशिश करने के लिए कि मैं क्या काम नहीं कर रहा हूं, मैं निम्नलिखित परीक्षण कोड का उपयोग कर रहा हूं (हालांकि मैं यहां वास्तविक होस्ट/उपयोगकर्ता नाम/पासवर्ड को अनामित कर रहा हूं):

final DefaultFtpsSessionFactory sessionFactory = new DefaultFtpsSessionFactory(); 
sessionFactory.setHost("XXXXXXXXX"); 
sessionFactory.setPort(990); 
sessionFactory.setUsername("XXXXXXX"); 
sessionFactory.setPassword("XXXXXXX"); 
sessionFactory.setClientMode(2); 
sessionFactory.setFileType(2); 
sessionFactory.setUseClientMode(true); 
sessionFactory.setImplicit(true); 
sessionFactory.setTrustManager(TrustManagerUtils.getAcceptAllTrustManager()); 
sessionFactory.setProt("P"); 
sessionFactory.setProtocol("TLSv1.2"); 
sessionFactory.setProtocols(new String[]{"TLSv1.2"}); 
sessionFactory.setSessionCreation(true); 
sessionFactory.setCipherSuites(new String[]{"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256"}); 

final FtpSession session = sessionFactory.getSession(); 
//try { 
    final FTPFile[] ftpFiles = session.list("/"); 
    logger.debug("FtpFiles: {}", (Object[]) ftpFiles); 
//} catch (Exception ignored) {} 
session.close(); 

मैं सभी टीएलएस डीबगिंग जानकारी मुद्रित करने के लिए -Djavax.net.debug=all के साथ इस कोड को चला रहा हूं।

FTPS सर्वर से मुख्य "नियंत्रण" कनेक्शन ठीक काम करता है, लेकिन जब यह सूची (या किसी अन्य डेटा कनेक्शन मैं कोशिश की है) के लिए डेटा कनेक्शन खोलने की कोशिश करता है, मैं एक javax.net.ssl.SSLHandshakeException: Remote host closed connection during handshake, java.io.EOFException: SSL peer shut down incorrectly की वजह से मिलता है। अगर मैं uncomment निगलने-अपवाद session.list आदेश के आसपास ब्लॉक को पकड़ने, तो मैं (javax.net.debug उत्पादन हालांकि) देख सकते हैं कि सर्वर डेटा कनेक्शन एसएसएल हाथ मिलाना अस्वीकृत करने के बाद निम्न संदेश भेजा:

main, READ: TLSv1.2 Application Data, length = 129 
Padded plaintext after DECRYPTION: len = 105 
0000: 34 35 30 20 54 4C 53 20 73 65 73 73 69 6F 6E 20 450 TLS session 
0010: 6F 66 20 64 61 74 61 20 63 6F 6E 6E 65 63 74 69 of data connecti 
0020: 6F 6E 20 68 61 73 20 6E 6F 74 20 72 65 73 75 6D on has not resum 
0030: 65 64 20 6F 72 20 74 68 65 20 73 65 73 73 69 6F ed or the sessio 
0040: 6E 20 64 6F 65 73 20 6E 6F 74 20 6D 61 74 63 68 n does not match 
0050: 20 74 68 65 20 63 6F 6E 74 72 6F 6C 20 63 6F 6E the control con 
0060: 6E 65 63 74 69 6F 6E 0D 0A      nection.. 

ऐसा प्रतीत होता है (और यह मेरा पहला समय एफटीपीएस से निपट रहा है, हालांकि मैंने पहले सादे एफ़टीपी के साथ निपटाया है) यह है कि सर्वर नियंत्रण और डेटा कनेक्शन दोनों पर प्रमाणीकरण और एन्क्रिप्शन सुनिश्चित करता है कि "सामान्य" नियंत्रण कनेक्शन और प्रमाणीकरण स्थापित करने के लिए टीएलएस कनेक्शन वहां होता है, प्रत्येक डेटा कनेक्शन के लिए क्लाइंट को उसी टीएलएस सत्र से कनेक्ट करने की आवश्यकता होती है। यह मुझे समझ में आता है कि यह कैसे काम किया जाना चाहिए, लेकिन अपाचे कॉमन्स नेट एफटीपीएस कार्यान्वयन ऐसा नहीं लगता है। ऐसा लगता है कि एक नया टीएलएस सत्र स्थापित करने की कोशिश कर रहा है, और इसलिए सर्वर प्रयास को खारिज कर रहा है।

this question about resuming SSL sessions in JSSE के आधार पर, ऐसा प्रतीत होता है कि जावा प्रत्येक होस्ट/पोस्ट संयोजन के लिए एक अलग सत्र मानता है या आवश्यक है। मेरी परिकल्पना यह है कि चूंकि एफटीपीएस डेटा कनेक्शन नियंत्रण कनेक्शन की तुलना में एक अलग बंदरगाह पर है, इसलिए यह मौजूदा सत्र नहीं ढूंढ रहा है और एक नया स्थापित करने की कोशिश कर रहा है, इसलिए कनेक्शन विफल हो जाता है।

  1. सर्वर नियंत्रण पोर्ट पर के रूप में डेटा बंदरगाह पर एक ही टीएलएस सत्र की आवश्यकता होती है में FTPS मानक का पालन नहीं कर रहा है:

    मैं तीन मुख्य संभावनाओं को देखने। मैं FileZilla 3.13.1 का उपयोग कर सर्वर ठीक से कनेक्ट कर सकता हूं (उसी होस्ट/उपयोगकर्ता/पासवर्ड का उपयोग कर रहा हूं क्योंकि मैं अपने कोड में उपयोग करने की कोशिश कर रहा हूं)। सर्वर लॉगिन पर खुद को "फाइलज़िला सर्वर 0.9.53 बीटा" के रूप में पहचानता है, इसलिए शायद यह चीजों को करने का कुछ प्रकार का मालिकाना फाइलज़िला तरीका है, और एक ही टीएलएस सत्र का उपयोग करने के लिए जावा को मनाने के लिए मुझे कुछ अजीब बात है।

  2. अपाचे कॉमन्स नेट क्लाइंट वास्तव में एफटीपीएस मानक का पालन नहीं करता है, और केवल कुछ सबसेट की अनुमति देता है जो डेटा कनेक्शन को सुरक्षित करने की अनुमति नहीं देता है। यह अजीब लगेगा, क्योंकि यह जावा के भीतर से एफटीपीएस से कनेक्ट करने का मानक तरीका प्रतीत होता है।
  3. मैं पूरी तरह से कुछ खो रहा हूं और इसे गलत तरीके से याद कर रहा हूं।

मैं इस तरह के एफटीपीएस सर्वर से कनेक्ट करने के तरीके के बारे में किसी भी दिशा की सराहना करता हूं। धन्यवाद।

+0

में झूठी करने के लिए EXExtendedMasterSecret का उपयोग करें विवरण के लिए कृपया इस लिंक को देखें https://stackoverflow.com/questions/46631315/when-using-java-apache-ftpclient-for-ftp-tls-getting-remote-host-closed -कनेक्ट/4861677 9 # 48616779 –

उत्तर

13

वास्तव में कुछ एफ़टीपी (एस) सर्वरों को यह आवश्यक है कि डेटा कनेक्शन के लिए टीएलएस/एसएसएल सत्र का पुन: उपयोग किया जाए। यह एक सुरक्षा उपाय है जिसके द्वारा सर्वर सत्यापित कर सकता है कि डेटा कनेक्शन का उपयोग उसी क्लाइंट द्वारा नियंत्रण कनेक्शन के रूप में किया जाता है।

आम FTP सर्वर के लिए कुछ संदर्भों:


क्या आप मदद कर सकते हैं कार्यान्वयन के साथ था टी Cyberduck एफ़टीपी (एस) ग्राहक समर्थन TLS/SSL सत्र पुन: उपयोग करता है और यह अपाचे कॉमन्स नेट लाइब्रेरी का उपयोग करता:

  • https://trac.cyberduck.io/ticket/5087 - डेटा कनेक्शन

  • पर पुन: उपयोग सत्र कुंजी अपने FTPClient.java कोड देखें (कॉमन्स नेट फैली FTPSClient), विशेष रूप से अपने override of _prepareDataSocket_ method:

    @Override 
    protected void _prepareDataSocket_(final Socket socket) throws IOException { 
        if(preferences.getBoolean("ftp.tls.session.requirereuse")) { 
         if(socket instanceof SSLSocket) { 
          // Control socket is SSL 
          final SSLSession session = ((SSLSocket) _socket_).getSession(); 
          if(session.isValid()) { 
           final SSLSessionContext context = session.getSessionContext(); 
           context.setSessionCacheSize(preferences.getInteger("ftp.ssl.session.cache.size")); 
           try { 
            final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache"); 
            sessionHostPortCache.setAccessible(true); 
            final Object cache = sessionHostPortCache.get(context); 
            final Method method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class); 
            method.setAccessible(true); 
            method.invoke(cache, String.format("%s:%s", socket.getInetAddress().getHostName(), 
              String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT), session); 
            method.invoke(cache, String.format("%s:%s", socket.getInetAddress().getHostAddress(), 
              String.valueOf(socket.getPort())).toLowerCase(Locale.ROOT), session); 
           } 
           catch(NoSuchFieldException e) { 
            // Not running in expected JRE 
            log.warn("No field sessionHostPortCache in SSLSessionContext", e); 
           } 
           catch(Exception e) { 
            // Not running in expected JRE 
            log.warn(e.getMessage()); 
           } 
          } 
          else { 
           log.warn(String.format("SSL session %s for socket %s is not rejoinable", session, socket)); 
          } 
         } 
        } 
    } 
    
  • ऐसा लगता है कि _prepareDataSocket_ विधि कॉमन्स नेट FTPSClient को जोड़ा गया है विशेष रूप से TLS/SSL सत्र पुन: उपयोग के कार्यान्वयन की अनुमति के लिए:
    https://issues.apache.org/jira/browse/NET-426

    पुनः उपयोग के लिए एक देशी समर्थन अभी भी लंबित है:
    https://issues.apache.org/jira/browse/NET-408

  • सत्र पुन: उपयोग समर्थन के साथ आपको अपने कस्टम FTPSClient कार्यान्वयन को वापस करने के लिए आपको वसंत एकीकरण DefaultFtpsSessionFactory.createClientInstance() को ओवरराइड करने की आवश्यकता होगी।

+0

आपको बहुत बहुत धन्यवाद। मैं अपाचे कॉमन्स नेट जेरा के माध्यम से देख रहा था, लेकिन जाहिर है कि आपने जितना दूर किया था उतना खोद नहीं था। और एक कामकाजी परियोजना से नमूना कोड बहुत अच्छा काम कर रहा है –

+0

आपका स्वागत है। मुझे वास्तव में [वही समस्या] थी (http://stackoverflow.com/q/7786352/850848) अतीत में (बस सी ++/ओपनएसएसएल में, मैं जावा नहीं करता), इसलिए मुझे पता था कि Google के लिए क्या करना है। –

+0

पता नहीं क्यों, लेकिन यह openjdk संस्करण "1.8.0_151" के साथ काम कर रहा है और ओपनजेडके संस्करण "1.8.0_161" के साथ काम नहीं कर रहा है –

2

मेरे लिए मार्टिन Prikryl के सुझाव काम करने के लिए मैं न केवल socket.getInetAddress().getHostName() के तहत, लेकिन यह भी socket.getInetAddress().getHostAddress() तहत कुंजी संग्रहीत करने के लिए किया था। (समाधान here से चोरी हो।)

0

आप इस SSLSessionReuseFTPSClient वर्ग का उपयोग कर सकते हैं:

import java.io.IOException; 
import java.lang.reflect.Field; 
import java.lang.reflect.Method; 
import java.net.Socket; 
import java.util.Locale; 

import javax.net.ssl.SSLSession; 
import javax.net.ssl.SSLSessionContext; 
import javax.net.ssl.SSLSocket; 

import org.apache.commons.net.ftp.FTPSClient; 

public class SSLSessionReuseFTPSClient extends FTPSClient { 

    // adapted from: 
    // https://trac.cyberduck.io/browser/trunk/ftp/src/main/java/ch/cyberduck/core/ftp/FTPClient.java 
    @Override 
    protected void _prepareDataSocket_(final Socket socket) throws IOException { 
     if (socket instanceof SSLSocket) { 
      // Control socket is SSL 
      final SSLSession session = ((SSLSocket) _socket_).getSession(); 
      if (session.isValid()) { 
       final SSLSessionContext context = session.getSessionContext(); 
       try { 
        final Field sessionHostPortCache = context.getClass().getDeclaredField("sessionHostPortCache"); 
        sessionHostPortCache.setAccessible(true); 
        final Object cache = sessionHostPortCache.get(context); 
        final Method method = cache.getClass().getDeclaredMethod("put", Object.class, Object.class); 
        method.setAccessible(true); 
        method.invoke(cache, String 
          .format("%s:%s", socket.getInetAddress().getHostName(), String.valueOf(socket.getPort())) 
          .toLowerCase(Locale.ROOT), session); 
        method.invoke(cache, String 
          .format("%s:%s", socket.getInetAddress().getHostAddress(), String.valueOf(socket.getPort())) 
          .toLowerCase(Locale.ROOT), session); 
       } catch (NoSuchFieldException e) { 
        throw new IOException(e); 
       } catch (Exception e) { 
        throw new IOException(e); 
       } 
      } else { 
       throw new IOException("Invalid SSL Session"); 
      } 
     } 
    } 
} 

और OpenJDK 1.8.0_161 के साथ:

हम सेट करना होगा:

System.setProperty("jdk.tls.useExtendedMasterSecret", "false"); 

के अनुसार http://www.oracle.com/technetwork/java/javase/8u161-relnotes-4021379.html

जोड़ा गया टीएलएस सत्र हैश और विस्तारित मास्टर गुप्त एक्सटेंशन समर्थन

संगतता समस्याओं के मामले में, कोई एप्लिकेशन सिस्टम प्रॉपर्टी jdk.tls को सेट करके इस एक्सटेंशन की बातचीत को अक्षम कर सकता है।JDK

संबंधित मुद्दे