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