5

I am trying to download a file from my FTP Server but from my production server (from my laptop I am able to download the file) I get the error "A call to SSPI failed" and the inner exception "The message received was unexpected or badly formatted". This my code:

var ftpServer = "ftp://xxx.yyy.zz/";
var ftpUsername = "aaaaa";
var ftpPassword = "bbbbb";
var downloadedFilePath = "c:\\temp\\";
var downloadedFileName = "file.xls";                      
FtpWebRequest request = null;

ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;

ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
                    return true;
                };

// Get the object used to communicate with the server.
request = (FtpWebRequest)WebRequest.Create(ftpServer + "file_on_ftp_server.xls");

// download the file
request.Method = WebRequestMethods.Ftp.DownloadFile;

request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);

request.EnableSsl = true;
request.KeepAlive = false;

FtpWebResponse response = (FtpWebResponse)request.GetResponse();    // <--- ERROR

Stream responseStream = response.GetResponseStream();
FileStream writer = new FileStream(downloadedFilePath + downloadedFileName, FileMode.Create);
Patrick
  • 2,995
  • 14
  • 64
  • 125
  • 2
    You are using ssl/tls so the URL should be FTPS:// (not FTP://). SSL, TLS 1.0, TLS 1.12 are obsolete and you must use TLS 1.2 or TLS 1.3. So you probably need to use Net 4.7.2 or later where TLS is performed by operating System (not in Net). Net does not support all the needed encryption algorithms to perform TLS. Check your certificate and see what encryption mode is used. TLS is performed before the FTP connect is done. The server sends a certificate block with names of certificates allowed. You are forcing only one certificate to be used. – jdweng Jun 29 '22 at 11:20
  • Hi, thank you very much for your help. I started to change to ftps:// but I get the error "The URI prefix is not recognized." :( – Patrick Jun 29 '22 at 11:31
  • 1
    I forgot with Net version of library You set SSL=true instead of FTPS. The error indicates the encryption/decryption failed. What version of Net are you using? If you are using Net 4.7.2 the csproj file defaults to using operating system for TLS. So then the issue is with the operating system not supporting the encryption mode. Best way of checking issue is to use a sniffer and see the version of TLS that is being used on working and non working. The TLS version is visible using the sniffer. – jdweng Jun 29 '22 at 11:44
  • I am using 4.5.2 - on that server I already have other applications running TLS 1.2 - on my laptop it's working fine and using 4.5.2 but not in my Windows 2008 R2 - in the meanwhile I have updated to 4.7.2 but the error remains. – Patrick Jun 29 '22 at 14:36
  • 1
    Search web for updating Window 8 R2 for TLS 1.2 if using Operating System. Does Windows 8 machine have all the patches for Net 4.5.2? Is certificate loaded in the Windows 8 machine? – jdweng Jun 29 '22 at 15:53
  • The issue is on my Windows Web Server 2008 R2 the production server. My laptop with windows 10 that I use for development is running fine when connecting to the FTP Server. – Patrick Jun 29 '22 at 16:24
  • 1
    The client request the version of TLS that is used. The server sends a certificate block with names of certificate as part of TLS. So the most likely issue is the certificate is not loaded on the server. Other possibility is Working apps on 2008 R2 could be using a different encryption mode. Are you sure you are working with TLS 1.2 and not TLS 1.3? I do not think this is the case since you only have Net 4.5.2 and working with different server. Net 4.5.2 doesn't really work with TLS 1.3. – jdweng Jun 29 '22 at 16:49
  • I am using TLS 1.2 and based on your suggestion I have installed on the server .Net 4.7.2 and updated the application. How can I do or where can I check the certificates issues you mention? – Patrick Jun 30 '22 at 08:17
  • 1
    One way is to use a sniffer and compare TLS certificate block on working and non working machines. See following : https://www.entrust.com/knowledgebase/ssl/how-to-view-ssl-tls-certificate-details-in-chrome-56?force_isolation=true – jdweng Jun 30 '22 at 09:57
  • It's a console Application, any suggestion for a sniffer in this situation? – Patrick Jun 30 '22 at 10:19
  • 1
    The certificates are still stored in the Users Temp folder under browser settings which can be seen in Chrome. I usually download Wireshark for free. – jdweng Jun 30 '22 at 10:41
  • 1
    [A call to SSPI Failed - The message received was unexpected or badly formatted](https://stackoverflow.com/questions/42493912/a-call-to-sspi-failed-the-message-received-was-unexpected-or-badly-formatted) may help – Eskandar Abedini Jul 01 '22 at 13:35
  • 1
    @Patrick, what do you think about this thread: https://github.com/robinrodricks/FluentFTP/issues/676, this contains mentions to an API dedicated to resolve several requirementss/problems for FTP Request for C# Developer, see more at https://github.com/robinrodricks/FluentFTP/wiki/FTP-Connection – Antonio Leonardo Jul 18 '22 at 17:55
  • 1
    I think it should be sftp not ftp or ftps – Brandon Kauffman Jul 19 '22 at 01:26
  • @AntonioLeonardo and Brandon Kauffman, Thanks! I have decided to transfer the platform to a more recent server where I have discovered that there I dont have this issue. – Patrick Jul 19 '22 at 10:05
  • 1
    Very good @Patrick, it's the best decision. – Antonio Leonardo Jul 19 '22 at 11:20

4 Answers4

2

To use SSL certificate inside .Net framework we need to provide both certificate and its corresponding private key together. To achieve this we need to use p12(.pfx) file which combined this two. In my project, I have used self-signed certificate using OpenSSL so I used below command to combine certificate and private key

pkcs12 -export -out ca.pfx -inkey ca.key -in ca.crt

pkcs12 -export -out client.pfx -inkey client.key -in client.crt

which will create p12(.pfx) file for each certificate. Then used them into your code like below .

see below lonk for more information

Reference

maybe this link also help you .

Good luck

  • Hi thanks. Any example where I can see your solution with my code? – Patrick Jul 04 '22 at 10:44
  • 1
    @Patrick this two links maybe help you : [first](https://stackoverflow.com/questions/43993106/a-call-to-sspi-failed-see-inner-exception-paho-m2mqtt-dot-netc-client-ssl-tl) [second](https://stackoverflow.com/questions/37925505/a-call-to-sspi-failed-see-inner-exception-the-local-security-authority-cannot) – Mohammad Sadegh Mazaheri Jul 05 '22 at 04:50
  • I have checked those but they are not easy to understand :( – Patrick Jul 06 '22 at 15:05
2

Based on the previous suggestions your code should look like this:

var ftpServer = "ftp://xxx.yyy.zz/";
var ftpUsername = "aaaaa";
var ftpPassword = "bbbbb";
var downloadedFilePath = "c:\\temp\\";
var downloadedFileName = "file.xls";                      
var certName = "MyCertName"   //Use yours
var password = "MyCertPassword";   //Use yours
FtpWebRequest request = null;

ServicePointManager.SecurityProtocol = ServicePointManager.SecurityProtocol | SecurityProtocolType.Tls12;

ServicePointManager.ServerCertificateValidationCallback = delegate (object s, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors) {
   return true;
};

//Load certificates (one or more)
X509Certificate2Collection certificates = new X509Certificate2Collection();
    certificates.Import(certName, password, X509KeyStorageFlags.MachineKeySet | X509KeyStorageFlags.PersistKeySet);

// Get the object used to communicate with the server.
request = (FtpWebRequest)WebRequest.Create(ftpServer + "file_on_ftp_server.xls");

// download the file
request.Method = WebRequestMethods.Ftp.DownloadFile;

request.Credentials = new NetworkCredential(ftpUsername, ftpPassword);

request.ClientCertificates = certificates;   //Use certificates

request.EnableSsl = true;
request.KeepAlive = false;

FtpWebResponse response = (FtpWebResponse)request.GetResponse();    // <--- ERROR

Stream responseStream = response.GetResponseStream();
FileStream writer = new FileStream(downloadedFilePath + downloadedFileName, FileMode.Create);

Note: I have not tested the solution because I don't have an environment to test it on

ale91
  • 316
  • 3
  • 8
  • Hi thanks! Because I dont understand all the certificate part, when you said "certName = "MyCertName" //Use yours" - where do I get the certificate and how do I install it in my server? – Patrick Jul 08 '22 at 10:21
  • 1
    Hi! You have to install inside windows server OS like any other certificates, the certificates is the one that you use to connect to FTP server (I think that you installed it on your laptop if the code is working on it). Once installed you can check it opening certificate manager on windows server and searching for it. – ale91 Jul 08 '22 at 11:59
  • I use FileZilla to connect from my laptop and I have never used from the server because I connecting before without any security measure, so this is why I have no idea where to get the certificate. – Patrick Jul 08 '22 at 15:04
2

SSPI is a Windows component. If you are on Windows 2008, it means it's very old.

The error "The message received was unexpected or badly formatted" seems to indicate that the server certificate uses an unsupported format (for instance, uses an unknown cipher), and thus can't be processed by the SSPI layer.

You have several solutions:

  • Upgrade Windows
  • Downgrade the server certificate
  • Don't use SSL
  • Use an alternative FTP library that does not rely on SSPI
Olivier
  • 13,283
  • 1
  • 8
  • 24
  • Hi Thanks! Looking to those options the last one seems the most simple to implement because the others are more critical to change. Any sugestions on the FTP library to use in this case? – Patrick Jul 14 '22 at 09:37
  • 1
    @Patrick You may try [WinSCP](https://winscp.net/eng/docs/introduction). It has both a [command-line](https://winscp.net/eng/docs/scripting) and a [.NET](https://winscp.net/eng/docs/library) interface. – Olivier Jul 15 '22 at 09:46
  • I have tested your suggestion in a 2016 Server version and it worked like a charm so I think it will be a better option to migrate the project to that server and in this case solve the problem, what do you think? – Patrick Jul 15 '22 at 14:11
  • 1
    @Patrick Migrating to 2016 is the best solution. 2008 is outdated and not supported by Microsoft anymore. – Olivier Jul 16 '22 at 06:33
2

The TLS handshake was failing because two servers were unable to agree on a common cipher suite.

IIS Crypto to enable additional cipher suites on web app's server. downloaded and ran IIS Crypto, checkmarked additional cipher suites on its Cipher Suites tab after then restarted the machine.

If diagnose failure, I'd recommend installing Wireshark on the machine with your .NET Core app. If TLS Handshake Failure again,a message like: Alert (Level: Fatal, Description: Handshake Failure) Some cipher suites are more secure than others, so check ref link may help to solve your problem.

temporary solution:
set the minimum key length for schannel using windows registry:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\KeyExchangeAlgorithms\Diffie-Hellman]
"ClientMinKeyBitLength"=dword:00000200

Ref: Link1, link2, link3 ,Link4

MD. RAKIB HASAN
  • 3,670
  • 4
  • 22
  • 35
  • 1
    Hi, thanks! For now based on @Oliver answer I have decided to try to upgrade the server to 2016, I have tested the application there and it's working fine. – Patrick Jul 18 '22 at 08:18