Nico Nico - 29 days ago 10
Java Question

How to verify kerberos token?

so it's me again with some AD and Kerberos problems.

Alright cool, I get a kerberos token from the WWW-Authenticate header. Now I want to verify this token against an AD but I don't know how.

I found some stuff from GSSAPI but didn't see a function or method to take an byte[] as Kerberos token or any other way.

I am running an Java EE web application.

What can I do with this token to get the user and especially an "this token and user are legit" from the AD?

EDIT:

So as said in the comments I'm really close to being able to perform SSO. So I will update you guys with what I have.

I got a server named ping01 and my local machine. Running on a Tomcat as the user mgmt I have my application. So I did all this:

Created the SPN HTTP/ping01.domain@DOMAIN at user mgmt.

krb5.conf:

[libdefaults]
default_tkt_enctypes = aes256-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
default_tgs_enctypes = aes256-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
permitted_enctypes = aes256-cts rc4-hmac des3-cbc-sha1 des-cbc-md5 des-cbc-crc
default_realm = DOMAIN
kdc_timesync = 1
ccache_type = 5
forwardable = true
proxiable = true

[realms]
DOMAIN = {
kdc = server
admin_server = DOMAIN
default_domain = DOMAIN
}

[domain_realm]
domain = DOMAIN


[login]
krb4_convert = true
krb4_get_tickets = false


Also I have this code:

/**
* Gets the jaas krb 5 ticket cfg.
*
* @param principal the principal
* @param realm the realm
* @param keyTab the key tab
* @return the jaas krb 5 ticket cfg
*/
private static Configuration getJaasKrb5TicketCfg( final String principal, final String realm, final File keyTab )
{
return new Configuration()
{
@Override
public AppConfigurationEntry[] getAppConfigurationEntry( String name )
{
Map<String, String> options = new HashMap<>();
options.put( "principal", principal );
options.put( "realm", realm );
options.put( "doNotPrompt", "true" );
options.put( "useKeyTab", "true" );
options.put( "keyTab", keyTab.getAbsolutePath() );
options.put( "storeKey", "true" );
options.put( "isInitiator", "false" );

return new AppConfigurationEntry[] {
new AppConfigurationEntry( "com.sun.security.auth.module.Krb5LoginModule", LoginModuleControlFlag.REQUIRED, options ) };
}
};
}

/** {@inheritDoc} */
@Override
public boolean isTicketValid( String spn, byte[] ticket )
{
LoginContext ctx = null;
try
{
/** define the principal who will validate the ticket */
Principal principal = new KerberosPrincipal( spn, KerberosPrincipal.KRB_NT_SRV_INST );
Set<Principal> principals = new HashSet<>();
principals.add( principal );

/** define the subject to execute our secure action as */
Subject subject = new Subject( false, principals, new HashSet<>(), new HashSet<>() );

/** login the subject */
/**
* TODO: Find the correct way to use the commented out version!
*/
// ctx = new LoginContext( "http_ping01_feltengroup", subject );
ctx = new LoginContext( "doesn't matter", subject, null,
getJaasKrb5TicketCfg( "HTTP/ping01.domain@DOMAIN", "DOMAIN",
new File( "http_ping01_test.ktab" ) ) );
ctx.login();

/** create a validator for the ticket and execute it */
SingleSignOnImpl validateAction = new SingleSignOnImpl( ticket, spn, log );
String username = Subject.doAs( subject, validateAction );
log.info( "Validated service ticket for user " + username + " to access service " + spn );
return true;
}
catch ( PrivilegedActionException e )
{
/**
* Error reasons for this Exception: - Incorrect Kerberos Mechanism - Incorrect Token received
* - Incorrect keytab
*/
log.error( "Invalid ticket for " + spn + ": " + e );
}
catch ( LoginException e )
{
/**
* Error reasons for this Exception: - False krb5.conf (can't reach KDC) - incorrect SPN -
* False login.conf
*/
log.error( "Error creating validation LoginContext for " + spn + ": " + e );
}
finally
{
try
{
if ( ctx != null )
{
ctx.logout();
}
}
catch ( LoginException e )
{
log.error( "" + e );
}
}

return false;
}


In addition to this I have the privilegedAction in a seperate class:

public class SingleSignOnImpl implements PrivilegedExceptionAction<String>
{

/** The ticket from the client. */
private final byte[] ticket;

/** The Service principal name (SPN). */
private final String spn;

/** The log. */
private Log log;

/**
* Inits the.
*
* @param log the log
*/
@Inject
public void init( Log log )
{
this.log = log;
}

/**
* Instantiates a new single sign on impl.
*
* @param ticket the ticket
* @param spn the spn
*/
public SingleSignOnImpl( byte[] ticket, String spn, Log log )
{
this.ticket = ticket;
this.spn = spn;
this.log = log;
}

/** {@inheritDoc} */
@Override
public String run() throws Exception
{
/**
* Kerberos V5 Mechanism or SPNEGO required. the Legacy mechanism is NOT supported. SPNEGO
* (1.3.6.1.5.5.2); Kerberos V5 (1.2.840.113554.1.2.2)
*/
final Oid spnegoOid = new Oid( "1.3.6.1.5.5.2" );

GSSManager gssmgr = GSSManager.getInstance();

/** tell the GSSManager the Kerberos name of the service */
GSSName serviceName = gssmgr.createName( this.spn, GSSName.NT_USER_NAME );

/**
* get the service's credentials. note that this run() method was called by Subject.doAs(), so
* the service's credentials are already available in the Subject
*/
GSSCredential serviceCredentials = gssmgr.createCredential( serviceName, GSSCredential.INDEFINITE_LIFETIME, spnegoOid,
GSSCredential.ACCEPT_ONLY );

/** create a security context for decrypting the service ticket */
GSSContext gssContext = gssmgr.createContext( serviceCredentials );

/** decrypt the service ticket */
log.info( "Entering accpetSecContext..." );
gssContext.acceptSecContext( this.ticket, 0, this.ticket.length );

/**
* get the client name from the decrypted service ticket note that Active Directory created the
* service ticket, so we can trust it
*/
String clientName = gssContext.getSrcName().toString();
log.info( "request from Client {0}", clientName );

/** clean up the context. This is very important */
gssContext.dispose();

return clientName;
}

}


My log spits out this:

AUTHTOKEN: YIIG2gYGKw......
2016-11-08 14:52:34,269 INFO Entering accpetSecContext...
2016-11-08 14:52:34,269 ERROR Invalid ticket for HTTP/ping01.domain@DOMAIN: java.security.PrivilegedActionException: GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
2016-11-08 14:52:34,269 INFO VALID: false


But the good news is that I get a Kerberos token. he enters the Context and can decrypt it. When using klist in cmd I even see a cached ticket for my service. as shown here: directUpload

Active Directory and KDC runs smoothly and no error or warning is shown when I request the ticket to access the service.

The byte[] with the token is taken from the httprequest from the header and decoded from string to byte[] with Base64.getMimeDecoder.decode();

does anybody see my mistake? After rebooting and purging the ticket cache I still get the message.

Answer

Without more details on your architecture, I can tell you that the examination of the security token - containing the Kerberos ticket - takes place on your application server - it never contacts AD. GSSAPI security functions handle this - you don't code for that. When you (as an application server) get a Kerberos ticket from a user you know that user is legit - users don't get a ticket in the first place unless their identity has already been proven to AD - that's how Kerberos works. Check out this URL for more information: http://docs.oracle.com/javase/7/docs/technotes/guides/security/jgss/single-signon.html