Explicit FTPs with User Certificate

Jan 30, 2013 at 7:41 AM

Hello

First of all, thanks for your amazing, usefull job with this library !

I'm trying to connect to a FTP Server using SSL/TLS Explicit with a certificate

Here's my code :

X509Certificate cert_grt = new X509Certificate(settings["PathCert"]);

using (FtpClient conn = new FtpClient())
{
 conn.Host = settings["ftpsHost"];
 conn.Port = Convert.ToInt32(settings["ftpsPort"]);
 conn.Credentials = new NetworkCredential(settings["ftpsUser"], settings["ftpsPass"]);
 conn.EncryptionMode = FtpEncryptionMode.Explicit;
 conn.ClientCertificates.Add(cert_grt);
 conn.Connect();
}

The text of the throw Exception is (translated from french..) : Error on a SSPI call, inspect inner exception

Strack Trace

   à System.Net.FtpClient.FtpSocketStream.ActivateEncryption(String targethost, X509CertificateCollection clientCerts)
   à System.Net.FtpClient.FtpClient.Connect()
   à ConnecteurFTPs.Program.Main(String[] args) dans XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX\Program.cs:ligne 30
   à System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   à System.AppDomain.nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   à System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   à Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   à System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   à System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   à System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   à System.Threading.ThreadHelper.ThreadStart()

Any idea will be greatly appreciated !

Thanks in advance

Jan 30, 2013 at 10:25 AM

it connects and then an exception is thrown

AUTH TLS

234 AUTH TLS successful

'ConnecteurFTPs.vshost.exe' (Managé (v4.0.30319)) : 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.resources\v4.0_4.0.0.0_fr_b77a5c561934e089\System.resources.dll' chargé

Une exception de première chance de type 'System.Security.Authentication.AuthenticationException' s'est produite dans System.Net.FtpClient.dll

Coordinator
Jan 30, 2013 at 10:59 AM

I will try to dig into the problem today. In the mean time, if you can provide any other information such as a full transaction log it will help if for no other reason I can get an idea where the problem is occurring. It looks like since it read the AUTH TLS response that encryption was successfully setup so the problem may be else where.

Coordinator
Jan 30, 2013 at 1:17 PM

What FTP server software are you using this feature with?

Jan 30, 2013 at 2:29 PM

No idea of the FTP Server

I did manage to connect to the FTP Server using libcurl ! (i previously extract key and ca from cert file)

Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);

Easy easy = new Easy();
easy.SetOpt(CURLoption.CURLOPT_URL, url);

easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYPEER, false);
easy.SetOpt(CURLoption.CURLOPT_SSL_VERIFYHOST, false);
easy.SetOpt(CURLoption.CURLOPT_FTP_SSL, CURLftpSSL.CURLFTPSSL_TRY);
easy.SetOpt(CURLoption.CURLOPT_FTP_USE_EPSV, false);

easy.SetOpt(CURLoption.CURLOPT_SSLVERSION, 3);
easy.SetOpt(CURLoption.CURLOPT_SSLKEY, settings["PathCertKey"]);
easy.SetOpt(CURLoption.CURLOPT_SSLKEYPASSWD, "KEYPASSWD");
easy.SetOpt(CURLoption.CURLOPT_SSLCERT, settings["PathCertCA"]);

// For debugging will print headers to console.
Easy.HeaderFunction hf = new Easy.HeaderFunction(OnHeaderData);
easy.SetOpt(CURLoption.CURLOPT_HEADERFUNCTION, hf);

// For debugging will print received data to console.
Easy.DebugFunction df = new Easy.DebugFunction(OnDebug);
easy.SetOpt(CURLoption.CURLOPT_DEBUGFUNCTION, df);
easy.SetOpt(CURLoption.CURLOPT_VERBOSE, true);

// List directory only
easy.SetOpt(CURLoption.CURLOPT_FTPLISTONLY, true);

CURLcode code = easy.Perform();
if (code != CURLcode.CURLE_OK)
{
 Console.WriteLine("Request failed!");
}

easy.Cleanup();

 

 

Coordinator
Jan 30, 2013 at 2:32 PM

I'm in the process of setting up IIS FTP with client certificate authentication. Once I get the server configured I can work on testing. I've never used (or tested) this feature personally, I added client certificate support at the request of another user and they only recently verified that it worked for them.

Jan 30, 2013 at 2:47 PM

More info on FTP Server

COMMAND:> [30/01/2013 15:58:02] PWD

[30/01/2013 15:58:02] 257 "/" is the current directory

STATUS:> [30/01/2013 15:58:02] Home directory: /

COMMAND:> [30/01/2013 15:58:02] FEAT

[30/01/2013 15:58:02] Informational Message Only:

211-Features:

MDTM

MFMT

TVFS

UTF8

AUTH TLS

MFF modify;UNIX.group;UNIX.mode;

MLST modify*;perm*;size*;type*;unique*;UNIX.group*;UNIX.mode*;UNIX.owner*;

PBSZ

PROT

SITE MKDIR

SITE RMDIR

SITE UTIME

SITE SYMLINK

REST STREAM

LANG en-US*;fr-FR

SIZE

211 End

 

Coordinator
Jan 30, 2013 at 7:44 PM

I've been working on this and I can't reproduce the problem using IIS 7 with basic (non active directory or 1 to 1 mapping) client certificate requirement. When I provide a valid private key as my client certificate my IIS 7 test environment accepts the certificate and it's business as usual. If I do not provide a valid key I still don't get any exception, it just tells me the client certificate was invalid and aborts the login process. I'm not sure what's happening to cause the above exception.

Coordinator
Jan 30, 2013 at 8:00 PM

Also, use X509Certificate2. I've been unable to get X509Certificate to work and from my reading it's because it's an incomplete implementation.

Jan 31, 2013 at 7:35 AM
X509Certificate2 cert_grt = new X509Certificate2(settings["PathCert"]);
conn.Host = settings["ftpsHost"];
conn.Port = Convert.ToInt32(settings["ftpsPort"]);
conn.Credentials = new NetworkCredential(settings["ftpsUser"], settings["ftpsPass"]);
conn.EncryptionMode = FtpEncryptionMode.Explicit;
conn.DataConnectionType = FtpDataConnectionType.PASV;
conn.DataConnectionEncryption = true;
conn.EnableThreadSafeDataConnections = false;
conn.ClientCertificates.Add(cert_grt);
conn.ValidateCertificate += new FtpSslValidation(OnValidateCertificate);
conn.Connect();

static void OnValidateCertificate(FtpClient control, FtpSslValidationEventArgs e)
    {
        e.Accept = true;
    }
I put a breakpoint in the OnValidateCertificate method, but it nevers gets into it ! is it normal ?
Coordinator
Jan 31, 2013 at 1:05 PM
Edited Jan 31, 2013 at 1:05 PM
It's normal if it's throwing an exception before TLS/SSL can be negotiated which seems to be the case here. What is the contents of the InnerException?

try {

}
catch(Exception ex) {
if(ex.InnerException != null)
     Console.WriteLine(ex.InnerException.ToString());
}
Jan 31, 2013 at 2:43 PM
In french
Message = "Le message reçu était inattendu ou formaté de façon incorrecte"
Translated
Unexpected message received or incorrectly formated
Coordinator
Feb 1, 2013 at 12:30 AM
Check this Stack Overflow thread which suggests the certificate is not in the correct format:

http://stackoverflow.com/questions/13697230/ssl-stream-failed-to-authenticate-as-client-in-apns-sharp
Feb 1, 2013 at 1:10 PM
Thanks for the research

I'll try it and give you some news, i was using a pem certificate...
Feb 1, 2013 at 2:15 PM
It WORKED !

THANKS SO MUCH !

your library is more elegant than CURL...
I prefere to use yours !
Coordinator
Feb 1, 2013 at 2:15 PM
No a problem
Coordinator
Feb 1, 2013 at 2:23 PM
Added this information to the documentation page.