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