I am trying to translate a PowerShell API call to C#. For some compatibility reasons I have to use .net framework 4.7.1.
When I call the API with PowerShell code I get a clear error message "No client certificate". But when I try to do the same thing in .net 4.7.1 I only get an html error page that is not even showing the error message. How do I get the error message?
Original Powershell code:
$outfile="C:\temp\OutFile.xml"
#Force Tls 1.2
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
#Clientcert
$Certificate = Get-ChildItem Cert:\LocalMachine\My\xxxxx
#$Certificate
$LicenseKey = "xxxxx"
Invoke-RestMethod -OutFile $outfile -Certificate $Certificate -Uri https://My-API-URL
LicenseKey=$LicenceKey
Powershell output:
Invoke-RestMethod : {"Status":401,"Message":"No client certificate."}
At C:\temp\Testing_API_call.ps1:14 char:1
+ Invoke-RestMethod -OutFile $outfile -Certificate $Certificate -Uri $U ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidOperation: (System.Net.HttpWebRequest:HttpWebRequest) [Invoke-RestMethod], WebException
+ FullyQualifiedErrorId : WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeRestMethodCommand
My C# code:
public string GetDataFromMyApi(string requestUri, X509Certificate2 certificate)
{
string result = null;
HttpResponseMessage response;
HttpClientHandler handler = new HttpClientHandler();
handler.ClientCertificates.Add(certificate);
using (HttpClient client = new HttpClient(handler))
{
try
{
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
response = client.GetAsync(requestUri).Result;
}
catch (Exception ex)
{
throw new Exception($"Exception when getting data from API. requestUri: {requestUri} Exception: {ex.Message}");
}
}
string responseContent;
if (response.IsSuccessStatusCode == false)
{
if (response.StatusCode == HttpStatusCode.BadRequest)
{
Console.WriteLine(response.ToString());
}
responseContent = response.Content.ReadAsStringAsync().Result;
throw new Exception($"API call did not return success code. ReasonPhase: {response.ReasonPhrase}. StatusCode: {response.StatusCode}. Response content: {responseContent}.");
}
else //response.IsSuccessStatusCode == true
{
try
{
result = response.Content.ReadAsStringAsync().Result;
}
catch (Exception ex)
{
throw new Exception($"API response could not be serialized. requestUri: {requestUri} Exception: {ex.Message}");
}
}
return result;
}
The response.ToString()
looks like this:
StatusCode: 400
ReasonPhrase: 'Bad Request'
Version: 1.1
Content: System.Net.Http.StreamContent
Headers:
{
X-XSS-Protection: 1; mode=block
X-Frame-Options: SAMEORIGIN
Feature-Policy: accelerometer 'none';camera 'none';geolocation 'none';gyroscope 'none';magnetometer 'none';microphone 'none';payment 'none';usb 'none'
Referrer-Policy: same-origin
Cache-Control: private
Date: Wed, 15 Feb 2023 14:03:35 GMT
P3P: CP=NID DSP NOI COR, policyref=/w3c/p3p.xml
Server: Microsoft-IIS/10.0
X-AspNet-Version: 4.0.30319
X-Powered-By: ASP.NET
Content-Length: 3525
Content-Type: text/html; charset=utf-8
}
The response content looks (a little simplified) like this:
<!DOCTYPE html>
<html>
<head>
<title>Runtime Error</title>
</head>
<body bgcolor="white">
<span>
<H1>Server Error in 'my.api' Application.<hr width=100% size=1 color=silver></H1>
<h2> <i>Runtime Error</i> </h2>
</span>
<b> Description: </b>An application error occurred on the server. The current custom error settings for this application prevent the details of the application error from being viewed remotely (for security reasons). It could, however, be viewed by browsers running on the local server machine.
<br>
<br>
<b>Details:</b> To enable the details of this specific error message to be viewable on remote machines, please create a <customErrors> tag within a "web.config" configuration file located in the root directory of the current web application. This <customErrors> tag should then have its "mode" attribute set to "Off".<br><br>
<b>Notes:</b> The current error page you are seeing can be replaced by a custom error page by modifying the "defaultRedirect" attribute of the application's <customErrors> configuration tag to point to a custom error page URL.<br><br>
<br>
</body>
</html>