2017-05-23 2 views
0

J'essaie d'autoriser un client Winforms dans l'application WCF à l'aide d'AD FS.Windows Server 2016 Service ADFS et WCF

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; 
    } 

Voici le message d'exception que je reçois dans la méthode TryWcf:

************** Exception Text ************** 
System.ServiceModel.Security.SecurityNegotiationException: SOAP security negotiation with 'https://s-pdc.cpofeatures.com/adfs/services/trust/2005/certificatemixed' for target 'https://s-pdc.cpofeatures.com/adfs/services/trust/2005/certificatemixed' failed. See inner exception for more details. ---> System.InvalidOperationException: The client certificate is not provided. Specify a client certificate in ClientCredentials. 

Server stack trace: 
    at System.ServiceModel.ClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement, Boolean disableInfoCard) 
    at System.ServiceModel.ClientCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement) 
    at System.ServiceModel.Security.SecurityProtocol.AddSupportingTokenProviders(SupportingTokenParameters supportingTokenParameters, Boolean isOptional, IList`1 providerSpecList) 
    at System.ServiceModel.Security.SecurityProtocol.OnOpen(TimeSpan timeout) 
    at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout) 
    at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout) 
    at System.ServiceModel.Channels.SecurityChannelFactory`1.ClientSecurityChannel`1.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) 

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 System.ServiceModel.ICommunicationObject.Open(TimeSpan timeout) 
    at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout) 
    --- End of inner exception stack trace --- 

Server stack trace: 
    at System.ServiceModel.Security.IssuanceTokenProviderBase`1.DoNegotiation(TimeSpan timeout) 
    at System.ServiceModel.Security.IssuanceTokenProviderBase`1.GetTokenCore(TimeSpan timeout) 
    at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout) 
    at System.ServiceModel.Security.Tokens.IssuedSecurityTokenProvider.GetTokenCore(TimeSpan timeout) 
    at System.IdentityModel.Selectors.SecurityTokenProvider.GetToken(TimeSpan timeout) 
    at System.ServiceModel.Security.SecurityProtocol.TryGetSupportingTokens(SecurityProtocolFactory factory, EndpointAddress target, Uri via, Message message, TimeSpan timeout, Boolean isBlockingCall, IList`1& supportingTokens) 
    at System.ServiceModel.Security.SymmetricSecurityProtocol.TryGetTokenSynchronouslyForOutgoingSecurity(Message message, SecurityProtocolCorrelationState correlationState, Boolean isBlockingCall, TimeSpan timeout, SecurityToken& token, SecurityTokenParameters& tokenParameters, SecurityToken& prerequisiteWrappingToken, IList`1& supportingTokens, SecurityProtocolCorrelationState& newCorrelationState) 
    at System.ServiceModel.Security.SymmetricSecurityProtocol.SecureOutgoingMessageCore(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState) 
    at System.ServiceModel.Security.MessageSecurityProtocol.SecureOutgoingMessage(Message& message, TimeSpan timeout, SecurityProtocolCorrelationState correlationState) 
    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.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 WindowsFormsApplication1.ServiceReference1.IService1.GetData(Int32 value) 
    at WindowsFormsApplication1.Form1.TryWcf(GenericXmlSecurityToken token) in c:\sources\adfs\WcfService1\WindowsFormsApplication1\Form1.cs:line 50 
    at WindowsFormsApplication1.Form1.button_Login_Click(Object sender, EventArgs e) in c:\sources\adfs\WcfService1\WindowsFormsApplication1\Form1.cs:line 40 

Voici le code utilisé pour obtenir un jeton 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; 
      } 
     } 

Web.config de la WCF service:

<?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> 

App.conf ig des WinForms a est énorme, il est généré avec Ajouter la fonction de service de référence, ainsi que je devais ajouter ce qui suit:

<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> 

Ainsi, la manière manuelle de la validation des œuvres symboliques, et je suis en mesure d'obtenir des réclamations dans la liste, y compris nom d'utilisateur, email, etc. de LDAP, mais le client de WCF fait quelque chose de différent.

Si je comprends bien, WCF essaie d'authentifier avec certificat: https://s-pdc.cpofeatures.com/adfs/services/trust/2005/certificatemixed

Quel certificat que je dois configurer, ou est-il possible de configurer WCF de travailler de la même façon fonctionne mon code personnalisé? Si je comprends bien, il suffit de valider le jeton.

Quoi d'autre manque-t-il ici?

MISE À JOUR:

J'ai aussi essayé:

var service = channelFactory.CreateChannelWithIssuedToken(token); 

Obtenir l'exception: "Provenant jeton de clé ne peut pas tirer la clé du secret"

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) 

Répondre

0

Vous pouvez essayer ce qui suit ,

void TryWcf(GenericXmlSecurityToken token) 
{ 
    ... 
    var service = channelFactory.CreateChannelWithIssuedToken(token); 
    ... 
} 

et en ajoutant les attributs suivants à votre WCF web.config

<security mode="TransportWithMessageCredential"> <message establishSecurityContext="true">

+0

Je reçois "Provenant Key Token ne peut pas tirer la clé du secret.", Mis à jour la question. –

+0

J'ai édité la réponse et ajouté une autre option qui peut aider. – Gilligan