Edit 2:
The question I should have asked is:
How do I stop my KDC from being hammered for repeated SGT requests because Java GSS is not using the credentials cache.
I leave my original answer in place at the bottom, because if largely focusses on the original question.
After another round of deep debugging and testing, I have found an acceptable solution to the root problem.
Using Java GSS API with JAAS, as opposed to "pure" GSS without JAAS in my original solution makes a big difference!
Yes, existing Service Tickets (SGTs) that may be in the credentials cache are not being loaded,
nor are any newly acquired SGTs written back to the cache, however the KDC is not be constantly hammered (the real problem).
Both pure GSS, and GSS with JAAS use a client principal subject. The subject has an in-memory privateCredentials set,
which is used to store TGTs and SGTs.
The key difference is:
"pure GSS": the subject + privateCredentials is created within the GSSContext, and lives only as long as the GSSContext lives.
GSS with JAAS: the subject is created by JAAS, outside the GSSContext, and thus can live for the life of the application,
spanning many GSSContexts during the life of the application.
The first GSSContext established will query the subject's privateCredentials for a SGT, not find one,
then request a SGT from the KDC.
The SGT is added to the subject's privateCredentials, and as the subject lives longer than the GSSContext,
it is available, as is the SGT, when following GSSContexts are created. These will find the SGT in the subject's privateCredentials, and do not need to hit the KDC for a new SGT.
So seen in the light of my particular Java Fat Client, opened once and likely to run for hours, everything is ok.
The first GSSContext created will hit the KDC for a SGT which will then be used by all following GSSContexts created until the client is closed.
The credentials cache is not being used, but that does not hurt.
In the light of a much shorter lived client, reopened many many times, and perhaps in parallel,
then use / non-use of the credentials cache might be a more serious issue.
private void initJAASandGSS() {
LoginContext loginContext = null;
TextCallbackHandler cbHandler = new TextCallbackHandler();
try {
loginContext = new LoginContext("wSOXClientGSSJAASLogin", cbHandler);
loginContext.login();
mySubject = loginContext.getSubject();
} catch (LoginException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
gssManager = GSSManager.getInstance();
try {
//TODO: LAMB: This name should be got from config / built from config / serviceIdentifier
serverName = gssManager.createName("HTTP/[email protected]", null);
Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
} catch (GSSException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
private String getGSSwJAASServiceToken() {
byte[] token = null;
String encodedToken = null;
token = Subject.doAs(mySubject, new PrivilegedAction<byte[]>(){
public byte[] run(){
try{
System.setProperty("javax.security.auth.useSubjectCredsOnly","true");
GSSContext context = gssManager.createContext(serverName,
krb5Oid,
null,
GSSContext.DEFAULT_LIFETIME);
context.requestMutualAuth(false);
context.requestConf(false);
context.requestInteg(true);
byte[] ret = new byte[0];
ret = context.initSecContext(ret, 0, ret.length);
context.dispose();
return ret;
} catch(Exception e){
Log.log(Log.ERROR, e);
throw new otms.util.OTMSRuntimeException("Start Client (Kerberos) failed, cause: " + e.getMessage());
}
}
});
encodedToken = Base64.getEncoder().encodeToString(token);
return encodedToken;
}
End Edit 2: Original answer below:
Question 1: Does the Java GSS-API not save service tickets to the credentials cache? Or can I change my code so it does so?
Edit: Root Cause Analysis.
After many hours debugging the sun.security.* classes, I now understand what GSS and Java Security code is doing / not doing - at least in Java 8 u 131.
In this example we have a credential cache, of a type Java GSS can access, containing a valid Ticket Granting Ticket (TGT) and a valid Service Ticket (SGT).
1) When the client principal Subject is created, the TGT is loaded from the cache (Credentials.acquireTGTFromCache()), and stored in the privateCredentials set of the Subject. --> (OK)
Only the TGT is loaded, SGTs are NOT loaded and saved to the Subject privateCredentials. -->(NOT OK)
2) Later, deep in the GSSContext.initSecContext() process, the security code actually tries to retrieve a Service Ticket from the privateCredentials of the Subject. The relevant code is Krb5Context.initSecContext() / KrbUtils.getTicket() / SubjectComber.find()/findAux(). However as SGTs were never loaded in step 1) an SGT will not be found! Therefore a new SGT is requested from the KDC and used.
This is repeated for each Service request.
Just for fun, and strictly as a proof-of-concept hack, I added a few lines of code between the login, and the initSecContext() to parse the credentials cache, extract the credentials, convert to Krb Credentials, and add them to the Subject’s private credentials.
This done, in step 2) the existing SGT is found and used. No new SGT is requested from the KDC.
I will not post the code for this hack as it calls sun internal classes that we should not be calling, and I don’t wish to inspire anybody else to do so. Nor do I intend to use this hack as a solution.
—> The root cause problem is not that the service ticket are not SAVED to the cache; but rather
a) that SGTs are not LOADED from the credential cache to the Subject of the client principal
and
b) that there is no public API or configuration settings to do so.
This affects GSS-API both with and without JAAS.
So where does this leave me?
i) Use Java GSS-API / GSS-API with JAAS “as is”, with each SGT Request hitting the KDC —> Not good.
ii) As suggested by Samson in the comments below, use Java GSS-API only for initial login of the application, then for all further calls use an alternative security mechanism for subsequent calls (a kind of self-built kerberos-light) using tokens or cookies.
iii) Consider alternatives to GSS-API such as Apache Kerby kerb-client. This has implications outside the scope of this answer, and may well prove to be jumping from the proverbial frying pan to the fire.
I have submitted a Java Feature Request to Oracle, suggesting that SGTs should be retrieved from the cache and stored in the Subject credentials (as already the case for TGTs).
http://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8180144
Question 2: Is there any downside to the fact that the service ticket is not saved to the cache?
Using the credentials cache for Service Tickets reduces interaction between the client and the KDC. The corollary to this is that where service tickets are not cached, each request will require interaction with the KDC, which could lead to the KDC being hammered.