locked
.Net Core consuming WCF service - The target principal name is incorrect RRS feed

  • Question

  • User-50787436 posted

    Hi all,

    I have WCF services written in .Net framework 4.0 (C#), exposed with basicHttpBinding, with the following binding configuration:

    <system.serviceModel>
    	<services>
    		<service behaviorConfiguration="APIServiceBehavior" name="MyService">
    			<endpoint binding="basicHttpBinding" bindingConfiguration="SecureAPIService" contract="IMyService" />
    			<endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="" name="mexHttpBinding" contract="IMetadataExchange" />
    		</service>
    	</services>
    	<behaviors>
          <serviceBehaviors>
    		<behavior name="APIServiceBehavior">
              <serviceDebug includeExceptionDetailInFaults="true" />
              <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
              <dataContractSerializer maxItemsInObjectGraph="2147483647" />
            </behavior>
    	  </serviceBehaviors>
    	</behaviors>  
        <bindings>
          <basicHttpBinding>
    	  <binding name="SecureAPIService" closeTimeout="00:10:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
              <readerQuotas maxDepth="32" maxStringContentLength="2097152" maxArrayLength="16384" maxBytesPerRead="8192" maxNameTableCharCount="16384"/>
    					<security mode="Transport">
    						<transport clientCredentialType="Windows"/>
    					</security>
    				</binding>
    		</basicHttpBinding>
    	</bindings>
    </system.serviceModel>

    This is hosted in IIS (Windows 2008 Server R2), with windows authentication enabled, first Negotiate, then NTLM.

    Then I have a .Net Core 3.1 API, hosted on prem on a different machine than the WCF service. It runs the following code to create the channel:

    private T CreateChannel(string wsUrl)
            {
                BasicHttpsBinding binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.Windows;
    
                binding.MaxBufferSize = int.MaxValue;
                binding.MaxBufferPoolSize = int.MaxValue;
                binding.MaxReceivedMessageSize = int.MaxValue;
                binding.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
                binding.AllowCookies = true;
                binding.ReceiveTimeout = TimeSpan.FromSeconds(60);
                binding.CloseTimeout = TimeSpan.FromSeconds(60);
    
                EndpointIdentity endpointIdentity = new UpnEndpointIdentity("myuser@mydomain");
    
                ChannelFactory<T> factory = new ChannelFactory<T>(binding, new EndpointAddress(new Uri(wsUrl), endpointIdentity));
    
                factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication()
                {
                    CertificateValidationMode = X509CertificateValidationMode.None
                };
    
                factory.Credentials.Windows.ClientCredential = new NetworkCredential() { UserName = "user", Password = "pass", Domain = "domain"};
    
                T channel = factory.CreateChannel();
    
                return channel;
    
            }

    The problem is when I call the service, I get the following error:

    The target principal name is incorrect  I've tried to change windows credentials on request to match the ones on the application pool on the WCF remote service, no success.  

    The only thing I've accomplished so far is to change the client credential type to Ntlm on the API side when I create the channel and it worked, but this doesn't give me the confidence, not sure.  Does this behavior make any sense?  

    BTW let me add that the API is on one domain and the WCF services are on another domain, and there is a trust relationship. But I was hoping to achieve this without having to specify Ntlm as the client credential type.  

    Is this the expected behavior?  I'd appreciate any help, hope I've explained myself well enough, thanks in advance 

    Tuesday, January 12, 2021 7:50 PM

Answers

  • User-50787436 posted

    Hi all,

    this situation seems to be something not related with this setup, for the moment we've moved on by using the following configurations:

    <system.serviceModel>
    	<services>
    		<service behaviorConfiguration="APIServiceBehavior" name="MyService">
    			<endpoint binding="basicHttpBinding" bindingConfiguration="SecureAPIService" contract="IMyService" />
    			<endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="" name="mexHttpBinding" contract="IMetadataExchange" />
    		</service>
    	</services>
    	<behaviors>
          <serviceBehaviors>
    		<behavior name="APIServiceBehavior">
              <serviceDebug includeExceptionDetailInFaults="true" />
              <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
              <dataContractSerializer maxItemsInObjectGraph="2147483647" />
            </behavior>
    	  </serviceBehaviors>
    	</behaviors>  
        <bindings>
          <basicHttpBinding>
    	  <binding name="SecureAPIService" closeTimeout="00:10:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
              <readerQuotas maxDepth="32" maxStringContentLength="2097152" maxArrayLength="16384" maxBytesPerRead="8192" maxNameTableCharCount="16384"/>
    					<security mode="Transport">
    					</security>
    				</binding>
    		</basicHttpBinding>
    	</bindings>
    </system.serviceModel>
    private T CreateChannel(string wsUrl)
            {
                BasicHttpsBinding binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
    
                binding.MaxBufferSize = int.MaxValue;
                binding.MaxBufferPoolSize = int.MaxValue;
                binding.MaxReceivedMessageSize = int.MaxValue;
                binding.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
                binding.AllowCookies = true;
                binding.ReceiveTimeout = TimeSpan.FromSeconds(60);
                binding.CloseTimeout = TimeSpan.FromSeconds(60);
    			
                ChannelFactory<T> factory = new ChannelFactory<T>(binding, new EndpointAddress(wsUrl));
    
                factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication()
                {
                    CertificateValidationMode = X509CertificateValidationMode.None
                };
    
                factory.Credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
    
                T channel = factory.CreateChannel();
    
                return channel;
    
            }

    So basically on the WCF side we've removed the need for a clientCredential, and on the consumer (the .Net Core API) we've removed it as well, along with the upn.

    We will look at this later but it's working and even when compared with previous working version of Ntlm it is faster.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 15, 2021 4:26 PM

All replies

  • User1312693872 posted

    Hi,pacojones

    This may be caused by EndpointIdentity. I tried to use the ChannelFactory(ServiceEndpoint endpoint) method and found that it can be called

    normally, so I suggest you try to delete the endpointIdentity parameter.

    ChannelFactory<T> factory = new ChannelFactory<T>(binding, new EndpointAddress(new Uri(wsUrl)));

    Best Regards,

    Jerry Cai

    Wednesday, January 13, 2021 9:13 AM
  • User-50787436 posted

    Hi Jerry thank you for the reply. I also tried that way and it doesn't work the same result.

    I found another solution, so the working options on the table are:

    • Set the clientCredentialType as Ntlm on the .Net Core when calling the service
    • Setting the clientCredentialType to None on the WCF service binding, which means removing a layer of security on the process

    Other option than these two aren't working

    The Upn seems to make no difference on this

    Wednesday, January 13, 2021 2:34 PM
  • User-50787436 posted

    Hi all,

    this situation seems to be something not related with this setup, for the moment we've moved on by using the following configurations:

    <system.serviceModel>
    	<services>
    		<service behaviorConfiguration="APIServiceBehavior" name="MyService">
    			<endpoint binding="basicHttpBinding" bindingConfiguration="SecureAPIService" contract="IMyService" />
    			<endpoint address="mex" binding="mexHttpBinding" bindingConfiguration="" name="mexHttpBinding" contract="IMetadataExchange" />
    		</service>
    	</services>
    	<behaviors>
          <serviceBehaviors>
    		<behavior name="APIServiceBehavior">
              <serviceDebug includeExceptionDetailInFaults="true" />
              <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
              <dataContractSerializer maxItemsInObjectGraph="2147483647" />
            </behavior>
    	  </serviceBehaviors>
    	</behaviors>  
        <bindings>
          <basicHttpBinding>
    	  <binding name="SecureAPIService" closeTimeout="00:10:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="65536" maxBufferPoolSize="524288" maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
              <readerQuotas maxDepth="32" maxStringContentLength="2097152" maxArrayLength="16384" maxBytesPerRead="8192" maxNameTableCharCount="16384"/>
    					<security mode="Transport">
    					</security>
    				</binding>
    		</basicHttpBinding>
    	</bindings>
    </system.serviceModel>
    private T CreateChannel(string wsUrl)
            {
                BasicHttpsBinding binding = new BasicHttpsBinding(BasicHttpsSecurityMode.Transport);
                binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None;
    
                binding.MaxBufferSize = int.MaxValue;
                binding.MaxBufferPoolSize = int.MaxValue;
                binding.MaxReceivedMessageSize = int.MaxValue;
                binding.ReaderQuotas = System.Xml.XmlDictionaryReaderQuotas.Max;
                binding.AllowCookies = true;
                binding.ReceiveTimeout = TimeSpan.FromSeconds(60);
                binding.CloseTimeout = TimeSpan.FromSeconds(60);
    			
                ChannelFactory<T> factory = new ChannelFactory<T>(binding, new EndpointAddress(wsUrl));
    
                factory.Credentials.ServiceCertificate.SslCertificateAuthentication = new X509ServiceCertificateAuthentication()
                {
                    CertificateValidationMode = X509CertificateValidationMode.None
                };
    
                factory.Credentials.Windows.ClientCredential = System.Net.CredentialCache.DefaultNetworkCredentials;
    
                T channel = factory.CreateChannel();
    
                return channel;
    
            }

    So basically on the WCF side we've removed the need for a clientCredential, and on the consumer (the .Net Core API) we've removed it as well, along with the upn.

    We will look at this later but it's working and even when compared with previous working version of Ntlm it is faster.

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, January 15, 2021 4:26 PM