Configuración de la vinculación de tokens con Prueba de Posesión de mTLS (PoP de mTLS)

Note

No todos los clientes pueden obtener certificados poP de mTLS porque esta característica está actualmente en versión preliminar privada.

El enlace de tokens de certificado (también conocido como Demostración de Posesión TLS Mutua - mTLS PoP) es una característica de seguridad avanzada que enlaza criptográficamente los tokens de acceso a un certificado específico X.509. RFC 8705 describe este enlace. El enlace garantiza que incluso si se intercepta un token, un atacante no puede usarlo sin posesión de la clave privada correspondiente.

Comprender cómo funciona la vinculación de tokens

En los pasos siguientes se describe el flujo de enlace de tokens desde la adquisición a través de la comprobación.

  1. Token Acquisition: al solicitar un token de acceso con el enlace de tokens habilitado, Microsoft Identity Web incluye la huella digital del certificado en la solicitud de token.
  2. Asociación de tokens: el servidor de autorización inserta una afirmación (confirmación) en el token emitido que contiene la huella digital SHA-256 del certificado (cnf)
  3. Llamada API: el cliente presenta el token enlazado y el certificado al llamar a la API de bajada.
  4. Comprobación: la API valida que el certificado presentado coincide con la referencia de certificado en la notificación del 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

Revisión de las ventajas de seguridad

El enlace de tokens proporciona las siguientes ventajas para proteger las aplicaciones.

  • Protección contra robo de tokens: los tokens robados son inútiles sin el certificado correspondiente.
  • Prevención de ataques de reproducción: los tokens no se pueden reproducir desde distintos clientes
  • Autenticación mejorada: combina "algo que tiene" (certificado) con flujos tradicionales de OAuth2
  • arquitectura de Confianza cero: se alinea con los principios de zero trust mediante el enlace de credenciales a dispositivos específicos

Configuración del enlace de tokens

Configure tanto la aplicación cliente como el servidor de API para habilitar el enlace de tokens poP de mTLS.

Configuración de la aplicación cliente

Complete los pasos siguientes para configurar la aplicación cliente para el enlace de tokens.

1. Configurar las opciones de Microsoft Entra ID

En su appsettings.json, configure su configuración de Microsoft Entra, incluido el 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 la API descendente con vinculación de tokens

Configure la sección de API descendente con el 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" ]
  }
}

Propiedades de configuración importantes:

  • ProtocolScheme: debe establecerse en "MTLS_POP" para habilitar el enlace de tokens.
  • RequestAppToken: debe ser true (el enlace de tokens actualmente solo admite tokens de aplicación)
  • Scopes: ámbitos de API necesarios para la llamada a la API descendente

3. Registrar servicios

Registre el servicio de API de bajada en el código de inicio de la aplicación. En el ejemplo siguiente se muestran los enfoques de aplicación de consola y 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"));

Configuración del servidor de API

La API de bajada debe validar tanto el token como el enlace de certificado. Este es un ejemplo completo:

1. Registrar controladores de autenticación

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. Implementación del controlador de autenticación poP de 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);
    }
}

Uso del enlace de tokens en aplicaciones

En los ejemplos siguientes se muestra cómo integrar el enlace de tokens poP de mTLS en diferentes tipos de aplicación.

Llamar APIs desde una aplicación de consola o aplicación daemon

En el ejemplo siguiente se muestra una aplicación de consola o daemon que llama a una API downstream con la vinculación de tokens PoP de 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; }
}

Llamada a las API desde una aplicación web de ASP.NET Core

En el ejemplo siguiente se muestra un controlador que llama a una API downstream con enlace de tokens PoP de 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; }
}

Configuración de DownstreamApiOptions mediante programación

En el ejemplo siguiente se establecen las opciones poP de mTLS directamente en el código en lugar de los archivos de configuración.

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

Uso de MicrosoftIdentityMessageHandler con enlace de token

MicrosoftIdentityMessageHandler admite el enlace de tokens PoP de mTLS a través de los métodos de extensión AddMicrosoftIdentityMessageHandler. Cuando ProtocolScheme se establece en "MTLS_POP", el controlador adquiere automáticamente un token enlazado y envía solicitudes a través de un cliente HTTP configurado con mTLS.

Configurar opciones en línea

En el ejemplo siguiente se registra un cliente HTTP con la configuración de poP de mTLS insertada y se muestra su uso en un servicio.

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

Carga de la configuración desde appsettings.json

También puede cargar la configuración de enlace de tokens desde el archivo de configuración.

appsettings.json:

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

Program.cs: el código siguiente registra el cliente HTTP mediante la sección de configuración.

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

Aplicación del enlace de tokens por solicitud

Utiliza opciones por solicitud cuando algunas peticiones necesiten vinculación de tokens y otras no.

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 obtener más información sobre MicrosoftIdentityMessageHandler, consulte la documentación de API personalizadas.

Creación de un HttpClient personalizado con el proveedor de encabezados de autorización

Use este enfoque para escenarios que requieren más control sobre las solicitudes HTTP. En el ejemplo siguiente se adquiere un encabezado de autorización enlazado y se crea un 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);
    }
}

Examen de la estructura del token

En los ejemplos siguientes se muestra cómo difieren los tokens estándar y enlazados.

Comparación de tokens estándar de OAuth2

Un token de OAuth2 estándar no contiene información de enlace de certificados.

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

Revisión de tokens PoP de mTLS con vinculación

Un token PoP de mTLS incluye la reclamación cnf que enlaza el token a un 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"
  }
}

La cnf afirmación (confirmación) contiene la huella digital SHA-256 del certificado, codificado en Base64-Url.

Descripción de las limitaciones actuales

Revise las siguientes restricciones antes de implementar la vinculación de tokens PoP de mTLS.

Solo se admiten tokens de aplicación

Actualmente, la vinculación de tokens solo admite tokens de solo aplicación. No se admiten tokens de usuario delegados.

Establecimiento del esquema de protocolo

La ProtocolScheme propiedad debe establecerse explícitamente en "MTLS_POP" para habilitar el enlace de tokens. Si no se establece, se usa la autenticación de portador estándar.

Cumplir los requisitos del certificado

  • El certificado debe configurarse en ClientCredentials con SendX5c establecido en true.
  • El certificado debe ser accesible en el momento de la adquisición del token.

Solucionar problemas comunes

Use las instrucciones siguientes para diagnosticar y resolver problemas de enlace de tokens.

Resolución de problemas comunes

1. "Falta la notificación 'cnf' en el token"

Causa: el enlace de tokens no se configuró correctamente o el token es un token de portador estándar.

Solución: compruebe que ProtocolScheme está establecido en "MTLS_POP" y RequestAppToken es true.

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

2. "Error de coincidencia de huella digital del certificado"

Causa: El certificado presentado a la API no coincide con el usado para la adquisición de tokens.

Solución:

  • Compruebe que se usa el mismo certificado para la adquisición de tokens y las llamadas API.
  • Verifique la configuración de carga de certificados en ClientCredentials
  • Asegúrese de que el certificado no ha expirado o renovado

3. "Falta un certificado, que es necesario para el enlace de tokens"

Cause: no hay ningún certificado configurado en las configuraciones de Microsoft Entra.

Solución: agregue un certificado a su ClientCredentials configuración y establezca SendX5c a true.

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

4. "El enlace de tokens requiere la adquisición de tokens de aplicación habilitada"

Causa: RequestAppToken no está establecido en true.

Solución: Configure RequestAppToken a true en las opciones.

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

Depuración de la vinculación de tokens

Use las técnicas siguientes para investigar los problemas de enlace de tokens.

Habilitación del registro detallado

Agregue la siguiente configuración para habilitar el registro de nivel de depuración para Microsoft. Identity.Web.

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

Inspección de notificaciones de token

Utilice el siguiente código para enumerar todas las afirmaciones de un token y verificar la afirmación 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");

Comprobación de la huella digital del certificado

Use el código siguiente para calcular y mostrar la huella digital SHA-256 de un certificado para la comparación.

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 las directrices de seguridad.

Aplique las siguientes prácticas de seguridad al implementar el enlace de tokens.

Administración de certificados de forma segura

  • Store de forma segura: Usar Azure Key Vault o almacenes de certificados seguros
  • Rotar regularmente: Implementar procedimientos de rotación de certificados
  • Supervisión de la expiración: Configuración de alertas para la expiración del certificado
  • Restringir el acceso: limitar quién puede acceder a las claves privadas del certificado

Protección de conexiones de red

  • Requerir TLS 1.2 y versiones posteriores: asegúrese de que todas las conexiones usan versiones modernas de TLS.
  • Validar certificados: implementar la validación de certificados adecuada en el servidor
  • Uso de cifrados seguros: Configuración de conjuntos de cifrado seguros

Maneja tokens de forma segura

  • Duración corta: use tokens de vida corta (recomendado: 1 hora)
  • Almacenamiento adecuado: nunca almacenar ni exponer tokens
  • Validar exhaustivamente: comprobación de todas las reclamaciones, expiración y vinculación

Seguimiento de los procedimientos recomendados

Tenga en cuenta las siguientes recomendaciones al implementar el enlace de tokens poP de mTLS.

  1. Usar siempre HTTPS: mTLS PoP requiere transporte seguro
  2. Use un certificado que almacene el material de clave privada en hardware, por ejemplo, en TPM: Usar seguridad de hardware en lugar de software para mejorar la protección
  3. Implementación de un manejo adecuado de errores: Manejar de manera eficiente los errores de certificado y token
  4. Supervisión de la expiración del certificado: Automatización de la renovación de certificados
  5. Uso de certificados independientes por entorno: desarrollo, almacenamiento provisional y certificados de producción
  6. Registrar eventos de seguridad: Rastrear errores de enlace de tokens y desajustes de certificados
  7. Prueba de la rotación de certificados: asegúrese de que la aplicación controla las actualizaciones de certificados.
  8. Documentar la configuración: Mantener la documentación clara de los requisitos de certificado

Exploración del código de ejemplo

Los ejemplos completos funcionales que demuestran la vinculación de tokens PoP mTLS están disponibles en el repositorio.

Estos ejemplos muestran:

  • Completar la configuración de cliente y servidor
  • Adquisición de tokens con vinculación de certificados
  • Implementación del controlador de autenticación personalizada
  • Validación de certificados y comprobación de huella digital