IDX10501:签名验证失败。无法匹配按键

问题描述 投票:0回答:4

请帮助我了解 ASP netcore 应用程序和 netcore Kestrel 托管应用程序的 JWT 令牌验证之间的区别。

有两个应用程序使用源代码验证令牌,如下所示:

public static IServiceCollection AddJwtToken(this IServiceCollection services, OAuthConfig config)
{
    services.AddMvc();
    services.AddAuthorization();

    Logger.DebugFormat("AddJwtBearer authority:{0} audience:{1}", config.GetAuthority(), config.Resource);

    services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
        .AddJwtBearer(options => new JwtBearerOptions
        {
            Authority = config.GetAuthority(),
            Audience = config.Resource,
    });

    return services;
}

它非常简单,如果从 asp net core 2.2 应用程序验证令牌,它会很好地工作

// in the asp.net core
var builder = WebHost.CreateDefaultBuilder(args);
builder
        .UseStartup<Startup>()
        .ConfigureKestrel(_ => _.ConfigureEndpoints())
        .UseSerilog();

还有另一个应用程序(控制台)使用

UseKestrel

启动相同的休息服务主机
//in the console app
var builder = WebHost.CreateDefaultBuilder()
    .UseNLog()
    .UseKestrel(_ => _.ConfigureEndpoints())
    .UseStartup<Startup>();

唯一的一个显着区别是,对于 ASP.NET Core,通过

UseKestrel
在控制台中有
ConfigureKestrel

相同的源代码(和配置)用于从 Azure AD 获取令牌。 请在这里找到它的要点。 它被配置为从

https://login.microsoftonline.com/{tenant}/v2.0
提供商获取令牌。两种情况使用相同的令牌端点、clientid、秘密和范围值。

问题在于

AddJwtBearer
在 ASP.NET Core 中验证令牌,但不在控制台应用程序中验证令牌。 错误是

Microsoft.IdentityModel.Tokens.SecurityTokenSignatureKeyNotFoundException: IDX10501: Signature validation failed. Unable to match keys:
kid: 'BB8CeFVqyaGrGNuehJIiL4dfjzw',
token: '{"typ":"JWT","alg":"RS256","kid":"BB8CeFVqyaGrGNuehJIiL4dfjzw"}.{"aud":"2c163c99-935b-4362-ae0d-657f589f5565","iss":"https://login.microsoftonline.com/{tenantidhere}/v2.0

为什么 asp.net core 主机验证令牌(对于第一个

AddJwtBearer
实现)而控制台主机失败?

谢谢你

c# asp.net-core azure-active-directory openid
4个回答
37
投票

为了解决此错误,我必须从 openid 提供商加载密钥,如下所示:

Logger.DebugFormat("AddJwtBearer authority:{0} audience:{1}", config.GetAuthority(), config.Resource);

IList<string> validissuers = new List<string>()
{
    config.GetAuthority(),
};

var configManager = new ConfigurationManager<OpenIdConnectConfiguration>($"{validissuers.Last()}/.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());

var openidconfig = configManager.GetConfigurationAsync().Result;

services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
    .AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, _ =>
    {
        _.TokenValidationParameters = new Microsoft.IdentityModel.Tokens.TokenValidationParameters()
        {
            ValidateAudience = true,
            ValidAudience = config.Resource,

            ValidateIssuer = true,
            ValidIssuers = new[] { config.GetAuthority() },

            ValidateIssuerSigningKey = true,
            IssuerSigningKeys = openidconfig.SigningKeys,

            RequireExpirationTime = true,
            ValidateLifetime = true,
            RequireSignedTokens = true,
        };

        _.RequireHttpsMetadata = false;

    });

它开始适用于这两种情况。但是旧的

AddJwtBearer
实现和新的实现有什么区别(与密钥验证相关)?使用
IssuerSigningKeys = openidconfig.SigningKeys
下载和提供密钥,但为什么它没有通过
.well-known/openid-configuration
中间件使用
AddJwtBearer
自动加载?


21
投票

就我而言,相同的错误是由于无意中使用了从一个环境(https://dev/identity)接收并在另一个环境(即 http://local/identity)中验证的令牌。


2
投票

就我而言,这从来都不是我的代码的问题,而是 Azure AD B2C 的默认登录-注册用户流程的问题。

在我意识到这实际上可能是仅存在于用户流程中的问题之前,我已经经历了整个潜在修复的兔子洞。

如果您使用用户流程,我建议您尝试创建自定义策略,看看问题是否仍然存在。

我按照文档创建了 OpenId connect SignIn-SignUp 自定义策略: https://learn.microsoft.com/en-us/azure/active-directory-b2c/identity-provider-generic-openid-connect?pivots=b2c-custom-policy

我在下面添加了我自己的代码

B2C_1A_signup_signin_SSO

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
  PolicySchemaVersion="0.3.0.0"
  TenantId="yourtenant.onmicrosoft.com"
  PolicyId="B2C_1A_signup_signin_SSO"
  PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_signup_signin_SSO">

  <BasePolicy>
    <TenantId>yourtenant.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkExtensions_SSO</PolicyId>
  </BasePolicy>

  <RelyingParty>
    <DefaultUserJourney ReferenceId="SignUpOrSignInSSO" />
    <Endpoints>
      <!--points to refresh token journey when app makes refresh token request-->
      <Endpoint Id="Token" UserJourneyReferenceId="RedeemRefreshToken" />

    </Endpoints>
    <TechnicalProfile Id="PolicyProfile">
      <DisplayName>PolicyProfile</DisplayName>
      <Protocol Name="OpenIdConnect" />
      <OutputClaims>
        <OutputClaim ClaimTypeReferenceId="displayName" />
        <OutputClaim ClaimTypeReferenceId="givenName" />
        <OutputClaim ClaimTypeReferenceId="surname" />
        <OutputClaim ClaimTypeReferenceId="email" />
        <OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="sub"/>
        <OutputClaim ClaimTypeReferenceId="identityProvider" />
        <OutputClaim ClaimTypeReferenceId="tenantId" AlwaysUseDefaultValue="true" DefaultValue="{Policy:TenantObjectId}" />

      </OutputClaims>
      <SubjectNamingInfo ClaimType="sub" />
    </TechnicalProfile>
  </RelyingParty>
</TrustFrameworkPolicy>

B2C_1A_TrustFrameworkExtensions_SSO

<?xml version="1.0" encoding="utf-8" ?>
<TrustFrameworkPolicy xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06" PolicySchemaVersion="0.3.0.0" TenantId="yourtenant.onmicrosoft.com" PolicyId="B2C_1A_TrustFrameworkExtensions_SSO" PublicPolicyUri="http://yourtenant.onmicrosoft.com/B2C_1A_TrustFrameworkExtensions_SSO">

  <BasePolicy>
    <TenantId>yourtenant.onmicrosoft.com</TenantId>
    <PolicyId>B2C_1A_TrustFrameworkLocalization</PolicyId>
  </BasePolicy>
  <BuildingBlocks>
    <ClaimsSchema>
      <ClaimType Id="issuerUserId">
        <DisplayName>Username</DisplayName>
        <DataType>string</DataType>
        <UserHelpText/>
        <UserInputType>TextBox</UserInputType>
        <Restriction>
          <Pattern RegularExpression="^[a-zA-Z0-9]+[a-zA-Z0-9_-]*$" HelpText="The username you provided is not valid. It must begin with an alphabet or number and can contain alphabets, numbers and the following symbols: _ -" />
        </Restriction>
      </ClaimType>
      <ClaimType Id="alternativeSecurityId">
        <DisplayName>AlternativeSecurityId</DisplayName>
        <DataType>string</DataType>
        <UserHelpText/>
      </ClaimType>

      <ClaimType Id="identityProvider">
        <DisplayName>Identity Provider</DisplayName>
        <DataType>string</DataType>
        <DefaultPartnerClaimTypes>
          <Protocol Name="OAuth2" PartnerClaimType="idp" />
          <Protocol Name="OpenIdConnect" PartnerClaimType="idp" />
          <Protocol Name="SAML2" PartnerClaimType="http://schemas.microsoft.com/identity/claims/identityprovider" />
        </DefaultPartnerClaimTypes>
        <UserHelpText/>
      </ClaimType>


      <ClaimType Id="upnUserName">
        <DisplayName>UPN User Name</DisplayName>
        <DataType>string</DataType>
        <UserHelpText>The user name for creating user principal name.</UserHelpText>
      </ClaimType>
    </ClaimsSchema>
    <ClaimsTransformations>
      <ClaimsTransformation Id="CreateRandomUPNUserName" TransformationMethod="CreateRandomString">
        <InputParameters>
          <InputParameter Id="randomGeneratorType" DataType="string" Value="GUID" />
        </InputParameters>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="upnUserName" TransformationClaimType="outputClaim" />
        </OutputClaims>
      </ClaimsTransformation>

      <ClaimsTransformation Id="CreateUserPrincipalName" TransformationMethod="FormatStringClaim">
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="upnUserName" TransformationClaimType="inputClaim" />
        </InputClaims>
        <InputParameters>
          <InputParameter Id="stringFormat" DataType="string" Value="cpim_{0}@{RelyingPartyTenantId}" />
        </InputParameters>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="userPrincipalName" TransformationClaimType="outputClaim" />
        </OutputClaims>
      </ClaimsTransformation>

      <ClaimsTransformation Id="CreateAlternativeSecurityId" TransformationMethod="CreateAlternativeSecurityId">
        <InputClaims>
          <InputClaim ClaimTypeReferenceId="issuerUserId" TransformationClaimType="key" />
          <InputClaim ClaimTypeReferenceId="identityProvider" TransformationClaimType="identityProvider" />
        </InputClaims>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="alternativeSecurityId" TransformationClaimType="alternativeSecurityId" />
        </OutputClaims>
      </ClaimsTransformation>

      <ClaimsTransformation Id="CreateSubjectClaimFromAlternativeSecurityId" TransformationMethod="CreateStringClaim">
        <InputParameters>
          <InputParameter Id="value" DataType="string" Value="Not supported currently. Use oid claim." />
        </InputParameters>
        <OutputClaims>
          <OutputClaim ClaimTypeReferenceId="sub" TransformationClaimType="createdClaim" />
        </OutputClaims>
      </ClaimsTransformation>
    </ClaimsTransformations>
  </BuildingBlocks>

  <ClaimsProviders>
    <ClaimsProvider>
      <Domain>contoso.com</Domain>
      <DisplayName>Login with Contoso SSO</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="contoso-OpenIdConnect">
          <DisplayName>Contoso SSO</DisplayName>
          <Description>Login with your Contoso account</Description>
          <Protocol Name="OpenIdConnect"/>
          <Metadata>
            <Item Key="METADATA">https://login.microsoftonline.com/contoso.com/v2.0/.well-known/openid-configuration</Item>
            <Item Key="client_id">your-client-id</Item>
            <Item Key="response_types">code</Item>
            <Item Key="scope">openid profile</Item>
            <Item Key="response_mode">form_post</Item>
            <Item Key="HttpBinding">POST</Item>
            <Item Key="UsePolicyInRedirectUri">false</Item>
          </Metadata>
          <CryptographicKeys>
            <Key Id="client_secret" StorageReferenceId="B2C_1A_ContosoOidcSecret"/>
          </CryptographicKeys>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="issuerUserId" PartnerClaimType="oid"/>
            <OutputClaim ClaimTypeReferenceId="tenantId" PartnerClaimType="tid"/>
            <OutputClaim ClaimTypeReferenceId="givenName" PartnerClaimType="given_name" />
            <OutputClaim ClaimTypeReferenceId="surName" PartnerClaimType="family_name" />
            <OutputClaim ClaimTypeReferenceId="displayName" PartnerClaimType="name" />
            <OutputClaim ClaimTypeReferenceId="email" PartnerClaimType="email" />
            <OutputClaim ClaimTypeReferenceId="authenticationSource" DefaultValue="socialIdpAuthentication" AlwaysUseDefaultValue="true" />
            <OutputClaim ClaimTypeReferenceId="identityProvider" PartnerClaimType="iss" />
            <OutputClaim ClaimTypeReferenceId="objectId" />
          </OutputClaims>
          <OutputClaimsTransformations>
            <OutputClaimsTransformation ReferenceId="CreateRandomUPNUserName"/>
            <OutputClaimsTransformation ReferenceId="CreateUserPrincipalName"/>
            <OutputClaimsTransformation ReferenceId="CreateAlternativeSecurityId"/>
            <OutputClaimsTransformation ReferenceId="CreateSubjectClaimFromAlternativeSecurityId"/>
          </OutputClaimsTransformations>
          <UseTechnicalProfileForSessionManagement ReferenceId="SM-SocialLogin"/>
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>

    <ClaimsProvider>
      <DisplayName>Session Management</DisplayName>
      <TechnicalProfiles>
        <TechnicalProfile Id="SM-Noop">
          <DisplayName>Noop Session Management Provider</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.NoopSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </TechnicalProfile>

        <TechnicalProfile Id="SM-AAD">
          <DisplayName>Session Mananagement Provider</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.DefaultSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <PersistedClaims>
            <PersistedClaim ClaimTypeReferenceId="objectId" />

            <PersistedClaim ClaimTypeReferenceId="authenticationSource" />
            <PersistedClaim ClaimTypeReferenceId="identityProvider" />
            <PersistedClaim ClaimTypeReferenceId="newUser" />
            <PersistedClaim ClaimTypeReferenceId="executed-SelfAsserted-Input" />
          </PersistedClaims>
          <OutputClaims>
            <OutputClaim ClaimTypeReferenceId="objectIdFromSession" DefaultValue="true" />
          </OutputClaims>
        </TechnicalProfile>

        <!-- Profile name is being used to disambiguate AAD session between sign up and sign in -->
        <TechnicalProfile Id="SM-SocialSignup">
          <IncludeTechnicalProfile ReferenceId="SM-AAD" />
        </TechnicalProfile>

        <TechnicalProfile Id="SM-SocialLogin">
          <DisplayName>Session Mananagement Provider</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.ExternalLoginSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
          <Metadata>
            <Item Key="AlwaysFetchClaimsFromProvider">true</Item>
          </Metadata>
          <PersistedClaims>
            <PersistedClaim ClaimTypeReferenceId="alternativeSecurityId" />
          </PersistedClaims>
        </TechnicalProfile>

        <!-- Session management technical profile for OIDC based tokens -->
        <TechnicalProfile Id="SM-jwt-issuer">
          <DisplayName>Session Management Provider</DisplayName>
          <Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.OAuthSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
        </TechnicalProfile>
      </TechnicalProfiles>
    </ClaimsProvider>
  </ClaimsProviders>

  <UserJourneys>
    <UserJourney Id="SignUpOrSignInSSO">
      <OrchestrationSteps>

        <OrchestrationStep Order="1" Type="CombinedSignInAndSignUp" ContentDefinitionReferenceId="api.signuporsignin">
          <ClaimsProviderSelections>
            <ClaimsProviderSelection TargetClaimsExchangeId="ContosoOpenIdExchange" />
          </ClaimsProviderSelections>
        </OrchestrationStep>

        <OrchestrationStep Order="2" Type="ClaimsExchange">
          <ClaimsExchanges>
            <ClaimsExchange Id="ContosoOpenIdExchange" TechnicalProfileReferenceId="contoso-OpenIdConnect" />
          </ClaimsExchanges>
        </OrchestrationStep>

        <OrchestrationStep Order="3" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="JwtIssuer" />

      </OrchestrationSteps>
      <ClientDefinition ReferenceId="DefaultWeb" />
    </UserJourney>

  </UserJourneys>

</TrustFrameworkPolicy>


0
投票

这很奇怪,但我在一个小型Azure Web应用程序中遇到了同样的错误,因为我的部署配置参数没有按照我的预期更新,而是保留下来并指向以前的权威机构、发行者和受众。在我手动更新这些配置值后,一切都按预期工作。

© www.soinside.com 2019 - 2024. All rights reserved.