Configurar a associação de token com mTLS Proof-of-Possession (mTLS PoP)

Note

Nem todos os clientes têm permissão para obter certificados MTLS PoP porque esse recurso está atualmente em versão prévia privada.

A associação de token de certificado (também conhecida como mTLS PoP – Prova de Posse de TLS Mútua) é um recurso de segurança avançado que associa criptograficamente tokens de acesso a um certificado X.509 específico. O RFC 8705 descreve essa associação. A associação garante que, mesmo que um token seja interceptado, um invasor não poderá usá-lo sem a posse da chave privada correspondente.

Entender como a associação de token funciona

As etapas a seguir descrevem o fluxo de vinculação de token desde a aquisição até a verificação.

  1. Token Acquisition: ao solicitar um token de acesso com a associação de token habilitada, Microsoft Identity Web inclui a impressão digital do certificado na solicitação de token
  2. Associação de token: o servidor de autorização insere uma cnf declaração (confirmação) no token emitido que contém a impressão digital SHA-256 do certificado (x5t#S256)
  3. Chamada à API: o cliente apresenta o token associado e o certificado ao chamar a API downstream
  4. Verificação: a API valida que o certificado apresentado corresponde à referência de certificado na declaração do cnf 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

Examinar os benefícios de segurança

A associação de token fornece as seguintes vantagens para proteger seus aplicativos.

  • Proteção contra roubo de token: tokens roubados são inúteis sem o certificado correspondente
  • Prevenção de ataque de repetição: os tokens não podem ser utilizados a partir de clientes diferentes
  • Autenticação Aprimorada: combina "algo que você tem" (certificado) com fluxos OAuth2 tradicionais
  • Confiança Zero Architecture: alinha-se com princípios zero trust associando credenciais a dispositivos específicos

Configurar associação de token

Configure o aplicativo cliente e o servidor de API para habilitar a associação de token poP do mTLS.

Configurar o aplicativo do cliente

Conclua as etapas a seguir para configurar o aplicativo cliente para associação de token.

1. Definir configurações de Microsoft Entra ID

Em seu appsettings.json, defina suas configurações de 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 downstream com associação de token

Configure sua seção de API downstream com o MTLS_POP esquema de protocolo:

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

Propriedades de configuração importantes:

  • ProtocolScheme: deve ser definido para "MTLS_POP" habilitar a associação de token
  • RequestAppToken: deve ser true (a associação de token atualmente dá suporte apenas a tokens de aplicativo)
  • Scopes: escopos de API necessários para a chamada à API downstream

3. Registrar serviços

Registre o serviço de API downstream no código de inicialização do aplicativo. O exemplo a seguir mostra abordagens de aplicativo de console e 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 de API

A API downstream deve validar o token e a associação de certificado. Aqui está um exemplo completo:

1. Registrar manipuladores 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 manipulador de autenticação PoP do 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 associação de token em aplicativos

Os exemplos a seguir mostram como integrar a associação de token poP do mTLS em diferentes tipos de aplicativo.

Chamar APIs a partir de um aplicativo de console ou daemon

O exemplo a seguir demonstra um aplicativo de console ou daemon que chama uma API downstream com vinculaçã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; }
}

Chamar APIs de um aplicativo Web ASP.NET Core

O exemplo a seguir mostra um controlador que chama uma API downstream com vinculação de token 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 programmaticamente

O exemplo a seguir define as opções de PoP do mTLS diretamente no código em vez de arquivos 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" };
            });
    }
}

Usar MicrosoftIdentityMessageHandler com associação de token

MicrosoftIdentityMessageHandler dá suporte à associação de token PoP mTLS por meio dos métodos de extensão AddMicrosoftIdentityMessageHandler. Quando ProtocolScheme é definido como "MTLS_POP", o manipulador adquire automaticamente um token associado e envia solicitações por meio de um cliente HTTP configurado pelo mTLS.

Configurar opções integradas

O exemplo a seguir registra um cliente HTTP com configuração de PoP mTLS embutida e mostra seu uso em um 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ções de appsettings.json

Você também pode carregar as configurações de associação de token do arquivo de configuração.

appsettings.json:

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

Program.cs: o código a seguir registra o cliente HTTP usando a seção de configuração.

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

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

Use opções por solicitação quando algumas solicitações precisarem de associação de token e outras 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 obter mais informações sobre MicrosoftIdentityMessageHandler, consulte a documentação de APIs personalizadas.

Criar um HttpClient customizado com um provedor de cabeçalho de autorização

Use essa abordagem para cenários que exigem mais controle sobre solicitações HTTP. O exemplo a seguir adquire um cabeçalho de autorização associado e cria um cliente HTTP configurado por 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);
    }
}

Examinar a estrutura do token

Os exemplos a seguir mostram como os tokens padrão e associados diferem.

Comparar tokens padrão do OAuth2

Um token OAuth2 padrão não contém informações de associação de certificado.

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

Revisar tokens de PoP do mTLS com vinculação

Um token pop mTLS inclui a cnf declaração que associa 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 cnf declaração (confirmação) contém a impressão digital SHA-256 do certificado, codificada em Base64Url.

Entender as limitações atuais

Revise as restrições a seguir antes de implementar a vinculação de token PoP do mTLS.

Dar suporte somente a tokens de aplicativo

A associação de token atualmente dá suporte apenas a tokens de aplicativo (somente aplicativo). Não há suporte para tokens delegados de usuário.

Definir o esquema de protocolo

A propriedade ProtocolScheme deve ser definida explicitamente como "MTLS_POP" para habilitar a vinculação de token. Se não for definido, a autenticação de portador padrão será usada.

Atender aos requisitos de certificado

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

Solucionar problemas comuns

Use as diretrizes a seguir para diagnosticar e resolver problemas de associação de token.

Resolver problemas comuns

1. "Declaração 'cnf' ausente no token"

Causa: a associação de token não foi configurada corretamente ou o token é um token de portador padrão.

Solução: verifique se o ProtocolScheme está definido como "MTLS_POP" e se o RequestAppToken está true.

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

2. "Incompatibilidade de impressão digital do certificado"

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

Solução:

  • Verifique se o mesmo certificado é usado para aquisição de token e chamadas à API
  • Verificar a configuração de carregamento de certificado em ClientCredentials
  • Verifique se o certificado não expirou ou foi renovado

3. "Um certificado, que é necessário para associação de token, está ausente"

Cause: nenhum certificado está configurado nas configurações de Microsoft Entra.

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

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

4. "A associação de token requer a aquisição de token de aplicativo habilitada"

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

Solução: defina RequestAppTokentrue em suas opções.

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

Vinculação de token de depuração

Use as técnicas a seguir para investigar problemas de associação de token.

Habilitar registro em log detalhado

Adicione a configuração a seguir para habilitar o registro em log no nível de depuração para Microsoft. Identity.Web.

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

Inspecionar declarações de token

Use o código a seguir para listar todas as declarações em um token e verificar a cnf declaração.

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 a impressão digital do certificado

Use o código a seguir 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 associação de token.

Gerenciar certificados com segurança

  • Armazenar com segurança: Use o Azure Key Vault ou repositórios de certificados seguros
  • Girar regularmente: implementar procedimentos de rotação de certificados
  • Monitorar a expiração: configurar alertas para a expiração do certificado
  • Restringir o acesso: limitar quem pode acessar chaves privadas de certificado

Proteger conexões de rede

  • Exigir TLS 1.2+: verifique se todas as conexões usam versões modernas do TLS
  • Validar certificados: implementar a validação de certificado adequada no servidor
  • Usar criptografias fortes: configurar conjuntos de criptografia seguros

Manipular tokens com segurança

  • Tempos de vida curtos: usar tokens de curta duração (recomendado: 1 hora)
  • Armazenamento adequado: nunca registre ou exponha tokens
  • Validar minuciosamente: verificar todas as declarações, a expiração e a vinculação

Seguir as práticas recomendadas

Ao implantar a vinculação de token PoP do mTLS, tenha em mente as seguintes recomendações.

  1. Sempre use HTTPS: mTLS PoP requer transporte seguro
  2. Use um certificado que armazene o material de chave privada no hardware, por exemplo, no TPM: Use um hardware em vez de segurança de software para melhor proteção.
  3. Implementar o tratamento de erros adequado: administrar de forma elegante erros de certificado e token
  4. Monitorar a expiração do certificado: automatizar a renovação do certificado
  5. Usar certificados separados por ambiente: Certificados de desenvolvimento, preparo e produção
  6. Eventos de segurança de log: rastrear falhas de associação de token e incompatibilidades de certificado
  7. Testar a rotação de certificados: verifique se o aplicativo lida com atualizações de certificado
  8. Documente sua configuração: manter a documentação clara dos requisitos de certificado

Explorar o código de exemplo

Amostras funcionais completas demonstrando a vinculação de token PoP do mTLS estão disponíveis no repositório:

Estes exemplos demonstram:

  • Concluir a configuração do cliente e do servidor
  • Aquisição de token com associação de certificado
  • Implementação do manipulador de autenticação personalizada
  • Validação de certificado e verificação de impressão digital