Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update mTLS to .NET 8 #162

Merged
merged 5 commits into from
Jan 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions IdentityServer/v7/Basics/IdentityServer/src/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public class Program
{
public static int Main(string[] args)
{
Console.Title = "IdentityServer";

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
Expand Down
53 changes: 53 additions & 0 deletions IdentityServer/v7/MTLS/.vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"version": "0.2.0",
"compounds": [
{
"name": "Run All",
"configurations": ["IdentityServerHost", "Api", "ClientCredentials"],
"presentation": {
"group": "10-compunds",
}
}
],
"configurations": [
{
"name": "IdentityServerHost",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-identityserverhost",
"program": "${workspaceFolder}/IdentityServerHost/bin/Debug/net8.0/IdentityServerHost.dll",
"args": [],
"cwd": "${workspaceFolder}/IdentityServerHost",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"console": "externalTerminal",
},
{
"name": "Api",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-api",
"program": "${workspaceFolder}/Api/bin/Debug/net8.0/Api.dll",
"args": [],
"cwd": "${workspaceFolder}/Api/",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"console": "externalTerminal",
},
{
"name": "ClientCredentials",
"type": "coreclr",
"request": "launch",
"preLaunchTask": "build-clientcredentials",
"program": "${workspaceFolder}/ClientCredentials/bin/Debug/net8.0/ClientCredentials.dll",
"args": [],
"cwd": "${workspaceFolder}/ClientCredentials",
"console": "externalTerminal",
"env": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
}
]
}
53 changes: 53 additions & 0 deletions IdentityServer/v7/MTLS/.vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "build",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/MTLS.sln",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-identityserverhost",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/IdentityServerHost",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-api",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/Api",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
},
{
"label": "build-clientcredentials",
"type": "process",
"command": "dotnet",
"args": [
"build",
"${workspaceFolder}/ClientCredentials",
"/property:GenerateFullPaths=true",
"/consoleloggerparameters:NoSummary"
],
"problemMatcher": "$msCompile"
}
]
}
7 changes: 3 additions & 4 deletions IdentityServer/v7/MTLS/Api/Api.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
<Project Sdk="Microsoft.NET.Sdk.Web">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.AspNetCore" Version="4.1.0" />

<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.1" />
<PackageReference Include="Serilog.AspNetCore" Version="8.0.0" />
</ItemGroup>

</Project>
86 changes: 42 additions & 44 deletions IdentityServer/v7/MTLS/Api/ConfirmationValidationMiddleware.cs
Original file line number Diff line number Diff line change
@@ -1,68 +1,66 @@
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json.Linq;
using System;
using System.Security.Cryptography;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Logging;
using System.Text.Json;

namespace SampleApi
namespace Api;

public static class ConfirmationValidationExtensions
{
public static class ConfirmationValidationExtensions
public static IApplicationBuilder UseConfirmationValidation(this IApplicationBuilder app, ConfirmationValidationMiddlewareOptions options = default)
{
public static IApplicationBuilder UseConfirmationValidation(this IApplicationBuilder app, ConfirmationValidationMiddlewareOptions options = default)
{
return app.UseMiddleware<ConfirmationValidationMiddleware>(options ?? new ConfirmationValidationMiddlewareOptions());
}
return app.UseMiddleware<ConfirmationValidationMiddleware>(options ?? new ConfirmationValidationMiddlewareOptions());
}
}

public class ConfirmationValidationMiddlewareOptions
{
public string JwtBearerSchemeName { get; set; } = JwtBearerDefaults.AuthenticationScheme;
}

public class ConfirmationValidationMiddlewareOptions
// this middleware validate the cnf claim (if present) against the thumbprint of the X.509 client certificate for the current client
public class ConfirmationValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly ConfirmationValidationMiddlewareOptions _options;

public ConfirmationValidationMiddleware(RequestDelegate next, ILogger<ConfirmationValidationMiddlewareOptions> logger, ConfirmationValidationMiddlewareOptions options = null)
{
public string JwtBearerSchemeName { get; set; } = JwtBearerDefaults.AuthenticationScheme;
_next = next;
_logger = logger;
_options ??= new ConfirmationValidationMiddlewareOptions();
}

// this middleware validate the cnf claim (if present) against the thumbprint of the X.509 client certificate for the current client
public class ConfirmationValidationMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly ConfirmationValidationMiddlewareOptions _options;

public ConfirmationValidationMiddleware(RequestDelegate next, ILogger<ConfirmationValidationMiddlewareOptions> logger, ConfirmationValidationMiddlewareOptions options = null)
{
_next = next;
_logger = logger;
_options ??= new ConfirmationValidationMiddlewareOptions();
}

public async Task Invoke(HttpContext ctx)
public async Task Invoke(HttpContext ctx)
{
if (ctx.User.Identity.IsAuthenticated)
{
if (ctx.User.Identity.IsAuthenticated)
var cnfJson = ctx.User.FindFirst("cnf")?.Value;
if (!String.IsNullOrWhiteSpace(cnfJson))
{
var cnfJson = ctx.User.FindFirst("cnf")?.Value;
if (!String.IsNullOrWhiteSpace(cnfJson))
{
var certificate = await ctx.Connection.GetClientCertificateAsync();
var thumbprint = Base64UrlTextEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256));

var cnf = JObject.Parse(cnfJson);
var sha256 = cnf.Value<string>("x5t#S256");
var certificate = await ctx.Connection.GetClientCertificateAsync();
var thumbprint = Base64UrlTextEncoder.Encode(certificate.GetCertHash(HashAlgorithmName.SHA256));

if (String.IsNullOrWhiteSpace(sha256) ||
!thumbprint.Equals(sha256, StringComparison.OrdinalIgnoreCase))
{
_logger.LogError("certificate thumbprint does not match cnf claim.");
await ctx.ChallengeAsync(_options.JwtBearerSchemeName);
return;
}

_logger.LogDebug("certificate thumbprint matches cnf claim.");
var sha256 = JsonDocument.Parse(cnfJson).RootElement.GetString("x5t#S256");

if (String.IsNullOrWhiteSpace(sha256) ||
!thumbprint.Equals(sha256, StringComparison.OrdinalIgnoreCase))
{
_logger.LogError("certificate thumbprint does not match cnf claim.");
await ctx.ChallengeAsync(_options.JwtBearerSchemeName);
return;
}

_logger.LogDebug("certificate thumbprint matches cnf claim.");
}

await _next(ctx);
}

await _next(ctx);
}
}
33 changes: 16 additions & 17 deletions IdentityServer/v7/MTLS/Api/IdentityController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,25 @@
using Microsoft.Extensions.Logging;
using System.Linq;

namespace SampleApi.Controllers
namespace Api.Controllers;

[Route("identity")]
public class IdentityController : ControllerBase
{
[Route("identity")]
public class IdentityController : ControllerBase
{
private readonly ILogger<IdentityController> _logger;
private readonly ILogger<IdentityController> _logger;

public IdentityController(ILogger<IdentityController> logger)
{
_logger = logger;
}
public IdentityController(ILogger<IdentityController> logger)
{
_logger = logger;
}

// this action simply echoes the claims back to the client
[HttpGet]
public ActionResult Get()
{
var claims = User.Claims.Select(c => new { c.Type, c.Value });
_logger.LogInformation("claims: {claims}", claims);
// this action simply echoes the claims back to the client
[HttpGet]
public ActionResult Get()
{
var claims = User.Claims.Select(c => new { c.Type, c.Value });
_logger.LogInformation("claims: {claims}", claims);

return new JsonResult(claims);
}
return new JsonResult(claims);
}
}
44 changes: 22 additions & 22 deletions IdentityServer/v7/MTLS/Api/Program.cs
Original file line number Diff line number Diff line change
@@ -1,36 +1,36 @@
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;
using Serilog;
using Serilog.Events;
using Serilog.Sinks.SystemConsole.Themes;

namespace SampleApi
namespace Api;

public class Program
{
public class Program
public static void Main(string[] args)
{
public static void Main(string[] args)
{
Console.Title = "Sample API";
Console.Title = "API";

BuildWebHost(args).Run();
}
BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Verbose()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
.CreateLogger();
public static IHost BuildWebHost(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
.CreateLogger();

return WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.UseSerilog()
.Build();
}
return Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseSerilog()
.Build();
}
}
Loading
Loading