package web.header;
/*
* Copyright 2004, 2005 The Apache Software Foundation
*
* Licensed 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.
*
* $Header:$Factory
*/
/*
* Note: This sample was developed based on the example in the article:
* http://www-128.ibm.com/developerworks/webservices/library/ws-tip-extend/
*/
import java.util.Iterator;
import java.util.Map;
import javax.xml.namespace.QName;
import javax.xml.rpc.handler.Handler;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.soap.Name;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPException;
import javax.xml.soap.SOAPHeader;
import javax.xml.soap.SOAPHeaderElement;
import javax.xml.soap.SOAPMessage;
import javax.xml.soap.SOAPPart;
/**
* Provides common behavior for client & service handlers,
* generalized as outgoing and incoming message processing.
*/
abstract class SignHandler implements Handler
{
/**
* Header element namespace
*/
private static final String SIGN_NS_URI
= "uri://org.example.webservices.signature.Sign";
/**
* Header element namespace prefix
*/
private static final String SIGN_PREFIX
= "sign";
/**
* Header element
*/
private static final String SIGN_ELEMENT
= "sign";
/**
* Header element qualifed name
*/
private static final QName SIGN_HEADER
= new QName(SIGN_NS_URI, SIGN_ELEMENT);
private SignatureTool signatureTool
= new SignatureToolImpl();
private HandlerInfo info;
/**
* Name of required property, in HandlerInfo handler configuration,
* that specifies who signs outgoing messages.
*/
public static final String SIGNERS_NAME_PROPERTY
= "org.example.webservices.signature.SignersName";
/**
* Set of SOAP headers that this handler knows how to process.
* It is expected that this is set on the HandlerInfo for
* instances of this handler's subclasses.
*/
public static final QName[] HEADERS
= new QName[] { SIGN_HEADER };
public void init(HandlerInfo config) {
info = config;
}
public void destroy() {}
public QName[] getHeaders() { return info.getHeaders(); }
/**
* Obtain signers name from handler configuration (HandlerInfo),
* and sign message.
* @throws SignException if SIGNERS_NAME_PROPERTY is not available
* in the handler config.
*/
public void signOutgoing(SOAPMessageContext mc) throws SignException {
/**
* SIGNERS_NAME_PROPERTY is required to be on the configuration.
*/
Map config = info.getHandlerConfig();
if (config == null) {
throw new SignException("Signers Name not provided");
}
Object nameObj = config.get(SIGNERS_NAME_PROPERTY);
if (!(nameObj instanceof String)) {
throw new SignException("Signers Name not provided");
}
String name = (String)nameObj;
try {
/**
* Dig down into message, locate or create header block.
*/
SOAPMessage msg = mc.getMessage();
SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
SOAPHeader header = envelope.getHeader();
if (header == null) {
header = envelope.addHeader();
}
/**
* Create new header element.
* Use simple-security as role
*/
SOAPHeaderElement headerElement
= (SOAPHeaderElement)header.addChildElement(SIGN_ELEMENT,
SIGN_PREFIX,
SIGN_NS_URI);
headerElement.setActor("simple-security");
// Just sign with name in the header, leave the content alone
// /**
// * Locate portion of message content that is to be signed.
// */
// SOAPElement content = getContent(part);
/**
* Create new element representing signature,
* and add as child to new header element.
*/
SOAPElement element = signatureTool.getSignature(name); //, content);
headerElement.addChildElement(element);
} catch (SOAPException e) {
e.printStackTrace();
throw new SignException("Unable to sign message", e);
}
}
/**
* Look for signature on incoming message.
* If signature not found, then continue message processing.
* If signature is found, then verify that it is "correct".
* If correct, then continue message processing.
* If not correct, then throw SignException.
*/
public void checkIncoming(SOAPMessageContext mc) throws SignException {
int validSignatures = 0;
boolean expectedSignature = false;
try {
SOAPMessage msg = mc.getMessage();
SOAPPart part = msg.getSOAPPart();
SOAPEnvelope envelope = part.getEnvelope();
// /**
// * Locate portion of message content that is to be signed.
// */
// SOAPElement content = getContent(part);
/**
* Dig down through SOAP headers looking for matches to
* SIGN_HEADER, that are also targeted for this SOAP Node.
*/
SOAPHeader header = envelope.getHeader();
if (header != null) {
/**
* The roles the node acts in are specified by the
* MessageContext.getRoles() method. This code
* examines each role independently.
*/
String[] roles = mc.getRoles();
/**
* If no roles are found, then default to
* "ultimate destination".
*/
if (roles == null || roles.length == 0) {
System.out.println("checkIncoming: no roles, " +
"default to ultimate destination");
roles = new String[] { "" };
}
/**
* Examine headers bound to each role this node acts in.
*/
for (int ridx = 0; ridx < roles.length; ridx++) {
String role = roles[ridx];
System.out.println("checkIncoming: role == \"" +
role + "\"");
/**
* Headers are determined to be targeted for a SOAP Node by
* matching the node's roles with the header's actor role.
*
* So now go through list of headers associated with
* the role we are currently working on.
*/
Iterator headerElementIter
= header.examineHeaderElements(role);
while (headerElementIter.hasNext()) {
SOAPHeaderElement headerElement
= (SOAPHeaderElement)headerElementIter.next();
/**
* Is header recognized by this handler?
*/
Name headerElementName
= headerElement.getElementName();
if (equals(headerElementName, SIGN_HEADER)) {
System.out.println("checkIncoming: header match" +
": \"" +
elementName(headerElementName) +
"\"");
/**
* We got this far, we expect a signature now.
*/
expectedSignature = true;
/**
* Look for SOAPElement(s) in header,
* ignoring mixed content.
*/
Iterator headerIter
= headerElement.getChildElements();
while (headerIter.hasNext()) {
Object elementObj = headerIter.next();
if (elementObj instanceof SOAPElement) {
SOAPElement element
= (SOAPElement)elementObj;
signatureTool.isSignatureValid(element); //,content);
validSignatures++;
}
}
}
}
}
} else {
System.out.println("Header not found!");
}
} catch (SOAPException e) {
e.printStackTrace();
throw new SignException("Unable to verify signature", e);
}
/**
* Throw an exception if we ever got to a point where we
* expected a signature, but then we never found one
* [this logic is weak, but sufficient].
*/
if (expectedSignature && validSignatures < 1) {
throw new SignException("Expected signature(s) not found");
}
}
/**
* Compare two qnames
*/
private boolean equals(Name name, QName qname) {
return (name == null && qname == null) ||
(name.getURI().equals(qname.getNamespaceURI()) &&
name.getLocalName().equals(qname.getLocalPart()));
}
// /**
// * Helper method, return content of SOAPPart that we want to sign.
// */
// private SOAPElement getContent(SOAPPart part) throws SOAPException {
// SOAPEnvelope envelope = part.getEnvelope();
// return envelope.getBody();
// }
/**
* Return name of SOAPElement as a String.
*/
private String elementName(SOAPElement e) {
return elementName(e.getElementName());
}
/**
* Return name of SOAPElement as a String.
*/
private String elementName(Name name) {
return "{" + name.getURI() + "}" + name.getLocalName();
}
}