Connect TcpClient through Fiddler proxy - c#

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.

Related

Unable to establish https connection with gRPC

I have gRPC client / server code happily working on my local machine with the client using ChannelCredentials.Insecure but need to switch to a secure mode. I dont need any certificate checks just encrypted traffic.
Ive been unable to find any configuration of client server that talks to each other.
Running c# core server (gRPC 2.27.0), & c# .net framework client (gRPC 2.28.1).
The server says it publishing on http & https as below:
[20:12:58 DBG] Using development certificate: CN=localhost (Thumbprint: 3EDA2E5BD559D75C9DCF058E0A6994EED859CD34)
[20:12:58 INF] Now listening on: https://localhost:5001
[20:12:58 INF] Now listening on: http://localhost:5000
and client works with:
ChannelBase channel = new Channel("localhost:5000", ChannelCredentials.Insecure);
var client = new MyApp.MyAppClient(channel);
var response = client.Test(request)
If I switch client to SslCredentials as below
ChannelBase channel = new Channel("localhost:5001", new SslCredentials());
var client = new MyApp.MyAppClient(channel);
var response = client.Test(request)
I get the following errors.
server error
[19:32:53 DBG] Failed to authenticate HTTPS connection.
System.IO.IOException: Authentication failed because the remote party has closed the transport stream.
at System.Net.Security.SslStream.StartReadFrame(Byte[] buffer, Int32 readBytes, AsyncProtocolRequest asyncRequest)
at System.Net.Security.SslStream.PartialFrameCallback(AsyncProtocolRequest asyncRequest)
client error
Grpc.Core.RpcException: 'Status(StatusCode=Unavailable, Detail="failed to connect to all addresses")'
I've also tried adding (server) with no change in errors.
services.AddAuthentication(CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.RevocationMode = X509RevocationMode.NoCheck;
options.ValidateCertificateUse = false;
options.AllowedCertificateTypes = CertificateTypes.SelfSigned;
});
Any suggestions as to what i need to do to make them talk.
So you need to specify roots.pem that correspond to the dev certificate you're using on your server when creating SslCredentials.
If you use parameterless SslCredentials(), it will use the default trust roots which only work for certificates that have been signed by publicly trusted certificate authorities (e.g. if you're talking "official" servers such as googleapis.com etc, definitely not the case for your development certificates that you are using on your server). So you need to use SslCredentials(yourCustomTrustRootsPem).
Btw, with SSL/TLS, there's no such thing as "no certificate checks". The client will ALWAYS check that it connected to the right server (server's certificate are verifiable by their trust roots) - without that the secure communication would make no sense because it would be too susceptible to man-in-the-middle attack. (the server checking authenticity of client is optional though).
Feel free to refer to https://github.com/jtattermusch/grpc-authentication-kubernetes-examples for extra resources.
I had a similar problem and finally found a solution to establish HTTPs connection between
.NET Framework 4.7.2 client (WPF app) and
ASP .NET Core 3.1 gRPC Server (Console).
Indeed the answer of Jan Tattermusch is correct. You need to supply the server side certificate as PEM to the constructor of SslCredentials and in addition, the certificate has to contain the DNS name or IP of the url you are contacting the server through. In your case, I suggest to manually create a self-signed certificate for each server instead of using the developer certificate. This one can be downloaded by your client and then be passed as SslCredentials. Works like a charm.
See my more details on how to do all the details in my answer to a similar question here:
https://stackoverflow.com/a/63565090/378415
Try to check on which port Grpc Service is running after deployment in any way either windows service or other , then give the same in client to consume the service. Eg: If your service is running on https://localhost:5000 then give the same in client.
It will work for sure.
Mostly you will get the above issue when you make a mistake of mismatching the address of service to client.

How to use WebSocket in one server and access it in another PC in the same LAN?

When I use WebSocket server in C#, and access it in local PC, it works normally. But when I try to access with another PC, it doesn't work.
I'm using WebSocket library from Fleck.
Code in C# WebSocket Server:
_server = new Fleck.WebSocketServer("wss://127.0.0.1:8181");
_server.Certificate = new X509Certificate2(#"C:\test.com.pfx", "123");
Code in HTML/Javascript:
websocket = new WebSocket('wss://192.168.1.37:8181');
When I work with insecure WebSocket "ws://127.0.0.1:8181" when I try to access the local IP "ws://192.168.1.37:8181", it the Chrome Console sends the error:
The page at 'https://www.websocket.org/echo.html' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://192.168.1.37:8181/?encoding=text'. This request has been blocked; this endpoint must be available over WSS.
When I work with Secure WebSocket "wss://127.0.0.1:8181"
it sends the error:
WebSocket connection to 'wss://192.168.1.37:8181/?encoding=text' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED
It only works when I type "wss://127.0.0.1:8181" in WebSocket client, but I want to work with the standard IP, to be accessed by other PC.
I found the solution, here the steps if someone needs:
You need to start the Fleck WebSocket server as bellow:
_server = new Fleck.WebSocketServer("wss://0.0.0.0:8181");
_server.Certificate = new X509Certificate2(#"C:\test.com.pfx", "123");
Then in the PC that you want to access though WebSocket client, you need to access first with the browser:
https://192.168.1.37:8181
Then you allow the access. (It'll let the Certification of an Unknown User)
After that you WebSocket Client will work.

c# proxy ssl / tls passthrough without certificate

This is the issue.
I have an https request. The request is is being sent as an SSL / TLS request (Not the CONNECT .... that comes from a browser with the proxy setup).
I need to write a proxy in c# that blocks a specific https://foo.com/foo.htm request but lets through https://foo.com/anything_else.htm.
I can do this fine creating a MITM attack with a new certificate etc etc.
But Im now wondering if there is an easy way to do this Im missing without using a MITM attack as I have no need to decrypt the data. I only need to know the URI/file.
I can easily just transfer streams but I want to know if there is a simple way to transfer the streams after I have read the URI and file.
I can write some fancy code to pull apart the tcp request and thats what I may have to do.
Anybody any ideas before I go down this path. Remember there is no CONNECT request. Just direct SSL / TLS.
The main reason for this is it just makes things simpler not creating self signed certificates etc.
Maybe its even possible to use the real certificate somehow from the server end as I dont need to decrypt any of the no header data.
I find the networking side of c# is not very well documented and a little all over the place.
Just for reference i can get the URI from the TcpClient using:
IPEndPoint ipEndPoint = (IPEndPoint)clientTcpClient.Client.RemoteEndPoint;
IPAddress ipAddress = ipEndPoint.Address;
// Get the hostname.
IPHostEntry ipHostEntry = Dns.GetHostEntry(ipAddress);
String hostName = ipHostEntry.HostName;
// Get the port.
Int32 port = ipEndPoint.Port;
But not the requested page.
While the target hostname might be visible in the TLS handshake as SNI extension or by analyzing the certificate returned by the server the path component of the URL is only contained in the HTTP request. Since this HTTP request is only done after TLS handshake and the request is thus already encrypted you cannot get to the full path without decrypting the request. This means that blocking access to a specific path is not possible without SSL man in the middle and thus requires a certificate for the target site owned by the man in the middle and trusted by the client.
Not that this is true for CONNECT requests too since these requests only contain the target hostname but the path component is again only contained in the encrypted HTTP request sent inside the tunnel created by CONNECT.

SSL Socket between .Net and Java with client authentication

I am trying to create an SSL Socket Server/Client between .NET and Java. In this case, my SSL Socket Server will run in .net and the client runs in Java under Linux. My problem is that the connection fails during the handshaking, specifically when the server request a certificate from the client, the client is unable to send something back and the connection fails.
In .net I am using sslStream to establish the connection and on Java I am using the standard SSLSocket. Some code snippets are below, but this is what I have so far:
On the server side (Windows), I have a private certificate in the Personal/Certificates folders under MMC. I have a public certificate from the client in the Trusted People/Certificates. Both certificates were issued by the same CA. The certificate chain for both certificates have multiple levels, but it is the same for both. The root level certificate in the chain is also installed in the trusted Certification Authorities/Certificates folder.
On the client side (Linux), I have a keystore that contains the private certificate that matches the public certificate installed at the server. I have a trust store that contains the public certificate from the server, matching the server's private one.
On the server side (.net) I am using a Socket that does an asynchronous read and then it gets wrapped into an SSLStream, the code snippet is like this:
NetworkStream ns = new NetworkStream(socket, false);
SslStream ssl = new SslStream(ns, true);
ssl.AuthenticateAsServer(serverCertificate, true, SslProtocols.Default, true);
The client code is pretty much standard code:
SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
InetAddress addr = InetAddress.getByName(servername);
SSLSocket socket = (SSLSocket) factory.createSocket(addr,port);
socket.setUseClientMode(true);
socket.setNeedClientAuth(true);
socket.setWantClientAuth(true);
socket.startHandshake();
os = new DataOutputStream(socket.getOutputStream());
is = new DataInputStream(socket.getInputStream());
byte[] outBuf = new byte[50];
os.write("SEND SOMETHING".getBytes("UTF-8"));
is.read(outBuf);
In java I have set the proper varialbes to point to the trust and key store with their password.
Now, following the standard SSL Handshake, this is what happens:
ClientHello
ServerHello
Server sends public certificate
Client matches the public certificate with the one on the trust store
Server sends the Certificate request
With the certificate request the server sends a list of valid CAs, on this list only the my root CA is sent (among a long list of other well known CAs.).
Client certificate is null.
Server receives a null certificate from the client, thus closes the connection.
And that is it, the client won't send a valid certificate back to the server. I have some questions on this:
Has anybody experienced something like this?
Regarding that list of CAs sent by the server (Windows), How does .net determine what to send to the client? Is there a way to modify that list?
Do I need to send the all the authorities in the chain used to sign my certificate in that list of CAs? or is the Root one enough?
Am I missing something on either side of my code?
Any help will be greatly appreciated it.
In
The following two statements are useless on the client side (although they shouldn't hurt):
socket.setNeedClientAuth(true);
socket.setWantClientAuth(true);
The fact that you see the Certificate Request message and the Client Certificate message shows that the server is configured properly.
The most likely cause that comes to mind for the absence of certificate in the client certificate message is that the keystore (on the client side) might not be configured properly. You may be interested in this answer to make sure that your client key store is configured properly. More specifically, you need to make sure that the private key for your client certificate was imported in the same alias as the certificate chain (and that it's the chain going back to a CA advertised in the Certificate Request message).
(Regarding the rest of your question, I'm not sure how to modify the CA list sent by the server when using SslStream in C#. This earlier question would seem to suggest there is no solution, although newer versions of .Net may have addresses the issue since this question was asked. I haven't been able to find anything that would do it by looking at the SslStream API documentation and related classes, but this doesn't mean it doesn't exist.)

XMPP TLS connection with SslStream

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.

Categories

Resources