wcf icon indicating copy to clipboard operation
wcf copied to clipboard

svcutil generating incorrect client for WSHttpBinding with SecurityMode.TransportWithMessageCredential

Open samsp-msft opened this issue 3 years ago • 2 comments

Defining a WCF Core service using WSHttpBinding with SecurityMode.TransportWithMessageCredential. Creating a client using .NET Framework, will correctly be able to call the service.

The generated code for a .NET core client does not, and fails.

The service endpoint is defined using:

 public void Configure(IApplicationBuilder app)
        {
            var wsHttpBindingWithCredential = new WSHttpBinding(SecurityMode.TransportWithMessageCredential);
            wsHttpBindingWithCredential.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

            app.UseServiceModel(builder =>
            {
                // Add the Echo Service
                builder.AddService<EchoService>(serviceOptions =>
                {
                    // Set a base address for all bindings to the service, and WSDL discovery
                    serviceOptions.BaseAddresses.Add(new Uri($"http://localhost:{Program.HTTP_PORT}/EchoService"));
                    serviceOptions.BaseAddresses.Add(new Uri($"https://localhost:{Program.HTTPS_PORT}/EchoService"));
                })
                // Add WSHttpBinding endpoints
                .AddServiceEndpoint<EchoService, IEchoService>(new WSHttpBinding(SecurityMode.None), "/wsHttp")
                .AddServiceEndpoint<EchoService, IEchoService>(new WSHttpBinding(SecurityMode.Transport), "/wsHttp")
                .AddServiceEndpoint<EchoService, IEchoService>(wsHttpBindingWithCredential, "/wsHttpUserPassword", ep => { ep.Name = "AuthenticatedEP"; });

                builder.ConfigureServiceHostBase<EchoService>(CustomUserNamePasswordValidator.AddToHost);

                // Configure WSDL to be available over http & https
                var serviceMetadataBehavior = app.ApplicationServices.GetRequiredService<CoreWCF.Description.ServiceMetadataBehavior>();
                serviceMetadataBehavior.HttpGetEnabled = serviceMetadataBehavior.HttpsGetEnabled = true;
            });
        }

The GetBindingForEndpoint generated code for the service definition is:

         if ((endpointConfiguration == EndpointConfiguration.AuthenticatedEP))
            {
                System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
                System.ServiceModel.Channels.TransportSecurityBindingElement userNameOverTransportSecurityBindingElement = System.ServiceModel.Channels.SecurityBindingElement.CreateUserNameOverTransportBindingElement();
                userNameOverTransportSecurityBindingElement.MessageSecurityVersion = System.ServiceModel.MessageSecurityVersion.WSSecurity11WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
                result.Elements.Add(userNameOverTransportSecurityBindingElement);
                System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
                result.Elements.Add(textBindingElement);
                System.ServiceModel.Channels.HttpsTransportBindingElement httpsBindingElement = new System.ServiceModel.Channels.HttpsTransportBindingElement();
                httpsBindingElement.AllowCookies = true;
                httpsBindingElement.MaxBufferSize = int.MaxValue;
                httpsBindingElement.MaxReceivedMessageSize = int.MaxValue;
                result.Elements.Add(httpsBindingElement);
                return result;
            }

Which is incorrectly trying to use CreateUserNameOverTransportBindingElement(); which is incorrect for this binding type.

samsp-msft avatar May 18 '22 17:05 samsp-msft

I have the same problem. Instead of CreateCertificateOverTransportBindingElement the client binding is generated with CreateUserNameOverTransportBindingElement.

Binding (Server)

var binding = new BasicHttpBinding(BasicHttpSecurityMode.TransportWithMessageCredential);
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
binding.Security.Message.ClientCredentialType = BasicHttpMessageCredentialType.Certificate;

Configuration (WcfTestClient)

<customBinding>
    <binding name="HttpsCertificate_ICoreService" sendTimeout="00:05:00">
        <security defaultAlgorithmSuite="Default" authenticationMode="CertificateOverTransport"
            requireDerivedKeys="true" securityHeaderLayout="Lax" includeTimestamp="true"
            messageSecurityVersion="WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10">
            <localClientSettings detectReplays="false" />
            <localServiceSettings detectReplays="false" />
        </security>
        <textMessageEncoding messageVersion="Soap11" />
        <httpsTransport requireClientCertificate="true" />
    </binding>
</customBinding>

Generated (Client)

if ((endpointConfiguration == EndpointConfiguration.HttpsCertificate_ICoreService))
{
    System.ServiceModel.Channels.CustomBinding result = new System.ServiceModel.Channels.CustomBinding();
    System.ServiceModel.Channels.TransportSecurityBindingElement userNameOverTransportSecurityBindingElement = System.ServiceModel.Channels.SecurityBindingElement.CreateUserNameOverTransportBindingElement();
    userNameOverTransportSecurityBindingElement.MessageSecurityVersion = System.ServiceModel.MessageSecurityVersion.WSSecurity10WSTrustFebruary2005WSSecureConversationFebruary2005WSSecurityPolicy11BasicSecurityProfile10;
    userNameOverTransportSecurityBindingElement.SecurityHeaderLayout = System.ServiceModel.Channels.SecurityHeaderLayout.Lax;
    result.Elements.Add(userNameOverTransportSecurityBindingElement);
    System.ServiceModel.Channels.TextMessageEncodingBindingElement textBindingElement = new System.ServiceModel.Channels.TextMessageEncodingBindingElement();
    textBindingElement.MessageVersion = System.ServiceModel.Channels.MessageVersion.Soap11;
    result.Elements.Add(textBindingElement);
    System.ServiceModel.Channels.HttpsTransportBindingElement httpsBindingElement = new System.ServiceModel.Channels.HttpsTransportBindingElement();
    httpsBindingElement.AllowCookies = true;
    httpsBindingElement.MaxBufferSize = int.MaxValue;
    httpsBindingElement.MaxReceivedMessageSize = int.MaxValue;
    result.Elements.Add(httpsBindingElement);
    return result;
}

petarpetrovt avatar Jun 14 '22 11:06 petarpetrovt

  1. dotnet-svcutil now generates WSHttpBinding instead of Custombinding with these changes in: https://github.com/dotnet/wcf/pull/4774/commits
  2. custom binding hard coded generation of CreateUserNameOverTransportBindingElement , PR #4843 addresses it

imcarolwang avatar Aug 24 '22 04:08 imcarolwang