Configurar a associação de token com a Prova de Posse de mTLS (mTLS PoP)

Note

Nem todos os clientes podem obter certificados PoP mTLS porque esta funcionalidade está atualmente em pré-visualização privada.

A associação de token ao certificado (também conhecida como mTLS PoP - Mutual TLS Proof-of-Possession) é uma funcionalidade de segurança avançada que realiza a vinculação criptográfica de tokens de acesso a um certificado X.509 específico. O RFC 8705 descreve esta ligação. A ligação garante que, mesmo que um token seja intercetado, um atacante não pode usá-lo sem possuir a chave privada correspondente.

Compreenda como funciona a ligação de tokens

Os passos seguintes descrevem o fluxo de ligação de tokens desde a aquisição até à verificação.

  1. Aquisição de Tokens: Ao solicitar um token de acesso com ligação de token ativada, Microsoft Identity Web inclui a impressão digital do certificado no pedido do token
  2. Ligação de Token: O servidor de autorização incorpora uma cnf reivindicação (de confirmação) no token emitido contendo a impressão digital SHA-256 do certificado (x5t#S256)
  3. Chamada API: O cliente apresenta tanto o token associado como o certificado ao chamar a API a jusante
  4. Verificação: A API valida que o certificado apresentado corresponde à referência do certificado na reivindicação cnf do token
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

Analise os benefícios de segurança

A ligação de tokens oferece as seguintes vantagens para proteger as suas aplicações.

  • Proteção contra Roubo de Tokens: Os tokens roubados são inúteis sem o certificado correspondente
  • Prevenção de Ataques de Replay: Os tokens não podem ser retransmitidos por diferentes clientes
  • Autenticação Melhorada: Combina "algo que tens" (certificado) com fluxos OAuth2 tradicionais
  • Confiança Zero Arquitetura: Alinha-se com os princípios zero trust ao vincular credenciais a dispositivos específicos

Configurar ligação de tokens

Configure tanto a aplicação cliente como o servidor API para permitir a ligação de tokens PoP mTLS.

Configurar o aplicativo cliente

Complete os seguintes passos para configurar a aplicação cliente para a ligação de tokens.

1. Configurar as definições do Microsoft Entra ID

No seu appsettings.json, configure as suas definições do Microsoft Entra, incluindo o certificado.

{
  "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. Configurar a API descendente com associação de token

Configure a sua secção de API a jusante com o esquema de MTLS_POP protocolo:

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

Propriedades Importantes de Configuração:

  • ProtocolScheme: Deve ser definido para "MTLS_POP" para ativar a ligação de tokens
  • RequestAppToken: Deve ser true (a ligação de tokens atualmente suporta apenas tokens de aplicação)
  • Scopes: Escopos de API necessários para a chamada de API descendente

3. Serviços de registo

Registe o serviço de API a jusante no código de arranque da sua aplicação. O exemplo seguinte mostra tanto as abordagens da aplicação de consola como do ASP.NET Core.

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"));

Configurar o servidor API

A API a jusante deve validar tanto o token como a ligação do certificado. Aqui está um exemplo completo:

1. Registar gestores de autenticação

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. Implementar o handler de autenticação PoP mTLS

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);
    }
}

Usar ligação de tokens em aplicações

Os exemplos seguintes mostram como integrar a ligação de tokens PoP mTLS em diferentes tipos de aplicação.

Chamar APIs a partir de uma aplicação de consola ou daemonizada

O exemplo seguinte demonstra uma aplicação de consola ou daemon que invoca uma API a jusante com associação de token PoP mTLS.

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; }
}

APIs de chamadas a partir de uma aplicação web ASP.NET Core

O exemplo seguinte mostra um controlador que efetua uma chamada para uma API a jusante com binding de tokens PoP mTLS.

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; }
}

Configurar DownstreamApiOptions programaticamente

O exemplo seguinte define as opções MTLS PoP diretamente em código em vez de ficheiros de configuração.

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" };
            });
    }
}

Utilizar MicrosoftIdentityMessageHandler com ligação de token

MicrosoftIdentityMessageHandler suporta a vinculação de tokens PoP mTLS através dos métodos de extensão AddMicrosoftIdentityMessageHandler. Quando ProtocolScheme está definido para "MTLS_POP", o handler adquire automaticamente um token vinculado e envia pedidos através de um cliente HTTP configurado em mTLS.

Configurar opções em linha

O exemplo seguinte regista um cliente HTTP com configuração MTLS PoP inline e mostra a sua utilização num serviço.

// 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();
    }
}

Carregar configuração de appsettings.json

Também podes carregar as definições de associação de tokens a partir do teu ficheiro de configuração.

appsettings.json:

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

Program.cs: O código seguinte regista o cliente HTTP usando a secção de configuração.

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

Aplicar ligação de token por solicitação

Use opções por pedido quando alguns pedidos precisam de vinculação de token e outros não.

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();
}

Para mais informações sobre MicrosoftIdentityMessageHandler, consulte a documentação de APIs Personalizadas.

Crie um HttpClient personalizado com provedor de cabeçalhos de autorização

Use esta abordagem para cenários que exijam mais controlo sobre pedidos HTTP. O exemplo seguinte adquire um cabeçalho de autorização limitada e cria um cliente HTTP configurado em mTLS.

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);
    }
}

Analisar a estrutura dos tokens

Os exemplos seguintes mostram como os tokens standard e bound diferem.

Compare os tokens padrão OAuth2

Um token OAuth2 padrão não contém informação de ligação de certificados.

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

Revise tokens mTLS PoP com binding

Um token PoP mTLS inclui a cnf reivindicação que vincula o token a um certificado específico.

{
  "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"
  }
}

A reivindicação (confirmação) cnf contém a impressão digital SHA-256 do certificado, que está codificada em Base64Url.

Compreender as limitações atuais

Reveja as seguintes restrições antes de implementar a ligação de tokens PoP mTLS.

Suporta apenas tokens de aplicação

Atualmente, a ligação de tokens suporta apenas tokens de aplicação (tokens apenas para aplicação). Tokens delegados (de utilizador) não são suportados.

Definir o esquema do protocolo

A ProtocolScheme propriedade deve estar explicitamente definida como "MTLS_POP" para permitir a ligação de tokens. Se não estiver definido, é usada a autenticação padrão do Portador.

Cumprir os requisitos do certificado

  • O certificado deve ser configurado em ClientCredentials com SendX5c definido como true
  • O certificado deve estar acessível no momento da aquisição do token

Resolver problemas comuns

Use as seguintes orientações para diagnosticar e resolver problemas de ligação de tokens.

Resolver Problemas Comuns

Falta de declaração 'cnf' no token

Causa: A ligação do token não foi devidamente configurada ou o token é um token Bearer padrão.

Solução: Verificar que ProtocolScheme está definido para "MTLS_POP" e RequestAppToken é true.

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

2. "Incompatibilidade da impressão digital (thumbprint) do certificado"

Causa: O certificado apresentado à API não corresponde ao usado para aquisição de tokens.

Solução:

  • Verifique se o mesmo certificado é usado tanto para aquisição de tokens como para chamadas API
  • Verifique a configuração de carregamento do certificado em ClientCredentials
  • Certifique-se de que o certificado não está expirado nem renovado

3. "Falta um certificado, que é necessário para a vinculação de tokens"

Cause: Nenhum certificado está configurado nas definições Microsoft Entra.

Solução: Adicione um certificado à sua ClientCredentials configuração e defina SendX5c para true.

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

4. "A ligação de token requer aquisição de token de aplicação ativada"

Causa: RequestAppToken não está definido para true.

Solução: Defina RequestAppToken como true nas suas opções.

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

Ligação de tokens de depuração

Utilize as seguintes técnicas para investigar questões relacionadas com a ligação de tokens.

Ativar registo detalhado

Adicione a seguinte configuração para permitir o registo de nível de depuração para Microsoft.Identity.Web.

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

Inspecionar as declarações do token

Use o seguinte código para listar todas as reivindicações num token e verificar a reivindicação 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");

Verificar impressão digital do certificado

Use o seguinte código para calcular e exibir a impressão digital SHA-256 de um certificado para comparação.

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}");

Siga as diretrizes de segurança

Aplique as seguintes práticas de segurança ao implementar a vinculação de tokens.

Gerir certificados de forma segura

  • Armazene de forma segura: Utilize Azure Key Vault ou armazenamentos de certificados seguros
  • Rodar regularmente: Implementar procedimentos de rotação de certificados
  • Expiração do monitor: Configurar alertas para a expiração do certificado
  • Restringir acesso: Limitar quem pode aceder às chaves privadas dos certificados

Ligações de rede seguras

  • Exigir TLS 1.2+: Garantir que todas as ligações utilizam versões modernas de TLS
  • Validar certificados: Implementar a validação adequada de certificados no servidor
  • Use cifras fortes: Configure conjuntos de cifras seguras

Gerir tokens de forma segura

  • Tempo de vida curto: Use tokens de curta duração (recomendado: 1 hora)
  • Armazenamento adequado: Nunca logar ou expor tokens
  • Valide minuciosamente: Verifique todas as declarações, a expiração e a vinculação

Siga as melhores práticas

Tenha em mente as seguintes recomendações ao implementar a ligação de tokens PoP mTLS.

  1. Use sempre HTTPS: o mTLS PoP requer transporte seguro
  2. Use um certificado que armazene o material da chave privada em hardware, por exemplo, no TPM: Use hardware em vez de segurança por software para melhor proteção
  3. Implementar um tratamento adequado de erros: Gerir corretamente erros de certificados e tokens
  4. Expiração do certificado de monitorização: Renovação automática de certificados
  5. Use certificados separados por ambiente: dev, staging e certificados de produção
  6. Eventos de segurança de registo: Rastrear falhas de ligação de tokens e incompatibilidades de certificados
  7. Rotação de certificados de teste: Certifique-se de que a sua aplicação lida com as atualizações de certificados
  8. Documente a sua configuração: Mantenha uma documentação clara dos requisitos do certificado

Explorar código de exemplo

Amostras completas e funcionais que demonstram a ligação de tokens MTLS PoP estão disponíveis no repositório:

Estas amostras demonstram:

  • Configuração completa do cliente e servidor
  • Aquisição de token com ligação de certificados
  • Implementação de manipulador de autenticação personalizado
  • Validação de certificados e verificação de impressão digital