What is the difference between GSS API and SSPI API when using Kerberos with delegation?
I have middleware running Java code in a Tomcat Server. The middleware authenticates the user with Kerberos (GSS API). If no Kerberos token is present in the Authorization header the middleware returns a 401 and attaches a WWW-Authenticate:Negotiate response header to initialize a SPNEGO Authentication.
The check of the incoming Service Ticket using GSSContext.acceptSecContext works fine.
However, I have some issues in the delegation case.
As the name “middleware” indicates, my java service must call a backend service using Kerberos authentication with the original user principal. For that, I implemented the Kerberos Java GSS API delegation mechanism. Also, the AD was configured properly, and the tomcat runs as a service with a specific service account.
To test this implementation, I wrote a Java test client utilizing the GSS API to get a ticket for the middleware. Running the Java test client with admin rights or getting a forwardable ticket using kinit -f the client and middleware combination works fine: The client gets a ticket, the middleware accepts the ticket, GSSContext.getCredDelegState() returns true, using GSSContext.getDelegCred() the middleware gets delegation credentials and the login in the backend works fine.
Also, I tested the middleware implementation with browsers and a small C# test client. Both uses SPNEGO. In this case the authorization works, too. I get the message that the authentication succeeded, and I get the user Principal. Using browsers or my C# test client I get the following debug print in the middleware:
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt false ticketCache is null isInitiator false KeyTab is D:/app/Tomcat_9019_SSO/conf/tomcat.keytab refreshKrb5Config is true principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Refreshing Kerberos configuration
Java config name: C:\Windows\kerb5.ini
Loading krb5 profile at C:\Windows\kerb5.ini
Loaded from Java config
>>> KdcAccessibility: reset
principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Will use keytab
Commit Succeeded
2020-03-18 06:36:50.254 INFO .e.s.a.t.a.KerberosCheckAuthTicketAction [TC~3~c80e3d5b-3] : Starting check of incoming Kerberos service ticket.
Search Subject for SPNEGO ACCEPT cred (<<DEF>>, sun.security.jgss.spnego.SpNegoCredElement)
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Entered Krb5Context.acceptSecContext with state=STATE_NEW
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Using builtin default etypes for permitted_enctypes
default etypes for permitted_enctypes: 18 17 20 19 16 23.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
MemoryCache: add 1584509810/000627/5EBDF35F49476E365F32DE53C3CAFA81C4730A13D881ECA15E9F43023F99A80B/CLIENTUSERD@MYDOMAIN.NET to CLIENTUSERD@MYDOMAIN.NET|HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
>>> KrbApReq: authenticate succeed.
Krb5Context setting peerSeqNumber to: 947381056
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Krb5Context setting mySeqNumber to: 214468704
>>> Constrained deleg from GSSCaller{UNKNOWN}
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt false ticketCache is null isInitiator true KeyTab is D:/app/Tomcat_9019_SSO/conf/tomcat.keytab refreshKrb5Config is false principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
default etypes for default_tkt_enctypes: 23 18 17.
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=kb01.mydomain.net UDP:88, timeout=30000, number of retries =3, #bytes=174
>>> KDCCommunication: kdc=kb01.mydomain.net UDP:88, timeout=30000,Attempt =1, #bytes=174
>>> KrbKdcReq send: #bytes read=175
>>>Pre-Authentication Data:
PA-DATA type = 11
PA-ETYPE-INFO etype = 23, salt =
>>>Pre-Authentication Data:
PA-DATA type = 19
PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 16
>>>Pre-Authentication Data:
PA-DATA type = 15
>>> KdcAccessibility: remove kb01.mydomain.net
>>> KDCRep: init() encoding tag is 126 req type is 11
>>>KRBError:
sTime is Wed Mar 18 06:36:50 CET 2020 1584509810000
suSec is 765149
error code is 25
error Message is Additional pre-authentication required
sname is krbtgt/MYDOMAIN.NET@MYDOMAIN.NET
eData provided.
msgType is 30
>>>Pre-Authentication Data:
PA-DATA type = 11
PA-ETYPE-INFO etype = 23, salt =
>>>Pre-Authentication Data:
PA-DATA type = 19
PA-ETYPE-INFO2 etype = 23, salt = null, s2kparams = null
>>>Pre-Authentication Data:
PA-DATA type = 2
PA-ENC-TIMESTAMP
>>>Pre-Authentication Data:
PA-DATA type = 16
>>>Pre-Authentication Data:
PA-DATA type = 15
KrbAsReqBuilder: PREAUTH FAILED/REQ, re-send AS-REQ
default etypes for default_tkt_enctypes: 23 18 17.
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
default etypes for default_tkt_enctypes: 23 18 17.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>> KrbAsReq creating message
>>> KrbKdcReq send: kdc=kb01.mydomain.net UDP:88, timeout=30000, number of retries =3, #bytes=253
>>> KDCCommunication: kdc=kb01.mydomain.net UDP:88, timeout=30000,Attempt =1, #bytes=253
>>> KrbKdcReq send: #bytes read=90
>>> KrbKdcReq send: kdc=kb01.mydomain.net TCP:88, timeout=30000, number of retries =3, #bytes=253
>>> KDCCommunication: kdc=kb01.mydomain.net TCP:88, timeout=30000,Attempt =1, #bytes=253
>>>DEBUG: TCPClient reading 2154 bytes
>>> KrbKdcReq send: #bytes read=2154
>>> KdcAccessibility: remove kb01.mydomain.net
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
[Krb5LoginModule] authentication failed
Message stream modified (41)
Using the Java client, I get this debug print in the middleware:
Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt false ticketCache is null isInitiator false KeyTab is D:/app/Tomcat_9019_SSO/conf/tomcat.keytab refreshKrb5Config is true principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET tryFirstPass is false useFirstPass is false storePass is false clearPass is false
Refreshing Kerberos configuration
Java config name: C:\Windows\kerb5.ini
Loading krb5 profile at C:\Windows\kerb5.ini
Loaded from Java config
>>> KdcAccessibility: reset
principal is HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Will use keytab
Commit Succeeded
2020-03-18 06:47:41.029 INFO .e.s.a.t.a.KerberosCheckAuthTicketAction [TC~9~c80e3d5b-9] : Starting check of incoming Kerberos service ticket.
Search Subject for SPNEGO ACCEPT cred (<<DEF>>, sun.security.jgss.spnego.SpNegoCredElement)
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Search Subject for Kerberos V5 ACCEPT cred (<<DEF>>, sun.security.jgss.krb5.Krb5AcceptCredential)
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Found KeyTab D:\app\Tomcat_9019_SSO\conf\tomcat.keytab for HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Entered Krb5Context.acceptSecContext with state=STATE_NEW
Looking for keys for: HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
Added key: 23version: 0
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Using builtin default etypes for permitted_enctypes
default etypes for permitted_enctypes: 18 17 20 19 16 23.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
MemoryCache: add 1584510459/567826/FDE0027391B8BF26BF807FF04E5FD5F7CE38794A3264EB298BB36F736B2CF050/CLIENTUSERD@MYDOMAIN.NET to CLIENTUSERD@MYDOMAIN.NET|HTTP/SERVICE.MYDOMAIN.NET@MYDOMAIN.NET
>>> KrbApReq: authenticate succeed.
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
>>>Delegated Creds have pname=CLIENTUSERD@MYDOMAIN.NET sname=krbtgt/MYDOMAIN.NET@MYDOMAIN.NET authtime=20200318054735Z starttime=20200318054739Z endtime=20200318154735ZrenewTill=null
Krb5Context setting peerSeqNumber to: 99984043
>>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType
Krb5Context setting mySeqNumber to: 161819208
However, the main issue here is, that in case of the Java client the delegation works and in case of browsers and C# client the delegation does not work. Note, the browsers have been configured to whitelist the domain for delegation.
Additional Information: I configured constrained delegation. The Tomcat with the middleware is running on a Windows 2016 server as a Service with an AD service account.
I compared the service tickets which has been send to the middleware:
Java (forwardable), delegation works: 10980 byte
C# (delegation doesn’t work): 8572 byte
Browser (delegation doesn’t work): 8572 byte
For comparison I used kinit without -f option to get a tgt which is not forwardable and measured the size:
Java (not forwardable, delegation doesn’t work): 8174 bytes
Btw., this produces the same error.