/**
* Check the Signature in a XML element.
*
* This function expects the XML element to contain a Signature-element
* which contains a reference to the XML-element. This is common for both
* messages and assertions.
*
* Note that this function only validates the element itself. It does not
* check this against any local keys.
*
* If no Signature-element is located, this function will return FALSE. All
* other validation errors result in an exception. On successful validation
* an array will be returned. This array contains the information required to
* check the signature against a public key.
*
* @param DOMElement $root The element which should be validated.
* @return array|bool An array with information about the Signature-element.
* @throws Exception
*/
public static function validateElement(DOMElement $root)
{
/* Create an XML security object. */
$objXMLSecDSig = new XMLSecurityDSig();
/* Both SAML messages and SAML assertions use the 'ID' attribute. */
$objXMLSecDSig->idKeys[] = 'ID';
/* Locate the XMLDSig Signature element to be used. */
$signatureElement = self::xpQuery($root, './ds:Signature');
if (count($signatureElement) === 0) {
/* We don't have a signature element ot validate. */
return FALSE;
} elseif (count($signatureElement) > 1) {
throw new Exception('XMLSec: more than one signature element in root.');
}
$signatureElement = $signatureElement[0];
$objXMLSecDSig->sigNode = $signatureElement;
/* Canonicalize the XMLDSig SignedInfo element in the message. */
$objXMLSecDSig->canonicalizeSignedInfo();
/* Validate referenced xml nodes. */
if (!$objXMLSecDSig->validateReference()) {
throw new Exception('XMLsec: digest validation failed');
}
/* Check that $root is one of the signed nodes. */
$rootSigned = FALSE;
/** @var DOMNode $signedNode */
foreach ($objXMLSecDSig->getValidatedNodes() as $signedNode) {
if ($signedNode->isSameNode($root)) {
$rootSigned = TRUE;
break;
} elseif ($root->parentNode instanceof DOMDocument && $signedNode->isSameNode($root->ownerDocument)) {
/* $root is the root element of a signed document. */
$rootSigned = TRUE;
break;
}
}
if (!$rootSigned) {
throw new Exception('XMLSec: The root element is not signed.');
}
/* Now we extract all available X509 certificates in the signature element. */
$certificates = array();
foreach (self::xpQuery($signatureElement, './ds:KeyInfo/ds:X509Data/ds:X509Certificate') as $certNode) {
$certData = trim($certNode->textContent);
$certData = str_replace(array("\r", "\n", "\t", ' '), '', $certData);
$certificates[] = $certData;
}
$ret = array('Signature' => $objXMLSecDSig, 'Certificates' => $certificates);
return $ret;
}