将 Kerberos 与委派一起使用时,GSS API 和 SSPI API 有什么区别?
我有中间件在 Tomcat 服务器中运行 Java 代码。中间件使用 Kerberos (GSS API) 对用户进行身份验证。如果授权标头中不存在 Kerberos 令牌,则中间件返回 401 并附加 WWW-Authenticate:Negotiate 响应标头以初始化 SPNEGO 身份验证。
使用 GSSContext.acceptSecContext 检查传入的服务票证工作正常。
但是,我在委托案中遇到了一些问题。
正如名称“中间件”所示,我的 java 服务必须调用后端服务,使用 Kerberos 身份验证和原始用户主体。为此,我实现了 Kerberos Java GSS API 委托机制。此外,AD 配置正确,并且 tomcat 作为具有特定服务帐户的服务运行。
为了测试这个实现,我编写了一个 Java 测试客户端,利用 GSS API 来获取中间件的票证。使用管理员权限运行 Java 测试客户端或使用 kinit -f 获取可转发票证 客户端和中间件组合工作正常:客户端获得票证,中间件接受票证,GSSContext.getCredDelegState() 返回 true,使用 GSSContext.getDelegCred( ) 中间件获取委托凭证,并且后端的登录工作正常。
此外,我使用浏览器和一个小型 C# 测试客户端测试了中间件实现。两者都使用 SPNEGO。在这种情况下,授权也有效。我收到身份验证成功的消息,并且我得到了用户 Principal。使用浏览器或我的 C# 测试客户端,我在中间件中得到以下调试打印:
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)
使用 Java 客户端,我在中间件中得到了这个调试打印:
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
然而,这里的主要问题是,在 Java 客户端的情况下,委派工作,而在浏览器和 C# 客户端的情况下,委派不起作用。请注意,浏览器已配置为将域列入白名单以进行委派。
附加信息: 我配置了约束委派。带有中间件的 Tomcat 作为具有 AD 服务帐户的服务在 Windows 2016 服务器上运行。
我比较了已发送到中间件的服务票:
Java(可转发),委托工作:10980 字节
C#(委托不起作用):8572 字节
浏览器(委托不起作用):8572 字节
为了比较,我使用不带 -f 选项的 kinit 来获得一个不可转发的 tgt 并测量大小:
Java(不可转发,委托不起作用):8174 字节
顺便说一句,这会产生相同的错误。