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_SHA256 &&
$algo !== $key->type) {
174 $key = self::castKey($key,
$algo);
178 if ($objXMLSecDSig->verify($key) !== 1) {
179 throw new \Exception(
"Unable to validate Signature");
193 assert(is_string(
$query));
194 static $xpCache = null;
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);
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__');
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);
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);
397 $objXMLSecDSig->add509Cert($certificate,
true);
400 $objXMLSecDSig->insertSignature($root, $insertBefore);
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)
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) {
494 $key = substr($key, 0, $keySize);
495 } elseif (strlen($key) < $keySize) {
496 $key = str_pad($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);
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);
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');
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();
if($err=$client->getError()) $namespace
getSymmetricKeySize()
Retrieve the key size for the symmetric encryption algorithm.
static extractLocalizedStrings(\DOMElement $parent, $namespaceURI, $localName)
Extract localized strings from a set of nodes.
static extractStrings(\DOMElement $parent, $namespaceURI, $localName)
Extract strings from a set of nodes.
if(@file_exists(dirname(__FILE__).'/lang/eng.php')) $certificate
static insertSignature(XMLSecurityKey $key, array $certificates, \DOMElement $root, \DOMNode $insertBefore=null)
Insert a Signature-node.
static decryptElement(\DOMElement $encryptedData, XMLSecurityKey $inputKey, array $blacklist=array())
Decrypt an encrypted element.
static createKeyDescriptor($x509Data)
Create a KeyDescriptor with the given certificate.
static addStrings(\DOMElement $parent, $namespace, $name, $localized, array $values)
Append string elements.
static addString(\DOMElement $parent, $namespace, $name, $value)
Append string element.
static parseBoolean(\DOMElement $node, $attributeName, $default=null)
Parse a boolean attribute.
static xpQuery(\DOMNode $node, $query)
Do an XPath query on an XML node.
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 castKey(XMLSecurityKey $key, $algorithm, $type='public')
Helper function to convert a XMLSecurityKey to the correct algorithm.
static addNameId(\DOMElement $node, array $nameId)
Create a NameID element.