springframework org dependencia java active-directory kerberos spnego

java - org - suma de comprobación fallada: Kerberos/Spring/Active Directory(2008)



spring security web (2)

Estamos teniendo problemas para lograr que la autenticación Kerberos / AD funcione con una aplicación web Spring, y creo que el problema tiene que ver con los tipos de cifrado para los tickets de Kerberos y el nivel funcional del dominio de Active Directory.

La configuración básica es:

Tengo un entorno en el que el nivel funcional del dominio de Active Directory es Windows Server 2003 y todo funciona bien, y los clientes se autentican como se espera si están conectados al dominio. Usando kerbtray para examinar los tickets en este entorno, puedo ver que todos tienen tanto el tipo de encriptación como el tipo de cifrado de clave "RSADSI RC4-HMAC".

Tengo un nuevo dominio con Windows Server 2008 de nivel funcional, y aquí es donde la autenticación no funciona. El error de la aplicación que se devuelve al intentar validar el ticket es:

Kerberos validation not successful... Caused by: 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.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:146) at org.springframework.security.extensions.kerberos.SunJaasKerberosTicketValidator$KerberosValidateAction.run(SunJaasKerberosTicketValidator.java:136) ... 34 more Caused by: KrbException: Checksum failed at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(Unknown Source) at sun.security.krb5.internal.crypto.ArcFourHmacEType.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) ... 43 more Caused by: java.security.GeneralSecurityException: Checksum failed at sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(Unknown Source) at sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(Unknown Source)

El seguimiento de la pila muestra "ArcfourCrypto.decrypt", por lo que presumiblemente trata el ticket de Kerberos como RC4-HMAC. Usando kerbtray nuevamente para examinar los tickets, esta vez hay 2 tickets en el cliente para el dominio: krbtgt / .COM. Ambos tickets tienen cifrado de clave tipo RSADS1 RC4-HMAC, uno también tiene esto para el tipo de cifrado de tickets, pero el otro tiene "Kerberos AES256-CTS-HMAC-SHA1-96".

No estoy seguro de que esta sea la causa del problema, pero es la única diferencia que he podido encontrar en los dos entornos que podría explicar la excepción de autenticación. Intenté cambiar la política de cifrado de AD, probé IE y Firefox, y casi todo lo demás en lo que podía pensar, pero nada me ha funcionado.

Cualquier ayuda para abordar esto sería muy apreciada. Preferiría solucionarlo en Java porque probablemente no pueda dictar demasiado sobre la configuración AD de producción.


El problema es cómo se genera token vs cómo se valida en el lado del servidor. Desde la traza de excepción, muestra que el problema es que el lado del cliente no está configurando suma de comprobación y el lado del servidor está buscando validar la suma de comprobación. Checksum es uno de los valores de params establecidos en el token que tiene un significado obvio.

Debe haber una forma en 2008 de desactivar esta característica para ignorar la verificación de suma de comprobación. Pero abre otra puerta y es posible que deba evaluar el riesgo residual.


El problema parece estar en la tabla de claves. Hay algunas secuencias de acción que conducen a algunos estados específicos de archivo keytab: (A) keytab funciona con Java pero no funciona con k5start / kinit; (B) keytab no funciona con Java, pero funciona con k5start / kinit; (C) keytab funciona con ambos.

El código corto de Java que permite verificar si Java puede autenticarse usando el archivo keytab:

import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.util.HashMap; import java.util.Map; import java.util.Properties; import javax.security.auth.Subject; import com.sun.security.auth.module.Krb5LoginModule; /** * This is simple Java program that tests ability to authenticate * with Kerberos using the JDK implementation. * * The program uses no libraries but JDK itself. */ public class Krb { private void loginImpl(final String propertiesFileName) throws Exception { System.out.println("NB: system property to specify the krb5 config: [java.security.krb5.conf]"); //System.setProperty("java.security.krb5.conf", "/etc/krb5.conf"); System.out.println(System.getProperty("java.version")); System.setProperty("sun.security.krb5.debug", "true"); final Subject subject = new Subject(); final Krb5LoginModule krb5LoginModule = new Krb5LoginModule(); final Map<String,String> optionMap = new HashMap<String,String>(); if (propertiesFileName == null) { //optionMap.put("ticketCache", "/tmp/krb5cc_1000"); optionMap.put("keyTab", "/etc/krb5.keytab"); optionMap.put("principal", "foo"); // default realm optionMap.put("doNotPrompt", "true"); optionMap.put("refreshKrb5Config", "true"); optionMap.put("useTicketCache", "true"); optionMap.put("renewTGT", "true"); optionMap.put("useKeyTab", "true"); optionMap.put("storeKey", "true"); optionMap.put("isInitiator", "true"); } else { File f = new File(propertiesFileName); System.out.println("======= loading property file ["+f.getAbsolutePath()+"]"); Properties p = new Properties(); InputStream is = new FileInputStream(f); try { p.load(is); } finally { is.close(); } optionMap.putAll((Map)p); } optionMap.put("debug", "true"); // switch on debug of the Java implementation krb5LoginModule.initialize(subject, null, new HashMap<String,String>(), optionMap); boolean loginOk = krb5LoginModule.login(); System.out.println("======= login: " + loginOk); boolean commitOk = krb5LoginModule.commit(); System.out.println("======= commit: " + commitOk); System.out.println("======= Subject: " + subject); } public static void main(String[] args) throws Exception { System.out.println("A property file with the login context can be specified as the 1st and the only paramater."); final Krb krb = new Krb(); krb.loginImpl(args.length == 0 ? null : args[0]); } }

y el archivo de propiedad para usar:

#ticketCache=/tmp/krb5cc_1000 keyTab=/etc/krb5.keytab principal=foo doNotPrompt=true refreshKrb5Config=true useTicketCache=true renewTGT=true useKeyTab=true storeKey=true isInitiator=true

(A continuación suponemos que krb / kdc está correctamente instalado y configurado, la base de datos se crea con kdb5_util. El estado inicial de cada secuencia de comandos es: el archivo keytab eliminado, el token caché se borra, el usuario "foo" se elimina de la base de datos. )

La siguiente secuencia de acción lleva al estado keytab (A):

$ echo -e "foo/nfoo" | kadmin.local -q "addprinc foo" $ echo -e "foo/nfoo" | kadmin.local -q "ktadd foo" $ java -cp . Krb ./krb5.properties # Now java auth okay, but the following command fails: $ k5start foo Kerberos initialization for [email protected] Password for [email protected]: k5start: error getting credentials: Decrypt integrity check failed $

La siguiente secuencia de acción lleva al estado keytab (B):

$ echo -e "foo/nfoo" | kadmin.local -q "addprinc foo" $ echo -e "foo/nfoo" | kadmin.local -q "ktadd foo" $ echo -e "foo/nfoo" | kadmin.local -q "cpw foo" $ java -cp . Krb ./krb5.properties A property file with the login context can be specified as the 1st and the only paramater. NB: system property to specify the krb5 config: [java.security.krb5.conf] 1.6.0_33 ======= loading property file [/tmp/krb-test/yhadoop-common/./krb5.properties] Debug is true storeKey true useTicketCache true useKeyTab true doNotPrompt true ticketCache is null isInitiator true KeyTab is /etc/krb5.keytab refreshKrb5Config is true principal is foo tryFirstPass is false useFirstPass is false storePass is false clearPass is false Refreshing Kerberos configuration Config name: /etc/krb5.conf >>> KdcAccessibility: reset >>> KdcAccessibility: reset Acquire TGT from Cache >>>KinitOptions cache name is /tmp/krb5cc_0 Principal is [email protected] null credentials from Ticket Cache >>> KeyTabInputStream, readName(): EXAMPLE.COM >>> KeyTabInputStream, readName(): foo >>> KeyTab: load() entry length: 49; type: 23 Added key: 23version: 3 Ordering keys wrt default_tkt_enctypes list default etypes for default_tkt_enctypes: 23. 0: EncryptionKey: keyType=23 kvno=3 keyValue (hex dump)= 0000: 5F 7F 9B 42 BB 02 51 81 32 05 1D 7B C0 9F 19 C0 _..B..Q.2....... principal''s key obtained from the keytab Acquire TGT using AS Exchange default etypes for default_tkt_enctypes: 23. >>> KrbAsReq calling createMessage >>> KrbAsReq in createMessage >>> KrbKdcReq send: kdc=localhost UDP:88, timeout=30000, number of retries =3, #bytes=128 >>> KDCCommunication: kdc=localhost UDP:88, timeout=30000,Attempt =1, #bytes=128 >>> KrbKdcReq send: #bytes read=611 >>> KrbKdcReq send: #bytes read=611 >>> KdcAccessibility: remove localhost:88 >>> EType: sun.security.krb5.internal.crypto.ArcFourHmacEType Checksum failed ! [Krb5LoginModule] authentication failed Checksum failed Exception in thread "main" javax.security.auth.login.LoginException: Checksum failed at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:696) at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:542) at Krb.loginImpl(Krb.java:65) at Krb.main(Krb.java:77) Caused by: KrbException: Checksum failed at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(ArcFourHmacEType.java:85) at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(ArcFourHmacEType.java:77) at sun.security.krb5.EncryptedData.decrypt(EncryptedData.java:168) at sun.security.krb5.KrbAsRep.<init>(KrbAsRep.java:87) at sun.security.krb5.KrbAsReq.getReply(KrbAsReq.java:446) at sun.security.krb5.Credentials.sendASRequest(Credentials.java:401) at sun.security.krb5.Credentials.acquireTGT(Credentials.java:350) at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:672) ... 3 more Caused by: java.security.GeneralSecurityException: Checksum failed at sun.security.krb5.internal.crypto.dk.ArcFourCrypto.decrypt(ArcFourCrypto.java:388) at sun.security.krb5.internal.crypto.ArcFourHmac.decrypt(ArcFourHmac.java:74) at sun.security.krb5.internal.crypto.ArcFourHmacEType.decrypt(ArcFourHmacEType.java:83) ... 10 more $

Pero el "k5start foo" está bien en este estado, así como también "kinit foo".

Y la siguiente secuencia de acción lleva al estado (C):

$ echo -e "foo/nfoo" | kadmin.local -q "addprinc foo" $ ktutil ktutil: addent -password -p foo -k 1 -e rc4-hmac Password for [email protected]: ktutil: wkt /etc/krb5.keytab ktutil: q

después de eso, tanto k5start / kinit como la verificación java dan resultado positivo.

Ambiente:

yum list krb5-appl-servers krb5-libs krb5-server krb5-workstation kstart pam_krb5 ... Installed Packages krb5-libs.x86_64 1.9-33.el6_3.3 @updates krb5-server.x86_64 1.9-33.el6_3.3 @updates krb5-workstation.x86_64 1.9-33.el6_3.3 @updates kstart.x86_64 4.1-2.el6 @epel ... $ cat /etc/redhat-release CentOS release 6.3 (Final) $ java -version java version "1.6.0_33" Java(TM) SE Runtime Environment (build 1.6.0_33-b03) Java HotSpot(TM) 64-Bit Server VM (build 20.8-b03, mixed mode)

También el mismo comportamiento observado con Java 7. También se observó el mismo comportamiento en Ubuntu preciso (12.04.1 LTS) con los kerberos 5-1.10.3 de MIT compilados a partir de la distribución fuente.