title: Validate a kerberos service token with keytab
body: <p>A similar question has been asked before on this forum, but the accepted answer hasn't been helpful to me. My server has a Kerberos keytab file that I'd like to use to validate a service ticket sent by a client. </p>
  
  <p>Following Sun's documentation and sample code scattered elsewhere this is what I currently have:</p>
  
  <ol>
  <li>Client requests a service ticket from the KDC for MYSPN</li>
  <li>Client sends the file to the Service Server to request access to the service.</li>
  <li>Server grants access after validating service ticket with keytab for MYSPN it already has.</li>
  </ol>
  
  <p>I'm having trouble getting #3 to work. The specific error I get is: </p>
  
  <pre><code>GSSException: No valid credentials provided (Mechanism level: Attempt to obtain new ACCEPT credentials failed!)
      at sun.security.jgss.krb5.Krb5AcceptCredential.getInstance(Krb5AcceptCredential.java:100)
      at sun.security.jgss.krb5.Krb5MechFactory.getCredentialElement(Krb5MechFactory.java:128)
      at sun.security.jgss.krb5.Krb5MechFactory.getMechanismContext(Krb5MechFactory.java:200)
      at sun.security.jgss.GSSManagerImpl.getMechanismContext(GSSManagerImpl.java:231)
      at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:319)
      at sun.security.jgss.GSSContextImpl.acceptSecContext(GSSContextImpl.java:285)
      at cms.gssapi.Server$1.run(Server.java:188)
      at cms.gssapi.Server$1.run(Server.java:1)
      at java.security.AccessController.doPrivileged(Native Method)
      at javax.security.auth.Subject.doAs(Subject.java:357)
      at cms.gssapi.Server.acceptSecurityContext(Server.java:182)
      at cms.gssapi.Server.main(Server.java:59)
  Caused by: javax.security.auth.login.LoginException: Unable to obtain Princpal Name for authentication 
      at com.sun.security.auth.module.Krb5LoginModule.promptForName(Krb5LoginModule.java:750)
      at com.sun.security.auth.module.Krb5LoginModule.attemptAuthentication(Krb5LoginModule.java:646)
      at com.sun.security.auth.module.Krb5LoginModule.login(Krb5LoginModule.java:559)
      at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
      at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
      at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
      at java.lang.reflect.Method.invoke(Method.java:616)
      at javax.security.auth.login.LoginContext.invoke(LoginContext.java:784)
      at javax.security.auth.login.LoginContext.access$000(LoginContext.java:203)
      at javax.security.auth.login.LoginContext$5.run(LoginContext.java:721)
      at javax.security.auth.login.LoginContext$5.run(LoginContext.java:719)
      at java.security.AccessController.doPrivileged(Native Method)
      at javax.security.auth.login.LoginContext.invokeCreatorPriv(LoginContext.java:718)
      at javax.security.auth.login.LoginContext.login(LoginContext.java:590)
      at sun.security.jgss.GSSUtil.login(GSSUtil.java:264)
      at sun.security.jgss.krb5.Krb5Util.getKeys(Krb5Util.java:202)
      at sun.security.jgss.krb5.Krb5AcceptCredential$1.run(Krb5AcceptCredential.java:95)
      at sun.security.jgss.krb5.Krb5AcceptCredential$1.run(Krb5AcceptCredential.java:93)
      at java.security.AccessController.doPrivileged(Native Method)
      at sun.security.jgss.krb5.Krb5AcceptCredential.getInstance(Krb5AcceptCredential.java:92)
      ... 11 more
  </code></pre>
  
  <p>This is a snippet of the server code that tries to validate the client's service ticket using keytab:</p>
  
  <pre><code> public static void main( String[] args) {
        Properties props = new Properties();
        props.load( new FileInputStream( "server.properties"));
        System.setProperty( "sun.security.krb5.debug", "true");
        System.setProperty( "java.security.krb5.realm", props.getProperty( "realm"));
        System.setProperty( "java.security.krb5.kdc", props.getProperty( "kdc"));
        System.setProperty( "java.security.auth.login.config", "./jaas.conf");
        System.setProperty( "javax.security.auth.useSubjectCredsOnly", "false");
        krb5Oid = new Oid( "1.2.840.113554.1.2.2");
        Server server = new Server();
        server.login();
        byte serviceTicket[] = readClientTicketFromFile();
        String clientName = server.acceptSecurityContext(serviceTicket);
    }
  
    private static byte[] readClientTicketFromFile() throws IOException {
      BufferedReader in = new BufferedReader( new FileReader( "serviceticket.token"));
      String str;
      StringBuffer buffer = new StringBuffer();
      while ((str = in.readLine()) != null) {
         buffer.append( str + "\n");
      }
      in.close();
      BASE64Decoder decoder = new BASE64Decoder();
      return decoder.decodeBuffer( buffer.toString());
    }
  
    private static Oid krb5Oid;
  
    private Subject login() throws LoginException {
      LoginContext loginCtx = null;
      loginCtx = new LoginContext( "Server", new LoginCallbackHandler(null,null));   
      System.out.println("Attempting to do Login...");
      loginCtx.login();
      Subject ret_sub = loginCtx.getSubject();
       return ret_sub;
    }
  
  
    private String acceptSecurityContext( final byte[] serviceTicket) 
        throws GSSException {
      krb5Oid = new Oid( "1.2.840.113554.1.2.2");
      return Subject.doAs( subject, new PrivilegedAction&lt;String&gt;() {
        public String run() {
          try {
            GSSManager manager = GSSManager.getInstance();
            GSSContext context = manager.createContext( (GSSCredential) null);
            context.acceptSecContext( serviceTicket, 0, serviceTicket.length);
            return context.getSrcName().toString();
          }
          catch ( Exception e) {
            e.printStackTrace();
            return null;
          }
        }
      });
    }
  </code></pre>
  
  <p>Here is my server.properties</p>
  
  <pre><code>realm=MYREALM
  kdc=192.168.1.1
  </code></pre>
  
  <p>and jaas.conf file</p>
  
  <pre><code>Client {
    com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=false
    useKeyTab=false
    storeKey=false
    isInitiator=true
    debug=true
    ;
  };
  
  Server {
       com.sun.security.auth.module.Krb5LoginModule required 
       useKeyTab=true 
       storeKey=true 
       doNotPrompt=true
       isInitiator=false
       keyTab="/etc/krb5.keytab"
       principal="MYSPN/host.domain.com@MYREALM"
       debug=true
       ;
  };
  </code></pre>
  
  <p>I'm sure the service ticket is valid because the server can authenticate if I provide password for the service account, but I want to use a keytab file instead of password.</p>