1

I am attempting to obtain a Kerberos Token from a C# Application (not web-based, a standalone executable).

I have referred to this question to figure out how to do it, but even trying both answers, I get the same problem.

When I reach the GetToken line (using Furkat's answer as a reference here), I get an exception:

KerberosRequestorSecurityToken securityToken = tokenProvider.GetToken(TimeSpan.FromMinutes(1)) as KerberosRequestorSecurityToken;

Here is the exception and the nested innerExceptions:

Exception: The NetworkCredentials provided were unable to create a Kerberos credential, see inner exception for details.
innerException: Authenticating to a service running under a user account which requires Kerberos multilegs, is not supported.
innerException: The function completed successfully, but must be called again to complete the context

I have some serious problems trying to find any examples of this working for a non-web based application, the StackOverflow question I linked is pretty much the closest I've got to getting what I need.

I also have problems figuring out exactly how things are supposed to work, since I can't get an example to work on my side. I'm looking for some sort of unique token for the user, that can then be passed to a SAML POST call to a server for Single Sign On. What will this token look like? Is it right to use TokenImpersonationLevel.Impersonation, instead of Identification here? (Identification gives me the same problem).

So my question is about my error and how to fix it, but I would really appreciate an explanation with the answer, telling me about the context (what was going wrong, what I misunderstood, etc).

Here's my complete Method. It's in Proof-Of-Concept stage right now, so forgive the temporary bad naming and ugly code. I'm making lots of trial-and-error.

public string Method5()
{

    try
    {
        var userName1 = new WindowsPrincipal(WindowsIdentity.GetCurrent()).Identity.Name;

        var domainName = userName1.Split('\\').First();
        var userName = userName1.Split('\\').Last();

        AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
        var domain = Domain.GetCurrentDomain().ToString();

        using (var domainContext = new PrincipalContext(ContextType.Domain, domain))
        {
            string spn = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, userName).UserPrincipalName;
            KerberosSecurityTokenProvider tokenProvider = new KerberosSecurityTokenProvider(spn, TokenImpersonationLevel.Impersonation, CredentialCache.DefaultNetworkCredentials);
            KerberosRequestorSecurityToken securityToken = tokenProvider.GetToken(TimeSpan.FromMinutes(1)) as KerberosRequestorSecurityToken;
            string serviceToken = Convert.ToBase64String(securityToken.GetRequest());
            return serviceToken;
        }
    }
    catch (Exception ex)
    {
        return "Failure";
    }
}
Kaito Kid
  • 983
  • 4
  • 15
  • 34
  • The only google results I found were old and unresolved. someone did say "Try setting NegotiateServiceCredential to true." I have no idea if that would help, but it's worth a try. – Terry Carmen Aug 20 '18 at 19:49
  • @TerryCarmen That's exactly my problem, every issue remotely close to mine seems to be ignored or unresolved. I couldn't find "NegotiateServiceCredential" anywhere... – Kaito Kid Aug 20 '18 at 19:51
  • Sorry, that's all I found. You should be able to figure out what's going on if you watch the protocol exchange with Wireshark. – Terry Carmen Aug 20 '18 at 19:56
  • I found something more useful. Apparenlty it's related to service account permissions. See: https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.messagesecurityoverhttp.negotiateservicecredential?redirectedfrom=MSDN&view=netframework-4.7.2#System_ServiceModel_MessageSecurityOverHttp_NegotiateServiceCredential – Terry Carmen Aug 21 '18 at 15:20

1 Answers1

2

The error indicates that you are requesting a Kerberos User2User token. The multileg bit is correct, but somewhat misleading. The issue is that AD determines it's a U2U request and makes the API return a specific error, indicating it's U2U and requires a retry with different parameters. .NET doesn't understand this retry, hence the error.

The reason you're requesting a U2U token is because you're calling the token provider asking for it to request a token to access the given SPN, which in this case is just an ordinary user. This is generally not useful in client/server applications.

KerberosSecurityTokenProvider tokenProvider = new KerberosSecurityTokenProvider(spn, TokenImpersonationLevel.Impersonation, CredentialCache.DefaultNetworkCredentials);

What this code is doing is saying for a user that has been inferred by impersonation or authentication previously, request a token so that user can access a remote service {SPN}. A token is only useful for a single user to a single service. You can't just collect a token and user it everywhere. This is not how Kerberos-proper works. Kerberos determines the name of that service by the SPN. In this case it already knows who the caller is.

So, the correct solution is:

var identity = Thread.CurrentPrincipal.Identity; // domain\username1

var spn = "host/someservice.domain.com";
var tokenProvider = new KerberosSecurityTokenProvider(spn);
var securityToken = tokenProvider.GetToken(TimeSpan.FromMinutes(1)); // token for username1 to host/someservice.domain.com
Steve
  • 4,463
  • 1
  • 19
  • 24