Konfigurera tokenbindning med mTLS Proof-of-Possession (mTLS PoP)

Observera

Alla klienter kan inte hämta mTLS PoP-certifikat eftersom den här funktionen för närvarande är i privat förhandsversion.

Certifikattokenbindning (även kallat mTLS PoP – Mutual TLS Proof-of-Possession) är en avancerad säkerhetsfunktion som kryptografiskt binder åtkomsttoken till ett specifikt X.509-certifikat. RFC 8705 beskriver den här bindningen. Bindningen säkerställer att även om en token fångas upp kan en angripare inte använda den utan innehav av motsvarande privata nyckel.

Förstå hur tokenbindning fungerar

Följande steg beskriver tokenbindningsflödet från förvärv via verifiering.

  1. Token Acquisition: När du begär en åtkomsttoken med tokenbindning aktiverat innehåller Microsoft Identity Web certifikatets tumavtryck i tokenbegäran
  2. Tokenbindning: Auktoriseringsservern bäddar in ett cnf (bekräftelse)-anspråk i den utfärdade token som innehåller certifikatets SHA-256 tumavtryck (x5t#S256)
  3. API-anrop: Klienten presenterar både den bundna token och certifikatet när den anropar det underordnade API:et
  4. Verifiering: API:et verifierar att certifikatet som visas matchar certifikatreferensen i tokens cnf anspråk
sequenceDiagram
    participant Client
    participant EntraID as Microsoft Entra ID
    participant API

    Client->>EntraID: Token request with certificate thumbprint
    EntraID->>Client: Token with cnf claim (bound to certificate)
    Client->>API: MTLS_POP token + Client certificate
    API->>API: Validate token and certificate binding
    API->>Client: Protected resource

Granska säkerhetsfördelar

Tokenbindning ger följande fördelar för att skydda dina program.

  • Skydd mot tokenstöld: Stulna token är värdelösa utan motsvarande certifikat
  • Skydd mot reprisattacker: Token kan inte spelas upp från olika klienter
  • Förbättrad autentisering: Kombinerar "något du har" (certifikat) med traditionella OAuth2-flöden
  • Nulová dôvera (Zero Trust) Architecture: Överensstämmer med zero trust principer genom att binda autentiseringsuppgifter till specifika enheter

Konfigurera tokenbindning

Konfigurera både klientprogrammet och API-servern för att aktivera mTLS PoP-tokenbindning.

Konfigurera klientprogrammet

Utför följande steg för att konfigurera klientprogrammet för tokenbindning.

1. Konfigurera inställningar för Microsoft Entra ID

I din appsettings.json konfigurerar du dina Microsoft Entra inställningar, inklusive certifikatet:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",
    "ClientCredentials": [
      {
        "SourceType": "StoreWithDistinguishedName",
        "CertificateStorePath": "CurrentUser/My",
        "CertificateDistinguishedName": "CN=YourCertificate"
      }
    ],
    "SendX5c": true
  }
}

2. Konfigurera underordnat API med tokenbindning

Konfigurera ditt underordnade API-avsnitt med MTLS_POP protokollschemat:

{
  "DownstreamApi": {
    "BaseUrl": "https://api.contoso.com/",
    "RelativePath": "api/data",
    "ProtocolScheme": "MTLS_POP",
    "RequestAppToken": true,
    "Scopes": [ "api://your-api-scope/.default" ]
  }
}

Viktiga konfigurationsegenskaper:

  • ProtocolScheme: Måste anges till "MTLS_POP" för att aktivera tokenbindning
  • RequestAppToken: Måste vara true (tokenbindning stöder för närvarande endast programtoken)
  • Scopes: API-omfång som krävs för det underordnade API-anropet

3. Registrera tjänster

Registrera den underordnade API-tjänsten i programmets startkod. I följande exempel visas både konsolapp och ASP.NET Core metoder.

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Option 1: Using TokenAcquirerFactory (for console apps, background services)
var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();

tokenAcquirerFactory.Services.AddDownstreamApi(
    "DownstreamApi",
    tokenAcquirerFactory.Configuration.GetSection("DownstreamApi"));

var serviceProvider = tokenAcquirerFactory.Build();

// Option 2: Using ASP.NET Core DI (for web apps, web APIs)
builder.Services.AddAuthentication()
    .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"));

builder.Services.AddDownstreamApi(
    "DownstreamApi",
    builder.Configuration.GetSection("DownstreamApi"));

Konfigurera API-servern

Det underordnade API:et måste verifiera både token- och certifikatbindningen. Här är ett fullständigt exempel:

1. Registrera autentiseringshanterare

using Microsoft.Identity.Web;

var builder = WebApplication.CreateBuilder(args);

// Add standard JWT Bearer authentication
builder.Services.AddMicrosoftIdentityWebApiAuthentication(builder.Configuration);

// Add custom MTLS_POP authentication handler
builder.Services.AddAuthentication()
    .AddScheme<AuthenticationSchemeOptions, MtlsPopAuthenticationHandler>(
        "MTLS_POP",
        options => { });

var app = builder.Build();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();
app.Run();

2. Implementera mTLS PoP-autentiseringshanterare

using System.Security.Claims;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using System.Text.Encodings.Web;
using System.Text.Json;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

public class MtlsPopAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public const string ProtocolScheme = "MTLS_POP";

    public MtlsPopAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder)
        : base(options, logger, encoder)
    {
    }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // 1. Extract the MTLS_POP authorization header
        var authHeader = Request.Headers.Authorization.FirstOrDefault();
        if (string.IsNullOrEmpty(authHeader) ||
            !authHeader.StartsWith($"{ProtocolScheme} ", StringComparison.OrdinalIgnoreCase))
        {
            return AuthenticateResult.NoResult();
        }

        var authToken = authHeader.Substring($"{ProtocolScheme} ".Length).Trim();

        try
        {
            // 2. Parse the JWT token
            var handler = new JsonWebTokenHandler();
            var token = handler.ReadJsonWebToken(authToken);

            // 3. Extract the 'cnf' claim
            var cnfClaim = token.Claims.FirstOrDefault(c => c.Type == "cnf");
            if (cnfClaim == null)
            {
                return AuthenticateResult.Fail("Missing 'cnf' claim in MTLS_POP token");
            }

            // 4. Extract certificate thumbprint from cnf claim
            var cnfJson = JsonDocument.Parse(cnfClaim.Value);
            if (!cnfJson.RootElement.TryGetProperty("x5t#S256", out var x5tS256Element))
            {
                return AuthenticateResult.Fail("Missing 'x5t#S256' in cnf claim");
            }

            var expectedThumbprint = x5tS256Element.GetString();

            // 5. Get client certificate from TLS connection
            var clientCert = Context.Connection.ClientCertificate;
            if (clientCert != null)
            {
                var actualThumbprint = GetCertificateThumbprint(clientCert);

                // 6. Validate certificate binding
                if (!string.Equals(actualThumbprint, expectedThumbprint,
                    StringComparison.OrdinalIgnoreCase))
                {
                    return AuthenticateResult.Fail(
                        "Certificate thumbprint mismatch with cnf claim");
                }
            }

            // 7. Create claims principal
            var claims = token.Claims.Select(c => new Claim(c.Type, c.Value)).ToList();
            var identity = new ClaimsIdentity(claims, ProtocolScheme);
            var principal = new ClaimsPrincipal(identity);
            var ticket = new AuthenticationTicket(principal, ProtocolScheme);

            return AuthenticateResult.Success(ticket);
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error validating mTLS PoP token");
            return AuthenticateResult.Fail($"Validation error: {ex.Message}");
        }
    }

    private static string GetCertificateThumbprint(X509Certificate2 certificate)
    {
        using var sha256 = SHA256.Create();
        var hash = sha256.ComputeHash(certificate.RawData);
        return Base64UrlEncoder.Encode(hash);
    }
}

Använda tokenbindning i program

I följande exempel visas hur du integrerar mTLS PoP-tokenbindning i olika programtyper.

Anropa API:er från en konsol eller ett daemonprogram

I följande exempel visas en konsol eller ett daemonprogram som anropar ett underordnat API med mTLS PoP-tokenbindning.

using Microsoft.Extensions.DependencyInjection;
using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;

public class Program
{
    public static async Task Main(string[] args)
    {
        // Create and configure token acquirer
        var tokenAcquirerFactory = TokenAcquirerFactory.GetDefaultInstance();

        tokenAcquirerFactory.Services.AddDownstreamApi(
            "SecureApi",
            tokenAcquirerFactory.Configuration.GetSection("SecureApi"));

        var serviceProvider = tokenAcquirerFactory.Build();

        // Get IDownstreamApi instance
        var downstreamApi = serviceProvider.GetRequiredService<IDownstreamApi>();

        // Call API with mTLS PoP token
        var response = await downstreamApi.GetForAppAsync<ApiResponse>("SecureApi");

        Console.WriteLine($"Result: {response?.Data}");
    }
}

public class ApiResponse
{
    public string? Data { get; set; }
}

Anropa API:er från en ASP.NET Core webbapp

I följande exempel visas en kontrollant som anropar ett underordnat API med mTLS PoP-tokenbindning.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Identity.Abstractions;

[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
    private readonly IDownstreamApi _downstreamApi;
    private readonly ILogger<DataController> _logger;

    public DataController(
        IDownstreamApi downstreamApi,
        ILogger<DataController> logger)
    {
        _downstreamApi = downstreamApi;
        _logger = logger;
    }

    [HttpGet]
    public async Task<IActionResult> GetSecureData()
    {
        try
        {
            // Call downstream API with mTLS PoP token binding
            var data = await _downstreamApi.GetForAppAsync<SecureData>(
                "SecureApi");

            return Ok(data);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Failed to retrieve secure data");
            return StatusCode(500, "Failed to retrieve data");
        }
    }
}

public class SecureData
{
    public string? Id { get; set; }
    public string? Value { get; set; }
}

Konfigurera DownstreamApiOptions programmatiskt

I följande exempel anges mTLS PoP-alternativ direkt i kod i stället för konfigurationsfiler.

using Microsoft.Identity.Abstractions;
using Microsoft.Identity.Web;

public class SecureApiService
{
    private readonly IDownstreamApi _downstreamApi;

    public SecureApiService(IDownstreamApi downstreamApi)
    {
        _downstreamApi = downstreamApi;
    }

    public async Task<T?> CallSecureApiAsync<T>(string endpoint) where T : class
    {
        return await _downstreamApi.GetForAppAsync<T>(
            serviceName: null,
            downstreamApiOptionsOverride: options =>
            {
                options.BaseUrl = "https://api.secure.com";
                options.RelativePath = endpoint;
                options.ProtocolScheme = "MTLS_POP";
                options.RequestAppToken = true;
                options.Scopes = new[] { "api://secure-api/.default" };
            });
    }
}

Använda MicrosoftIdentityMessageHandler med tokenbindning

MicrosoftIdentityMessageHandler stöder mTLS PoP-tokenbindning via tilläggsmetoderna AddMicrosoftIdentityMessageHandler . När ProtocolScheme är inställt på "MTLS_POP"hämtar hanteraren automatiskt en bunden token och skickar begäranden via en mTLS-konfigurerad HTTP-klient.

Konfigurera infogade alternativ

I följande exempel registreras en HTTP-klient med infogad mTLS PoP-konfiguration och dess användning i en tjänst.

// Program.cs
services.AddHttpClient("MtlsPopClient", client =>
{
    client.BaseAddress = new Uri("https://api.contoso.com");
})
.AddMicrosoftIdentityMessageHandler(options =>
{
    options.Scopes.Add("api://contoso/.default");
    options.ProtocolScheme = "MTLS_POP";
    options.RequestAppToken = true;
});

// Usage in a service
public class SecureApiService
{
    private readonly HttpClient _httpClient;

    public SecureApiService(IHttpClientFactory factory)
    {
        _httpClient = factory.CreateClient("MtlsPopClient");
    }

    public async Task<string> GetSecureDataAsync()
    {
        // Authentication and mTLS certificate binding are automatic
        var response = await _httpClient.GetAsync("/api/secure-data");
        response.EnsureSuccessStatusCode();
        return await response.Content.ReadAsStringAsync();
    }
}

Läs in konfiguration från appsettings.json

Du kan också läsa in tokenbindningsinställningar från konfigurationsfilen.

appsettings.json:

{
  "DownstreamApis": {
    "SecureApi": {
      "Scopes": ["api://secure-api/.default"],
      "ProtocolScheme": "MTLS_POP",
      "RequestAppToken": true
    }
  }
}

Program.cs: Följande kod registrerar HTTP-klienten med hjälp av konfigurationsavsnittet.

services.AddHttpClient("SecureApiClient", client =>
{
    client.BaseAddress = new Uri("https://secure-api.example.com");
})
.AddMicrosoftIdentityMessageHandler(
    configuration.GetSection("DownstreamApis:SecureApi"),
    "SecureApi");

Tillämpa tokenbindning för varje begäran

Använd alternativ per begäran när vissa begäranden behöver tokenbindning och andra inte:

services.AddHttpClient("FlexibleClient")
    .AddMicrosoftIdentityMessageHandler();

// In a service:
public async Task<string> CallWithTokenBindingAsync()
{
    var request = new HttpRequestMessage(HttpMethod.Get, "https://api.contoso.com/secure")
        .WithAuthenticationOptions(options =>
        {
            options.Scopes.Add("api://contoso/.default");
            options.ProtocolScheme = "MTLS_POP";
            options.RequestAppToken = true;
        });

    var response = await _httpClient.SendAsync(request);
    response.EnsureSuccessStatusCode();
    return await response.Content.ReadAsStringAsync();
}

Mer information om finns i dokumentationen om MicrosoftIdentityMessageHandleranpassade API:er.

Skapa en anpassad HttpClient med auktoriseringshuvudprovider

Använd den här metoden för scenarier som kräver mer kontroll över HTTP-begäranden. I följande exempel hämtas ett bundet auktoriseringshuvud och en mTLS-konfigurerad HTTP-klient skapas.

using Microsoft.Identity.Abstractions;
using System.Net.Http.Headers;

public class CustomApiClient
{
    private readonly IAuthorizationHeaderProvider _authProvider;
    private readonly IHttpClientFactory _httpClientFactory;

    public CustomApiClient(
        IAuthorizationHeaderProvider authProvider,
        IHttpClientFactory httpClientFactory)
    {
        _authProvider = authProvider;
        _httpClientFactory = httpClientFactory;
    }

    public async Task<string> CallApiWithCustomLogicAsync()
    {
        // Create downstream API options for mTLS PoP
        var apiOptions = new DownstreamApiOptions
        {
            BaseUrl = "https://api.contoso.com",
            ProtocolScheme = "MTLS_POP",
            RequestAppToken = true,
            Scopes = new[] { "api://contoso/.default" }
        };

        // Get authorization header with binding certificate info
        var authResult = await (_authProvider as IBoundAuthorizationHeaderProvider)
            ?.CreateBoundAuthorizationHeaderAsync(apiOptions)!;

        if (authResult.IsSuccess)
        {
            // Create HTTP client with certificate binding
            var httpClient = authResult.Value.BindingCertificate != null
                ? CreateMtlsHttpClient(authResult.Value.BindingCertificate)
                : _httpClientFactory.CreateClient();

            // Set authorization header
            httpClient.DefaultRequestHeaders.Authorization =
                AuthenticationHeaderValue.Parse(authResult.Value.AuthorizationHeaderValue);

            // Make API call
            var response = await httpClient.GetAsync(
                $"{apiOptions.BaseUrl}/api/endpoint");

            return await response.Content.ReadAsStringAsync();
        }

        throw new InvalidOperationException("Failed to acquire token");
    }

    private HttpClient CreateMtlsHttpClient(X509Certificate2 certificate)
    {
        var handler = new HttpClientHandler();
        handler.ClientCertificates.Add(certificate);
        return new HttpClient(handler);
    }
}

Granska tokenstrukturen

I följande exempel visas hur standard- och bundna token skiljer sig åt.

Jämföra OAuth2-standardtoken

En OAuth2-standardtoken innehåller ingen information om certifikatbindning.

{
  "aud": "api://your-api",
  "iss": "https://login.microsoftonline.com/tenant-id/",
  "iat": 1234567890,
  "exp": 1234571490,
  "appid": "client-id",
  "tid": "tenant-id"
}

Granska mTLS PoP-token med bindning

En mTLS PoP-token innehåller anspråket cnf som binder token till ett specifikt certifikat.

{
  "aud": "api://your-api",
  "iss": "https://login.microsoftonline.com/tenant-id/",
  "iat": 1234567890,
  "exp": 1234571490,
  "appid": "client-id",
  "tid": "tenant-id",
  "cnf": {
    "x5t#S256": "buc7x2HxS_hPnVJb9J5mwPr6jCw8Y_2LHDz-gp_-6KM"
  }
}

Anspråket cnf (bekräftelse) innehåller SHA-256-tumavtrycket för certifikatet, Base64Url-encoded.

Förstå aktuella begränsningar

Granska följande begränsningar innan du implementerar mTLS PoP-tokenbindning.

Endast stöd för programtoken

Tokenbindning stöder för närvarande endast programtoken (endast app). Delegerade (användar)token stöds inte.

Ange protokollschemat

Egenskapen ProtocolScheme måste uttryckligen anges för att aktivera tokenbindning och sättas till "MTLS_POP". Om den inte har angetts används standard Bearer-autentisering.

Uppfylla certifikatkraven

  • Certifikatet måste konfigureras i ClientCredentials med SendX5c inställt på true
  • Certifikatet måste vara tillgängligt vid anskaffning av token

Felsökning av vanliga problem

Använd följande vägledning för att diagnostisera och lösa problem med tokenbindning.

Lösa vanliga problem

1. "Saknade 'cnf'-anspråk i token"

Orsak: Tokenbindningen har inte konfigurerats korrekt eller så är token en standard-Bearer-token.

Lösning: Kontrollera att ProtocolScheme är inställt på "MTLS_POP" och RequestAppToken är true.

{
  "DownstreamApi": {
    "ProtocolScheme": "MTLS_POP",  // ensure this is set
    "RequestAppToken": true
  }
}

2. "Felmatchning av tumavtryck för certifikat"

Orsak: Certifikatet som visas för API:et matchar inte det som används för tokenförvärv.

Lösning:

  • Kontrollera att samma certifikat används för både tokenförvärv och API-anrop
  • Kontrollera konfigurationen för certifikatinläsning i ClientCredentials
  • Kontrollera att certifikatet inte har upphört att gälla eller förnyats

3. "Ett certifikat som krävs för tokenbindning saknas"

Cause: Inget certifikat har konfigurerats i Microsoft Entra inställningar.

Lösning: Lägg till ett certifikat i konfigurationen ClientCredentials och ange SendX5c till true.

{
  "AzureAd": {
    "ClientCredentials": [
      {
        "SourceType": "StoreWithDistinguishedName",
        "CertificateStorePath": "CurrentUser/My",
        "CertificateDistinguishedName": "CN=YourCertificate"
      }
    ],
    "SendX5c": true  // required for token binding
  }
}

4. "Tokenbindning kräver aktiverat anskaffning av apptoken"

Orsak: RequestAppToken är inte inställt på true.

Lösning: Ange RequestAppToken till true i dina alternativ.

var options = new DownstreamApiOptions
{
    ProtocolScheme = "MTLS_POP",
    RequestAppToken = true,  // must be true
};

Felsöka tokenbindning

Använd följande tekniker för att undersöka problem med tokenbindning.

Aktivera detaljerad loggning

Lägg till följande konfiguration för att aktivera loggning på felsökningsnivå för Microsoft. Identity.Web.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.Identity.Web": "Debug"
    }
  }
}

Granska token-krav

Använd följande kod för att lista alla anspråk i en token och söka efter anspråket cnf .

var handler = new JsonWebTokenHandler();
var token = handler.ReadJsonWebToken(tokenString);

foreach (var claim in token.Claims)
{
    Console.WriteLine($"{claim.Type}: {claim.Value}");
}

// Look for 'cnf' claim with x5t#S256
var cnfClaim = token.Claims.FirstOrDefault(c => c.Type == "cnf");

Verifiera certifikatets tumavtryck

Använd följande kod för att beräkna och visa ett certifikats SHA-256 tumavtryck som jämförelse.

using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
using Microsoft.IdentityModel.Tokens;

var cert = new X509Certificate2("path/to/cert.pfx", "password");
using var sha256 = SHA256.Create();
var hash = sha256.ComputeHash(cert.RawData);
var thumbprint = Base64UrlEncoder.Encode(hash);
Console.WriteLine($"Certificate thumbprint: {thumbprint}");

Följ säkerhetsriktlinjerna

Tillämpa följande säkerhetsmetoder när du implementerar tokenbindning.

Hantera certifikat på ett säkert sätt

  • Arkiv på ett säkert sätt: Använd Azure Key Vault eller säkra certifikatarkiv
  • Rotera regelbundet: Implementera procedurer för certifikatrotation
  • Övervaka förfallodatum: Konfigurera aviseringar för certifikatets giltighetstid
  • Begränsa åtkomst: Begränsa vem som kan komma åt privata certifikatnycklar

Säkra nätverksanslutningar

  • Kräv TLS 1.2+: Kontrollera att alla anslutningar använder moderna TLS-versioner
  • Verifiera certifikat: Implementera korrekt certifikatverifiering på servern
  • Använd starka chiffer: Konfigurera säkra chiffersviter

Hantera token på ett säkert sätt

  • Kort livslängd: Använd kortlivade token (rekommenderas: 1 timme)
  • Rätt lagring: Registrera eller exponera aldrig tokens
  • Verifiera noggrant: Kontrollera alla anspråk, förfallodatum och bindningar

Följ metodtipsen

Tänk på följande rekommendationer när du distribuerar mTLS PoP-tokenbindning.

  1. Använd alltid HTTPS: mTLS PoP kräver säker transport
  2. Använd ett certifikat som lagrar det privata nyckelmaterialet i maskinvara, t.ex. i TPM: Använd maskinvara över programvarusäkerhet för bättre skydd
  3. Implementera korrekt felhantering: Hantera certifikat- och tokenfel korrekt
  4. Övervaka certifikatets giltighetstid: Automatisera certifikatförnyelse
  5. Använda separata certifikat per miljö: Utvecklings-, mellanlagrings- och produktionscertifikat
  6. Loggsäkerhetshändelser: Spåra tokenbindningsfel och felmatchade certifikat
  7. Testcertifikatrotation: Kontrollera att programmet hanterar certifikatuppdateringar
  8. Dokumentera konfigurationen: Behåll tydlig dokumentation om certifikatkrav

Utforska exempelkod

Fullständiga arbetsexempel som visar mTLS PoP-tokenbindning är tillgängliga på lagringsplatsen:

Dessa exempel visar:

  • Slutför klient- och serverkonfigurationen
  • Tokenförvärv med certifikatbindning
  • Implementering av anpassad autentiseringshanterare
  • Verifiering av certifikat och tumavtryck