PageRenderTime 42ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/library/Zend/InfoCard/Xml/Security.php

https://bitbucket.org/fabiancarlos/feature_seguimentos
PHP | 327 lines | 173 code | 56 blank | 98 comment | 21 complexity | a9c56cbadf1f4e9fc595acc3004aa82b MD5 | raw file
  1. <?php
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Zend_InfoCard
  17. * @subpackage Zend_InfoCard_Xml_Security
  18. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  19. * @license http://framework.zend.com/license/new-bsd New BSD License
  20. * @version $Id: Security.php 23775 2011-03-01 17:25:24Z ralph $
  21. */
  22. /**
  23. * Zend_InfoCard_Xml_Security_Transform
  24. */
  25. require_once 'Zend/InfoCard/Xml/Security/Transform.php';
  26. /**
  27. *
  28. * @category Zend
  29. * @package Zend_InfoCard
  30. * @subpackage Zend_InfoCard_Xml_Security
  31. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  32. * @license http://framework.zend.com/license/new-bsd New BSD License
  33. */
  34. class Zend_InfoCard_Xml_Security
  35. {
  36. /**
  37. * ASN.1 type INTEGER class
  38. */
  39. const ASN_TYPE_INTEGER = 0x02;
  40. /**
  41. * ASN.1 type BIT STRING class
  42. */
  43. const ASN_TYPE_BITSTRING = 0x03;
  44. /**
  45. * ASN.1 type SEQUENCE class
  46. */
  47. const ASN_TYPE_SEQUENCE = 0x30;
  48. /**
  49. * The URI for Canonical Method C14N Exclusive
  50. */
  51. const CANONICAL_METHOD_C14N_EXC = 'http://www.w3.org/2001/10/xml-exc-c14n#';
  52. /**
  53. * The URI for Signature Method SHA1
  54. */
  55. const SIGNATURE_METHOD_SHA1 = 'http://www.w3.org/2000/09/xmldsig#rsa-sha1';
  56. /**
  57. * The URI for Digest Method SHA1
  58. */
  59. const DIGEST_METHOD_SHA1 = 'http://www.w3.org/2000/09/xmldsig#sha1';
  60. /**
  61. * The Identifier for RSA Keys
  62. */
  63. const RSA_KEY_IDENTIFIER = '300D06092A864886F70D0101010500';
  64. /**
  65. * Constructor (disabled)
  66. *
  67. * @return void
  68. */
  69. private function __construct()
  70. {
  71. }
  72. /**
  73. * Validates the signature of a provided XML block
  74. *
  75. * @param string $strXMLInput An XML block containing a Signature
  76. * @return bool True if the signature validated, false otherwise
  77. * @throws Zend_InfoCard_Xml_Security_Exception
  78. */
  79. static public function validateXMLSignature($strXMLInput)
  80. {
  81. if(!extension_loaded('openssl')) {
  82. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  83. throw new Zend_InfoCard_Xml_Security_Exception("You must have the openssl extension installed to use this class");
  84. }
  85. $sxe = simplexml_load_string($strXMLInput);
  86. if(!isset($sxe->Signature)) {
  87. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  88. throw new Zend_InfoCard_Xml_Security_Exception("Could not identify XML Signature element");
  89. }
  90. if(!isset($sxe->Signature->SignedInfo)) {
  91. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  92. throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a SignedInfo block");
  93. }
  94. if(!isset($sxe->Signature->SignatureValue)) {
  95. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  96. throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a SignatureValue block");
  97. }
  98. if(!isset($sxe->Signature->KeyInfo)) {
  99. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  100. throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a KeyInfo block");
  101. }
  102. if(!isset($sxe->Signature->KeyInfo->KeyValue)) {
  103. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  104. throw new Zend_InfoCard_Xml_Security_Exception("Signature is missing a KeyValue block");
  105. }
  106. switch((string)$sxe->Signature->SignedInfo->CanonicalizationMethod['Algorithm']) {
  107. case self::CANONICAL_METHOD_C14N_EXC:
  108. $cMethod = (string)$sxe->Signature->SignedInfo->CanonicalizationMethod['Algorithm'];
  109. break;
  110. default:
  111. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  112. throw new Zend_InfoCard_Xml_Security_Exception("Unknown or unsupported CanonicalizationMethod Requested");
  113. break;
  114. }
  115. switch((string)$sxe->Signature->SignedInfo->SignatureMethod['Algorithm']) {
  116. case self::SIGNATURE_METHOD_SHA1:
  117. $sMethod = (string)$sxe->Signature->SignedInfo->SignatureMethod['Algorithm'];
  118. break;
  119. default:
  120. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  121. throw new Zend_InfoCard_Xml_Security_Exception("Unknown or unsupported SignatureMethod Requested");
  122. break;
  123. }
  124. switch((string)$sxe->Signature->SignedInfo->Reference->DigestMethod['Algorithm']) {
  125. case self::DIGEST_METHOD_SHA1:
  126. $dMethod = (string)$sxe->Signature->SignedInfo->Reference->DigestMethod['Algorithm'];
  127. break;
  128. default:
  129. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  130. throw new Zend_InfoCard_Xml_Security_Exception("Unknown or unsupported DigestMethod Requested");
  131. break;
  132. }
  133. $base64DecodeSupportsStrictParam = version_compare(PHP_VERSION, '5.2.0', '>=');
  134. if ($base64DecodeSupportsStrictParam) {
  135. $dValue = base64_decode((string)$sxe->Signature->SignedInfo->Reference->DigestValue, true);
  136. } else {
  137. $dValue = base64_decode((string)$sxe->Signature->SignedInfo->Reference->DigestValue);
  138. }
  139. if ($base64DecodeSupportsStrictParam) {
  140. $signatureValue = base64_decode((string)$sxe->Signature->SignatureValue, true);
  141. } else {
  142. $signatureValue = base64_decode((string)$sxe->Signature->SignatureValue);
  143. }
  144. $transformer = new Zend_InfoCard_Xml_Security_Transform();
  145. foreach($sxe->Signature->SignedInfo->Reference->Transforms->children() as $transform) {
  146. $transformer->addTransform((string)$transform['Algorithm']);
  147. }
  148. $transformed_xml = $transformer->applyTransforms($strXMLInput);
  149. $transformed_xml_binhash = pack("H*", sha1($transformed_xml));
  150. if(!self::_secureStringCompare($transformed_xml_binhash, $dValue)) {
  151. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  152. throw new Zend_InfoCard_Xml_Security_Exception("Locally Transformed XML does not match XML Document. Cannot Verify Signature");
  153. }
  154. $public_key = null;
  155. switch(true) {
  156. case isset($sxe->Signature->KeyInfo->KeyValue->X509Certificate):
  157. $certificate = (string)$sxe->Signature->KeyInfo->KeyValue->X509Certificate;
  158. $pem = "-----BEGIN CERTIFICATE-----\n" .
  159. wordwrap($certificate, 64, "\n", true) .
  160. "\n-----END CERTIFICATE-----";
  161. $public_key = openssl_pkey_get_public($pem);
  162. if(!$public_key) {
  163. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  164. throw new Zend_InfoCard_Xml_Security_Exception("Unable to extract and prcoess X509 Certificate from KeyValue");
  165. }
  166. break;
  167. case isset($sxe->Signature->KeyInfo->KeyValue->RSAKeyValue):
  168. if(!isset($sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Modulus) ||
  169. !isset($sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Exponent)) {
  170. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  171. throw new Zend_InfoCard_Xml_Security_Exception("RSA Key Value not in Modulus/Exponent form");
  172. }
  173. $modulus = base64_decode((string)$sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Modulus);
  174. $exponent = base64_decode((string)$sxe->Signature->KeyInfo->KeyValue->RSAKeyValue->Exponent);
  175. $pem_public_key = self::_getPublicKeyFromModExp($modulus, $exponent);
  176. $public_key = openssl_pkey_get_public ($pem_public_key);
  177. break;
  178. default:
  179. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  180. throw new Zend_InfoCard_Xml_Security_Exception("Unable to determine or unsupported representation of the KeyValue block");
  181. }
  182. $transformer = new Zend_InfoCard_Xml_Security_Transform();
  183. $transformer->addTransform((string)$sxe->Signature->SignedInfo->CanonicalizationMethod['Algorithm']);
  184. // The way we are doing our XML processing requires that we specifically add this
  185. // (even though it's in the <Signature> parent-block).. otherwise, our canonical form
  186. // fails signature verification
  187. $sxe->Signature->SignedInfo->addAttribute('xmlns', 'http://www.w3.org/2000/09/xmldsig#');
  188. $canonical_signedinfo = $transformer->applyTransforms($sxe->Signature->SignedInfo->asXML());
  189. if(@openssl_verify($canonical_signedinfo, $signatureValue, $public_key)) {
  190. return (string)$sxe->Signature->SignedInfo->Reference['URI'];
  191. }
  192. return false;
  193. }
  194. /**
  195. * Transform an RSA Key in Modulus/Exponent format into a PEM encoding and
  196. * return an openssl resource for it
  197. *
  198. * @param string $modulus The RSA Modulus in binary format
  199. * @param string $exponent The RSA exponent in binary format
  200. * @return string The PEM encoded version of the key
  201. */
  202. static protected function _getPublicKeyFromModExp($modulus, $exponent)
  203. {
  204. $modulusInteger = self::_encodeValue($modulus, self::ASN_TYPE_INTEGER);
  205. $exponentInteger = self::_encodeValue($exponent, self::ASN_TYPE_INTEGER);
  206. $modExpSequence = self::_encodeValue($modulusInteger . $exponentInteger, self::ASN_TYPE_SEQUENCE);
  207. $modExpBitString = self::_encodeValue($modExpSequence, self::ASN_TYPE_BITSTRING);
  208. $binRsaKeyIdentifier = pack( "H*", self::RSA_KEY_IDENTIFIER );
  209. $publicKeySequence = self::_encodeValue($binRsaKeyIdentifier . $modExpBitString, self::ASN_TYPE_SEQUENCE);
  210. $publicKeyInfoBase64 = base64_encode( $publicKeySequence );
  211. $publicKeyString = "-----BEGIN PUBLIC KEY-----\n";
  212. $publicKeyString .= wordwrap($publicKeyInfoBase64, 64, "\n", true);
  213. $publicKeyString .= "\n-----END PUBLIC KEY-----\n";
  214. return $publicKeyString;
  215. }
  216. /**
  217. * Encode a limited set of data types into ASN.1 encoding format
  218. * which is used in X.509 certificates
  219. *
  220. * @param string $data The data to encode
  221. * @param const $type The encoding format constant
  222. * @return string The encoded value
  223. * @throws Zend_InfoCard_Xml_Security_Exception
  224. */
  225. static protected function _encodeValue($data, $type)
  226. {
  227. // Null pad some data when we get it (integer values > 128 and bitstrings)
  228. if( (($type == self::ASN_TYPE_INTEGER) && (ord($data) > 0x7f)) ||
  229. ($type == self::ASN_TYPE_BITSTRING)) {
  230. $data = "\0$data";
  231. }
  232. $len = strlen($data);
  233. // encode the value based on length of the string
  234. // I'm fairly confident that this is by no means a complete implementation
  235. // but it is enough for our purposes
  236. switch(true) {
  237. case ($len < 128):
  238. return sprintf("%c%c%s", $type, $len, $data);
  239. case ($len < 0x0100):
  240. return sprintf("%c%c%c%s", $type, 0x81, $len, $data);
  241. case ($len < 0x010000):
  242. return sprintf("%c%c%c%c%s", $type, 0x82, $len / 0x0100, $len % 0x0100, $data);
  243. default:
  244. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  245. throw new Zend_InfoCard_Xml_Security_Exception("Could not encode value");
  246. }
  247. require_once 'Zend/InfoCard/Xml/Security/Exception.php';
  248. throw new Zend_InfoCard_Xml_Security_Exception("Invalid code path");
  249. }
  250. /**
  251. * Securely compare two strings for equality while avoided C level memcmp()
  252. * optimisations capable of leaking timing information useful to an attacker
  253. * attempting to iteratively guess the unknown string (e.g. password) being
  254. * compared against.
  255. *
  256. * @param string $a
  257. * @param string $b
  258. * @return bool
  259. */
  260. static protected function _secureStringCompare($a, $b)
  261. {
  262. if (strlen($a) !== strlen($b)) {
  263. return false;
  264. }
  265. $result = 0;
  266. for ($i = 0; $i < strlen($a); $i++) {
  267. $result |= ord($a[$i]) ^ ord($b[$i]);
  268. }
  269. return $result == 0;
  270. }
  271. }