Friday, July 18, 2014

ColdFusion sFTP and ZLIB

A client last week needed an automated sFTP push. I was able to reach the host, but was receiving the error: "Algorithm negotiation fail". Since Filezilla could connect fine, my first thought was that they were using a cipher that was too advanced for ColdFusion 9 to handle.


To get more information on the connection, I looked to Filezilla's debugging tools. Under [Settings], [Debug] I bumped the logging level to [2 - Info]


In the connection scroll, I saw something interesting. The ciphers were ordinary: SHA1 and Blowfish-128, but zlib stood out. Zlib compresses data at the transmission level, similar to gzip but patently different. ColdFusion doesn't handle gzip natively (though there are clever workarounds), so I suspected that CFFTP was having a problem with zlib.

I searched the internet for Java libraries that could sFTP with zlib enabled and the overwhelming majority of links pointed me to JCraft JSch. To my surprise, I discovered that ColdFusion 9 implements a modified version of JSch 1.41 library (C:\ColdFusion9\lib\jsch-0.1.41m.jar). The zlib functionality is not part of the core JSch, but is part of jzlib.jar (also available free from JCraft). I added jzlib.jar to the classpath and it worked like a charm. Here is my proof of concept code:

<cfscript>
FTPSERVER = "someserver.somewhere.com"; 
FTPPORT = "22"; 
FTPUSER = "someuser"; 
FTPPW = "somepassword"; 

jschObj = createobject('java',"com.jcraft.jsch.JSch"); 
jschSession = jschObj.getSession(FTPUSER, FTPSERVER); 
jschConfig = createObject("java","java.util.Properties"); 
jschConfig.put("StrictHostKeyChecking","no"); 
jschConfig.put("compression.s2c", "zlib,none"); //server to client
jschConfig.put("compression.c2s", "zlib,none"); //client to server
jschSession.setConfig(jschConfig); 
jschSession.setPort(FTPPORT); 
jschSession.setPassword(FTPPW); 
jschSession.connect(); 
jschChannel = jschSession.openChannel("sftp"); 
jschChannel.connect(); 
if (jschSession.isConnected()) { 
 WriteOutput("CONNECTED!");
} 
jschChannel.cd("/Test"); 
FileInputStream = createobject("java", "java.io.FileInputStream").init(expandPath("payload_test.txt"));
jschChannel.put(FileInputStream, "payload_test.txt"); 
WriteDump(jschChannel); 
jschChannel.disconnect(); 
jschSession.disconnect();
</cfscript>

The two commented lines enable zlib for upstream and downstream transmissions. This resolved our connection issues.