Spring Security Kerberos windows 身份验证中的错误

Posted

技术标签:

【中文标题】Spring Security Kerberos windows 身份验证中的错误【英文标题】:Error in Spring Security Kerberos windows authentication 【发布时间】:2016-03-24 08:25:45 【问题描述】:

我正在尝试在我们的环境中设置基于 Spring 的安全 Web 应用程序。如中所述 http://docs.spring.io/spring-security-kerberos/docs/1.0.1.RELEASE/reference/htmlsingle/#setupwinkerberos

为了确认一切设置正确,我正在尝试运行 Spring Boot Security 示例应用程序(按照此处所述构建:http://docs.spring.io/spring-security-kerberos/docs/1.0.1.RELEASE/reference/htmlsingle/#samples-sec-server-win-auth)

这是我们在 TEST 域下的测试环境:

Active Directory(称为 AD 服务器)域控制器,Windows 2008 R2 64 位 计算机名称:adjavatest1 计算机全名:adjavatest1.test.company.info 用户:TEST\administrator 客户端电脑,Windows 7 计算机名称:adjavatest2 计算机全名:adjavatest2.test.company.info 用户:TEST\administrator 应用服务器(简称 Web 服务器) 计算机名称:kpiq-dev 计算机全名:kpiq-dev.test.company.info 用户:TEST\administrator

到目前为止,我已经做了以下步骤来配置环境和应用程序

1) 在AD服务器上设置SPN

setspn -A HTTP/adjavatest1.test.company.info TEST\administrator

(许多来源建议创建 SPN“HTTP/adjavatest1”和“HOST/adjavatest1” - 我已经尝试过,没有任何区别。)

2) 在 AD 服务器上验证 SPN

>setspn -L TEST\administrator
Registered ServicePrincipalNames for CN=Administrator,CN=Users,DC=test,DC=company,DC=info:
HTTP/adjavatest1.test.company.info

3) 映射用户/服务并在 AD 服务器上生成 keytab 文件

>ktpass -princ HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO -pass pswd123 -mapuser TEST\Administrator -out .\ adjavatest1.HTTP.keytab -ptype KRB5_NT_PRINCIPAL -crypto All
Targeting domain controller:  adjavatest1.test.company.info
Using legacy password setting method
Successfully mapped HTTP/adjavatest1.test.company.info to Administrator.
Key created.
Key created.
Key created.
Key created.
Key created.
Output keytab to .\ adjavatest1.HTTP.keytab:
Keytab version: 0x502
keysize 85 HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO ptype 1 (KRB5_NT_PRINCIPAL) vno 5 etype 0x1 (DES-CBC-CRC) keylength 8 (0x6da81379831f37ad)
keysize 85 HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO ptype 1 (KRB5_NT_PRINCIPAL) vno 5 etype 0x3 (DES-CBC-MD5) keylength 8 (0x6da81379831f37ad)
keysize 93 HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO ptype 1 (KRB5_NT_PRINCIPAL) vno 5 etype 0x17 (RC4-HMAC ) keylength 16 (0xe32edb70a8df744e3b0f87ea7ff515f7)
keysize 109 HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO ptype 1 (KRB5_NT_PRINCIPAL) vno 5 etype 0x12 (AES256-SHA1) keylength 32 (0xf744e212c2e48e34c815364c0b5290a68b37b6c65a7cd0befcbcc2625e3e6c79)
keysize 93 HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO ptype 1 (KRB5_NT_PRINCIPAL) vno 5 etype 0x11 (AES128-SHA1) keylength 16 (0x20f3474a818d4d326136449a8a660e2c)

4) 将 keytab 文件复制到 C;\SpringSSO 目录下的 Web 服务器上

5) 在 Web 服务器上验证 keytab 使用来自 MIT kerberos 工具的 kinit c:\SpringSSO>kinit -V -k -t adjavatest1.HTTP.keytab HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO 使用现有缓存:初始默认 ccache 使用主体:HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO 使用 keytab:adjavatest1.HTTP.keytab 通过 Kerberos v5 认证 使用 jdk 的 kinit c:\SpringSSO>kinit -k -t adjavatest1.HTTP.keytab HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO 新票据存储在缓存文件 C:\Users\administrator.TEST\krb5cc_administrator

6) 在两个位置的 Web 服务器上的 jre/lib/security 中安装“Kerberos 和无限强度策略”:

c:\Program Files\Java\jre1.8.0_65\lib\security\
c:\Program Files\Java\jdk1.8.0_65\jre\lib\security\

7) 检查 Web 服务器上的 windows 注册表:

HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Kerberos\
Value Name: allowtgtsessionkey
Value: 0x1

8) 构建取自 https://github.com/spring-projects/spring-security-kerberos/tree/master/spring-security-kerberos-samples 的 spring-security-kerberos-samples\sec-server-win-auth 应用程序 在 application.yml 中使用配置属性

server:
    port: 80
app:
    ad-domain: TEST.COMPANY.INFO
    ad-server: ldap://ADJAVATEST1.TEST.COMPANY.INFO/
    service-principal: HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO
    keytab-location:  adjavatest1.HTTP.keytab
    ldap-search-base: DC=TEST,DC=COMPANY,DC=INFO
    ldap-search-filter: "(| (userPrincipalName=0) (sAMAccountName=0))"

9) 将Spring boot应用部署到C:\SpringSSO目录下的Web Server

10) 在 Web Server 上启动 Web 应用程序 c:\SpringSSO>java -Dsun.security.krb5.debug=true -Djava.security.krb5.conf=.\krb5.conf -jar sec-server-win-auth-1.0.2.BUILD-SNAPSHOT.jar

krb5.conf 中的 Kerberos 配置(我尝试了不同的 enctype,“arcfour-hmac-md5”只是最后一次实验)

[libdefaults]
 default_realm = TEST.COMPANY.INFO
 permitted_enctypes = arcfour-hmac-md5 rc4-hmac aes256-cts aes128-cts des3-cbc-sha1 des-cbc-md5 des-cbc-crc
 default_tgs_enctypes = arcfour-hmac-md5 rc4-hmac aes256-cts aes128-cts des3-cbc-sha1 des-cbc-md5 des-cbc-crc
 default_tkt_enctypes = arcfour-hmac-md5 rc4-hmac aes256-cts aes128-cts des3-cbc-sha1 des-cbc-md5 des-cbc-crc
 dns_lookup_kdc = true
 dns_lookup_realm = false

[realms]
 TEST.COMPANY.INFO = 
     kdc =  ADJAVATEST1.TEST.COMPANY.INFO
     admin_server =  ADJAVATEST1.TEST.COMPANY.INFO
     master_kdc =  ADJAVATEST1.TEST.COMPANY.INFO
     default_domain = TEST.COMPANY.INFO
 

[domain_realm]
 .TEST.COMPANY.INFO = TEST.COMPANY.INFO
 TEST.COMPANY.INFO = TEST.COMPANY.INFO

11) 在客户端的 IE 浏览器中 将路径 *.test.company.info 添加到 IE 浏览器作为内网模式 将浏览器指向 http://kpiq-dev.test.company.info/hello

12) 将浏览器指向 http://kpiq-dev.test.company.info/hello

13) 查看Web Server的日志提示服务器无法编码

2015-12-17 08:55:35.893 DEBUG 1876 --- [p-nio-80-exec-3] w.a.SpnegoAuthenticationProcessingFilter : Received Negotiate Header for request http:// kpiq-dev.test.company.info/hello: Negotiate YIIH ...trucated... H4qgvsM
2015-12-17 08:55:35.893 DEBUG 1876 --- [p-nio-80-exec-3] o.s.s.authentication.ProviderManager     : Authentication attempt using org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider
2015-12-17 08:55:35.893 DEBUG 1876 --- [p-nio-80-exec-3] .a.KerberosServiceAuthenticationProvider : Try to validate Kerberos Token
Found KeyTab c:\SpringSSO\ adjavatest1.HTTP.keytab for HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO
Found KeyTab c:\SpringSSO\ adjavatest1.HTTP.keytab for HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO
Entered Krb5Context.acceptSecContext with state=STATE_NEW
Java config name: .\krb5.conf
Loaded from Java config
>>> KeyTabInputStream, readName(): TEST.COMPANY.INFO
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName():  adjavatest1.test.company.info
>>> KeyTab: load() entry length: 85; type: 1
>>> KeyTabInputStream, readName(): TEST.COMPANY.INFO
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName():  adjavatest1.test.company.info
>>> KeyTab: load() entry length: 85; type: 3
>>> KeyTabInputStream, readName(): TEST.COMPANY.INFO
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName():  adjavatest1.test.company.info
>>> KeyTab: load() entry length: 93; type: 23
>>> KeyTabInputStream, readName(): TEST.COMPANY.INFO
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName():  adjavatest1.test.company.info
>>> KeyTab: load() entry length: 109; type: 18
>>> KeyTabInputStream, readName(): TEST.COMPANY.INFO
>>> KeyTabInputStream, readName(): HTTP
>>> KeyTabInputStream, readName():  adjavatest1.test.company.info
>>> KeyTab: load() entry length: 93; type: 17
Looking for keys for: HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO
Added key: 17version: 5
Added key: 18version: 5
Added key: 23version: 5
Found unsupported keytype (3) for HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO
Found unsupported keytype (1) for HTTP/adjavatest1.test.company.info@TEST.COMPANY.INFO
>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
2015-12-17 08:55:36.236  WARN 1876 --- [p-nio-80-exec-3] w.a.SpnegoAuthenticationProcessingFilter : Negotiate Header was invalid: Negotiate YIIHNAYGKwYBBQU ...trucated... dH4qgvsM

org.springframework.security.authentication.BadCredentialsException: Kerberos validation not successful
            at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:71)
            at org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:64)
            at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:156)
            at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:177)
            at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:446)
            at org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145)
            at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
...trucated...
            at java.lang.Thread.run(Unknown Source)
Caused by: java.security.PrivilegedActionException: null
            at java.security.AccessController.doPrivileged(Native Method)
            at javax.security.auth.Subject.doAs(Unknown Source)
            at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:68)
            ... 45 common frames omitted
Caused by: org.ietf.jgss.GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
            at sun.security.jgss.krb5.Krb5Context.acceptSecContext(Unknown Source)
            at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
            at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
            at sun.security.jgss.spnego.SpNegoContext.GSS_acceptSecContext(Unknown Source)
            at sun.security.jgss.spnego.SpNegoContext.acceptSecContext(Unknown Source)
            at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
            at sun.security.jgss.GSSContextImpl.acceptSecContext(Unknown Source)
            at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:170)
            at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:153)
            ... 48 common frames omitted
Caused by: sun.security.krb5.KrbCryptoException: Checksum failed
            at sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType.decrypt(Unknown Source)
            at sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType.decrypt(Unknown Source)
            at sun.security.krb5.EncryptedData.decrypt(Unknown Source)
            at sun.security.krb5.KrbApReq.authenticate(Unknown Source)
            at sun.security.krb5.KrbApReq.<init>(Unknown Source)
            at sun.security.jgss.krb5.InitSecContextToken.<init>(Unknown Source)
            ... 57 common frames omitted
Caused by: java.security.GeneralSecurityException: Checksum failed
            at sun.security.krb5.internal.crypto.dk.AesDkCrypto.decryptCTS(Unknown Source)
            at sun.security.krb5.internal.crypto.dk.AesDkCrypto.decrypt(Unknown Source)
            at sun.security.krb5.internal.crypto.Aes256.decrypt(Unknown Source)
            ... 63 common frames omitted

为什么安全尝试加密 Aes256CtsHmacSha1EType,而不是 rc4-hmac? 有什么建议我可以尝试下一步吗?

提前谢谢你。

【问题讨论】:

也许可以尝试通过仅使用一种编码类型来简化事情,例如 RC4-HMAC。在您的 krb5.conf 中使用 -crypto RC4-HMAC-NT*__enctypes=rc4-hmac 生成密钥表? 使用仅为 RC4-HMAC-NT 生成的 keytab 文件,我无法运行 kinit(来自 MIT)。 kinit:获取初始凭据时出现一般预身份验证失败 您是否尝试使用该密钥表运行该应用程序?这是一个可怜的 kinit 不起作用,但解决方案不需要它......支持的加密可能会有所不同。 AD 和 Spring/Java 都支持 RC4-HMAC。 【参考方案1】:

两个可能的错误:

1) krb5.conf 未正确加载

2) 服务主体配置不正确

第一个解决方案:

在第 8 点)添加应用程序: kerberos-conf: /home/xyz/krb5.conf in application.yaml

在 Spring Security 项目中创建附加类

    @Configuration
    public class KerberosGlobalConfig 

        @Value("$app.kerberos-conf")
        private String kerberosGlobalConfPath;

        @Bean
        public GlobalSunJaasKerberosConfig globalSunJaasKerberosConfig() 
            GlobalSunJaasKerberosConfig globalSunJaasKerberosConfig = new 
            GlobalSunJaasKerberosConfig();
            //TODO remove hardcoding
            globalSunJaasKerberosConfig.setDebug(true);
            globalSunJaasKerberosConfig.setKrbConfLocation(kerberosGlobalConfPath);
            return globalSunJaasKerberosConfig;
        

     

【讨论】:

【参考方案2】:

它失败的原因是因为在票证验证器中,您正在尝试验证 NTLM 令牌,而不是 kerberos 票证。此行在您的日志中向您显示:

>>> EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType 2015-12-17 08:55:36.236 WARN 1876 --- [p-nio-80-exec-3] w.a.SpnegoAuthenticationProcessingFilter : Negotiate Header was invalid: Negotiate YIIHNAYGKwYBBQU ...trucated... dH4qgvsM

以 YII 开头的标头是 NTLM,这意味着您的初始验证无法获得 Kerberos 票证,因此浏览器按优先级顺序尝试下一个协议,在本例中为 NTLM。

看起来您的 SPN 是正确的,我建议您检查帐户和 keytab 文件的一些内容。确保您映射到 SPN 并用于生成密钥表的用户帐户在 Active Directory 用户管理中选择了正确的选项,即允许使用 Kerberos 进行 126 位和 256 位加密,并允许该帐户用于 kerberos 委托。

此外,此链接很重要: https://www.chromium.org/developers/design-documents/http-authentication

此链接概述了 Chrome 如何从请求 URL 构造 SPN 以执行 SPNEGO/SSO。基本上,您可能必须在 DNS 中设置转发区域以帮助解决您遇到的问题。 Chrome 浏览器从 IE 获取设置,因此在 IE 中设置 Intranet 设置应该可以处理该问题,但对于 SPN 解析,它会尝试将 URL 中的值解析为 CNAME,然后解析为 DNS 中的 A 条目。这可能会导致您看到的行为。通常,当您看到发送的是 NTLM 令牌而不是 Kerberos 票证时,可以追溯到浏览器试图使用错误的 SPN 来查询和获取 Kerberos 票证。如果您可以让从浏览器构建的 SPN 与您映射到您正在使用的服务帐户的 SPN 同步,那么您应该很高兴。

【讨论】:

以上是关于Spring Security Kerberos windows 身份验证中的错误的主要内容,如果未能解决你的问题,请参考以下文章

Spring Security Kerberos 与基本链接

BadCredentialsException:Kerberos 验证不成功

Spring-security、Tomcat 和 SPNEGO - 最佳方法

安全框架Shiro和Spring Security比较

Flink kafka kerberos的配置

学好Spring Security 和Apache Shiro你需要具备这些条件