1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.wss4j.stax.impl.securityToken;
20
21 import java.io.IOException;
22 import java.security.Key;
23 import java.security.Principal;
24 import java.security.PrivilegedActionException;
25 import java.util.Set;
26
27 import javax.crypto.spec.SecretKeySpec;
28 import javax.security.auth.Subject;
29 import javax.security.auth.callback.Callback;
30 import javax.security.auth.callback.CallbackHandler;
31 import javax.security.auth.callback.UnsupportedCallbackException;
32 import javax.security.auth.kerberos.KerberosTicket;
33 import javax.security.auth.login.LoginContext;
34 import javax.security.auth.login.LoginException;
35
36 import org.apache.wss4j.common.ext.WSSecurityException;
37 import org.apache.wss4j.common.ext.WSSecurityException.ErrorCode;
38 import org.apache.wss4j.common.kerberos.KerberosClientExceptionAction;
39 import org.apache.wss4j.common.kerberos.KerberosContext;
40 import org.apache.wss4j.common.kerberos.KerberosContextAndServiceNameCallback;
41 import org.apache.wss4j.common.util.KeyUtils;
42 import org.apache.wss4j.stax.securityToken.WSSecurityTokenConstants;
43 import org.apache.xml.security.exceptions.XMLSecurityException;
44 import org.apache.xml.security.stax.impl.securityToken.GenericOutboundSecurityToken;
45
46 public class KerberosClientSecurityToken extends GenericOutboundSecurityToken {
47
48 private CallbackHandler callbackHandler;
49 private Key secretKey;
50 private byte[] ticket;
51
52 public KerberosClientSecurityToken(byte[] ticket, Key secretKey, String id) {
53 super(id, WSSecurityTokenConstants.KERBEROS_TOKEN);
54 this.ticket = ticket;
55 this.secretKey = secretKey;
56 }
57
58 public KerberosClientSecurityToken(CallbackHandler callbackHandler, String id) {
59 super(id, WSSecurityTokenConstants.KERBEROS_TOKEN);
60 this.callbackHandler = callbackHandler;
61 }
62
63 private void getTGT() throws WSSecurityException {
64 try {
65 KerberosContextAndServiceNameCallback contextAndServiceNameCallback = new KerberosContextAndServiceNameCallback();
66 callbackHandler.handle(new Callback[]{contextAndServiceNameCallback});
67
68 if (contextAndServiceNameCallback.getContextName() == null) {
69 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "kerberosCallbackContextNameNotSupplied");
70 }
71 if (contextAndServiceNameCallback.getServiceName() == null) {
72 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, "kerberosCallbackServiceNameNotSupplied");
73 }
74
75 LoginContext loginContext = new LoginContext(contextAndServiceNameCallback.getContextName(), callbackHandler);
76 loginContext.login();
77
78 Subject clientSubject = loginContext.getSubject();
79 Set<Principal> clientPrincipals = clientSubject.getPrincipals();
80 if (clientPrincipals.isEmpty()) {
81 throw new WSSecurityException(
82 WSSecurityException.ErrorCode.FAILURE,
83 "kerberosLoginError",
84 new Object[] {"No Client principals found after login"}
85 );
86 }
87
88 KerberosTicket tgt = getKerberosTicket(clientSubject, null);
89
90
91 KerberosClientExceptionAction action =
92 new KerberosClientExceptionAction(clientPrincipals.iterator().next(),
93 contextAndServiceNameCallback.getServiceName(),
94 contextAndServiceNameCallback.isUsernameServiceNameForm(),
95 contextAndServiceNameCallback.isRequestCredDeleg());
96 KerberosContext krbCtx = null;
97 try {
98 krbCtx = Subject.doAs(clientSubject, action);
99
100
101 Key sessionKey = krbCtx.getSecretKey();
102 if (sessionKey != null) {
103 secretKey = new SecretKeySpec(sessionKey.getEncoded(), sessionKey.getAlgorithm());
104 } else {
105 KerberosTicket serviceTicket = getKerberosTicket(clientSubject, tgt);
106 secretKey = serviceTicket.getSessionKey();
107 }
108
109 ticket = krbCtx.getKerberosToken();
110 } catch (PrivilegedActionException e) {
111 Throwable cause = e.getCause();
112 if (cause instanceof WSSecurityException) {
113 throw (WSSecurityException) cause;
114 } else {
115 throw new WSSecurityException(
116 ErrorCode.FAILURE, new Exception(cause), "kerberosServiceTicketError"
117 );
118 }
119 }
120 } catch (LoginException | UnsupportedCallbackException | IOException e) {
121 throw new WSSecurityException(WSSecurityException.ErrorCode.FAILURE, e);
122 }
123 }
124
125
126
127
128
129 private KerberosTicket getKerberosTicket(Subject clientSubject, KerberosTicket previousTicket) {
130 Set<KerberosTicket> privateCredentials = clientSubject.getPrivateCredentials(KerberosTicket.class);
131 if (privateCredentials == null || privateCredentials.isEmpty()) {
132 return null;
133 }
134
135 for (KerberosTicket privateCredential : privateCredentials) {
136 if (!privateCredential.equals(previousTicket)) {
137 return privateCredential;
138 }
139 }
140 return null;
141 }
142
143 @Override
144 public Key getSecretKey(String algorithmURI) throws XMLSecurityException {
145 Key key = super.getSecretKey(algorithmURI);
146 if (key != null) {
147 return key;
148 }
149 if (this.secretKey == null) {
150 getTGT();
151 }
152
153 byte[] sk = this.secretKey.getEncoded();
154
155 key = KeyUtils.prepareSecretKey(algorithmURI, sk);
156 setSecretKey(algorithmURI, key);
157 return key;
158 }
159
160 public byte[] getTicket() throws XMLSecurityException {
161 if (this.ticket == null) {
162 getTGT();
163 }
164 return ticket;
165 }
166 }