Microsoft 身分識別平台 中的簽署金鑰變換

本文討論您需要瞭解 Microsoft 身分識別平台 用來簽署安全性令牌的公鑰。 請務必注意,這些密鑰會定期變換,而且在緊急情況下可以立即變換。 所有使用 Microsoft 身分識別平台的應用程式都應該能夠以程式設計方式處理金鑰變換程式。 您將瞭解金鑰的運作方式、如何評估變換對應用程式的影響,以及如何更新應用程式,或建立定期的手動變換程式,以在必要時處理密鑰變換。

Microsoft 身分識別平台 中的簽署金鑰概觀

Microsoft 身分識別平台 會使用以業界標準為基礎的公鑰密碼編譯,來建立本身與使用它的應用程式之間的信任。 實際上,這會以下列方式運作:Microsoft 身分識別平台 會使用由公鑰和私鑰組組成的簽署金鑰。 當使用者登入使用 Microsoft 身分識別平台 進行驗證的應用程式時,Microsoft 身分識別平台 會建立包含使用者相關信息的安全性令牌。 此令牌會由 Microsoft 身分識別平台 使用其私鑰簽署,然後才會傳送回應用程式。 若要確認令牌有效且源自 Microsoft 身分識別平台,應用程式必須使用租使用者 OpenID 連線 探索檔或 SAML/WS-Fed 同盟元數據檔中所公開 Microsoft 身分識別平台 公開的公鑰來驗證令牌的簽章。

基於安全性考慮,Microsoft 身分識別平台 的簽署密鑰會定期變換,並在緊急情況下立即變換。 這些金鑰變換之間沒有設定或保證的時間 - 任何與 Microsoft 身分識別平台 整合的應用程式都應該準備好處理金鑰變換事件,無論可能發生的頻率。 如果您的應用程式未處理突然重新整理,並嘗試使用過期的金鑰來驗證令牌上的簽章,您的應用程式會錯誤地拒絕令牌。 檢查更新每 24 小時是最佳作法,如果遇到未在應用程式快取中使用密鑰驗證的令牌,則會立即重新整理金鑰檔(最多每五分鐘一次)。

OpenID 連線 探索檔和同盟元數據檔中一律有一個以上的有效密鑰可用。 您的應用程式應該準備好使用檔中指定的任何和所有索引鍵,因為一個密鑰可能很快就會復原,另一個可能是它的取代,依此類推。 隨著我們支援新的平臺、新的雲端或新的驗證通訊協定,目前存在的密鑰數目可能會隨著時間變化,而 Microsoft 身分識別平台的內部架構。 JSON 回應中的索引鍵順序,或公開金鑰的順序,都不應被視為對您應用程式有意義的順序。

僅支援單一簽署密鑰的應用程式,或需要手動更新簽署金鑰的應用程式原本就較不安全且較不可靠。 它們應該更新為使用 標準連結庫 ,以確保它們一律使用最新的簽署密鑰,以及其他最佳做法。

如何評估您的應用程式是否會受到影響,以及該如何處理

應用程式處理金鑰變換的方式取決於變數,例如應用程式類型,或使用何種身分識別通訊協議和連結庫。 下列各節會評估最常見的應用程式類型是否受到金鑰變換的影響,並提供如何更新應用程式以支援自動變換或手動更新密鑰的指引。

本指南不適用於

  • 從 Microsoft Entra 應用連結庫新增的應用程式有個別的簽署密鑰指引。 更多資訊
  • 透過應用程式 Proxy 發佈的內部部署應用程式不需要擔心簽署金鑰。

存取資源的原生用戶端應用程式

只存取資源的應用程式(例如 Microsoft Graph、KeyVault、Outlook API 和其他 Microsoft API)只會取得令牌,並傳遞至資源擁有者。 假設它們未保護任何資源,它們不會檢查令牌,因此不需要確保其已正確簽署。

原生用戶端應用程式,無論是桌面或行動裝置,都屬於此類別,因此不會受到變換的影響。

Web 應用程式/API 存取資源

只存取資源的應用程式(例如 Microsoft Graph、KeyVault、Outlook API 和其他 Microsoft API)只會取得令牌,並傳遞至資源擁有者。 假設它們未保護任何資源,它們不會檢查令牌,因此不需要確保其已正確簽署。

使用僅限應用程式流程的 Web 應用程式和 Web API(用戶端認證/用戶端憑證)來要求令牌屬於此類別,因此不會受到變換的影響。

Web 應用程式/API 保護資源和使用 Azure App 服務 建置

Azure App 服務 的驗證/授權 (EasyAuth) 功能已具備自動處理密鑰變換所需的邏輯。

使用 ASP.NET OWIN OpenID 連線、WS-Fed 或 WindowsAzureActiveDirectoryBearerAuthentication 中間件來保護資源的 Web 應用程式/API

如果您的應用程式使用 ASP.NET OWIN OpenID 連線、WS-Fed 或 WindowsAzureActiveDirectoryBearerAuthentication 中間件,則它已經有自動處理密鑰變換的必要邏輯。

您可以在應用程式的Startup.cs或Startup.Auth.cs檔案中尋找下列任何代碼段,以確認您的應用程式正在使用其中任何一個代碼段。

app.UseOpenIdConnectAuthentication(
    new OpenIdConnectAuthenticationOptions
    {
        // ...
    });
app.UseWsFederationAuthentication(
    new WsFederationAuthenticationOptions
    {
        // ...
    });
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
    new WindowsAzureActiveDirectoryBearerAuthenticationOptions
    {
        // ...
    });

使用 .NET Core OpenID 連線 或 JwtBearerAuthentication 中間件保護資源的 Web 應用程式/API

如果您的應用程式使用 ASP.NET OWIN OpenID 連線 或 JwtBearerAuthentication 中間件,則它已經有自動處理密鑰變換的必要邏輯。

您可以在應用程式的Startup.cs或Startup.Auth.cs中尋找下列任何代碼段,以確認您的應用程式正在使用其中任何一個代碼段

app.UseOpenIdConnectAuthentication(
     new OpenIdConnectAuthenticationOptions
     {
         // ...
     });
app.UseJwtBearerAuthentication(
    new JwtBearerAuthenticationOptions
    {
     // ...
     });

使用 Node.js passport-azure-ad 模組保護資源的 Web 應用程式/API

如果您的應用程式使用 Node.js passport-ad 模組,則它已經有自動處理金鑰變換的必要邏輯。

您可以在應用程式的 app.js中搜尋下列代碼段,以確認您的應用程式 passport-ad

var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;

passport.use(new OIDCStrategy({
    //...
));

Web 應用程式/API 可保護資源,並使用 Visual Studio 2015 或更新版本建立

如果您的應用程式是使用 Visual Studio 2015 或更新版本中的 Web 應用程式範本所建置,而且您已從 [變更驗證] 功能表中選取 [公司或學校帳戶],則它已經有自動處理密鑰變換的必要邏輯。 此邏輯內嵌在 OWIN OpenID 連線 中間件中、擷取及快取 OpenID 連線 探索檔中的索引鍵,並定期重新整理它們。

如果您手動將驗證新增至解決方案,您的應用程式可能沒有必要的金鑰變換邏輯。 您可以自行撰寫,或使用任何其他連結庫或手動實作任何支援的通訊協定,遵循 Web 應用程式/API 中的步驟。

保護資源和使用Visual Studio 2013建立的Web應用程式

如果您的應用程式是使用 Visual Studio 2013 中的 Web 應用程式範本所建置,而且您已從 [變更驗證] 功能表中選取 [組織帳戶],則它已經有自動處理密鑰變換的必要邏輯。 此邏輯會將您組織的唯一標識符和簽署金鑰資訊儲存在與專案相關聯的兩個資料庫數據表中。 您可以在專案的 Web.config 檔案中找到資料庫的 連接字串。

如果您手動將驗證新增至解決方案,您的應用程式可能沒有必要的金鑰變換邏輯。 您必須自行撰寫,或使用任何其他連結庫或手動實作任何支援的通訊協定,遵循 Web 應用程式/API 中的步驟。

下列步驟可協助您確認邏輯在應用程式中正常運作。

  1. 在 Visual Studio 2013 中,開啟方案,然後在右側視窗的 [伺服器總管] 索引標籤上選取 。
  2. 展開 [數據 連線、Default 連線 ion,然後展開 [數據表]。 找出IssuingAuthorityKeys數據表,以滑鼠右鍵按兩下它,然後選取 [顯示資料表數據]。
  3. IssuingAuthorityKeys 數據表中,至少有一個數據列對應至密鑰的指紋值。 刪除資料表中的任何數據列。
  4. 以滑鼠右鍵按兩下 [租使用者] 資料表,然後選取 [ 顯示數據表數據]。
  5. Tenants 數據表中,至少有一個數據列對應至唯一目錄租用戶標識碼。 刪除資料表中的任何數據列。 如果您未刪除 Tenants 數據表和 IssuingAuthorityKeys 數據表中的數據列,您會在運行時間收到錯誤。
  6. 建置並執行應用程式。 登入您的帳戶之後,您可以停止應用程式。
  7. 返回伺服器總管,並查看IssuingAuthorityKeysTenants 資料表中的值。 您會發現它們已自動重新填入同盟元數據檔中的適當資訊。

使用 Visual Studio 2013 保護資源及建立的 Web API

如果您使用 Web API 範本在 Visual Studio 2013 中建立 Web API 應用程式,然後從 [變更驗證] 功能表中選取 [組織帳戶],則您的應用程式中已經有必要的邏輯。

如果您手動設定驗證,請遵循下列指示,瞭解如何設定 Web API 以自動更新其密鑰資訊。

下列代碼段示範如何從同盟元數據檔取得最新的密鑰,然後使用 JWT 令牌處理程式 來驗證令牌。 代碼段假設您將使用自己的快取機制來保存密鑰,以驗證 Microsoft 身分識別平台 未來的令牌,無論是在資料庫、組態檔或其他地方。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IdentityModel.Tokens;
using System.Configuration;
using System.Security.Cryptography.X509Certificates;
using System.Xml;
using System.IdentityModel.Metadata;
using System.ServiceModel.Security;
using System.Threading;

namespace JWTValidation
{
    public class JWTValidator
    {
        private string MetadataAddress = "[Your Federation Metadata document address goes here]";

        // Validates the JWT Token that's part of the Authorization header in an HTTP request.
        public void ValidateJwtToken(string token)
        {
            JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler()
            {
                // Do not disable for production code
                CertificateValidator = X509CertificateValidator.None
            };

            TokenValidationParameters validationParams = new TokenValidationParameters()
            {
                AllowedAudience = "[Your App ID URI goes here]",
                ValidIssuer = "[The issuer for the token goes here, such as https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/]",
                SigningTokens = GetSigningCertificates(MetadataAddress)

                // Cache the signing tokens by your desired mechanism
            };

            Thread.CurrentPrincipal = tokenHandler.ValidateToken(token, validationParams);
        }

        // Returns a list of certificates from the specified metadata document.
        public List<X509SecurityToken> GetSigningCertificates(string metadataAddress)
        {
            List<X509SecurityToken> tokens = new List<X509SecurityToken>();

            if (metadataAddress == null)
            {
                throw new ArgumentNullException(metadataAddress);
            }

            using (XmlReader metadataReader = XmlReader.Create(metadataAddress))
            {
                MetadataSerializer serializer = new MetadataSerializer()
                {
                    // Do not disable for production code
                    CertificateValidationMode = X509CertificateValidationMode.None
                };

                EntityDescriptor metadata = serializer.ReadMetadata(metadataReader) as EntityDescriptor;

                if (metadata != null)
                {
                    SecurityTokenServiceDescriptor stsd = metadata.RoleDescriptors.OfType<SecurityTokenServiceDescriptor>().First();

                    if (stsd != null)
                    {
                        IEnumerable<X509RawDataKeyIdentifierClause> x509DataClauses = stsd.Keys.Where(key => key.KeyInfo != null && (key.Use == KeyType.Signing || key.Use == KeyType.Unspecified)).
                                                             Select(key => key.KeyInfo.OfType<X509RawDataKeyIdentifierClause>().First());

                        tokens.AddRange(x509DataClauses.Select(token => new X509SecurityToken(new X509Certificate2(token.GetX509RawData()))));
                    }
                    else
                    {
                        throw new InvalidOperationException("There is no RoleDescriptor of type SecurityTokenServiceType in the metadata");
                    }
                }
                else
                {
                    throw new Exception("Invalid Federation Metadata document");
                }
            }
            return tokens;
        }
    }
}

保護資源並使用Visual Studio 2012建立的Web應用程式

如果您的應用程式是建置在 Visual Studio 2012 中,您可能會使用身分識別和存取工具來設定您的應用程式。 您也可能使用 驗證簽發者名稱登入 (VINR) 。 VINR 負責維護受信任識別提供者 (Microsoft 身分識別平台) 的相關信息,以及用來驗證其所簽發令牌的密鑰。 VINR 也可讓您輕鬆地下載與目錄相關聯的最新同盟元數據檔、檢查組態是否與最新檔過期,以及更新應用程式以視需要使用新密鑰,來自動更新儲存在 Web.config 檔案中的金鑰資訊。

如果您使用 Microsoft 所提供的任何程式碼範例或逐步解說檔建立應用程式,您的專案已包含金鑰變換邏輯。 您會發現下列程式代碼已存在於您的專案中。 如果您的應用程式還沒有此邏輯,請遵循下列步驟來新增它,並確認其正常運作。

  1. 方案總管 中,為適當的專案新增 System.IdentityModel 元件的參考
  2. 開啟Global.asax.cs檔案,並新增下列using指示詞:
    using System.Configuration;
    using System.IdentityModel.Tokens;
    
  3. 將下列方法新增至 Global.asax.cs 檔案:
    protected void RefreshValidationSettings()
    {
     string configPath = AppDomain.CurrentDomain.BaseDirectory + "\\" + "Web.config";
     string metadataAddress =
                   ConfigurationManager.AppSettings["ida:FederationMetadataLocation"];
     ValidatingIssuerNameRegistry.WriteToConfig(metadataAddress, configPath);
    }
    
  4. 在 Global.asax.cs 的 Application_Start() 方法中叫用 RefreshValidation 設定() 方法,如下所示:
    protected void Application_Start()
    {
     AreaRegistration.RegisterAllAreas();
     ...
     RefreshValidationSettings();
    }
    

遵循這些步驟之後,應用程式的 Web.config 將會使用同盟元數據檔中的最新資訊來更新,包括最新的密鑰。 每當應用程式集區在 IIS 中回收時,就會發生此更新;根據預設,IIS 會設定為每隔 29 小時回收應用程式一次。

請遵循下列步驟來確認密鑰變換邏輯是否正常運作。

  1. 確認應用程式使用上述程式代碼之後,請開啟 Web.config 檔案並流覽至< issuerNameRegistry> 區塊,特別尋找下列幾行:
    <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
         <authority name="https://sts.windows.net/aaaabbbb-0000-cccc-1111-dddd2222eeee/">
           <keys>
             <add thumbprint="3A38FA984E8560F19AADC9F86FE9594BB6AD049B" />
           </keys>
    
  2. 在 [新增指紋=“”> 設定中<,將任何字元取代為不同的字元,以變更指紋值。 儲存 Web.config 檔案。
  3. 建置應用程式,然後執行它。 如果您可以完成登入程式,您的應用程式會從目錄的同盟元數據檔下載所需的資訊,以成功更新密鑰。 如果您在登入時遇到問題,請閱讀使用 Microsoft 身分識別平台 將登入新增至 Web 應用程式一文,或下載並檢查下列程式代碼範例:適用於 Microsoft Entra ID 的多租使用者雲端應用程式,以確保應用程式中的變更正確無誤。

使用任何其他連結庫保護資源的 Web 應用程式/API,或手動實作任何支援的通訊協定

如果您使用其他連結庫或手動實作任何支援的通訊協定,您必須檢閱連結庫或實作,以確保密鑰是從 OpenID 連線 探索檔或同盟元數據檔擷取。 檢查此專案的方法之一,是在程式碼或連結庫的程式代碼中搜尋任何呼叫 OpenID 探索檔或同盟元數據檔。

如果金鑰儲存在應用程式中某處或硬式編碼,您可以依照本指引檔結尾的指示執行手動變換,手動擷取密鑰並據以更新金鑰。 強烈建議您增強應用程式,以支援使用本文中所述的任何方法自動變換,以避免未來中斷和額外負荷,如果 Microsoft 身分識別平台 增加變換頻率或有緊急頻外變換。

如何測試您的應用程式,以判斷它是否會受到影響

您可以使用下列 PowerShell 腳本來驗證應用程式是否支援自動密鑰變換。

若要使用PowerShell檢查及更新簽署金鑰,您需要 MSIdentityTools PowerShell 模組。

  1. 安裝 MSIdentityTools PowerShell 模組:

    Install-Module -Name MSIdentityTools
    
  2. 使用 連線-MgGraph 命令搭配系統管理員帳戶登入,以同意所需的範圍:

     Connect-MgGraph -Scope "Application.ReadWrite.All"
    
  3. 取得可用的簽署金鑰指紋清單:

    Get-MsIdSigningKeyThumbprint
    
  4. 挑選任何密鑰指紋,並設定 Microsoft Entra ID 搭配您的應用程式使用該金鑰(從 Microsoft Entra 系統管理中心取得應用程式識別碼):

    Update-MsIdApplicationSigningKeyThumbprint -ApplicationId <ApplicationId> -KeyThumbprint <Thumbprint>
    
  5. 登入以取得新的令牌來測試 Web 應用程式。 密鑰更新變更是即時的,但請確定您使用新的瀏覽器會話(例如,Internet Explorer 的“InPrivate”、“Chrome 的 ”Incognito“ 或 Firefox 的 ”Private“ 模式),以確保您已發行新的令牌。

  6. 針對每個傳回的簽署金鑰指紋,執行 Update-MsIdApplicationSigningKeyThumbprint Cmdlet 並測試 Web 應用程式登入程式。

  7. 如果 Web 應用程式正確登入,則支援自動變換。 如果沒有,請修改您的應用程式以支援手動變換。 如需詳細資訊,請參閱建立手動變換程式

  8. 執行下列腳本以還原為一般行為:

    Update-MsIdApplicationSigningKeyThumbprint -ApplicationId <ApplicationId> -Default
    

如果您的應用程式不支援自動變換,如何執行手動變換

如果您的應用程式不支援自動變換,您必須建立一個程式,定期監視 Microsoft 身分識別平台 的簽署密鑰,並據以執行手動變換。

若要使用 PowerShell 檢查及更新簽署金鑰,您需要 MSIdentityTools PowerShell 模組。

  1. MSIdentityTools安裝 PowerShell 模組:

    Install-Module -Name MSIdentityTools
    
  2. 取得最新的簽署密鑰(從 Microsoft Entra 系統管理中心取得租使用者識別碼):

    Get-MsIdSigningKeyThumbprint -Tenant <tenandId> -Latest
    
  3. 將此金鑰與應用程式目前硬式編碼或設定為使用的金鑰進行比較。

  4. 如果最新的金鑰與應用程式所使用的金鑰不同,請下載最新的簽署金鑰:

    Get-MsIdSigningKeyThumbprint -Latest -DownloadPath <DownloadFolderPath>
    
  5. 更新應用程式的程式代碼或組態,以使用新的金鑰。

  6. 設定 Microsoft Entra ID 以搭配您的應用程式使用該最新的密鑰(從 Microsoft Entra 系統管理中心取得應用程式識別碼):

    Get-MsIdSigningKeyThumbprint -Latest | Update-MsIdApplicationSigningKeyThumbprint -ApplicationId <ApplicationId>
    
  7. 登入以取得新的令牌來測試 Web 應用程式。 密鑰更新變更是即時的,但請確定您使用新的瀏覽器會話(例如,Internet Explorer 的“InPrivate”、“Chrome 的 ”Incognito“ 或 Firefox 的 ”Private“ 模式),以確保您已發行新的令牌。

  8. 如果您遇到任何問題,請還原為先前使用的金鑰,並連絡 Azure 支援:

    Update-MsIdApplicationSigningKeyThumbprint -ApplicationId <ApplicationId> -KeyThumbprint <PreviousKeyThumbprint>
    
  9. 更新應用程式以支援手動變換之後,請還原為一般行為:

    Update-MsIdApplicationSigningKeyThumbprint -ApplicationId <ApplicationId> -Default