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