2

I'm using ACS/Service Identities as a temporary STS while I get things into place. Unfortunately, while I appear to be able to get a SAML 1.1 token fine from ACS, the second I try to pass it into my WCF service things go crazy. As far as I can tell, the token isn't expired (it's being used promptly), I'm not sure how it could be invalid, and nothing I've done with logging has displayed to me any detail on what exactly could be wrong. I'm tempted to assign blame to the binding, because I've never done a formal WCF/WIF binding before. Can anyone see anything wrong with the client/server bindings I'm using (the client was generated via service reference), or suggest an alterative avenue of investigation?

BTW, both the server and client are running on the same development machine.

Web.config:

<configuration>
  <configSections>
    <section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </configSections>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
    <add key="ida:FederationMetadataLocation" value="--omitted--" />
    <add key="ida:ProviderSelection" value="productionSTS" />
  </appSettings>
  <location path="FederationMetadata">
    <system.web>
      <authorization>
        <allow users="*" />
      </authorization>
    </system.web>
  </location>
  <system.web>
    <compilation debug="true" targetFramework="4.5">
      <assemblies>
        <add assembly="System.Data.Entity, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </assemblies>
    </compilation>
    <httpRuntime targetFramework="4.5" />
  </system.web>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="false" />
          <serviceCredentials useIdentityConfiguration="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add scheme="https" binding="ws2007FederationHttpBinding" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <bindings>
      <ws2007FederationHttpBinding>
        <binding name="">
          <security mode="TransportWithMessageCredential">
            <message issuedKeyType="BearerKey" issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"/>
          </security>
        </binding>
      </ws2007FederationHttpBinding>
    </bindings>
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true" />
    <!--
      To browse web app root directory during debugging, set the value below to true.
      Set to false before deployment to avoid disclosing web app folder information.
    -->
    <directoryBrowse enabled="true" />
  </system.webServer>
  <system.identityModel>
    <identityConfiguration>
      <audienceUris>
        <add value="https://localhost:44300/Service1.svc" />
      </audienceUris>
      <issuerNameRegistry>
        <trustedIssuers>
          <add name="--omitted--" thumbprint="--omitted--"/>
        </trustedIssuers>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None"/>
    </identityConfiguration>
  </system.identityModel>
</configuration>

App.config:

  <system.serviceModel>
    <bindings>
        <basicHttpBinding>
            <binding name="BasicHttpBinding_IService1" />
        </basicHttpBinding>
        <ws2007FederationHttpBinding>
            <binding name="WS2007FederationHttpBinding_IService1">
                <security mode="TransportWithMessageCredential">
                    <message issuedKeyType="BearerKey" issuedTokenType="">
                        <tokenRequestParameters>
                            <trust:SecondaryParameters xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">
                                <trust:TokenType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1</trust:TokenType>
                                <trust:KeyType xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://docs.oasis-open.org/ws-sx/ws-trust/200512/Bearer</trust:KeyType>
                                <trust:CanonicalizationAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/10/xml-exc-c14n#</trust:CanonicalizationAlgorithm>
                                <trust:EncryptionAlgorithm xmlns:trust="http://docs.oasis-open.org/ws-sx/ws-trust/200512">http://www.w3.org/2001/04/xmlenc#aes256-cbc</trust:EncryptionAlgorithm>
                            </trust:SecondaryParameters>
                        </tokenRequestParameters>
                    </message>
                </security>
            </binding>
        </ws2007FederationHttpBinding>
    </bindings>
    <client>
        <endpoint address="http://localhost:51853/Service1.svc" binding="basicHttpBinding"
            bindingConfiguration="BasicHttpBinding_IService1" contract="ServiceReference1.IService1"
            name="BasicHttpBinding_IService1" />
        <endpoint address="https://localhost:44300/Service1.svc" binding="ws2007FederationHttpBinding"
            bindingConfiguration="WS2007FederationHttpBinding_IService1"
            contract="ServiceReference1.IService1" name="WS2007FederationHttpBinding_IService1" />
    </client>
  </system.serviceModel>

Client code:

    static void Main(string[] args)
    {
        var factory = new WSTrustChannelFactory(new UserNameWSTrustBinding(SecurityMode.TransportWithMessageCredential), new EndpointAddress("--Azure ACS URL omitted--"));
        factory.TrustVersion = TrustVersion.WSTrust13;
        factory.Credentials.UserName.UserName = "--omitted--";
        factory.Credentials.UserName.Password = "--omitted--";

        var rst = new RequestSecurityToken
        {
            RequestType = RequestTypes.Issue,
            KeyType = KeyTypes.Bearer,
            AppliesTo = new EndpointReference("https://localhost:44300/Service1.svc")
        };

        SecurityToken token = factory.CreateChannel().Issue(rst);

        var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
        binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
        binding.Security.Message.EstablishSecurityContext = false;

        var factory2 = new ChannelFactory<IService1>(binding, new EndpointAddress("https://localhost:44300/Service1.svc"));
        factory2.Credentials.SupportInteractive = false;
        factory2.Credentials.UseIdentityConfiguration = true;

        var proxy = factory2.CreateChannelWithIssuedToken(token);

        var info = proxy.GetData("testing"); // Exception thrown here
    }

Exception:

System.ServiceModel.Security.MessageSecurityException was unhandled
HResult=-2146233087
Message=An unsecured or incorrectly secured fault was received from the other party. See the inner FaultException for the fault code and detail.
Source=mscorlib
StackTrace:
  Server stack trace: 
   at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.ProcessReply(Message reply, SecurityProtocolCorrelationState correlationState, TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
   at System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)
Exception rethrown at [0]: 
   at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
   at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
   at TestWCFClient.ServiceReference1.IService1.GetData(String value)
   at TestWCFClient.Program.Main(String[] args) in c:\Users\nicole\Documents\Visual Studio 2012\Projects\TestWCFClient\Program.cs:line 43
   at System.AppDomain._nExecuteAssembly(RuntimeAssembly assembly, String[] args)
   at System.AppDomain.ExecuteAssembly(String assemblyFile, Evidence assemblySecurity, String[] args)
   at Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly()
   at System.Threading.ThreadHelper.ThreadStart_Context(Object state)
   at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
   at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
   at System.Threading.ThreadHelper.ThreadStart()
InnerException: System.ServiceModel.FaultException
   HResult=-2146233087
   Message=The message could not be processed. This is most likely because the action 'http://tempuri.org/IService1/GetData' is incorrect or because the message contains an invalid or expired security context token or because there is a mismatch between bindings. The security context token would be invalid if the service aborted the channel due to inactivity. To prevent the service from aborting idle sessions prematurely increase the Receive timeout on the service endpoint's binding.
   InnerException: 

ETA: In addition, I've tried: switching to SAML 2.0, switching to JWT with the preview JWT token handler, changing the receive timeout, switching the host machine time to UTC, syncing the host with the Windows Time Service explicitly, and waiting for five minutes after the token is issued before using it.

4

1 回答 1

1

The MessageSecurityException was right: it's a binding error.

I was mixing a couple of code samples together and got bitten by the mismatch. EstablishSecurityContext is not just window dressing, it is a real part of the binding, and the values must match between service and client.

My application code reads:

    var binding = new WS2007FederationHttpBinding(WSFederationHttpSecurityMode.TransportWithMessageCredential);
    binding.Security.Message.IssuedKeyType = SecurityKeyType.BearerKey;
    binding.Security.Message.EstablishSecurityContext = false;  // this line is the problem

The service binding is:

<bindings>
  <ws2007FederationHttpBinding>
    <binding name="">
      <security mode="TransportWithMessageCredential">
        <message issuedKeyType="BearerKey" issuedTokenType="http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV1.1"/> <!-- this line does not match -->
      </security>
    </binding>
  </ws2007FederationHttpBinding>
</bindings>

The service binding should be:

<bindings>
  <ws2007FederationHttpBinding>
    <binding name="">
      <security mode="TransportWithMessageCredential">
        <message issuedKeyType="BearerKey" establishSecurityContext="false"/> 
      </security>
    </binding>
  </ws2007FederationHttpBinding>
</bindings>

And presto, it works.

于 2013-04-26T21:14:34.740 回答