Java 使用GSSAPI从有效的ldap服务票证创建LdapContext

Java 使用GSSAPI从有效的ldap服务票证创建LdapContext,java,kerberos,jndi,jaas,gssapi,Java,Kerberos,Jndi,Jaas,Gssapi,请注意:-我只想验证是否可以使用JAAS/GSSAPI实现以下目标。我找不到任何指针 首先,让我澄清我的应用程序的限制: 我们不能有一个静态的krb.conf文件。它是动态的、不断变化的。因此,在内存中缓存LoginContext对象是不可能的,因为一旦krb.conf文件被修改,主体的凭据就会失效 我们想要管理大量领域,而这些领域我们没有任何先验信息,因此静态krb.conf文件是不可能的 “sun.security.krb5.internal.tools.Kinit”是专有的,并且总是会收到

请注意:-我只想验证是否可以使用JAAS/GSSAPI实现以下目标。我找不到任何指针

首先,让我澄清我的应用程序的限制:

  • 我们不能有一个静态的krb.conf文件。它是动态的、不断变化的。因此,在内存中缓存LoginContext对象是不可能的,因为一旦krb.conf文件被修改,主体的凭据就会失效
  • 我们想要管理大量领域,而这些领域我们没有任何先验信息,因此静态krb.conf文件是不可能的
  • sun.security.krb5.internal.tools.Kinit
    ”是专有的,并且总是会收到一条警告,它可能在将来的版本中被删除。因此,我们不能使用它来生成缓存,因为存在获得运行时问题的风险。注意:即使krb.conf文件本机更改,缓存的TGT也不会过期。但是这里的问题是,我们必须使生成和维护krb.conf文件的代码变得非常复杂。我们希望避免这种情况
  • 我们保留使用Runtime#exec(…)运行kinit工具的选项,作为生成TGT缓存文件的最后手段(这将减少对sun.security.krb5.internal.tools.kinit包的依赖),但将使krb.conf生成和维护逻辑更加复杂
  • 我在寻找一个更简单的解决办法
  • 我们当前的实现非常简单:我们更改krb.conf文件以适应当前的领域和域信息,然后删除该文件。一切都非常动态,非常高效,但问题是,我们无法缓存TGT和服务票证,这增加了KDC服务器上的负载,并导致性能下降 在全面介绍之后,让我们转到我正在尝试的PoC。请在以下位置提供指针或:

  • 我是否朝着正确的方向前进
  • 我所设想的事情是可能的还是不可能的
  • 如果你们中有人遇到过这样的情况,你们采用了什么策略
  • 如果没有可用的缓存数据,请正常继续创建LoginContext:

        System.setProperty("java.security.krb5.conf", "C:\\Windows\\krb5.ini");
        System.setProperty("sun.security.jgss.debug","true");
        LoginContext lc = null;
        try {
            lc = new LoginContext(LdapCtxGSSAPIEx.class.getName(),
                    new LoginCallbackHandler(username,password));
    
            // Attempt authentication
            // You might want to do this in a "for" loop to give
            // user more than one chance to enter correct username/password
            lc.login();
    
        } catch (LoginException le) {
            System.err.println("Authentication attempt failed" + le);
            System.exit(-1);
        }
    
    解决方案#1:

    创建有效的
    LoginContext
    后,使用GSSAPI获取ldap的服务票证。我知道另一种在PrivilegedAction#run()中形成LdapContext的方法(实际上是我们当前的实现,如解决方案2所示)。但这对缓存没有帮助。因此,尝试使用PoC来存储服务票证,而不是TGT获取ldap服务票证如下所示

        // servicePrincipalName = ldap/ad01.example.lab@EXAMPLE.LAB
        GSSManager manager = GSSManager.getInstance();
        GSSName serverName = manager.createName( servicePrincipalName,
                GSSName.NT_HOSTBASED_SERVICE);
        final GSSContext context = manager.createContext( serverName, krb5OID, null,
                GSSContext.DEFAULT_LIFETIME);
        // The GSS context initiation has to be performed as a privileged action.
        byte[] serviceTicket = Subject.doAs( subject, new PrivilegedAction<byte[]>() {
            public byte[] run() {
                try {
                    byte[] token = new byte[0];
                    // This is a one pass context initialisation.
                    context.requestMutualAuth( false);
                    context.requestCredDeleg( false);
                    return context.initSecContext( token, 0, token.length);
                }
                catch ( GSSException e) {
                    e.printStackTrace();
                    return null;
                }
            }
        });
    
    当此行:
    DirContext ctx=new InitialDirContext(env):使用带有服务主体的新的
    PrivateCredential
    填充
    主题
    。示例。lab@EXAMPLE.LAB

    问题:我是否可以存储此服务票证以供进一步参考,从而创建LdapContext,而不是一次又一次地执行所有身份验证步骤

    什么是授权证书?他们会帮助解决我的问题吗


    更新: 为什么要在缓存中存储服务票证而不是TGT?-->为了避免显式的扭结。存储服务票据将很容易与我们当前的解决方案相适应。此外,这将是一个临时解决方案,因为我们将根据客户的需要向他们询问krb.conf文件,并免除创建krb.conf文件的责任。但现在,我们必须这样做

    请帮忙

        // servicePrincipalName = ldap/ad01.example.lab@EXAMPLE.LAB
        GSSManager manager = GSSManager.getInstance();
        GSSName serverName = manager.createName( servicePrincipalName,
                GSSName.NT_HOSTBASED_SERVICE);
        final GSSContext context = manager.createContext( serverName, krb5OID, null,
                GSSContext.DEFAULT_LIFETIME);
        // The GSS context initiation has to be performed as a privileged action.
        byte[] serviceTicket = Subject.doAs( subject, new PrivilegedAction<byte[]>() {
            public byte[] run() {
                try {
                    byte[] token = new byte[0];
                    // This is a one pass context initialisation.
                    context.requestMutualAuth( false);
                    context.requestCredDeleg( false);
                    return context.initSecContext( token, 0, token.length);
                }
                catch ( GSSException e) {
                    e.printStackTrace();
                    return null;
                }
            }
        });
    
     DirContext ctx = Subject.doAs(lc.getSubject(), new JndiAction<DirContext>(args,providerURL ));
    
    
     class JndiAction<DirContext> implements java.security.PrivilegedAction<DirContext> {
     .......
          public DirContext run() {
              return performJndiOperation(args, this.providerURL);
          }
    
          public DirContext performJndiOperation(String[] args, String providerURL){
             ......
             env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
    
             // Must use fully qualified hostname
             env.put(Context.PROVIDER_URL, providerURL);
    
             // Request the use of the "GSSAPI" SASL mechanism
             // Authenticate by using already established Kerberos credentials
             env.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");
    
             // Request mutual authentication
             env.put("javax.security.sasl.server.authentication", "true"); 
    
             DirContext ctx = new InitialDirContext(env);
    
             return ctx;
             ......
    
          }
     .......
    
     }