Windows Server 2016 ADFS和WCF服务

Windows Server 2016 ADFS和WCF服务,wcf,token,adfs,claims-based-identity,windows-server-2016,Wcf,Token,Adfs,Claims Based Identity,Windows Server 2016,我正在尝试使用AD FS在WCF应用程序中授权Winforms客户端 private void button_Login_Click(object sender, EventArgs e) { RequestSecurityTokenResponse rstr; var genericToken = Token.GetToken(@"cpofeatures\testuser", "Abc_123", "https://app1.cpofeatur

我正在尝试使用AD FS在WCF应用程序中授权Winforms客户端

    private void button_Login_Click(object sender, EventArgs e)
    {
        RequestSecurityTokenResponse rstr;

        var genericToken = Token.GetToken(@"cpofeatures\testuser", "Abc_123", "https://app1.cpofeatures.com/Service1.svc", out rstr) as GenericXmlSecurityToken;

        textBox_Log.Text += genericToken.Id;

        // This one works fine.
        TryManualAuth(genericToken);

        // This one fails.
        TryWcf(genericToken);
    }


    void TryWcf(GenericXmlSecurityToken token)
    {
        ChannelFactory<IService1> channelFactory = new ChannelFactory<IService1>("WS2007FederationHttpBinding_IService1", new EndpointAddress("http://app1.cpofeatures.com/Service1.svc"));

        channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
        var service = channelFactory.CreateChannelWithActAsToken(token);
        service.GetData(123);

        Log("Result from service: " + service.GetData(123));
    }

    private void TryManualAuth(GenericXmlSecurityToken genericToken)
    {
        const string certSubject = "s-pdc.cpofeatures.com";
        //const string certSubject = "CN = ADFS Signing - s-pdc.cpofeatures.com";

        var relyingPartyId = "https://app1.cpofeatures.com/Service1.svc";

        if (genericToken != null)
        {
            //Setup the handlers needed to convert the generic token to a SAML Token
            var tokenHandlers = new SecurityTokenHandlerCollection(new SecurityTokenHandler[] { new SamlSecurityTokenHandler() });
            tokenHandlers.Configuration.AudienceRestriction = new AudienceRestriction();
            tokenHandlers.Configuration.AudienceRestriction.AllowedAudienceUris.Add(new Uri(relyingPartyId));
            var trusted = new TrustedIssuerNameRegistry(certSubject);
            tokenHandlers.Configuration.IssuerNameRegistry = trusted;
            //convert the generic security token to a saml token
            var samlToken = tokenHandlers.ReadToken(new XmlTextReader(new StringReader(genericToken.TokenXml.OuterXml)));
            //convert the saml token to a claims principal
            var identities = tokenHandlers.ValidateToken(samlToken);
            foreach (var claimsIdentity in identities)
            {
                var claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
                //Display token information
                Log("Name : " + claimsPrincipal.Identity.Name);
                Log("Auth Type : " + claimsPrincipal.Identity.AuthenticationType);
                Log("Is Authed : " + claimsPrincipal.Identity.IsAuthenticated);
                foreach (var c in claimsPrincipal.Claims)
                    Log(c.Type + " / " + c.Value);
            }
        }
    }

    //The token handler calls this to check the token is from a trusted issuer before converting it to a claims principal
    //In this case I authenticate this by checking the certificate name used to sign the tokenpublic
    class TrustedIssuerNameRegistry : IssuerNameRegistry
    {
        private string _certSubject;
        public TrustedIssuerNameRegistry(string certSubject) { _certSubject = certSubject; }
        public override string GetIssuerName(SecurityToken securityToken)
        {
            var x509Token = securityToken as X509SecurityToken; if (x509Token != null && x509Token.Certificate.SubjectName.Name != null && x509Token.Certificate.SubjectName.Name.Contains(_certSubject)) return x509Token.Certificate.SubjectName.Name;
            throw new SecurityTokenException("Untrusted issuer.");
        }
    }

    private void Log(string what)
    {
        textBox_Log.Text += what + Environment.NewLine;
    }
以下是用于从ADFS获取令牌的代码:

       internal class Token
       {
           public static SecurityToken GetToken(string username, string password, string appliesTo, out RequestSecurityTokenResponse rsts)
           {
               WS2007HttpBinding binding = new WS2007HttpBinding();
               binding.Security.Message.EstablishSecurityContext = false;
               binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
               binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
               binding.Security.Mode = SecurityMode.TransportWithMessageCredential;

               WSTrustChannelFactory trustChannelFactory = new WSTrustChannelFactory(binding, new EndpointAddress("https://s-pdc.cpofeatures.com/adfs/services/trust/13/usernamemixed"));
               trustChannelFactory.TrustVersion = TrustVersion.WSTrust13;
               trustChannelFactory.Credentials.UserName.UserName = username;
               trustChannelFactory.Credentials.UserName.Password = password;

               RequestSecurityToken requestToken = new RequestSecurityToken(RequestTypes.Issue) {
                   KeyType = KeyTypes.Bearer,
               };

               requestToken.AppliesTo = new EndpointReference(appliesTo);
               WSTrustChannel tokenClient = (WSTrustChannel)trustChannelFactory.CreateChannel();
               SecurityToken token = tokenClient.Issue(requestToken, out rsts);

               return token;
           }
       }
WCF服务的Web.config:

<?xml version="1.0"?>
<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="https://s-pdc.cpofeatures.com/FederationMetadata/2007-06/FederationMetadata.xml" />
    <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" />
    <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">
            <!--Certificate added by Identity and Access Tool for Visual Studio.-->
            <serviceCertificate findValue="CN=app1.cpofeatures.com" storeLocation="LocalMachine" storeName="My" x509FindType="FindBySubjectDistinguishedName">
            </serviceCertificate>
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add scheme="http" binding="ws2007FederationHttpBinding" />
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    <bindings>
      <ws2007FederationHttpBinding>
        <binding name="">
          <security mode="Message">
            <message>
              <issuerMetadata address="https://s-pdc.cpofeatures.com/adfs/services/trust/mex" />
            </message>
          </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://app1.cpofeatures.com/Service1.svc" />
        <add value="http://app1.cpofeatures.com/Service1.svc" />
      </audienceUris>
      <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
        <authority name="http://s-pdc.cpofeatures.com/adfs/services/trust">
          <keys>
            <add thumbprint="9E9DDC6708052E36D13020A9A3238016EDD57B60" />
          </keys>
          <validIssuers>
            <add name="http://s-pdc.cpofeatures.com/adfs/services/trust" />
          </validIssuers>
        </authority>
      </issuerNameRegistry>
      <!--certificationValidationMode set to "None" by the the Identity and Access Tool for Visual Studio. For development purposes.-->
      <certificateValidation certificateValidationMode="None" />
    </identityConfiguration>
  </system.identityModel>
</configuration>
获取异常:“派生密钥令牌无法从密钥派生密钥”


您可以尝试以下方法

void TryWcf(GenericXmlSecurityToken token)
{
    ...
    var service = channelFactory.CreateChannelWithIssuedToken(token);
    ...
}
并将以下属性添加到WCF web.config


我得到“派生密钥令牌无法从密钥派生密钥”。更新了问题。我编辑了答案并添加了另一个可能有用的选项。
  <system.identityModel>
    <identityConfiguration>
      <audienceUris>
        <add value="https://app1.cpofeatures.com/Service1.svc" />
        <add value="http://app1.cpofeatures.com/Service1.svc" />
      </audienceUris>
    </identityConfiguration>
  </system.identityModel>
</configuration>
var service = channelFactory.CreateChannelWithIssuedToken(token);
Server stack trace: 
   at System.ServiceModel.Security.Tokens.DerivedKeySecurityToken.Initialize(String id, Int32 generation, Int32 offset, Int32 length, String label, Byte[] nonce, SecurityToken tokenToDerive, SecurityKeyIdentifierClause tokenToDeriveIdentifier, String derivationAlgorithm, Boolean initializeDerivedKey)
   at System.ServiceModel.Security.SendSecurityHeader.SignWithSupportingTokens()
   at System.ServiceModel.Security.SendSecurityHeader.CompleteSecurityApplication()
   at System.ServiceModel.Security.SecurityAppliedMessage.OnWriteMessage(XmlDictionaryWriter writer)
   at System.ServiceModel.Channels.BufferedMessageWriter.WriteMessage(Message message, BufferManager bufferManager, Int32 initialOffset, Int32 maxSizeQuota)
   at System.ServiceModel.Channels.TextMessageEncoderFactory.TextMessageEncoder.WriteMessage(Message message, Int32 maxMessageSize, BufferManager bufferManager, Int32 messageOffset)
   at System.ServiceModel.Channels.HttpOutput.SerializeBufferedMessage(Message message, Boolean shouldRecycleBuffer)
   at System.ServiceModel.Channels.HttpOutput.Send(TimeSpan timeout)
   at System.ServiceModel.Channels.HttpChannelFactory`1.HttpRequestChannel.HttpChannelRequest.SendRequest(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Channels.SecurityChannelFactory`1.SecurityRequestChannel.Request(Message message, TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.DoOperation(SecuritySessionOperation operation, EndpointAddress target, Uri via, SecurityToken currentToken, TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionSecurityTokenProvider.GetTokenCore(TimeSpan timeout)
   at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout)
   at System.ServiceModel.Security.SecuritySessionClientSettings`1.ClientSecuritySessionChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.OnOpen(TimeSpan timeout)
   at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.CallOpenOnce.System.ServiceModel.Channels.ServiceChannel.ICallOnce.Call(ServiceChannel channel, TimeSpan timeout)
   at System.ServiceModel.Channels.ServiceChannel.CallOnceManager.CallOnce(TimeSpan timeout, CallOnceManager cascade)
   at System.ServiceModel.Channels.ServiceChannel.EnsureOpened(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)
void TryWcf(GenericXmlSecurityToken token)
{
    ...
    var service = channelFactory.CreateChannelWithIssuedToken(token);
    ...
}