Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Note
Tous les clients ne sont pas autorisés à obtenir des certificats mTLS PoP, car cette fonctionnalité est actuellement en préversion privée.
La liaison de jeton de certificat (également appelée mTLS PoP - Preuve de possession TLS mutuelle) est une fonctionnalité de sécurité avancée qui lie par chiffrement les jetons d’accès à un certificat X.509 spécifique. RFC 8705 décrit cette liaison. La liaison garantit que même si un jeton est intercepté, un attaquant ne peut pas l’utiliser sans possession de la clé privée correspondante.
Comprendre le fonctionnement de la liaison des jetons
Les étapes suivantes décrivent le processus de liaison des jetons depuis l’acquisition jusqu’à la vérification.
- Token Acquisition : lors de la demande d'un jeton d'accès avec la liaison de jeton activée, Microsoft Identity Web inclut l'empreinte numérique du certificat dans la demande de jeton
-
Liaison de jeton : le serveur d’autorisation incorpore une
cnfrevendication (confirmation) dans le jeton émis contenant l’empreinte SHA-256 du certificat (x5t#S256) - Appel d’API : le client présente à la fois le jeton lié et le certificat lors de l’appel de l’API en aval
-
Vérification : l’API valide que le certificat présenté correspond à la référence de certificat dans la revendication du
cnfjeton
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
Passer en revue les avantages de sécurité
La liaison de jeton offre les avantages suivants pour sécuriser vos applications.
- Protection contre le vol de jeton : les jetons volés sont inutiles sans le certificat correspondant
- Prévention des attaques par rejeu : les jetons ne peuvent pas être rejoués par différents clients
- Authentification améliorée : combine « quelque chose que vous avez » (certificat) avec des flux OAuth2 traditionnels
- architecture Confiance nulle : s’aligne sur les principes de zero trust en liant les informations d’identification à des appareils spécifiques
Configurer la liaison des jetons
Configurez l’application cliente et le serveur d’API pour activer la liaison de jeton PoP mTLS.
Configurer l’application cliente
Effectuez les étapes suivantes pour configurer l’application cliente pour la liaison de jetons.
1. Configurer les paramètres de Microsoft Entra ID
Dans votre appsettings.json, configurez vos paramètres de Microsoft Entra, y compris le certificat :
{
"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. Configurer l'API en aval avec la Token Binding
Configurez votre section d'API en aval avec le schéma de protocole MTLS_POP :
{
"DownstreamApi": {
"BaseUrl": "https://api.contoso.com/",
"RelativePath": "api/data",
"ProtocolScheme": "MTLS_POP",
"RequestAppToken": true,
"Scopes": [ "api://your-api-scope/.default" ]
}
}
Propriétés de configuration importantes :
-
ProtocolScheme: doit être défini à"MTLS_POP"pour activer la liaison de jeton -
RequestAppToken: doit êtretrue(la liaison de jeton prend actuellement en charge uniquement les jetons d’application) -
Scopes: étendues d’API requises pour l’appel d’API en aval
3. Inscrire des services
Inscrivez le service d’API en aval dans le code de démarrage de votre application. L’exemple suivant montre à la fois les approches d’application console et de 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"));
Configurer le serveur d’API
L’API en aval doit valider le jeton et la liaison de certificat. Voici un exemple complet :
1. Inscrire des gestionnaires d’authentification
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. Implémenter le gestionnaire d’authentification 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);
}
}
Utiliser la liaison de jetons dans les applications
Les exemples suivants montrent comment intégrer la liaison de jetons poP mTLS dans différents types d’application.
Appeler des API à partir d’une console ou d’une application démon
L'exemple suivant illustre une application console ou application démon qui appelle une API en aval avec la liaison de jeton 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; }
}
Appeler des API à partir d’une application web ASP.NET Core
L’exemple suivant montre un contrôleur qui appelle une API en aval avec une liaison de jeton 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; }
}
Configurer DownstreamApiOptions de manière programmatique
L’exemple suivant définit les options poP mTLS directement dans le code au lieu de fichiers de configuration.
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" };
});
}
}
Utiliser MicrosoftIdentityMessageHandler avec la liaison de jetons
MicrosoftIdentityMessageHandler prend en charge la liaison des jetons PoP mTLS via les méthodes d'extension AddMicrosoftIdentityMessageHandler. Lorsqu’il ProtocolScheme est défini sur "MTLS_POP", le gestionnaire acquiert automatiquement un jeton lié et envoie des requêtes via un client HTTP configuré avec mTLS.
Configurer les options en ligne
L’exemple suivant enregistre un client HTTP avec une configuration mTLS PoP inline et affiche son utilisation dans un service.
// 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();
}
}
Charger la configuration à partir de appsettings.json
Vous pouvez également charger des paramètres de liaison de jeton à partir de votre fichier de configuration.
appsettings.json:
{
"DownstreamApis": {
"SecureApi": {
"Scopes": ["api://secure-api/.default"],
"ProtocolScheme": "MTLS_POP",
"RequestAppToken": true
}
}
}
Program.cs : le code suivant inscrit le client HTTP à l’aide de la section de configuration.
services.AddHttpClient("SecureApiClient", client =>
{
client.BaseAddress = new Uri("https://secure-api.example.com");
})
.AddMicrosoftIdentityMessageHandler(
configuration.GetSection("DownstreamApis:SecureApi"),
"SecureApi");
Appliquer un liage de jeton par requête
Utilisez des options par requête lorsque certaines requêtes nécessitent une liaison de jetons et que d'autres non.
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();
}
Pour plus d’informations sur MicrosoftIdentityMessageHandlerles API personnalisées, consultez la documentation sur les API personnalisées.
Créer un HttpClient personnalisé avec le fournisseur d’en-tête d’autorisation
Utilisez cette approche pour les scénarios qui nécessitent davantage de contrôle sur les requêtes HTTP. L’exemple suivant acquiert un en-tête d’autorisation lié et crée un client HTTP configuré par 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);
}
}
Examiner la structure des jetons
Les exemples suivants montrent comment les jetons standard et liés diffèrent.
Comparer les jetons OAuth2 standard
Un jeton OAuth2 standard ne contient aucune information de liaison de certificat.
{
"aud": "api://your-api",
"iss": "https://login.microsoftonline.com/tenant-id/",
"iat": 1234567890,
"exp": 1234571490,
"appid": "client-id",
"tid": "tenant-id"
}
Passer en revue les jetons poP mTLS avec la liaison
Un jeton poP mTLS inclut la cnf revendication qui lie le jeton à un certificat spécifique.
{
"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 revendication cnf (confirmation) contient l'empreinte SHA-256 du certificat, encodée au format Base64Url.
Comprendre les limitations actuelles
Passez en revue les contraintes suivantes avant d’implémenter la liaison de jeton mTLS PoP.
Prendre en charge uniquement les jetons d’application
La liaison de jeton prend actuellement en charge les jetons d’application uniquement ("app-only"). Les jetons délégués (utilisateur) ne sont pas pris en charge.
Définir le schéma de protocole
La propriété ProtocolScheme doit être explicitement définie à "MTLS_POP" afin d’activer la liaison de jeton. S’il n’est pas défini, l’authentification du porteur standard est utilisée.
Répondre aux exigences de certificat
- Le certificat doit être configuré dans
ClientCredentialsavecSendX5cdéfini surtrue - Le certificat doit être accessible au moment de l’acquisition de jetons
Résoudre les problèmes courants
Utilisez les instructions suivantes pour diagnostiquer et résoudre les problèmes de liaison de jeton.
Résoudre les problèmes courants
1. « Revendication « cnf » manquante dans le jeton »
Cause : la liaison de jeton n’a pas été correctement configurée ou le jeton est un jeton porteur standard.
Solution : vérifiez que ProtocolScheme est définie sur "MTLS_POP" et que RequestAppToken est true.
{
"DownstreamApi": {
"ProtocolScheme": "MTLS_POP", // ensure this is set
"RequestAppToken": true
}
}
2. « Incompatibilité de l’empreinte numérique du certificat »
Cause : le certificat présenté à l’API ne correspond pas à celui utilisé pour l’acquisition de jetons.
Solution:
- Vérifiez que le même certificat est utilisé pour l’acquisition de jetons et les appels d’API
- Vérifier la configuration du chargement du certificat dans
ClientCredentials - Vérifier que le certificat n’a pas expiré ou renouvelé
3. « Un certificat, requis pour la liaison de jeton, est manquant »
Cause : aucun certificat n’est configuré dans les paramètres Microsoft Entra.
Solution : ajoutez un certificat à votre ClientCredentials configuration et définissez SendX5c sur true.
{
"AzureAd": {
"ClientCredentials": [
{
"SourceType": "StoreWithDistinguishedName",
"CertificateStorePath": "CurrentUser/My",
"CertificateDistinguishedName": "CN=YourCertificate"
}
],
"SendX5c": true // required for token binding
}
}
4. « La liaison de jeton nécessite une acquisition activée des jetons d'application »
Cause : RequestAppToken n’est pas définie sur true.
Solution : définissez RequestAppToken sur true dans vos options.
var options = new DownstreamApiOptions
{
ProtocolScheme = "MTLS_POP",
RequestAppToken = true, // must be true
};
Liaison de jeton de débogage
Utilisez les techniques suivantes pour analyser les problèmes de liaison de jeton.
Activer la journalisation détaillée
Ajoutez la configuration suivante pour activer la journalisation au niveau du débogage pour Microsoft. Identity.Web.
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Identity.Web": "Debug"
}
}
}
Inspecter les revendications de jeton
Utilisez le code suivant pour répertorier toutes les revendications dans un jeton et rechercher la cnf revendication.
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");
Vérifier l’empreinte numérique du certificat
Utilisez le code suivant pour calculer et afficher l’empreinte NUMÉRIQUE SHA-256 d’un certificat pour la comparaison.
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}");
Suivez les instructions de sécurité
Appliquez les pratiques de sécurité suivantes lorsque vous mettez en œuvre la liaison de jeton.
Gérer les certificats de manière sécurisée
- Stockez de manière sécurisée : utilisez Azure Key Vault ou d'autres magasins de certificats sécurisés
- Rotation régulière : Mettre en œuvre des procédures de rotation de certificats
- Expiration du moniteur : configurer des alertes pour l’expiration du certificat
- Restreindre l’accès : Limiter les personnes pouvant accéder aux clés privées de certificat
Sécuriser les connexions réseau
- Exiger TLS 1.2+ : vérifiez que toutes les connexions utilisent des versions TLS modernes
- Valider les certificats : Implémenter une validation de certificat appropriée sur le serveur
- Utiliser des chiffrements forts : configurer des suites de chiffrement sécurisées
Gérer les jetons de manière sécurisée
- Durées de vie courtes : Utiliser des jetons de courte durée (recommandé : 1 heure)
- Stockage approprié : Ne jamais journaliser ou exposer des jetons
- Valider soigneusement : vérifier toutes les réclamations, l’expiration et le liaisonnement
Suivre les bonnes pratiques
Gardez à l’esprit les recommandations suivantes lorsque vous déployez la liaison de jeton mTLS PoP.
- Toujours utiliser HTTPS : mTLS PoP nécessite un transport sécurisé
- Utilisez un certificat qui stocke le matériel de clé privée dans le matériel, par exemple, dans le TPM : privilégiez le matériel au logiciel pour une meilleure protection
- Implémenter une gestion appropriée des erreurs : gérer correctement les erreurs de certificat et de jeton
- Surveiller l’expiration du certificat : Automatiser le renouvellement des certificats
- Utiliser des certificats distincts par environnement : dev, préproduction et certificats de production
- Consigner les événements de sécurité : surveiller les échecs de liaison de jeton et les incompatibilités de certificat
- Tester la rotation des certificats : vérifiez que votre application gère les mises à jour des certificats
- Documenter votre configuration : conservez la documentation claire des exigences de certificat
Contenu connexe
- Documentation de Microsoft Identity Web
- Vue d’ensemble de l’appel d’API en aval
- Documentation sur les API personnalisées
- Informations d’identification de certificat Microsoft Entra
- Authentification du client OAuth 2.0 Mutual-TLS
Explorer un exemple de code
Des exemples de mise en œuvre complets illustrant la liaison de jeton PoP mTLS sont disponibles dans le référentiel.
-
Application cliente :
tests/DevApps/MtlsPop/MtlsPopClient -
Serveur d’API web :
tests/DevApps/MtlsPop/MtlsPopWebApi
Ces exemples illustrent :
- Configuration complète du client et du serveur
- Acquisition de jetons avec liaison de certificat
- Implémentation de gestionnaire d’authentification personnalisée
- Validation de certificat et vérification de l’empreinte numérique