I've read at several places (like here, here or here) that it's a bad practice to dispose of the HttpClient directly after a request and it's better to dispose of it after all the request have been made, to allow reuse of the connection.
To try that out, I've created an instance of HttpClient and added to a static field in my class this way:
public class Test
{
private static X509Certificate2 _certificate;
private static HttpClient HttpClient { get; set; }
...
public Test()
{
...
if (HttpClient == null)
{
LoadCertificate();
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
var handler = new WebRequestHandler();
handler.ClientCertificates.Add(_certificate);
HttpClient = new HttpClient(handler, false);
}
}
private void LoadCertificate()
{
using (var store = new X509Store(StoreName.My, CertificateStoreLocation))
{
store.Open(OpenFlags.ReadOnly);
var certificates = store.Certificates.Find(X509FindType.FindBySubjectName, CertificateFriendlyName, true);
if (certificates.Count != 1)
throw new ArgumentException(
$"Cannot find a valid certificate with name {CertificateFriendlyName} in {CertificateStoreLocation}");
_certificate = certificates[0];
store.Close();
}
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
}
}
I'm then using my instance to call a web service through this command:
var result = await HttpClient.PostAsJsonAsync(completeUri, request);
The first time I'm running the code, everything works fine and I get a response correctly, but then, all the following time I get an unauthorized from the server telling me that I didn't use a client certificate.
It's like if for the following calls, the WebRequestHandler wasn't took into consideration.
Your fix should look like this:
handler.PreAuthenticate = true;
Once you establish a connection to a service, you can re-use it to communicate with it using different clients with different auth information. That means, the service needs to know which client sent a request each time, otherwise it could be a security breach - e.g. executing a request under last connected client. It depends on your authentication mechanism, but basically WebRequestHandler sets flag IsAuthenticated after the first request, and stops sending the auth information on next requests. The PreAuthenticate options forces to send the auth info on every request.
Related
public async Task<string> echo()
{
using var httpClientHandler = new HttpClientHandler
{
ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
};
// The port number must match the port of the gRPC server.
using var httpClient = new HttpClient(httpClientHandler);
using var channel = GrpcChannel.ForAddress("http://10.0.50.120:30000", new GrpcChannelOptions { HttpClient = httpClient });
var client = new StorageService.StorageServiceClient(channel);
var reply = await client.echoAsync(new EchoRequest { });
return reply.ServiceName;
}
I made a client with blazer and I want to communicate with other servers through echo. However, an exception has occurred.
"The connection failed because the target computer refused to connect. " I thought it was not possible because of the authentication of entering the ID and password of the server. I want to know if that's the right problem and if it is, I want to know how to enter it with code.
I'm working on several Dotnet Core APIs hosted on a Kubernettes cluster and some of the APIs do call other APIs, and that's when the exception in title is thrown.
It doesn't matter whether I edit the appsettings.json and replace all https by http -in fact people at devops team suggested me to do that- as the same exception is thrown.
This is the little piece of code I use for the http call:
int idCity = Convert.ToInt32(Utils.GetConfig().GetSection("Settings")["idCity"]);
using (HttpClient client = new HttpClient())
{
client.BaseAddress = new Uri(Utils.GetConfig().GetSection("xxx")["xxxx"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
string queryString = "?startDate=" + startDate + "&endDate=" + endDate + "&idCity=" + idCity;
HttpResponseMessage response = client.GetAsync(queryString).GetAwaiter().GetResult();
if (response.IsSuccessStatusCode)
{
var resultHolidays = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
return JsonConvert.DeserializeObject<JSONGeneric<HolidayDTO>>(resultHolidays);
}
else
{
return null;
}
}
I have a copy of the certificate in .crt format and also tried:
string certPath = Path.Combine(_env.ContentRootPath, _configuration.GetSection("Certificate")["certificatePath"]);
string pwd = _configuration.GetSection("Certificate")["certificatePwd"];
HttpClientHandler requestHandler = new HttpClientHandler();
requestHandler.ClientCertificates.Add(new X509Certificate2(certPath, pwd,
X509KeyStorageFlags.MachineKeySet));
using (HttpClient client = new HttpClient(requestHandler))
{
...
}
To no avail, as the same exception is thrown.
I'm not an expert on working with certificates, but I truly need to make this to work, to be able to make on api in a pod call other api, so any help will be much appreciated.
Update 1: The "weird" thing is that if I just copy the url to be requested -no matter if you use http or https- and paste it into a browser with the certificate installed it does work. If you copy and paste the http version of the url n the browser, Kubernettes (or whoever it is) does a redirection to the https version but in the end you get results. Not from .Net
I would start by disabling certificate validation in the client and see what is the behavior. You can do it like this:
var httpHandler = new HttpClientHandler {
ServerCertificateCustomValidationCallback = (m, crt, chn, e) => true
};
using var httpClient = new HttpClient(httpHandler);
// rest of the code
If the call succeeds, the next step is to adapt the certificate validation callback to check the server's certificate.
Note: in your example you're configuring a client certificate, which is useful if you host a service and want to authorize your clients based on their certificates, as described here. From the problem description I understand that what you need is the opposite: validate the server certificate in your client.
var srvCrt = new X509Certificate2(certPath, pwd);
var httpHandler = new HttpClientHandler {
ServerCertificateCustomValidationCallback = (m, crt, chn, e) => {
return crt.Thumbprint == srvCrt.Thumbprint;
}
};
using var httpClient = new HttpClient(httpHandler);
// rest of the code
I have the same certificate (use x509certificate2) in the application and in the server (both in C#),
I have to extend the validity of the certificate file in the server,
I use in SSL3 with the certificate to connect to soap web service from client (with set or get)
My question Is it mandatory for the same certificate to be in the app?
try
{
// Initiate a new request to the WS
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Ssl3 | SecurityProtocolType.Tls;
MyServiceSSL.MyWebServiceClient clClient = new MyServiceSSL.MyWebServiceClient();
clClient.Endpoint.Address = new System.ServiceModel.EndpointAddress(sConnStr);
ServicePointManager.ServerCertificateValidationCallback += new RemoteCertificateValidationCallback(ValidateRemoteCertificate);
try
{
clClient.ClientCredentials.ClientCertificate.SetCertificate(
StoreLocation.LocalMachine,
StoreName.TrustedPeople,
X509FindType.FindBySubjectName,
"myDomain.com");
}
catch (Exception e)
{
...
}
MyServiceSSL.myDataRequest clRequest = new MyServiceSSL.myDataRequest();
// Set up request parameters
clRequest = new myDataRequest();
clResult = clClient.myFunc(clRequest.MyRequest_1);
I have to extend the validity of the certificate file in the server,
Does that mean the validity date? If so, of course you need to renew both otherwise one would be expired and therefore not valid any more.
BTW: You should not use SSL3
How to do certificate authentication in Simple.OData.Client? I have X509Certificate2 which i want to use while calling the api. I use .net framework 4.6.
I did some search and I came to know it is possible to add through HttpClientHandler. But I'm not able to figure out how to do that. Below is the code i have.
void foo()
{
var clientSettings = new ODataClientSettings("");
clientSettings.OnApplyClientHandler = new Action<HttpClientHandler>(AddClientCertificate);
var client = new ODataClient(clientSettings);
}
private void AddClientCertificate(HttpClientHandler handler )
{
// I have working code to retrieve the certificate.
X509Certificate2 targetCertificate = RetrieveCertificate();
//TODO : Add the certificate to the HttpClientHandler
}
Short:
Use the ODataClientSettings.OnCreateMessageHandler and return a WebRequestHandler and setting the ClientCertificates.
I have found the solution from this github issue:
Having looked at the code again what you need to do is assign a delegate to OnCreateMessageHandler rather than OnApplyClientHandler as the underlying code creates a HttpClientHandler and you need a WebRequestHandler e.g.
var setting = new ODataClientSettings(baseAddresss, credentials)
{
OnCreateMessageHandler = {
var handler = new WebRequestHandler();
handler.ClientCertificates.Add(certificate);
return handler;
}
}
Note that if you do this, it won't call OnApplyClientHandler so you will have to also allocate any other message handlers in this delegate.
I can't easily check this out since I don't have access to a certificate secured site, but there's nothing in the code to suggest this won't work.
Hope one of the below code snippets work fine!
X509Certificate2 targetCertificate = RetrieveCertificate();
handler.ClientCertificates.Add(targetCertificate);
var filePath = rootPath + #"/App_Data/apigee.pfx";
X509Certificate2Collection certificates = new X509Certificate2Collection();
certificates.Import(filePath, "test", X509KeyStorageFlags.MachineKeySet |
X509KeyStorageFlags.PersistKeySet);
httpClientHandler.ClientCertificates.AddRange(certificates);
I'm trying to fetch free / busy using EWS
Ive installed the latest nuget package Microsoft.Exchange.WebServices
I'm also setting everything I know of to ignore cert. errors:
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls | SecurityProtocolType.Tls11 | SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;
The code I'm using to fetch appointments:
//Set up the service with correct credentials.
var service = new ExchangeService(ExchangeVersion.Exchange2007_SP1)
{
Credentials = new WebCredentials(Account, Password, Domain),
TraceEnabled = true,
EnableScpLookup = false
};
service.Url = new Uri(ServiceUrl);
// Create a list of attendees.
var attendees = Contacts.Select(contact => new AttendeeInfo { SmtpAddress = contact.Email, AttendeeType = MeetingAttendeeType.Required }).ToList();
// Specify availability options.
var myOptions = new AvailabilityOptions
{
MeetingDuration = 30,
RequestedFreeBusyView = FreeBusyViewType.DetailedMerged,
DetailedSuggestionsWindow = new TimeWindow(DateTime.Now, DateTime.Now.AddDays(Days))
};
// Return a set of free/busy times.
var freeBusyResults = service.GetUserAvailability(attendees, new TimeWindow(DateTime.Now, DateTime.Now.AddDays(Days)),AvailabilityData.FreeBusyAndSuggestions, myOptions);
This code works for 5 out of 6 exchange servers I have, but one of them gives "The request failed. The request was aborted: Could not create SSL/TLS secure channel." error message.
If I set up fiddler to act as an proxy for the call, and tell fiddler to decrypt, everything works.
I just want to ignore ALL ssl errors and get the data, how do I do that?
The Managed API is just using HTTPWebRequest as the underlying class to do the Request/response. If it works with fiddler mostly likely your problem is environmental/Client related. I would suggest you enable tracing https://blogs.msdn.microsoft.com/dgorti/2005/09/18/using-system-net-tracing/ you should then be able to see what happens at the lower level when it fails.