I am having problems authenticating via SPNEGO from a Web Browser (Internet Explorer 11) to a Web Service offered by a custom Java Application Server.
I can successfully authenticate using SPNEGO to the same Application Server using a custom Java Client Application.
Implementation details of the custom Java Client and Application server can be found below.
I suspect that SPNEGO from a Web Browser is not working because:
a) Is the Token from Internet Explorer a valid SPNEGO Token?
The GSSAPI token provided by the Web Browser is different to that provided by my Java Client, and may not be a valid SPNEGO / Kerberos token. The Java Client provides an Authorization Header starting with “Negotiate YIMMQA...” (OK), whereas the Web Browser provides an Authorization Header starting with “Negotiate oYIMRz...” (Probably NOT OK).
and / or
b) Format of the Server Principal Name
For historical reasons, the Application Server is running using a Service Principal Name that is actually a Microsoft Active Directory User Principal (format = "user@DOMAIN"), whereas I strongly suspect that Web Browser SPNEGO implementations use the requested URL to build a Service Principal Name. Indeed this is exactly what my custom Java Client does when running on Linux against a Linux backend.
Implementation Details:
The Java Application Server runs on Windows Server 2012. The Kerberos / SPNEGO implementation is pure Java JAAS + GSSAPI.
The Java Client runs on Windows (7 / 10), and can be configured to use either Java SSPI (via Waffle), or JAAS + GSSAPI. Both implementations create GSS Tokens accepted by the Server.
The GSS / SPNEGO tokens generated are transported in the headers of Web Service requests (Client) and responses (Server).
The Server is using the Oids "1.3.6.1.5.5.2" (SPNEGO) and "1.2.840.113554.1.2.2" (Kerberos).
Testing using the Custom Java Client (OK):
The Server is able to authenticate the Java Client in a single handshake. The Java Client directly calls the Web Service with an Authorization Header starting with “Negotiate YIMMQA...” After Base64 decoding on the Server the gssapiData 3140 bytes long and the call to acceptSecContext() is successful.
If I convert the gssapiData from this call to a string, and search this for anything human readable, then towards the start I find “EXAMPLE.COM” and “user-DEV”. This looks like the SPN that the Server is using, an Active Directory User Principal (“[email protected]”).
Testing using Internet Explorer 11 (NOT OK):
The first call from the Browser has an empty Authorization Header. My Server prompts with “Negotiate” —> OK.
The second call from the Browser has an Authorization Header starting with “Negotiate YH4GBis...”. Once Base64 decoded, the gssapiData is 128 bytes long. Clearly this does not contain a service ticket.
If I convert the gssapiData to a string, in the middle I find the characters “NTLMSSP”. I guess the Browser is suggesting NTLM. My Server rejects this call.
The third call from the Browser has an Authorization Header starting with “Negotiate oYIMRz...” Once Base64 decoded, the gssapiData is 3147 bytes long (very close to the length of that from the Java Client).
However when my Server does a acceptSecContext() on this, It throws the error. “GSS Exception: Defective Token detected (Mechanism level: GSS Header did not find the right tag). —> NOT OK.
This suggests to me that the token is not valid, or I am using the wrong Oids to read it.
If I convert the gssapiData from this call to a string, then towards the start I find “HTTP” and “APPSERVER.example.com”. This looks like a Kerberos Service Principal Name (SPN) built using the URL as a basis. —> This suggests to me that my Application Server should be running with an SPN in a format something like “HTTP/APPSERVER.example.com” or “HTTP/[email protected]” (the second being the format my Linux / FreeIPA configuration uses).
As a side note: On the Windows platform that is the focus of this question I do not have the rights to create / change SPNs or aliases to the same, or to try different Web Browsers. On my Linux development environment I do, which may provide additional input . . .
network.negotiate-auth.trusted-uris=<domain of your app server>
network.negotiate-auth.using-native-gsslib=true
network.auth.use-sspi=false
network.negotiate-auth.allow-non-fqdn = false