Wcf: A call to SSPI failed: The target principal name is incorrect

Created on 28 Apr 2018  路  16Comments  路  Source: dotnet/wcf

I have ported a simple method consuming MS Dynamics AX WCF Service from .net framework to .net core 2.0 (console application).

In .net core project I added the connected service with no problem. All types were auto-generated correctly and I didn't have to do much refactoring.
However when I call the client this is the exception I'm getting:
Could you assist me please?
I'm using VS 2017 v15.6.6

Unhandled Exception: System.ServiceModel.Security.SecurityNegotiationException: A call to SSPI failed, see inner exception. ---> System.Security.Authentication.AuthenticationException: A call to SSPI failed, see inner exception. ---> System.ComponentModel.Win32Exception: The target principal name is incorrect --- End of inner exception stack trace --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Net.Security.NegoState.StartSendAuthResetSignal(LazyAsyncResult lazyResult, Byte[] message, Exception exception) at System.Net.Security.NegoState.StartSendBlob(Byte[] message, LazyAsyncResult lazyResult) at System.Net.Security.NegoState.CheckCompletionBeforeNextSend(Byte[] message, LazyAsyncResult lazyResult) at System.Net.Security.NegoState.ProcessReceivedBlob(Byte[] message, LazyAsyncResult lazyResult) at System.Net.Security.NegoState.ReadCallback(IAsyncResult transportResult) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Net.Security.NegoState.EndProcessAuthentication(IAsyncResult result) at System.Net.Security.NegotiateStream.EndAuthenticateAsClient(IAsyncResult asyncResult) at System.Threading.Tasks.TaskFactory1.FromAsyncCoreLogic(IAsyncResult iar, Func2 endFunction, Action1 endAction, Task1 promise, Boolean requiresSynchronization) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.ServiceModel.Channels.WindowsStreamSecurityUpgradeProvider.WindowsStreamSecurityUpgradeInitiator.<OnInitiateUpgradeAsync>d__12.MoveNext() --- End of inner exception stack trace --- at System.Runtime.AsyncResult.End[TAsyncResult](IAsyncResult result) at System.ServiceModel.Channels.ServiceChannel.SendAsyncResult.End(SendAsyncResult result) at System.ServiceModel.Channels.ServiceChannel.EndCall(String action, Object[] outs, IAsyncResult result) at System.ServiceModel.Channels.ServiceChannelProxy.TaskCreator.<>c__DisplayClass1_0.<CreateGenericTask>b__0(IAsyncResult asyncResult) --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter1.GetResult()
at WcfTest.Program.d__1.MoveNext() in C:UsersvmiSourceReposWcfTestWcfTestProgram.cs:line 195
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at WcfTest.Program.d__1.MoveNext() in C:UsersvmiSourceReposWcfTestWcfTestProgram.cs:line 200
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Runtime.CompilerServices.TaskAwaiter1.GetResult() at WcfTest.Program.<Main>d__0.MoveNext() in C:\Users\vmi\Source\Repos\WcfTest\WcfTest\Program.cs:line 65 --- End of stack trace from previous location where exception was thrown --- at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at WcfTest.Program.<Main>(String[] args)

customer assistance

Most helpful comment

I'm having the same issue with an AIF Dynamics Service only in .net core, do you know how to set in code the userPrincipalName? or the needed service indentity on the channel factory as you mentioned? Thanks :)

All 16 comments

You are using Windows authentication as your authentication mechanism. When using Windows authentication, the identity of the server is also validated. The way this happens is when you get an authentication token from the domain controller to give to the server, the token is obtained for a specific identity of the server. The default identity used is HOST/dnsname. This identity (or spn) can only be used by the SYSTEM or NETWORK SERVICE account (which includes the default identity used in IIS for the application pool user). If the server isn't running with one of these accounts (or equivalent in the case of IIS), then a different identity will be used. Either an explicit identity will be provided on the servers binding (in which case the user running the service needs permission to that identity) or the service will use the identity of the user it is running with. In the case of a user identity, this will be in the active directory username format of user@domain, e.g. [email protected].
One of two things is happening. Either you need to specify the correct server identity in your client binding, or we might have a bug in the .Net core stack somewhere (WCF, corefx, WCF Connected Service tool etc). So now I've given you some background on what's happening, we need to narrow it down.
Are you running on Linux or Windows?
Can you generate a client running on the full framework which works correctly? If so, can you provide the binding config which is generated for the full framework and the binding code generated for .Net Core including the code which creates the endpoint. Feel free to anonymize any usernames, host names and domain names, just be consistent across the two sets of bindings. E.g. you could use hostname myserver, domain contoso.com and username user.

I use Windows.
Here is the binding config from .Net Framework project (working):

<endpoints> <endpoint normalizedDigest="<?xml version="1.0" encoding="utf-16"?><Data address="net.tcp://[MY_SERVER]:[PORT]/[URI]" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_SalesOrderService" contract="SalesService.SalesOrderService" name="NetTcpBinding_SalesOrderService"><identity><userPrincipalName value="myuser@mydomain"/></identity></Data>" digest="<?xml version="1.0" encoding="utf-16"?><Data address="net.tcp://[MY_SERVER]:[PORT]/[URI]" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_SalesOrderService" contract="SalesService.SalesOrderService" name="NetTcpBinding_SalesOrderService"><identity><userPrincipalName value="myuser@mydomain"/></identity></Data>" contractName="SalesService.SalesOrderService" name="NetTcpBinding_SalesOrderService" /> </endpoints>

And this is the binding config from .Net Core 2.0 project (not working):

{ "ProviderId": "Microsoft.VisualStudio.ConnectedService.Wcf", "Version": "15.0.20119.1312", "ExtendedData": { "Uri": "http://[MY_SERVER]:[PORT]/[URI]", "Namespace": "SalesService", "SelectedAccessLevelForGeneratedClass": "Public", "GenerateMessageContract": false, "ReuseTypesinReferencedAssemblies": true, "ReuseTypesinAllReferencedAssemblies": true, "CollectionTypeReference": { "Item1": "System.Array", "Item2": "System.Runtime.dll" }, "DictionaryCollectionTypeReference": { "Item1": "System.Collections.Generic.Dictionary2",
"Item2": "System.Collections.dll"
},
"CheckedReferencedAssemblies": [],
"InstanceId": null,
"Name": "SalesService",
"Metadata": {}
}
}`

Thank you.

You didn't quite provide the information needed from the Connected Service output. The binding is done in code and not config file with Connected Services. The problem is caused by the service identity not being correctly set on the ChannelFactory. From your existing config the relevant section is:

<identity>
  <userPrincipalName value="myuser@mydomain"/>
</identity>

The error message in the exception is basically saying that the security token you got from the domain controller to authenticate to the server was for a server with a different identity. If the identity is missing, we use the dns hostname and generate an ServicePrincipleName with the value HOST/hostname.

I'm having the same issue with an AIF Dynamics Service only in .net core, do you know how to set in code the userPrincipalName? or the needed service indentity on the channel factory as you mentioned? Thanks :)

@mconnew could you please chime in?

I'm having the same issue with an AIF Dynamics Service only in .net core, do you know how to set in code the userPrincipalName? or the needed service indentity on the channel factory as you mentioned? Thanks :)

Did you find any solution? I have exactly the same problem

As i can remember, there was a problem in the port of the soap service a was using, one url looks like this x.x.x.x:8101/DynamicsAx/Services/TIFacturasServicios and the other was x.x.x.x:8201/DynamicsAx/Services/TIFacturasServicios i was using the incorrect, i don't remember what was the correct... after that i simply used the connected services tool in VS and everything worked fine

Well, I've set my identity with new UpnEndpointIdentity("[email protected]") (I guess UPN = UserPrincipalName) and now it works, thanks for good willings anyway @naojamg :)

I don't know how, but it looks like you predicted my next problem and your advice with switching the ports was soooo helpful.
But why is AX generating "wrong" port? (Maybe not really wrong)
Many thanks @naojamg 鉂わ笍

@Dawiducik hahaha AX is a little old, i was using 2012 version and the ports was generated automatically by AX, it seems like both ports are used but i never knew why one is only the correct for the connection (銉籣銉汇兙

Well, I've set my identity with new UpnEndpointIdentity("[email protected]")

I think we're having the same issue as Dawiducik, as we're also trying to get an AIF services working using Net Core 3.1 in our Dynamics AX 2012 environment. I see where he added the statement UpnEndPointIdentity("[email protected]"), but where does this statement go?

@chessdr
This is part of my code with TimesheetService running.

using AxServices.Timesheet; // I "scrapped" my WSDL model to this namespace

var timesheetServiceUrl = "net.tcp://<dynamics-address>:8201/DynamicsAx/Services/TimesheetServices";

// My binding, note that it doesn't have to be TCP, can be HTTP as well
var binding = new NetTcpBinding
{
    MaxReceivedMessageSize = 2000000 // Just to test, change if needed larger
};
binding.Security.Mode = SecurityMode.Transport;
binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;

// Prepare identity
var endpointIdentity = new UpnEndpointIdentity("[email protected]"); // Where "[email protected]" is the user running AOS instance on server

try
{
    var uri = new Uri(timesheetServiceUrl);
    var endpointAddress = new EndpointAddress(uri, endpointIdentity); // You can see "UpnEndpointIdentity" referenced here.
    var timesheetClient = new TSTimesheetClient(binding, endpointAddress);

    await timesheetClient.OpenAsync();

    // Prepare context to your company
    var context = new AxServices.Timesheet.CallContext
    {
        Company = "COM1" 
    };

    // and call the method
    var timesheet = await timesheetClient.getTimesheetDetailsByNumberAsync(context, "XYZ-281441");

    // you can also reference "null" as the first argument
    // var timesheet = await timesheetClient.getTimesheetDetailsByNumberAsync(null, "XYZ-281441");

    // then process timesheet as you like
    // "Dump" is my extension on object, serializing to JSON and streaming output to console
    timesheet.response.Dump();

    var timesheetId = timesheet.response.parmTimesheetNbr;
    Console.WriteLine($"Received timesheet: {timesheetId}.");

    await timesheetClient.CloseAsync();
}
catch (TimeoutException ex)
{
    // Handle exception
}
catch (CommunicationException ex)
{
    // Handle exception
}

We were able to use the above code with just a few modifications as we were creating an AX Transfer Order, and it worked great! Now we can go try and create AX Purchase Orders and AX Sales Orders. Thank you so much, Dawiducik for the great example above!!

@chessdr and @Dawiducik, FYI, UpnEndpointIdentity only works for NetTcpBinding. For HTTP, we are lacking functionality in HttpClient. If you need to use HTTP, I can point you to the open issue for you to add your voice.

@mconnew Point me to the issue in HttpClient, I will look at it in free time

The issue is dotnet/runtime#25320

Was this page helpful?
0 / 5 - 0 ratings