PageRenderTime 30ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/Users/authentication/SAMLAuthenticate/lib/xmlseclibs/xmlseclibs.php

https://bitbucket.org/cviolette/sugarcrm
PHP | 1516 lines | 1282 code | 125 blank | 109 comment | 272 complexity | 8b832c004e559e701915bb57b661dda7 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. <?php
  2. /**
  3. * xmlseclibs.php
  4. *
  5. * Copyright (c) 2007, Robert Richards <rrichards@cdatazone.org>.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * * Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * * Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * * Neither the name of Robert Richards nor the names of his
  21. * contributors may be used to endorse or promote products derived
  22. * from this software without specific prior written permission.
  23. *
  24. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  25. * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  26. * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
  27. * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28. * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  29. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
  30. * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  31. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
  32. * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  33. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
  34. * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  35. * POSSIBILITY OF SUCH DAMAGE.
  36. *
  37. * @author Robert Richards <rrichards@cdatazone.org>
  38. * @copyright 2007 Robert Richards <rrichards@cdatazone.org>
  39. * @license http://www.opensource.org/licenses/bsd-license.php BSD License
  40. * @version 1.2.2
  41. */
  42. /*
  43. Functions to generate simple cases of Exclusive Canonical XML - Callable function is C14NGeneral()
  44. i.e.: $canonical = C14NGeneral($domelement, TRUE);
  45. */
  46. /* helper function */
  47. function sortAndAddAttrs($element, $arAtts) {
  48. $newAtts = array();
  49. foreach ($arAtts AS $attnode) {
  50. $newAtts[$attnode->nodeName] = $attnode;
  51. }
  52. ksort($newAtts);
  53. foreach ($newAtts as $attnode) {
  54. $element->setAttribute($attnode->nodeName, $attnode->nodeValue);
  55. }
  56. }
  57. /* helper function */
  58. function canonical($tree, $element, $withcomments) {
  59. if ($tree->nodeType != XML_DOCUMENT_NODE) {
  60. $dom = $tree->ownerDocument;
  61. } else {
  62. $dom = $tree;
  63. }
  64. if ($element->nodeType != XML_ELEMENT_NODE) {
  65. if ($element->nodeType == XML_DOCUMENT_NODE) {
  66. foreach ($element->childNodes AS $node) {
  67. canonical($dom, $node, $withcomments);
  68. }
  69. return;
  70. }
  71. if ($element->nodeType == XML_COMMENT_NODE && ! $withcomments) {
  72. return;
  73. }
  74. $tree->appendChild($dom->importNode($element, TRUE));
  75. return;
  76. }
  77. $arNS = array();
  78. if ($element->namespaceURI != "") {
  79. if ($element->prefix == "") {
  80. $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
  81. } else {
  82. $prefix = $tree->lookupPrefix($element->namespaceURI);
  83. if ($prefix == $element->prefix) {
  84. $elCopy = $dom->createElementNS($element->namespaceURI, $element->nodeName);
  85. } else {
  86. $elCopy = $dom->createElement($element->nodeName);
  87. $arNS[$element->namespaceURI] = $element->prefix;
  88. }
  89. }
  90. } else {
  91. $elCopy = $dom->createElement($element->nodeName);
  92. }
  93. $tree->appendChild($elCopy);
  94. /* Create DOMXPath based on original document */
  95. $xPath = new DOMXPath($element->ownerDocument);
  96. /* Get namespaced attributes */
  97. $arAtts = $xPath->query('attribute::*[namespace-uri(.) != ""]', $element);
  98. /* Create an array with namespace URIs as keys, and sort them */
  99. foreach ($arAtts AS $attnode) {
  100. if (array_key_exists($attnode->namespaceURI, $arNS) &&
  101. ($arNS[$attnode->namespaceURI] == $attnode->prefix)) {
  102. continue;
  103. }
  104. $prefix = $tree->lookupPrefix($attnode->namespaceURI);
  105. if ($prefix != $attnode->prefix) {
  106. $arNS[$attnode->namespaceURI] = $attnode->prefix;
  107. } else {
  108. $arNS[$attnode->namespaceURI] = NULL;
  109. }
  110. }
  111. if (count($arNS) > 0) {
  112. asort($arNS);
  113. }
  114. /* Add namespace nodes */
  115. foreach ($arNS AS $namespaceURI=>$prefix) {
  116. if ($prefix != NULL) {
  117. $elCopy->setAttributeNS("http://www.w3.org/2000/xmlns/",
  118. "xmlns:".$prefix, $namespaceURI);
  119. }
  120. }
  121. if (count($arNS) > 0) {
  122. ksort($arNS);
  123. }
  124. /* Get attributes not in a namespace, and then sort and add them */
  125. $arAtts = $xPath->query('attribute::*[namespace-uri(.) = ""]', $element);
  126. sortAndAddAttrs($elCopy, $arAtts);
  127. /* Loop through the URIs, and then sort and add attributes within that namespace */
  128. foreach ($arNS as $nsURI=>$prefix) {
  129. $arAtts = $xPath->query('attribute::*[namespace-uri(.) = "'.$nsURI.'"]', $element);
  130. sortAndAddAttrs($elCopy, $arAtts);
  131. }
  132. foreach ($element->childNodes AS $node) {
  133. canonical($elCopy, $node, $withcomments);
  134. }
  135. }
  136. /*
  137. $element - DOMElement for which to produce the canonical version of
  138. $exclusive - boolean to indicate exclusive canonicalization (must pass TRUE)
  139. $withcomments - boolean indicating wether or not to include comments in canonicalized form
  140. */
  141. function C14NGeneral($element, $exclusive=FALSE, $withcomments=FALSE) {
  142. /* IF PHP 5.2+ then use built in canonical functionality */
  143. $php_version = explode('.', PHP_VERSION);
  144. if (($php_version[0] > 5) || ($php_version[0] == 5 && $php_version[1] >= 2) ) {
  145. return $element->C14N($exclusive, $withcomments);
  146. }
  147. /* Must be element or document */
  148. if (! $element instanceof DOMElement && ! $element instanceof DOMDocument) {
  149. return NULL;
  150. }
  151. /* Currently only exclusive XML is supported */
  152. if ($exclusive == FALSE) {
  153. throw new Exception("Only exclusive canonicalization is supported in this version of PHP");
  154. }
  155. $copyDoc = new DOMDocument();
  156. canonical($copyDoc, $element, $withcomments);
  157. return $copyDoc->saveXML($copyDoc->documentElement, LIBXML_NOEMPTYTAG);
  158. }
  159. class XMLSecurityKey {
  160. const TRIPLEDES_CBC = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
  161. const AES128_CBC = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
  162. const AES192_CBC = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
  163. const AES256_CBC = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
  164. const RSA_1_5 = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
  165. const RSA_OAEP_MGF1P = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
  166. const DSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#dsa-sha1';
  167. const RSA_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
  168. const RSA_SHA256 = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
  169. private $cryptParams = array();
  170. public $type = 0;
  171. public $key = NULL;
  172. public $passphrase = "";
  173. public $iv = NULL;
  174. public $name = NULL;
  175. public $keyChain = NULL;
  176. public $isEncrypted = FALSE;
  177. public $encryptedCtx = NULL;
  178. public $guid = NULL;
  179. /**
  180. * This variable contains the certificate as a string if this key represents an X509-certificate.
  181. * If this key doesn't represent a certificate, this will be NULL.
  182. */
  183. private $x509Certificate = NULL;
  184. public function __construct($type, $params=NULL) {
  185. srand();
  186. switch ($type) {
  187. case (XMLSecurityKey::TRIPLEDES_CBC):
  188. $this->cryptParams['library'] = 'mcrypt';
  189. $this->cryptParams['cipher'] = MCRYPT_TRIPLEDES;
  190. $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  191. $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#tripledes-cbc';
  192. break;
  193. case (XMLSecurityKey::AES128_CBC):
  194. $this->cryptParams['library'] = 'mcrypt';
  195. $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
  196. $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  197. $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes128-cbc';
  198. break;
  199. case (XMLSecurityKey::AES192_CBC):
  200. $this->cryptParams['library'] = 'mcrypt';
  201. $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
  202. $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  203. $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes192-cbc';
  204. break;
  205. case (XMLSecurityKey::AES256_CBC):
  206. $this->cryptParams['library'] = 'mcrypt';
  207. $this->cryptParams['cipher'] = MCRYPT_RIJNDAEL_128;
  208. $this->cryptParams['mode'] = MCRYPT_MODE_CBC;
  209. $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#aes256-cbc';
  210. break;
  211. case (XMLSecurityKey::RSA_1_5):
  212. $this->cryptParams['library'] = 'openssl';
  213. $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
  214. $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-1_5';
  215. if (is_array($params) && ! empty($params['type'])) {
  216. if ($params['type'] == 'public' || $params['type'] == 'private') {
  217. $this->cryptParams['type'] = $params['type'];
  218. break;
  219. }
  220. }
  221. throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  222. return;
  223. case (XMLSecurityKey::RSA_OAEP_MGF1P):
  224. $this->cryptParams['library'] = 'openssl';
  225. $this->cryptParams['padding'] = OPENSSL_PKCS1_OAEP_PADDING;
  226. $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p';
  227. $this->cryptParams['hash'] = NULL;
  228. if (is_array($params) && ! empty($params['type'])) {
  229. if ($params['type'] == 'public' || $params['type'] == 'private') {
  230. $this->cryptParams['type'] = $params['type'];
  231. break;
  232. }
  233. }
  234. throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  235. return;
  236. case (XMLSecurityKey::RSA_SHA1):
  237. $this->cryptParams['library'] = 'openssl';
  238. $this->cryptParams['method'] = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
  239. $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
  240. if (is_array($params) && ! empty($params['type'])) {
  241. if ($params['type'] == 'public' || $params['type'] == 'private') {
  242. $this->cryptParams['type'] = $params['type'];
  243. break;
  244. }
  245. }
  246. throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  247. break;
  248. case (XMLSecurityKey::RSA_SHA256):
  249. $this->cryptParams['library'] = 'openssl';
  250. $this->cryptParams['method'] = 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256';
  251. $this->cryptParams['padding'] = OPENSSL_PKCS1_PADDING;
  252. $this->cryptParams['digest'] = 'SHA256';
  253. if (is_array($params) && ! empty($params['type'])) {
  254. if ($params['type'] == 'public' || $params['type'] == 'private') {
  255. $this->cryptParams['type'] = $params['type'];
  256. break;
  257. }
  258. }
  259. throw new Exception('Certificate "type" (private/public) must be passed via parameters');
  260. break;
  261. default:
  262. throw new Exception('Invalid Key Type');
  263. return;
  264. }
  265. $this->type = $type;
  266. }
  267. public function generateSessionKey() {
  268. $key = '';
  269. if (! empty($this->cryptParams['cipher']) && ! empty($this->cryptParams['mode'])) {
  270. $keysize = mcrypt_module_get_algo_key_size($this->cryptParams['cipher']);
  271. /* Generating random key using iv generation routines */
  272. if (($keysize > 0) && ($td = mcrypt_module_open(MCRYPT_RIJNDAEL_256, '',$this->cryptParams['mode'], ''))) {
  273. if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
  274. $keysize = 16;
  275. if ($this->type == XMLSecurityKey::AES256_CBC) {
  276. $keysize = 32;
  277. } elseif ($this->type == XMLSecurityKey::AES192_CBC) {
  278. $keysize = 24;
  279. }
  280. }
  281. while (strlen($key) < $keysize) {
  282. $key .= mcrypt_create_iv(mcrypt_enc_get_iv_size ($td),MCRYPT_RAND);
  283. }
  284. mcrypt_module_close($td);
  285. $key = substr($key, 0, $keysize);
  286. $this->key = $key;
  287. }
  288. }
  289. return $key;
  290. }
  291. public function loadKey($key, $isFile=FALSE, $isCert = FALSE) {
  292. if ($isFile) {
  293. $this->key = file_get_contents($key);
  294. } else {
  295. $this->key = $key;
  296. }
  297. if ($isCert) {
  298. $this->key = openssl_x509_read($this->key);
  299. openssl_x509_export($this->key, $str_cert);
  300. $this->x509Certificate = $str_cert;
  301. $this->key = $str_cert;
  302. } else {
  303. $this->x509Certificate = NULL;
  304. }
  305. if ($this->cryptParams['library'] == 'openssl') {
  306. if ($this->cryptParams['type'] == 'public') {
  307. // Disable this part due to openssl bug on some systems
  308. // that think public key is private key. Certificate
  309. // should still serve as key for verification purposes
  310. // By smalyshev 1 May 2012
  311. // $this->key = openssl_get_publickey($this->key);
  312. } else {
  313. $this->key = openssl_get_privatekey($this->key, $this->passphrase);
  314. }
  315. } else if ($this->cryptParams['cipher'] == MCRYPT_RIJNDAEL_128) {
  316. /* Check key length */
  317. switch ($this->type) {
  318. case (XMLSecurityKey::AES256_CBC):
  319. if (strlen($this->key) < 25) {
  320. throw new Exception('Key must contain at least 25 characters for this cipher');
  321. }
  322. break;
  323. case (XMLSecurityKey::AES192_CBC):
  324. if (strlen($this->key) < 17) {
  325. throw new Exception('Key must contain at least 17 characters for this cipher');
  326. }
  327. break;
  328. }
  329. }
  330. }
  331. private function encryptMcrypt($data) {
  332. $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
  333. $this->iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
  334. mcrypt_generic_init($td, $this->key, $this->iv);
  335. if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) {
  336. $bs = mcrypt_enc_get_block_size($td);
  337. for ($datalen0=$datalen=strlen($data); (($datalen%$bs)!=($bs-1)); $datalen++)
  338. $data.=chr(rand(1, 127));
  339. $data.=chr($datalen-$datalen0+1);
  340. }
  341. $encrypted_data = $this->iv.mcrypt_generic($td, $data);
  342. mcrypt_generic_deinit($td);
  343. mcrypt_module_close($td);
  344. return $encrypted_data;
  345. }
  346. private function decryptMcrypt($data) {
  347. $td = mcrypt_module_open($this->cryptParams['cipher'], '', $this->cryptParams['mode'], '');
  348. $iv_length = mcrypt_enc_get_iv_size($td);
  349. $this->iv = substr($data, 0, $iv_length);
  350. $data = substr($data, $iv_length);
  351. mcrypt_generic_init($td, $this->key, $this->iv);
  352. $decrypted_data = mdecrypt_generic($td, $data);
  353. mcrypt_generic_deinit($td);
  354. mcrypt_module_close($td);
  355. if ($this->cryptParams['mode'] == MCRYPT_MODE_CBC) {
  356. $dataLen = strlen($decrypted_data);
  357. $paddingLength = substr($decrypted_data, $dataLen - 1, 1);
  358. $decrypted_data = substr($decrypted_data, 0, $dataLen - ord($paddingLength));
  359. }
  360. return $decrypted_data;
  361. }
  362. private function encryptOpenSSL($data) {
  363. if ($this->cryptParams['type'] == 'public') {
  364. if (! openssl_public_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
  365. throw new Exception('Failure encrypting Data');
  366. return;
  367. }
  368. } else {
  369. if (! openssl_private_encrypt($data, $encrypted_data, $this->key, $this->cryptParams['padding'])) {
  370. throw new Exception('Failure encrypting Data');
  371. return;
  372. }
  373. }
  374. return $encrypted_data;
  375. }
  376. private function decryptOpenSSL($data) {
  377. if ($this->cryptParams['type'] == 'public') {
  378. if (! openssl_public_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
  379. throw new Exception('Failure decrypting Data');
  380. return;
  381. }
  382. } else {
  383. if (! openssl_private_decrypt($data, $decrypted, $this->key, $this->cryptParams['padding'])) {
  384. throw new Exception('Failure decrypting Data');
  385. return;
  386. }
  387. }
  388. return $decrypted;
  389. }
  390. private function signOpenSSL($data) {
  391. $algo = OPENSSL_ALGO_SHA1;
  392. if (! empty($this->cryptParams['digest'])) {
  393. $algo = $this->cryptParams['digest'];
  394. }
  395. if (! openssl_sign ($data, $signature, $this->key, $algo)) {
  396. throw new Exception('Failure Signing Data: ' . openssl_error_string() . ' - ' . $algo);
  397. return;
  398. }
  399. return $signature;
  400. }
  401. private function verifyOpenSSL($data, $signature) {
  402. $algo = OPENSSL_ALGO_SHA1;
  403. if (! empty($this->cryptParams['digest'])) {
  404. $algo = $this->cryptParams['digest'];
  405. }
  406. return openssl_verify ($data, $signature, $this->key, $algo);
  407. }
  408. public function encryptData($data) {
  409. switch ($this->cryptParams['library']) {
  410. case 'mcrypt':
  411. return $this->encryptMcrypt($data);
  412. break;
  413. case 'openssl':
  414. return $this->encryptOpenSSL($data);
  415. break;
  416. }
  417. }
  418. public function decryptData($data) {
  419. switch ($this->cryptParams['library']) {
  420. case 'mcrypt':
  421. return $this->decryptMcrypt($data);
  422. break;
  423. case 'openssl':
  424. return $this->decryptOpenSSL($data);
  425. break;
  426. }
  427. }
  428. public function signData($data) {
  429. switch ($this->cryptParams['library']) {
  430. case 'openssl':
  431. return $this->signOpenSSL($data);
  432. break;
  433. }
  434. }
  435. public function verifySignature($data, $signature) {
  436. switch ($this->cryptParams['library']) {
  437. case 'openssl':
  438. return $this->verifyOpenSSL($data, $signature);
  439. break;
  440. }
  441. }
  442. public function getAlgorith() {
  443. return $this->cryptParams['method'];
  444. }
  445. static function makeAsnSegment($type, $string) {
  446. switch ($type){
  447. case 0x02:
  448. if (ord($string) > 0x7f)
  449. $string = chr(0).$string;
  450. break;
  451. case 0x03:
  452. $string = chr(0).$string;
  453. break;
  454. }
  455. $length = strlen($string);
  456. if ($length < 128){
  457. $output = sprintf("%c%c%s", $type, $length, $string);
  458. } else if ($length < 0x0100){
  459. $output = sprintf("%c%c%c%s", $type, 0x81, $length, $string);
  460. } else if ($length < 0x010000) {
  461. $output = sprintf("%c%c%c%c%s", $type, 0x82, $length/0x0100, $length%0x0100, $string);
  462. } else {
  463. $output = NULL;
  464. }
  465. return($output);
  466. }
  467. /* Modulus and Exponent must already be base64 decoded */
  468. static function convertRSA($modulus, $exponent) {
  469. /* make an ASN publicKeyInfo */
  470. $exponentEncoding = XMLSecurityKey::makeAsnSegment(0x02, $exponent);
  471. $modulusEncoding = XMLSecurityKey::makeAsnSegment(0x02, $modulus);
  472. $sequenceEncoding = XMLSecurityKey:: makeAsnSegment(0x30, $modulusEncoding.$exponentEncoding);
  473. $bitstringEncoding = XMLSecurityKey::makeAsnSegment(0x03, $sequenceEncoding);
  474. $rsaAlgorithmIdentifier = pack("H*", "300D06092A864886F70D0101010500");
  475. $publicKeyInfo = XMLSecurityKey::makeAsnSegment (0x30, $rsaAlgorithmIdentifier.$bitstringEncoding);
  476. /* encode the publicKeyInfo in base64 and add PEM brackets */
  477. $publicKeyInfoBase64 = base64_encode($publicKeyInfo);
  478. $encoding = "-----BEGIN PUBLIC KEY-----\n";
  479. $offset = 0;
  480. while ($segment=substr($publicKeyInfoBase64, $offset, 64)){
  481. $encoding = $encoding.$segment."\n";
  482. $offset += 64;
  483. }
  484. return $encoding."-----END PUBLIC KEY-----\n";
  485. }
  486. public function serializeKey($parent) {
  487. }
  488. /**
  489. * Retrieve the X509 certificate this key represents.
  490. *
  491. * Will return the X509 certificate in PEM-format if this key represents
  492. * an X509 certificate.
  493. *
  494. * @return The X509 certificate or NULL if this key doesn't represent an X509-certificate.
  495. */
  496. public function getX509Certificate() {
  497. return $this->x509Certificate;
  498. }
  499. }
  500. class XMLSecurityDSig {
  501. const XMLDSIGNS = 'http://www.w3.org/2000/09/xmldsig#';
  502. const SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
  503. const SHA256 = 'http://www.w3.org/2001/04/xmlenc#sha256';
  504. const SHA512 = 'http://www.w3.org/2001/04/xmlenc#sha512';
  505. const RIPEMD160 = 'http://www.w3.org/2001/04/xmlenc#ripemd160';
  506. const C14N = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
  507. const C14N_COMMENTS = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments';
  508. const EXC_C14N = 'http://www.w3.org/2001/10/xml-exc-c14n#';
  509. const EXC_C14N_COMMENTS = 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments';
  510. const template = '<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
  511. <ds:SignedInfo>
  512. <ds:SignatureMethod />
  513. </ds:SignedInfo>
  514. </ds:Signature>';
  515. public $sigNode = NULL;
  516. public $idKeys = array();
  517. public $idNS = array();
  518. private $signedInfo = NULL;
  519. private $xPathCtx = NULL;
  520. private $canonicalMethod = NULL;
  521. private $prefix = 'ds';
  522. private $searchpfx = 'secdsig';
  523. /* This variable contains an associative array of validated nodes. */
  524. private $validatedNodes = NULL;
  525. public function __construct() {
  526. $sigdoc = new DOMDocument();
  527. $sigdoc->loadXML(XMLSecurityDSig::template);
  528. $this->sigNode = $sigdoc->documentElement;
  529. }
  530. private function getXPathObj() {
  531. if (empty($this->xPathCtx) && ! empty($this->sigNode)) {
  532. $xpath = new DOMXPath($this->sigNode->ownerDocument);
  533. $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  534. $this->xPathCtx = $xpath;
  535. }
  536. return $this->xPathCtx;
  537. }
  538. static function generate_GUID($prefix='pfx') {
  539. $uuid = md5(uniqid(rand(), true));
  540. $guid = $prefix.substr($uuid,0,8)."-".
  541. substr($uuid,8,4)."-".
  542. substr($uuid,12,4)."-".
  543. substr($uuid,16,4)."-".
  544. substr($uuid,20,12);
  545. return $guid;
  546. }
  547. public function locateSignature($objDoc) {
  548. if ($objDoc instanceof DOMDocument) {
  549. $doc = $objDoc;
  550. } else {
  551. $doc = $objDoc->ownerDocument;
  552. }
  553. if ($doc) {
  554. $xpath = new DOMXPath($doc);
  555. $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  556. $query = ".//secdsig:Signature";
  557. $nodeset = $xpath->query($query, $objDoc);
  558. $this->sigNode = $nodeset->item(0);
  559. return $this->sigNode;
  560. }
  561. return NULL;
  562. }
  563. public function createNewSignNode($name, $value=NULL) {
  564. $doc = $this->sigNode->ownerDocument;
  565. if (! is_null($value)) {
  566. $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name, $value);
  567. } else {
  568. $node = $doc->createElementNS(XMLSecurityDSig::XMLDSIGNS, $this->prefix.':'.$name);
  569. }
  570. return $node;
  571. }
  572. public function setCanonicalMethod($method) {
  573. switch ($method) {
  574. case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
  575. case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
  576. case 'http://www.w3.org/2001/10/xml-exc-c14n#':
  577. case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
  578. $this->canonicalMethod = $method;
  579. break;
  580. default:
  581. throw new Exception('Invalid Canonical Method');
  582. }
  583. if ($xpath = $this->getXPathObj()) {
  584. $query = './'.$this->searchpfx.':SignedInfo';
  585. $nodeset = $xpath->query($query, $this->sigNode);
  586. if ($sinfo = $nodeset->item(0)) {
  587. $query = './'.$this->searchpfx.'CanonicalizationMethod';
  588. $nodeset = $xpath->query($query, $sinfo);
  589. if (! ($canonNode = $nodeset->item(0))) {
  590. $canonNode = $this->createNewSignNode('CanonicalizationMethod');
  591. $sinfo->insertBefore($canonNode, $sinfo->firstChild);
  592. }
  593. $canonNode->setAttribute('Algorithm', $this->canonicalMethod);
  594. }
  595. }
  596. }
  597. private function canonicalizeData($node, $canonicalmethod, $arXPath=NULL, $prefixList=NULL) {
  598. $exclusive = FALSE;
  599. $withComments = FALSE;
  600. switch ($canonicalmethod) {
  601. case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
  602. $exclusive = FALSE;
  603. $withComments = FALSE;
  604. break;
  605. case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
  606. $withComments = TRUE;
  607. break;
  608. case 'http://www.w3.org/2001/10/xml-exc-c14n#':
  609. $exclusive = TRUE;
  610. break;
  611. case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
  612. $exclusive = TRUE;
  613. $withComments = TRUE;
  614. break;
  615. }
  616. /* Support PHP versions < 5.2 not containing C14N methods in DOM extension */
  617. $php_version = explode('.', PHP_VERSION);
  618. if (($php_version[0] < 5) || ($php_version[0] == 5 && $php_version[1] < 2) ) {
  619. if (! is_null($arXPath)) {
  620. throw new Exception("PHP 5.2.0 or higher is required to perform XPath Transformations");
  621. }
  622. return C14NGeneral($node, $exclusive, $withComments);
  623. }
  624. return $node->C14N($exclusive, $withComments, $arXPath, $prefixList);
  625. }
  626. public function canonicalizeSignedInfo() {
  627. $doc = $this->sigNode->ownerDocument;
  628. $canonicalmethod = NULL;
  629. if ($doc) {
  630. $xpath = $this->getXPathObj();
  631. $query = "./secdsig:SignedInfo";
  632. $nodeset = $xpath->query($query, $this->sigNode);
  633. if ($signInfoNode = $nodeset->item(0)) {
  634. $query = "./secdsig:CanonicalizationMethod";
  635. $nodeset = $xpath->query($query, $signInfoNode);
  636. if ($canonNode = $nodeset->item(0)) {
  637. $canonicalmethod = $canonNode->getAttribute('Algorithm');
  638. }
  639. $this->signedInfo = $this->canonicalizeData($signInfoNode, $canonicalmethod);
  640. return $this->signedInfo;
  641. }
  642. }
  643. return NULL;
  644. }
  645. public function calculateDigest ($digestAlgorithm, $data) {
  646. switch ($digestAlgorithm) {
  647. case XMLSecurityDSig::SHA1:
  648. $alg = 'sha1';
  649. break;
  650. case XMLSecurityDSig::SHA256:
  651. $alg = 'sha256';
  652. break;
  653. case XMLSecurityDSig::SHA512:
  654. $alg = 'sha512';
  655. break;
  656. case XMLSecurityDSig::RIPEMD160:
  657. $alg = 'ripemd160';
  658. break;
  659. default:
  660. throw new Exception("Cannot validate digest: Unsupported Algorith <$digestAlgorithm>");
  661. }
  662. if (function_exists('hash')) {
  663. return base64_encode(hash($alg, $data, TRUE));
  664. } elseif (function_exists('mhash')) {
  665. $alg = "MHASH_" . strtoupper($alg);
  666. return base64_encode(mhash(constant($alg), $data));
  667. } elseif ($alg === 'sha1') {
  668. return base64_encode(sha1($data, TRUE));
  669. } else {
  670. throw new Exception('xmlseclibs is unable to calculate a digest. Maybe you need the mhash library?');
  671. }
  672. }
  673. public function validateDigest($refNode, $data) {
  674. $xpath = new DOMXPath($refNode->ownerDocument);
  675. $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  676. $query = 'string(./secdsig:DigestMethod/@Algorithm)';
  677. $digestAlgorithm = $xpath->evaluate($query, $refNode);
  678. $digValue = $this->calculateDigest($digestAlgorithm, $data);
  679. $query = 'string(./secdsig:DigestValue)';
  680. $digestValue = $xpath->evaluate($query, $refNode);
  681. return ($digValue == $digestValue);
  682. }
  683. public function processTransforms($refNode, $objData) {
  684. $data = $objData;
  685. $xpath = new DOMXPath($refNode->ownerDocument);
  686. $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  687. $query = './secdsig:Transforms/secdsig:Transform';
  688. $nodelist = $xpath->query($query, $refNode);
  689. $canonicalMethod = 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315';
  690. $arXPath = NULL;
  691. $prefixList = NULL;
  692. foreach ($nodelist AS $transform) {
  693. $algorithm = $transform->getAttribute("Algorithm");
  694. switch ($algorithm) {
  695. case 'http://www.w3.org/2001/10/xml-exc-c14n#':
  696. case 'http://www.w3.org/2001/10/xml-exc-c14n#WithComments':
  697. $node = $transform->firstChild;
  698. while ($node) {
  699. if ($node->localName == 'InclusiveNamespaces') {
  700. if ($pfx = $node->getAttribute('PrefixList')) {
  701. $arpfx = array();
  702. $pfxlist = preg_split("/\s/", $pfx);
  703. foreach ($pfxlist AS $pfx) {
  704. $val = trim($pfx);
  705. if (! empty($val)) {
  706. $arpfx[] = $val;
  707. }
  708. }
  709. if (count($arpfx) > 0) {
  710. $prefixList = $arpfx;
  711. }
  712. }
  713. break;
  714. }
  715. $node = $node->nextSibling;
  716. }
  717. case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315':
  718. case 'http://www.w3.org/TR/2001/REC-xml-c14n-20010315#WithComments':
  719. $canonicalMethod = $algorithm;
  720. break;
  721. case 'http://www.w3.org/TR/1999/REC-xpath-19991116':
  722. $node = $transform->firstChild;
  723. while ($node) {
  724. if ($node->localName == 'XPath') {
  725. $arXPath = array();
  726. $arXPath['query'] = '(.//. | .//@* | .//namespace::*)['.$node->nodeValue.']';
  727. $arXpath['namespaces'] = array();
  728. $nslist = $xpath->query('./namespace::*', $node);
  729. foreach ($nslist AS $nsnode) {
  730. if ($nsnode->localName != "xml") {
  731. $arXPath['namespaces'][$nsnode->localName] = $nsnode->nodeValue;
  732. }
  733. }
  734. break;
  735. }
  736. $node = $node->nextSibling;
  737. }
  738. break;
  739. }
  740. }
  741. if ($data instanceof DOMNode) {
  742. $data = $this->canonicalizeData($objData, $canonicalMethod, $arXPath, $prefixList);
  743. }
  744. return $data;
  745. }
  746. public function processRefNode($refNode) {
  747. $dataObject = NULL;
  748. if ($uri = $refNode->getAttribute("URI")) {
  749. $arUrl = parse_url($uri);
  750. if (empty($arUrl['path'])) {
  751. if ($identifier = $arUrl['fragment']) {
  752. $xPath = new DOMXPath($refNode->ownerDocument);
  753. if ($this->idNS && is_array($this->idNS)) {
  754. foreach ($this->idNS AS $nspf=>$ns) {
  755. $xPath->registerNamespace($nspf, $ns);
  756. }
  757. }
  758. $iDlist = '@Id="'.$identifier.'"';
  759. if (is_array($this->idKeys)) {
  760. foreach ($this->idKeys AS $idKey) {
  761. $iDlist .= " or @$idKey='$identifier'";
  762. }
  763. }
  764. $query = '//*['.$iDlist.']';
  765. $dataObject = $xPath->query($query)->item(0);
  766. } else {
  767. $dataObject = $refNode->ownerDocument;
  768. }
  769. } else {
  770. $dataObject = file_get_contents($arUrl);
  771. }
  772. } else {
  773. $dataObject = $refNode->ownerDocument;
  774. }
  775. $data = $this->processTransforms($refNode, $dataObject);
  776. if (!$this->validateDigest($refNode, $data)) {
  777. return FALSE;
  778. }
  779. if ($dataObject instanceof DOMNode) {
  780. /* Add this node to the list of validated nodes. */
  781. if(! empty($identifier)) {
  782. $this->validatedNodes[$identifier] = $dataObject;
  783. } else {
  784. $this->validatedNodes[] = $dataObject;
  785. }
  786. }
  787. return TRUE;
  788. }
  789. public function getRefNodeID($refNode) {
  790. if ($uri = $refNode->getAttribute("URI")) {
  791. $arUrl = parse_url($uri);
  792. if (empty($arUrl['path'])) {
  793. if ($identifier = $arUrl['fragment']) {
  794. return $identifier;
  795. }
  796. }
  797. }
  798. return null;
  799. }
  800. public function getRefIDs() {
  801. $refids = array();
  802. $doc = $this->sigNode->ownerDocument;
  803. $xpath = $this->getXPathObj();
  804. $query = "./secdsig:SignedInfo/secdsig:Reference";
  805. $nodeset = $xpath->query($query, $this->sigNode);
  806. if ($nodeset->length == 0) {
  807. throw new Exception("Reference nodes not found");
  808. }
  809. foreach ($nodeset AS $refNode) {
  810. $refids[] = $this->getRefNodeID($refNode);
  811. }
  812. return $refids;
  813. }
  814. public function validateReference() {
  815. $doc = $this->sigNode->ownerDocument;
  816. if (! $doc->isSameNode($this->sigNode)) {
  817. $this->sigNode->parentNode->removeChild($this->sigNode);
  818. }
  819. $xpath = $this->getXPathObj();
  820. $query = "./secdsig:SignedInfo/secdsig:Reference";
  821. $nodeset = $xpath->query($query, $this->sigNode);
  822. if ($nodeset->length == 0) {
  823. throw new Exception("Reference nodes not found");
  824. }
  825. /* Initialize/reset the list of validated nodes. */
  826. $this->validatedNodes = array();
  827. foreach ($nodeset AS $refNode) {
  828. if (! $this->processRefNode($refNode)) {
  829. /* Clear the list of validated nodes. */
  830. $this->validatedNodes = NULL;
  831. throw new Exception("Reference validation failed");
  832. }
  833. }
  834. return TRUE;
  835. }
  836. private function addRefInternal($sinfoNode, $node, $algorithm, $arTransforms=NULL, $options=NULL) {
  837. $prefix = NULL;
  838. $prefix_ns = NULL;
  839. $id_name = 'Id';
  840. $overwrite_id = TRUE;
  841. $force_uri = FALSE;
  842. if (is_array($options)) {
  843. $prefix = empty($options['prefix'])?NULL:$options['prefix'];
  844. $prefix_ns = empty($options['prefix_ns'])?NULL:$options['prefix_ns'];
  845. $id_name = empty($options['id_name'])?'Id':$options['id_name'];
  846. $overwrite_id = !isset($options['overwrite'])?TRUE:(bool)$options['overwrite'];
  847. $force_uri = !isset($options['force_uri'])?FALSE:(bool)$options['force_uri'];
  848. }
  849. $attname = $id_name;
  850. if (! empty($prefix)) {
  851. $attname = $prefix.':'.$attname;
  852. }
  853. $refNode = $this->createNewSignNode('Reference');
  854. $sinfoNode->appendChild($refNode);
  855. if (! $node instanceof DOMDocument) {
  856. $uri = NULL;
  857. if (! $overwrite_id) {
  858. $uri = $node->getAttributeNS($prefix_ns, $attname);
  859. }
  860. if (empty($uri)) {
  861. $uri = XMLSecurityDSig::generate_GUID();
  862. $node->setAttributeNS($prefix_ns, $attname, $uri);
  863. }
  864. $refNode->setAttribute("URI", '#'.$uri);
  865. } elseif ($force_uri) {
  866. $refNode->setAttribute("URI", '');
  867. }
  868. $transNodes = $this->createNewSignNode('Transforms');
  869. $refNode->appendChild($transNodes);
  870. if (is_array($arTransforms)) {
  871. foreach ($arTransforms AS $transform) {
  872. $transNode = $this->createNewSignNode('Transform');
  873. $transNodes->appendChild($transNode);
  874. if (is_array($transform) &&
  875. (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116'])) &&
  876. (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']))) {
  877. $transNode->setAttribute('Algorithm', 'http://www.w3.org/TR/1999/REC-xpath-19991116');
  878. $XPathNode = $this->createNewSignNode('XPath', $transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['query']);
  879. $transNode->appendChild($XPathNode);
  880. if (! empty($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'])) {
  881. foreach ($transform['http://www.w3.org/TR/1999/REC-xpath-19991116']['namespaces'] AS $prefix => $namespace) {
  882. $XPathNode->setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:$prefix", $namespace);
  883. }
  884. }
  885. } else {
  886. $transNode->setAttribute('Algorithm', $transform);
  887. }
  888. }
  889. } elseif (! empty($this->canonicalMethod)) {
  890. $transNode = $this->createNewSignNode('Transform');
  891. $transNodes->appendChild($transNode);
  892. $transNode->setAttribute('Algorithm', $this->canonicalMethod);
  893. }
  894. $canonicalData = $this->processTransforms($refNode, $node);
  895. $digValue = $this->calculateDigest($algorithm, $canonicalData);
  896. $digestMethod = $this->createNewSignNode('DigestMethod');
  897. $refNode->appendChild($digestMethod);
  898. $digestMethod->setAttribute('Algorithm', $algorithm);
  899. $digestValue = $this->createNewSignNode('DigestValue', $digValue);
  900. $refNode->appendChild($digestValue);
  901. }
  902. public function addReference($node, $algorithm, $arTransforms=NULL, $options=NULL) {
  903. if ($xpath = $this->getXPathObj()) {
  904. $query = "./secdsig:SignedInfo";
  905. $nodeset = $xpath->query($query, $this->sigNode);
  906. if ($sInfo = $nodeset->item(0)) {
  907. $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
  908. }
  909. }
  910. }
  911. public function addReferenceList($arNodes, $algorithm, $arTransforms=NULL, $options=NULL) {
  912. if ($xpath = $this->getXPathObj()) {
  913. $query = "./secdsig:SignedInfo";
  914. $nodeset = $xpath->query($query, $this->sigNode);
  915. if ($sInfo = $nodeset->item(0)) {
  916. foreach ($arNodes AS $node) {
  917. $this->addRefInternal($sInfo, $node, $algorithm, $arTransforms, $options);
  918. }
  919. }
  920. }
  921. }
  922. public function addObject($data, $mimetype=NULL, $encoding=NULL) {
  923. $objNode = $this->createNewSignNode('Object');
  924. $this->sigNode->appendChild($objNode);
  925. if (! empty($mimetype)) {
  926. $objNode->setAtribute('MimeType', $mimetype);
  927. }
  928. if (! empty($encoding)) {
  929. $objNode->setAttribute('Encoding', $encoding);
  930. }
  931. if ($data instanceof DOMElement) {
  932. $newData = $this->sigNode->ownerDocument->importNode($data, TRUE);
  933. } else {
  934. $newData = $this->sigNode->ownerDocument->createTextNode($data);
  935. }
  936. $objNode->appendChild($newData);
  937. return $objNode;
  938. }
  939. public function locateKey($node=NULL) {
  940. if (empty($node)) {
  941. $node = $this->sigNode;
  942. }
  943. if (! $node instanceof DOMNode) {
  944. return NULL;
  945. }
  946. if ($doc = $node->ownerDocument) {
  947. $xpath = new DOMXPath($doc);
  948. $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  949. $query = "string(./secdsig:SignedInfo/secdsig:SignatureMethod/@Algorithm)";
  950. $algorithm = $xpath->evaluate($query, $node);
  951. if ($algorithm) {
  952. try {
  953. $objKey = new XMLSecurityKey($algorithm, array('type'=>'public'));
  954. } catch (Exception $e) {
  955. return NULL;
  956. }
  957. return $objKey;
  958. }
  959. }
  960. return NULL;
  961. }
  962. public function verify($objKey) {
  963. $doc = $this->sigNode->ownerDocument;
  964. $xpath = new DOMXPath($doc);
  965. $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  966. $query = "string(./secdsig:SignatureValue)";
  967. $sigValue = $xpath->evaluate($query, $this->sigNode);
  968. if (empty($sigValue)) {
  969. throw new Exception("Unable to locate SignatureValue");
  970. }
  971. return $objKey->verifySignature($this->signedInfo, base64_decode($sigValue));
  972. }
  973. public function signData($objKey, $data) {
  974. return $objKey->signData($data);
  975. }
  976. public function sign($objKey) {
  977. if ($xpath = $this->getXPathObj()) {
  978. $query = "./secdsig:SignedInfo";
  979. $nodeset = $xpath->query($query, $this->sigNode);
  980. if ($sInfo = $nodeset->item(0)) {
  981. $query = "./secdsig:SignatureMethod";
  982. $nodeset = $xpath->query($query, $sInfo);
  983. $sMethod = $nodeset->item(0);
  984. $sMethod->setAttribute('Algorithm', $objKey->type);
  985. $data = $this->canonicalizeData($sInfo, $this->canonicalMethod);
  986. $sigValue = base64_encode($this->signData($objKey, $data));
  987. $sigValueNode = $this->createNewSignNode('SignatureValue', $sigValue);
  988. if ($infoSibling = $sInfo->nextSibling) {
  989. $infoSibling->parentNode->insertBefore($sigValueNode, $infoSibling);
  990. } else {
  991. $this->sigNode->appendChild($sigValueNode);
  992. }
  993. }
  994. }
  995. }
  996. public function appendCert() {
  997. }
  998. public function appendKey($objKey, $parent=NULL) {
  999. $objKey->serializeKey($parent);
  1000. }
  1001. /**
  1002. * This function inserts the signature element.
  1003. *
  1004. * The signature element will be appended to the element, unless $beforeNode is specified. If $beforeNode
  1005. * is specified, the signature element will be inserted as the last element before $beforeNode.
  1006. *
  1007. * @param $node The node the signature element should be inserted into.
  1008. * @param $beforeNode The node the signature element should be located before.
  1009. */
  1010. public function insertSignature($node, $beforeNode = NULL) {
  1011. $document = $node->ownerDocument;
  1012. $signatureElement = $document->importNode($this->sigNode, TRUE);
  1013. if($beforeNode == NULL) {
  1014. $node->insertBefore($signatureElement);
  1015. } else {
  1016. $node->insertBefore($signatureElement, $beforeNode);
  1017. }
  1018. }
  1019. public function appendSignature($parentNode, $insertBefore = FALSE) {
  1020. $beforeNode = $insertBefore ? $parentNode->firstChild : NULL;
  1021. $this->insertSignature($parentNode, $beforeNode);
  1022. }
  1023. static function get509XCert($cert, $isPEMFormat=TRUE) {
  1024. $certs = XMLSecurityDSig::staticGet509XCerts($cert, $isPEMFormat);
  1025. if (! empty($certs)) {
  1026. return $certs[0];
  1027. }
  1028. return '';
  1029. }
  1030. static function staticGet509XCerts($certs, $isPEMFormat=TRUE) {
  1031. if ($isPEMFormat) {
  1032. $data = '';
  1033. $certlist = array();
  1034. $arCert = explode("\n", $certs);
  1035. $inData = FALSE;
  1036. foreach ($arCert AS $curData) {
  1037. if (! $inData) {
  1038. if (strncmp($curData, '-----BEGIN CERTIFICATE', 22) == 0) {
  1039. $inData = TRUE;
  1040. }
  1041. } else {
  1042. if (strncmp($curData, '-----END CERTIFICATE', 20) == 0) {
  1043. $inData = FALSE;
  1044. $certlist[] = $data;
  1045. $data = '';
  1046. continue;
  1047. }
  1048. $data .= trim($curData);
  1049. }
  1050. }
  1051. return $certlist;
  1052. } else {
  1053. return array($certs);
  1054. }
  1055. }
  1056. static function staticAdd509Cert($parentRef, $cert, $isPEMFormat=TRUE, $isURL=False, $xpath=NULL) {
  1057. if ($isURL) {
  1058. $cert = file_get_contents($cert);
  1059. }
  1060. if (! $parentRef instanceof DOMElement) {
  1061. throw new Exception('Invalid parent Node parameter');
  1062. }
  1063. $baseDoc = $parentRef->ownerDocument;
  1064. if (empty($xpath)) {
  1065. $xpath = new DOMXPath($parentRef->ownerDocument);
  1066. $xpath->registerNamespace('secdsig', XMLSecurityDSig::XMLDSIGNS);
  1067. }
  1068. $query = "./secdsig:KeyInfo";
  1069. $nodeset = $xpath->query($query, $parentRef);
  1070. $keyInfo = $nodeset->item(0);
  1071. if (! $keyInfo) {
  1072. $inserted = FALSE;
  1073. $keyInfo = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:KeyInfo');
  1074. $query = "./secdsig:Object";
  1075. $nodeset = $xpath->query($query, $parentRef);
  1076. if ($sObject = $nodeset->item(0)) {
  1077. $sObject->parentNode->insertBefore($keyInfo, $sObject);
  1078. $inserted = TRUE;
  1079. }
  1080. if (! $inserted) {
  1081. $parentRef->appendChild($keyInfo);
  1082. }
  1083. }
  1084. // Add all certs if there are more than one
  1085. $certs = XMLSecurityDSig::staticGet509XCerts($cert, $isPEMFormat);
  1086. // Atach X509 data node
  1087. $x509DataNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Data');
  1088. $keyInfo->appendChild($x509DataNode);
  1089. // Atach all certificate nodes
  1090. foreach ($certs as $X509Cert){
  1091. $x509CertNode = $baseDoc->createElementNS(XMLSecurityDSig::XMLDSIGNS, 'ds:X509Certificate', $X509Cert);
  1092. $x509DataNode->appendChild($x509CertNode);
  1093. }
  1094. }
  1095. public function add509Cert($cert, $isPEMFormat=TRUE, $isURL=False) {
  1096. if ($xpath = $this->getXPathObj()) {
  1097. self::staticAdd509Cert($this->sigNode, $cert, $isPEMFormat, $isURL, $xpath);
  1098. }
  1099. }
  1100. /* This function retrieves an associative array of the validated nodes.
  1101. *
  1102. * The array will contain the id of the referenced node as the key and the node itself
  1103. * as the value.
  1104. *
  1105. * Returns:
  1106. * An associative array of validated nodes or NULL if no nodes have been validated.
  1107. */
  1108. public function getValidatedNodes() {
  1109. return $this->validatedNodes;
  1110. }
  1111. }
  1112. class XMLSecEnc {
  1113. const template = "<xenc:EncryptedData xmlns:xenc='http://www.w3.org/2001/04/xmlenc#'>
  1114. <xenc:CipherData>
  1115. <xenc:CipherValue></xenc:CipherValue>
  1116. </xenc:CipherData>
  1117. </xenc:EncryptedData>";
  1118. const Element = 'http://www.w3.org/2001/04/xmlenc#Element';
  1119. const Content = 'http://www.w3.org/2001/04/xmlenc#Content';
  1120. const URI = 3;
  1121. const XMLENCNS = 'http://www.w3.org/2001/04/xmlenc#';
  1122. private $encdoc = NULL;
  1123. private $rawNode = NULL;
  1124. public $type = NULL;
  1125. public $encKey = NULL;
  1126. public function __construct() {
  1127. $this->encdoc = new DOMDocument();
  1128. $this->encdoc->loadXML(XMLSecEnc::template);
  1129. }
  1130. public function setNode($node) {
  1131. $this->rawNode = $node;
  1132. }
  1133. public function encryptNode($objKey, $replace=TRUE) {
  1134. $data = '';
  1135. if (empty($this->rawNode)) {
  1136. throw new Exception('Node to encrypt has not been set');
  1137. }
  1138. if (! $objKey instanceof XMLSecurityKey) {
  1139. throw new Exception('Invalid Key');
  1140. }
  1141. $doc = $this->rawNode->ownerDocument;
  1142. $xPath = new DOMXPath($this->encdoc);
  1143. $objList = $xPath->query('/xenc:EncryptedData/xenc:CipherData/xenc:CipherValue');
  1144. $cipherValue = $objList->item(0);

Large files files are truncated, but you can click here to view the full file