How to check that a certificate was applied in a named HttpClient? - c#

I have registered a named HttpClient in my app. I would like to add a test before i use this client to ensure that it has the certificate applied.
var clientCertificate = new X509Certificate2(pathToCert, passwordToCert);
var handler = new HttpClientHandler();
handler.ClientCertificates.Add(clientCertificate);
services.AddHttpClient(name, client =>
{
client.BaseAddress = new Uri(hostName);
client.DefaultRequestHeaders.Add("Accept", "application/json");
client.DefaultRequestHeaders.Add("User-Agent", userAgent);
}).ConfigurePrimaryHttpMessageHandler(() => handler)
.UseHttpClientMetrics()
.SetHandlerLifetime(TimeSpan.FromMinutes(5)) //Set lifetime to five minutes
.AddPolicyHandler(RetryPolicy.GetRetryPolicy());
The issue is I cant seem to find it anywhere in the client object that says it has the message handler.
_client = httpClientFactory.CreateClient(_settings.Name);
I dont seem to have access to any of the parameters that start with _

It's not possible out of the box, but you can use the FieldInfo.class from System.Reflections to get the needed information:
_client = httpClientFactory.CreateClient(_settings.Name);
var handler = _client.BaseType.GetField("_handler", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(_client) as HttpClientHandler;
With that procedure, you should be able to get the other fields, which are not accessable by default, too. You just need to make sure, from which class/type the property is from.

Related

.Net Core + Kubernettes > AuthenticationException: The remote certificate is invalid according to the validation procedure

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

Azure App Service: Is it possible to enable ARR Affinity for specific requests?

I have stateless web applications deployed to Azure App Service and have ARR Affinity disabled in the Application Settings. Is it possible to leave this disabled, but enable it for specific requests?
I came across a post by benjaminperkins from 2016 that indicated it was possible to disable this for individual requests by adding a "Arr-Disable-Session-Affinity" header, but I would like the reverse of this, "Arr-Enable-Session-Affinity".
I would like to be able to make requests to individual instances to warm-up an in-memory cache following a remote operation. I understand how to build a URL request to an App Server web app instance when ARRAffinity is enabled, but this won't work for me as I do not want to enable this globally.
Here is an example of what I would like to do:
ServiceClientCredentials serviceCreds =
await ApplicationTokenProvider.LoginSilentAsync(this.config.ADTenant, this.config.ADApplicationId, this.config.ADKey);
ResourceManagementClient resourceClient = new ResourceManagementClient(serviceCreds);
resourceClient.SubscriptionId = this.config.SubscriptionId;
WebSiteManagementClient webClient = new WebSiteManagementClient(serviceCreds);
webClient.SubscriptionId = this.config.SubscriptionId;
string urlPath = "custompath/";
SiteInner site =
webClient.WebApps.List().Where(a => a.Id == "webapp id").FirstOrDefault();
IList<SiteInstanceInner> instances =
webClient.WebApps.ListInstanceIdentifiers(site.ResourceGroup, site.Name).ToList();
SiteInstanceInner instance = instances.FirstOrDefault();
// these are initialized as a singleton, inline here as an example
HttpClientHandler handler = new HttpClientHandler() { UseCookies = false };
HttpClient httpClient = new HttpClient(handler, disposeHandler: false);
httpClient.Timeout = new TimeSpan(0, 0, 30);
webClient = await GetWebsiteClientIfNull(webClient);
Uri url = new Uri($"http://{site.DefaultHostName}/{urlPath}");
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, url);
message.Headers.Add("Arr-Enable-Session-Affinity", bool.TrueString);
message.Headers.Add("Cookie", $"ARRAffinity={WebUtility.UrlEncode(instance.Name)};");
HttpResponseMessage response = await webClient.HttpClient.SendAsync(message);
Is there a way to do this when ARRAffinity is disabled on the App Service?

create proxy in c# and use in httpclient

i am using Instasharper (private Instagram API).
my app is Instagram bot for follow and unfollow ,
and i read some where that for one ip there is limitation,
that we can only use 5 account login for one IP in Instagram.
but i have 300-500 user.
should i use proxy?
i dont know how to create proxy for each user and use it.
this is how we create instagram private api and use it
var api = new InstaApiBuilder()
.UseLogger(new SomeLogger())
.SetUser(new UserCredentials(...You user...))
.UseHttpClient(httpHandlerWithSomeProxy)
.Build();
i should create proxy and use it in httpclient
.UseHttpClient(httpHandlerWithSomeProxy)
can u give me some links or helps
tanks
I have solved something similar before by creating an http client with a custom message handler.
Am I right in thinking the parameter to UseHttpClient is an HttpClient?
If so,
e.g.
public class MyHttpMessageHandler : System.Net.Http.HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(
HttpRequestMessage request,
CancellationToken cancellationToken)
{
//...(your implementation here)
}
}
...then:
MyHttpMessageHandler myMessageHandler = new MyHttpMessageHandler();
HttpClient httpHandlerWithSomeProxy = new HttpClient(myMessageHandler);
var api = new InstaApiBuilder()
.UseLogger(new SomeLogger())
.SetUser(new UserCredentials(...You user...))
.UseHttpClient(httpHandlerWithSomeProxy)
.Build();
of course you can pass parameters to MockHttpMessageHandler's constructor if you want to give it some data.
i do this and i think its right way
and i should this for every user
but how can i find proxy adress and port. can anybody explain that to me
tanks
InstaApi _instaApi;
var userSession = new UserSessionData
{
UserName = "",
Password = ""
};
HttpClientHandler handler = new HttpClientHandler()
{
Proxy = new WebProxy("http://127.0.0.1:8888"),
UseProxy = true,
};
_instaApi = new InstaApiBuilder()
.SetUser(userSession)
.UseHttpClientHandler(handler)
.Build();

How to use certificate authentication in Simple.OData.Client?

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);

How do I add client certificates in Web API [duplicate]

I want to test my Web API service using in-memory HttpServer.
The current setup looks the following:
var httpConfig = CreateTestHttpConfiguration();
var server = new HttpServer(httpConfig);
var handler = new WebRequestHandler();
var cert = new X509Certificate2("filename", "password");
handler.ClientCertificates.Add(cert);
var client = HttpClientFactory.Create(handler, server);
I can make requests to the server using these client and everything works except that certificate is not added to the request.
As I understand that happens since server executes before handler (I can't rearrange them since they implement different interfaces) and since server immediately responses handler is not even executed (I've tested this assumption using HttpClientHandler subclass instead of handler).
So my question is: How can I add the client certificate for in-memory testing?
This approach will do it:
var server = new HttpServer(configuration);
var invoker = new HttpMessageInvoker(server);
var certificate = GetCertificate();
var request = new HttpRequestMessage(HttpMethod.Get, "http://localhost/YourPath");
request.Properties[HttpPropertyKeys.ClientCertificateKey] = certificate;
var result = await invoker.SendAsync(request, CancellationToken.None);

Categories