HttpRequestMessage.GetClientCertificate() returns null in ASP Web API DelegatingHandler - asp.net

I have an ASP Web Api (.Net Framework 4.6.1) which accepts client certificates. The requirement is to send a custom validation message in the response of a request that has an invalid certificate.
For example, if the certificate is missing I should send back "Client certificate is missing", if the OCSP validation fails, I should send back "Certificate has been revoked", etc.
This is the code:
public class CertificateMessageHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var certificate = request.GetClientCertificate();
}
}
I have a client application where I select what certificate I want to use, and it does a request to the web api application (which is hosted on another machine). If the certificate is valid, then request.GetClientCertificates() returns the certificate, otherwise, if the certificate is expired or self-signed, request.GetClientCertificates() return null.
I have disable the automatic CLR validation by the IIS:
netsh http show sslcert
netsh http delete sslcert ipport=0.0.0.0:443
netsh http add sslcert ipport=0.0.0.0:443 e104e... appid={4dc3e181-...} certstorename=My verifyclientcertrevocation=disable
I have set:
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\HTTP\Parameters\SslBindingInfo\0.0.0.0:443\DefaultSslCertCheckMode=1
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\SecurityProviders\SCHANNEL\SendTrustedIssuerList=0
None of the above settings worked.
Note: a 3rd party that uses the web app might send a self-signed certificate and the business logic should reject the request for such certificates, therefore, the inclusion of the CA, that was used to sign the certificate, in the Trusted Root Store, isn't possible.
Any help is appreciated on how to get the client certificate from the request.
EDIT: it seems that the module "IIS Web Core" validates the certificate against the certificates store way before the request is "forwarded" by IIS to my application:

You could try to check the below settings:
set the iis SSL setting to accept:
and set below code in web.config file:
<iisClientCertificateMappingAuthentication enabled="true">
</iisClientCertificateMappingAuthentication>
Edit:
Asp.net Core is a framework and Hostable Web Core (known as HWC) is a new concept in IIS to host a website/web services inside your own process. In short a smaller hosted version of IIS (an IIS express edition?).
This is accomplished by making a LoadLibrary call to load hwebcore.dll (%systemdrive%\Windows\System32\inetsrv\hwebcore.dll)
Try to disable the Hostable Web Core feature by following below steps:
Open control panel.
Click on “Turn Windows features on or off” from the left pane.
Locate Internet Information Services(IIS) Hostable Web Core from the list and uncheck the checkbox.
restart iis after doing changes.
https://blogs.iis.net/sukesh/iis7-hosted-web-core-custom-service-webcoreservice
refer this below links or more detail:
HttpRequestMessage.GetClientCertificate() returns null in Web API
How to use a client certificate to authenticate and authorize in a Web API
Client Authentication for WebAPI 2

Related

Azure AD reply url failing on html handler

Via ASP.NET I have created a startup file that will use Azure AD to log in a user
e.g.
public void Configuration(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
{
ClientId = "42067b8d-b972-44e9-af86-ef60bc6d6fdb",
Authority = "https://login.windows.net/...com",
RedirectUri = "http://localhost:50560/content/story_html5.html",
PostLogoutRedirectUri = "http://localhost:50560/content/story_html5.html",
Scope = OpenIdConnectScope.OpenIdProfile,
ResponseType = OpenIdConnectResponseType.IdToken
});
}
And as you can see my RedirectUri in hitting a static file html file.
On my app registration in Azure portal my manifest for the replyUrls states
"replyUrls": [
"http://localhost:50560/content/story_html5.html"
],
So everything is working and connecting correctly.
(if I use a aspx for example the redirection would work)
However using the .html file I'm getting the error
HTTP Error 405.0 - Method Not Allowed
The page you are looking for cannot be displayed because an invalid
method (HTTP verb) is being used.
All I believe I need to do is add the html handler to Azure AD, does anyone know how to do this?
Thanks
This has nothing to do with Azure AD, but your configuration. Your end. Your Project. Your IIS config. Because sign-in response is a HTTP POST for security reasons. And static files handler in IIS does not accept anything beside GET for obvious reasons.
More information you will find here and there.
First, why would you want to redirect to a static page?! With the redirection after OIDC login, the IdP (Identity Provider, understand Azure AD in that case) sends valuable information which is needed by the OIDC middleware (understand the .UseOpenIdConnectAuthentication method) to be able to verify the token and initialize user session. By sending the sign-in response back to a static page you accomplish couple of things:
You cut out the OIDC middleware from the authentication - it is no longer able to process the response. Because it will not listen on static file requests. Static files are processed outside your OWIN authentication middleware.
Thus not able to verify authenticity of the user.
Thus not able to create secure cookie.
Thus not able to sign-in the user into your application.
Conclusion
Do not change the reply URL for your ASP.NET middleware, unless you explicitly and knowingly want to override the complete handling of sign-in responses.

.Net Core Web API with Client Certificate Authentication

I've developed a simple WEB API service in .Net Core 2.1
I'm trying to implement a client certificate authentication, so I can give access to the APIs only to the clients that have a specific certificate installed on their machine.
The clients access the API using a browser (Chrome, Edge, IE11 or Firefox).
I've added in the API method the request for the certificate:
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
X509Certificate2 cert = Request.HttpContext.Connection.ClientCertificate;
if (cert!=null && cert.Verify())
{
//more verification here...
return Content("....", "application/json");
}
else
{
return Content("....", "application/json");
}
}
then I've installed a self-signed certificate and added to the Trusted Root, enabling the Client Authentication purpose.
but the variable cert is always null and the browser didn't even prompt me to use a certificate when I request the page.
I suppose because I have to set somewhere that the web server must ask for the client certificate as it is possible to set in IIS, but in my development environment, I'm using IIS Express.
How can I force IIS express to request a client certificate?
For proper certificate authentication using the ASP.NET Core authentication stack, you can also check out idunno.Authentication.Certificate by Barry Dorrans himself. It allows you to enable certificate authentication for your application and handles it like any other authentication scheme, so you can keep actual certificate-based logic out of your business logic.
This project sort of contains an implementation of Certificate Authentication for ASP.NET Core. Certificate authentication happens at the TLS level, long before it ever gets to ASP.NET Core, so, more accurately this is an authentication handler that validates the certificate and then gives you an event where you can resolve that certificate to a ClaimsPrincipal.
You must configure your host for certificate authentication, be it IIS, Kestrel, Azure Web Applications or whatever else you're using.
Make sure to also check out the “documentation” on how to set this up properly, since it requires configuration of the host to work properly, just like you did with IIS Express. Instructions for other servers like raw Kestrel, IIS, Azure or general reverse proxies are included.
In order to enable IIS Express to start requesting client certificates and therefore pass them to the server side, the configuration file must be edited:
The whole configuration is inside the solution folder in the .vs\config\applicationhost.config
Ensure the following values are set:
<security>
<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />
and
<iisClientCertificateMappingAuthentication enabled="true"></iisClientCertificateMappingAuthentication>
For local testing, you can enable SSL in IIS Express from Visual Studio. In the Properties window, set SSL Enabled to True. Note the value of SSL URL; use this URL for testing HTTPS connections.
For Who needs
Details here
For .NET 3.1+ there is now official package supporting this feature:
builder.Services.AddAuthentication(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService = context.HttpContext.RequestServices
.GetRequiredService<ICertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
var claims = new[]
{
new Claim(
ClaimTypes.NameIdentifier,
context.ClientCertificate.Subject,
ClaimValueTypes.String, context.Options.ClaimsIssuer),
new Claim(
ClaimTypes.Name,
context.ClientCertificate.Subject,
ClaimValueTypes.String, context.Options.ClaimsIssuer)
};
context.Principal = new ClaimsPrincipal(
new ClaimsIdentity(claims, context.Scheme.Name));
context.Success();
}
return Task.CompletedTask;
}
};
});
There is also configuration required on the server side, see:
https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-6.0

Client Certificate Authentication in .NET (Windows Server)

I have a very weird issue with a Web Application I have deployed.
Relevant Data:
Application Server: IIS 7.5
Server: Windows Server 2008 R2 Standard SP1
Framework: ASP.NET
.NET Framework: 4 (4.0.30319)
Application Pool: Integrated
I the web application I make use of a service that's authenticated with Client Certificate Authentication. I don't have problems relating to the authentication itself (it is working on my development environment). But I am seeing problems whenever I want to use the service from the server (production) environment.
Here's the relevant portion of the CODE:
private void SetupClienteCertificate(HttpWebRequest req)
{
var binding = new BasicHttpBinding();
binding.Security.Mode = BasicHttpSecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Certificate;
var crt = new X509Certificate2(
txtClientCertificateFile.Text,
txtCertificatePassword.Text,
X509KeyStorageFlags.MachineKeySet
);
req.ClientCertificates.Add(crt);
}
The error I am getting is pretty common and self explanatory:
The request was aborted: Could not create SSL/TLS secure channel.
The very weird part of it is that if I run the exact same code from a C# .Net Windows Forms Desktop Application (with the same .NET 4 framework) I can get the code to communicate with the server.
So my question is: Why is it working from the Desktop Application and not working from the ASP.NET Web Application?
Some stuff that I've already made sure of:
SSLv3, TLS, TLSv1, TLSv2 are enabled in the registry
I am ignoring SSL CERTIFICATE ERRORS (not necessary, but JUST IN CASE)
Restarted the Application Pool every time I change something configuration wise.
Any ideas?
I think I get your problem. Can you ensure the account under which application pool is running have sufficient privilege to read certificate from certificate
store.

Unable to set proxy for SignalR HubConnection

Using version 2.0 for Asp.NET SignalR, we have created a prototype application that has a WPF client application and a web site that has SignalR configured. This prototype works correctly when run on the local developer computer and when the web site was deployed to an internal development server.
An issue that has been encountered once the web site was deployed to an external server; the following exception is encountered when the HubConnection.Start method is called:
HttpClientException
A first chance exception of type 'Microsoft.AspNet.SignalR.Client.HttpClientException' occurred in mscorlib.dll
Additional information: StatusCode: 407, ReasonPhrase: 'Proxy Authentication Required ( Forefront TMG requires authorization to fulfill the request. Access to the Web Proxy filter is denied…
The network that the developer computer is on requires the use of a proxy to reach the Internet. The web site that has the SignalR component also has some WCF endpoints; these can be connected to using the HttpClient within the WPF client application when the proxy is set in code. The same approach to set the proxy was done on the HubConnection but the error is encountered.
Below is code on how the proxy is set to the HubConnection; the same credentials work when accessing the other, non-signalR, endpoints:
var proxyInfo = new WebProxy(new Uri(“theAddress”));
proxyInfo.Credentials = new NetworkCredential(“theUserName”, “thePassword”, “theDomain”);
hubConnection.Proxy = proxyInfo;
Is there something else that has to be set with the HubConnection for it to use the proxy?
Thanks,
Scott
The issue is that there is a bug with the 4.5 .NET Client for SignalR; the proxy information is not being sent with the requests in the HubConnection. This is a regression from the 1.0 release.
The link below contains the information:
https://github.com/SignalR/SignalR/issues/2856

HTTP Error 401.1 when using WinHttp.WinHttpRequest.5.1 in classic ASP site

General information
Operating System: Windows Server 2003 R2 Service pack 2
Webserver: IIS 6
NTAuthenticationProviders: NTLM only
Webapplication: Classic ASP
Browsers used: IE7, IE8, IE9
There’s a Classic ASP web application called knowledgebase, within an IIS website called eblcplaza like so: eblcplaza/knowledgebase/.
eblcplaza has anonymous access AND Integrated Windows Authentication enabled.
knowledgebase has anonymous access disabled and Integrated Windows Authentication enabled
knowledgebase is a Classic ASP application has its own Application pool which runs under the predefined Application pool identity “Network service”
When I’m logged in with my NT account I can access any page I want just fine. The problem is with the WinHttp.WinHttpRequest.5.1 component. It’s used in some parts of knowledgebase to do a server side request to retrieve content from some .asp scripts which reside within the web application.
The problem started when Anonymous access was turned off on knowledgebase . Note, turning it back on is not an option.
Example of a request using WinHttpRequest:
set WinHTTPRequest = Server.CreateObject("WinHttp.WinHttpRequest.5.1")
WinHTTPRequest.SetTimeouts 20000, 20000, 20000, 20000
call WinHTTPRequest.Open("POST", someUrlToAspScript, false)
WinHTTPRequest.SetAutoLogonPolicy 0
WinHTTPRequest.setRequestHeader "Content-Type", "application/x-www-form-urlencoded"
WinHTTPRequest.Send strQueryString
Response.Write(WinHTTPRequest.ResponseText)
With SetAutoLoginPolicy set to 0, I get the following error message on the pages where WinHttpRequest is used:
You do not have permission to view this directory or page using the credentials that you supplied.
HTTP Error 401.1 - Unauthorized: Access is denied due to invalid credentials.
Internet Information Services (IIS)
With SetAutoLoginPolicy set to 2 (Do not automatically send user credentials according to MSDN), I get the following error message on the pages where WinHttpRequest is used:
You do not have permission to view this directory or page using the credentials that you supplied because your Web browser is sending a WWW-Authenticate header field that the Web server is not configured to accept.
HTTP Error 401.2 - Unauthorized: Access is denied due to server configuration.
I know for a fact that my NT user account has the proper rights to access those .asp scripts and so does the Network Service account.
I tried figuring out what could be the problem for several days know, tried setting the NTAuthenticationProviders to only NTLM and both Negotiate and NTLM amongst other things, but nothing worked so far.
Please help me out, It’s starting to drive me crazy.
Regards,
Bart
I guess the pages in knowledgebase are accessed with the anonymous account where you start from at eblcplaza. Try to enable NTLM only on the page in eblcplaza where you use the request, you can do that on that file only. Like that your credentials get passed to knowledgebase. On both pages log the Session("username") variable.
First of all let's clear up what it is you asking the server to do. It will have demanded your credentials from the client with which it is now impersonating you for security purposes. The WinHTTP request it is making to a service (WinHTTP doesn't know that its the exact same application) that now demands credentials. What you want this impersonating thread to do is use your creds to authenticate against an "external" service.
I suspect that what is happening here is that the server is not cleared to re-use your credentials in this way. If I recall correctly (which may not be that certain) a server needs to be granted the right to delegate in order to do that. It may also be possible to allow this if Kerberos is used instead of NTLM to perform windows integrated security.
However all that may be academic. You should understand that an app making a http request to itself has the potential to hang when under load in a way that would require a recycle to release.
Consider this alternative. Given that ServicePage.asp is a page used both directly by the browser and by an internal ClientPage.asp do the following.
Rip out the service code from ServicePage.asp and place in a VBScript class in a new ServiceInclude.asp. Now add the this ServiceInclude.asp as an include file in ServicePage.asp where ServicePage.asp only contains the plumbing necessary to instance the class and use it to generate its output.
Modify ClientPage.asp so that instead of attempting WinHttp to ServicePage.asp it simply includes the ServiceInclude.asp, instances the contained class and uses the class to provide the service required.

Resources