14

JavaGSS-APIを使用してKeyDistributionCenter(KDC)からサービスチケットを取得する方法を知っている人はいますか?

最初にKrb5LoginModuleを使用してJAAS経由で認証し、チケットキャッシュからTGTをフェッチするシッククライアントアプリケーションがあります(バックグラウンド:Windowsは、たとえばKerberos実装を使用し、チケット許可チケットを安全なメモリ領域に格納します)。LoginManagerから、TGTを含むSubjectオブジェクトを取得します。これで、サービス用に特定のGSSCredentialオブジェクトを作成するときに、サービスチケットがサブジェクトのプライベートクレデンシャルにも入れられることを期待していました(Webのどこかで読んだことがあります)。だから私は次のことを試しました:

// 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が見つかりませんでした)。

4

2 に答える 2

17

サービスチケットを取得するという私の理解は間違っていました。サービスから資格情報を取得する必要はありません。これはクライアントでは不可能です。クライアントには実際にはサーバーの 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 を確立することをお勧めします。そうしないと、セキュリティ ホールの可能性があるため、自分が何をしているのかを本当に理解しておく必要があります。詳細については、kerberos を使用した Sun SSO チュートリアルを私よりも注意深く読んでください。

編集: Windows XP SP2 を使用していることを忘れていました。このバージョンの Windows には、Windows RAM での TGT の使用を禁止する新しい「機能」があります。これを許可するには、レジストリを編集する必要があります。詳細については、私のように「KrbException: KDC has no support for encryption type (14)」が発生した場合に備えて、 JGSS トラブルシューティング ページのトピックを参照してください。

于 2008-12-18T07:24:08.730 に答える
9

このコードを使用するには多くの問題がありましたが、少なくとも解決策はあります。私はここにそれを投稿します、多分それはあなたの何人かを助けるでしょう...

/**
 * 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);
    }
}
于 2012-10-18T06:26:31.453 に答える