2008-12-16 10 views
11

¿Alguien sabe cómo obtener un boleto de servicio del Key Distribution Center (KDC) usando la API GSS de Java?¿Cómo obtener un ticket de servicio kerberos a través de GSS-API?

Tengo una aplicación cliente-gruesa que primero se autentica vía JAAS utilizando el Krb5LoginModule para obtener el TGT del caché del ticket (fondo: Windows utiliza una implementación kerberos y almacena el ticket en un área segura de memoria). Desde LoginManager obtengo el objeto Subject que contiene el TGT. Ahora esperaba que cuando creara un objeto GSSCredential específico para mi servicio, el boleto de servicio también se incluiría en las credenciales privadas del Sujeto (lo he leído en algún lugar de la web). Así que he intentado lo siguiente:

// Exception handling ommitted 
LoginContext lc = new LoginContext("HelloEjbClient", new DialogCallbackHandler()); 
lc.login() 
Subject.doAs(lc.getSubject(), new PrivilegedAction() { 

    public Object run() { 
     GSSManager manager = GSSManager.getInstance(); 
     GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME); 
     GSSCredential clientCreds = manager.createCredential(clientName, 8 * 3600, createKerberosOid(), GSSCredential.INITIATE_ONLY); 

     GSSName serverName = manager.createName("[email protected]", GSSName.NT_HOSTBASED_SERVICE); 
     manager.createCredential(serverName, GSSCredential.INDEFINITE_LIFETIME, createKerberosOid(), GSSCredential.INITIATE_ONLY); 
     return null; 
    } 

    private Oid createKerberosOid() { 
     return new Oid("1.2.840.113554.1.2.2"); 
    } 

}); 

Desafortunadamente me siento un GSSException: No hay credenciales válidas siempre (nivel Mecanismo: No se pudo encontrar ninguna TGT Kerberos).

Respuesta

14

Mi comprensión de obtener el boleto de servicio era incorrecta. No necesito obtener las credenciales del servicio; esto no es posible en el cliente, porque el cliente realmente no tiene un TGT para el servidor y, por lo tanto, no tiene los derechos para obtener las credenciales del servicio. Lo que falta aquí es crear un nuevo GSSContext e inicializarlo. El valor de retorno de este método contiene el ticket de servicio, si lo he entendido correctamente. Aquí hay un ejemplo de código de trabajo. Se debe ejecutar en un PrivilegedAction en nombre de un objeto conectado:

GSSManager manager = GSSManager.getInstance(); 
    GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME); 
    GSSCredential clientCred = manager.createCredential(clientName, 
                 8 * 3600, 
                 createKerberosOid(), 
                 GSSCredential.INITIATE_ONLY); 

    GSSName serverName = manager.createName("[email protected]", GSSName.NT_HOSTBASED_SERVICE); 

    GSSContext context = manager.createContext(serverName, 
               createKerberosOid(), 
               clientCred, 
               GSSContext.DEFAULT_LIFETIME); 
    context.requestMutualAuth(true); 
    context.requestConf(false); 
    context.requestInteg(true); 

    byte[] outToken = context.initSecContext(new byte[0], 0, 0); 
    System.out.println(new BASE64Encoder().encode(outToken)); 
    context.dispose(); 

El outToken contiene continuación contiene el vale de servicio. Sin embargo, esta no es la forma en que se pretendía utilizar el GSS-API. Su objetivo era ocultar esos detalles al código, por lo que es mejor establecer un GSSContext utilizando el GSS-API en ambos lados. De lo contrario, realmente debería saber lo que está haciendo debido a posibles agujeros de seguridad. Para obtener más información, lea el Sun SSO tutorial with kerberos con más cuidado que yo.

EDITAR: Olvidé que estoy usando Windows XP con SP2. Hay una nueva "característica" en esta versión de Windows que no permite usar el TGT en la RAM de Windows. Tienes que editar el registro para permitir esto. Para obtener más información, eche un vistazo al tema JGSS Troubleshooting page en caso de que experimente una "KrbException: KDC no tiene soporte para el tipo de cifrado (14)" como yo lo hice.

+0

@Michael: Gracias por su contribución. Como puedes ver, escribí esto hace bastante tiempo, así que no recuerdo los detalles exactos, pero estoy bastante seguro de que esto solo fue un ejemplo. ¿Qué crees que se puede hacer para mejorar esta respuesta? –

+0

revise su enlace al tutorial de Sun SSO con kerberos y eche un vistazo a la figura 6. Aquí está el ciclo sobre el que escribí. –

+0

Creo que no incluí el ciclo porque no pude entender qué se supone que deben hacer los métodos readToken() y sendToken (...). –

7

Tuve muchos problemas para usar este código, pero tengo al menos una solución. Lo publico aquí, quizás ayudará a algunos de ustedes ...

/** 
* Tool to retrieve a kerberos ticket. This one will not be stored in the windows ticket cache. 
*/ 
public final class KerberosTicketRetriever 
{ 
    private final static Oid KERB_V5_OID; 
    private final static Oid KRB5_PRINCIPAL_NAME_OID; 

    static { 
     try 
     { 
      KERB_V5_OID = new Oid("1.2.840.113554.1.2.2"); 
      KRB5_PRINCIPAL_NAME_OID = new Oid("1.2.840.113554.1.2.2.1"); 

     } catch (final GSSException ex) 
     { 
      throw new Error(ex); 
     } 
    } 

    /** 
    * Not to be instanciated 
    */ 
    private KerberosTicketRetriever() {}; 

    /** 
    * 
    */ 
    private static class TicketCreatorAction implements PrivilegedAction 
    { 
     final String userPrincipal; 
     final String applicationPrincipal; 

     private StringBuffer outputBuffer; 

     /** 
     * 
     * @param userPrincipal p.ex. <tt>[email protected]</tt> 
     * @param applicationPrincipal p.ex. <tt>HTTP/webserver.myfirm.com</tt> 
     */ 
     private TicketCreatorAction(final String userPrincipal, final String applicationPrincipal) 
     { 
      this.userPrincipal = userPrincipal; 
      this.applicationPrincipal = applicationPrincipal; 
     } 

     private void setOutputBuffer(final StringBuffer newOutputBuffer) 
     { 
      outputBuffer = newOutputBuffer; 
     } 

     /** 
     * Only calls {@link #createTicket()} 
     * @return <tt>null</tt> 
     */ 
     public Object run() 
     { 
      try 
      { 
       createTicket(); 
      } 
      catch (final GSSException ex) 
      { 
       throw new Error(ex); 
      } 

      return null; 
     } 

     /** 
     * 
     * @throws GSSException 
     */ 
     private void createTicket() throws GSSException 
     { 
      final GSSManager manager = GSSManager.getInstance(); 
      final GSSName clientName = manager.createName(userPrincipal, KRB5_PRINCIPAL_NAME_OID); 
      final GSSCredential clientCred = manager.createCredential(clientName, 
        8 * 3600, 
        KERB_V5_OID, 
        GSSCredential.INITIATE_ONLY); 

      final GSSName serverName = manager.createName(applicationPrincipal, KRB5_PRINCIPAL_NAME_OID); 

      final GSSContext context = manager.createContext(serverName, 
        KERB_V5_OID, 
        clientCred, 
        GSSContext.DEFAULT_LIFETIME); 
      context.requestMutualAuth(true); 
      context.requestConf(false); 
      context.requestInteg(true); 

      final byte[] outToken = context.initSecContext(new byte[0], 0, 0); 

      if (outputBuffer !=null) 
      { 
       outputBuffer.append(String.format("Src Name: %s\n", context.getSrcName())); 
       outputBuffer.append(String.format("Target : %s\n", context.getTargName())); 
       outputBuffer.append(new BASE64Encoder().encode(outToken)); 
       outputBuffer.append("\n"); 
      } 

      context.dispose(); 
     } 
    } 

    /** 
    * 
    * @param realm p.ex. <tt>MYFIRM.COM</tt> 
    * @param kdc p.ex. <tt>kerbserver.myfirm.com</tt> 
    * @param applicationPrincipal cf. {@link #TicketCreatorAction(String, String)} 
    * @throws GSSException 
    * @throws LoginException 
    */ 
    static public String retrieveTicket(
      final String realm, 
      final String kdc, 
      final String applicationPrincipal) 
    throws GSSException, LoginException 
    { 

     // create the jass-config-file 
     final File jaasConfFile; 
     try 
     { 
      jaasConfFile = File.createTempFile("jaas.conf", null); 
      final PrintStream bos = new PrintStream(new FileOutputStream(jaasConfFile)); 
      bos.print(String.format(
        "Krb5LoginContext { com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useTicketCache=true debug=true ; };" 
      )); 
      bos.close(); 
      jaasConfFile.deleteOnExit(); 
     } 
     catch (final IOException ex) 
     { 
      throw new IOError(ex); 
     } 

     // set the properties 
     System.setProperty("java.security.krb5.realm", realm); 
     System.setProperty("java.security.krb5.kdc", kdc); 
     System.setProperty("java.security.auth.login.config",jaasConfFile.getAbsolutePath()); 

     // get the Subject(), i.e. the current user under Windows 
     final Subject subject = new Subject(); 
     final LoginContext lc = new LoginContext("Krb5LoginContext", subject, new DialogCallbackHandler()); 
     lc.login(); 

     // extract our principal 
     final Set<Principal> principalSet = subject.getPrincipals(); 
     if (principalSet.size() != 1) 
      throw new AssertionError("No or several principals: " + principalSet); 
     final Principal userPrincipal = principalSet.iterator().next(); 

     // now try to execute the SampleAction as the authenticated Subject 
     // action.run() without doAsPrivileged leads to 
     // No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt) 
     final TicketCreatorAction action = new TicketCreatorAction(userPrincipal.getName(), applicationPrincipal); 
     final StringBuffer outputBuffer = new StringBuffer(); 
     action.setOutputBuffer(outputBuffer); 
     Subject.doAsPrivileged(lc.getSubject(), action, null); 

     return outputBuffer.toString(); 
    } 

    public static void main (final String args[]) throws Throwable 
    { 
     final String ticket = retrieveTicket("MYFIRM.COM", "kerbserver", "HTTP/webserver.myfirm.com"); 
     System.out.println(ticket); 
    } 
} 
Cuestiones relacionadas