EDIT: This code does work. I had the wrong value in the Entity List Environment Variable
I've been trying to add certificates to the HTTP client factory using dependency injection. I will need to use many different certificates to connect to the 3rd party depending on the API I need to connect to and my certificates are stored in my Azure Key Vault.
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddHttpClient();
builder.Services.AddSingleton<HTTPClientService>();
string entityList = Environment.GetEnvironmentVariable("EntityList");
foreach (var entity in entityList.Split(','))
{
builder.Services.AddHttpClient(entity).ConfigurePrimaryHttpMessageHandler(() =>
{
var kvClient = new CertificateClient(new Uri(Environment.GetEnvironmentVariable("KeyVaultUri")), new DefaultAzureCredential());
var entityCert = kvClient.DownloadCertificate(entity);
HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificateOptions = ClientCertificateOption.Manual;
handler.ClientCertificates.Add(entityCert);
return handler;
});
}
}
I've tried many examples I have found on here and the pattern I've used works locally but by loading the certificates from file as my work's policy does not allow us to access the key vault locally.
When I run this code in Azure after injecting the HTTP Client Factory into the function:-
HttpClient client = _httpClientFactory.CreateClient(entityName);
string uri = string.Format(Settings.Uri, entityName);
var content = new ByteArrayContent(Encoding.UTF8.GetBytes(jsonContent));
var res = httpClient.PostAsync(uri, content);
I get the following error on PostAsync
System.Net.Http.HttpRequestException: The SSL connection could not be established, see inner exception.
---> System.IO.IOException: Received an unexpected EOF or 0 bytes from the transport stream.
Which suggests that the named HTTP Client has been created, but the certificate has not been added to the client.
Are the KeyVault or Environment Variables not available when the injection runs or is there a different way I should be doing this?
If I create the HTTPClient
within the function body (i.e. without using dependency injection) and download the certificates, I am able to connect and post data.
Turns out it is human error due to unfamiliarity with the
HTTPClientFactory
. TheEntityList
I had was not correct and I wrongly assumed that if you call theCreateClient(string name)
method, it would throw an exception if you ask for one that has not previously been defined.