2011-12-14 21 views
8

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 Kerberos y Active Directory nivel funcional de dominio.Suma de comprobación fallida: Kerberos/Spring/Active Directory (2008)

La configuración básica es:

Yo tengo uno entorno donde el nivel funcional del dominio de Active Directory es Windows Server 2003 y todo funciona bien, con clientes autenticados 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 nivel funcional Windows Server 2008, y aquí es donde la autenticación no funciona. El error de aplicación regresó al intentar validar el billete 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 está tratando el billete 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.

+0

¿Revisaste la comunicación en Wireshark y te examiné los boletos? –

+0

Gracias @ Michael-O: utilicé Wireshark cuando trabajaba en esto antes, pero ya no tengo la salida disponible. No pude hacer que esto funcionara en absoluto. Revertí el dominio de prueba al nivel funcional de Windows Server 2003 y luego funcionó normalmente. Ahora necesito configurar un nuevo dominio de prueba 2008 para tratar de encontrar una solución viable ... – slt

Respuesta

0

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 manera 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.

16

Parece que el problema está en la tabla de claves. Hay algunas secuencias de acción que conducen a algunos estados específicos del 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 Java corta que permite comprobar si Java puede autenticar usando el archivo de tabla de claves:

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 propiedades a utilizar:

#ticketCache=/tmp/krb5cc_1000 
keyTab=/etc/krb5.keytab 
principal=foo 

doNotPrompt=true 
refreshKrb5Config=true 
useTicketCache=true 
renewTGT=true 
useKeyTab=true 
storeKey=true 
isInitiator=true 

(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 conduce al estado de tabla de claves (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 conduce al estado de tabla de claves (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 "kinit foo".


y la siguiente secuencia de acción conduce a 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 que tanto k5start/kinit y la verificación java dan resultado positivo.


Medio 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 precisa (12.04.1 LTS) con Kerberos del MIT 5-1.10.3 compilado de la distribución fuente

+4

¡Buena respuesta completa! – Hbcdev

+0

@Ivan - Puedo recuperar el ticket de keytab. Sin embargo, con el código de Java 7, esto falló repentinamente con Checksum failed error. ¿Volver a crear la pestaña es la única opción? –

Cuestiones relacionadas