1

我目前正在开发一个小型 ASP.NET 项目,该项目涉及针对第 3 方身份提供者对用户进行身份验证。我遇到了由 SP 发起的 SLO 请求的问题。

我正在使用WIF SAML 2.0 扩展来处理 SAML 协议。

调试我的应用程序时,它立即崩溃,给我以下错误消息(我删除了 RawData 的值,即用于签名的 X509Certificate 的公钥):

The token resolver is unable to resolve the token reference 'SecurityKeyIdentifier
   (
   IsReadOnly = False,
   Count = 1,
   Clause[0] = X509RawDataKeyIdentifierClause(RawData = )
   )
'.

Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.InvalidOperationException: The token resolver is unable to resolve the token reference 'SecurityKeyIdentifier
   (
   IsReadOnly = False,
   Count = 1,
   Clause[0] = X509RawDataKeyIdentifierClause(RawData = )
   )
'.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[InvalidOperationException: The token resolver is unable to resolve the token reference 'SecurityKeyIdentifier
    (
    IsReadOnly = False,
    Count = 1,
    Clause[0] = X509RawDataKeyIdentifierClause(RawData = )
    )
'.]
   System.IdentityModel.Selectors.SecurityTokenResolver.ResolveToken(SecurityKeyIdentifier keyIdentifier) +226933
   Microsoft.IdentityModel.Web.Saml2AuthenticationModule.ReadSelfMetadata(Stream stream, String fileName, String& entityId, EndpointConfiguration& endpointConfiguration, Boolean& signAuthenticationRequests, X509Certificate2& signingCertificate) +771

[ConfigurationErrorsException: ID4451: The signing key specified in metadata could not be found.  Update the key identifier in metadata or ensure the key is present in the ServiceTokenResolver.  See the inner exception for more details.]
   Microsoft.IdentityModel.Web.Saml2AuthenticationModule.ReadSelfMetadata(Stream stream, String fileName, String& entityId, EndpointConfiguration& endpointConfiguration, Boolean& signAuthenticationRequests, X509Certificate2& signingCertificate) +940
   Microsoft.IdentityModel.Web.Saml2AuthenticationModule..ctor() +606

[TargetInvocationException: Exception has been thrown by the target of an invocation.]
   System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck) +0
   System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache) +98
   System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache) +241
   System.Activator.CreateInstance(Type type, Boolean nonPublic) +69
   System.RuntimeType.CreateInstanceImpl(BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) +1136
   System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture, Object[] activationAttributes) +111
   System.Activator.CreateInstance(Type type, BindingFlags bindingAttr, Binder binder, Object[] args, CultureInfo culture) +23
   System.Web.HttpRuntime.CreateNonPublicInstance(Type type, Object[] args) +60
   System.Web.HttpApplication.BuildIntegratedModuleCollection(List`1 moduleList) +231
   System.Web.HttpApplication.GetModuleCollection(IntPtr appContext) +1365
   System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +95
   System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +194
   System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +339
   System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +253

[HttpException (0x80004005): Exception has been thrown by the target of an invocation.]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +9090876
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +97
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +256

我的 SP 元数据如下:

<md:EntityDescriptor
  xmlns:md="urn:oasis:names:tc:SAML:2.0:metadata"
  xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion"
  entityID="urn:mace:feide.no:services:no.ntnu.test_lesesalplass">

<!--Find where to turn off LogOutRequestSigning!-->
  <md:SPSSODescriptor
     WantAssertionsSigned="false" 
     AuthnRequestsSigned="false"
     protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">

    <md:KeyDescriptor use="signing">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>
            [...]
          </ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>

    <md:KeyDescriptor use="encryption">
      <ds:KeyInfo xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
        <ds:X509Data>
          <ds:X509Certificate>
            [...]
          </ds:X509Certificate>
        </ds:X509Data>
      </ds:KeyInfo>
    </md:KeyDescriptor>

    <md:NameIDFormat>
      urn:oasis:names:tc:SAML:2.0:nameid-format:transient
    </md:NameIDFormat>

    <md:SingleLogoutService
      Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
      Location="http://localhost:52681/LeseSalReg/saml/redirect/slo"
      ResponseLocation="http://localhost:52681/LeseSalReg/saml/redirect/sloresponse"
      />

    <md:AssertionConsumerService
      isDefault="true" index="0"
      Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
      Location="http://localhost:52681/LeseSalReg/saml/post/ac" 
      />

  </md:SPSSODescriptor>
</md:EntityDescriptor>

我的 web.config 如下所示:

<?xml version="1.0"?>

<configuration>

  <configSections>
    <section name="microsoft.identityModel" type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
    <section name="microsoft.identityModel.saml" type="Microsoft.IdentityModel.Web.Configuration.MicrosoftIdentityModelSamlSection, Microsoft.IdentityModel.Protocols"/>
  </configSections>

  <connectionStrings>
    <add name="ApplicationServices"
         connectionString="data source=.\SQLEXPRESS;Integrated Security=SSPI;AttachDBFilename=|DataDirectory|\aspnetdb.mdf;User Instance=true"
         providerName="System.Data.SqlClient" />
    <add name="StudySpaceRegEntities"
         connectionString="metadata=res://*/LeseSalRegEntity.csdl|res://*/LeseSalRegEntity.ssdl|res://*/LeseSalRegEntity.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=MATSHO;initial catalog=StudySpaceReg;integrated security=True;multipleactiveresultsets=True;App=EntityFramework&quot;"
         providerName="System.Data.EntityClient" />
  </connectionStrings>

  <system.web>

    <compilation debug="true" targetFramework="4.0" >
      <assemblies>
        <add assembly="Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      </assemblies>
    </compilation>

    <siteMap enabled="false">
      <providers>
        <clear/>
        <add name="AspNetXmlSiteMapProvider" type="System.Web.XmlSiteMapProvider" siteMapFile="Web.sitemap" securityTrimmingEnabled="true" />
      </providers>
    </siteMap>

    <authentication mode="Forms">
      <forms loginUrl="~/About.aspx" timeout="2880" defaultUrl="~/Register.aspx" />
    </authentication>

    <pages controlRenderingCompatibilityVersion="4.0" clientIDMode="AutoID" masterPageFile="~/Site.Master"/>

    <membership>
      <providers>
        <clear/>
        <add name="AspNetSqlMembershipProvider" type="System.Web.Security.SqlMembershipProvider" connectionStringName="ApplicationServices"
             enablePasswordRetrieval="false" enablePasswordReset="true" requiresQuestionAndAnswer="false" requiresUniqueEmail="false"
             maxInvalidPasswordAttempts="5" minRequiredPasswordLength="6" minRequiredNonalphanumericCharacters="0" passwordAttemptWindow="10"
             applicationName="/" />
      </providers>
    </membership>

    <profile>
      <providers>
        <clear/>
        <add name="AspNetSqlProfileProvider" type="System.Web.Profile.SqlProfileProvider" connectionStringName="ApplicationServices" applicationName="/"/>
      </providers>
    </profile>

    <roleManager enabled="false">
      <providers>
        <clear/>
        <add name="AspNetSqlRoleProvider" type="System.Web.Security.SqlRoleProvider" connectionStringName="ApplicationServices" applicationName="/" />
        <add name="AspNetWindowsTokenRoleProvider" type="System.Web.Security.WindowsTokenRoleProvider" applicationName="/" />
      </providers>
    </roleManager>

    <httpModules>
      <add name="Saml2AuthenticationModule" type="Microsoft.IdentityModel.Web.Saml2AuthenticationModule"/>
      <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule"/>
    </httpModules>
  </system.web>

  <system.webServer>

    <validation validateIntegratedModeConfiguration="false"/>
    <modules>
      <add name="Saml2AuthenticationModule" type="Microsoft.IdentityModel.Web.Saml2AuthenticationModule"/>
      <add name="SessionAuthenticationModule" type="Microsoft.IdentityModel.Web.SessionAuthenticationModule"/>
    </modules>

  </system.webServer>

  <microsoft.identityModel>
    <service>

      <audienceUris>
        <clear/>
        <add value="urn:mace:feide.no:services:no.ntnu.test_lesesalplass"/>
      </audienceUris>

      <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry">
        <trustedIssuers>
          <add name="https://idp-test.feide.no" thumbprint="fa982efdb69f26e8073c8f815a82a0c5885960a2"/>
        </trustedIssuers>
      </issuerNameRegistry>

      <securityTokenHandlers>
        <remove type="Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
        <add type="Microsoft.IdentityModel.Tokens.Saml2.Saml2SecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

        <remove type="Microsoft.IdentityModel.Tokens.X509SecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add type="Microsoft.IdentityModel.Tokens.X509SecurityTokenHandler, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />

        <securityTokenHandlerConfiguration saveBootstrapTokens="true" >
          <audienceUris mode="Always" />
        </securityTokenHandlerConfiguration>
      </securityTokenHandlers>
    </service>
  </microsoft.identityModel>

  <microsoft.identityModel.saml metadata="App_Data\SPmeta.xml">
    <identityProviders>
      <metadata file="App_Data\IdPmeta.xml"/>
    </identityProviders>
  </microsoft.identityModel.saml>

如果我在 SP 元数据中注释掉签名证书的 KeyDescriptor,应用程序不会崩溃。但是,当我尝试注销时,出现以下异常:

ID4450:“LogoutRequest”类型的消息必须在发送前签名。设置消息的 SigningCredentials 属性,或使用非空 X509Certificate2 配置 Saml2MessageDecorator。

我认为 WIF 不支持未签名的 SLO 请求,因此我必须对其进行签名。由于我无法理解的原因,SecurityTokenResolver 似乎无法解析元数据中定义的用于签名的 X509Certificate。

任何指针将不胜感激。

4

1 回答 1

1

解决了它:
问题源于对证书如何工作的理解不足:我试图使用相同的 X509Certificate 对 IdP 和 SP 消息进行签名。

创建了一个 X509Certificate并将其放在我计算机的“个人商店”中。对 web.config 进行了以下更改:

<microsoft.identityModel>
    <service>

      <serviceCertificate>
        <certificateReference
            storeName="My"
            x509FindType="FindByThumbprint"
            storeLocation="LocalMachine"
            findValue="82581cf4710c951d0f2e89e97c3a41d2b4a18890"
        />  
     </serviceCertificate>
[...]

证书的 base64 编码版本已放入 SP 元数据中。SecurityTokenHandler 然后能够签署我的 LogoutRequests。

于 2012-05-31T10:52:43.650 回答