Building a small FTPS client (using TcpClient) to better understand how FTP works (mostly a learning experience).
So far I can connect on the control channel (port 21), issue AUTH TLS, grab the SslStream of the TcpClient connected on port 21, issue User/Pass commands and authenticate successfully over the SslStream.
Now, when I pass the FTP server PASV, I open a new TcpClient with that connection, no problem. My question(s) is(are) this(these): How do I get an SslStream for that new connection? Do I need to re-authenticate that SslStream?
Right now my code just hangs on AuthenticateAsClient:
var sslStream = new SslStream(_tcpDataClient.GetStream(), true, OnCertificateValidation, null);
try
{
sslStream.AuthenticateAsClient(_ftpServer, null, System.Security.Authentication.SslProtocols.Ssl3 | System.Security.Authentication.SslProtocols.Tls, true);
The same code on my control TcpClient works fine:
var sslStream = new SslStream(_tcpControlClient.GetStream(), true, OnCertificateValidation, null);
try
{
sslStream.AuthenticateAsClient(_ftpServer, null, System.Security.Authentication.SslProtocols.Ssl3 | System.Security.Authentication.SslProtocols.Tls, true);
Now, the control TcpClient runs right after an AUTH TLS command is sent to the server. I've tried sending AUTH TLS on the data channel with no luck.
Hoping someone can point me in the right direction. Thanks!
edit
A quick comment - if I use the straight GetStream from _tcpDataClient, I get a 250 response on the Control channel. This leads me to think my Data channel isn't encrypted? Is that correct?
Related
How can I use a Fiddler proxy with a TcpClient? The answer on this similar question did not work for me: How to use Proxy with TcpClient.ConnectAsync()?
var client = new Pop3Client();
var tcpClient = new TcpClient(hostname, port);
var sslStream = new SslStream(tcpClient.GetStream());
sslStream.AuthenticateAsClient(hostname);
client.Connect(sslStream);
After some discussion it turned out that the code to create a connection through a proxy which was referenced in the question actually worked, but
SSL decryption need to be off in Fiddler.
Otherwise Fiddler will not pass the original TLS handshake through but will create one between Fiddler and Server and another one between Client and Fiddler, where the last one has a certificate created by Fiddler. The client will usually not trust this certificate by default and thus fail the TLS handshake.
Moreover, Fiddler expects the traffic inside the TLS connection to be HTTP, i.e. the client sends a HTTP request and the server sends a HTTP response back. POP3 works differently by having both a different message syntax and by having the server start with sending and not the client.
It really has to be client.Connect(sslStream) as shown in the question and not something like client.Connect(tcpStream) as the OP had in its actual code. In the last case the client will just try to read the encrypted data from the connection and thus fail.
Little Background
I have a Windows service (written in C# .NET) that listens on several different ports using TcpListener. some of the ports are (8090, 8091, 554, 25100, 25101).
Each port listener performs different tasks which I'm not going to mention in detail here in this question.
The service keeps on running OK for weeks without any trouble and then all of sudden one of the port 8091 stops receiving new client connections. However other ports remain functioning OK and so as the windows service.
How this port (8091) is being used
User can access the Windows service port from the browser using a secure HTTPS URL. For example look at this URL.
https://XYZServer.net:8091/ABC/?access=123
In the Windows Service I am opening this port using TCPListner. Some sample code is written below.
m_listener = new TcpListener(IPAddress.Any, 8091);
m_listener.Start();
m_listener.BeginAcceptTcpClient(DoAcceptWebTcpClientCallback, m_listener);
And then when a new client connection request comes in. See some sample code.
private void DoAcceptWebTcpClientCallback(IAsyncResult ar)
{
try
{
var client = m_listener.EndAcceptTcpClient(ar);
var webConnection = CreateConnection(client, m_waitForDeviceTimeout);
webConnection.Start();
}
catch (Exception ex)
{
s_log.Error(ex, "{0}: Exception", m_serverName);
}
try
{
BeginAccept();
}
catch (Exception ex)
{
s_log.Error(ex, "{0}: Exception", m_serverName);
}
}
I am associating the sslStream with the client connection. Also I am authenticating the Server using a certificate without authenticating the client. Sample code below
protected override WebConnection CreateConnection(TcpClient client, int waitForFb4Timeout)
{
return new HttpsConnection(SERVER_NAME, client, m_serverCertificate, waitForFb4Timeout); // Create an ssl/tls connection using the configured certificate
}
public HttpsConnection(string serverName, TcpClient client, X509Certificate serverCertificate, int waitForFb4Timeout) :
base (serverName, client, GetStream(client, serverCertificate), DeviceConnection.ProtocolEnum.HTTP, waitForFb4Timeout)
{
}
private static Stream GetStream(TcpClient client, X509Certificate serverCertificate)
{
// Create the SslStream using the client's network stream.
var sslStream = new SslStream(client.GetStream(), false);
// Authenticate the server but don't require the client to authenticate.
sslStream.AuthenticateAsServer(serverCertificate, false, SslProtocols.Tls, true);
return sslStream;
}
Windows service relays all the messages coming on this port to another destination (a WebSocker Server) and relays the response back to the browser. User can then see the page on the screen. This keeps on working fine for weeks and then all of sudden users can't browse the page and new client connection requests on the port 8091 stops coming.
When this happens, no exceptions are raised. It is really annoying and hard to understand whats really going wrong here..
One more thing I would like to mention that in the same windows service we are making lots of outbound connections which is using ephemperal port range (49000-65535). I can see all of these ports beings used and then reused again and again. However there are no evidence of all the ports being used at once. I know that there are ways to increase the port range but I have not tried this yet as I'm not convinced that we are reaching to the limit.
I can see several exceptions in the code related to authentication on this port.
2018-08-11 01:39:43.8952|ERROR|Fb4RelayServerLib.WebConnections.WebServer.DoAcceptWebTcpClientCallback: HTTPS: Exception System.IO.IOException: Authentication failed because the remote party has closed the transport stream.
People have suggested on different forums about using
SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12
Whereas I am only using the SslProtocols.Tls only (see code). I have not tried above as in my case I am getting this exception rarely and for the same client IP which is also working fine.
I'm really stuck and don't know how to troubleshoot and further investigate this issue. Any help in this regard will be very helpful. Many thanks in advance and sorry for a lengthy question.
Thank You
Right, after a lot of struggle I finally resolve the issue.
I did two things mainly to address the problem.
1- Add the support for tls1.1 and tls1.2 which my product was lacking originally. (As I posted in my question as well)
2- Replace AuthenticateAsServer with AuthenticateAsserverAsync.
Actually for some unknown reason the AuthenticateAsServer function was hanging and not allowing the application to accept any new client connections.
Doing above changes, it seems to be working fine so far. Keeping my fingers crossed :)
I have some SSL client code (C#) which looks like:
X509Certificate2 cert = new X509Certificate2(#"client.pfx");
X509Certificate2Collection x09s = new X509Certificate2Collection();
x09s.Add(cert);
TcpClient client = new TcpClient(controllerIP, port);
sslStream = new SslStream(client.GetStream(), false, OnCertificateValidation,
new LocalCertificateSelectionCallback(SelectLocalCertificate));
sslStream.AuthenticateAsClient("", x09s, SslProtocols.Default, false);
This is communicating with a linux server running with openssl. It all works great when the client is Windows 7. When I try on a Windows 8 client however, the AuthenticateAsClient always get an exception with the message: "Unable to read data from the transport connection. A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond."
The LocalCertificateSelectionCallback function is called and returns properly.
What changed in Windows 8?
Thanks...
I know this is an old thread, but still, I got same answer form AuthenticateAsClient in Windows 10.
AuthenticateAsClient was successful when I increased response time in client (TcpClient) and streams (NetworkStream, SslSteram).
But for some weird reason in Windows 10, after I dispose of connection objects (SslStream and TcpClient) and try to go through connection steps again, AuthenticateAsClient is successful each second time. (success, fail, success, fail ...).
For Windows 7 everything works perfectly.
My system is accessing internet via a proxy server
(IE proxy server address : myserver.mydomain.com , port: 80).
Im trying to send some data to a TCP server using class "System.Net.Sockets.TcpClient". But i am unable to connect. If I try using static IP internet, i am able to connect.
Is there any way to provide proxy address ? I am pretty sure the problem is the proxy, because i have tried from two systems which do not use proxy and it worked fine.
My application is a console application.
My Code is something like :
TcpClient tcpClient = new TcpClient("tcpserver.com", 3101);
string message = "message";
byte[] auditMessageStream;
auditMessageStream = Encoding.UTF8.GetBytes(message);
int i = tcpClient.Client.Send(auditMessageStream);
You can use this opensource lib to create a socket conection through a proxy.
Watch this too
You need HTTP tunneling unless your proxy is a SOCKS proxy.
I am trying to create a simple xmpp client that connects to Gtalk.
The first part of the handshake seems to work.
Ror the TLS handshake I created a client SslStream, connected to the intended server (talk.google.com) and successfully got authenticated .
The first SSlStream.Read is to receive the greeting reply, it went fine . I do a SslStream.write to send my first command, but when i do my Sslstream.Read() to get the reply , i get this error."System.IO.IOException: Unable to read data from the transport connection: An established connection was aborted by the software in your host machine."
Can anyone point me to the right direction?
I am using code very similar to the example on msdn http://msdn.microsoft.com/en-us/library/system.net.security.sslstream.aspx
except that I switch from a Network stream to a Sslstream when TLS is negotiated.
netStream.Flush();
sslStream = new SslStream(netStream,
true,
new RemoteCertificateValidationCallback(ValidateServerCertificate),
null
);
sslStream.AuthenticateAsClient("talk.google.com");
I'd try using one of the existing XMPP libraries for .Net:
Jabber-Net: http://code.google.com/p/jabber-net/
agsXMPP: http://www.ag-software.de/agsxmpp-sdk/
Even if you don't use of these libs, you'll get some good ideas from looking at the code.
In this case, you probably want:
sslStream.AuthenticateAsClient("gmail.com");
where gmail.com is the domain name from the JID you're trying to log in as.