Search code examples
c#kerberoswindows-authentication.net-4.8spnego

Generate SPNEGO kerberos token for IIS Windows Authnetication


In C# .NET Framework 4.8, I need to generate kerberos token in SPNGEO format so I can send it in Authorization header as Negotiate to my IIS server, where I have Windows Authentication enabled.

I am using code from this topic How to get Service Token from Kerberos using SSPI It is generating the kerberos token, but its not in SPNEGO format, so its not working when I use it.

I would like to obtain the same token as is generated with the use of Kerberos.NET library. Their token is working for me, but the issue is that I need to use credentials for generation and I don't want that, so I cannot use this library. I want to use DefaultCredentials but to get same token as with the use of from Kerberos.NET library. I was also trying to use SSPI directly and set package to Negotiate, which should give me SPNEGO format, but the token generated was not same as from Kerberos.NET library so its also not working.

How can I solve this please? Or maybe is there any configuraiton on my IIS that it will also accept tokens which are not in SPENGO format? Please help


Solution

  • Looks like this is what you need:

    Recommended Way

    I'd recommend using the built-in framework classes WebRequest or HttpClient which can do Negotiate (SPNEGO) authorization this way:

    var webRequest = WebRequest.CreateHttp(uri);
    webRequest.UseDefaultCredentials = true;
    
    using var httpClient = new HttpClient(new HttpClientHandler {
        UseDefaultCredentials = true,
    });
    

    Workarounds

    You can still try to generate the token on your own, but since Negotiate is a handshake, you would still need to get a server response to complete the handshake.

    .NET 8

    using System.Net;
    using System.Net.Security;
    
    var authClientOptions = new NegotiateAuthenticationClientOptions
    {
        Package = "Negotiate",
        TargetName = $"HTTP/{hostFqdn}",
        RequiredProtectionLevel = ProtectionLevel.Sign,
        Credential = CredentialCache.DefaultNetworkCredentials,
    };
    
    using var authContext = new NegotiateAuthentication(authClientOptions);
    var authClientToServer = authContext.GetOutgoingBlob("", out var statusCode);
    
    Console.WriteLine(authClientToServer);
    // statusCode is ContinueNeeded
    

    Verify:

    curl <url> -s -v -o nul -H "Authorization: Negotiate <token>"
    

    .NET 4.8

    Some of the classes that can do Negotiate handshake under the hood are NegotiateClient and NegotiateStream.

    using var ms = new MemoryStream();
    using var ns = new NegotiateStream(ms);
    try
    {
        ns.AuthenticateAsClient(
            CredentialCache.DefaultNetworkCredentials,
            binding: null,
            $"HTTP/{hostFqdn}",
            ProtectionLevel.Sign,
            TokenImpersonationLevel.Delegation);
    }
    catch (AuthenticationException e)
    {
    }
    
    const int headerSize = 5;
    var authClientToServer = Convert.ToBase64String(
        ms.GetBuffer(),
        headerSize,
        (int)ms.Length - headerSize);
    
    Console.WriteLine(authClientToServer);