0

I'd like to use UserNamePasswordValidator with wsDualHttpBinding but here is my problem:

If I use:

<security mode="Message" > <message clientCredentialType="UserName" /> </security>

I get this exception:

Identity check failed for outgoing message. The expected DNS identity of the remote endpoint was 'localhost' but the remote endpoint provided DNS claim 'Theater'. If this is a legitimate remote endpoint, you can fix the problem by explicitly specifying DNS identity 'Theater' as the Identity property of EndpointAddress when creating channel proxy.

If I change the DNS to Theater then it times out. If i don't use the security mode the UserNamePasswordValidator doesn't get called. I read that if I would use wsHttpBinding I could set the security to TransportWithMessageCredential and that would work but I need the dual. Here is the config:

     <system.serviceModel>
     <bindings>
      <wsDualHttpBinding>
        <binding maxReceivedMessageSize="268435456" maxBufferPoolSize="268435456">
          <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="268435456" maxBytesPerRead="4096" maxNameTableCharCount="2147483647" />
              <security mode="Message" >
            <message clientCredentialType="UserName"  />
          </security> 
        </binding>
      </wsDualHttpBinding>
    </bindings>
          <services>
      <service name="WcfService1.Service1" behaviorConfiguration="ServiceBehavior">      
        <endpoint address="" binding="wsDualHttpBinding" contract="WcfService1.IService1">
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>   
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
          <serviceMetadata httpGetEnabled="true"/>
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WcfService1.OperatorValidator, WcfService1"/>
            <serviceCertificate findValue="Theater" storeLocation="LocalMachine" storeName="TrustedPeople" x509FindType="FindBySubjectName"/>
          </serviceCredentials>   
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <protocolMapping>
      <add binding="basicHttpsBinding" scheme="https" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </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>

Validator:

namespace WcfService1
{
    public class OperatorValidator : UserNamePasswordValidator
    {

        public override void Validate(String userName, String password)
        {
            if (String.IsNullOrEmpty(userName) || String.IsNullOrEmpty(password))
                throw new SecurityTokenException("Username and password cannot be empty.");

            try
            {
                using (Theater4Entities entities = new Theater4Entities())
                {

                    Byte[] passwordBytes;

                    using (SHA1CryptoServiceProvider provider = new SHA1CryptoServiceProvider())

                    {
                        passwordBytes = provider.ComputeHash(Encoding.UTF8.GetBytes(password)); 
                    }

                    Operator currentOperator = entities.Operator.FirstOrDefault(op => op.UserName == userName);


                    if (currentOperator == null || !passwordBytes.SequenceEqual(currentOperator.UserPassword))

                        throw new SecurityTokenException("Username or password does not match.");


                }
            }
            catch (SecurityTokenException)
            {
                throw;
            }
            catch
            {
                throw new SecurityTokenException("Unexpected error occured.");
            }
        }
    }
}

And here is where I catch the exception:

public WAF1ClientViewModel()
{
    _callbackService = new RentalServiceCallback();
    InstanceContext context = new InstanceContext(_callbackService);
    _client = new Service1Client(context);
 }
.
.
.
  private void LoginExecuted(PasswordBox passwordBox)
    {
        try
        {
            _client.ClientCredentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;

            _client.ClientCredentials.UserName.UserName = UserName;
            _client.ClientCredentials.UserName.Password = passwordBox.Password;

            _client.Login(UserName);
            _isLoggedIn = true;
            OnLoginSuccess(); 
        }
        catch
        {
            OnLoginFailed();
        }
    }

I would be open to any alternative method.

FaNaT
  • 226
  • 4
  • 15

1 Answers1

1

WCF side

You would have to use Custom Binding and use authenticationMode="SecureConversation" as shown below

<customBinding>
    <binding name="CustomWSDualHttpBinding" receiveTimeout="00:10:00" sendTimeout="00:10:00">
      <reliableSession inactivityTimeout="00:01:00" maxPendingChannels="16384" maxTransferWindowSize="4096" maxRetryCount="2"/>
      <security authenticationMode="SecureConversation" requireDerivedKeys="true">
        <secureConversationBootstrap authenticationMode ="UserNameForCertificate"/>
      </security>
      <compositeDuplex />
      <oneWay />
      <textMessageEncoding />
      <httpTransport />
    </binding>
  </customBinding>

EDIT: To increase the maximum array length quota and change the buffer size use below binding

<binding name="CustomWSDualHttpBinding" receiveTimeout="00:10:00" sendTimeout="00:10:00">
      <reliableSession inactivityTimeout="00:01:00" maxPendingChannels="16384" maxTransferWindowSize="4096" maxRetryCount="2"/>
      <binaryMessageEncoding>
        <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      </binaryMessageEncoding>
      <security authenticationMode="SecureConversation" requireDerivedKeys="true">
        <secureConversationBootstrap authenticationMode ="UserNameForCertificate"/>
      </security>
      <compositeDuplex />
      <oneWay />
      <httpTransport hostNameComparisonMode="StrongWildcard" transferMode="Buffered" maxBufferPoolSize="1073741824" maxBufferSize="1073741824" maxReceivedMessageSize="1073741824" />
    </binding>

Include a service certificate and put that in the service behavior

<serviceBehaviors>
    <behavior name="passwordValidatorServiceBehavior">
      <serviceDebug includeExceptionDetailInFaults="true"/>
      <serviceMetadata httpGetEnabled="true"/>
      <serviceCredentials>
        <userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="WCFCallbackTry.Custom.CustomUserNameValidator.CustomUserNamePasswordValidator, WCFCallbackTry"/>
        <serviceCertificate findValue="9d4c78cde9d2b82d751a5416fd2eb6df98d3b236" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
      </serviceCredentials>
    </behavior>
  </serviceBehaviors>

Then expose the endpoints

<services>
  <service behaviorConfiguration="passwordValidatorServiceBehavior" name="WCFCallbackTry.Service1">
    <endpoint address="http://MachineName:8018/Service1.svc" bindingConfiguration="CustomWSDualHttpBinding" binding="customBinding"
      contract="WCFCallbackTry.IService" name="HttpEndPoint" />

    <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    <host>
      <baseAddresses>
        <add baseAddress="http://MachineName:8018/Service1.svc"/>
      </baseAddresses>
    </host>
  </service>
</services>

Client side Call the service as below

ServiceReference1.ServiceClient client = new ServiceReference1.ServiceClient(new System.ServiceModel.InstanceContext(new CallBack()), "HttpEndPoint");
        client.ClientCredentials.UserName.UserName = Environment.UserDomainName + @"\" + Environment.UserName;
        client.ClientCredentials.UserName.Password = "aWNhdGU/56gfhvYmplY3RD~";

Include the DNS in your code if necessary

EndpointIdentity identity = EndpointIdentity.CreateDnsIdentity("MachineName");
EndpointAddress endpointAddress = new EndpointAddress(uri, identity);
client.Endpoint.Address = endpointAddress;

Hope this helps

dera
  • 401
  • 2
  • 4
  • Well, it helped now it let's me log in if I enter the correct log in if I enter an incorrect then I get a time out exception (don't know why, shouldn't I get a SecurityTokenException ?). But if I want to get data from the service it throws a "he maximum array length quota (16384) has been exceeded while reading XML data." I previously had this problem but i set the max data size high enough. – FaNaT Apr 16 '14 at 08:47
  • @ZoltánNémeth -See the updated answer for maximum array length quota. The WCF service would generate the SecurityTokenException but in order to propagate that or any other user friendly message to the client, you would have to use FaultContracts – dera Apr 16 '14 at 09:50
  • I'm sorry that I'm giving you more trouble, but from this point I'm totally clueless. New exception: The server did not provide a meaningful reply; this might be caused by a contract mismatch, a premature session shutdown or an internal server error. – FaNaT Apr 16 '14 at 11:00
  • @ZoltánNémeth - Start WCF tracing. Refer [link](http://stackoverflow.com/questions/4271517/how-to-turn-on-wcf-tracing) – dera Apr 16 '14 at 11:39
  • Recived bad HTTP response Exception type: System.ServiceModel.ProtocolException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Message: The remote server returned an unexpected response: (413) Request Entity Too Large. – FaNaT Apr 16 '14 at 12:44
  • and this one: ontent Type application/soap+xml; charset=utf-8 was sent to a service expecting application/soap+msbin1. The client and service bindings may be mismatched. – FaNaT Apr 16 '14 at 13:04
  • @ZoltánNémeth -Change the maxReceivedMessageSize  in the custom binding as shown   – dera Apr 16 '14 at 13:08
  • @ZoltánNémeth - Is the client service reference updated. If yes try changing the binaryMessageEncoding to some other encoding. – dera Apr 16 '14 at 13:29
  • yeah, I always update the reference after modifying the service. Changed the encoding to textMessageEncoding and tried different message versions same result. – FaNaT Apr 16 '14 at 14:16
  • @ZoltánNémeth - Please refer [link](http://stackoverflow.com/questions/8250251/wcf-content-type-text-xml-charset-utf-8-was-not-supported-by-service) – dera Apr 17 '14 at 03:11
  • I found that google link but only now stroke me that I should manually copy the customBinding in to the App.config. Now it let's me log in and get data from the service but when the service wants to call back it freezes out don't even get an exception. So it is basically a one way connection :( – FaNaT Apr 17 '14 at 09:33
  • @ZoltánNémeth - I do not know whether you have configured your services over IIS or Windows host. Still you could check whether the firewall is blocking your port. Please see [link](http://stackoverflow.com/questions/59677/wsdualhttpbinding-for-duplex-callbacks) – dera Apr 17 '14 at 09:48
  • Finally I figured it out. I added this "[CallbackBehavior(UseSynchronizationContext = false)]" to the class where I tell the program what to do with the callbacks. But the main problem. There was some problem with the database I used to get the datas. Restarted it, re added the ado.net entity and now it is fast and works.. Thank you for your help I have no idea how could I do this shit without help. – FaNaT Apr 18 '14 at 09:20