IIS couldn't find a valid certificate with subject - asp.net

I deploy the ASP.NET Angular SPA template RC2 app with the Identity Server option on my local IIS server. I get below exception. IIS can't access my local certification info.
Application: w3wp.exe
CoreCLR Version: 6.0.21.52210
.NET Version: 6.0.0
Description: The process was terminated due to an unhandled exception.
Exception Info: System.InvalidOperationException: Couldn't find a valid certificate with subject 'CN=my-subdomain.azurewebsites.net' on the 'CurrentUser\My'
at Microsoft.AspNetCore.ApiAuthorization.IdentityServer.SigningKeysLoader.LoadFromStoreCert(String subject, String storeName, StoreLocation storeLocation, DateTimeOffset currentTime)
at Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ConfigureSigningCredentials.LoadKey()
at Microsoft.AspNetCore.ApiAuthorization.IdentityServer.ConfigureSigningCredentials.Configure(ApiAuthorizationOptions options)
at Microsoft.Extensions.Options.OptionsFactory`1.Create(String name)
at Microsoft.Extensions.Options.UnnamedOptionsManager`1.get_Value()
at Microsoft.Extensions.DependencyInjection.IdentityServerBuilderConfigurationExtensions.<>c.<AddClients>b__8_1(IServiceProvider sp)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
I created a certificate via
New-SelfSignedCertificate -DnsName "my-subdomain.azurewebsites.net" -CertStoreLocation "cert:\CurrentUser\My"
I set my appsettings.json as
"IdentityServer": {
"Key": {
"Type": "Store",
"StoreName": "My",
"StoreLocation": "CurrentUser",
"Name": "CN=my-subdomain.azurewebsites.net"
},
"Clients": {
"MarketPlace": {
"Profile": "IdentityServerSPA"
}
}
When I publish this site with the same configuration as folder publish, it works properly. How can I fix the IIS error so it can access the certificate?

I had a similar issue with .NetCore web API (.NetCore 5.0) implemented using CLEAN architecture (CQRS pattern), I needed 2 certificates to run my application.
CA issued certificate for external communication outside the scope of the Application Level
Self-Signed for communication within the Application Level, for example, communication with other services/class library services or maybe to DB.
(For internal communication app will look for a certificate created at the Domain level or Server level)
So in the app setting production JSON, we need to point to a self-signed certificate, while publishing in the application in IIS we need to select CA issued certificate from the SSL Certificate dropdown in "Edit Binding" screen.
point 1: CA issued certificate shall be imported to IIS.
point 2: while self-signed shall be present in MMC, personal or root or web hosting, In local machine or current user, the same shall be referred in app setting production JSON.
Clean Architecture:
https://github.com/jasontaylordev/CleanArchitecture

Related

Finding expired ssl certificate used by my website in local development

I have a set of web application and projects which I used to test OAuth2 login flows by using Identity Server 4. This is about 2 years old project and I update it regularly to use latest .Net Core or IdentityServer4 packages.
When I tried to open it yesterday, I got the warning message saying my ssl certificate is expired on 21st Dec 2021.
It makes sense and I opened Certificate Management Stores for both Local Machine and Current User. I found the expired certificate and I removed them from both Personal and Trusted Root Certification Authorities.
Then, I executed "dotnet dev-certs https --trust" command to execute a new cert and install it to my stores. The new certificate with expiry date 07/01/2023 is added and I copied to
Trusted Root Certification Authorities too.
I tried to open my Idsrv and Web Application again for testing.
My Web Application is using the newly added certificate correctly.
But my Idsrv4 project is still using the expired certificate which I already deleted. I couldn't find anywhere in the Certificate Store. I used Find feature in MMC Consoles and I found only the new certificate with 2023 Expiry Date.
I cleaned my solution multiple time. Deleted both bin and obj folders. Tried different browsers and the certificate error is still there.
My set up for Idsrv is simple and standard:
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(Config.GetUsers())
.AddInMemoryIdentityResources(Config.GetIdentityResources())
.AddInMemoryApiResources(Config.GetApiResources())
.AddInMemoryApiScopes(Config.GetApiScopes())
.AddInMemoryClients(Config.GetClients())
.AddProfileService<ProfileService>();
Here is Configure method:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseIdentityServer();
var localizationOptions = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value;
app.UseRequestLocalization(localizationOptions);
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
Could you please help me how I can force my Identity Server 4 project to use the newly generated certificate? Or where I can find and remove that old expired certificate?
I guess there is a difference when you run kestrel directly, or when you run it through IIS-Express. Try with Kestrel directly?
Otherwise, look at this article that might help you:
How to Change the HTTPS Certificate in IIS Express
I found the root cause of the problem. I'm using Kestrel to run the app and it's due to the Kestrel:Certificates:DevelopmentPassword value in the secrets file.
Because of that setting, it's still linked to the expired certificate which must be somewhere in my machine. Just by deleting this line and my web app starts using the correct ssl certificate.
Updating HTTPS Certificate IIS Express or using Jexus Manager also helps the problem (does not solve completely). But it solves the issue only when you are using IISExpress. If you start your web app with Project setting, IISExpress ssl configs are not used.

How to specify server certificate for a self-contained Kestrel/Blazor application deployed on a production server?

I created a Blazor application to be run using Kestrel (.Net core 3.1).
Import NuGet package Microsoft.AspNetCore.Authentication.Negotiate
Add the following code in ConfigureService() in Startup.cs.
services.AddAuthentication(NegotiateDefaults.AuthenticationScheme).AddNegotiate();
services.AddSingleton<ValidateAuthentication>();
Add the following code in Configure() in Startup.cs. They are added between app.UseRouting(); and app.UseEndpoints(...;
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<ValidateAuthentication>();
Add the class
internal class ValidateAuthentication : IMiddleware
{
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.User.Identity.IsAuthenticated)
await next(context);
else
await context.ChallengeAsync();
}
}
Add the following code in the Program.cs (To make sure the remote machine can access the website)
webBuilder.UseUrls(new string[] { "https://0.0.0.0:5001", "http://0.0.0.0:5000" });
I published (as self hosted) the application to a local folder and it works fine on my PC. Running .\myApp.exe and then browsing http://localhost.5000 will redirect to https://localhost:5001 and show the page.
Then the published folder was copied to a Windows 2012 Server. However, running the application gets error:
PS C:\Website\Portal> .\MyApp.exe
crit: Microsoft.AspNetCore.Server.Kestrel[0]
Unable to start Kestrel.
System.InvalidOperationException: Unable to configure HTTPS endpoint. No server certificate was specified, and the defau
lt developer certificate could not be found or is out of date.
To generate a developer certificate run 'dotnet dev-certs https'. To trust the certificate (Windows and macOS only) run
'dotnet dev-certs https --trust'.
For more information on configuring HTTPS see https://go.microsoft.com/fwlink/?linkid=848054.
at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions, Action`1 configure
Options)
at Microsoft.AspNetCore.Hosting.ListenOptionsHttpsExtensions.UseHttps(ListenOptions listenOptions)
at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext con
text)
How to specify a server certificate for the application?
This Microsoft Docs article explains how to startup an ASP.Net Core app with certificates when it ships as a Docker container. You basically set ennvironment variables, e.g.
ASPNETCORE_URLS
ASPNETCORE_HTTPS_PORT
ASPNETCORE_Kestrel__Certificates__Default__Path
ASPNETCORE_Kestrel__Certificates__Default__Password
I guess you may do the same on the host directly but keep in mind that those settings won't be isolated and may affect other apps. Also note that you would be storing a password in an environment variable. I also found this article explaining how to configure Kestrel via the launch settings json.
I came across this question by accident. Solution is not tested. Just replying since there is no better suggestion yet.

ASP.NET Core Azure AD OpenID - Token Signature validation fails on production server

I have an ASP.NET Core 1.1 project, with authentication using Azure AD OpenID. Authentication works fine on my dev box (windows 10), both on IIS express and full IIS. However, when I deploy the project to my server (Server 2008R2), after the after logging into the Microsoft redirect site, the server throws this error:
Microsoft.IdentityModel.Tokens.SecurityTokenInvalidSignatureException: IDX10503: Signature validation failed. Keys tried: 'Microsoft.IdentityModel.Tokens.X509SecurityKey , KeyId: VWVIc1WD1Tksbb301sasM5kOq5Q
Internal.Cryptography.CryptoThrowHelper+WindowsCryptographicException: An internal error occurred
at Internal.Cryptography.Helpers.OpenStorageProvider(CngProvider provider)
at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, String curveName, CngKeyBlobFormat format, CngProvider provider)
at System.Security.Cryptography.CngKey.Import(Byte[] keyBlob, CngKeyBlobFormat format)
at Internal.Cryptography.Pal.X509Pal.DecodePublicKey(Oid oid, Byte[] encodedKeyValue, Byte[] encodedParameters, ICertificatePal certificatePal)
at Internal.Cryptography.Pal.CertificateExtensionsCommon.GetPublicKey[T](X509Certificate2 certificate, Predicate`1 matchesConstraints)
at Microsoft.IdentityModel.Tokens.X509SecurityKey.get_PublicKey()
at Microsoft.IdentityModel.Tokens.CryptoProviderFactory.IsSupportedAlgorithm(String algorithm, SecurityKey key)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(Byte[] encodedBytes, Byte[] signature, SecurityKey key, String algorithm, TokenValidationParameters validationParameters)
at System.IdentityModel.Tokens.Jwt.JwtSecurityTokenHandler.ValidateSignature(String token, TokenValidationParameters validationParameters)
When I take the token received from Azure, and plug it into JWT.io, it verifies the signature successfully.
To recap:
dev box (iis express and iis) token signature verifies successfully
JWT.io debug site, token and signature verifies successfully
deployed on server (iis 7.5) signature verification fails
Does it have anything to do with the https cert I have on the server? On my dev box I am using a self-signed cert created with powershell. On the server, we have a cert issued from go-daddy.
Thanks for any help.
This exception can also occur when the 'CNG Key isolation' Windows service is not running.
See https://blogs.msdn.microsoft.com/dsnotes/2012/08/17/cngkey-import-throws-an-exception-saying-an-internal-error-occurred-when-importing-a-key-blob-under-a-non-admin-user-from-a-wcf-web-service/
The issue was the identity running the application pool. Not high enough rights.

AspNet.Security.OpenIdConnect.Server (ASP.NET vNext) Authority Configuration in Mixed http/https Environments

I am using Visual Studio 2015 Enterprise and ASP.NET vNext Beta8 to build an endpoint that both issues and consumes JWT tokens as described in detail here. As explained in that article the endpoint uses AspNet.Security.OpenIdConnect.Server (AKA OIDC) to do the heavy lifting.
While standing this prototype up in our internal development environment we have encountered a problem using it with a load balancer. In particular, we think it has to do with the "Authority" setting on app.UseJwtBearerAuthentication and our peculiar mix of http/https. With our load balanced environment, any attempt to call a REST method using the token yields this exception:
WebException: The remote name could not be resolved: 'devapi.contoso.com.well-known'
HttpRequestException: An error occurred while sending the request.
IOException: IDX10804: Unable to retrieve document from: 'https://devapi.contoso.com.well-known/openid-configuration'.
Consider the following steps to reproduce (this is for prototyping and should not be considered production worthy):
We created a beta8 prototype using OIDC as described here.
We deployed the project to 2 identically configured IIS 8.5 servers running on Server 2012 R2. The IIS servers host a beta8 site called "API" with bindings to port 80 and 443 for the host name "devapi.contoso.com" (sanitized for purposes of this post) on all available IP addresses.
Both IIS servers have a host entry that point to themselves:
127.0.0.1 devapi.contoso.com
Our network admin has bound a * certificate (*.contoso.com) with our Kemp load balancer and configured the DNS entry for https://devapi.contoso.com to resolve to the load balancer.
Now this is important, the load balancer has also been configured to proxy https traffic to the IIS servers using http (not, repeat, not on https). It has been explained to me that this is standard operating procedure for our company because they only have to install the certificate in one place. We're not sure why our network admin bound 443 in IIS since it, in theory, never receives any traffic on this port.
We make a secure post via https to https://devapi.contoso.com/authorize/v1 to fetch a token, which works fine (the details of how to make this post are here ):
{
"sub": "todo",
"iss": "https://devapi.contoso.com/",
"aud": "https://devapi.contoso.com/",
"exp": 1446158373,
"nbf": 1446154773
}
We then use this token in another secure get via https to https://devapi.contoso.com/values/v1/5.
OpenIdConnect.OpenIdConnectConfigurationRetriever throws the exception:
WebException: The remote name could not be resolved: 'devapi.contoso.com.well-known'
HttpRequestException: An error occurred while sending the request.
IOException: IDX10804: Unable to retrieve document from: 'https://devapi.contoso.com.well-known/openid-configuration'.
We think this is happening because OIDC is attempting to consult the host specified in "options.Authority", which we set at startup time to https://devapi.contoso.com/. Further we speculate that because our environment has been configured to translate https traffic to non https traffic between the load balancer and IIS something is going wrong when the framework tries to resolve https://devapi.contoso.com/. We have tried many configuration changes including even pointing the authority to non-secure http://devapi.contoso.com to no avail.
Any assistance in helping us understand this problem would be greatly appreciated.
#Pinpoint was right. This exception was caused by the OIDC configuration code path that allows IdentityModel to initiate non-HTTPS calls. In particular the code sample we were using was sensitive to missing trailing slash in the authority URI. Here is a code fragment that uses the Uri class to combine paths in a reliable way, regardless of whether the Authority URI has a trailing slash:
public void Configure(IApplicationBuilder app, IOptions<AppSettings> appSettings)
{
.
.
.
// Add a new middleware validating access tokens issued by the OIDC server.
app.UseJwtBearerAuthentication
(
options =>
{
options.AuthenticationScheme = JwtBearerDefaults.AuthenticationScheme ;
options.AutomaticAuthentication = false ;
options.Authority = new Uri(appSettings.Value.AuthAuthority).ToString() ;
options.Audience = new Uri(appSettings.Value.AuthAuthority).ToString() ;
// Allow IdentityModel to use HTTP
options.ConfigurationManager =
new ConfigurationManager<OpenIdConnectConfiguration>
(
metadataAddress : new Uri(new Uri(options.Authority), ".well-known/openid-configuration").ToString(),
configRetriever : new OpenIdConnectConfigurationRetriever() ,
docRetriever : new HttpDocumentRetriever { RequireHttps = false }
);
}
);
.
.
.
}
In this example we're pulling in the Authority URI from config.json via "appSettings.Value.AuthAuthority" and then sanitizing/combining it using the Uri class.

Calling Remote Web Service -- "The request failed with HTTP status 401: Unauthorized"

I am calling a remote service and authenticating using a certificate. When testing with a Console App, everything works fine. When calling from an ASP.NET Website (.NET 4.0, IIS7) I receive a response code of 401 -- Unauthorized.
I am adding the certificate using code such as:
var client = new TheGeneratedProxy();
client.ClientCertificates.Add(new X509Certificate("D:\cert.pfx", "myPassword"));
(NOTE: I have also loaded the .pfx into the local Certificate Store using IE. The certificate is loaded into my "Personal" store -- so I suspect this to be the problem, since the Website will be running under a different account.)
I think the problem is that your IIS user (Network Service / ASPNET) doesn't have access to the certificate. In order to grant Network Service to access the certificate in the store, download the following tool: winhttpcertcfg (http://www.microsoft.com/downloads/details.aspx?familyid=c42e27ac-3409-40e9-8667-c748e422833f&displaylang=en)
Now open command prompt and type:
winhttpcertcfg –g –c LOCAL_MACHINE\My –s ORGNAME –a "Network Service"
Please note that "Network Service" can be substituted with any other account. I.e. if you you have configured a custom user for your app pool, you should put this user as the value for the -a parameter.
ORGNAME should be substituted with the Organisation name you specified during the creation of your cert.

Resources