Certificate validation/installation for FTPS (SSL)

2019-08-13 07:28发布

问题:

I am using FileZilla as the server and a DNS service, so that I wouldn't have to use my local machine IP (but I've tried the following methods on both).

After trying System.Net.FtpWebRequest to work, I've read around (including a few posts on SO) and found out that the SSL support is not very adequate with that library. It was working with regular FTP, but when I tried forcing SSL, I was getting a certificate validation error saying: The remote certificate is invalid according to the validation procedure.

So, I've done some searching around and found Alex FTPS Client library. Here's the code I wrote up:

class FTPSWorker
    {
        public static void UploadFile(string sourceFile, string targetFile, string ftpIP, string ftpUser, string ftpPass)
        {
            try
            {
                using (FTPSClient client = new FTPSClient())
                {
                    client.Connect(ftpIP, new NetworkCredential(ftpUser, ftpPass),
                                   ESSLSupportMode.CredentialsRequired | ESSLSupportMode.DataChannelRequested);
                    client.SetTransferMode(ETransferMode.Binary);
                    client.PutFile(sourceFile, targetFile);
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

Unfortunately, I was getting the same exact certificate error. I can, however, access the FTP server perfectly fine using FileZilla client. So, I figured there would have to be a certificate issue.

I should note that my server was showing the following log entries:

Welcome Message
AUTH TLS
234 Using authentication type TLS
SSL connection established
disconnected

While the client (C# WPF application) was getting this error:

The remote certificate is invalid according to the validation procedure.

This is absolutely exact same error if I use the .NET library and MSDN code.

I've done more research and found solutions similar to these:

The remote certificate is invalid according to the validation procedure

"The remote certificate is invalid according to the validation procedure." using Gmail SMTP server

But they just seem like risky hacks... And while they do work, is there a way to have certification information to appear and maybe have user validate it/install it besides the basic Yes/No that it's currently using?

My code right now (I ditched Alex's library and went back to default .NET):

ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(FTPWorker.ValidateServerCertificate);

public class FTPWorker
{
    public static void UploadFile(string sourceFile, string targetFile, string ftpIP, string ftpUser, string ftpPass)
    {
        try
        {
            string filename = "ftp://" + ftpIP + "/test/" + targetFile;
            FtpWebRequest ftpReq = (FtpWebRequest)WebRequest.Create(filename);
            ftpReq.Method = WebRequestMethods.Ftp.UploadFile;
            ftpReq.Credentials = new NetworkCredential(ftpUser, ftpPass);
            ftpReq.UsePassive = true;
            ftpReq.EnableSsl = true;
            ftpReq.UseBinary = true;
            ftpReq.KeepAlive = false;

            byte[] b = File.ReadAllBytes(sourceFile);

            ftpReq.ContentLength = b.Length;

            using (Stream s = ftpReq.GetRequestStream())
            {
                s.Write(b, 0, b.Length);
            }

            FtpWebResponse ftpResp = (FtpWebResponse)ftpReq.GetResponse();

            if (ftpResp != null)
            {
                MessageBox.Show(ftpResp.StatusDescription);
            }
        }
        catch (Exception e)
        {
            MessageBox.Show(e.Message);
        }
    }

    public static bool ValidateServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        if (sslPolicyErrors == SslPolicyErrors.None)
            return true;
        else
        {
            if (System.Windows.Forms.MessageBox.Show("The server certificate is not valid.\nAccept?", 
                   "Certificate Validation", System.Windows.Forms.MessageBoxButtons.YesNo,
                   System.Windows.Forms.MessageBoxIcon.Question) == System.Windows.Forms.DialogResult.Yes)
                return true;
            else
                return false;
        }
    }
}

回答1:

So, for anyone that had the same issue, I ended up just giving the user a warning regarding the certificate and an option to accept or deny based on the link I provided in my original post. In order for a certificate to be validated, it has to be real and not a locally created one. So, that's the only workaround there is for now.



回答2:

The Alex ftps will do the same certificate validation if you specify it to. In your client.connect add the remotecertificatevalidationcallback to accept the certificate

client.Connect(ftpIP, new NetworkCredential(ftpUser, ftpPass),
                               ESSLSupportMode.CredentialsRequired | ESSLSupportMode.DataChannelRequested, 
                           new RemoteCertificateValidationCallback(ValidateTestServerCertificate));

Then below.

private static bool ValidateTestServerCertificate(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
    {
        // Accept any certificate
        return true;
    }

I wanted to use the default .net. but I'm stuck connecting to a server that's using implicit. :(