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);
Related
I have an API Gateway that uses IAM authorization. I have a C# application that I'm hoping to call the API with. I started with a GetMethodRequest but I don't see anyway to set the PathPart parameter.
var userId = _remoteCredentials.UserId;
var key = _remoteCredentials.Key;
var client = new AmazonAPIGatewayClient(userId, key, Amazon.RegionEndpoint.USEast2);
GetMethodRequest getMethodRequest = new GetMethodRequest();
getMethodRequest.HttpMethod = HttpMethod.Get.ToString();
getMethodRequest.ResourceId = "4abcde";
getMethodRequest.RestApiId = "aasfasdfs";
var task = Task.Run(async () => await client.GetMethodAsync(getMethodRequest).ConfigureAwait(false));
I was expecting something like the Test-AGInvokeMethod in the Powershell SDK which allows me to set the query string and the path.
$response = Test-AGInvokeMethod
-RestApiId aasfasdfs
-ResourceId 4abcde
-HttpMethod GET
-PathWithQueryString '/etl/upload_url'
Any help is greatly appreciated.
EDIT Below is something of a solution that I ended up using the AWS4RequestSigner is a library that I found on Github
var signer = new AWS4RequestSigner(userId, key);
var destinationUrl = string.Format("https://ad9vxabc123.execute-api.us-east-2.amazonaws.com/dev/etl/summary/latest?tms_id={0}&model_id={1}", _tmsId, _modelId);
var request = new HttpRequestMessage
{
Method = HttpMethod.Get,
RequestUri = new Uri(destinationUrl),
};
var signed = Task.Run(async () => await signer.Sign(request, "execute-api", "us-east-2").ConfigureAwait(false));
var signedResult = signed.Result;
The AmazonAPIGatewayClient is for managing your API Gateway e.g. adding new stages or deleting API keys.
You're looking to invoke a method on your API Gateway, like Test-AGInvokeMethod does.
To invoke your API gateway, you need to call the deployed API endpoint using a HTTP client.
.NET's in-built HttpClient is a good start.
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 am trying to connect from a client to the service. The service is configurated to use a self signed Ssl certificate and I am trying to configurate the client with the client certificate. I am using this code:
string cacert = System.IO.File.ReadAllText("certificados/ca.crt");
string cert = System.IO.File.ReadAllText("certificados/client.crt");
string key = System.IO.File.ReadAllText("certificados/client.key");
KeyCertificatePair keypair = new KeyCertificatePair(cert, key);
SslCredentials sslCreds = new SslCredentials(cacert, keypair);
var channel = GrpcChannel.ForAddress("https://x.x.x.x:5001", new GrpcChannelOptions { Credentials = sslCreds });
var client = new Gestor.GestorClient(channel);
But I am getting the following error: using SslCredentials with non-null arguments is not supported by GrpcChannel.
I don't understand very good the message error. SslCredentials is ChannelCredentials? type, and SslCreds is Grpc.Core.SslCredentials. It can be compiled, so the type I guess it is correct.
What I would like to know it is how I can configure the client to use the self signed certificate that I have created.
Thanks.
The SslCredentials support in only available grpc-dotnet is to provide some level of compatibility with Grpc.Core in the most common use case, it doesn't expose all the functionality though. In grpc-dotnet, only SslCredentials() (parameterless which uses the default roots) is supported. If you want to provide your self-signed creds, you can certainly do that, you'll need to use a different API for configuring GrpcChannel:
See example here (creating a GrpcChannel with custom credentials).
https://github.com/grpc/grpc-dotnet/blob/dd72d6a38ab2984fd224aa8ed53686dc0153b9da/testassets/InteropTestsClient/InteropClient.cs#L170
I spend a fair bit of time googling around for solutions to this problem, and didn't find a concise answer. Here is ultimately how I was able to configure a dotnet client to use mutual SSL authentication:
MyService.MyServiceClient GetClient(){
var httpClientHandler = new HttpClientHandler();
// Validate the server certificate with the root CA
httpClientHandler.ServerCertificateCustomValidationCallback = (message, cert, chain, _) => {
chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;
chain.ChainPolicy.CustomTrustStore.Add(new X509Certificate2("ca.crt"));
return chain.Build(cert);
};
// Pass the client certificate so the server can authenticate the client
var clientCert = X509Certificate2.CreateFromPemFile("client.crt", "client.key");
httpClientHandler.ClientCertificates.Add(clientCert);
// Create a GRPC Channel
var httpClient = new HttpClient(httpClientHandler);
var channel = GrpcChannel.ForAddress("https://localhost:8080", new GrpcChannelOptions{
HttpClient = httpClient,
});
return new MyService.MyServiceClient(channel);
}
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?
Am trying to query an odata web.api hosted on IIS7. The site requires a client cert. How do I attach the certificate to the query? Using web.api 2, framework 4.5, mvc5
string certPath = #"E:\ClientCertificate.cer";
Uri uri = new Uri("https://server/odata/");
var container = new CourseService.Container(uri);
container.ClientCertificate = new X509Certificate(certPath);
The extension to the container class was achieved by reading this:
http://bartwullems.blogspot.co.uk/2013/03/odata-attach-client-certificate-through.htm
You could attach the certificate to request in SendRequest2 event yourself:
context.SendingRequest2 += (sender, eventArgs) =>
{
// We can safely cast RequestMessage to HttpWebRequestMessage if this is not in batch.
if (!eventArgs.IsBatchPart)
{
((HttpWebRequestMessage)eventArgs.RequestMessage).HttpWebRequest.ClientCertificates.Add(theCertificate);
}
};