我正在尝试使用 Spring Boot、Ldap 和 kerberos 实现 SSO。对于不同的加密类型,我遇到多个校验和失败的错误。
环境细节:-
机器:Windows 10
JDK版本:Oracle 1.8.0_144(64位)
我似乎遇到了一个死胡同,我无法找到任何解决方案。
这是我在运行时遇到的错误
Added key: 17version: 5
Added key: 18version: 5
Added key: 23version: 5
Found unsupported keytype (3) for HTTP/host.test@EXAMPLE.COM
Found unsupported keytype (1) for HTTP/host.test@EXAMPLE.COM
>>> EType: sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType
2019-08-30 11:26:10.746 WARN 6640 --- [p-bio-80-exec-9] w.a.SpnegoAuthenticationProcessingFilter : Negotiate Header was invalid: Negotiate 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
org.springframework.security.authentication.BadCredentialsException: Kerberos validation not successful
at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:71) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
at org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider.authenticate(KerberosServiceAuthenticationProvider.java:64) ~[spring-security-kerberos-core-1.0.1.RELEASE.jar:1.0.1.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:174) ~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.authentication.ProviderManager.authenticate(ProviderManager.java:199) ~[spring-security-core-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter$AuthenticationManagerDelegator.authenticate(WebSecurityConfigurerAdapter.java:494) ~[spring-security-config-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.kerberos.web.authentication.SpnegoAuthenticationProcessingFilter.doFilter(SpnegoAuthenticationProcessingFilter.java:145) ~[spring-security-kerberos-web-1.0.1.RELEASE.jar:1.0.1.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:200) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:331) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:116) [spring-security-web-4.2.3.RELEASE.jar:4.2.3.RELEASE]
Caused by: java.security.PrivilegedActionException: null
at java.security.AccessController.doPrivileged(Native Method) ~[na:1.8.0_144]
at javax.security.auth.Subject.doAs(Subject.java:422) ~[na:1.8.0_144]
at org.springframework.security.kerberos.authentication.sun.SunJaasKerberosTicketValidator.validateTicket(SunJaasKerberosTicketValidator.java:68)
Caused by: org.ietf.jgss.GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
at sun.security.jgss.krb5.Krb5Context.acceptSecContext(Krb5Context.java:856) ~[na:1.8.0_144]
at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:342) ~[na:1.8.0_144]
at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:285) ~[na:1.8.0_144]
at sun.security.jgss.spnego.SpNegoContext.GSS_acceptSecContext(SpNegoContext.java:906) ~[na:1.8.0_144]
at sun.security.jgss.spnego.SpNegoContext.acceptSecContext(SpNegoContext.java:556) ~[na:1.8.0_144]
at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:342) ~[na:1.8.0_144]
at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:285) ~[na:1.8.0_144]
Caused by: sun.security.krb5.KrbCryptoException: Checksum failed
at sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType.decrypt(Aes128CtsHmacSha1EType.java:102) ~[na:1.8.0_144]
at sun.security.krb5.internal.crypto.Aes128CtsHmacSha1EType.decrypt(Aes128CtsHmacSha1EType.java:94) ~[na:1.8.0_144]
at sun.security.krb5.EncryptedData.decrypt(EncryptedData.java:175) ~[na:1.8.0_144]
at sun.security.krb5.KrbApReq.authenticate(KrbApReq.java:281) ~[na:1.8.0_144]
at sun.security.krb5.KrbApReq.<init>(KrbApReq.java:149) ~[na:1.8.0_144]
at sun.security.jgss.krb5.InitSecContextToken.<init>(InitSecContextToken.java:108)
Caused by: java.security.GeneralSecurityException: Checksum failed
at sun.security.krb5.internal.crypto.dk.AesDkCrypto.decryptCTS(AesDkCrypto.java:451) ~[na:1.8.0_144]
我从 Windows 服务器生成的 Keytab 文件,其中 AD 可通过此命令使用 -
ktpass /out test.keytab /mapuser ldap-read@EXAMPLE.COM /princ HTTP/host.test@EXAMPLE.COM /pass password /ptype KRB5_NT_PRINCIPAL /crypto All
为同一用户设置SPN
Setspn –A HTTP/host.test@EXAMPLE.COM ldap-read
通过命令检查keytab文件
ktab -l -e -t -k "C:\Install\test.keytab"
这是结果
Keytab name: C:\Install\test.keytab
KVNO Timestamp Principal
---- -------------- ---------------------------------------------------------------------
5 1/1/70 5:30 AM http/host.test@example.com (1:DES CBC mode with CRC-32)
5 1/1/70 5:30 AM http/host.test@example.com (3:DES CBC mode with MD5)
5 1/1/70 5:30 AM http/host.test@example.com (23:RC4 with HMAC)
5 1/1/70 5:30 AM http/host.test@example.com (18:AES256 CTS mode with HMAC SHA1-96)
5 1/1/70 5:30 AM http/host.test@example.com (17:AES128 CTS mode with HMAC SHA1-96)
还更新了安全选项的客户端和服务器设置并标记检查以允许加密类型
这是安全配置java文件
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Value("${ldap.url}")
private String ldapUrl;
@Value("${ldap.manager.dn}")
private String ldapManagerDn;
@Value("${ldap.manager.password}")
private String ldapManagerPassword;
@Value("${ldap.user.searchbase}")
private String ldapUserSearchBase;
@Value("${app.service-principal}")
private String servicePrincipal;
@Autowired
private StaffService userService;
@Autowired
CustomAuthenticationProvider customAuthProvider;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable().exceptionHandling().authenticationEntryPoint(spnegoEntryPoint())
.and().authorizeRequests()
.antMatchers("/signup","/logout").permitAll()
.antMatchers("/","/login","/index","/index/**","/projects","/projects/**","/project")
.fullyAuthenticated()
.and()
.formLogin().loginPage("/login")
.defaultSuccessUrl("/index")
.and()
.logout().permitAll()
.and().headers()
.frameOptions()
.sameOrigin()
.and()
.addFilterBefore(
spnegoAuthenticationProcessingFilter(authenticationManagerBean()),
BasicAuthenticationFilter.class);
}
@Override
public void configure(AuthenticationManagerBuilder auth) throws Exception{
auth.
authenticationProvider(kerberosAuthenticationProvider())
.authenticationProvider(kerberosServiceAuthenticationProvider());
}
@Bean
public KerberosAuthenticationProvider kerberosAuthenticationProvider() {
KerberosAuthenticationProvider provider =
new KerberosAuthenticationProvider();
SunJaasKerberosClient client = new SunJaasKerberosClient();
client.setDebug(true);
provider.setKerberosClient(client);
provider.setUserDetailsService(userService);
return provider;
}
@Bean
public SpnegoEntryPoint spnegoEntryPoint() {
return new SpnegoEntryPoint("/login");
}
@Bean
public SpnegoAuthenticationProcessingFilter spnegoAuthenticationProcessingFilter(
AuthenticationManager authenticationManager) {
SpnegoAuthenticationProcessingFilter filter =
new SpnegoAuthenticationProcessingFilter();
try {
filter.setAuthenticationManager(authenticationManagerBean());
} catch (Exception e) {
e.printStackTrace();
}
return filter;
}
@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider() {
KerberosServiceAuthenticationProvider provider =
new KerberosServiceAuthenticationProvider();
provider.setTicketValidator(sunJaasKerberosTicketValidator());
provider.setUserDetailsService(userService);
return provider;
}
@Bean
public SunJaasKerberosTicketValidator sunJaasKerberosTicketValidator() {
SunJaasKerberosTicketValidator ticketValidator =
new SunJaasKerberosTicketValidator();
ticketValidator.setServicePrincipal(servicePrincipal);
FileSystemResource fs = new FileSystemResource("C:/install/test.keytab");
ticketValidator.setKeyTabLocation(fs);
ticketValidator.setDebug(true);
return ticketValidator;
}
@Bean
public LdapTemplate ldapTemplate() throws Exception{
LdapTemplate ldapTemplate = new LdapTemplate();
ldapTemplate.setIgnorePartialResultException(true);
ldapTemplate.setContextSource(kerberosLdapContextSource());
return ldapTemplate;
}
@Bean
public KerberosLdapContextSource kerberosLdapContextSource() {
KerberosLdapContextSource contextSource = new KerberosLdapContextSource(ldapUrl);
contextSource.setBase(ldapUserSearchBase);
contextSource.setUserDn(ldapManagerDn);
contextSource.setPassword(ldapManagerPassword);
SunJaasKrb5LoginConfig loginConfig = new SunJaasKrb5LoginConfig();
FileSystemResource fs = new FileSystemResource("C:/install/test.keytab");
loginConfig.setKeyTabLocation(fs);
loginConfig.setServicePrincipal(servicePrincipal);
loginConfig.setDebug(true);
loginConfig.setUseTicketCache(true);
loginConfig.setIsInitiator(true);
contextSource.setLoginConfig(loginConfig);
return contextSource;
}
@Bean
public LdapUserDetailsService ldapUserDetailsService() {
FilterBasedLdapUserSearch userSearch =
new FilterBasedLdapUserSearch(ldapUserSearchBase, ldapUserSearchFilter, kerberosLdapContextSource());
LdapUserDetailsService service = new LdapUserDetailsService(userSearch);
CustomLdapUserDetailsMapper customLdapUserDetailsMapper = new CustomLdapUserDetailsMapper();
service.setUserDetailsMapper(customLdapUserDetailsMapper);
return service;
}
@Bean
public BCryptPasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
@Bean
public RestTemplate restTemplate() {
FileSystemResource fs = new FileSystemResource("C:/install/test.keytab");
return new KerberosRestTemplate(fs.getPath(),servicePrincipal);
}
}
以下是Windows 10 机器中位于C:\Windows的krb5.ini文件的内容:-
[libdefaults]
forwardable = true
dns_lookup_kdc = true
dns_lookup_realm = true
default_realm = BAUER.DE
default_keytab_name = "C:\Install\test.keytab"
default_tkt_enctypes = rc4-hmac aes256-cts aes128-cts des3-cbc-sha1 des-cbc-md5 des-cbc-crc
default_tgs_enctypes = rc4-hmac aes256-cts aes128-cts des3-cbc-sha1 des-cbc-md5 des-cbc-crc
permitted_enctypes = rc4-hmac aes256-cts aes128-cts des3-cbc-sha1 des-cbc-md5 des-cbc-crc
[realms]
EXAMPLE.COM = {
kdc = host.test
}
[domain_realm]
.host.test = EXAMPLE.COM
host.test = EXAMPLE.COM
我还更新了C:\Program Files\Java\jre1.8.0_191\lib\security 和 C:\Program Files\Java\jdk1.8.0_144\jre\lib\security 文件夹下的JCE jar文件。
应该怎么做才能克服这个异常?是否有任何 Java 代码需要更新或 kerberos 的任何配置需要更改。有没有人有任何想法?
更新 1:
校验和问题仍然可用。但是,当我使用 kinit 命令从 JAVA JDK 检查 keytab 文件时,我看到了一个新问题,
我从 JDK 1.8 bin 目录中的服务器执行了以下命令
kinit -k -t "C:\Install\test.keytab" HTTP/host.test@EXAMPLE.COM
并得到 ICMP 端口不可达错误
java.net.PortUnreachableException: ICMP Port Unreachable
at java.net.DualStackPlainDatagramSocketImpl.socketReceiveOrPeekData(Native Method)
at java.net.DualStackPlainDatagramSocketImpl.receive0(DualStackPlainDatagramSocketImpl.java:124)
at java.net.AbstractPlainDatagramSocketImpl.receive(AbstractPlainDatagramSocketImpl.java:143)
at java.net.DatagramSocket.receive(DatagramSocket.java:812)
at sun.security.krb5.internal.UDPClient.receive(NetClient.java:206)
at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:411)
at sun.security.krb5.KdcComm$KdcCommunication.run(KdcComm.java:364)
at java.security.AccessController.doPrivileged(Native Method)
at sun.security.krb5.KdcComm.send(KdcComm.java:348)
at sun.security.krb5.KdcComm.sendIfPossible(KdcComm.java:253)
at sun.security.krb5.KdcComm.send(KdcComm.java:229)
at sun.security.krb5.KdcComm.send(KdcComm.java:200)