title: How to verify kerberos token?
body: <p>so it's me again with some AD and Kerberos problems.</p>
  
  <p>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. </p>
  
  <p>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.</p>
  
  <p>I am running an Java EE web application. </p>
  
  <p>What can I do with this token to get the user and especially an "this token and user are legit" from the AD?</p>
  
  <p>EDIT:</p>
  
  <p>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.</p>
  
  <p>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:</p>
  
  <p>Created the SPN HTTP/ping01.cool.domain@COOL.DOMAIN at user mgmt. </p>
  
  <p>krb5.conf:</p>
  
  <pre><code>[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 = COOL.DOMAIN
  kdc_timesync = 1
  ccache_type = 5
  forwardable = true
  proxiable = true
  
  [realms]
  COOL.DOMAIN = {
  kdc = kdc.cool.domain
  admin_server = COOL.DOMAIN
  default_domain = COOL.DOMAIN
  }
  
  [domain_realm]
  cool.domain = COOL.DOMAIN
  ping01 = COOL.DOMAIN
  
  
  
  [login]
  krb4_convert = true
  krb4_get_tickets = false
  </code></pre>
  
  <p>Also I have this code:</p>
  
  <pre><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&lt;String, String&gt; options = new HashMap&lt;&gt;();
          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&lt;Principal&gt; principals = new HashSet&lt;&gt;();
        principals.add( principal );
  
        /** define the subject to execute our secure action as */
        Subject subject = new Subject( false, principals, new HashSet&lt;&gt;(), new HashSet&lt;&gt;() );
  
        /** login the subject */
        /**
         * TODO: Find the correct way to use the commented out version!
         */
        // ctx = new LoginContext( "http_ping01_domain", subject );
        ctx = new LoginContext( "doesn't matter", subject, null,
            getJaasKrb5TicketCfg( "HTTP/ping01.cool.domain@COOL.DOMAIN", "COOL.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;
    }
  </code></pre>
  
  <p>In addition to this I have the privilegedAction in a seperate class:</p>
  
  <pre><code>public class SingleSignOnImpl implements PrivilegedExceptionAction&lt;String&gt;
  {
  
    /** 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;
    }
  
  }
  </code></pre>
  
  <p>My log spits out this:</p>
  
  <pre><code>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.cool.domain@COOL.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
  </code></pre>
  
  <p>But the good news is that I get a Kerberos token. he enters the Context and can decrypt it. When using <strong>klist</strong> in cmd I even see a cached ticket for my service. as shown here: <a href="http://fs5.directupload.net/images/161108/2t78o599.png" rel="noreferrer">directUpload</a></p>
  
  <p>Active Directory and KDC runs smoothly and no error or warning is shown when I request the ticket to access the service. </p>
  
  <p>The byte[] with the token is taken from the httprequest from the header and decoded from string to byte[] with Base64.getMimeDecoder.decode();</p>
  
  <p>does anybody see my mistake? After rebooting and purging the ticket cache I still get the message.</p>
  
  <p>EDIT 2:</p>
  
  <p>I digged around more and tried around with ktab and kinit. When trying to get a ticket for the SPN with kinit I run into this:</p>
  
  <pre><code>kinit -J-Dsun.security.krb5.debug=true -k -t http_ping01.ktab HTTP/ping01.cool.domain@COOL.DOMAIN
  
  &gt;&gt;&gt;KinitOptions cache name is C:\Users\Nico.DOMAIN\XXXX_Nico
  Principal is HTTP/ping01.cool.domain@COOL.DOMAIN
  
  &gt;&gt;&gt; Kinit using keytab
  &gt;&gt;&gt; Kinit keytab file name: http_ping01.ktab
  Java config name: null
  
  LSA: Found Ticket
  LSA: Made NewWeakGlobalRef
  LSA: Found PrincipalName
  LSA: Made NewWeakGlobalRef
  LSA: Found DerValue
  LSA: Made NewWeakGlobalRef
  LSA: Found EncryptionKey
  LSA: Made NewWeakGlobalRef
  LSA: Found TicketFlags
  LSA: Made NewWeakGlobalRef
  LSA: Found KerberosTime
  LSA: Made NewWeakGlobalRef
  LSA: Found String
  LSA: Made NewWeakGlobalRef
  LSA: Found DerValue constructor
  LSA: Found Ticket constructor
  LSA: Found PrincipalName constructor
  LSA: Found EncryptionKey constructor
  LSA: Found TicketFlags constructor
  LSA: Found KerberosTime constructor
  LSA: Finished OnLoad processing
  
  Native config name: C:\Windows\krb5.ini
  Loaded from native config
  &gt;&gt;&gt; Kinit realm name is COOL.DOMAIN
  &gt;&gt;&gt; Creating KrbAsReq
  &gt;&gt;&gt; KrbKdcReq local addresses for my_computer are:
  
  [My addresses]
  
  &gt;&gt;&gt; KdcAccessibility: reset
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01.cool.domain
  &gt;&gt;&gt; KeyTab: load() entry length: 98; type: 18
  
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01.cool.domain
  &gt;&gt;&gt; KeyTab: load() entry length: 82; type: 17
  
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01.cool.domain
  &gt;&gt;&gt; KeyTab: load() entry length: 82; type: 23
  
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01.cool.domain
  &gt;&gt;&gt; KeyTab: load() entry length: 90; type: 16
  
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01
  &gt;&gt;&gt; KeyTab: load() entry length: 80; type: 18
  
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01
  &gt;&gt;&gt; KeyTab: load() entry length: 64; type: 17
  
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01
  &gt;&gt;&gt; KeyTab: load() entry length: 64; type: 23
  
  &gt;&gt;&gt; KeyTabInputStream, readName(): COOL.DOMAIN
  &gt;&gt;&gt; KeyTabInputStream, readName(): HTTP
  &gt;&gt;&gt; KeyTabInputStream, readName(): ping01
  &gt;&gt;&gt; KeyTab: load() entry length: 72; type: 16
  
  Looking for keys for: HTTP/ping01.cool.domain@COOL.DOMAIN
  Added key: 16version: 2
  Added key: 23version: 2
  Added key: 17version: 2
  Added key: 18version: 2
  default etypes for default_tkt_enctypes: 18 17 23 16.
  
  &gt;&gt;&gt; KrbAsReq creating message
  &gt;&gt;&gt; KrbKdcReq send: kdc=KDC.cool.domain UDP:88, timeout=30000, number of retries =3, #bytes=270
  &gt;&gt;&gt; KDCCommunication: kdc=KDC.cool.domain UDP:88, timeout=30000,Attempt =1, #bytes=270
  &gt;&gt;&gt; KrbKdcReq send: #bytes read=106
  &gt;&gt;&gt; KdcAccessibility: remove KDC.cool.domain
  &gt;&gt;&gt; KDCRep: init() encoding tag is 126 req type is 11
  
  &gt;&gt;&gt;KRBError:
           sTime is Wed Nov 09 10:54:46 CET 2016 1478685286000
           suSec is 578393
           error code is 6
           error Message is Client not found in Kerberos database
           sname is ActiveDirectory/COOL.DOMAIN@COOL.DOMAIN
           msgType is 30
  Exception: krb_error 6 Client not found in Kerberos database (6) Client not found in Kerberos database
  
  KrbException: Client not found in Kerberos database (6)
          at sun.security.krb5.KrbAsRep.&lt;init&gt;(KrbAsRep.java:76)
          at sun.security.krb5.KrbAsReqBuilder.send(KrbAsReqBuilder.java:316)
          at sun.security.krb5.KrbAsReqBuilder.action(KrbAsReqBuilder.java:361)
          at sun.security.krb5.internal.tools.Kinit.&lt;init&gt;(Kinit.java:219)
          at sun.security.krb5.internal.tools.Kinit.main(Kinit.java:113)
  Caused by: KrbException: Identifier doesn't match expected value (906)
          at sun.security.krb5.internal.KDCRep.init(KDCRep.java:140)
          at sun.security.krb5.internal.ASRep.init(ASRep.java:64)
          at sun.security.krb5.internal.ASRep.&lt;init&gt;(ASRep.java:59)
          at sun.security.krb5.KrbAsRep.&lt;init&gt;(KrbAsRep.java:60)
          ... 4 more
  </code></pre>
  
  <p>so I looked at the object ping01 in our active directory. It already got a bunch of servicePrincipalName attributes:</p>
  
  <blockquote>
    <p>servicePrincipalName: someService/PING01.cool.domain  </p>
    
    <p>servicePrincipalName: someService/PING01</p>
    
    <p>servicePrincipalName: anotherService/PING01.cool.domain</p>
    
    <p>servicePrincipalName: anotherService/PING01  </p>
    
    <p>servicePrincipalName: HOST/PING01.cool.domain  </p>
    
    <p>servicePrincipalName: HOST/PING01  </p>
  </blockquote>
  
  <p>using <code>setspn -l mgmt</code> outputs the SPN I created though. Just not visible in the ldapBrowser at all.</p>
  
  <p>I am not sure if the object Ping01 (objectClass=computer) has a password or not, have to wait for an answer of the sys admin.</p>
  
  <p>EDIT 3:
  I figured out it must be some kind of SPN problem or at least an AD problem.
  From <strong>EDIT 2:</strong> you can see that even the windows native tool <strong>kinit</strong> can't perform the authentication since the kdc is sending the message he doesn't know the user. Why he states to the SPN as user is unclear to me but turning on more debug options gave me this output:</p>
  
  <pre><code>YIIG2gYGKwYBBQUCoIIGzjCCBsqgMDAuBgkqhkiC9xIBAgIGCSqGSIb3EgECAgYKKwYBBAGCNwICHgYKKwYBBAGCNwICCqKCBpQEggaQYIIGjAYJKoZIhvcSAQICAQBuggZ7MIIGd6ADAgEFoQMCAQ6iBwMFACAAAACjggUGYYIFAjCCBP6gAwIBBaETGxFGRUxURU5HUk9VUC5MT0NBTKIrMCmgAwIBAqEiMCAbBEhUVFAbGHBpbmcwMS5mZWx0ZW5ncm91cC5sb2NhbKOCBLMwggSvoAMCAR[...]
  INFO [stdout] Debug is true storeKey true useTicketCache false useKeyTab true doNotPrompt true ticketCache is null isInitiator false KeyTab is C:\path\to\http_ping01_test.ktab refreshKrb5Config is false principal is HTTP/ping01.cool.domain@COOL.DOMAIN tryFirstPass is false useFirstPass is false storePass is false clearPass is false
  INFO [stdout] principal is HTTP/ping01.cool.domain@COOL.DOMAIN
  INFO [stdout] Will use keytab
  INFO [stdout] Commit Succeeded
  INFO [stdout] Found KeyTab C:\path\to\http_ping01_test.ktab for HTTP/ping01.cool.domain@COOL.DOMAIN
  INFO Entering accpetSecContext...
  INFO [stdout] Entered SpNegoContext.acceptSecContext with state=STATE_NEW
  INFO [stdout] SpNegoContext.acceptSecContext: receiving token = a0 82 06 ce 30 82 06 ca a0 30 30 2e 06 09 2a 86 48 82 f7 12 01 02 02 06 09 2a 86 48 86 f7 12 01 02 02 06 0a 2b 06 01 04 01 82 37 02 02 1e 06 0a 2b 06 01 04 01 82 37 02 02 0a a2 82 06 94 04 82 06 90 60 82 06 8c 06 09 2a 86 48 86 f7 12 01 02 02 01 00 6e 82 06 7b 30 82 06 77 a0 03 02 01 05 a1 03 02 01 0e a2 07 03 05 00 20 00 00 00 a3 82 05 06 61 82 05 02 30 82 04 fe a0 03 02 01 05 a1 13 1b 11 46 45 4c 54 45 4e 47 52 4f 55 50 2e 4c 4f 43 41 4c a2 2b 30 29 a0 03 02 01 02 a1 22 30 20 1b 04 48 54 54 50 1b 18 70 69 6e 67 30 31 2e 66 65 6c 74 65 6e 67 72 6f 75 70 2e 6c 6f 63 61 6c a3 82 04 b3 30 82 04 af a0 03 02 01 12 a1 03 02 01 0f a2 82 04 a1 04 82 04 9d e6 c0 24 8d 0d 24 8e e1 4e e8 0d 4e 4d 5b 7e 06 58 d9 f2 04 a6 99 55 e2 61 67 99 60 ec 47 42 7d 60 64 4d bc f7 ef 99 5b f0 3e b8 2f 9a ff 2d 83 19 6d f1 5f ac 44 08 f3 50 d5 c9 53 af 6f d9 d6 81 c1 d7 24 03 6a 9d b4 9d 56 53 93 b3 1d 07 15 77 c5 fb 25 0f bc f8 97 8f 97 0c 26 ae 52 d0 fc f3 72 98 9c 79 4b af e2 88 3b a6 2b 1b 03 b0 93 b6 6a dd b3 c6 f8 c2 01 eb a4 1b 8a 64 74 cb 5b f4 4b 5c d7 02 48 1d 0d 5e 29 3d 2b 82 c5 79 a1 7a e1 4c 92 32 7c 6b f6 56 ff e1 3a 3f b7 ce 0c 92 f8 ae ce 03 f2 f5 18 53 5c 5b 08 07 60 d7 c0 38 7d d0 f5 fa 2b 63 97 61 75 86 b6 95 44 49 76 93 38 88 82 7f 90 07 d7 3d c9 bd c6 c7 b3 af 47 55 cc b0 1a cd 2a e8 4e d0 b9 42 9e 65 3e aa 88 ac b5 25 45 39 20 0f 3c 50 ed 2d 1a f5 24 04 5a 15 99 c9 2e c1 c6 40 4e 26 ea f2 c6 a9 bd 61 24 fc d4 25 6e ed c2 40 3a d6 18 9b 53 ac 4d a1 61 d2 12 aa 99 e1 90 6e 22 c9 14 82 49 78 43 ab 83 a1 60 a3 d0 1d 33 24 11 41 07 4d bb 9c 0e 38 e1 3c 86 6a 62 bc 2f 7c 47 34 b7 42 3e 28 2e 9b 26 66 a1 e8 61 5f 00 61 8a b9 2b 5b 9e b2 aa 1a 4d e7 4e d2 6d 52 e1 25 c4 89 ea 6e 85 1c 1a 56 e0 d9 a2 be 9f 7c ee 89 55 b8 39 cf b9 92 77 33 2d fa 64 29 50 38 2d 6d d7 9d be be 3c e2 04 4c 5c 3e 3b d1 09 39 08 bd 75 5b 9f 6a 89 32 f8 b2 a9 c7 a3 a1 de ca ea fd 62 18 7d df 5e 50 b5 8e 48 71 ec 66 70 ff 0e 1c 40 2a ad 9e f4 c4 15 45 ca 1b 15 b8 0e 30 76 76 9b 81 39 5b 94 c4 0a ec e0 a7 b4 ec 32 9a 4a 9d 74 86 a3 81 5a 91 8c 51 e1 5a f1 b8 44 fa 9d cc 16 34 c5 99 fb 7b 33 bc 06 99 51 9e ec 19 60 88  [...]
  INFO [stdout] SpNegoToken NegTokenInit: reading Mechanism Oid = 1.2.840.48018.1.2.2
  INFO [stdout] SpNegoToken NegTokenInit: reading Mechanism Oid = 1.2.840.113554.1.2.2
  INFO [stdout] SpNegoToken NegTokenInit: reading Mechanism Oid = 1.3.6.1.4.1.311.2.2.30
  INFO [stdout] SpNegoToken NegTokenInit: reading Mechanism Oid = 1.3.6.1.4.1.311.2.2.10
  INFO [stdout] SpNegoToken NegTokenInit: reading Mech Token
  INFO [stdout] SpNegoContext.acceptSecContext: received token of type = SPNEGO NegTokenInit
  INFO [stdout] SpNegoContext: negotiated mechanism = 1.2.840.113554.1.2.2
  INFO [stdout] SpNegoContext.acceptSecContext: negotiated mech adjusted to 1.2.840.48018.1.2.2
  INFO [stdout] Entered Krb5Context.acceptSecContext with state=STATE_NEW
  INFO [stdout] Looking for keys for: HTTP/ping01.cool.domain@COOL.DOMAIN
  INFO [stdout] Added key: 16version: 2
  INFO [stdout] Added key: 23version: 2
  INFO [stdout] Added key: 17version: 2
  INFO [stdout] Added key: 18version: 2
  INFO [stdout] &gt;&gt;&gt; EType: sun.security.krb5.internal.crypto.Aes256CtsHmacSha1EType
  ERROR Invalid ticket for HTTP/ping01.cool.domain@COOL.DOMAIN: java.security.PrivilegedActionException: GSSException: Failure unspecified at GSS-API level (Mechanism level: Checksum failed)
  INFO [stdout] [Krb5LoginModule]: Entering logout
  INFO [stdout] [Krb5LoginModule]: logged out Subject
  </code></pre>