You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Java中使用GSS API加载缓存的Kerberos TGS票据?

问题:Java GSS API无法读取缓存中的Kerberos TGS票据,报错"Service ticket not found in the subject"

已通过以下步骤生成并确认缓存中有有效票据:

  1. 使用kinit生成TGT票据
  2. 使用kvno cifs/DC1.proxyrealm.test生成CIFS服务的TGS票据
  3. 执行klist确认缓存中存在以下有效票据:
Ticket cache: FILE:/tmp/krb5cc_0
Default principal: Administrator@PROXYREALM.TEST

Valid starting     Expires            Service principal
22/06/25 12:48:10  22/06/25 22:48:10  krbtgt/PROXYREALM.TEST@PROXYREALM.TEST
    renew until 29/06/25 12:48:06
22/06/25 12:49:02  22/06/25 22:48:10  cifs/DC1.proxyrealm.test@PROXYREALM.TEST
    renew until 29/06/25 12:48:06

但使用Java GSS API加载缓存票据时,仅能识别TGT,报错Service ticket not found in the subject。Java调试日志显示已读取到CIFS服务票据,但无法匹配使用。

调试日志片段

Java config name: null
Native config name: /etc/krb5.conf
Loading config file from /etc/krb5.conf
# 省略配置加载内容
>>>DEBUG <CCacheInputStream>  client principal is Administrator@PROXYREALM.TEST
>>>DEBUG <CCacheInputStream> server principal is cifs/DC1.proxyrealm.test@PROXYREALM.TEST
>>>DEBUG <CCacheInputStream> key type: 18
>>>DEBUG <CCacheInputStream> auth time: Sun Jun 22 12:48:10 IDT 2025
>>>DEBUG <CCacheInputStream> start time: Sun Jun 22 12:49:02 IDT 2025
>>>DEBUG <CCacheInputStream> end time: Sun Jun 22 22:48:10 IDT 2025
>>>DEBUG <CCacheInputStream> renew_till time: Sun Jun 29 12:48:06 IDT 2025
>>> CCacheInputStream: readFlags()  FORWARDABLE; RENEWABLE; PRE_AUTH;
get normal credential
Found ticket for Administrator@PROXYREALM.TEST to go to krbtgt/PROXYREALM.TEST@PROXYREALM.TEST expiring on Sun Jun 22 22:48:10 IDT 2025
Entered Krb5Context.initSecContext with state=STATE_NEW
Service ticket not found in the subject

测试代码

public class GSSKdcProxyClient {

  public static final Oid KRB5_MECH_OID;
    static {
        try {
            KRB5_MECH_OID = new Oid("1.2.840.113554.1.2.2"); // Kerberos V5 OID
        } catch (Exception e) {
            throw new RuntimeException("Failed to initialize Kerberos OID", e);
        }
    }
    
public static void main(String[] args) throws GSSException {
     System.setProperty("javax.security.auth.useSubjectCredsOnly","false");
     System.setProperty("sun.security.krb5.debug", "true"); 
    try {
        
      GSSManager manager = GSSManager.getInstance();
      
       GSSName serverName = manager.createName("cifs/DC1.proxyrealm.test@PROXYREALM.TEST",GSSName.NT_HOSTBASED_SERVICE);
       // Acquire credentials and create context
       GSSContext context = manager.createContext(serverName,KRB5_MECH_OID, null, GSSContext.DEFAULT_LIFETIME);
       context.requestMutualAuth(false);
       context.initSecContext(new byte[0], 0, 0);
     
     if (context.isEstablished()) {
         System.out.println("GSS context established with: " + context.getSrcName());
     }
     context.dispose();
        

    } catch (Exception e) {
        e.printStackTrace();
    }
}

也曾尝试使用不同的GSSName构造方式:

GSSName serverName = manager.createName("DC1.proxyrealm.test",KRB5_PRINCIPAL_NT);

解决方案

1. 手动加载Kerberos缓存到Subject中

默认情况下,GSSManager可能无法正确从系统缓存中加载所有票据,需要通过LoginContext手动加载缓存凭证并在Subject的上下文中执行GSS操作:

import javax.security.auth.Subject;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;
import java.security.PrivilegedAction;

// 在main方法中替换原有逻辑
try {
    // 配置LoginContext使用Kerberos缓存
    System.setProperty("java.security.auth.login.config", "/path/to/krb5_login.conf");
    LoginContext lc = new LoginContext("KerberosCache");
    lc.login();
    Subject subject = lc.getSubject();

    // 在Subject上下文中执行GSS操作
    Subject.doAs(subject, (PrivilegedAction<Void>) () -> {
        try {
            GSSManager manager = GSSManager.getInstance();
            // 使用不带REALM的服务主体格式,让GSS自动匹配
            GSSName serverName = manager.createName("cifs/DC1.proxyrealm.test", GSSName.NT_HOSTBASED_SERVICE);
            GSSContext context = manager.createContext(serverName, KRB5_MECH_OID, null, GSSContext.DEFAULT_LIFETIME);
            context.requestMutualAuth(false);
            context.initSecContext(new byte[0], 0, 0);

            if (context.isEstablished()) {
                System.out.println("GSS context established with: " + context.getSrcName());
            }
            context.dispose();
        } catch (GSSException e) {
            e.printStackTrace();
        }
        return null;
    });

    lc.logout();
} catch (LoginException e) {
    e.printStackTrace();
}

对应的krb5_login.conf配置内容:

KerberosCache {
    com.sun.security.auth.module.Krb5LoginModule required
    useTicketCache=true
    ticketCache="/tmp/krb5cc_0"
    doNotPrompt=true;
};

2. 正确构造GSS服务主体名

  • 使用GSSName.NT_HOSTBASED_SERVICE类型时,格式应为service/hostname,不需要附加REALM(GSS会根据krb5.conf自动匹配)
  • 确保主体名与缓存中的服务票据完全一致(注意大小写,Kerberos主体名通常区分大小写)

3. 检查krb5.conf配置

确保domain_realm映射正确,让Java能够将主机名正确关联到对应的Kerberos域:

domain_realm = {
    .proxyrealm.test = PROXYREALM.TEST
    proxyrealm.test = PROXYREALM.TEST
}

4. 确保缓存权限正确

运行Java程序的用户需要对/tmp/krb5cc_0文件有读取权限,可执行以下命令调整:

chmod 600 /tmp/krb5cc_0
chown <java_user>:<java_group> /tmp/krb5cc_0

内容的提问来源于stack exchange,提问作者Tal Dulberg

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.12 19:54:51