Client terminate SSL Connection after server sends SERVER HELLO DONE - c#

It is my first question on Stack Overflow, I'll do my best !
I have already read multiple document on how SLL/TLS connection works. I didn't found any clue of what is happening.
I am currently connecting to a server through SSL.
My OS is Windows 10.
The server ask us a certificate which we provide.
Everything fine there. The SSL connection is established.
Now, when I try to establish the connection from my server, I receive a Web Exception ("The request was aborted: Could not create SSL/TLS secure channel.")
The server's OS is Windows Server 2012 R2.
I am pretty sure our server closes the connection (See attached picture at the bottom of the post).
Code to load the certificate
X509Certificate cubicCertificate;
try
{
cubicCertificate = X509CertificateHelper.GetCertificate2FromStore(cubicCertificateThumbPrint);
}
catch(Exception e)
{
m_Log.Trace("Cubic Http client initialization - Could not load certificate");
throw e;
}
var handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(cubicCertificate);
ServicePointManager.ServerCertificateValidationCallback = ValidateServerCertificate;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12;
m_HttpClient = new HttpClient(handler);
m_HttpClient.Timeout = new TimeSpan(0, 0, 30); // 30 seconds timeout
m_HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
Code to send a request
try
{
var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{cubicUrl}");
requestMessage.Content = new FormUrlEncodedContent(parameters);
m_Log.Trace($"URL - {m_HttpClient.BaseAddress}{url}");
Task <HttpResponseMessage> task = m_HttpClient.SendAsync(requestMessage);
responseMessage = task.Result;
}
catch(Exception e)
{
var serializedException = JsonConvert.SerializeObject(e);
m_Log.Trace(serializedException);
throw e;
}
We have already verified
That the certificate sent by the distant server is recognize by our server.
That our server can load our certificate. (It is loaded from the Store)
That our server can access the private key of our certificate
Any help is welcome !

Seems the remote server is enforcing Client Authentication (see Certificate Request) and probably it does not trust your certificate.

Related

How it works the security in gRPC?

I have a service that I am configurating in this way:
options.Listen(miAddress, 5001, l =>
{
l.Protocols = HttpProtocols.Http2;
System.Security.Cryptography.X509Certificates.X509Certificate2 miCertificado = new System.Security.Cryptography.X509Certificates.X509Certificate2(#"certificados\service.crt");
l.UseHttps(miCertificado);
});
But if I realized that I can configure the client to avoid the authentication, with this code:
var httpClientHandler = new System.Net.Http.HttpClientHandler();
httpClientHandler.ServerCertificateCustomValidationCallback =
System.Net.Http.HttpClientHandler.DangerousAcceptAnyServerCertificateValidator;
var httpClient = new System.Net.Http.HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress(_serviceAddress,
new GrpcChannelOptions { HttpClient = httpClient });
var client = new Gestor.GestorClient(channel);
In this case, the authentications is ignored and I can use call to the service.
I know that this ignore the authentication because if I try to use the client to use a certificate, I get an error that tells that the connection couldn't be stablish because of the SSL.
So my doubt is, there is some way to set the service to don't allow this kind of connections? If not, anyone could create a client that igonres this authentication and the security has no sense.
Thanks.
Using HTTPS is not the same as using authentication. All you're doing is encrypting the traffic between client and server, so that eavesdroppers can't read your plaintext traffic.
If you configure your client to accept any server certificate, whether that certificate is valid up till its root or not, does not "ignore authentication" - there was no authentication to begin with.

Client certificate with HttpClient in c#

Want to send data with client certificate (.p12 or .pfx) from Windows application to Server machine, Windows application developed in .Net Framework 4.6, OS is windows 10.
When hit from postman with client certificate (.p12 or .pfx) [Loaded in setting tab -> Add client certificate - > put hostname, select pfx file, put password], all working properly (client certificate send to server machine), but issue from below c# code,
X509Certificate2 certificate = new X509Certificate2(certificateFilePath, "password");
WebRequestHandler handler = new WebRequestHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ServerCertificateValidationCallback = (a, b, c, d) => { return true; };
handler.ClientCertificates.Add(certificate);
HttpClient request = new HttpClient(handler);
request.DefaultRequestHeaders.Add("User-Agent", UserAgent);
// added other headers and data
var result = request.PostAsync(url, byteContent).Result;
string resultContent = result.Content.ReadAsStringAsync().Result;
Also cross check with fiddler for Postman hit and c# hit.
When server not receive client certificate, it return 403 error.
The HttpStatus code 403 can caused by TLS issues due to not invoking API with the expected server TLS version. You can check the outcome of resultContent from the code line string resultContent = result.Content.ReadAsStringAsync().Result;
To set the SslProtocol, either you can set at Handler like (if you are targeting .Net 4.7 onwards or .Net core)
WebRequestHandler handler = new WebRequestHandler();
handler.SslProtocols = SslProtocols.Tls13 | SslProtocols.Tls12 | SslProtocols.Tls11 | SslProtocols.Tls;
or at application level using ServicePointManager in Startup method (or .Net framework version before 4.7)
System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls13 | SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls;
A side note suggestion - I would suggest you to use pure async/await pattern. Don't use the sync call on the IO request by invoking .Result.
I assume that your handler has no access to the private key for authentication.
This may be caused due to line 1 in your example, in which you import the certificate with the default key storage flags. Of course this is just a guess and I don't have your certificate to check this, but you can verify that by calling
// Sample for RSA, use DSA if required
var privateKeyParams = ((RSA)certificate.PrivateKey).ExportParameters(true);
which will cause a CryptographicException ("Not supported" or similar) if the key parameters cannot be accessed.
Please try the following instead for loading the certificate:
X509Certificate2 certificate = new X509Certificate2(
certificateFilePath, "password",
X509KeyStorageFlags.Exportable | X509KeyStorageFlags.PersistKeySet | X509KeyStorageFlags.UserKeySet);
Just a further sidenote; If you use this in production code, be sure to extend your server certificate validation callback (your 4th line) to actually validate the server certificate. See X509Chain.Build, which also allows you to modify the validation options to your needs (what the path validation actually does can be read in RFC5280).

Client Certificates in Dotnet Core on Ubuntu

all - I've written a dotnet core API set that functions perfectly on windows. On Ubuntu 14.04, everything works except for one SOAP request to a vendor that uses a client certificate for authentication.
The request always times out. A Netstat trace shows that only 1 byte of data was sent to the remote service on 443. No communication happens for 100 seconds and then the app throws a timeout exception.
I've tried using openssl to export PEM and CRT files and referenced those in addition to the way the code is configured now (pfx w/ password). I've also loaded the certificate portions of the PFX into ca-certs.
Here's the code:
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var baseAddress = new Uri(mySettings.ClientUrl);
factory = new ChannelFactory<SingleSignOnSoap>(binding, new EndpointAddress(baseAddress));
if (RuntimeEnvironment.OperatingSystemPlatform == Platform.Windows)
{
//windows file location
factory.Credentials.ClientCertificate.Certificate = new X509Certificate2(mySettings.PrivateKeyWindowsPath, mySettings.PfxPass);
}
else
{
//linux file location
factory.Credentials.ClientCertificate.Certificate = new X509Certificate2(mySettings.ClientPrivateKeyUnixPath, mySettings.PfxPass);
}
serviceProxy = factory.CreateChannel();
RequestTicketRequest request = new RequestTicketRequest();
RequestTicketRequestBody requestBody = new RequestTicketRequestBody(xmlRequest);
request.Body = requestBody;
RequestTicketResponse response = serviceProxy.RequestTicket(request);
return response.Body.RequestTicketResult;
Wireshark and Tshark show the authentication is actually working ok. The timeout is happening because the ServiceFactory is waiting to receive the response, but the network has sent a connection reset flag ([RST, ACK]) to the remote server. I've been able to reproduce on multiple linux distros so I'm adding an issue to the dotnet core WCF team's queue on github.

Mutual authentication using a USB token slot with a X.509 certificate

I am trying to implement a a client library in C# to communicate with a tomcat server. The authentication should be done by using a Feitian epass2003 token with a X.509 certificate inside mutual SSL authentication in a windows client.
However i am having a bad time cause everytime I run the client windows requests the token password in order to proceed the operation which is a overkill to the user and not acceptable.
I would like to know if its possible to dismiss this request and add it in some way in the code. Or if there is other way to use the token without using the windows cert manager.
this is how I am extending the connector:
class WebClientEx : WebClient
{
public int Timeout { get; set; }
public X509Certificate certificate { get; set; }
protected override WebRequest GetWebRequest(Uri address)
{
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
HttpWebRequest request = (HttpWebRequest)base.GetWebRequest(address);
request.Credentials = CredentialCache.DefaultCredentials;
request.AuthenticationLevel = AuthenticationLevel.MutualAuthRequested;
request.ClientCertificates.Add(this.certificate);
request.Timeout = this.Timeout;
return request;
}
...
}
This is an example how I am connecting to my server:
byte[] certificate = getCertificate("autenticacao"); // Get certificate from token
X509Certificate cert = new X509Certificate(certificate);
var post = new NameValueCollection();
post["test"] = "1";
using (var wb = new WebClientEx(cert))
{
try
{
wb.Timeout = 30000;
var response = wb.UploadValues("https://localhost:8443", "POST", post);
Console.WriteLine("Done.");
}
catch (WebException e)
{
// Handle WebException
}
}
This is windows request screen:
Best regards,
TLS allows for session reuse using a session ticket. See RFC5077 Transport Layer Security (TLS) Session Resumption without Server-Side State, read Speeding up SSL: enabling session reuse. Server support varies. Afaik the .Net Framework client side support is, basically, none. See TLS/SSL and .NET Framework 4.0.
Hardware modules do not allow the private key to ever leave the module and the PIN is required on each access. So if the TLS handshake requires the key, then the PIN dialog is unavoidable, your only chance is to try avoiding the private key requirement, and that is only doable with reusable TLS session tickets, afaik.
You may reconsider the mutual TLS requirement on each access. Access one resource (ie. login page), get an access ticket (cookie) then use this on authenticating accessing the rest of the resources.
PS. SSL is a no-option, been obsolete for years everybody talks about TLS nowadays.

.NET Mutual SSL handshake 'Client Authentication'

I have been working on this problem for the last couple of days and I am not getting anywhere.
The scenario is:
An iOS app in the field will call my REST Service (.NET).
My REST service will call the Apache web service using the Mutual SSL handshake.
Whatever data I receive I have to pass back to iOS devices in the field.
The only issue is the 2nd part of communication between My REST Service and Apache Web service.
Client Certificates have been signed using the key usages of Client authentication, Digital Certificate, Key Encipherment. The root signers of my certificate have been placed on the Apache server. If we try using the Web browser we can perform the handshake without any issues.
Sample code which I am using to perform the authentication using SslStream. Using this method I get an error stating
The message received was unexpected or badly formatted
and the guys who manage the Apache web server say that they can see the request coming in, but according to them they are not receiving a certificate with this.
var certificate = #“certificate.cer”;
var hostAddress = “hostAddress";
var certificates = new X509Certificate2Collection(new X509Certificate2(certificate));
RunClient(hostAddress, 1316, certificates);
static void RunClient(string hostName, int port, X509Certificate2Collection certificates)
{
TcpClient client = new TcpClient(hostName, port);
SslStream sslStream = new SslStream(client.GetStream(), false, ValidateServerCertificate);
try
{
sslStream.AuthenticateAsClient(hostName, certificates, SslProtocols.Ssl3, true);
Write("authenticated.");
}
catch (AuthenticationException ex)
{
Write("Inner: " + ex.InnerException.Message);
}
catch (Exception ex)
{
Write(ex.Message);
}
}
Sample code which I am using to perform the authentication using HttpWebRequest. Using this method gives me the following issue
The request was aborted: Could not create SSL/TLS secure channel.
var certificate = #“certificate.cer”;
var hostAddress = “hostAddress";
var certificates = new X509Certificate2Collection(new X509Certificate2(certificate));
public static T Post(string url, string body, X509Certificate2 cert)
{
var webRequest = FBJsonRequestService.CreateRequest(url, cert, WebRequestMethods.Http.Post, body);
using (var webResponse = webRequest.GetResponse())
{
return CreateResponse(webResponse);
}
}
var webRequest = (HttpWebRequest) WebRequest.Create(url);
webRequest.ClientCertificates.Add(cert);
//webRequest.AuthenticationLevel = AuthenticationLevel.MutualAuthRequested;
webRequest.Credentials = CredentialCache.DefaultNetworkCredentials;
webRequest.Method = method;
webRequest.ContentType = "application/json; charset=utf-8";
if (body != null)
{
using (var streamWriter = new StreamWriter(webRequest.GetRequestStream()))
{
streamWriter.Write(body);
}
}
return webRequest;
I hope this makes sense. All I want to know is if whatever I am doing is right or I am doing it wrong.
I think the content of file certificate.cer is incorrect. Extension .cer in my opinion contains only certificate but not private key.
You have to use certificate.p12 or certificate.pfx that contains also private key. These extensions represent PKCS#12 standard. There can also be whole certificate chain included in these files.
You can load a p12 file using different constructor of X509Certificate2 class. Please look at this documentation.
Thanks Pepo,
I figured this out that I was using the wrong file. I had to use the PFX file instead of CER. My issue turned out to be something else altogether. It was some security restriction of Certificate itself. Before posting this I wasn't even sure that whether I was doing the right thing.
I have created the following blog for someone in future needs any help regarding this.
The another reason for the exception "The request was aborted: Could not create SSL/TLS secure channel"
is that the server may not support the security protocol type.
The another protocol type is TLS whcih is more secure than SSL3,
The following code could help you
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;

Categories

Resources