/** * 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.net.URL; import java.security.Principal; import java.security.PrivilegedExceptionAction; import java.util.Set; import javax.security.auth.Subject; import javax.security.auth.kerberos.KerberosTicket; import javax.security.auth.login.LoginContext; import javax.xml.namespace.QName; import javax.xml.ws.Service; import org.apache.cxf.Bus; import org.apache.cxf.bus.spring.SpringBusFactory; import org.apache.cxf.testutil.common.AbstractBusClientServerTestBase; import org.apache.cxf.testutil.common.TestUtil; 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.client.Krb5Conf; import org.apache.kerby.kerberos.kerb.client.KrbClient; import org.apache.kerby.kerberos.kerb.client.KrbConfig; import org.apache.kerby.kerberos.kerb.server.KdcConfigKey; import org.apache.kerby.kerberos.kerb.server.SimpleKdcServer; import org.apache.kerby.kerberos.kerb.type.ticket.SgtTicket; import org.apache.kerby.kerberos.kerb.type.ticket.TgtTicket; import org.apache.kerby.kerberos.provider.token.JwtTokenProvider; import org.apache.wss4j.dom.engine.WSSConfig; import org.example.contract.doubleit.DoubleItPortType; 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; /** * There are two test-cases covered in this class, one that uses a WS-SecurityPolicy * KerberosToken policy, and the other that uses a SpnegoContextToken policy. * * Both testcases start up a KDC locally using Apache Kerby. In each case, the service endpoint * has a TransportBinding policy, with a corresponding EndorsingSupportingToken which is either * a KerberosToken or SpnegoContextToken. The client will obtain a service ticket from the KDC * and include it in the security header of the service request. */ public class AuthenticationTest extends org.junit.Assert { private static final String NAMESPACE = "http://www.example.org/contract/DoubleIt"; private static final QName SERVICE_QNAME = new QName(NAMESPACE, "DoubleItService"); private static final String PORT = TestUtil.getPortNumber(Server.class); 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(); } 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("sun.security.krb5.debug", "true"); System.setProperty("java.security.auth.login.config", basedir + "/target/test-classes/kerberos/kerberos.jaas"); System.setProperty("java.security.krb5.conf", basedir + "/target/krb5.conf"); Assert.assertTrue( "Server failed to launch", // run the server in the same process // set this to false to fork AbstractBusClientServerTestBase.launchServer(Server.class, true) ); } @AfterClass public static void tearDown() throws KrbException { if (kerbyServer != null) { kerbyServer.stop(); } } @org.junit.Test public void unitTest() throws Exception { KrbClient client = new KrbClient(); client.setKdcHost("localhost"); client.setKdcTcpPort(kerbyServer.getKdcPort()); client.setAllowUdp(false); // client.setKdcUdpPort(Integer.parseInt(KDC_UDP_PORT)); client.setKdcRealm(kerbyServer.getKdcSetting().getKdcRealm()); client.init(); TgtTicket tgt; SgtTicket tkt; try { tgt = client.requestTgt("alice@service.ws.apache.org", "alice"); assertTrue(tgt != null); tkt = client.requestSgt(tgt, "bob/service.ws.apache.org@service.ws.apache.org"); assertTrue(tkt != null); } catch (Exception e) { e.printStackTrace(); Assert.fail(); } } @org.junit.Test public void unitTestUsingKrb5Conf() throws Exception { File confFile = new File(System.getProperty(Krb5Conf.KRB5_CONF)); KrbConfig krbConfig = new KrbConfig(); krbConfig.addKrb5Config(confFile); KrbClient client = new KrbClient(krbConfig); client.init(); TgtTicket tgt; SgtTicket tkt; try { tgt = client.requestTgt("alice@service.ws.apache.org", "alice"); assertTrue(tgt != null); tkt = client.requestSgt(tgt, "bob/service.ws.apache.org@service.ws.apache.org"); assertTrue(tkt != null); } catch (Exception e) { e.printStackTrace(); Assert.fail(); } } @org.junit.Test public void unitGSSTest() throws Exception { LoginContext loginContext = new LoginContext("alice", 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()); KerberosTicket tgt = privateCredentials.iterator().next(); assertNotNull(tgt); // Get the service ticket KerberosClientExceptionAction action = new KerberosClientExceptionAction(clientPrincipals.iterator().next(), "bob@service.ws.apache.org"); byte[] ticket = (byte[]) Subject.doAs(clientSubject, action); assertNotNull(ticket); loginContext.logout(); validateServiceTicket(ticket); } 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); } @org.junit.Test public void testKerberos() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = AuthenticationTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); URL wsdl = AuthenticationTest.class.getResource("DoubleIt.wsdl"); Service service = Service.create(wsdl, SERVICE_QNAME); QName portQName = new QName(NAMESPACE, "DoubleItKerberosTransportPort"); DoubleItPortType transportPort = service.getPort(portQName, DoubleItPortType.class); TestUtil.updateAddressPort(transportPort, PORT); doubleIt(transportPort, 25); } @org.junit.Test public void testSpnego() throws Exception { SpringBusFactory bf = new SpringBusFactory(); URL busFile = AuthenticationTest.class.getResource("cxf-client.xml"); Bus bus = bf.createBus(busFile.toString()); SpringBusFactory.setDefaultBus(bus); SpringBusFactory.setThreadDefaultBus(bus); URL wsdl = AuthenticationTest.class.getResource("DoubleIt.wsdl"); Service service = Service.create(wsdl, SERVICE_QNAME); QName portQName = new QName(NAMESPACE, "DoubleItSpnegoTransportPort"); DoubleItPortType transportPort = service.getPort(portQName, DoubleItPortType.class); TestUtil.updateAddressPort(transportPort, PORT); doubleIt(transportPort, 25); } private static void doubleIt(DoubleItPortType port, int numToDouble) { int resp = port.doubleIt(numToDouble); Assert.assertEquals(numToDouble * 2 , resp); } /** * 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 { return secContext.acceptSecContext(ticket, 0, ticket.length); } finally { if (null != secContext) { secContext.dispose(); } } } } }