ILIAS  release_5-3 Revision v5.3.23-19-g915713cf615
XMLSecEnc.php
Go to the documentation of this file.
1<?php
3
4use DOMDocument;
5use DOMNode;
6use DOMXPath;
7use Exception;
9
51{
52 const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
53 <xenc:CipherData>
54 <xenc:CipherValue></xenc:CipherValue>
55 </xenc:CipherData>
56</xenc:EncryptedData>";
57
58 const Element = 'http://www.w3.org/2001/04/xmlenc#Element';
59 const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
60 const URI = 3;
61 const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
62
64 private $encdoc = null;
65
67 private $rawNode = null;
68
70 public $type = null;
71
73 public $encKey = null;
74
76 private $references = array();
77
78 public function __construct()
79 {
80 $this->_resetTemplate();
81 }
82
83 private function _resetTemplate()
84 {
85 $this->encdoc = new DOMDocument();
86 $this->encdoc->loadXML(self::template);
87 }
88
95 public function addReference($name, $node, $type)
96 {
97 if (! $node instanceOf DOMNode) {
98 throw new Exception('$node is not of type DOMNode');
99 }
100 $curencdoc = $this->encdoc;
101 $this->_resetTemplate();
103 $this->encdoc = $curencdoc;
105 $element = $encdoc->documentElement;
106 $element->setAttribute("Id", $refuri);
107 $this->references[$name] = array("node" => $node, "type" => $type, "encnode" => $encdoc, "refuri" => $refuri);
108 }
109
113 public function setNode($node)
114 {
115 $this->rawNode = $node;
116 }
117
127 public function encryptNode($objKey, $replace = true)
128 {
129 $data = '';
130 if (empty($this->rawNode)) {
131 throw new Exception('Node to encrypt has not been set');
132 }
133 if (! $objKey instanceof XMLSecurityKey) {
134 throw new Exception('Invalid Key');
135 }
136 $doc = $this->rawNode->ownerDocument;
137 $xPath = new DOMXPath($this->encdoc);
138 $objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue');
139 $cipherValue = $objList->item(0);
140 if ($cipherValue == null) {
141 throw new Exception('Error locating CipherValue element within template');
142 }
143 switch ($this->type) {
144 case (self::Element):
145 $data = $doc->saveXML($this->rawNode);
146 $this->encdoc->documentElement->setAttribute('Type', self::Element);
147 break;
148 case (self::Content):
149 $children = $this->rawNode->childNodes;
150 foreach ($children AS $child) {
151 $data .= $doc->saveXML($child);
152 }
153 $this->encdoc->documentElement->setAttribute('Type', self::Content);
154 break;
155 default:
156 throw new Exception('Type is currently not supported');
157 }
158
159 $encMethod = $this->encdoc->documentElement->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod'));
160 $encMethod->setAttribute('Algorithm', $objKey->getAlgorithm());
161 $cipherValue->parentNode->parentNode->insertBefore($encMethod, $cipherValue->parentNode->parentNode->firstChild);
162
163 $strEncrypt = base64_encode($objKey->encryptData($data));
164 $value = $this->encdoc->createTextNode($strEncrypt);
165 $cipherValue->appendChild($value);
166
167 if ($replace) {
168 switch ($this->type) {
169 case (self::Element):
170 if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
171 return $this->encdoc;
172 }
173 $importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true);
174 $this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode);
175 return $importEnc;
176 case (self::Content):
177 $importEnc = $this->rawNode->ownerDocument->importNode($this->encdoc->documentElement, true);
178 while ($this->rawNode->firstChild) {
179 $this->rawNode->removeChild($this->rawNode->firstChild);
180 }
181 $this->rawNode->appendChild($importEnc);
182 return $importEnc;
183 }
184 } else {
185 return $this->encdoc->documentElement;
186 }
187 }
188
193 public function encryptReferences($objKey)
194 {
195 $curRawNode = $this->rawNode;
196 $curType = $this->type;
197 foreach ($this->references AS $name => $reference) {
198 $this->encdoc = $reference["encnode"];
199 $this->rawNode = $reference["node"];
200 $this->type = $reference["type"];
201 try {
202 $encNode = $this->encryptNode($objKey);
203 $this->references[$name]["encnode"] = $encNode;
204 } catch (Exception $e) {
205 $this->rawNode = $curRawNode;
206 $this->type = $curType;
207 throw $e;
208 }
209 }
210 $this->rawNode = $curRawNode;
211 $this->type = $curType;
212 }
213
220 public function getCipherValue()
221 {
222 if (empty($this->rawNode)) {
223 throw new Exception('Node to decrypt has not been set');
224 }
225
226 $doc = $this->rawNode->ownerDocument;
227 $xPath = new DOMXPath($doc);
228 $xPath->registerNamespace('xmlencr', self::XMLENCNS);
229 /* Only handles embedded content right now and not a reference */
230 $query = "./xmlencr:CipherData/xmlencr:CipherValue";
231 $nodeset = $xPath->query($query, $this->rawNode);
232 $node = $nodeset->item(0);
233
234 if (!$node) {
235 return null;
236 }
237
238 return base64_decode($node->nodeValue);
239 }
240
254 public function decryptNode($objKey, $replace=true)
255 {
256 if (! $objKey instanceof XMLSecurityKey) {
257 throw new Exception('Invalid Key');
258 }
259
260 $encryptedData = $this->getCipherValue();
261 if ($encryptedData) {
262 $decrypted = $objKey->decryptData($encryptedData);
263 if ($replace) {
264 switch ($this->type) {
265 case (self::Element):
266 $newdoc = new DOMDocument();
267 $newdoc->loadXML($decrypted);
268 if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
269 return $newdoc;
270 }
271 $importEnc = $this->rawNode->ownerDocument->importNode($newdoc->documentElement, true);
272 $this->rawNode->parentNode->replaceChild($importEnc, $this->rawNode);
273 return $importEnc;
274 case (self::Content):
275 if ($this->rawNode->nodeType == XML_DOCUMENT_NODE) {
276 $doc = $this->rawNode;
277 } else {
278 $doc = $this->rawNode->ownerDocument;
279 }
280 $newFrag = $doc->createDocumentFragment();
281 $newFrag->appendXML($decrypted);
282 $parent = $this->rawNode->parentNode;
283 $parent->replaceChild($newFrag, $this->rawNode);
284 return $parent;
285 default:
286 return $decrypted;
287 }
288 } else {
289 return $decrypted;
290 }
291 } else {
292 throw new Exception("Cannot locate encrypted data");
293 }
294 }
295
304 public function encryptKey($srcKey, $rawKey, $append=true)
305 {
306 if ((! $srcKey instanceof XMLSecurityKey) || (! $rawKey instanceof XMLSecurityKey)) {
307 throw new Exception('Invalid Key');
308 }
309 $strEncKey = base64_encode($srcKey->encryptData($rawKey->key));
310 $root = $this->encdoc->documentElement;
311 $encKey = $this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptedKey');
312 if ($append) {
313 $keyInfo = $root->insertBefore($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'), $root->firstChild);
314 $keyInfo->appendChild($encKey);
315 } else {
316 $this->encKey = $encKey;
317 }
318 $encMethod = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:EncryptionMethod'));
319 $encMethod->setAttribute('Algorithm', $srcKey->getAlgorith());
320 if (! empty($srcKey->name)) {
321 $keyInfo = $encKey->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyInfo'));
322 $keyInfo->appendChild($this->encdoc->createElementNS('http://www.w3.org/2000/09/xmldsig#', 'dsig:KeyName', $srcKey->name));
323 }
324 $cipherData = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherData'));
325 $cipherData->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:CipherValue', $strEncKey));
326 if (is_array($this->references) && count($this->references) > 0) {
327 $refList = $encKey->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:ReferenceList'));
328 foreach ($this->references AS $name => $reference) {
329 $refuri = $reference["refuri"];
330 $dataRef = $refList->appendChild($this->encdoc->createElementNS(self::XMLENCNS, 'xenc:DataReference'));
331 $dataRef->setAttribute("URI", '#' . $refuri);
332 }
333 }
334 return;
335 }
336
342 public function decryptKey($encKey)
343 {
344 if (! $encKey->isEncrypted) {
345 throw new Exception("Key is not Encrypted");
346 }
347 if (empty($encKey->key)) {
348 throw new Exception("Key is missing data to perform the decryption");
349 }
350 return $this->decryptNode($encKey, false);
351 }
352
357 public function locateEncryptedData($element)
358 {
359 if ($element instanceof DOMDocument) {
360 $doc = $element;
361 } else {
362 $doc = $element->ownerDocument;
363 }
364 if ($doc) {
365 $xpath = new DOMXPath($doc);
366 $query = "//*[local-name()='EncryptedData' and namespace-uri()='".self::XMLENCNS."']";
367 $nodeset = $xpath->query($query);
368 return $nodeset->item(0);
369 }
370 return null;
371 }
372
378 public function locateKey($node=null)
379 {
380 if (empty($node)) {
381 $node = $this->rawNode;
382 }
383 if (! $node instanceof DOMNode) {
384 return null;
385 }
386 if ($doc = $node->ownerDocument) {
387 $xpath = new DOMXPath($doc);
388 $xpath->registerNamespace('xmlsecenc', self::XMLENCNS);
389 $query = ".//xmlsecenc:EncryptionMethod";
390 $nodeset = $xpath->query($query, $node);
391 if ($encmeth = $nodeset->item(0)) {
392 $attrAlgorithm = $encmeth->getAttribute("Algorithm");
393 try {
394 $objKey = new XMLSecurityKey($attrAlgorithm, array('type' => 'private'));
395 } catch (Exception $e) {
396 return null;
397 }
398 return $objKey;
399 }
400 }
401 return null;
402 }
403
410 public static function staticLocateKeyInfo($objBaseKey=null, $node=null)
411 {
412 if (empty($node) || (! $node instanceof DOMNode)) {
413 return null;
414 }
415 $doc = $node->ownerDocument;
416 if (!$doc) {
417 return null;
418 }
419
420 $xpath = new DOMXPath($doc);
421 $xpath->registerNamespace('xmlsecenc', self::XMLENCNS);
422 $xpath->registerNamespace('xmlsecdsig', XMLSecurityDSig::XMLDSIGNS);
423 $query = "./xmlsecdsig:KeyInfo";
424 $nodeset = $xpath->query($query, $node);
425 $encmeth = $nodeset->item(0);
426 if (!$encmeth) {
427 /* No KeyInfo in EncryptedData / EncryptedKey. */
428 return $objBaseKey;
429 }
430
431 foreach ($encmeth->childNodes AS $child) {
432 switch ($child->localName) {
433 case 'KeyName':
434 if (! empty($objBaseKey)) {
435 $objBaseKey->name = $child->nodeValue;
436 }
437 break;
438 case 'KeyValue':
439 foreach ($child->childNodes AS $keyval) {
440 switch ($keyval->localName) {
441 case 'DSAKeyValue':
442 throw new Exception("DSAKeyValue currently not supported");
443 case 'RSAKeyValue':
444 $modulus = null;
445 $exponent = null;
446 if ($modulusNode = $keyval->getElementsByTagName('Modulus')->item(0)) {
447 $modulus = base64_decode($modulusNode->nodeValue);
448 }
449 if ($exponentNode = $keyval->getElementsByTagName('Exponent')->item(0)) {
450 $exponent = base64_decode($exponentNode->nodeValue);
451 }
452 if (empty($modulus) || empty($exponent)) {
453 throw new Exception("Missing Modulus or Exponent");
454 }
455 $publicKey = XMLSecurityKey::convertRSA($modulus, $exponent);
456 $objBaseKey->loadKey($publicKey);
457 break;
458 }
459 }
460 break;
461 case 'RetrievalMethod':
462 $type = $child->getAttribute('Type');
463 if ($type !== 'http://www.w3.org/2001/04/xmlenc#EncryptedKey') {
464 /* Unsupported key type. */
465 break;
466 }
467 $uri = $child->getAttribute('URI');
468 if ($uri[0] !== '#') {
469 /* URI not a reference - unsupported. */
470 break;
471 }
472 $id = substr($uri, 1);
473
474 $query = '//xmlsecenc:EncryptedKey[@Id="'.XPath::filterAttrValue($id, XPath::DOUBLE_QUOTE).'"]';
475 $keyElement = $xpath->query($query)->item(0);
476 if (!$keyElement) {
477 throw new Exception("Unable to locate EncryptedKey with @Id='$id'.");
478 }
479
480 return XMLSecurityKey::fromEncryptedKeyElement($keyElement);
481 case 'EncryptedKey':
483 case 'X509Data':
484 if ($x509certNodes = $child->getElementsByTagName('X509Certificate')) {
485 if ($x509certNodes->length > 0) {
486 $x509cert = $x509certNodes->item(0)->textContent;
487 $x509cert = str_replace(array("\r", "\n", " "), "", $x509cert);
488 $x509cert = "-----BEGIN CERTIFICATE-----\n".chunk_split($x509cert, 64, "\n")."-----END CERTIFICATE-----\n";
489 $objBaseKey->loadKey($x509cert, false, true);
490 }
491 }
492 break;
493 }
494 }
495 return $objBaseKey;
496 }
497
503 public function locateKeyInfo($objBaseKey=null, $node=null)
504 {
505 if (empty($node)) {
506 $node = $this->rawNode;
507 }
508 return self::staticLocateKeyInfo($objBaseKey, $node);
509 }
510}
An exception for terminatinating execution or to throw for unit testing.
addReference($name, $node, $type)
Definition: XMLSecEnc.php:95
locateKey($node=null)
Returns the key from the DOM.
Definition: XMLSecEnc.php:378
encryptKey($srcKey, $rawKey, $append=true)
Encrypt the XMLSecurityKey.
Definition: XMLSecEnc.php:304
static staticLocateKeyInfo($objBaseKey=null, $node=null)
Definition: XMLSecEnc.php:410
locateKeyInfo($objBaseKey=null, $node=null)
Definition: XMLSecEnc.php:503
getCipherValue()
Retrieve the CipherValue text from this encrypted node.
Definition: XMLSecEnc.php:220
decryptNode($objKey, $replace=true)
Decrypt this encrypted node.
Definition: XMLSecEnc.php:254
encryptNode($objKey, $replace=true)
Encrypt the selected node with the given key.
Definition: XMLSecEnc.php:127
static generateGUID($prefix='pfx')
Generate guid.
static convertRSA($modulus, $exponent)
Hint: Modulus and Exponent must already be base64 decoded.
static fromEncryptedKeyElement(DOMElement $element)
Create key from an EncryptedKey-element.
if(!array_key_exists('StateId', $_REQUEST)) $id
if($format !==null) $name
Definition: metadata.php:146
$query