View Javadoc

1   package org.astrogrid.security;
2   
3   import java.io.ByteArrayOutputStream;
4   import org.apache.axis.AxisFault;
5   import org.apache.axis.MessageContext;
6   import org.apache.axis.SOAPPart;
7   import org.apache.axis.handlers.BasicHandler;
8   import org.apache.log4j.Logger;
9   import org.apache.xml.security.c14n.Canonicalizer;
10  import org.apache.xml.security.Init;
11  import org.astrogrid.security.wsse.WsseSignature;
12  import org.w3c.dom.Document;
13  
14  /***
15   * An Axis message-handler to sign the SOAP body of a message.
16   * It is adapted from the WSDoAllSender class in WSS4J, written by
17   * Werner Dittmann.
18   *
19   * @author Guy Rixon
20   */ 
21  public class AxisClientCredentialHandler extends BasicHandler {
22    
23    private static Logger log = 
24        Logger.getLogger("org.astrogrid.security.AxisClientCredentialHandler");
25    
26    public void invoke(MessageContext msgContext) throws AxisFault {
27      
28      // Sign only outgoing messages.
29      if (msgContext.getPastPivot()) {
30        return;
31      }
32      
33      // Turn all exceptions into SOAP faults.
34      try {
35        
36        // Do nothing unless authentication is turned on.
37        Boolean authenticate = (Boolean)msgContext.getProperty("org.astrogrid.security.authenticate");
38        if (authenticate == null || authenticate.booleanValue() == false) {
39          return;
40        }
41        
42        // When Axis finally serializes the message in the connection to the
43        // service it likes to play around with the XML; this invalidates the
44        // signature. Tell it not to meddle.
45        msgContext.setProperty("disablePrettyXML", Boolean.TRUE);
46        
47        // Retrieve the user credentials set up for this handler.
48        SecurityGuard guard = (SecurityGuard)msgContext.getProperty("org.astrogrid.security.guard");
49        if (guard == null) {
50          throw new Exception("No security information was given.");
51        }
52      
53        // Get the SOAP envelop as a DOM.
54        Document envelope =
55            msgContext.getCurrentMessage().getSOAPEnvelope().getAsDocument();
56        if (envelope == null) {
57          throw new Exception("SOAP Envelope is null");
58        }
59        
60        // Sign the message using WSS4J. By default, the WSSignEnvelope signs the
61        // the SOAP body as a whole, which is correct for this use case.
62        Init.init();
63        WsseSignature signature = new WsseSignature(envelope, null);
64        signature.sign(guard);
65        
66        // Copy the signed message into the Axis engine. The canonicalizer
67        // doesn't add the XML prologue-line. That would be OK, but Axis then 
68        // muddles the length of the message in HTTP transmissions. Therefore,
69        // add the XML prologue explicitly here.
70        Canonicalizer canonicalizer 
71            = Canonicalizer.getInstance(Canonicalizer.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
72        ByteArrayOutputStream os = new ByteArrayOutputStream();
73        os.write("<?xml version='1.0'?>".getBytes());
74        os.write(canonicalizer.canonicalizeSubtree(envelope));
75        SOAPPart sp =
76            (org.apache.axis.SOAPPart)(msgContext.getCurrentMessage().getSOAPPart());
77        sp.setCurrentMessage(os.toByteArray(), SOAPPart.FORM_BYTES);
78      }
79      catch (Throwable t) {
80        t.printStackTrace();
81        log.error(t);
82        throw new AxisFault("Failed to sign a request message", t);
83      }
84    }
85  }