【问题标题】:How to obtain a kerberos service ticket via GSS-API?如何通过 GSS-API 获取 kerberos 服务票据?
【发布时间】:2008-12-16 10:09:57
【问题描述】:

有谁知道如何使用 Java GSS-API 从密钥分发中心 (KDC) 获取服务票证?

我有一个胖客户端应用程序,它首先使用 Krb5LoginModule 通过 JAAS 进行身份验证,以从票证缓存中获取 TGT(背景:Windows 例如使用 kerberos 实现并将票证授予票证存储在安全的内存区域中)。从 LoginManager 我得到包含 TGT 的 Subject 对象。现在我希望当我为我的服务创建一个特定的 GSSCredential 对象时,服务票证也将被放入主题的私有凭据中(我已经在网络的某个地方读过)。所以我尝试了以下方法:

// Exception handling ommitted
LoginContext lc = new LoginContext("HelloEjbClient", new DialogCallbackHandler());
lc.login()
Subject.doAs(lc.getSubject(), new PrivilegedAction() {

    public Object run() {
        GSSManager manager = GSSManager.getInstance();
        GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME);
        GSSCredential clientCreds = manager.createCredential(clientName, 8 * 3600, createKerberosOid(), GSSCredential.INITIATE_ONLY);

        GSSName serverName = manager.createName("myService@localhost", GSSName.NT_HOSTBASED_SERVICE);
        manager.createCredential(serverName, GSSCredential.INDEFINITE_LIFETIME, createKerberosOid(), GSSCredential.INITIATE_ONLY);
        return null;
    }

    private Oid createKerberosOid() {
        return new Oid("1.2.840.113554.1.2.2");
    }

});

不幸的是,我收到 GSSException:未提供有效凭据(机制级别:未能找到任何 Kerberos tgt)。

【问题讨论】:

    标签: java single-sign-on kerberos


    【解决方案1】:

    我对获取服务票的理解是错误的。我不需要从服务中获取凭据 - 这在客户端上是不可能的,因为客户端确实没有服务器的 TGT,因此无权获取服务凭据。 这里缺少的是创建一个新的 GSSContext 并对其进行初始化。如果我理解正确的话,此方法的返回值包含服务票证。这是一个工作代码示例。它必须代表已登录的主题在 PrivilegedAction 中运行:

        GSSManager manager = GSSManager.getInstance();
        GSSName clientName = manager.createName("clientUser", GSSName.NT_USER_NAME);
        GSSCredential clientCred = manager.createCredential(clientName,
                                                            8 * 3600,
                                                            createKerberosOid(),
                                                            GSSCredential.INITIATE_ONLY);
    
        GSSName serverName = manager.createName("http@server", GSSName.NT_HOSTBASED_SERVICE);
    
        GSSContext context = manager.createContext(serverName,
                                                   createKerberosOid(),
                                                   clientCred,
                                                   GSSContext.DEFAULT_LIFETIME);
        context.requestMutualAuth(true);
        context.requestConf(false);
        context.requestInteg(true);
    
        byte[] outToken = context.initSecContext(new byte[0], 0, 0);
        System.out.println(new BASE64Encoder().encode(outToken));
        context.dispose();
    

    outToken 包含然后包含服务票证。然而,这不是 GSS-API 的使用方式。它的目标是将这些细节隐藏到代码中,因此最好在双方都使用 GSS-API 建立 GSSContext。否则,由于潜在的安全漏洞,您真的应该知道自己在做什么。 如需更多信息,请比我更仔细地阅读Sun SSO tutorial with kerberos

    编辑: 只是忘记了我使用的是带有 SP2 的 Windows XP。此版本的 Windows 中有一个新的“功能”,它不允许在 Windows RAM 中使用 TGT。您必须编辑注册表以允许这样做。有关更多信息,请查看 JGSS Troubleshooting page 主题,以防您像我一样遇到“KrbException:KDC 不支持加密类型 (14)”。

    【讨论】:

    • @Michael:感谢您的贡献。正如你所看到的,我很久以前写过这个,所以我不记得确切的细节,但我很确定这只是一个例子。您认为可以做些什么来改进这个答案?
    • 使用 kerberos 检查您到 Sun SSO 教程的链接,并查看图 6。这是我写的循环。
    • 我认为我没有包含循环,因为我无法弄清楚 readToken() 和 sendToken(...) 方法应该做什么。
    • 在 HTTP readToken 请求 HTTP 响应并提取令牌的情况下,sendToken 执行相反的操作。
    • 此令牌创建能否与 Active Directory 上的两个用户一起使用,即 user1 作为客户端进行 http api 调用,将此令牌作为 http 服务器传递给 user2。如果我理解正确,服务器应该能够解密这个令牌。
    【解决方案2】:

    我在使用这段代码时遇到了很多问题,但我至少有一个解决方案。我把它贴在这里,也许它会对你们中的一些人有所帮助......

    /**
     * Tool to retrieve a kerberos ticket. This one will not be stored in the windows ticket cache.
     */
    public final class KerberosTicketRetriever
    {
        private final static Oid KERB_V5_OID;
        private final static Oid KRB5_PRINCIPAL_NAME_OID;
    
        static {
            try
            {
                KERB_V5_OID = new Oid("1.2.840.113554.1.2.2");
                KRB5_PRINCIPAL_NAME_OID = new Oid("1.2.840.113554.1.2.2.1");
    
            } catch (final GSSException ex)
            {
                throw new Error(ex);
            }
        }
    
        /**
         * Not to be instanciated
         */
        private KerberosTicketRetriever() {};
    
        /**
         *
         */
        private static class TicketCreatorAction implements PrivilegedAction
        {
            final String userPrincipal;
            final String applicationPrincipal;
    
            private StringBuffer outputBuffer;
    
            /**
             *
             * @param userPrincipal  p.ex. <tt>MuelleHA@MYFIRM.COM</tt>
             * @param applicationPrincipal  p.ex. <tt>HTTP/webserver.myfirm.com</tt>
             */
            private TicketCreatorAction(final String userPrincipal, final String applicationPrincipal)
            {
                this.userPrincipal = userPrincipal;
                this.applicationPrincipal = applicationPrincipal;
            }
    
            private void setOutputBuffer(final StringBuffer newOutputBuffer)
            {
                outputBuffer = newOutputBuffer;
            }
    
            /**
             * Only calls {@link #createTicket()}
             * @return <tt>null</tt>
             */
            public Object run()
            {
                try
                {
                    createTicket();
                }
                catch (final GSSException  ex)
                {
                    throw new Error(ex);
                }
    
                return null;
            }
    
            /**
             *
             * @throws GSSException
             */
            private void createTicket () throws GSSException
            {
                final GSSManager manager = GSSManager.getInstance();
                final GSSName clientName = manager.createName(userPrincipal, KRB5_PRINCIPAL_NAME_OID);
                final GSSCredential clientCred = manager.createCredential(clientName,
                        8 * 3600,
                        KERB_V5_OID,
                        GSSCredential.INITIATE_ONLY);
    
                final GSSName serverName = manager.createName(applicationPrincipal, KRB5_PRINCIPAL_NAME_OID);
    
                final GSSContext context = manager.createContext(serverName,
                        KERB_V5_OID,
                        clientCred,
                        GSSContext.DEFAULT_LIFETIME);
                context.requestMutualAuth(true);
                context.requestConf(false);
                context.requestInteg(true);
    
                final byte[] outToken = context.initSecContext(new byte[0], 0, 0);
    
                if (outputBuffer !=null)
                {
                    outputBuffer.append(String.format("Src Name: %s\n", context.getSrcName()));
                    outputBuffer.append(String.format("Target  : %s\n", context.getTargName()));
                    outputBuffer.append(new BASE64Encoder().encode(outToken));
                    outputBuffer.append("\n");
                }
    
                context.dispose();
            }
        }
    
        /**
         *
         * @param realm p.ex. <tt>MYFIRM.COM</tt>
         * @param kdc p.ex. <tt>kerbserver.myfirm.com</tt>
         * @param applicationPrincipal   cf. {@link #TicketCreatorAction(String, String)}
         * @throws GSSException
         * @throws LoginException
         */
        static public String retrieveTicket(
                final String realm,
                final String kdc,
                final String applicationPrincipal)
        throws GSSException, LoginException
        {
    
            // create the jass-config-file
            final File jaasConfFile;
            try
            {
                jaasConfFile = File.createTempFile("jaas.conf", null);
                final PrintStream bos = new PrintStream(new FileOutputStream(jaasConfFile));
                bos.print(String.format(
                        "Krb5LoginContext { com.sun.security.auth.module.Krb5LoginModule required refreshKrb5Config=true useTicketCache=true debug=true ; };"
                ));
                bos.close();
                jaasConfFile.deleteOnExit();
            }
            catch (final IOException ex)
            {
                throw new IOError(ex);
            }
    
            // set the properties
            System.setProperty("java.security.krb5.realm", realm);
            System.setProperty("java.security.krb5.kdc", kdc);
            System.setProperty("java.security.auth.login.config",jaasConfFile.getAbsolutePath());
    
            // get the Subject(), i.e. the current user under Windows
            final Subject subject = new Subject();
            final LoginContext lc = new LoginContext("Krb5LoginContext", subject, new DialogCallbackHandler());
            lc.login();
    
            // extract our principal
            final Set<Principal> principalSet = subject.getPrincipals();
            if (principalSet.size() != 1)
                throw new AssertionError("No or several principals: " + principalSet);
            final Principal userPrincipal = principalSet.iterator().next();
    
            // now try to execute the SampleAction as the authenticated Subject
            // action.run() without doAsPrivileged leads to
            //   No valid credentials provided (Mechanism level: Failed to find any Kerberos tgt)
            final TicketCreatorAction action = new TicketCreatorAction(userPrincipal.getName(), applicationPrincipal);
            final StringBuffer outputBuffer = new StringBuffer();
            action.setOutputBuffer(outputBuffer);
            Subject.doAsPrivileged(lc.getSubject(), action, null);
    
            return outputBuffer.toString();
        }
    
        public static void main (final String args[]) throws Throwable
        {
            final String ticket = retrieveTicket("MYFIRM.COM", "kerbserver", "HTTP/webserver.myfirm.com");
            System.out.println(ticket);
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-12
      • 1970-01-01
      • 1970-01-01
      • 2012-11-30
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      相关资源
      最近更新 更多