/**
* Copyright (c) 2011, SOCIETIES Consortium (WATERFORD INSTITUTE OF TECHNOLOGY (TSSG), HERIOT-WATT UNIVERSITY (HWU), SOLUTA.NET
* (SN), GERMAN AEROSPACE CENTRE (Deutsches Zentrum fuer Luft- und Raumfahrt e.V.) (DLR), Zavod za varnostne tehnologije
* informacijske družbe in elektronsko poslovanje (SETCCE), INSTITUTE OF COMMUNICATION AND COMPUTER SYSTEMS (ICCS), LAKE
* COMMUNICATIONS (LAKE), INTEL PERFORMANCE LEARNING SOLUTIONS LTD (INTEL), PORTUGAL TELECOM INOVAÇÃO, SA (PTIN), IBM Corp.,
* INSTITUT TELECOM (ITSUD), AMITEC DIACHYTI EFYIA PLIROFORIKI KAI EPIKINONIES ETERIA PERIORISMENIS EFTHINIS (AMITEC), TELECOM
* ITALIA S.p.a.(TI), TRIALOG (TRIALOG), Stiftelsen SINTEF (SINTEF), NEC EUROPE LTD (NEC))
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following
* conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
* SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.societies.security.digsig.certs;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.apache.xml.security.keys.KeyInfo;
import org.apache.xml.security.signature.SignedInfo;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.signature.XMLSignatureException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.societies.api.security.digsig.DigsigException;
import org.societies.security.digsig.util.XmlManipulator;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* @author Miroslav Pavleski, Mitja Vardjan
*/
public class SignatureCheck {
private static Logger LOG = LoggerFactory.getLogger(SignatureCheck.class);
private static final String SOP_XPATH = "/societies/serviceOperationPolicy";
private Document doc;
private XmlManipulator xml;
private List<XMLSignature> signatures = new ArrayList<XMLSignature>();
private boolean signaturesExtracted = false;
private XMLSignature customerSignature;
private X509Certificate customerCert;
public SignatureCheck(Document doc) {
LOG.debug("constructor()");
this.doc = doc;
xml = new XmlManipulator();
xml.setDocument(doc);
}
/**
* Checks that the provider signature is valid
*
* @return All valid signatures. Key = ID of the valid signature. Value = certificate.
* @throws DigsigException On any error, or if any signature is invalid.
*/
public HashMap<String, X509Certificate> verifyAllSignatures() throws DigsigException {
// Element sopElem = extractElement();
// String sopElemId = sopElem.getAttribute("Id");
HashMap<String, X509Certificate> validSigs = new HashMap<String, X509Certificate>();
// if (sopElemId == null) {
// // <serviceOperaitonPolicy> element must have an Id
// throw new DigsigException("sopElemId is null");
// }
// String sopElemRef = "#" + sopElemId;
// Make sure signatures were extracted
extractSignatures();
for (XMLSignature sig : signatures) {
LOG.debug("verifyAllSignatures(): Verifying signature {}", sig.getId());
// if (hasReference(sig, sopElemRef)) {
LOG.debug("verifyAllSignatures(): hasReference = true");
try {
KeyInfo keyInfo = sig.getKeyInfo();
X509Certificate sigCert = keyInfo.getX509Certificate();
if (sigCert == null) {
throw new DigsigException("sigCert is null");
}
boolean result = sig.checkSignatureValue(sigCert.getPublicKey());
if (!result) {
throw new DigsigException("BAD_REQUEST");
}
validSigs.put(sig.getId(), sigCert);
LOG.debug("verifyAllSignatures(): successfully verified signature {}", sig.getId());
} catch (DigsigException e) {
throw e;
} catch (XMLSignatureException e) {
throw new DigsigException(e, "BAD_REQUEST");
} catch (Exception e) {
throw new DigsigException(e, "INTERNAL_SERVER_ERROR");
}
// }
}
return validSigs;
}
/**
* Verifies consumer identity, validates the signature and returns the
* selected SOP element
*
* @return The referenced SOP element
* @throws DigsigException
*/
public Element getReferencedSOP() throws DigsigException {
// make sure the signatures are extracted
extractSignatures();
// we'll need this in order to establish SOP correct structure
Element sopElem = extractElement(SOP_XPATH);
String sopElemId = sopElem.getAttribute("Id");
LOG.debug("getReferencedSOP(): sopElemId = {}", sopElemId);
if (sopElemId == null) {
// <serviceOperaitonPolicy> element must have an Id
throw new DigsigException("sopElemId is null");
}
String sopElemRef = "#" + sopElemId;
for (XMLSignature sig : signatures) {
if (!hasReference(sig, sopElemRef)) {
// Signature must have only one reference referencing the
// selected SOP
String ref = extractSOPReference(sig);
// The sig element must have and Id
String sigId = sig.getElement().getAttribute("Id");
LOG.debug("getReferencedSOP(): sigId = {}", sigId);
if (sigId == null)
throw new DigsigException("sigId is null");
String refId = ref.substring(1);
Node sopNode = xml.getNode(String.format(
"/societies/serviceOperationPolicy/sop[@Id='%s']",
refId));
if (sopNode == null || !(sopNode instanceof Element)) {
// check that this is actual SOP
throw new DigsigException("sopNode is null");
}
if (!(sopNode instanceof Element)) {
// check that this is actual SOP
throw new DigsigException("sopNode not instance of Element");
}
try {
X509Certificate sigCert = sig.getKeyInfo()
.getX509Certificate();
if (sigCert == null)
throw new DigsigException("sigCert is null");
// TODO check consumer identity...
boolean result = sig.checkSignatureValue(sigCert.getPublicKey());
customerSignature = sig;
customerCert = sigCert;
if (result)
return (Element) sopNode;
} catch (XMLSignatureException e) {
throw new DigsigException(e, "could not check signature value");
} catch (Exception e) {
throw new DigsigException(e, "INTERNAL_SERVER_ERROR");
}
}
}
throw new DigsigException("BAD_REQUEST");
}
public XMLSignature getCustomerSignature() throws DigsigException {
if (customerSignature == null) {
getReferencedSOP();
}
return customerSignature;
}
public X509Certificate getCustomerCert() {
return customerCert;
}
/**
* Extracts all detached signatures under the document element
*
* @return true if successful
* @throws DigsigException
*/
private void extractSignatures() throws DigsigException {
if (signaturesExtracted)
return;
if (doc == null || doc.getDocumentElement() == null)
throw new DigsigException("doc or doc.getDocumentElement() is null");
NodeList childNodes = doc.getDocumentElement().getChildNodes();
if (childNodes == null || childNodes.getLength() == 0)
throw new DigsigException("no child nodes found");
// int numServiceOperationPolicyNodes = 0;
// This loop ensures that all subelements to the document element are
// either serviceOperationPolicy or ds:Signature
for (int i = 0; i < childNodes.getLength(); i++) {
Node childNode = childNodes.item(i);
if (!(childNode instanceof Element))
continue;
Element elem = (Element) childNode;
String elemName = elem.getLocalName();
String elemNS = elem.getNamespaceURI();
if ("http://www.w3.org/2000/09/xmldsig#".equals(elemNS)
&& "Signature".equals(elemName)) {
try {
XMLSignature sig = new XMLSignature(elem, null);
signatures.add(sig);
LOG.debug("extractSignatures(): extracted signature {}",
sig.getElement().getAttribute("Id"));
} catch (Exception e) {
throw new DigsigException(e, "could not extract signature");
}
// } else if ("serviceOperationPolicy".equals(elemName)
// && elemNS == null) {
// numServiceOperationPolicyNodes++;
// if (numServiceOperationPolicyNodes > 1)
// throw new DigsigException("numServiceOperationPolicyNodes is more than 1");
// } else {
// throw new DigsigException("BAD_REQUEST");
}
}
signaturesExtracted = true;
}
private Element extractElement(String xpath) throws DigsigException {
Node node = xml.getNode(xpath);
if (node == null)
throw new DigsigException("node is null");
if (!(node instanceof Element))
throw new DigsigException("node not instance of Element");
return (Element) node;
}
/**
* Checks if given XML Digital Signature references an Id element
*
* @param sig
* @param ref
* @return
*/
private static boolean hasReference(XMLSignature sig, String ref) {
LOG.debug("hasReference({}, {})", sig, ref);
SignedInfo si = sig.getSignedInfo();
if (si == null || si.getElement() == null)
return false;
// Loop through all reference nodes
NodeList list = si.getElement().getChildNodes();
if (list == null || list.getLength() == 0)
return false;
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node == null || !(node instanceof Element))
continue;
Element elem = (Element) node;
if ("http://www.w3.org/2000/09/xmldsig#".equals(elem
.getNamespaceURI())
&& "Reference".equals(elem.getLocalName())) {
if (ref.equals(elem.getAttribute("URI")))
return true;
}
}
return false;
}
private static String extractSOPReference(XMLSignature sig) {
SignedInfo si = sig.getSignedInfo();
if (si == null || si.getElement() == null)
return null;
// Loop through all reference nodes
NodeList list = si.getElement().getChildNodes();
if (list == null) {
// only one reference
return null;
}
int refElements = 0;
String result = null;
for (int i = 0; i < list.getLength(); i++) {
Node node = list.item(i);
if (node == null || !(node instanceof Element))
continue;
Element elem = (Element) node;
if ("http://www.w3.org/2000/09/xmldsig#".equals(elem
.getNamespaceURI())
&& "Reference".equals(elem.getLocalName())) {
// count elements
refElements++;
if (refElements > 1) {
// if more than one it is an error
return null;
}
result = elem.getAttribute("URI");
}
}
return result;
}
}