尝试使用Kerberos在运行在Windows上的Tomcat进行身份验证时,出现“GSSException检测到有缺陷的令牌”的错误。

18

我在运行Windows 2012时,尝试对Java Web容器进行身份验证(我尝试了Tomcat和Jetty),但遇到了困难。

每次尝试使用Negotiate身份验证方案时,都会出现错误:org.ietf.jgss.GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)

复现步骤:

首先设置一个 Windows Server 2012 或 2016 实例,并安装活动目录域服务。

例如,在我的情况下,我创建了:

  • NETBIOS域: NICKIS

  • Dns域: nickis.life

在活动目录中创建Kerberos主题用户

重要提示:确保名字、姓氏和全名相同!

在我的例子中,新建的用户是:

DN = CN=kerberos500,CN=Users,DC=nickis,DC=life

登录+域 = kerberos500@nickis.life

NETBIOS\samAccountName = NICKIS\kerberos500

从Windows活动目录服务器运行setspn命令

setspn -A HTTP/nickis.life@NICKIS.LIFE kerberos500

示例输出:

C:\Users\Administrator>setspn -A HTTP/nickis.life kerberos500
Checking domain DC=nickis,DC=life 
Registering ServicePrincipalNames for CN=kerberos500,CN=Users,DC=nickis,DC=life
        HTTP/kerberos500.nickis.life
Updated object

在 Windows Active Directory 服务器上运行 ktpass 命令。

ktpass -out c:\Users\Administrator\kerberos500.keytab -princ HTTP/nickis.life@NICKIS.LIFE -mapUser kerberos500 -mapOp set -pass XXXXpasswordforkerberos500userXXXX -crypto DES-CBC-MD5 -pType KRB5_NT_PRINCIPAL +DesOnly

示例输出:

C:\Users\Administrator>ktpass -out c:\Users\Administrator\kerberos500.keytab -princ HTTP/nickis.life@NICKIS.LIFE -mapUser kerberos500 -mapOp set -pass xxxxxxxx -crypto DES-CBC-MD5 -pType KRB5_NT_PRINCIPAL +DesOnly
Targeting domain controller: WIN-OVV6VHBGIB8.nickis.life
Using legacy password setting method
Successfully mapped HTTP/kerberos500.nickis.life to kerberos500.
Key created.
Output keytab to c:\Users\Administrator\kerberos500.keytab:
Keytab version: 0x502
keysize 71 HTTP/kerberos500.nickis.life@NICKIS.LIFE ptype 1 (KRB5_NT_PRINCIPAL) vno 3 etype 0x3 (DES-CBC-MD5) keylength 8 (0xcd07200bea625d20)
Account kerberos500 has been set for DES-only encryption.

现在,您将拥有一个keytab文件:

c:\Users\Administrator\kerberos500.keytab

还有一个用户主体:

HTTP/kerberos500.nickis.life@NICKIS.LIFE

这是提供给GSSApi以通过Kerberos进行单点登录所需的两个输入。

因此,我将这些输入部署到我的Web容器的Hadoop安全模块中的Kerberos安全域中。

Curl测试 我尝试使用curl进行测试,但失败了:

curl --negotiate -u : http://nickis.life:8080/my/webapp

Internet Explorer测试 我也尝试使用Internet Explorer。我在Internet Explorer中的受信任角色中添加了nickis.life域。然后在Internet Explorer中启动网站:http://nickis.life:8080

无论哪种方式,我都会收到下面的错误:

org.apache.hadoop.security.authentication.client.AuthenticationException: GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)
    at org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler.authenticate(KerberosAuthenticationHandler.java:398) ~[hadoop-auth-2.7.1.jar:?]

...

Caused by: org.ietf.jgss.GSSException: Defective token detected (Mechanism level: GSSHeader did not find the right tag)
    at sun.security.jgss.GSSHeader.<init>(Unknown Source) ~[?:1.8.0_131]
    at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source) ~[?:1.8.0_131]
    at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source) ~[?:1.8.0_131]
    at org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler$2.run(KerberosAuthenticationHandler.java:365) ~[hadoop-auth-2.7.1.jar:?]
    at org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler$2.run(KerberosAuthenticationHandler.java:347) ~[hadoop-auth-2.7.1.jar:?]
    at java.security.AccessController.doPrivileged(Native Method) ~[?:1.8.0_131]
    at javax.security.auth.Subject.doAs(Unknown Source) ~[?:1.8.0_131]
    at org.apache.hadoop.security.authentication.server.KerberosAuthenticationHandler.authenticate(KerberosAuthenticationHandler.java:347) ~[hadoop-auth-2.7.1.jar:?]

我被难住了。注意:我在这里和那里找到了几个链接,但它们都没有详细列出像我总结的这样的步骤,并且提供的解决方案都对我没用。

有人能追踪我错在哪里吗?

更新:

我有一个AD服务器,域设置为fusionis.life,AD服务器名为WIN-OVV6VHBGIB8.fusionis.life
  • 我将Tomcat服务器移到了域中的另一台Windows机器上。DESKTOP-VTPBE99.fusionis.life
  • 我打开了dnsmgmt.msc并添加了一个“转发查找区”,其中“kerberos500.nickis.life”是一个A主机,其设置为DESKTOP-VTPBE99.fusionis.life盒子的IP。
  • 我删除了AD帐户,重新创建了它,然后像票证中的一个答案建议的那样重新生成了keytab。
  • C:\Users\Administrator>ktpass -out c:\Users\Administrator\kerberos500.keytab -princ HTTP/kerberos500.nickis.life@NICKIS.LIFE -mapUser kerberos500 -mapOp set -pass xxxxxxxxx -crypto ALL -pType KRB5_NT_PRINCIPAL 针对域控制器: WIN-OVV6VHBGIB8.fusionis.life 使用传统的密码设置方法 成功将HTTP/kerberos500.nickis.life映射到kerberos500。 密钥已创建。 密钥已创建。 密钥已创建。 密钥已创建。 密钥已创建。 键输出到c:\Users\Administrator\kerberos500.keytab: Keytab版本:0x502 keysize 67 HTTP/kerberos500.nickis.life@NICKIS.LIFE ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x1 (DES-CBC-CRC) keylength 8 (0x04e30b9183ba8389) keysize 67 HTTP/kerberos500.nickis.life@NICKIS.LIFE ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x3 (DES-CBC-MD5) keylength 8 (0x04e30b9183ba8389) keysize 75 HTTP/kerberos500.nickis.life@NICKIS.LIFE ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x17 (RC4-HMAC) keylength 16 (0xe39a141de38abd8750bf9c0bf49fd1c5) keysize 91 HTTP/kerberos500.nickis.life@NICKIS.LIFE ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x12 (AES256-SHA1) keylength 32 (0xe368a1b060cfe4816f522c1c5f62ca07fe201ed96c6d018054dfbd5b86251892) keysize 75 HTTP/kerberos500.nickis.life@NICKIS.LIFE ptype 1 (KRB5_NT_PRINCIPAL) vno 4 etype 0x11 (AES128-SHA1) keylength 16 (0x1b1a548fa2893a78c6f4c7f9c482b614)

    我将keytab更新文件保存在服务器上,然后将服务主体更新为HTTP/kerberos500.nickis.life@NICKIS.LIFE

    我以域用户身份登录到tomcat机器,将http://kerberos500.nickis.life添加到受信任站点,然后导航至http://kerberos500.nickis.life:8764

    我检查了kerberos500 AD“账户”选项卡中加密复选框的所有组合。

    现在我遇到一个新的错误...

    GSSException:未提供有效凭证(机制级别:未能找到任何Kerberos凭证)

    更新:

    终于解决了。我之所以出现这个最终的错误,是因为我需要将fusionis.life放在与nickis.life相同的主机上。


    这是一个非常广泛的问题。为了更好地获得答案,我建议在其中添加kerberos和spnego标签。也许还可以加上一些赏金。这只是我的个人意见。 - John R Smith
    @JohnRSmith 好的,我会做的。 - Nicholas DiPiazza
    有人提到可能是大小写敏感性导致了我的问题。 - Nicholas DiPiazza
    我怀疑它的大小写敏感性。很多东西都是不区分大小写的,比如网页浏览器URL。这可能会影响到一些活动目录方面的情况。 - John R Smith
    抱歉,我在使用Windows + VirtualBox时遇到了问题,导致我需要测试的虚拟机被破坏了。我正在努力解决这个问题。 - Nicholas DiPiazza
    是的,我正在跟随建议进行工作。不幸的是,为了采纳建议答案,需要花费大量时间来设置各种不同的事项。目前还没有解决方案。 - Nicholas DiPiazza
    3个回答

    25

    “Defective token detected”这个错误通常意味着检测到一个令牌。这是当失败时,流行的Web浏览器内部使用的Negotiate机制 - 除非Web服务器另有指示。在操作系统上,它上面的IE Web浏览器(如果正确配置Firefox)基本上会说,如果您不使用Kerberos,我将向您发送一个NTLM令牌。然后服务器回复“绝不”,我甚至不认识NTLM,所以我称你发给我的是有问题的。由于您似乎是第一次设置此项,因此可能没有为Kerberos失败时配置任何备用机制(例如NTLM),因此出现了该错误消息。我们通过了解为什么Kerberos失败来解决这个问题。我认为我在你的问题中看到了失败的原因,与SPN和受信任站点有关。即使解决了这两个问题,也有第三个和第四个原因可能会导致它继续失败,涉及加密。
    1. HTTP服务的与浏览器输入的URL不匹配。这些需要匹配,否则Kerberos会失败。为了正常工作,浏览器应该使用:http://kerberos500.nickis.life:8080,而不是http://nickis.life:8080。我基于您在创建语法中所见到的内容来说。在那里,您已将SPN编码为:HTTP/kerberos500.nickis.life@NICKIS.LIFE。这就是为什么您需要使用http://kerberos500.nickis.life:8080。当您告诉浏览器转到http://nickis.life:8080时,浏览器将无法知道如何访问您的Web服务。对于顶部的URL,浏览器会假定它需要在您的Active Directory 上运行Web服务(任何只有nickis.life的内容都假定在域控制器上运行)。出于安全原因,域控制器不应该运行Web服务器。
    2. 您需要在IE设置中将http://kerberos500.nickis.life添加为受信任站点。或者,*.nickis.life也可以。 (您称其为受信任角色,实际上称为受信任站点)。
    3. 您正在将Kerberos加密类型限制为DES-CBC-MD5。从Windows Server 2008 Active Directory R2开始,默认情况下禁用DES。这是一种过时且通常不安全的加密类型。更好的方法是使用AES128,甚至更好的是使用AES256。您可以通过按照下面的示例重新生成keytab来解决此问题。
    4. 在AD用户帐户kerberos500中,转到“帐户”选项卡,向下滚动,并选中DES、AES 128和AES 256的所有框,然后单击“确定”退出对话框。即使您在上面做了一切正确的事情,也必须勾选这些框,否则Kerberos身份验证仍将失败。
    如何正确重新生成keytab:当您计划为该用户帐户创建keytab时,不应运行setspn -a命令向AD用户添加SPN。原因是因为keytab创建命令在命令的一部分中将SPN添加到用户帐户中。如果按照上面的建议操作后您的情况仍无法解决,则需要通过setspn -D删除SPN,如下所示:
    setspn -D HTTP/nickis.life@NICKIS.LIFE kerberos500
    

    重新生成keytab之后,我的唯一更改是告诉它使用所有加密类型。客户端和服务器将在身份验证过程中协商出最强的共同加密方式。
    ktpass -out c:\Users\Administrator\kerberos500.keytab -princ HTTP/nickis.life@NICKIS.LIFE -mapUser kerberos500 -mapOp set -pass XXXXpasswordforkerberos500userXXXX -crypto ALL -pType KRB5_NT_PRINCIPAL
    

    然后用新的keytab替换旧的keytab。关于keytabs的更多深入信息,您可以阅读我的技术文章,了解如何创建Kerberos keytabs:Kerberos Keytabs - Explained。我经常根据在这个论坛上看到的问题返回并编辑它。
    顺便说一下,HTTP/kerberos500.nickis.life是一个服务主体,不是像您在问题中写的用户主体。我只使用Web浏览器来测试像这样的HTTP场景中的Kerberos,我不使用cURL。
    我相信如果您认真阅读我上面突出显示的四点,您将解决这个问题。
    EDIT1:此答案假设您在具有完全限定域名为kerberos500.nickis.life的主机上运行HTTP服务。如果您没有这样的主机名称,请稍微更改我的答案。请告诉我任何问题。 EDIT2: 为了使用http://nickis.life:8080的URL实现认证目标,您可以继续使用已经创建的相同keytab。
    在AD帐户NICKIS\kerberos500中,转到“帐户”选项卡,向下滚动并勾选“此帐户使用Kerberos DES加密类型”。
    然后通过组策略在AD域级别启用DES加密本身。要执行此操作,请执行以下操作:
    1. 打开组策略管理控制台(GPMC)。
    2. 编辑默认域策略GPO。(更安全的方法是创建一个新的域级别GPO并进行编辑,但这取决于您的选择)。
    3. 导航到计算机配置 > 策略 > Windows设置 > 安全设置 > 本地策略 > 安全选项 >“网络安全性: 配置允许Kerberos使用的加密类型”,并选中DES_CBC_MD5和DES_CBC_MD5的两个复选框。重要提示: 在同一组策略中,还请确保选中RC4、AES128和AES256的复选框。这些加密类型不会用于您的网站票证,但会用于域中的其他所有内容。然后点击确认并关闭GPMC对话框。
    4. 在DC服务器和客户端上运行“gpupdate /force”命令。
    5. 在客户端上运行"klist purge"以清除所有Kerberos票证。
    6. 在Web浏览器中,清除缓存并删除所有cookie。
    7. 确保DC服务器允许8080 TCP入站。
    8. 再次尝试。

    参考资料: Windows配置Kerberos支持的加密类型

    编辑3: 避免在同一台机器上运行Kerberos KDC(DC)、客户端和服务器。即使你做了其他正确的事情,这也是获得“缺陷令牌错误”的经典方法。

    编辑4: (由OP验证的最终更新)查看新创建的ktpass keytab输出,我看到这个: 目标域控制器: WIN-OVV6VHBGIB8.fusionis.life。现在,keytab中定义的SPN是HTTP/kerberos500.nickis.life。AD域名与您定义的SPN不同,因此除非在这些域之间建立了某种信任设置,否则这将无法工作。如果您没有信任关系,则需要使用SPN HTTP/kerberos500.fusionis.life。


    1
    这个答案假设你有一个在域名为kerberos500.nickis.life的主机上运行的HTTP服务。如果你没有这样一个带有那个名字的主机,我的答案会稍作调整。请告诉我任何变化。 - T-Heron
    2
    重要提示:在同一组策略中,请确保RC4、AES128和AES256的复选框也被选中!这些加密类型不会用于您网站的票证,但它们将用于域中的其他所有内容。 - T-Heron
    1
    明白了。您正在测试一个复杂的场景,使用一种加密协议(DES),该协议自Windows Server 2008 R2版本以来已被默认禁用于Active Directory。当前标准是Windows Server 2016,它使用AES256。我在这个答案中尽力了,但我认为我无法涵盖您在测试所有这些内容时可能遇到的所有细微差别。 - T-Heron
    1
    我已经给你解决这个问题的框架了。你只需要坚持下去,你就能解决它。一旦你在你的DC上完成测试,你就必须回到我的原始答案,以使Kerberos SSO能够在单独的主机上与AD配合工作。 - T-Heron
    1
    现在有很多评论,这可能会被移动到系统的聊天中,如果移动了,请跟着我。我查看了您的新ktpass密钥表创建输出,并看到了这个:Targeting domain controller: WIN-OVV6VHBGIB8.fusionis.life。现在,您在密钥表中定义的SPN是HTTP/kerberos500.nickis.life。AD域名与您定义的SPN不同,因此,除非在这些域之间建立了某种信任设置,否则这将无法工作。如果您没有信任关系,则需要改用HTTP/kerberos500.fusionis.life作为SPN。 - T-Heron
    显示剩余8条评论

    1

    对我来说,问题在于配置的SPN与指定的URL不同。

    假设SPN是HTTP/serviceX.domain.com,当访问服务时,应该通过http://serviceX.doamin.com进行访问,否则会抛出Defective token错误。

    (我也发现所有与Keberos相关的错误消息都非常令人困惑,也许MIT故意让它难以理解,相应地,攻击将更加困难。)


    -1

    尝试这个:

    解决方案1

    在Windows命令提示符下运行以下命令:

    ksetup /addkdc ksetup /addhosttorealmmap

    并在浏览器中设置SPNEGO设置。

    解决方案2

    使用Firefox尝试,在此之前执行以下操作:

    1)在Firefox中打开此URL

    about:config

    2)设置:network.negotiate-auth.trusted-uris

    为需要协商身份验证的任何集群DNS域(如启用Kerberos的集群HTTP身份验证)设置。

    例如:

    network.negotiate-auth.trusted-uris=.lily.cloudera.com,.solr.cloudera.com

    2) 将此设置为:network.auth.use-sspi=false 3) 重新启动Firefox 4) 您需要从此处下载Windows安装程序:

    http://web.mit.edu/kerberos/dist/#kfw-4.0

    5)将 Kerberos 客户端配置复制到此处

    C:\ProgramData\MIT\Kerberos5\krb5.ini

    6) 使用MIT kerberos GUI客户端创建一个票据

    7) 使用Firefox再次尝试

    希望能够帮到您。


    4
    OP的SPN与在Web浏览器中输入的内容不匹配,因此在他首先解决这个问题之前,Kerberos将永远不会起作用。此外,还存在至少一个或两个以上的问题,我在上面已经概述了。 :-) - T-Heron
    2
    ksetup /addkdc ksetup /addhosttorealmmap complains /AddHostToRealmMap requires 2 options (only 0 supplied). - Nicholas DiPiazza

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接