将证书用于Microsoft。Identity.Web

Microsoft.Identity.Web 支持基于证书的身份验证,作为机密客户端应用程序的安全替代方法。 证书使用非对称加密,因此只有私钥持有者才能进行身份验证。

在本文中,将配置来自各种源的证书凭据,将其注册到应用,并在生产环境中对其进行管理。

为何使用证书?

因子 客户端密码 证书
安全性 共享机密(对称) 非对称密钥对
旋转 要求应用重新部署或配置更改 可以通过密钥保管库实现自动化
暴露风险 配置中的机密可能会泄露 私钥保留在安全存储中
遵从性 可能不符合企业策略 满足大多数企业安全要求
推荐用于 开发、原型制作 生产工作负荷

重要

Microsoft建议对生产应用程序的客户端机密使用证书。 为获得最高安全状况,请在托管环境支持时使用 无证书身份验证 (托管标识或工作负荷联合身份验证)。

工作原理

  1. 使用私钥生成或获取 X.509 证书。
  2. 使用 Microsoft Entra 应用注册证书的 公钥(或指纹)。
  3. 在运行时,Microsoft。Identity.Web 从配置的源加载证书(包括私钥)。
  4. 该库使用私钥对客户端断言进行签名,然后将该断言发送到 Microsoft Entra ID,以获取令牌。

证书源

Microsoft。Identity.Web 支持从多个源加载证书:

源类型 SourceType 最佳用途
Azure 密钥保管库 KeyVault 生产(推荐)
证书存储 StoreWithThumbprintStoreWithDistinguishedName 本地Windows服务器
文件路径 Path 开发、容器化应用
Base64 编码的字符串 Base64Encoded Kubernetes 密钥、CI/CD 管道

ClientCertificates(或AzureAd)配置部分的AzureAdB2C数组中配置证书凭据。 可以为轮换方案指定多个证书 - Microsoft。Identity.Web 使用它找到的第一个有效证书。


Azure 密钥保管库是生产中证书的建议源。 它提供集中式管理、访问控制、审核和自动轮换功能。

配置

将证书配置添加到您的appsettings.json中。

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientCertificates": [
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://your-keyvault-name.vault.azure.net",
        "KeyVaultCertificateName": "your-certificate-name"
      }
    ]
  }
}
财产 说明
SourceType 必须是 "KeyVault"
KeyVaultUrl Azure 密钥保管库的 URI(例如,https://myapp-kv.vault.azure.net)。
KeyVaultCertificateName 存储在密钥保管库中的证书的名称。

设置 密钥保管库 访问策略

应用程序的标识必须有权从密钥保管库(密钥保管库)读取证书。 授予此权限的方式取决于是使用保管库访问策略模型还是Azure基于角色的访问控制(RBAC)。

选项 1:保管库访问策略

az keyvault set-policy \
  --name your-keyvault-name \
  --object-id <app-or-managed-identity-object-id> \
  --certificate-permissions get list \
  --secret-permissions get

注释

--secret-permissions get 权限是必需的,因为Azure 密钥保管库将私钥存储为链接到证书的机密。 Microsoft。Identity.Web 需要访问证书及其私钥。

选项 2:Azure RBAC

密钥保管库 证书用户 角色分配给应用程序标识:

az role assignment create \
  --role "Key Vault Certificate User" \
  --assignee <app-or-managed-identity-object-id> \
  --scope /subscriptions/<sub-id>/resourceGroups/<rg>/providers/Microsoft.KeyVault/vaults/<vault-name>

使用托管标识访问密钥保管库

当你的应用程序在 Azure(应用服务、Azure Functions、Azure Kubernetes 服务、虚拟机)中运行时,请使用托管标识来验证访问 密钥保管库。 这样就无需任何凭据即可访问保管库本身。

系统分配的托管标识

如果应用启用了系统分配的托管标识,Microsoft。Identity.Web 自动使用 DefaultAzureCredential 对密钥保管库进行身份验证。 除了ClientCertificates条目之外,不需要额外的配置。

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientCertificates": [
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://your-keyvault-name.vault.azure.net",
        "KeyVaultCertificateName": "your-certificate-name"
      }
    ]
  }
}

用户分配的托管标识

对于用户分配的托管标识,请在密钥保管库证书描述符上指定 ManagedIdentityClientId

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientCertificates": [
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://your-keyvault-name.vault.azure.net",
        "KeyVaultCertificateName": "your-certificate-name",
        "ManagedIdentityClientId": "user-assigned-managed-identity-client-id"
      }
    ]
  }
}

小窍门

在开发期间本地运行时,DefaultAzureCredential 会回退到你的 Azure CLI 或 Visual Studio 凭据。 请确保使用 az login 登录,并且开发人员帐户具有适当的密钥保管库权限。


从证书存储区(仅限于Windows)

在Windows,可以从Windows证书存储加载证书。 这在本地或 IIS 托管的部署中很常见。

通过拇指指纹

使用 StoreWithThumbprint 通过其 SHA-1 指纹标识证书:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientCertificates": [
      {
        "SourceType": "StoreWithThumbprint",
        "CertificateStorePath": "CurrentUser/My",
        "CertificateThumbprint": "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2"
      }
    ]
  }
}
财产 说明
SourceType 必须是 "StoreWithThumbprint"
CertificateStorePath 证书存储位置。 常见值: "CurrentUser/My""LocalMachine/My"
CertificateThumbprint 证书的 SHA-1 指纹(40 个十六进制字符)。

按专有名称

使用 StoreWithDistinguishedName 按其主题名称标识证书:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientCertificates": [
      {
        "SourceType": "StoreWithDistinguishedName",
        "CertificateStorePath": "CurrentUser/My",
        "CertificateDistinguishedName": "CN=MyAppCertificate"
      }
    ]
  }
}
财产 说明
SourceType 必须是 "StoreWithDistinguishedName"
CertificateStorePath 证书存储位置。 常见值: "CurrentUser/My""LocalMachine/My"
CertificateDistinguishedName 证书的使用者可分辨名称(例如, "CN=MyAppCertificate")。

证书存储位置

下表列出了常见的证书存储路径以及访问它们所需的权限:

路径 说明 所需的权限
CurrentUser/My 当前用户的个人存储 用户级访问权限
LocalMachine/My 计算机范围的个人存储 管理员访问权限
LocalMachine/Root 受信任的根证书颁发机构 管理员访问权限
CurrentUser/Root 当前用户受信任的根证书颁发机构 用户级访问权限

注释

在 IIS 中托管时,应用程序池标识必须具有对证书私钥的读取访问权限。 可以使用证书 MMC 管理单元中的 “管理私钥 ”选项授予此权限。


从文件路径

可以直接从 .pfx 磁盘上的 (PKCS#12) 文件加载证书。

警告

不建议在生产环境中,将带有密码的证书文件存储在磁盘上。 仅对本地开发或文件系统受到保护的环境(例如,在容器中装载的机密)中使用此方法。

配置

将证书文件路径和密码添加到appsettings.json中。

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientCertificates": [
      {
        "SourceType": "Path",
        "CertificateDiskPath": "/path/to/certificate.pfx",
        "CertificatePassword": "your-certificate-password"
      }
    ]
  }
}
财产 说明
SourceType 必须是 "Path"
CertificateDiskPath .pfx 文件的绝对路径或相对路径。
CertificatePassword .pfx文件的密码。 如果证书没有密码,请省略此属性或将其设置为空字符串。

小窍门

若要避免以纯文本 appsettings.json形式存储密码,请从环境变量或机密管理器中引用密码:

使用.NET用户机密(开发):

dotnet user-secrets set "AzureAd:ClientCertificates:0:CertificatePassword" "your-password"

使用环境变量:

export AzureAd__ClientCertificates__0__CertificatePassword="your-password"

从 Base64 编码的值

可以将证书作为 Base64 编码的字符串提供。 通过环境变量、Kubernetes 机密或 CI/CD 管道变量注入证书时,此方法非常有用。

配置

将 Base64 编码的证书值添加到您的 appsettings.json 中:

{
  "AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantId": "your-tenant-id",
    "ClientId": "your-client-id",

    "ClientCertificates": [
      {
        "SourceType": "Base64Encoded",
        "Base64EncodedValue": "MIIKcQIBAzCCCi0GCSqGSIb3DQEHAaCCCh4Egg..."
      }
    ]
  }
}
财产 说明
SourceType 必须是 "Base64Encoded"
Base64EncodedValue 编码为 Base64 字符串的完整证书(包括私钥)。

生成 Base64 值

.pfx 文件转换为 Base64 字符串:

PowerShell:

$certBytes = [System.IO.File]::ReadAllBytes("path/to/certificate.pfx")
$base64 = [System.Convert]::ToBase64String($certBytes)
$base64 | Set-Clipboard  # Copies to clipboard

Bash:

base64 -w 0 path/to/certificate.pfx

与 Kubernetes 机密配合使用

将 Base64 编码的证书存储在 Kubernetes 机密中,并将其映射到环境变量:

apiVersion: v1
kind: Secret
metadata:
  name: app-cert-secret
type: Opaque
data:
  AzureAd__ClientCertificates__0__Base64EncodedValue: <base64-encoded-pfx>

在部署中引用该密钥:

env:
  - name: AzureAd__ClientCertificates__0__SourceType
    value: "Base64Encoded"
  - name: AzureAd__ClientCertificates__0__Base64EncodedValue
    valueFrom:
      secretKeyRef:
        name: app-cert-secret
        key: AzureAd__ClientCertificates__0__Base64EncodedValue

在 CI/CD 管道中使用

在Azure DevOps或GitHub Actions中,将 Base64 编码的证书存储为机密变量,然后在运行时将其设置为环境变量。

GitHub Actions example:

env:
  AzureAd__ClientCertificates__0__SourceType: "Base64Encoded"
  AzureAd__ClientCertificates__0__Base64EncodedValue: ${{ secrets.APP_CERTIFICATE_BASE64 }}

Azure DevOps example:

variables:
  AzureAd__ClientCertificates__0__SourceType: "Base64Encoded"
  AzureAd__ClientCertificates__0__Base64EncodedValue: $(AppCertificateBase64)

重要

尽管证书是 Base64 编码的,但它包含私钥,并且必须被视为机密。 始终在 CI/CD 管道中使用机密变量 — 从不将 Base64 编码的证书提交到源代码管理。


在 C# 代码中配置证书

除了 JSON 配置,还可以使用 CredentialDescription 中的 Microsoft.Identity.Abstractions 类以编程方式配置证书凭据。

辅助方法

CredentialDescription 类为每个证书源类型提供静态帮助程序方法:

using Microsoft.Identity.Abstractions;

// From Azure Key Vault
var kvCredential = CredentialDescription.FromKeyVault(
    "https://your-keyvault-name.vault.azure.net",
    "your-certificate-name");

// From certificate store (by thumbprint)
var thumbprintCredential = CredentialDescription.FromCertificateStore(
    "CurrentUser/My",
    thumbprint: "A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4E5F6A1B2");

// From certificate store (by distinguished name)
var dnCredential = CredentialDescription.FromCertificateStore(
    "CurrentUser/My",
    distinguishedName: "CN=MyAppCertificate");

// From file path
var pathCredential = CredentialDescription.FromCertificatePath(
    "/path/to/certificate.pfx",
    "your-certificate-password");

// From Base64-encoded string
var base64Credential = CredentialDescription.FromBase64String(
    "MIIKcQIBAzCCCi0GCSqGSIb3DQEHAaCCCh4Egg...");

在 ASP.NET Core中使用

配置身份验证时,直接传递凭据说明:

builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
    .AddMicrosoftIdentityWebApp(options =>
    {
        options.Instance = "https://login.microsoftonline.com/";
        options.TenantId = "your-tenant-id";
        options.ClientId = "your-client-id";
        options.ClientCredentials = new[]
        {
            CredentialDescription.FromKeyVault(
                "https://your-keyvault-name.vault.azure.net",
                "your-certificate-name")
        };
    });

小窍门

帮助程序方法等效于手动设置对象的属性 CredentialDescription 。 在代码中配置凭据而不是通过 appsettings.json时,它们提供更简洁的语法。


创建用于开发的自签名证书

对于本地开发和测试,可以创建自签名证书。 请勿在生产环境中使用自签名证书。

使用 PowerShell (Windows)

运行以下命令以创建自签名证书、导出证书并显示指纹:

$cert = New-SelfSignedCertificate `
  -Subject "CN=MyDevCertificate" `
  -CertStoreLocation "Cert:\CurrentUser\My" `
  -KeyExportPolicy Exportable `
  -KeySpec Signature `
  -KeyLength 2048 `
  -KeyAlgorithm RSA `
  -HashAlgorithm SHA256 `
  -NotAfter (Get-Date).AddYears(2)

# Export the .pfx file (with private key)
$password = ConvertTo-SecureString -String "YourPassword123!" -Force -AsPlainText
Export-PfxCertificate -Cert $cert -FilePath ".\MyDevCertificate.pfx" -Password $password

# Export the .cer file (public key only — for app registration)
Export-Certificate -Cert $cert -FilePath ".\MyDevCertificate.cer"

# Display the thumbprint
Write-Host "Thumbprint: $($cert.Thumbprint)"

使用 OpenSSL (跨平台)

运行以下命令以生成证书,将其打包为 .pfx 文件,并显示指纹:

# Generate a self-signed certificate and private key
openssl req -x509 -newkey rsa:2048 \
  -keyout key.pem -out cert.pem \
  -days 730 -nodes \
  -subj "/CN=MyDevCertificate"

# Package into a .pfx file
openssl pkcs12 -export \
  -out MyDevCertificate.pfx \
  -inkey key.pem -in cert.pem \
  -passout pass:YourPassword123!

# Get the thumbprint
openssl x509 -in cert.pem -noout -fingerprint -sha1

使用 .NET CLI

将开发 HTTPS 证书导出为 .pfx 文件:

dotnet dev-certs https --export-path ./MyDevCertificate.pfx --password YourPassword123!

注释

dotnet dev-certs 命令生成 HTTPS 开发证书。 虽然它可用于测试证书加载,但它主要用于本地 HTTPS,可能不适合所有身份验证测试方案。


在 Microsoft Entra ID 中注册证书

创建或获取证书后,必须在Microsoft Entra ID中向应用注册注册其公钥。

使用 Azure 门户

  1. 转到 Azure 门户并导航到 Microsoft Entra ID>应用注册
  2. 选择自己的应用程序。
  3. 选择“证书和机密”“证书”>“上传证书”>
  4. 上传包含.cer.pem文件。 不要上传 .pfx 包含私钥的文件。
  5. 请注意上传后显示的 指纹 值 - 可能需要它进行配置。

使用Azure CLI

az ad app credential reset \
  --id <application-client-id> \
  --cert @/path/to/certificate.pem \
  --append

--append 标志添加证书而不删除现有凭据。

使用 Microsoft Graph PowerShell

$certData = [System.IO.File]::ReadAllBytes(".\MyDevCertificate.cer")
$base64Cert = [System.Convert]::ToBase64String($certData)

$keyCredential = @{
    type = "AsymmetricX509Cert"
    usage = "Verify"
    key = [System.Convert]::FromBase64String($base64Cert)
    displayName = "MyAppCertificate"
}

Update-MgApplication -ApplicationId <app-object-id> -KeyCredentials @($keyCredential)

重要

仅将 公钥.cer.pem)上传到应用注册。 从不上传 .pfx 包含私钥的文件。 私钥必须保持安全存储和仅可供应用程序访问。


证书轮换

证书轮换在证书过期之前将其替换为新证书,以确保服务连续性。

策略:重叠证书

建议的方法使用重叠的有效期:

  1. 在当前证书过期之前生成新证书(例如提前 30-60 天)。
  2. 在 Microsoft Entra 应用注册中同时注册新证书和现有证书。 Microsoft Entra ID接受任何已注册证书签名的令牌。
  3. 将新证书部署到应用程序的证书源(密钥保管库、证书存储等)。
  4. 更新配置 (如有必要),以指向新证书。
  5. 确认所有实例都使用新实例后,从应用注册中删除旧证书

配置中的多个证书

Microsoft。Identity.Web 支持指定多个证书。 库函数按顺序尝试这些证书,并使用第一个有效的证书:

{
  "AzureAd": {
    "ClientCertificates": [
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://your-keyvault.vault.azure.net",
        "KeyVaultCertificateName": "new-cert-2026"
      },
      {
        "SourceType": "KeyVault",
        "KeyVaultUrl": "https://your-keyvault.vault.azure.net",
        "KeyVaultCertificateName": "current-cert-2025"
      }
    ]
  }
}

使用 Azure 密钥保管库 自动旋转

Azure 密钥保管库支持自动证书续订。 启用自动旋转时:

  1. 密钥保管库在过期之前生成新的证书版本。
  2. Microsoft Identity.Web 自动获取最新版本(在下一个证书获取时)。
  3. 旧证书版本在过期之前保持有效。

若要在密钥保管库中配置自动轮换,请执行:

az keyvault certificate set-attributes \
  --vault-name your-keyvault-name \
  --name your-certificate-name \
  --policy @rotation-policy.json

小窍门

对于具有长时间运行进程的应用程序,请考虑实现定期证书刷新。 Microsoft。Identity.Web 将证书缓存在内存中。 如果证书在密钥保管库中轮换,则下次需要创建新的 MSAL 机密客户端应用程序实例时,应用程序会选取新证书。


排查证书错误

本部分列出了常见的错误消息及其解决方案。

常见错误

找不到证书

错误消息

System.Security.Cryptography.CryptographicException: The certificate cannot be found.

可能的原因和解决方案:

原因 解决方案
指纹不正确 验证配置中的指纹是否与已安装的证书匹配。 删除任何隐藏字符(空格、不可见 Unicode)。
证书存储错误 确认 CertificateStorePath 与证书安装位置(CurrentUser/MyLocalMachine/My)的匹配。
未安装证书 使用 certmgr.msc (CurrentUser) 或 certlm.msc (LocalMachine) 将证书导入到正确的存储中。
密钥保管库名称不匹配 验证 KeyVaultUrlKeyVaultCertificateName 正确。
找不到文件 确认 CertificateDiskPath 指向现有 .pfx 文件和应用程序具有读取访问权限。

拒绝访问密钥保管库

错误消息

Azure.RequestFailedException: The user, group or application '...' does not have certificates get permission on key vault '...'

解决方案:

  • 验证访问策略是否同时授予get证书机密的权限。
  • 如果使用 Azure RBAC,请确保身份具有 密钥保管库 证书用户角色。
  • 对于托管身份,请确认身份已启用,并在策略中使用正确的对象 ID。

证书私钥不可访问

错误消息

System.Security.Cryptography.CryptographicException: Keyset does not exist.

解决方案:

  • 在 Windows/IIS 上,确保应用程序池标识具有对私钥read访问权限。 使用 MMC 证书管理单元,通过 “管理私钥”授予访问权限。
  • 在 Linux 上,验证 .pfx 该文件是否具有适当的文件权限(chmod 600)。
  • 确保使用私钥(Export-PfxCertificateopenssl pkcs12 -export)导出证书。

证书已过期

错误消息

AADSTS700027: Client assertion contains an invalid signature. The key was expired.

解决方案:

  • 检查证书的有效期: openssl x509 -in cert.pem -noout -dates
  • 生成新的证书并更新应用注册和应用程序的配置。
  • 实现证书轮换以防止将来的过期问题。 请参阅 证书轮换

证书密码错误

错误消息

System.Security.Cryptography.CryptographicException: The specified network password is not correct.

解决方案:

  • 验证 CertificatePassword 是否与导出 .pfx 文件时使用的密码匹配。
  • 如果使用环境变量,请检查编码问题(尾随换行符、特殊字符)。
  • 使用已知密码重新导出证书。

诊断清单

当证书身份验证不起作用时,请使用此清单:

  • [ ] 证书有效性 — 证书是否在其有效期内? 检查 NotBeforeNotAfter 日期。
  • [ ] 应用注册 — 证书的公钥是否已上传到正确的应用注册?
  • [ ] 指纹匹配 — 配置中的指纹是否与应用注册中的证书匹配?
  • [ ] 私钥访问 — 应用程序是否可以读取证书的私钥?
  • [ ] 密钥保管库权限 — 在密钥保管库源中,该标识是否拥有certificates/get权限和secrets/get权限?
  • [ ] 配置节 — 证书配置是否位于正确的节(AzureAdAzureAdB2C)下?
  • [ ] NuGet 包Microsoft.Identity.Web是最新的吗? 旧版本可能缺乏对某些证书源类型的支持。

启用日志记录

若要获取详细的诊断信息,请启用 MSAL 日志记录:

builder.Services.AddMicrosoftIdentityWebAppAuthentication(builder.Configuration, "AzureAd")
    .EnableTokenAcquisitionToCallDownstreamApi()
    .AddInMemoryTokenCaches();

builder.Logging.AddFilter("Microsoft.Identity", LogLevel.Debug);

查看有关证书加载、客户端断言创建和令牌获取的消息的日志。