41    public static function validateElement(\DOMElement $root)
 
   47        $objXMLSecDSig->idKeys[] = 
'ID';
 
   50        $signatureElement = self::xpQuery($root, 
'./ds:Signature');
 
   51        if (count($signatureElement) === 0) {
 
   55        } elseif (count($signatureElement) > 1) {
 
   56            throw new \Exception(
'XMLSec: more than one signature element in root.');
 
   58        $signatureElement = $signatureElement[0];
 
   59        $objXMLSecDSig->sigNode = $signatureElement;
 
   62        $objXMLSecDSig->canonicalizeSignedInfo();
 
   65        if (!$objXMLSecDSig->validateReference()) {
 
   66            throw new \Exception(
'XMLsec: digest validation failed');
 
   72        foreach ($objXMLSecDSig->getValidatedNodes() as $signedNode) {
 
   73            if ($signedNode->isSameNode($root)) {
 
   76            } elseif ($root->parentNode instanceof \DOMDocument && $signedNode->isSameNode($root->ownerDocument)) {
 
   83            throw new \Exception(
'XMLSec: The root element is not signed.');
 
   88        foreach (self::xpQuery($signatureElement, 
'./ds:KeyInfo/ds:X509Data/ds:X509Certificate') as $certNode) {
 
   89            $certData = trim($certNode->textContent);
 
   90            $certData = str_replace(array(
"\r", 
"\n", 
"\t", 
' '), 
'', $certData);
 
   95            'Signature' => $objXMLSecDSig,
 
  114        assert(is_string($algorithm));
 
  115        assert(
$type === 
"public" || 
$type === 
"private");
 
  118        if (
$key->type === $algorithm) {
 
  122        if (!in_array($algorithm, array(
 
  123            XMLSecurityKey::RSA_1_5,
 
  124            XMLSecurityKey::RSA_SHA1,
 
  125            XMLSecurityKey::RSA_SHA256,
 
  126            XMLSecurityKey::RSA_SHA384,
 
  127            XMLSecurityKey::RSA_SHA512
 
  129            throw new \Exception(
'Unsupported signing algorithm.');
 
  132        $keyInfo = openssl_pkey_get_details(
$key->key);
 
  133        if ($keyInfo === 
false) {
 
  134            throw new \Exception(
'Unable to get key details from XMLSecurityKey.');
 
  136        if (!isset($keyInfo[
'key'])) {
 
  137            throw new \Exception(
'Missing key in public key details.');
 
  141        $newKey->loadKey($keyInfo[
'key']);
 
  158        assert(array_key_exists(
"Signature", 
$info));
 
  161        $objXMLSecDSig = 
$info[
'Signature'];
 
  163        $sigMethod = self::xpQuery($objXMLSecDSig->sigNode, 
'./ds:SignedInfo/ds:SignatureMethod');
 
  164        if (empty($sigMethod)) {
 
  165            throw new \Exception(
'Missing SignatureMethod element.');
 
  167        $sigMethod = $sigMethod[0];
 
  168        if (!$sigMethod->hasAttribute(
'Algorithm')) {
 
  169            throw new \Exception(
'Missing Algorithm-attribute on SignatureMethod element.');
 
  171        $algo = $sigMethod->getAttribute(
'Algorithm');
 
  173        if (
$key->type === XMLSecurityKey::RSA_SHA1 && 
$algo !== 
$key->type) {
 
  178        if ($objXMLSecDSig->verify(
$key) !== 1) {
 
  179            throw new \Exception(
"Unable to validate Signature");
 
  193        assert(is_string(
$query));
 
  194        static $xpCache = 
null;
 
  196        if ($node instanceof \DOMDocument) {
 
  199            $doc = $node->ownerDocument;
 
  202        if ($xpCache === 
null || !$xpCache->document->isSameNode($doc)) {
 
  203            $xpCache = new \DOMXPath($doc);
 
  204            $xpCache->registerNamespace(
'soap-env', Constants::NS_SOAP);
 
  205            $xpCache->registerNamespace(
'saml_protocol', Constants::NS_SAMLP);
 
  206            $xpCache->registerNamespace(
'saml_assertion', Constants::NS_SAML);
 
  207            $xpCache->registerNamespace(
'saml_metadata', Constants::NS_MD);
 
  208            $xpCache->registerNamespace(
'ds', XMLSecurityDSig::XMLDSIGNS);
 
  209            $xpCache->registerNamespace(
'xenc', XMLSecEnc::XMLENCNS);
 
  229    public static function copyElement(\DOMElement $element, \DOMElement $parent = 
null)
 
  231        if ($parent === 
null) {
 
  232            $document = DOMDocumentFactory::create();
 
  234            $document = $parent->ownerDocument;
 
  237        $namespaces = array();
 
  238        for ($e = $element; $e !== 
null; $e = $e->parentNode) {
 
  239            foreach (Utils::xpQuery($e, 
'./namespace::*') as $ns) {
 
  240                $prefix = $ns->localName;
 
  241                if ($prefix === 
'xml' || $prefix === 
'xmlns') {
 
  244                $uri = $ns->nodeValue;
 
  245                if (!isset($namespaces[$prefix])) {
 
  246                    $namespaces[$prefix] = $uri;
 
  252        $newElement = $document->importNode($element, 
true);
 
  253        if ($parent !== 
null) {
 
  255            $parent->appendChild($newElement);
 
  258        foreach ($namespaces as $prefix => $uri) {
 
  259            $newElement->setAttributeNS($uri, $prefix . 
':__ns_workaround__', 
'tmp');
 
  260            $newElement->removeAttributeNS($uri, 
'__ns_workaround__');
 
  276    public static function parseBoolean(\DOMElement $node, $attributeName, $default = 
null)
 
  278        assert(is_string($attributeName));
 
  280        if (!$node->hasAttribute($attributeName)) {
 
  283        $value = $node->getAttribute($attributeName);
 
  284        switch (strtolower($value)) {
 
  292                throw new \Exception(
'Invalid value of boolean attribute ' . var_export($attributeName, 
true) . 
': ' . var_export($value, 
true));
 
  316        assert(array_key_exists(
"Value", 
$nameId));
 
  320        $nid->value = 
$nameId[
'Value'];
 
  322        if (array_key_exists(
'NameQualifier', 
$nameId) && 
$nameId[
'NameQualifier'] !== 
null) {
 
  323            $nid->NameQualifier = 
$nameId[
'NameQualifier'];
 
  325        if (array_key_exists(
'SPNameQualifier', 
$nameId) && 
$nameId[
'SPNameQualifier'] !== 
null) {
 
  326            $nid->SPNameQualifier = 
$nameId[
'SPNameQualifier'];
 
  328        if (array_key_exists(
'Format', 
$nameId) && 
$nameId[
'Format'] !== 
null) {
 
  329            $nid->Format = 
$nameId[
'Format'];
 
  345        $ret = array(
'Value' => trim(
$xml->textContent));
 
  347        foreach (array(
'NameQualifier', 
'SPNameQualifier', 
'SPProvidedID', 
'Format') as $attr) {
 
  348            if (
$xml->hasAttribute($attr)) {
 
  349                $ret[$attr] = 
$xml->getAttribute($attr);
 
  368        \DOMNode $insertBefore = 
null 
  371        $objXMLSecDSig->setCanonicalMethod(XMLSecurityDSig::EXC_C14N);
 
  373        switch (
$key->type) {
 
  374            case XMLSecurityKey::RSA_SHA256:
 
  375                $type = XMLSecurityDSig::SHA256;
 
  377            case XMLSecurityKey::RSA_SHA384:
 
  378                $type = XMLSecurityDSig::SHA384;
 
  380            case XMLSecurityKey::RSA_SHA512:
 
  381                $type = XMLSecurityDSig::SHA512;
 
  384                $type = XMLSecurityDSig::SHA1;
 
  387        $objXMLSecDSig->addReferenceList(
 
  390            array(
'http://www.w3.org/2000/09/xmldsig#enveloped-signature', XMLSecurityDSig::EXC_C14N),
 
  391            array(
'id_name' => 
'ID', 
'overwrite' => 
false)
 
  394        $objXMLSecDSig->sign(
$key);
 
  400        $objXMLSecDSig->insertSignature($root, $insertBefore);
 
  414    private static function doDecryptElement(\DOMElement $encryptedData, 
XMLSecurityKey $inputKey, array &$blacklist)
 
  418        $enc->setNode($encryptedData);
 
  419        $enc->type = $encryptedData->getAttribute(
"Type");
 
  421        $symmetricKey = $enc->locateKey($encryptedData);
 
  422        if (!$symmetricKey) {
 
  423            throw new \Exception(
'Could not locate key algorithm in encrypted data.');
 
  426        $symmetricKeyInfo = $enc->locateKeyInfo($symmetricKey);
 
  427        if (!$symmetricKeyInfo) {
 
  428            throw new \Exception(
'Could not locate <dsig:KeyInfo> for the encrypted key.');
 
  432        if ($symmetricKeyInfo->isEncrypted) {
 
  433            $symKeyInfoAlgo = $symmetricKeyInfo->getAlgorithm();
 
  435            if (in_array($symKeyInfoAlgo, $blacklist, 
true)) {
 
  436                throw new \Exception(
'Algorithm disabled: ' . var_export($symKeyInfoAlgo, 
true));
 
  439            if ($symKeyInfoAlgo === XMLSecurityKey::RSA_OAEP_MGF1P && $inputKeyAlgo === XMLSecurityKey::RSA_1_5) {
 
  446                $inputKeyAlgo = XMLSecurityKey::RSA_OAEP_MGF1P;
 
  450            if ($inputKeyAlgo !== $symKeyInfoAlgo) {
 
  451                throw new \Exception(
 
  452                    'Algorithm mismatch between input key and key used to encrypt ' .
 
  453                    ' the symmetric key for the message. Key was: ' .
 
  454                    var_export($inputKeyAlgo, 
true) . 
'; message was: ' .
 
  455                    var_export($symKeyInfoAlgo, 
true)
 
  460            $encKey = $symmetricKeyInfo->encryptedCtx;
 
  461            $symmetricKeyInfo->key = $inputKey->key;
 
  464            if ($keySize === 
null) {
 
  468                throw new \Exception(
'Unknown key size for encryption algorithm: ' . var_export($symmetricKey->type, 
true));
 
  472                $key = $encKey->decryptKey($symmetricKeyInfo);
 
  473                if (strlen(
$key) != $keySize) {
 
  474                    throw new \Exception(
 
  475                        'Unexpected key size (' . strlen(
$key) * 8 . 
'bits) for encryption algorithm: ' .
 
  476                        var_export($symmetricKey->type, 
true)
 
  479            } 
catch (\Exception $e) {
 
  481                Utils::getContainer()->getLogger()->error(
'Failed to decrypt symmetric key: ' . $e->getMessage());
 
  487                $encryptedKey = $encKey->getCipherValue();
 
  488                $pkey = openssl_pkey_get_details($symmetricKeyInfo->key);
 
  489                $pkey = sha1(serialize($pkey), 
true);
 
  490                $key = sha1($encryptedKey . $pkey, 
true);
 
  493                if (strlen(
$key) > $keySize) {
 
  495                } elseif (strlen(
$key) < $keySize) {
 
  499            $symmetricKey->loadkey(
$key);
 
  501            $symKeyAlgo = $symmetricKey->getAlgorithm();
 
  503            if ($inputKeyAlgo !== $symKeyAlgo) {
 
  504                throw new \Exception(
 
  505                    'Algorithm mismatch between input key and key in message. ' .
 
  506                    'Key was: ' . var_export($inputKeyAlgo, 
true) . 
'; message was: ' .
 
  507                    var_export($symKeyAlgo, 
true)
 
  510            $symmetricKey = $inputKey;
 
  514        if (in_array($algorithm, $blacklist, 
true)) {
 
  515            throw new \Exception(
'Algorithm disabled: ' . var_export($algorithm, 
true));
 
  519        $decrypted = $enc->decryptNode($symmetricKey, 
false);
 
  526        $xml = 
'<root xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" '.
 
  527                     'xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">' .
 
  532            $newDoc = DOMDocumentFactory::fromString(
$xml);
 
  533        } 
catch (RuntimeException $e) {
 
  534            throw new \Exception(
'Failed to parse decrypted XML. Maybe the wrong sharedkey was used?', 0, $e);
 
  537        $decryptedElement = $newDoc->firstChild->firstChild;
 
  538        if ($decryptedElement === 
null) {
 
  539            throw new \Exception(
'Missing encrypted element.');
 
  542        if (!($decryptedElement instanceof \DOMElement)) {
 
  543            throw new \Exception(
'Decrypted element was not actually a \DOMElement.');
 
  546        return $decryptedElement;
 
  561            return self::doDecryptElement($encryptedData, $inputKey, $blacklist);
 
  562        } 
catch (\Exception $e) {
 
  567            Utils::getContainer()->getLogger()->error(
'Decryption failed: ' . $e->getMessage());
 
  568            throw new \Exception(
'Failed to decrypt XML element.', 0, $e);
 
  582        assert(is_string($namespaceURI));
 
  583        assert(is_string($localName));
 
  586        for ($node = $parent->firstChild; $node !== 
null; $node = $node->nextSibling) {
 
  587            if ($node->namespaceURI !== $namespaceURI || $node->localName !== $localName) {
 
  591            if ($node->hasAttribute(
'xml:lang')) {
 
  592                $language = $node->getAttribute(
'xml:lang');
 
  596            $ret[$language] = trim($node->textContent);
 
  610    public static function extractStrings(\DOMElement $parent, $namespaceURI, $localName)
 
  612        assert(is_string($namespaceURI));
 
  613        assert(is_string($localName));
 
  616        for ($node = $parent->firstChild; $node !== 
null; $node = $node->nextSibling) {
 
  617            if ($node->namespaceURI !== $namespaceURI || $node->localName !== $localName) {
 
  620            $ret[] = trim($node->textContent);
 
  638        assert(is_string(
$name));
 
  639        assert(is_string($value));
 
  641        $doc = $parent->ownerDocument;
 
  644        $n->appendChild($doc->createTextNode($value));
 
  645        $parent->appendChild(
$n);
 
  662        assert(is_string(
$name));
 
  663        assert(is_bool($localized));
 
  665        $doc = $parent->ownerDocument;
 
  667        foreach ($values as 
$index => $value) {
 
  669            $n->appendChild($doc->createTextNode($value));
 
  671                $n->setAttribute(
'xml:lang', 
$index);
 
  673            $parent->appendChild(
$n);
 
  685        assert(is_string($x509Data));
 
  688        $x509Certificate->certificate = $x509Data;
 
  691        $x509Data->data[] = $x509Certificate;
 
  694        $keyInfo->info[] = $x509Data;
 
  697        $keyDescriptor->KeyInfo = $keyInfo;
 
  699        return $keyDescriptor;
 
  726        $regex = 
'/^(\\d\\d\\d\\d)-(\\d\\d)-(\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)(?:\\.\\d{1,9})?Z$/D';
 
  727        if (preg_match($regex, 
$time, $matches) == 0) {
 
  728            throw new \Exception(
 
  729                'Invalid SAML2 timestamp passed to xsDateTimeToTimestamp: ' . 
$time 
  735        $year   = intval($matches[1]);
 
  736        $month  = intval($matches[2]);
 
  737        $day    = intval($matches[3]);
 
  738        $hour   = intval($matches[4]);
 
  739        $minute = intval($matches[5]);
 
  740        $second = intval($matches[6]);
 
  744        $ts = gmmktime($hour, $minute, $second, $month, $day, $year);
 
  754        return ContainerSingleton::getInstance();
 
An exception for terminatinating execution or to throw for unit testing.
getSymmetricKeySize()
Retrieve the key size for the symmetric encryption algorithm.
static createKeyDescriptor($x509Data)
Create a KeyDescriptor with the given certificate.
static xpQuery(\DOMNode $node, $query)
Do an XPath query on an XML node.
static addNameId(\DOMElement $node, array $nameId)
Create a NameID element.
static addString(\DOMElement $parent, $namespace, $name, $value)
Append string element.
static castKey(XMLSecurityKey $key, $algorithm, $type='public')
Helper function to convert a XMLSecurityKey to the correct algorithm.
static parseBoolean(\DOMElement $node, $attributeName, $default=null)
Parse a boolean attribute.
static decryptElement(\DOMElement $encryptedData, XMLSecurityKey $inputKey, array $blacklist=array())
Decrypt an encrypted element.
static addStrings(\DOMElement $parent, $namespace, $name, $localized, array $values)
Append string elements.
static extractLocalizedStrings(\DOMElement $parent, $namespaceURI, $localName)
Extract localized strings from a set of nodes.
static xsDateTimeToTimestamp($time)
This function converts a SAML2 timestamp on the form yyyy-mm-ddThh:mm:ss(.s+)?Z to a UNIX timestamp.
static parseNameId(\DOMElement $xml)
Parse a NameID element.
static extractStrings(\DOMElement $parent, $namespaceURI, $localName)
Extract strings from a set of nodes.
static insertSignature(XMLSecurityKey $key, array $certificates, \DOMElement $root, \DOMNode $insertBefore=null)
Insert a Signature-node.
if($err=$client->getError()) $namespace
if(@file_exists(dirname(__FILE__).'/lang/eng.php')) $certificate