代码之家  ›  专栏  ›  技术社区  ›  Roland Schneider

如何通过GSS-API获取Kerberos服务票证?

  •  14
  • Roland Schneider  · 技术社区  · 16 年前

    有人知道如何使用JavaGSS-API从密钥分发中心(KDC)获得服务票吗?

    我有一个厚客户端应用程序,它首先通过JAAS进行身份验证,使用krb5loginmodule从票据缓存中获取TGT(背景:Windows,例如使用Kerberos实现并将票据授予票据存储在安全内存区域中)。从LoginManager中,我得到了包含TGT的主题对象。现在,我希望当我为我的服务创建一个特定的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)。

    2 回复  |  直到 9 年前
        1
  •  16
  •   Cerbrus    9 年前

    我对拿到服务票的理解是错误的。我不需要从服务获取凭据-这在客户端上是不可能的,因为客户端确实没有服务器的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)”,就像我所做的那样。

        2
  •  9
  •   Olivier Faucheux    12 年前

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

    /**
     * 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);
        }
    }