code: /**
   * 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.cxf.ws.security.policy.interceptors;
  
  import java.util.Collection;
  import java.util.Date;
  import java.util.logging.Logger;
  
  import javax.security.auth.callback.CallbackHandler;
  
  import org.w3c.dom.Element;
  
  import org.apache.cxf.binding.soap.SoapBindingConstants;
  import org.apache.cxf.binding.soap.SoapMessage;
  import org.apache.cxf.common.logging.LogUtils;
  import org.apache.cxf.endpoint.Endpoint;
  import org.apache.cxf.helpers.DOMUtils;
  import org.apache.cxf.interceptor.Fault;
  import org.apache.cxf.message.Exchange;
  import org.apache.cxf.message.Message;
  import org.apache.cxf.phase.AbstractPhaseInterceptor;
  import org.apache.cxf.phase.Phase;
  import org.apache.cxf.staxutils.W3CDOMStreamWriter;
  import org.apache.cxf.ws.addressing.AddressingProperties;
  import org.apache.cxf.ws.addressing.JAXWSAConstants;
  import org.apache.cxf.ws.policy.AssertionInfo;
  import org.apache.cxf.ws.policy.AssertionInfoMap;
  import org.apache.cxf.ws.security.SecurityConstants;
  import org.apache.cxf.ws.security.policy.SP12Constants;
  import org.apache.cxf.ws.security.tokenstore.SecurityToken;
  import org.apache.cxf.ws.security.tokenstore.TokenStore;
  import org.apache.cxf.ws.security.trust.STSUtils;
  import org.apache.cxf.ws.security.wss4j.WSS4JInInterceptor;
  import org.apache.neethi.All;
  import org.apache.neethi.Assertion;
  import org.apache.neethi.ExactlyOne;
  import org.apache.neethi.Policy;
  import org.apache.ws.security.WSConstants;
  import org.apache.ws.security.message.token.BinarySecurity;
  import org.apache.ws.security.message.token.SecurityContextToken;
  import org.apache.ws.security.spnego.SpnegoTokenContext;
  import org.apache.ws.security.util.Base64;
  import org.apache.ws.security.util.WSSecurityUtil;
  
  class SpnegoContextTokenInInterceptor extends AbstractPhaseInterceptor<SoapMessage> {
      static final Logger LOG = LogUtils.getL7dLogger(SpnegoContextTokenInInterceptor.class);
      
      public SpnegoContextTokenInInterceptor() {
          super(Phase.PRE_PROTOCOL);
      }
      
      public void handleMessage(SoapMessage message) throws Fault {
          AssertionInfoMap aim = message.get(AssertionInfoMap.class);
          // extract Assertion information
          if (aim != null) {
              Collection<AssertionInfo> ais = aim.get(SP12Constants.SPNEGO_CONTEXT_TOKEN);
              if (ais == null || ais.isEmpty()) {
                  return;
              }
              if (isRequestor(message)) {
                  //client side should be checked on the way out
                  for (AssertionInfo ai : ais) {
                      ai.setAsserted(true);
                  }      
                  return;
              }
              String s = (String)message.get(SoapBindingConstants.SOAP_ACTION);
              AddressingProperties inProps = (AddressingProperties)message
                  .getContextualProperty(JAXWSAConstants.SERVER_ADDRESSING_PROPERTIES_INBOUND);
              if (inProps != null && s == null) {
                  //MS/WCF doesn't put a soap action out for this, must check the headers
                  s = inProps.getAction().getValue();
              }
  
              if (s != null 
                  && s.contains("/RST/Issue")
                  && (s.startsWith(STSUtils.WST_NS_05_02)
                      || s.startsWith(STSUtils.WST_NS_05_12))) {
  
                  Policy p = new Policy();
                  ExactlyOne ea = new ExactlyOne();
                  p.addPolicyComponent(ea);
                  All all = new All();
                  Assertion ass = NegotiationUtils.getAddressingPolicy(aim, false);
                  all.addPolicyComponent(ass);
                  ea.addPolicyComponent(all);
                  
                  //setup endpoint and forward to it.
                  unmapSecurityProps(message);
                  String ns = STSUtils.WST_NS_05_12;
                  if (s.startsWith(STSUtils.WST_NS_05_02)) {
                      ns = STSUtils.WST_NS_05_02;
                  }
                  NegotiationUtils.recalcEffectivePolicy(message, ns, p, new SpnegoSTSInvoker(), false);
              } else {
                  message.getInterceptorChain().add(SpnegoContextTokenFinderInterceptor.INSTANCE);
              }
          }
      }
      
      private void unmapSecurityProps(Message message) {
          Exchange ex = message.getExchange();
          for (String s : SecurityConstants.ALL_PROPERTIES) {
              Object v = message.getContextualProperty(s);
              if (v != null) {
                  ex.put(s, v);
              }
          }
      }
  
      public class SpnegoSTSInvoker extends STSInvoker {
  
          void doIssue(
              Element requestEl,
              Exchange exchange, 
              Element binaryExchange,
              W3CDOMStreamWriter writer,
              String prefix, 
              String namespace
          ) throws Exception {
              
              SpnegoTokenContext spnegoToken = 
                  handleBinaryExchange(binaryExchange, exchange.getInMessage(), namespace);
              
              writer.writeStartElement(prefix, "RequestSecurityTokenResponseCollection", namespace);
              writer.writeStartElement(prefix, "RequestSecurityTokenResponse", namespace);
              
              String context = requestEl.getAttributeNS(null, "Context");
              if (context != null && !"".equals(context)) {
                  writer.writeAttribute("Context", context);
              }
              
              // Find TokenType and KeySize
              int keySize = 256;
              String tokenType = null;
              Element el = DOMUtils.getFirstElement(requestEl);
              while (el != null) {
                  String localName = el.getLocalName();
                  if (namespace.equals(el.getNamespaceURI())) {
                      if ("KeySize".equals(localName)) {
                          keySize = Integer.parseInt(el.getTextContent());
                      } else if ("TokenType".equals(localName)) {
                          tokenType = el.getTextContent();
                      }
                  }
                  
                  el = DOMUtils.getNextElement(el);
              }
              
              // Check received KeySize
              if (keySize < 128 || keySize > 512) {
                  keySize = 256;
              }
              
              // TokenType
              writer.writeStartElement(prefix, "TokenType", namespace);
              writer.writeCharacters(tokenType);
              writer.writeEndElement();
              
              writer.writeStartElement(prefix, "RequestedSecurityToken", namespace);
              
              // SecurityContextToken
              SecurityContextToken sct =
                  new SecurityContextToken(
                      NegotiationUtils.getWSCVersion(tokenType), writer.getDocument()
                  );
              
              // Lifetime
              Date created = new Date();
              Date expires = new Date();
              expires.setTime(created.getTime() + 300000L);
              
              SecurityToken token = new SecurityToken(sct.getIdentifier(), created, expires);
              token.setToken(sct.getElement());
              token.setTokenType(sct.getTokenType());
              
              writer.getCurrentNode().appendChild(sct.getElement());
              writer.writeEndElement();        
              
              // References
              writer.writeStartElement(prefix, "RequestedAttachedReference", namespace);
              token.setAttachedReference(
                  writeSecurityTokenReference(writer, "#" + sct.getID(), tokenType)
              );
              writer.writeEndElement();
              
              writer.writeStartElement(prefix, "RequestedUnattachedReference", namespace);
              token.setUnattachedReference(
                  writeSecurityTokenReference(writer, sct.getIdentifier(), tokenType)
              );
              writer.writeEndElement();
              
              writeLifetime(writer, created, expires, prefix, namespace);
              
              // KeySize
              writer.writeStartElement(prefix, "KeySize", namespace);
              writer.writeCharacters("" + keySize);
              writer.writeEndElement();
              
              byte[] secret = WSSecurityUtil.generateNonce(keySize / 8);
              byte[] key = spnegoToken.wrapKey(secret);
              
              writeProofToken(writer, prefix, namespace, key);
  
              writer.writeEndElement();
              
              /*
              // Second RequestSecurityTokenResponse containing the Authenticator
              // TODO
              writer.writeStartElement(prefix, "RequestSecurityTokenResponse", namespace);
              if (context != null && !"".equals(context)) {
                  writer.writeAttribute("Context", context);
              }
              writeAuthenticator(writer, prefix, namespace, secret);
              writer.writeEndElement();
              */
              
              writer.writeEndElement();
              
              spnegoToken.clear();
              
              token.setSecret(secret);
              ((TokenStore)exchange.get(Endpoint.class).getEndpointInfo()
                      .getProperty(TokenStore.class.getName())).add(token);
          }
          
          private SpnegoTokenContext handleBinaryExchange(
              Element binaryExchange,
              Message message,
              String namespace
          ) throws Exception {
              if (binaryExchange == null) {
                  throw new Exception("No BinaryExchange element received");
              }
              String encoding = binaryExchange.getAttributeNS(null, "EncodingType");
              if (!BinarySecurity.BASE64_ENCODING.equals(encoding)) {
                  throw new Exception("Unknown encoding type: " + encoding);
              }
  
              String valueType = binaryExchange.getAttributeNS(null, "ValueType");
              if (!(namespace + "/spnego").equals(valueType)) {
                  throw new Exception("Unknown value type: " + valueType);
              }
  
              String content = DOMUtils.getContent(binaryExchange);
              byte[] decodedContent = Base64.decode(content);
              
              String jaasContext = 
                  (String)message.getContextualProperty(SecurityConstants.KERBEROS_JAAS_CONTEXT_NAME);
              String kerberosSpn = 
                  (String)message.getContextualProperty(SecurityConstants.KERBEROS_SPN);
              CallbackHandler callbackHandler = 
                  NegotiationUtils.getCallbackHandler(
                      message.getContextualProperty(SecurityConstants.CALLBACK_HANDLER), this.getClass()
                  );
  
              SpnegoTokenContext spnegoToken = new SpnegoTokenContext();
              spnegoToken.validateServiceTicket(
                  jaasContext, callbackHandler, kerberosSpn, decodedContent
              );
              return spnegoToken;
          }
          
          private void writeProofToken(
              W3CDOMStreamWriter writer,
              String prefix, 
              String namespace,
              byte[] key
          ) throws Exception {
              // RequestedProofToken
              writer.writeStartElement(prefix, "RequestedProofToken", namespace);
              
              // EncryptedKey
              writer.writeStartElement(WSConstants.ENC_PREFIX, "EncryptedKey", WSConstants.ENC_NS);
              writer.writeStartElement(WSConstants.ENC_PREFIX, "EncryptionMethod", WSConstants.ENC_NS);
              writer.writeAttribute("Algorithm", namespace + "/spnego#GSS_Wrap");
              writer.writeEndElement();
              writer.writeStartElement(WSConstants.ENC_PREFIX, "CipherData", WSConstants.ENC_NS);
              writer.writeStartElement(WSConstants.ENC_PREFIX, "CipherValue", WSConstants.ENC_NS);
  
              writer.writeCharacters(Base64.encode(key));
              
              writer.writeEndElement();
              writer.writeEndElement();
              writer.writeEndElement();
              
              writer.writeEndElement();
          }
          
          /*
          private void writeAuthenticator(
              W3CDOMStreamWriter writer,
              String prefix, 
              String namespace,
              byte[] secret
          ) throws Exception {
              // Authenticator
              writer.writeStartElement(prefix, "Authenticator", namespace);
              
              // CombinedHash
              writer.writeStartElement(prefix, "CombinedHash", namespace);
              
              P_SHA1 psha1 = new P_SHA1();
              byte[] seed = "AUTH-HASH".getBytes();
              byte[] digest = psha1.createKey(secret, seed, 0, 32);
              writer.writeCharacters(Base64.encode(digest));
              
              writer.writeEndElement();
              
              writer.writeEndElement();
          }
          */
  
      }
      
      
      static final class SpnegoContextTokenFinderInterceptor 
          extends AbstractPhaseInterceptor<SoapMessage> {
          
          static final SpnegoContextTokenFinderInterceptor INSTANCE 
              = new SpnegoContextTokenFinderInterceptor();
          
          private SpnegoContextTokenFinderInterceptor() {
              super(Phase.PRE_PROTOCOL);
              addAfter(WSS4JInInterceptor.class.getName());
          }
  
          public void handleMessage(SoapMessage message) throws Fault {
              boolean foundSCT = NegotiationUtils.parseSCTResult(message);
              
              AssertionInfoMap aim = message.get(AssertionInfoMap.class);
              // extract Assertion information
              if (aim != null) {
                  Collection<AssertionInfo> ais = aim.get(SP12Constants.SPNEGO_CONTEXT_TOKEN);
                  if (ais == null || ais.isEmpty()) {
                      return;
                  }
                  for (AssertionInfo inf : ais) {
                      if (foundSCT) {
                          inf.setAsserted(true);
                      } else {
                          inf.setNotAsserted("No SecurityContextToken token found in message.");
                      }
                  }
              }
          }
      }
      
  
      
  }