org.apache.coheigea.cxf.kerberos.authentication.TokenPreAuthTest.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.coheigea.cxf.kerberos.authentication.TokenPreAuthTest.java

Source

/**
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements. See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership. The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License. You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.coheigea.cxf.kerberos.authentication;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.security.KeyStore;
import java.security.Principal;
import java.security.PrivilegedExceptionAction;
import java.util.Collections;
import java.util.Date;
import java.util.Properties;
import java.util.Set;

import javax.security.auth.Subject;
import javax.security.auth.kerberos.KerberosPrincipal;
import javax.security.auth.kerberos.KerberosTicket;
import javax.security.auth.login.LoginContext;

import org.apache.commons.io.IOUtils;
import org.apache.cxf.rs.security.jose.common.JoseConstants;
import org.apache.cxf.rs.security.jose.jwa.SignatureAlgorithm;
import org.apache.cxf.rs.security.jose.jws.JwsHeaders;
import org.apache.cxf.rs.security.jose.jws.JwsJwtCompactProducer;
import org.apache.cxf.rs.security.jose.jws.JwsSignatureProvider;
import org.apache.cxf.rs.security.jose.jws.JwsUtils;
import org.apache.cxf.rs.security.jose.jwt.JwtClaims;
import org.apache.kerby.kerberos.kdc.impl.NettyKdcServerImpl;
import org.apache.kerby.kerberos.kerb.KrbException;
import org.apache.kerby.kerberos.kerb.KrbRuntime;
import org.apache.kerby.kerberos.kerb.ccache.Credential;
import org.apache.kerby.kerberos.kerb.ccache.CredentialCache;
import org.apache.kerby.kerberos.kerb.client.KrbClient;
import org.apache.kerby.kerberos.kerb.client.KrbTokenClient;
import org.apache.kerby.kerberos.kerb.client.jaas.TokenCache;
import org.apache.kerby.kerberos.kerb.common.EncryptionUtil;
import org.apache.kerby.kerberos.kerb.crypto.EncryptionHandler;
import org.apache.kerby.kerberos.kerb.server.KdcConfigKey;
import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer;
import org.apache.kerby.kerberos.kerb.type.ad.AdToken;
import org.apache.kerby.kerberos.kerb.type.ad.AuthorizationData;
import org.apache.kerby.kerberos.kerb.type.ad.AuthorizationDataEntry;
import org.apache.kerby.kerberos.kerb.type.base.EncryptionKey;
import org.apache.kerby.kerberos.kerb.type.base.KeyUsage;
import org.apache.kerby.kerberos.kerb.type.base.KrbToken;
import org.apache.kerby.kerberos.kerb.type.ticket.EncTicketPart;
import org.apache.kerby.kerberos.kerb.type.ticket.SgtTicket;
import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket;
import org.apache.kerby.kerberos.kerb.type.ticket.Ticket;
import org.apache.kerby.kerberos.provider.token.JwtTokenProvider;
import org.apache.wss4j.common.util.Loader;
import org.apache.wss4j.dom.engine.WSSConfig;
import org.ietf.jgss.GSSContext;
import org.ietf.jgss.GSSCredential;
import org.ietf.jgss.GSSException;
import org.ietf.jgss.GSSManager;
import org.ietf.jgss.GSSName;
import org.ietf.jgss.Oid;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.BeforeClass;

/**
 * Some tests for Kerberos Token Pre-Authentication
 */
public class TokenPreAuthTest extends org.junit.Assert {

    private static SimpleKdcServer kerbyServer;

    @BeforeClass
    public static void setUp() throws Exception {

        WSSConfig.init();

        String basedir = System.getProperty("basedir");
        if (basedir == null) {
            basedir = new File(".").getCanonicalPath();
        }

        System.setProperty("sun.security.krb5.debug", "true");
        System.setProperty("java.security.auth.login.config",
                basedir + "/target/test-classes/kerberos/kerberos.jaas");

        KrbRuntime.setTokenProvider(new JwtTokenProvider());
        kerbyServer = new SimpleKdcServer();

        kerbyServer.setKdcRealm("service.ws.apache.org");
        kerbyServer.setAllowUdp(true);
        kerbyServer.setWorkDir(new File(basedir + "/target"));

        kerbyServer.setInnerKdcImpl(new NettyKdcServerImpl(kerbyServer.getKdcSetting()));

        kerbyServer.getKdcConfig().setString(KdcConfigKey.TOKEN_ISSUERS, "DoubleItSTSIssuer");
        kerbyServer.getKdcConfig().setString(KdcConfigKey.TOKEN_VERIFY_KEYS, "myclient.cer");
        kerbyServer.init();

        // Create principals
        String alice = "alice@service.ws.apache.org";
        String bob = "bob/service.ws.apache.org@service.ws.apache.org";
        kerbyServer.createPrincipal(alice, "alice");
        kerbyServer.createPrincipal(bob, "bob");
        kerbyServer.start();

        System.setProperty("java.security.krb5.conf", basedir + "/target/krb5.conf");
    }

    @AfterClass
    public static void tearDown() throws KrbException {
        if (kerbyServer != null) {
            kerbyServer.stop();
        }
    }

    // Use the TokenAuthLoginModule in Kerby to log in to the KDC using a JWT token
    @org.junit.Test
    public void unitTokenAuthGSSTest() throws Exception {

        // 1. Get a TGT from the KDC for the client + create an armor cache
        KrbClient client = new KrbClient();

        client.setKdcHost("localhost");
        client.setKdcTcpPort(kerbyServer.getKdcPort());
        client.setAllowUdp(false);

        client.setKdcRealm(kerbyServer.getKdcSetting().getKdcRealm());
        client.init();

        TgtTicket tgt = client.requestTgt("alice@service.ws.apache.org", "alice");
        assertNotNull(tgt);

        // Write to cache
        Credential credential = new Credential(tgt);
        CredentialCache cCache = new CredentialCache();
        cCache.addCredential(credential);
        cCache.setPrimaryPrincipal(tgt.getClientPrincipal());

        File cCacheFile = File.createTempFile("krb5_alice@service.ws.apache.org", "cc");
        cCache.store(cCacheFile);

        // Now read in JAAS config + substitute in the armor cache file path value
        String basedir = System.getProperty("basedir");
        if (basedir == null) {
            basedir = new File(".").getCanonicalPath();
        }
        File f = new File(basedir + "/target/test-classes/kerberos/kerberos.jaas");

        FileInputStream inputStream = new FileInputStream(f);
        String content = IOUtils.toString(inputStream, "UTF-8");
        inputStream.close();
        content = content.replaceAll("armorCacheVal", cCacheFile.getPath());

        File f2 = new File(basedir + "/target/test-classes/kerberos/kerberos.jaas");
        FileOutputStream outputStream = new FileOutputStream(f2);
        IOUtils.write(content, outputStream, "UTF-8");
        outputStream.close();

        // 2. Create a JWT token using CXF
        JwtClaims claims = new JwtClaims();
        claims.setSubject("alice");
        claims.setIssuer("DoubleItSTSIssuer");
        claims.setIssuedAt(new Date().getTime() / 1000L);
        claims.setExpiryTime(new Date().getTime() + (60L + 1000L));
        String address = "krbtgt/service.ws.apache.org@service.ws.apache.org";
        claims.setAudiences(Collections.singletonList(address));

        KeyStore keystore = KeyStore.getInstance("JKS");
        keystore.load(Loader.getResourceAsStream("clientstore.jks"), "cspass".toCharArray());

        Properties signingProperties = new Properties();
        signingProperties.put(JoseConstants.RSSEC_SIGNATURE_ALGORITHM, SignatureAlgorithm.RS256.name());
        signingProperties.put(JoseConstants.RSSEC_KEY_STORE, keystore);
        signingProperties.put(JoseConstants.RSSEC_KEY_STORE_ALIAS, "myclientkey");
        signingProperties.put(JoseConstants.RSSEC_KEY_PSWD, "ckpass");

        JwsHeaders jwsHeaders = new JwsHeaders(signingProperties);
        JwsJwtCompactProducer jws = new JwsJwtCompactProducer(jwsHeaders, claims);

        JwsSignatureProvider sigProvider = JwsUtils.loadSignatureProvider(signingProperties, jwsHeaders);

        String signedToken = jws.signWith(sigProvider);

        // Store the JWT token in the token cache
        File tokenCache = new File(basedir + "/target/tokencache.txt");
        if (!tokenCache.exists()) {
            tokenCache.createNewFile();
        }
        TokenCache.writeToken(signedToken, tokenCache.getPath());

        // 3. Now log in using JAAS
        LoginContext loginContext = new LoginContext("aliceTokenAuth", new KerberosCallbackHandler());
        loginContext.login();

        Subject clientSubject = loginContext.getSubject();
        //Set<Principal> clientPrincipals = clientSubject.getPrincipals();
        //assertFalse(clientPrincipals.isEmpty());

        // Get the TGT
        Set<KerberosTicket> privateCredentials = clientSubject.getPrivateCredentials(KerberosTicket.class);
        assertFalse(privateCredentials.isEmpty());

        // Get the service ticket using GSS
        KerberosClientExceptionAction action = new KerberosClientExceptionAction(
                new KerberosPrincipal("alice@service.ws.apache.org"), "bob@service.ws.apache.org");
        byte[] ticket = (byte[]) Subject.doAs(clientSubject, action);
        assertNotNull(ticket);

        loginContext.logout();

        validateServiceTicket(ticket);

        cCacheFile.delete();
        tokenCache.delete();
    }

    @org.junit.Test
    public void jwtUnitTestAccess() throws Exception {

        // Get a TGT
        KrbClient client = new KrbClient();

        client.setKdcHost("localhost");
        client.setKdcTcpPort(kerbyServer.getKdcPort());
        client.setAllowUdp(false);

        client.setKdcRealm(kerbyServer.getKdcSetting().getKdcRealm());
        client.init();

        TgtTicket tgt = client.requestTgt("alice@service.ws.apache.org", "alice");
        assertNotNull(tgt);

        // Write to cache
        Credential credential = new Credential(tgt);
        CredentialCache cCache = new CredentialCache();
        cCache.addCredential(credential);
        cCache.setPrimaryPrincipal(tgt.getClientPrincipal());

        File cCacheFile = File.createTempFile("krb5_alice@service.ws.apache.org", "cc");
        cCache.store(cCacheFile);

        KrbTokenClient tokenClient = new KrbTokenClient(client);

        tokenClient.setKdcHost("localhost");
        tokenClient.setKdcTcpPort(kerbyServer.getKdcPort());
        tokenClient.setAllowUdp(false);

        tokenClient.setKdcRealm(kerbyServer.getKdcSetting().getKdcRealm());
        tokenClient.init();

        // Create a JWT token using CXF
        JwtClaims claims = new JwtClaims();
        claims.setSubject("alice");
        claims.setIssuer("DoubleItSTSIssuer");
        claims.setIssuedAt(new Date().getTime() / 1000L);
        claims.setExpiryTime(new Date().getTime() + (60L + 1000L));
        String address = "bob/service.ws.apache.org@service.ws.apache.org";
        claims.setAudiences(Collections.singletonList(address));

        // Wrap it in a KrbToken + sign it
        CXFKrbToken krbToken = new CXFKrbToken(claims, false);
        krbToken.sign();

        // Now get a SGT using the JWT
        SgtTicket tkt;
        try {
            tkt = tokenClient.requestSgt(krbToken, "bob/service.ws.apache.org@service.ws.apache.org",
                    cCacheFile.getPath());
            assertTrue(tkt != null);

            // Decrypt the ticket
            Ticket ticket = tkt.getTicket();
            String bob = "bob/service.ws.apache.org@service.ws.apache.org";
            EncryptionKey key = EncryptionHandler.string2Key(bob, "bob", ticket.getEncryptedEncPart().getEType());

            EncTicketPart encPart = EncryptionUtil.unseal(ticket.getEncryptedEncPart(), key,
                    KeyUsage.KDC_REP_TICKET, EncTicketPart.class);

            // Examine the authorization data
            AuthorizationData authzData = encPart.getAuthorizationData();
            assertEquals(1, authzData.getElements().size());
            AuthorizationDataEntry dataEntry = authzData.getElements().iterator().next();
            AdToken token = dataEntry.getAuthzDataAs(AdToken.class);
            KrbToken decodedKrbToken = token.getToken();
            assertEquals("alice", decodedKrbToken.getSubject());
            assertEquals(address, decodedKrbToken.getAudiences().get(0));

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

        cCacheFile.delete();
    }

    @org.junit.Test
    public void jwtUnitTestIdentity() throws Exception {

        // Get a TGT
        KrbClient client = new KrbClient();

        client.setKdcHost("localhost");
        client.setKdcTcpPort(kerbyServer.getKdcPort());
        client.setAllowUdp(false);

        client.setKdcRealm(kerbyServer.getKdcSetting().getKdcRealm());
        client.init();

        TgtTicket tgt = client.requestTgt("alice@service.ws.apache.org", "alice");
        assertNotNull(tgt);

        // Write to cache
        Credential credential = new Credential(tgt);
        CredentialCache cCache = new CredentialCache();
        cCache.addCredential(credential);
        cCache.setPrimaryPrincipal(tgt.getClientPrincipal());

        File cCacheFile = File.createTempFile("krb5_alice@service.ws.apache.org", "cc");
        cCache.store(cCacheFile);

        KrbTokenClient tokenClient = new KrbTokenClient(client);

        tokenClient.setKdcHost("localhost");
        tokenClient.setKdcTcpPort(kerbyServer.getKdcPort());
        tokenClient.setAllowUdp(false);

        tokenClient.setKdcRealm(kerbyServer.getKdcSetting().getKdcRealm());
        tokenClient.init();

        // Create a JWT token using CXF
        JwtClaims claims = new JwtClaims();
        claims.setSubject("alice");
        claims.setIssuer("DoubleItSTSIssuer");
        claims.setIssuedAt(new Date().getTime() / 1000L);
        claims.setExpiryTime(new Date().getTime() + (60L + 1000L));
        String address = "krbtgt/service.ws.apache.org@service.ws.apache.org";
        claims.setAudiences(Collections.singletonList(address));

        // Wrap it in a KrbToken + sign it
        CXFKrbToken krbToken = new CXFKrbToken(claims, true);
        krbToken.sign();

        // Now get a TGT using the JWT token
        tgt = tokenClient.requestTgt(krbToken, cCacheFile.getPath());

        // Now get a SGT using the TGT
        SgtTicket tkt;
        try {
            tkt = tokenClient.requestSgt(tgt, "bob/service.ws.apache.org@service.ws.apache.org");
            assertTrue(tkt != null);

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

        cCacheFile.delete();
    }

    private void validateServiceTicket(byte[] ticket) throws Exception {
        // Get the TGT for the service
        LoginContext loginContext = new LoginContext("bob", new KerberosCallbackHandler());
        loginContext.login();

        Subject serviceSubject = loginContext.getSubject();
        Set<Principal> servicePrincipals = serviceSubject.getPrincipals();
        assertFalse(servicePrincipals.isEmpty());

        // Handle the service ticket
        KerberosServiceExceptionAction serviceAction = new KerberosServiceExceptionAction(ticket,
                "bob@service.ws.apache.org");

        Subject.doAs(serviceSubject, serviceAction);
    }

    /**
     * This class represents a PrivilegedExceptionAction implementation to obtain a service ticket from a Kerberos
     * Key Distribution Center.
     */
    private static class KerberosClientExceptionAction implements PrivilegedExceptionAction<byte[]> {

        private static final String JGSS_KERBEROS_TICKET_OID = "1.2.840.113554.1.2.2";

        private Principal clientPrincipal;
        private String serviceName;

        public KerberosClientExceptionAction(Principal clientPrincipal, String serviceName) {
            this.clientPrincipal = clientPrincipal;
            this.serviceName = serviceName;
        }

        public byte[] run() throws GSSException {
            GSSManager gssManager = GSSManager.getInstance();

            GSSName gssService = gssManager.createName(serviceName, GSSName.NT_HOSTBASED_SERVICE);
            Oid oid = new Oid(JGSS_KERBEROS_TICKET_OID);
            GSSName gssClient = gssManager.createName(clientPrincipal.getName(), GSSName.NT_USER_NAME);
            GSSCredential credentials = gssManager.createCredential(gssClient, GSSCredential.DEFAULT_LIFETIME, oid,
                    GSSCredential.INITIATE_ONLY);

            GSSContext secContext = gssManager.createContext(gssService, oid, credentials,
                    GSSContext.DEFAULT_LIFETIME);

            secContext.requestMutualAuth(false);
            secContext.requestCredDeleg(false);

            byte[] token = new byte[0];
            byte[] returnedToken = secContext.initSecContext(token, 0, token.length);

            secContext.dispose();

            return returnedToken;
        }
    }

    private static class KerberosServiceExceptionAction implements PrivilegedExceptionAction<byte[]> {

        private static final String JGSS_KERBEROS_TICKET_OID = "1.2.840.113554.1.2.2";

        private byte[] ticket;
        private String serviceName;

        public KerberosServiceExceptionAction(byte[] ticket, String serviceName) {
            this.ticket = ticket;
            this.serviceName = serviceName;
        }

        public byte[] run() throws GSSException {

            GSSManager gssManager = GSSManager.getInstance();

            GSSContext secContext = null;
            GSSName gssService = gssManager.createName(serviceName, GSSName.NT_HOSTBASED_SERVICE);

            Oid oid = new Oid(JGSS_KERBEROS_TICKET_OID);
            GSSCredential credentials = gssManager.createCredential(gssService, GSSCredential.DEFAULT_LIFETIME, oid,
                    GSSCredential.ACCEPT_ONLY);
            secContext = gssManager.createContext(credentials);

            try {
                byte[] outputToken = secContext.acceptSecContext(ticket, 0, ticket.length);
                /*
                if (secContext instanceof com.sun.security.jgss.ExtendedGSSContext) {
                com.sun.security.jgss.ExtendedGSSContext ex = (com.sun.security.jgss.ExtendedGSSContext)secContext;
                    
                com.sun.security.jgss.AuthorizationDataEntry[] authzDataEntries =
                    (com.sun.security.jgss.AuthorizationDataEntry[])ex.inquireSecContext(com.sun.security.jgss.InquireType.KRB5_GET_AUTHZ_DATA);
                System.out.println("AUTHZ DATA LEN: " + authzDataEntries.length);
                if (authzDataEntries != null && authzDataEntries.length > 0) {
                    System.out.println("REC AUTHZ DATA: " + new String(authzDataEntries[0].getData()));
                }
                }
                */
                return outputToken;
            } finally {
                if (null != secContext) {
                    secContext.dispose();
                }
            }
        }

    }
}