GSSException: Failure unspecified at GSS-API level (Mechanism level: Could not use DES Cipher - Output buffer must be (at least) 16 bytes long)
    hunterae 
    hunterae at gmail.com
       
    Fri Aug 12 17:13:20 EDT 2005
    
    
  
I am having a problem that may seem to indicate an issue between java
1.4 and 1.5 communication. I am using the GSS-API as a means of
communication between a client and server application. This problem
only occurs when the server is being run using java 1.5
and I connect from a client using java 1.4 or vice versa. I'm not sure
how to go about configuring to use the DES encryption type as you had
mentioned. I created a special SecurityContext class which both the
client and server use and simplifies the login/encryption/decryption
process for the client and server. Essentially, on startup, the server
logins into the SecurityContext object (using its login function) as a
Kerberos principal server. On startup, the client logins into the
SecurityContext object as a Kerberos principal user. The client then
using SecurityContext's initSecContext to initialize a GSSContext to
the Kerberos principal server, which the server later accepts using the
acceptSecContext method. Finally, wrap and unwrap are used to
encrypt/decrypt messages passed back and forth.
Here is the stack trace of the exception I am getting:
GSSException: Failure unspecified at GSS-API level (Mechanism level:
Could not use DES Cipher - Output buffer must be (at least) 16 bytes
long)
    at
sun.security.jgss.krb5.Message Token.getDesCbcChecksum(Messag
eToken.java:530)
    at
sun.security.jgss.krb5.Message Token.getChecksum(MessageToken
.java:453)
    at
sun.security.jgss.krb5.Message Token.verifySignAndSeqNumber(M
essageToken.java:325)
    at
sun.security.jgss.krb5.WrapTok en.getDataFromBuffer(WrapToken
.java:269)
    at sun.security.jgss.krb5.WrapTok en.getData(WrapToken.java:198)
    at sun.security.jgss.krb5.WrapTok en.getData(WrapToken.java:171)
    at sun.security.jgss.krb5.Krb5Con text.unwrap(Krb5Context.java:8
76)
    at sun.security.jgss.GSSContextIm pl.unwrap(GSSContextImpl.java:
362)
    at
edu.bu.rcs.objects.security.Se curityContext.unwrap(SecurityC
ontext.java:186)
    at edu.bu.rcs.legend.client.Clien t.issueCommand(Client.java:184 )
    at edu.bu.rcs.legend.client.Clien t.issueCommand(Client.java:153 )
    at edu.bu.rcs.legend.client.Clien t$1.run(Client.java:289)
    at java.security.AccessController .doPrivileged(Native Method)
    at javax.security.auth.Subject.do AsPrivileged(Subject.java:437)
    at
edu.bu.rcs.objects.security.Se curityContext.doAsPrivileged(S
ecurityContext.java:334)
    at edu.bu.rcs.legend.client.Clien t.login(Client.java:281)
    at
edu.bu.rcs.legend.gui.main.Mai nWindowPanel$3.run(MainWindowP
anel.java:381)
Both client and server have the following code in the main functions:
        URL config = Client.class.getResource("jaas .conf");
        System.setProperty("java.secur ity.auth.login.config", config
                .toExternalForm());
        String krb5Realm = "bu.edu";
        System.setProperty("java.secur ity.krb5.realm", krb5Realm);
        String krb5Kdc = "kerberos1.bu.edu";
        System.setProperty("java.secur ity.krb5.kdc", krb5Kdc);
I did this so I wouldn't have to pass this information as vm arguments.
The client jaas file looks like:
Client {
   com.sun.security.auth.module.K rb5LoginModule required;
};
The server jaas file looks like:
Server {
   com.sun.security.auth.module.K rb5LoginModule required
storeKey=true;
};
public class SecurityContext implements CallbackHandler {
    protected GSSContext context = null;
    protected LoginContext lc = null;
    protected String service = null;
    protected String username;
    protected char[] password;
    public SecurityContext() {
    }
    public void setServiceName(String service) {
        this.service = service;
    }
    public boolean login(String loginContext, String username, char[]
password) {
        this.username = username;
        this.password = password;
        lc = null;
        try {
            lc = new LoginContext(loginContext, this);
        } catch (LoginException le) {
            System.err
                    .println("Cannot create LoginContext. " +
le.getMessage());
            System.exit(-1);
        } catch (SecurityException se) {
            System.err
                    .println("Cannot create LoginContext. " +
se.getMessage());
            System.exit(-1);
        }
        int i;
        for (i = 0; i < 3; i++) {
            try {
                lc.login();
                return true;
            } catch (AccountExpiredException aee) {
                System.err.println("Your account has expired.  "
                        + "Please notify your administrator.");
                System.exit(-1);
            } catch (CredentialExpiredException cee) {
                System.err.println("Your credentials have expired.");
                System.exit(-1);
            } catch (FailedLoginException fle) {
                System.err.println("Authentica tion Failed");
                try {
                    Thread.sleep(3000);
                } catch (Exception e) {
                    // ignore
                }
            } catch (Exception e) {
                //System.err.println("Unexpect ed Exception - unable to
continue");
                //e.printStackTrace();
                return false;
            }
        }
        // did they fail three times?
        if (i == 3) {
            System.err.println("Sorry");
        }
        return false;
    }
    public byte[] acceptSecContext(byte[] token) {
        try {
            GSSManager manager = GSSManager.getInstance();
            context = manager.createContext((GSSCred ential) null);
            while (!context.isEstablished()) {
                /*
                 * Create a GSSContext to receive the incoming request
from the
                 * client. Use null for the server credentials passed
in. This
                 * tells the underlying mechanism to use whatever
credentials it
                 * has available that can be used to accept this
connection.
                 */
                token = context.acceptSecContext(token , 0,
token.length);
                if (token != null)
                    return token;
            }
        } catch (GSSException e) {
            e.printStackTrace();
        }
        return token;
    }
    public synchronized byte[] wrap(Object object) {
        /*
         * The first MessageProp argument is 0 to request the default
         * Quality-of-Protection. The second argument is true to
request privacy
         * (encryption of the message).
         */
        MessageProp prop = new MessageProp(0, true);
        /*
         * Encrypt the data and send it across. Integrity protection is
always
         * applied, irrespective of confidentiality (i.e., encryption).
You can
         * use the same token (byte array) as that used when
establishing the
         * context.
         */
        byte[] token = null;
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos;
        try {
            oos = new ObjectOutputStream(baos);
            oos.writeObject(object);
            byte[] messageBytes = baos.toByteArray();
            token = context.wrap(messageBytes, 0, messageBytes.length,
prop);
            oos.close();
            baos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (GSSException e) {
            e.printStackTrace();
        }
        return token;
    }
    public synchronized Object unwrap(byte[] token) {
        MessageProp prop = new MessageProp(0, true);
        Object object = null;
        try {
            token = context.unwrap(token, 0, token.length, prop);
            ObjectInputStream ois = new ObjectInputStream(
                    new ByteArrayInputStream(token));
            object = ois.readObject();
            ois.close();
        } catch (GSSException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (Exception e) {
                e.printStackTrace();
        }
        return object;
    }
    public byte[] initSecContext(byte[] token) {
        try {
            if (context == null) {
                /*
                 * This Oid is used to represent the Kerberos version 5
GSS-API
                 * mechanism. It is defined in RFC 1964. We will use
this Oid
                 * whenever we need to indicate to the GSS-API that it
must use
                 * Kerberos for some purpose.
                 */
                Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
                GSSManager manager = GSSManager.getInstance();
                /*
                 * Create a GSSName out of the server's name. The null
indicates
                 * that this application does not wish to make any
claims about
                 * the syntax of this name and that the underlying
mechanism
                 * should try to parse it as per whatever default
syntax it
                 * chooses.
                 */
                GSSName serverName = manager.createName(service, null);
                /*
                 * Create a GSSContext for mutual authentication with
the
                 * server. - serverName is the GSSName that represents
the
                 * server. - krb5Oid is the Oid that represents the
mechanism to
                 * use. The client chooses the mechanism to use. - null
is
                 * passed in for client credentials - DEFAULT_LIFETIME
lets the
                 * mechanism decide how long the context can remain
valid. Note:
                 * Passing in null for the credentials asks GSS-API to
use the
                 * default credentials. This means that the mechanism
will look
                 * among the credentials stored in the current Subject
to find
                 * the right kind of credentials that it needs.
                 */
                context = manager.createContext(serverNa me, krb5Oid,
null,
                        GSSContext.DEFAULT_LIFETIME);
                // Set the desired optional features on the context.
The client
                // chooses these options.
                context.requestMutualAuth(true ); // Mutual
authentication
                //context.requestConf(true); // Will use
confidentiality later
                //context.requestInteg(true); // Will use integrity
later
            }
            // token is ignored on the first call
            if (token == null)
                token = new byte[0];
            token = context.initSecContext(token, 0, token.length);
        } catch (GSSException e) {
            e.printStackTrace();
        }
        return token;
    }
    public void dispose() {
        if (context != null)
            try {
                context.dispose();
            } catch (GSSException e) {
                e.printStackTrace();
            }
    }
    public void logout() {
        if (lc != null) {
            try {
                lc.logout();
            } catch (LoginException e) {
                e.printStackTrace();
            }
            lc = null;
        }
    }
    public void handle(Callback[] callbacks) throws IOException,
            UnsupportedCallbackException {
        for (int i = 0; i < callbacks.length; i++) {
            if (callbacks[i] instanceof TextOutputCallback) {
                // display the message according to the specified type
                TextOutputCallback toc = (TextOutputCallback)
callbacks[i];
                switch (toc.getMessageType()) {
                case TextOutputCallback.INFORMATION :
                    break;
                case TextOutputCallback.ERROR:
                    break;
                case TextOutputCallback.WARNING:
                    break;
                default:
                    throw new IOException("Unsupported message type: "
                            + toc.getMessageType());
                }
            } else if (callbacks[i] instanceof NameCallback) {
                // prompt the user for a username
                NameCallback nc = (NameCallback) callbacks[i];
                nc.setName(username);
            } else if (callbacks[i] instanceof PasswordCallback) {
                // prompt the user for sensitive information
                PasswordCallback pc = (PasswordCallback) callbacks[i];
                pc.setPassword(password);
            } else {
                throw new UnsupportedCallbackException(c allbacks[i],
                        "Unrecognized Callback");
            }
        }
    }
    public Subject getSubject() {
        if (lc != null)
            return lc.getSubject();
        return null;
    }
    public Object doAsPrivileged(PrivilegedActio n action) {
        if (lc != null)
            return Subject.doAsPrivileged(lc.getS ubject(), action,
null);
        return null;
    }
    public boolean isEstablished() {
        if (context == null)
            return false;
        return context.isEstablished();
    }
    public String getUsername() {
        try {
            GSSName name = context.getSrcName();
            if (name != null)
                return name.toString();
        } catch (GSSException e) {
        } 
        return null; 
    } 
}
    
    
More information about the Kerberos
mailing list