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

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

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